Compare commits

...

22 Commits

Author SHA1 Message Date
手瓜一十雪
be40bbdf40 release: 2.5.0 2024-09-13 17:42:11 +08:00
手瓜一十雪
df4f42e79e fix: video name 2024-09-13 17:38:11 +08:00
Alen
5f80058f70 Merge pull request #360 from cnxysoft/upmain
fix: bugs
2024-09-13 17:30:23 +08:00
Alen
0cbe59052d Merge branch 'main' into upmain 2024-09-13 17:25:59 +08:00
Alen
af28a26e37 fix: 无法发送url视频 2024-09-13 17:22:32 +08:00
Alen
70c596df93 fix: headers分割 2024-09-13 16:11:15 +08:00
手瓜一十雪
748b51428c feat: createUidFromTinyId 2024-09-13 16:05:25 +08:00
手瓜一十雪
8ad746397c feat: JoinDragonGroupEmoji 2024-09-13 15:35:05 +08:00
手瓜一十雪
45baed2f9a tag: deprecated 2024-09-13 15:30:04 +08:00
手瓜一十雪
74185f2d33 fix 2024-09-13 14:05:04 +08:00
手瓜一十雪
90a91e4105 fix 2024-09-13 13:54:12 +08:00
手瓜一十雪
11aa3a0315 feat: fetchOtherProfileLike 2024-09-12 20:05:46 +08:00
手瓜一十雪
0c2e39214f style: lint 2024-09-12 19:55:56 +08:00
手瓜一十雪
d89620d7a6 style: folder 2024-09-12 19:55:26 +08:00
手瓜一十雪
edf80775b7 release: v2.4.9 2024-09-12 19:47:57 +08:00
手瓜一十雪
46e56ac726 remove: polyFill 2024-09-12 19:35:54 +08:00
手瓜一十雪
40b2f6bfd6 release: 2.4.7 2024-09-12 18:31:11 +08:00
手瓜一十雪
911e4921e2 fix: 删除旧文件 2024-09-12 18:15:38 +08:00
手瓜一十雪
1db9bb419d fix: 一处异常字段 2024-09-12 10:55:18 +08:00
手瓜一十雪
c6241a94e3 style: lint 2024-09-12 09:28:41 +08:00
手瓜一十雪
1cbf75ca36 style: lint 2024-09-12 09:28:26 +08:00
手瓜一十雪
8f85c897c8 refactor: SysMsg 2024-09-12 09:20:10 +08:00
35 changed files with 306 additions and 157 deletions

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "2.4.6", "version": "2.5.0",
"icon": "./logo.png", "icon": "./logo.png",
"authors": [ "authors": [
{ {

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "2.4.6", "version": "2.5.0",
"scripts": { "scripts": {
"build:framework": "vite build --mode framework", "build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell", "build:shell": "vite build --mode shell",

View File

@@ -1,4 +1,3 @@
import fs from 'fs';
import fsPromise from 'fs/promises'; import fsPromise from 'fs/promises';
import path from 'node:path'; import path from 'node:path';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
@@ -12,7 +11,7 @@ const FFMPEG_PATH = process.env.FFMPEG_PATH || 'ffmpeg';
async function guessDuration(pttPath: string, logger: LogWrapper) { async function guessDuration(pttPath: string, logger: LogWrapper) {
const pttFileInfo = await fsPromise.stat(pttPath); const pttFileInfo = await fsPromise.stat(pttPath);
let duration = Math.max(1, Math.floor(pttFileInfo.size / 1024 / 3)); // 3kb/s const duration = Math.max(1, Math.floor(pttFileInfo.size / 1024 / 3)); // 3kb/s
logger.log('通过文件大小估算语音的时长:', duration); logger.log('通过文件大小估算语音的时长:', duration);
return duration; return duration;
} }
@@ -20,7 +19,7 @@ async function guessDuration(pttPath: string, logger: LogWrapper) {
async function convert(filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> { async function convert(filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> {
return new Promise<Buffer>((resolve, reject) => { return new Promise<Buffer>((resolve, reject) => {
const cp = spawn(FFMPEG_PATH, ['-y', '-i', filePath, '-ar', '24000', '-ac', '1', '-f', 's16le', pcmPath]); const cp = spawn(FFMPEG_PATH, ['-y', '-i', filePath, '-ar', '24000', '-ac', '1', '-f', 's16le', pcmPath]);
cp.on('error', err => { cp.on('error', (err: Error) => {
logger.log('FFmpeg处理转换出错: ', err.message); logger.log('FFmpeg处理转换出错: ', err.message);
reject(err); reject(err);
}); });

View File

@@ -128,6 +128,7 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi
let url: string; let url: string;
let headers: Record<string, string> = { let headers: Record<string, string> = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36',
'referer': typeof options === 'string' ? options : options.url,
}; };
if (typeof options === 'string') { if (typeof options === 'string') {
url = options; url = options;
@@ -242,7 +243,6 @@ export async function uri2local(dir: string, uri: string, filename: string | und
const filenameTemp = tempName + fileExt; const filenameTemp = tempName + fileExt;
const filePath = path.join(dir, filenameTemp); const filePath = path.join(dir, filenameTemp);
fs.copyFileSync(HandledUri, filePath); fs.copyFileSync(HandledUri, filePath);
//console.log('复制文件到临时文件', HandledUri, filePath);
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
} }
//接下来都要有文件名 //接下来都要有文件名
@@ -250,7 +250,7 @@ export async function uri2local(dir: string, uri: string, filename: string | und
if (UriType == FileUriType.Remote) { if (UriType == FileUriType.Remote) {
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname)); const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
if (pathInfo.name) { if (pathInfo.name) {
let pathlen = 200 - dir.length - pathInfo.name.length; const pathlen = 200 - dir.length - pathInfo.name.length;
filename = pathlen > 0 ? pathInfo.name.substring(0, pathlen) : pathInfo.name.substring(pathInfo.name.length, pathInfo.name.length - 10);//过长截断 filename = pathlen > 0 ? pathInfo.name.substring(0, pathlen) : pathInfo.name.substring(pathInfo.name.length, pathInfo.name.length - 10);//过长截断
if (pathInfo.ext) { if (pathInfo.ext) {
filename += pathInfo.ext; filename += pathInfo.ext;
@@ -260,7 +260,6 @@ export async function uri2local(dir: string, uri: string, filename: string | und
const fileExt = path.extname(HandledUri).replace(/[/\\:*?"<>|]/g, '_').substring(0, 10); const fileExt = path.extname(HandledUri).replace(/[/\\:*?"<>|]/g, '_').substring(0, 10);
const filePath = path.join(dir, tempName + fileExt); const filePath = path.join(dir, tempName + fileExt);
const buffer = await httpDownload(HandledUri); const buffer = await httpDownload(HandledUri);
//fs.writeFileSync(filePath, buffer);
//没有文件就创建 //没有文件就创建
fs.writeFileSync(filePath, buffer, { flag: 'wx' }); fs.writeFileSync(filePath, buffer, { flag: 'wx' });
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };

View File

@@ -209,7 +209,8 @@ export function getQQVersionConfigPath(exePath: string = ''): string | undefined
return configVersionInfoPath; return configVersionInfoPath;
} }
export function calcQQLevel(level: QQLevel) { export function calcQQLevel(level?: QQLevel) {
if (!level) return 0;
const { crownNum, sunNum, moonNum, starNum } = level; const { crownNum, sunNum, moonNum, starNum } = level;
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum; return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;
} }

View File

@@ -1 +1 @@
export const napCatVersion = '2.4.6'; export const napCatVersion = '2.5.0';

View File

@@ -147,8 +147,17 @@ export class NTQQFileApi {
} catch (e) { } catch (e) {
logger.logError('获取视频信息失败,将使用默认值', e); logger.logError('获取视频信息失败,将使用默认值', e);
} }
let newFilePath = filePath + '.mp4';
fs.renameSync(filePath, newFilePath); let fileExt = 'mp4';
try {
const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext;
if (tempExt) fileExt = tempExt;
} catch (e) {
this.context.logger.logError('获取文件类型失败', e);
}
const newFilePath = filePath + '.' + fileExt;
fs.copyFileSync(filePath, newFilePath);
context.deleteAfterSentFiles.push(newFilePath);
filePath = newFilePath; filePath = newFilePath;
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO); const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
if (fileSize === 0) { if (fileSize === 0) {
@@ -188,11 +197,12 @@ export class NTQQFileApi {
thumbPath.set(0, _thumbPath); thumbPath.set(0, _thumbPath);
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : ''; const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : '';
context.deleteAfterSentFiles.push(path); context.deleteAfterSentFiles.push(path);
const uploadName = (fileName || _fileName).toLocaleLowerCase().endsWith('.' + fileExt.toLocaleLowerCase()) ? (fileName || _fileName) : (fileName || _fileName) + '.' + fileExt;
return { return {
elementType: ElementType.VIDEO, elementType: ElementType.VIDEO,
elementId: '', elementId: '',
videoElement: { videoElement: {
fileName: fileName || _fileName, fileName: uploadName,
filePath: path, filePath: path,
videoMd5: md5, videoMd5: md5,
thumbMd5, thumbMd5,

View File

@@ -32,10 +32,6 @@ export class NTQQGroupApi {
for (const group of this.groups) { for (const group of this.groups) {
this.groupCache.set(group.groupCode, group); this.groupCache.set(group.groupCode, group);
} }
// let text = await this.context.session.getMsgService().sendSsoCmdReqByContend(
// 'LightAppSvc.mini_app_share.AdaptShareInfo',
// JSON.stringify({ data: 'test' }));
// console.log(text);
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`); this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
} }
@@ -266,6 +262,23 @@ export class NTQQGroupApi {
} }
return member; return member;
} }
async searchGroup(groupCode: string) {
const [, ret] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelSearchService/searchGroup',
'NodeIKernelSearchListener/onSearchGroupResult',
[{
keyWords: groupCode,
groupNum: 25,
exactSearch: false,
penetrate: ''
}],
(ret) => ret.result === 0,
(params) => !!params.groupInfos.find(g => g.groupCode === groupCode),
1,
5000
);
return ret.groupInfos.find(g => g.groupCode === groupCode);
}
async getGroupMemberEx(GroupCode: string, uid: string, forced = false, retry = 2) { async getGroupMemberEx(GroupCode: string, uid: string, forced = false, retry = 2) {
const data = await solveAsyncProblem((eventWrapper: NTEventWrapper, GroupCode: string, uid: string, forced = false) => { const data = await solveAsyncProblem((eventWrapper: NTEventWrapper, GroupCode: string, uid: string, forced = false) => {
return eventWrapper.callNormalEventV2( return eventWrapper.callNormalEventV2(

View File

@@ -94,6 +94,7 @@ export class NTQQMsgApi {
pageLimit: 1, pageLimit: 1,
}); });
} }
//@deprecated
async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) { async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z); return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
} }

View File

@@ -11,7 +11,13 @@ export class NTQQUserApi {
this.context = context; this.context = context;
this.core = core; this.core = core;
} }
//self_tind格式
async createUidFromTinyId(tinyId: string) {
return this.context.session.getMsgService().createUidFromTinyId(this.core.selfInfo.uin, tinyId);
}
async getStatusByUid(uid: string) {
return this.context.session.getProfileService().getStatus(uid);
}
async getProfileLike(uid: string) { async getProfileLike(uid: string) {
return this.context.session.getProfileLikeService().getBuddyProfileLike({ return this.context.session.getProfileLikeService().getBuddyProfileLike({
friendUids: [uid], friendUids: [uid],
@@ -24,7 +30,18 @@ export class NTQQUserApi {
limit: 20, limit: 20,
}); });
} }
async fetchOtherProfileLike(uid: string) {
return this.context.session.getProfileLikeService().getBuddyProfileLike({
friendUids: [uid],
basic: 1,
vote: 1,
favorite: 0,
userProfile: 0,
type: 1,
start: 0,
limit: 20,
});
}
async setLongNick(longNick: string) { async setLongNick(longNick: string) {
return this.context.session.getProfileService().setLongNick(longNick); return this.context.session.getProfileService().setLongNick(longNick);
} }

View File

@@ -959,3 +959,18 @@ export interface TmpChatInfo {
sessionType: number; sessionType: number;
sig: string; sig: string;
} }
export interface MsgReqType {
peer: Peer,
byType: number,
msgId: string,
msgSeq: string,
msgTime: string,
clientSeq: string,
cnt: number,
queryOrder: boolean,
includeSelf: boolean,
includeDeleteMsg: boolean,
extraCnt: number
}
//getMsgsIncludeSelf Peer必须 byType 1
//getMsgsWithMsgTimeAndClientSeqForC2C Peer必须 byType 3

View File

@@ -216,7 +216,7 @@ export interface BuddyProfileLikeReq {
userProfile: number; userProfile: number;
type: number; type: number;
start: number; start: number;
limit: number; limit?: number;
} }
export interface QQLevel { export interface QQLevel {

View File

@@ -40,7 +40,7 @@ export class RkeyManager {
async refreshRkey(): Promise<any> { async refreshRkey(): Promise<any> {
//刷新rkey //刷新rkey
for (let url of this.serverUrl) { for (const url of this.serverUrl) {
try { try {
this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET'); this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET');
} catch (e) { } catch (e) {

View File

@@ -145,7 +145,6 @@ export class NapCatCore {
if (Info.status == 20) { if (Info.status == 20) {
this.selfInfo.online = false; this.selfInfo.online = false;
this.context.logger.log("账号状态变更为离线"); this.context.logger.log("账号状态变更为离线");
return;
} else { } else {
this.selfInfo.online = true; this.selfInfo.online = true;
} }

View File

@@ -0,0 +1,97 @@
import { ChatType } from '@/core';
export interface SearchGroupInfo {
groupCode: string;
ownerUid: string;
groupFlag: number;
groupFlagExt: number;
maxMemberNum: number;
memberNum: number;
groupOption: number;
classExt: number;
groupName: string;
fingerMemo: string;
groupQuestion: string;
certType: number;
shutUpAllTimestamp: number;
shutUpMeTimestamp: number;
groupTypeFlag: number;
privilegeFlag: number;
groupSecLevel: number;
groupFlagExt3: number;
isConfGroup: number;
isModifyConfGroupFace: number;
isModifyConfGroupName: number;
noFigerOpenFlag: number;
noCodeFingerOpenFlag: number;
groupFlagExt4: number;
groupMemo: string;
cmdUinMsgSeq: number;
cmdUinJoinTime: number;
cmdUinUinFlag: number;
cmdUinMsgMask: number;
groupSecLevelInfo: number;
cmdUinPrivilege: number;
cmdUinFlagEx2: number;
appealDeadline: number;
remarkName: string;
isTop: boolean;
richFingerMemo: string;
groupAnswer: string;
joinGroupAuth: string;
isAllowModifyConfGroupName: number;
}
export interface GroupInfo {
groupCode: string;
searchGroupInfo: SearchGroupInfo;
privilege: number;
}
export interface GroupSearchResult {
keyWord: string;
errorCode: number;
groupInfos: GroupInfo[];
penetrate: string;
isEnd: boolean;
nextPos: number;
}
export interface NodeIKernelSearchListener {
onSearchGroupResult(params: GroupSearchResult): void;
onSearchFileKeywordsResult(params: {
searchId: string,
hasMore: boolean,
resultItems: {
chatType: ChatType,
buddyChatInfo: any[],
discussChatInfo: any[],
groupChatInfo: {
groupCode: string,
isConf: boolean,
hasModifyConfGroupFace: boolean,
hasModifyConfGroupName: boolean,
groupName: string,
remark: string
}[],
dataLineChatInfo: any[],
tmpChatInfo: any[],
msgId: string,
msgSeq: string,
msgTime: string,
senderUid: string,
senderNick: string,
senderRemark: string,
senderCard: string,
elemId: string,
elemType: number,
fileSize: string,
filePath: string,
fileName: string,
hits: {
start: number,
end: number
}[]
}[]
}): void;
}

View File

@@ -1,39 +0,0 @@
import { ChatType } from '@/core';
export interface NodeIKernelSearchListener_Polyfill {
onSearchFileKeywordsResult(params: {
searchId: string,
hasMore: boolean,
resultItems: {
chatType: ChatType,
buddyChatInfo: any[],
discussChatInfo: any[],
groupChatInfo: {
groupCode: string,
isConf: boolean,
hasModifyConfGroupFace: boolean,
hasModifyConfGroupName: boolean,
groupName: string,
remark: string
}[],
dataLineChatInfo: any[],
tmpChatInfo: any[],
msgId: string,
msgSeq: string,
msgTime: string,
senderUid: string,
senderNick: string,
senderRemark: string,
senderCard: string,
elemId: string,
elemType: number,
fileSize: string,
filePath: string,
fileName: string,
hits: {
start: number,
end: number
}[]
}[]
}): void;
}

View File

@@ -9,7 +9,7 @@ export * from './NodeIKernelProfileListener';
export * from './NodeIKernelTicketListener'; export * from './NodeIKernelTicketListener';
export * from './NodeIKernelStorageCleanListener'; export * from './NodeIKernelStorageCleanListener';
export * from './NodeIKernelFileAssistantListener'; export * from './NodeIKernelFileAssistantListener';
export * from './NodeIKernelSearchListener_Polyfill'; export * from './NodeIKernelSearchListener';
import type { import type {
NodeIKernelBuddyListener, NodeIKernelBuddyListener,
@@ -19,11 +19,11 @@ import type {
NodeIKernelMsgListener, NodeIKernelMsgListener,
NodeIKernelProfileListener, NodeIKernelProfileListener,
NodeIKernelRobotListener, NodeIKernelRobotListener,
NodeIKernelSearchListener_Polyfill,
NodeIKernelSessionListener, NodeIKernelSessionListener,
NodeIKernelStorageCleanListener, NodeIKernelStorageCleanListener,
NodeIKernelTicketListener, NodeIKernelTicketListener,
} from '.'; } from '.';
import { NodeIKernelSearchListener } from './NodeIKernelSearchListener';
export type ListenerNamingMapping = { export type ListenerNamingMapping = {
NodeIKernelSessionListener: NodeIKernelSessionListener; NodeIKernelSessionListener: NodeIKernelSessionListener;
@@ -36,5 +36,5 @@ export type ListenerNamingMapping = {
NodeIKernelTicketListener: NodeIKernelTicketListener; NodeIKernelTicketListener: NodeIKernelTicketListener;
NodeIKernelStorageCleanListener: NodeIKernelStorageCleanListener; NodeIKernelStorageCleanListener: NodeIKernelStorageCleanListener;
NodeIKernelFileAssistantListener: NodeIKernelFileAssistantListener; NodeIKernelFileAssistantListener: NodeIKernelFileAssistantListener;
NodeIKernelSearchListener: NodeIKernelSearchListener_Polyfill; NodeIKernelSearchListener: NodeIKernelSearchListener;
}; };

View File

@@ -1,7 +1,7 @@
import { ElementType, MessageElement, Peer, RawMessage, SendMessageElement } from '@/core/entities'; import { ElementType, MessageElement, Peer, RawMessage, SendMessageElement } from '@/core/entities';
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener'; import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
import { GeneralCallResult } from '@/core/services/common'; import { GeneralCallResult } from '@/core/services/common';
import { QueryMsgsParams, TmpChatInfoApi } from '../entities/msg'; import { MsgReqType, QueryMsgsParams, TmpChatInfoApi } from '../entities/msg';
export interface NodeIKernelMsgService { export interface NodeIKernelMsgService {
@@ -147,12 +147,15 @@ export interface NodeIKernelMsgService {
msgList: RawMessage[] msgList: RawMessage[]
}>; }>;
//@deprecated
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>; getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
//@deprecated
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & { getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
msgList: RawMessage[] msgList: RawMessage[]
}>; }>;
//@deprecated
getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>; getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getMsgsWithStatus(params: { getMsgsWithStatus(params: {
@@ -168,7 +171,7 @@ export interface NodeIKernelMsgService {
getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<GeneralCallResult & { getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<GeneralCallResult & {
msgList: RawMessage[] msgList: RawMessage[]
}>; }>;
//@deprecated
getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & { getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & {
msgList: RawMessage[] msgList: RawMessage[]
}>; }>;
@@ -179,6 +182,8 @@ export interface NodeIKernelMsgService {
getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>; getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getMsgsExt(msgReq: MsgReqType): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getSingleMsg(Peer: Peer, msgSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>; getSingleMsg(Peer: Peer, msgSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getSourceOfReplyMsg(peer: Peer, MsgId: string, SourceSeq: string): unknown; getSourceOfReplyMsg(peer: Peer, MsgId: string, SourceSeq: string): unknown;
@@ -318,7 +323,7 @@ export interface NodeIKernelMsgService {
getFileThumbSavePath(...args: unknown[]): unknown; getFileThumbSavePath(...args: unknown[]): unknown;
translatePtt2Text(msgId: string, peer: Peer, msgElement: unknown): unknown; translatePtt2Text(msgId: string, peer: Peer, msgElement: MessageElement): unknown;
setPttPlayedState(...args: unknown[]): unknown; setPttPlayedState(...args: unknown[]): unknown;
@@ -391,7 +396,12 @@ export interface NodeIKernelMsgService {
getEmojiResourcePath(...args: unknown[]): unknown; getEmojiResourcePath(...args: unknown[]): unknown;
JoinDragonGroupEmoji(JoinDragonGroupEmojiReq: any/*joinDragonGroupEmojiReq*/): unknown; JoinDragonGroupEmoji(JoinDragonGroupEmojiReq: {
latestMsgSeq: string,
manageEmojiId: number,
manageMsgSeq: string,
peerContact: Peer
}): Promise<unknown>;
getMsgAbstracts(...args: unknown[]): unknown; getMsgAbstracts(...args: unknown[]): unknown;
@@ -518,7 +528,7 @@ export interface NodeIKernelMsgService {
canImportOldDbMsg(...args: unknown[]): unknown; canImportOldDbMsg(...args: unknown[]): unknown;
setPowerStatus(z: boolean): unknown; setPowerStatus(isPowerOn: boolean): unknown;
canProcessDataMigration(...args: unknown[]): unknown; canProcessDataMigration(...args: unknown[]): unknown;
@@ -607,7 +617,7 @@ export interface NodeIKernelMsgService {
setIKernelPublicAccountAdapter(...args: unknown[]): unknown; setIKernelPublicAccountAdapter(...args: unknown[]): unknown;
//tempChatGameSession有关 //tempChatGameSession有关
createUidFromTinyId(fromTinyId: string, toTinyId: string): unknown; createUidFromTinyId(fromTinyId: string, toTinyId: string): string;
dataMigrationGetDataAvaiableContactList(...args: unknown[]): unknown; dataMigrationGetDataAvaiableContactList(...args: unknown[]): unknown;

View File

@@ -1,4 +1,5 @@
import { ChatType } from '../entities'; import { ChatType } from '../entities';
import { GeneralCallResult } from './common';
export interface NodeIKernelSearchService { export interface NodeIKernelSearchService {
@@ -8,7 +9,12 @@ export interface NodeIKernelSearchService {
searchStranger(unknown: string, searchStranger: unknown, searchParams: unknown): Promise<unknown>; searchStranger(unknown: string, searchStranger: unknown, searchParams: unknown): Promise<unknown>;
searchGroup(...args: any[]): unknown;// needs 1 arguments searchGroup(param: {
keyWords: string,
groupNum: number,
exactSearch: boolean,
penetrate: string
}): Promise<GeneralCallResult>;// needs 1 arguments
searchLocalInfo(keywords: string, unknown: number/*4*/): unknown; searchLocalInfo(keywords: string, unknown: number/*4*/): unknown;

View File

@@ -7,17 +7,18 @@ const SchemaData = {
properties: { properties: {
group_id: { type: ['string', 'number'] }, group_id: { type: ['string', 'number'] },
folder_id: { type: 'string' }, folder_id: { type: 'string' },
folder: { type: 'string' }
}, },
required: ['group_id', 'folder_id'], required: ['group_id'],
} as const satisfies JSONSchema; } as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>; type Payload = FromSchema<typeof SchemaData>;
export class DeleteGroupFileFolder extends BaseAction<Payload, any> { export class DeleteGroupFileFolder extends BaseAction<Payload, any> {
actionName = ActionName.GoCQHTTP_DeleteGroupFileFolder; actionName = ActionName.GoCQHTTP_DeleteGroupFileFolder;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
return (await this.core.apis.GroupApi.DelGroupFileFolder( return (await this.core.apis.GroupApi.DelGroupFileFolder(
payload.group_id.toString(), payload.folder_id)).groupFileCommonResult; payload.group_id.toString(), payload.folder ?? payload.folder_id ?? '')).groupFileCommonResult;
} }
} }

View File

@@ -73,7 +73,7 @@ export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileRespon
headers[headerItem] = ''; headers[headerItem] = '';
} else { } else {
const key = headerItem.substring(0, spilt); const key = headerItem.substring(0, spilt);
headers[key] = headerItem.substring(0, spilt + 1); headers[key] = headerItem.substring(spilt + 1);
} }
} }
} }

View File

@@ -8,9 +8,10 @@ const SchemaData = {
properties: { properties: {
group_id: { type: ['string', 'number'] }, group_id: { type: ['string', 'number'] },
folder_id: { type: 'string' }, folder_id: { type: 'string' },
folder: { type: 'string' },
file_count: { type: ['string', 'number'] }, file_count: { type: ['string', 'number'] },
}, },
required: ['group_id', 'folder_id'], required: ['group_id'],
} as const satisfies JSONSchema; } as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>; type Payload = FromSchema<typeof SchemaData>;
@@ -26,7 +27,7 @@ export class GetGroupFilesByFolder extends BaseAction<any, any> {
startIndex: 0, startIndex: 0,
sortOrder: 2, sortOrder: 2,
showOnlinedocFolder: 0, showOnlinedocFolder: 0,
folderId: payload.folder_id, folderId: payload.folder ?? payload.folder_id ?? '',
}).catch(() => []); }).catch(() => []);
return { return {
files: ret.filter(item => item.fileInfo) files: ret.filter(item => item.fileInfo)

View File

@@ -21,24 +21,25 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
async _handle(payload: Payload): Promise<OB11User> { async _handle(payload: Payload): Promise<OB11User> {
const user_id = payload.user_id.toString(); const user_id = payload.user_id.toString();
const extendData = await this.core.apis.UserApi.getUserDetailInfoByUin(user_id); const extendData = await this.core.apis.UserApi.getUserDetailInfoByUin(user_id);
const uid = (await this.core.apis.UserApi.getUidByUinV2(user_id))!; let uid = (await this.core.apis.UserApi.getUidByUinV2(user_id));
if (!uid || uid.indexOf('*') != -1) { if (!uid) uid = extendData.detail.uid;
return { const info = (await this.core.apis.UserApi.getUserDetailInfo(uid));
...extendData.detail.simpleInfo.coreInfo, return {
...extendData.detail.commonExt, user_id: parseInt(extendData.detail.uin) ?? 0,
...extendData.detail.simpleInfo.baseInfo, uid: info.uid ?? uid,
...extendData.detail.simpleInfo.relationFlags, nickname: extendData.detail.simpleInfo.coreInfo.nick,
...extendData.detail.simpleInfo.status, age: extendData.detail.simpleInfo.baseInfo.age ?? info.age,
user_id: parseInt(extendData.detail.uin) || 0, qid: extendData.detail.simpleInfo.baseInfo.qid,
nickname: extendData.detail.simpleInfo.coreInfo.nick, qqLevel: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? info.qqLevel),
sex: OB11UserSex.unknown, sex: OB11Entities.sex(extendData.detail.simpleInfo.baseInfo.sex) ?? OB11UserSex.unknown,
age: extendData.detail.simpleInfo.baseInfo.age || 0, long_nick: extendData.detail.simpleInfo.baseInfo.longNick ?? info.longNick,
qid: extendData.detail.simpleInfo.baseInfo.qid, reg_time: extendData.detail.commonExt.regTime ?? info.regTime,
level: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? 0) || 0, is_vip: extendData.detail.simpleInfo.vasInfo?.svipFlag,
login_days: 0, is_years_vip: extendData.detail.simpleInfo.vasInfo?.yearVipFlag,
}; vip_level: extendData.detail.simpleInfo.vasInfo?.vipLevel,
} remark: extendData.detail.simpleInfo.coreInfo.remark ?? info.remark,
const data = { ...extendData, ...(await this.core.apis.UserApi.getUserDetailInfo(uid)) }; status: extendData.detail.simpleInfo.status?.status ?? info.status,
return OB11Entities.stranger(data); login_days: 0,//失效
};
} }
} }

View File

@@ -57,7 +57,6 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
const noticeShowEditCard = +(payload.is_show_edit_card ?? 0); const noticeShowEditCard = +(payload.is_show_edit_card ?? 0);
const noticeTipWindowType = +(payload.tip_window_type ?? 0); const noticeTipWindowType = +(payload.tip_window_type ?? 0);
const noticeConfirmRequired = +(payload.confirm_required ?? 1); const noticeConfirmRequired = +(payload.confirm_required ?? 1);
//const publishGroupBulletinResult = await this.core.apis.GroupApi.publishGroupBulletin(payload.group_id.toString(), payload.content, UploadImage, noticePinned, noticeConfirmRequired);
const publishGroupBulletinResult = await this.core.apis.WebApi.setGroupNotice( const publishGroupBulletinResult = await this.core.apis.WebApi.setGroupNotice(
payload.group_id.toString(), payload.group_id.toString(),
payload.content, payload.content,
@@ -71,7 +70,7 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
UploadImage?.height UploadImage?.height
); );
if (!publishGroupBulletinResult || publishGroupBulletinResult.ec != 0) { if (!publishGroupBulletinResult || publishGroupBulletinResult.ec != 0) {
throw `设置群公告失败,错误信息:${publishGroupBulletinResult?.em}`; throw new Error(`设置群公告失败,错误信息:${publishGroupBulletinResult?.em}`);
} }
return null; return null;
} }

View File

@@ -37,11 +37,11 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
if (!downloadResult.success) { if (!downloadResult.success) {
throw new Error(downloadResult.errMsg); throw new Error(downloadResult.errMsg);
} }
let msgContext: MessageContext = { const msgContext: MessageContext = {
peer: peer, peer: peer,
deleteAfterSentFiles: [] deleteAfterSentFiles: []
} };
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder_id); const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id);
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], [], true); await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], [], true);
return null; return null;
} }

View File

@@ -45,13 +45,13 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
throw new Error(downloadResult.errMsg); throw new Error(downloadResult.errMsg);
} }
let msgContext: MessageContext = { const msgContext: MessageContext = {
peer: await createContext(this.core, { peer: await createContext(this.core, {
user_id: payload.user_id.toString(), user_id: payload.user_id.toString(),
group_id: undefined, group_id: undefined,
}, ContextMode.Private), }, ContextMode.Private),
deleteAfterSentFiles: [] deleteAfterSentFiles: []
} };
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name); const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], [], true); await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], [], true);
return null; return null;

View File

@@ -20,7 +20,17 @@ class GetGroupInfo extends BaseAction<Payload, OB11Group> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const group = (await this.core.apis.GroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString()); const group = (await this.core.apis.GroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString());
if (!group) throw `${payload.group_id}不存在`; if (!group) {
const data = await this.core.apis.GroupApi.searchGroup(payload.group_id.toString());
if (!data) throw new Error('Group not found');
return {
...data.searchGroupInfo,
group_id: +payload.group_id,
group_name: data.searchGroupInfo.groupName,
member_count: data.searchGroupInfo.memberNum,
max_member_count: data.searchGroupInfo.maxMemberNum,
};
}
return OB11Entities.group(group); return OB11Entities.group(group);
} }
} }

View File

@@ -142,22 +142,22 @@ export class OneBotGroupApi {
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE //下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
const type = json.items[json.items.length - 1]?.txt; const type = json.items[json.items.length - 1]?.txt;
switch (type) { switch (type) {
case "头衔": { case "头衔": {
const memberUin = json.items[1].param[0]; const memberUin = json.items[1].param[0];
const title = json.items[3].txt; const title = json.items[3].txt;
logger.logDebug('收到群成员新头衔消息', json); logger.logDebug('收到群成员新头衔消息', json);
return new OB11GroupTitleEvent( return new OB11GroupTitleEvent(
this.core, this.core,
parseInt(msg.peerUid), parseInt(msg.peerUid),
parseInt(memberUin), parseInt(memberUin),
title, title,
); );
}; }
case "移出": case "移出":
logger.logDebug('收到机器人被踢消息', json); logger.logDebug('收到机器人被踢消息', json);
return; return;
default: default:
logger.logWarn('收到未知的灰条消息', json); logger.logWarn('收到未知的灰条消息', json);
} }
} }
} }

View File

@@ -34,6 +34,7 @@ import { RequestUtil } from '@/common/request';
import fs from 'node:fs'; import fs from 'node:fs';
import fsPromise from 'node:fs/promises'; import fsPromise from 'node:fs/promises';
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent'; import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
import { SysMessage, SysMessageType } from '@/core/proto/ProfileLike';
type RawToOb11Converters = { type RawToOb11Converters = {
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: ( [Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
@@ -185,7 +186,7 @@ export class OneBotMsgApi {
data: { data: {
file: 'marketface', file: 'marketface',
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, "." + _.key + ".jpg"), file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, "." + _.key + ".jpg"),
path: elementWrapper.elementId, path: url,
url: url, url: url,
file_unique: _.key file_unique: _.key
}, },
@@ -818,4 +819,38 @@ export class OneBotMsgApi {
return { path, fileName: inputdata.name ?? fileName }; return { path, fileName: inputdata.name ?? fileName };
} }
async parseSysMessage(msg: number[]) {
const sysMsg = SysMessage.decode(Uint8Array.from(msg)) as unknown as SysMessageType;
if (sysMsg.msgSpec.length === 0) {
return;
}
const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
if (msgType === 528 && subType === 39 && subSubType === 39) {
if (!sysMsg.bodyWrapper) return;
const event = await this.obContext.apis.UserApi.parseLikeEvent(sysMsg.bodyWrapper.wrappedBody);
return event;
}
/*
if (msgType === 732 && subType === 16 && subSubType === 16) {
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
if (greyTip.subTypeId === 36) {
const emojiLikeToOthers = EmojiLikeToOthersWrapper1
.fromBinary(greyTip.rest)
.wrapper!
.body!;
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
return;
}
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
greyTip.groupCode.toString(),
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
emojiLikeToOthers.msgSpec!.msgSeq.toString(),
emojiLikeToOthers.attributes!.emojiId,
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
}
}
*/
}
} }

View File

@@ -126,6 +126,7 @@ export class OB11Entities {
return { return {
group_id: parseInt(peerId), group_id: parseInt(peerId),
folder_id: folder.folderId, folder_id: folder.folderId,
folder:folder.folderId,
folder_name: folder.folderName, folder_name: folder.folderName,
create_time: folder.createTime, create_time: folder.createTime,
creator: parseInt(folder.createUin), creator: parseInt(folder.createUin),

View File

@@ -238,39 +238,10 @@ export class NapCatOneBot11Adapter {
private initMsgListener() { private initMsgListener() {
const msgListener = new NodeIKernelMsgListener(); const msgListener = new NodeIKernelMsgListener();
msgListener.onRecvSysMsg = async (msg) => { msgListener.onRecvSysMsg = (msg) => {
const sysMsg = SysMessage.decode(Uint8Array.from(msg)) as unknown as SysMessageType; this.apis.MsgApi.parseSysMessage(msg).then((event) => {
if (sysMsg.msgSpec.length === 0) { if (event) this.networkManager.emitEvent(event);
return; }).catch(e => this.context.logger.logError('constructSysMessage error: ', e));
}
const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
if (msgType === 528 && subType === 39 && subSubType === 39) {
if (!sysMsg.bodyWrapper) return;
let event = await this.apis.UserApi.parseLikeEvent(sysMsg.bodyWrapper.wrappedBody);
if (event) await this.networkManager.emitEvent(event);
};
/*
if (msgType === 732 && subType === 16 && subSubType === 16) {
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
if (greyTip.subTypeId === 36) {
const emojiLikeToOthers = EmojiLikeToOthersWrapper1
.fromBinary(greyTip.rest)
.wrapper!
.body!;
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
return;
}
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
greyTip.groupCode.toString(),
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
emojiLikeToOthers.msgSpec!.msgSeq.toString(),
emojiLikeToOthers.attributes!.emojiId,
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
}
}
*/
}; };
msgListener.onInputStatusPush = async data => { msgListener.onInputStatusPush = async data => {

View File

@@ -47,7 +47,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
} }
//鉴权 //鉴权
this.authorize(token, wsClient, wsReq); this.authorize(token, wsClient, wsReq);
let paramUrl = wsReq.url?.indexOf('?') !== -1 ? wsReq.url?.substring(0, wsReq.url?.indexOf('?')) : wsReq.url; const paramUrl = wsReq.url?.indexOf('?') !== -1 ? wsReq.url?.substring(0, wsReq.url?.indexOf('?')) : wsReq.url;
const isEventConnect = paramUrl === '/event' || paramUrl === '' || paramUrl === '/'; const isEventConnect = paramUrl === '/event' || paramUrl === '' || paramUrl === '/';
if (isEventConnect) { if (isEventConnect) {
this.connectEvent(core, wsClient); this.connectEvent(core, wsClient);

View File

@@ -1,4 +1,5 @@
export interface OB11User { export interface OB11User {
[key: string]: any;
user_id: number; user_id: number;
nickname: string; nickname: string;
remark?: string; remark?: string;
@@ -81,6 +82,7 @@ export interface OB11GroupFile {
export interface OB11GroupFileFolder { export interface OB11GroupFileFolder {
group_id: number, group_id: number,
folder_id: string, folder_id: string,
folder: string,
folder_name: string, folder_name: string,
create_time: number, create_time: number,
creator: number, creator: number,

View File

@@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) {
SettingItem( SettingItem(
'<span id="napcat-update-title">Napcat</span>', '<span id="napcat-update-title">Napcat</span>',
undefined, undefined,
SettingButton('V2.4.6', 'napcat-update-button', 'secondary'), SettingButton('V2.5.0', 'napcat-update-button', 'secondary'),
), ),
]), ]),
SettingList([ SettingList([

View File

@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
SettingItem( SettingItem(
'<span id="napcat-update-title">Napcat</span>', '<span id="napcat-update-title">Napcat</span>',
void 0, void 0,
SettingButton("V2.4.6", "napcat-update-button", "secondary") SettingButton("V2.5.0", "napcat-update-button", "secondary")
) )
]), ]),
SettingList([ SettingList([