mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b44e1618fb | ||
![]() |
1e13483bc3 | ||
![]() |
f9519d3923 | ||
![]() |
86cdfbb79b | ||
![]() |
a70585e854 | ||
![]() |
040d0a8635 | ||
![]() |
efa512ab21 | ||
![]() |
9b04aed8b3 | ||
![]() |
7087eafe37 | ||
![]() |
c81c4af653 | ||
![]() |
c05cc9dd02 | ||
![]() |
1a0da00f2d | ||
![]() |
31b0c1d3d7 | ||
![]() |
53c1d40bcf | ||
![]() |
97cacb4383 | ||
![]() |
e03905abaf | ||
![]() |
06eba28b4c | ||
![]() |
bbfeac46dd | ||
![]() |
2fe4da094a | ||
![]() |
b454d8c0f9 | ||
![]() |
1f9b5453cc | ||
![]() |
3261791e99 | ||
![]() |
3bb12e3f45 | ||
![]() |
1dc2f7e5a2 | ||
![]() |
2531b08538 | ||
![]() |
9fcfb5493c | ||
![]() |
4576354c51 | ||
![]() |
1dcf2ef0c6 | ||
![]() |
3642c65e8c | ||
![]() |
40e105994a | ||
![]() |
f2ee973882 |
2
.env.universal
Normal file
2
.env.universal
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_BUILD_TYPE = Production
|
||||||
|
VITE_BUILD_PLATFORM = Universal
|
@@ -34,7 +34,7 @@ NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现
|
|||||||
|
|
||||||
|
|
||||||
## 回家旅途
|
## 回家旅途
|
||||||
[QQ Group](https://qm.qq.com/q/NWP25OeV0c)
|
[QQ Group](https://qm.qq.com/q/I6LU87a0Yq)
|
||||||
|
|
||||||
## 感谢他们
|
## 感谢他们
|
||||||
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权
|
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权
|
||||||
|
BIN
external/LiteLoaderWrapper.zip
vendored
BIN
external/LiteLoaderWrapper.zip
vendored
Binary file not shown.
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "qq-chat",
|
"name": "qq-chat",
|
||||||
"version": "9.9.16-29456",
|
"version": "9.9.16-29927",
|
||||||
"verHash": "dd395162",
|
"verHash": "3e273e30",
|
||||||
"linuxVersion": "3.2.13-29456",
|
"linuxVersion": "3.2.13-29927",
|
||||||
"linuxVerHash": "e379390a",
|
"linuxVerHash": "833d113c",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "QQ",
|
"description": "QQ",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"qd": "externals/devtools/cli/index.js"
|
"qd": "externals/devtools/cli/index.js"
|
||||||
},
|
},
|
||||||
"main": "./loadNapCat.js",
|
"main": "./loadNapCat.js",
|
||||||
"buildVersion": "29456",
|
"buildVersion": "29927",
|
||||||
"isPureShell": true,
|
"isPureShell": true,
|
||||||
"isByteCodeShell": true,
|
"isByteCodeShell": true,
|
||||||
"platform": "win32",
|
"platform": "win32",
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "4.2.0",
|
"version": "4.2.8",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -2,11 +2,13 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.2.0",
|
"version": "4.2.8",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
|
||||||
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
||||||
"build:shell": "npm run build:webui && vite build --mode shell || exit 1",
|
"build:shell": "npm run build:webui && vite build --mode shell || exit 1",
|
||||||
"build:webui": "cd napcat.webui && vite build",
|
"build:webui": "cd napcat.webui && vite build",
|
||||||
|
"dev:universal": "vite build --mode universal",
|
||||||
"dev:framework": "vite build --mode framework",
|
"dev:framework": "vite build --mode framework",
|
||||||
"dev:shell": "vite build --mode shell",
|
"dev:shell": "vite build --mode shell",
|
||||||
"dev:webui": "cd napcat.webui && npm run webui:dev",
|
"dev:webui": "cd napcat.webui && npm run webui:dev",
|
||||||
|
@@ -96,7 +96,7 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.logError.bind(logger)('convert silk failed', error.stack);
|
logger.logError('convert silk failed', error.stack);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,27 +33,27 @@ export abstract class ConfigBase<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
read(copy_default: boolean = true): T {
|
read(copy_default: boolean = true): T {
|
||||||
const logger = this.core.context.logger;
|
|
||||||
const configPath = this.getConfigPath(this.core.selfInfo.uin);
|
const configPath = this.getConfigPath(this.core.selfInfo.uin);
|
||||||
if (!fs.existsSync(configPath) && copy_default) {
|
if (!fs.existsSync(configPath) && copy_default) {
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8'));
|
fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8'));
|
||||||
logger.log(`[Core] [Config] 配置文件创建成功!\n`);
|
this.core.context.logger.log(`[Core] [Config] 配置文件创建成功!\n`);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.logError.bind(logger)(`[Core] [Config] 创建配置文件时发生错误:`, e.message);
|
this.core.context.logger.logError(`[Core] [Config] 创建配置文件时发生错误:`, e.message);
|
||||||
}
|
}
|
||||||
} else if (!fs.existsSync(configPath) && !copy_default) {
|
} else if (!fs.existsSync(configPath) && !copy_default) {
|
||||||
fs.writeFileSync(configPath, '{}');
|
fs.writeFileSync(configPath, '{}');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
this.configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
logger.logDebug(`[Core] [Config] 配置文件${configPath}加载`, this.configData);
|
this.core.context.logger.logDebug(`[Core] [Config] 配置文件${configPath}加载`, this.configData);
|
||||||
return this.configData;
|
return this.configData;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e instanceof SyntaxError) {
|
if (e instanceof SyntaxError) {
|
||||||
logger.logError.bind(logger)(`[Core] [Config] 配置文件格式错误,请检查配置文件:`, e.message);
|
this.core.context.logger.logError(`[Core] [Config] 配置文件格式错误,请检查配置文件:`, e.message);
|
||||||
} else {
|
} else {
|
||||||
logger.logError.bind(logger)(`[Core] [Config] 读取配置文件时发生错误:`, e.message);
|
this.core.context.logger.logError(`[Core] [Config] 读取配置文件时发生错误:`, e.message);
|
||||||
}
|
}
|
||||||
return {} as T;
|
return {} as T;
|
||||||
}
|
}
|
||||||
@@ -61,14 +61,13 @@ export abstract class ConfigBase<T> {
|
|||||||
|
|
||||||
|
|
||||||
save(newConfigData: T = this.configData) {
|
save(newConfigData: T = this.configData) {
|
||||||
const logger = this.core.context.logger;
|
|
||||||
const selfInfo = this.core.selfInfo;
|
const selfInfo = this.core.selfInfo;
|
||||||
this.configData = newConfigData;
|
this.configData = newConfigData;
|
||||||
const configPath = this.getConfigPath(selfInfo.uin);
|
const configPath = this.getConfigPath(selfInfo.uin);
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(configPath, JSON.stringify(newConfigData, this.getKeys(), 2));
|
fs.writeFileSync(configPath, JSON.stringify(newConfigData, this.getKeys(), 2));
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.logError.bind(logger)(`保存配置文件 ${configPath} 时发生错误:`, e.message);
|
this.core.context.logger.logError(`保存配置文件 ${configPath} 时发生错误:`, e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -190,16 +190,17 @@ export async function checkUriType(Uri: string) {
|
|||||||
}, Uri);
|
}, Uri);
|
||||||
if (LocalFileRet) return LocalFileRet;
|
if (LocalFileRet) return LocalFileRet;
|
||||||
const OtherFileRet = await solveProblem((uri: string) => {
|
const OtherFileRet = await solveProblem((uri: string) => {
|
||||||
//再判断是否是Http
|
// 再判断是否是Http
|
||||||
if (uri.startsWith('http://') || uri.startsWith('https://')) {
|
if (uri.startsWith('http:') || uri.startsWith('https:')) {
|
||||||
return { Uri: uri, Type: FileUriType.Remote };
|
return { Uri: uri, Type: FileUriType.Remote };
|
||||||
}
|
}
|
||||||
//再判断是否是Base64
|
// 再判断是否是Base64
|
||||||
if (uri.startsWith('base64://')) {
|
if (uri.startsWith('base64:')) {
|
||||||
return { Uri: uri, Type: FileUriType.Base64 };
|
return { Uri: uri, Type: FileUriType.Base64 };
|
||||||
}
|
}
|
||||||
if (uri.startsWith('file://')) {
|
// 默认file://
|
||||||
let filePath: string = uri.slice(7);
|
if (uri.startsWith('file:')) {
|
||||||
|
const filePath: string = decodeURIComponent(uri.startsWith('file:///') && process.platform === 'win32' ? uri.slice(8) : uri.slice(7));
|
||||||
return { Uri: filePath, Type: FileUriType.Local };
|
return { Uri: filePath, Type: FileUriType.Local };
|
||||||
}
|
}
|
||||||
if (uri.startsWith('data:')) {
|
if (uri.startsWith('data:')) {
|
||||||
@@ -235,7 +236,7 @@ export async function uri2local(dir: string, uri: string, filename: string | und
|
|||||||
fs.copyFileSync(HandledUri, filePath);
|
fs.copyFileSync(HandledUri, filePath);
|
||||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
|
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
|
||||||
}
|
}
|
||||||
|
|
||||||
//接下来都要有文件名
|
//接下来都要有文件名
|
||||||
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));
|
||||||
|
@@ -69,7 +69,7 @@ export class RequestUtil {
|
|||||||
// 'Content-Length': Buffer.byteLength(postData),
|
// 'Content-Length': Buffer.byteLength(postData),
|
||||||
// },
|
// },
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const req = protocol.request(options, (res: any) => {
|
const req = protocol.request(options, (res: http.IncomingMessage) => {
|
||||||
let responseBody = '';
|
let responseBody = '';
|
||||||
res.on('data', (chunk: string | Buffer) => {
|
res.on('data', (chunk: string | Buffer) => {
|
||||||
responseBody += chunk.toString();
|
responseBody += chunk.toString();
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '4.2.0';
|
export const napCatVersion = '4.2.8';
|
||||||
|
@@ -41,7 +41,7 @@ export class NTQQFileApi {
|
|||||||
this.rkeyManager = new RkeyManager([
|
this.rkeyManager = new RkeyManager([
|
||||||
'https://rkey.napneko.icu/rkeys'
|
'https://rkey.napneko.icu/rkeys'
|
||||||
],
|
],
|
||||||
this.context.logger
|
this.context.logger
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,6 @@ export class NTQQFileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendVideoElement(context: SendMessageContext, filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
|
async createValidSendVideoElement(context: SendMessageContext, filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
|
||||||
const logger = this.core.context.logger;
|
|
||||||
let videoInfo = {
|
let videoInfo = {
|
||||||
width: 1920,
|
width: 1920,
|
||||||
height: 1080,
|
height: 1080,
|
||||||
@@ -152,9 +151,9 @@ export class NTQQFileApi {
|
|||||||
filePath,
|
filePath,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
videoInfo = await getVideoInfo(filePath, logger);
|
videoInfo = await getVideoInfo(filePath, this.context.logger);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.logError.bind(logger)('获取视频信息失败,将使用默认值', e);
|
this.context.logger.logError('获取视频信息失败,将使用默认值', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileExt = 'mp4';
|
let fileExt = 'mp4';
|
||||||
@@ -162,7 +161,7 @@ export class NTQQFileApi {
|
|||||||
const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext;
|
const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext;
|
||||||
if (tempExt) fileExt = tempExt;
|
if (tempExt) fileExt = tempExt;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.context.logger.logError.bind(logger)('获取文件类型失败', e);
|
this.context.logger.logError('获取文件类型失败', e);
|
||||||
}
|
}
|
||||||
const newFilePath = filePath + '.' + fileExt;
|
const newFilePath = filePath + '.' + fileExt;
|
||||||
fs.copyFileSync(filePath, newFilePath);
|
fs.copyFileSync(filePath, newFilePath);
|
||||||
@@ -183,7 +182,7 @@ export class NTQQFileApi {
|
|||||||
ffmpeg(filePath)
|
ffmpeg(filePath)
|
||||||
.on('error', (err) => {
|
.on('error', (err) => {
|
||||||
try {
|
try {
|
||||||
logger.logDebug('获取视频封面失败,使用默认封面', err);
|
this.context.logger.logDebug('获取视频封面失败,使用默认封面', err);
|
||||||
if (diyThumbPath) {
|
if (diyThumbPath) {
|
||||||
fsPromises.copyFile(diyThumbPath, thumbPath).then(() => {
|
fsPromises.copyFile(diyThumbPath, thumbPath).then(() => {
|
||||||
resolve(thumbPath);
|
resolve(thumbPath);
|
||||||
@@ -193,7 +192,7 @@ export class NTQQFileApi {
|
|||||||
resolve(thumbPath);
|
resolve(thumbPath);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.logError.bind(logger)('获取视频封面失败,使用默认封面失败', error);
|
this.context.logger.logError('获取视频封面失败,使用默认封面失败', error);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.screenshots({
|
.screenshots({
|
||||||
@@ -230,6 +229,7 @@ export class NTQQFileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendPttElement(pttPath: string): Promise<SendPttElement> {
|
async createValidSendPttElement(pttPath: string): Promise<SendPttElement> {
|
||||||
|
|
||||||
const { converted, path: silkPath, duration } = await encodeSilk(pttPath, this.core.NapCatTempPath, this.core.context.logger);
|
const { converted, path: silkPath, duration } = await encodeSilk(pttPath, this.core.NapCatTempPath, this.core.context.logger);
|
||||||
if (!silkPath) {
|
if (!silkPath) {
|
||||||
throw new Error('语音转换失败, 请检查语音文件是否正常');
|
throw new Error('语音转换失败, 请检查语音文件是否正常');
|
||||||
@@ -239,8 +239,7 @@ export class NTQQFileApi {
|
|||||||
throw new Error('文件异常,大小为0');
|
throw new Error('文件异常,大小为0');
|
||||||
}
|
}
|
||||||
if (converted) {
|
if (converted) {
|
||||||
fsPromises.unlink(silkPath).then().catch(
|
fsPromises.unlink(silkPath).then().catch((e) => this.context.logger.logError('删除临时文件失败', e)
|
||||||
(e) => this.context.logger.logError.bind(this.context.logger)('删除临时文件失败', e)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -307,18 +306,18 @@ export class NTQQFileApi {
|
|||||||
element.elementType === ElementType.FILE
|
element.elementType === ElementType.FILE
|
||||||
) {
|
) {
|
||||||
switch (element.elementType) {
|
switch (element.elementType) {
|
||||||
case ElementType.PIC:
|
case ElementType.PIC:
|
||||||
element.picElement!.sourcePath = elementResults[elementIndex];
|
element.picElement!.sourcePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.VIDEO:
|
case ElementType.VIDEO:
|
||||||
element.videoElement!.filePath = elementResults[elementIndex];
|
element.videoElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.PTT:
|
case ElementType.PTT:
|
||||||
element.pttElement!.filePath = elementResults[elementIndex];
|
element.pttElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.FILE:
|
case ElementType.FILE:
|
||||||
element.fileElement!.filePath = elementResults[elementIndex];
|
element.fileElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
elementIndex++;
|
elementIndex++;
|
||||||
}
|
}
|
||||||
@@ -454,7 +453,7 @@ export class NTQQFileApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.context.logger.logError.bind(this.context.logger)('获取rkey失败', error.message);
|
this.context.logger.logError('获取rkey失败', error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rkeyData.online_rkey) {
|
if (!rkeyData.online_rkey) {
|
||||||
@@ -464,7 +463,7 @@ export class NTQQFileApi {
|
|||||||
rkeyData.private_rkey = tempRkeyData.private_rkey;
|
rkeyData.private_rkey = tempRkeyData.private_rkey;
|
||||||
rkeyData.online_rkey = tempRkeyData.expired_time > Date.now() / 1000;
|
rkeyData.online_rkey = tempRkeyData.expired_time > Date.now() / 1000;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.context.logger.logError.bind(this.context.logger)('获取rkey失败 Fallback Old Mode', e);
|
this.context.logger.logError('获取rkey失败 Fallback Old Mode', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,7 +27,7 @@ export class NTQQGroupApi {
|
|||||||
this.core = core;
|
this.core = core;
|
||||||
}
|
}
|
||||||
async initApi() {
|
async initApi() {
|
||||||
this.initCache().then().catch(this.context.logger.logError.bind(this.context.logger));
|
this.initCache().then().catch(e => this.context.logger.logError(e));
|
||||||
}
|
}
|
||||||
async initCache() {
|
async initCache() {
|
||||||
this.groups = await this.getGroups();
|
this.groups = await this.getGroups();
|
||||||
|
@@ -31,7 +31,7 @@ export class NTQQPacketApi {
|
|||||||
await this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion())
|
await this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion())
|
||||||
.then()
|
.then()
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.logger.logError.bind(this.core.context.logger);
|
this.logger.logError(err);
|
||||||
this.errStack.push(err);
|
this.errStack.push(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -3,12 +3,12 @@ import { PicType } from '../types';
|
|||||||
export async function getFileTypeForSendType(picPath: string): Promise<PicType> {
|
export async function getFileTypeForSendType(picPath: string): Promise<PicType> {
|
||||||
const fileTypeResult = (await fileType.fileTypeFromFile(picPath))?.ext ?? 'jpg';
|
const fileTypeResult = (await fileType.fileTypeFromFile(picPath))?.ext ?? 'jpg';
|
||||||
const picTypeMap: { [key: string]: PicType } = {
|
const picTypeMap: { [key: string]: PicType } = {
|
||||||
'webp': PicType.NEWPIC_WEBP,
|
//'webp': PicType.NEWPIC_WEBP,
|
||||||
'gif': PicType.NEWPIC_GIF,
|
'gif': PicType.NEWPIC_GIF,
|
||||||
'png': PicType.NEWPIC_APNG,
|
// 'png': PicType.NEWPIC_APNG,
|
||||||
'jpg': PicType.NEWPIC_JPEG,
|
// 'jpg': PicType.NEWPIC_JPEG,
|
||||||
'jpeg': PicType.NEWPIC_JPEG,
|
// 'jpeg': PicType.NEWPIC_JPEG,
|
||||||
'bmp': PicType.NEWPIC_BMP,
|
// 'bmp': PicType.NEWPIC_BMP,
|
||||||
};
|
};
|
||||||
return picTypeMap[fileTypeResult] ?? PicType.NEWPIC_JPEG;
|
return picTypeMap[fileTypeResult] ?? PicType.NEWPIC_JPEG;
|
||||||
}
|
}
|
@@ -27,7 +27,6 @@ export class RkeyManager {
|
|||||||
await this.refreshRkey();
|
await this.refreshRkey();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`获取rkey失败: ${e}`);//外抛
|
throw new Error(`获取rkey失败: ${e}`);//外抛
|
||||||
//this.logger.logError.bind(this.logger)('获取rkey失败', e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.rkeyData;
|
return this.rkeyData;
|
||||||
@@ -50,7 +49,7 @@ export class RkeyManager {
|
|||||||
expired_time: temp.expired_time
|
expired_time: temp.expired_time
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.logError.bind(this.logger)(`[Rkey] Get Rkey ${url} Error `, e);
|
this.logger.logError(`[Rkey] Get Rkey ${url} Error `, e);
|
||||||
//是否为最后一个url
|
//是否为最后一个url
|
||||||
if (url === this.serverUrl[this.serverUrl.length - 1]) {
|
if (url === this.serverUrl[this.serverUrl.length - 1]) {
|
||||||
throw new Error(`获取rkey失败: ${e}`);//外抛
|
throw new Error(`获取rkey失败: ${e}`);//外抛
|
||||||
|
@@ -127,7 +127,7 @@ export class NapCatCore {
|
|||||||
await api.initApi();
|
await api.initApi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.initNapCatCoreListeners().then().catch(this.context.logger.logError.bind(this.context.logger));
|
this.initNapCatCoreListeners().then().catch((e) => this.context.logger.logError(e));
|
||||||
|
|
||||||
this.context.logger.setFileLogEnabled(
|
this.context.logger.setFileLogEnabled(
|
||||||
this.configLoader.configData.fileLog,
|
this.configLoader.configData.fileLog,
|
||||||
@@ -154,7 +154,7 @@ export class NapCatCore {
|
|||||||
const msgListener = new NodeIKernelMsgListener();
|
const msgListener = new NodeIKernelMsgListener();
|
||||||
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
||||||
// 下线通知
|
// 下线通知
|
||||||
this.context.logger.logError.bind(this.context.logger)('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc);
|
this.context.logger.logError('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc);
|
||||||
this.selfInfo.online = false;
|
this.selfInfo.online = false;
|
||||||
};
|
};
|
||||||
msgListener.onRecvMsg = (msgs) => {
|
msgListener.onRecvMsg = (msgs) => {
|
||||||
|
@@ -62,7 +62,7 @@ export const GroupChange = {
|
|||||||
operatorUid: ProtoField(5, ScalarType.STRING, true),
|
operatorUid: ProtoField(5, ScalarType.STRING, true),
|
||||||
increaseType: ProtoField(6, ScalarType.UINT32),
|
increaseType: ProtoField(6, ScalarType.UINT32),
|
||||||
field7: ProtoField(7, ScalarType.BYTES, true),
|
field7: ProtoField(7, ScalarType.BYTES, true),
|
||||||
}
|
};
|
||||||
|
|
||||||
export const PushMsgBody = {
|
export const PushMsgBody = {
|
||||||
responseHead: ProtoField(1, () => ResponseHead),
|
responseHead: ProtoField(1, () => ResponseHead),
|
||||||
|
@@ -163,7 +163,7 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
getGroupPortrait(): void;
|
getGroupPortrait(): void;
|
||||||
|
|
||||||
modifyGroupName(groupCode: string, groupName: string, arg: false): void;
|
modifyGroupName(groupCode: string, groupName: string, isNormalMember: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
modifyGroupRemark(groupCode: string, remark: string): void;
|
modifyGroupRemark(groupCode: string, remark: string): void;
|
||||||
|
|
||||||
|
@@ -1,6 +1,16 @@
|
|||||||
//LiteLoader需要提供部分IPC接口,以便于其他插件调用
|
//LiteLoader需要提供部分IPC接口,以便于其他插件调用
|
||||||
const { ipcMain } = require('electron');
|
const { ipcMain } = require('electron');
|
||||||
const napcat = require('./napcat.cjs');
|
const napcat = require('./napcat.cjs');
|
||||||
|
const { shell } = require('electron');
|
||||||
ipcMain.handle('napcat_get_webtoken', async (event, arg) => {
|
ipcMain.handle('napcat_get_webtoken', async (event, arg) => {
|
||||||
return napcat.NCgetWebUiUrl();
|
return napcat.NCgetWebUiUrl();
|
||||||
});
|
});
|
||||||
|
ipcMain.on('open_external_url', (event, url) => {
|
||||||
|
shell.openExternal(url);
|
||||||
|
});
|
||||||
|
ipcMain.handle('napcat_get_reactweb', async (event, arg) => {
|
||||||
|
let url = new URL(await napcat.NCgetWebUiUrl());
|
||||||
|
let port = url.port;
|
||||||
|
let token = url.searchParams.get('token');
|
||||||
|
return `https://napcat.152710.xyz/web_login?back=http://127.0.0.1:${port}&token=${token}`;
|
||||||
|
});
|
@@ -58,7 +58,7 @@ export async function NCoreInitFramework(
|
|||||||
await loaderObject.core.initCore();
|
await loaderObject.core.initCore();
|
||||||
|
|
||||||
//启动WebUi
|
//启动WebUi
|
||||||
InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger));
|
InitWebUi(logger, pathWrapper).then().catch(e => logger.logError(e));
|
||||||
//初始化LLNC的Onebot实现
|
//初始化LLNC的Onebot实现
|
||||||
await new NapCatOneBot11Adapter(loaderObject.core, loaderObject.context, pathWrapper).InitOneBot();
|
await new NapCatOneBot11Adapter(loaderObject.core, loaderObject.context, pathWrapper).InitOneBot();
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
const { contextBridge } = require('electron');
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
const { ipcRenderer } = require('electron');
|
|
||||||
|
|
||||||
const napcat = {
|
const napcat = {
|
||||||
getWebUiUrl: async () => {
|
getWebUiUrl: async () => {
|
||||||
return ipcRenderer.invoke('napcat_get_webtoken');
|
return ipcRenderer.invoke('napcat_get_webtoken');
|
||||||
},
|
},
|
||||||
|
openExternalUrl: async (url) => {
|
||||||
|
ipcRenderer.send('open_external_url', url);
|
||||||
|
},
|
||||||
|
getWebUiUrlReact: async () => {
|
||||||
|
return ipcRenderer.invoke('napcat_get_reactweb');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// 在window对象下导出只读对象
|
// 在window对象下导出只读对象
|
||||||
contextBridge.exposeInMainWorld('napcat', napcat);
|
contextBridge.exposeInMainWorld('napcat', napcat);
|
@@ -1,27 +1,20 @@
|
|||||||
export const onSettingWindowCreated = async (view) => {
|
export const onSettingWindowCreated = async (view) => {
|
||||||
|
|
||||||
// view.style.width = "100%";
|
|
||||||
// view.style.height = "100%";
|
|
||||||
// //添加iframe
|
|
||||||
// const iframe = document.createElement("iframe");
|
|
||||||
// iframe.src = await window.napcat.getWebUiUrl();
|
|
||||||
// iframe.width = "100%";
|
|
||||||
// iframe.height = "100%";
|
|
||||||
// iframe.style.border = "none";
|
|
||||||
// //去掉iframe滚动条
|
|
||||||
// //iframe.scrolling = "no";
|
|
||||||
// //有滚动条何尝不是一种美
|
|
||||||
// view.appendChild(iframe);
|
|
||||||
let webui = await window.napcat.getWebUiUrl();
|
let webui = await window.napcat.getWebUiUrl();
|
||||||
|
let webuiReact = await window.napcat.getWebUiUrlReact();
|
||||||
view.innerHTML = `
|
view.innerHTML = `
|
||||||
<setting-section data-title="">
|
<setting-section data-title="">
|
||||||
<setting-panel>
|
<setting-panel>
|
||||||
<setting-list data-direction="column">
|
<setting-list data-direction="column">
|
||||||
<setting-item>
|
<setting-item>
|
||||||
<setting-button data-type="primary" class="nc_openwebui">打开配置页面</setting-button>
|
<setting-button data-type="primary" class="nc_openwebui">在QQ内打开配置页面(VUE)</setting-button>
|
||||||
|
<setting-button data-type="primary" class="nc_openwebui_ex">在默认浏览器打开配置页面(VUE)</setting-button>
|
||||||
|
</setting-item>
|
||||||
|
<setting-item>
|
||||||
|
<setting-button data-type="primary" class="nc_openwebui_ex_react">在默认浏览器打开配置页面(React)</setting-button>
|
||||||
</setting-item>
|
</setting-item>
|
||||||
<setting-item>
|
<setting-item>
|
||||||
<div>
|
<div>
|
||||||
|
<setting-text>WebUi远程地址可以点击下方复制哦~</setting-text>
|
||||||
<setting-text class="nc_webui">WebUi</setting-text>
|
<setting-text class="nc_webui">WebUi</setting-text>
|
||||||
</div>
|
</div>
|
||||||
</setting-item>
|
</setting-item>
|
||||||
@@ -29,8 +22,27 @@ export const onSettingWindowCreated = async (view) => {
|
|||||||
</setting-panel>
|
</setting-panel>
|
||||||
</setting-section>
|
</setting-section>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
view.querySelector('.nc_openwebui').addEventListener('click', () => {
|
view.querySelector('.nc_openwebui').addEventListener('click', () => {
|
||||||
window.open(webui, '_blank');
|
window.open(webui, '_blank');
|
||||||
});
|
});
|
||||||
|
view.querySelector('.nc_openwebui_ex').addEventListener('click', () => {
|
||||||
|
window.napcat.openExternalUrl(webui);
|
||||||
|
});
|
||||||
|
|
||||||
|
view.querySelector('.nc_openwebui_ex_react').addEventListener('click', () => {
|
||||||
|
window.napcat.openExternalUrl(webuiReact);
|
||||||
|
});
|
||||||
|
|
||||||
view.querySelector('.nc_webui').innerText = webui;
|
view.querySelector('.nc_webui').innerText = webui;
|
||||||
};
|
|
||||||
|
// 添加点击复制功能
|
||||||
|
view.querySelector('.nc_webui').addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(webui);
|
||||||
|
alert('WebUi URL 已复制到剪贴板');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('复制到剪贴板失败: ', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -13,7 +13,7 @@ export class GoCQHTTPHandleQuickAction extends OneBotAction<Payload, null> {
|
|||||||
async _handle(payload: Payload): Promise<null> {
|
async _handle(payload: Payload): Promise<null> {
|
||||||
this.obContext.apis.QuickActionApi
|
this.obContext.apis.QuickActionApi
|
||||||
.handleQuickOperation(payload.context, payload.operation)
|
.handleQuickOperation(payload.context, payload.operation)
|
||||||
.catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
.catch(e => this.core.context.logger.logError(e));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,10 @@ export default class SetGroupName extends OneBotAction<Payload, null> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload): Promise<null> {
|
async _handle(payload: Payload): Promise<null> {
|
||||||
await this.core.apis.GroupApi.setGroupName(payload.group_id.toString(), payload.group_name);
|
let ret = await this.core.apis.GroupApi.setGroupName(payload.group_id.toString(), payload.group_name);
|
||||||
|
if (ret.result !== 0) {
|
||||||
|
throw new Error(`设置群名称失败 ErrCode: ${ret.result} ErrMsg: ${ret.errMsg}`);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -162,11 +162,10 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
finallySendElements: SendArkElement,
|
finallySendElements: SendArkElement,
|
||||||
res_id?: string
|
res_id?: string
|
||||||
} | null> {
|
} | null> {
|
||||||
const logger = this.core.context.logger;
|
|
||||||
const packetMsg: PacketMsg[] = [];
|
const packetMsg: PacketMsg[] = [];
|
||||||
for (const node of messageNodes) {
|
for (const node of messageNodes) {
|
||||||
if (dp >= 3) {
|
if (dp >= 3) {
|
||||||
logger.logWarn('转发消息深度超过3层,将停止解析!');
|
this.core.context.logger.logWarn('转发消息深度超过3层,将停止解析!');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!node.data.id) {
|
if (!node.data.id) {
|
||||||
@@ -191,29 +190,29 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
time: Number(node.data.time) || Date.now(),
|
time: Number(node.data.time) || Date.now(),
|
||||||
msg: sendElements,
|
msg: sendElements,
|
||||||
};
|
};
|
||||||
logger.logDebug(`handleForwardedNodesPacket[SendRaw] 开始转换 ${stringifyWithBigInt(packetMsgElements)}`);
|
this.core.context.logger.logDebug(`handleForwardedNodesPacket[SendRaw] 开始转换 ${stringifyWithBigInt(packetMsgElements)}`);
|
||||||
const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgWithSendMsgToPacketMsg(packetMsgElements);
|
const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgWithSendMsgToPacketMsg(packetMsgElements);
|
||||||
logger.logDebug(`handleForwardedNodesPacket[SendRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`);
|
this.core.context.logger.logDebug(`handleForwardedNodesPacket[SendRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`);
|
||||||
packetMsg.push(transformedMsg);
|
packetMsg.push(transformedMsg);
|
||||||
} else if (node.data.id) {
|
} else if (node.data.id) {
|
||||||
const id = node.data.id;
|
const id = node.data.id;
|
||||||
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(+id) || MessageUnique.getPeerByMsgId(id);
|
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(+id) || MessageUnique.getPeerByMsgId(id);
|
||||||
if (!nodeMsg) {
|
if (!nodeMsg) {
|
||||||
logger.logError.bind(this.core.context.logger)('转发消息失败,未找到消息', id);
|
this.core.context.logger.logError('转发消息失败,未找到消息', id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsg.Peer, [nodeMsg.MsgId])).msgList[0];
|
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsg.Peer, [nodeMsg.MsgId])).msgList[0];
|
||||||
logger.logDebug(`handleForwardedNodesPacket[PureRaw] 开始转换 ${stringifyWithBigInt(msg)}`);
|
this.core.context.logger.logDebug(`handleForwardedNodesPacket[PureRaw] 开始转换 ${stringifyWithBigInt(msg)}`);
|
||||||
await this.core.apis.FileApi.downloadRawMsgMedia([msg]);
|
await this.core.apis.FileApi.downloadRawMsgMedia([msg]);
|
||||||
const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgToPacketMsg(msg, msgPeer);
|
const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgToPacketMsg(msg, msgPeer);
|
||||||
logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`);
|
this.core.context.logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`);
|
||||||
packetMsg.push(transformedMsg);
|
packetMsg.push(transformedMsg);
|
||||||
} else {
|
} else {
|
||||||
logger.logDebug(`handleForwardedNodesPacket 跳过元素 ${stringifyWithBigInt(node)}`);
|
this.core.context.logger.logDebug(`handleForwardedNodesPacket 跳过元素 ${stringifyWithBigInt(node)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (packetMsg.length === 0) {
|
if (packetMsg.length === 0) {
|
||||||
logger.logWarn('handleForwardedNodesPacket 元素为空!');
|
this.core.context.logger.logWarn('handleForwardedNodesPacket 元素为空!');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const resid = await this.core.apis.PacketApi.pkt.operation.UploadForwardMsg(packetMsg, msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : 0);
|
const resid = await this.core.apis.PacketApi.pkt.operation.UploadForwardMsg(packetMsg, msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : 0);
|
||||||
@@ -253,14 +252,13 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
peerUid: this.core.selfInfo.uid,
|
peerUid: this.core.selfInfo.uid,
|
||||||
};
|
};
|
||||||
let nodeMsgIds: string[] = [];
|
let nodeMsgIds: string[] = [];
|
||||||
const logger = this.core.context.logger;
|
|
||||||
for (const messageNode of messageNodes) {
|
for (const messageNode of messageNodes) {
|
||||||
const nodeId = messageNode.data.id;
|
const nodeId = messageNode.data.id;
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
// 对Msgid和OB11ID混用情况兜底
|
// 对Msgid和OB11ID混用情况兜底
|
||||||
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
|
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
|
||||||
if (!nodeMsg) {
|
if (!nodeMsg) {
|
||||||
logger.logError.bind(this.core.context.logger)('转发消息失败,未找到消息', nodeId);
|
this.core.context.logger.logError('转发消息失败,未找到消息', nodeId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nodeMsgIds.push(nodeMsg.MsgId);
|
nodeMsgIds.push(nodeMsg.MsgId);
|
||||||
@@ -272,7 +270,7 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
const isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息
|
const isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息
|
||||||
if (isNodeMsg !== 0) {
|
if (isNodeMsg !== 0) {
|
||||||
if (isNodeMsg !== OB11Data.length) {
|
if (isNodeMsg !== OB11Data.length) {
|
||||||
logger.logError.bind(this.core.context.logger)('子消息中包含非node消息 跳过不合法部分');
|
this.core.context.logger.logError('子消息中包含非node消息 跳过不合法部分');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
|
const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
|
||||||
@@ -309,7 +307,7 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.logDebug('生成转发消息节点失败', e?.stack);
|
this.core.context.logger.logDebug('生成转发消息节点失败', e?.stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,7 +318,7 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
for (const msgId of nodeMsgIds) {
|
for (const msgId of nodeMsgIds) {
|
||||||
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId);
|
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId);
|
||||||
if (!nodeMsgPeer) {
|
if (!nodeMsgPeer) {
|
||||||
logger.logError.bind(this.core.context.logger)('转发消息失败,未找到消息', msgId);
|
this.core.context.logger.logError('转发消息失败,未找到消息', msgId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const nodeMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
|
const nodeMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
|
||||||
@@ -346,12 +344,12 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
|
if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
|
||||||
try {
|
try {
|
||||||
logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds);
|
this.core.context.logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds);
|
||||||
return {
|
return {
|
||||||
message: await this.core.apis.MsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds)
|
message: await this.core.apis.MsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds)
|
||||||
};
|
};
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.logError.bind(this.core.context.logger)('forward failed', e?.stack);
|
this.core.context.logger.logError('forward failed', e?.stack);
|
||||||
return {
|
return {
|
||||||
message: null
|
message: null
|
||||||
};
|
};
|
||||||
@@ -363,7 +361,6 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
chatType: ChatType.KCHATTYPEC2C,
|
chatType: ChatType.KCHATTYPEC2C,
|
||||||
peerUid: this.core.selfInfo.uid,
|
peerUid: this.core.selfInfo.uid,
|
||||||
};
|
};
|
||||||
const logger = this.core.context.logger;
|
|
||||||
//msg 为待克隆消息
|
//msg 为待克隆消息
|
||||||
const sendElements: SendMessageElement[] = [];
|
const sendElements: SendMessageElement[] = [];
|
||||||
|
|
||||||
@@ -372,12 +369,12 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sendElements.length === 0) {
|
if (sendElements.length === 0) {
|
||||||
logger.logDebug('需要clone的消息无法解析,将会忽略掉', msg);
|
this.core.context.logger.logDebug('需要clone的消息无法解析,将会忽略掉', msg);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return await this.core.apis.MsgApi.sendMsg(selfPeer, sendElements, true);
|
return await this.core.apis.MsgApi.sendMsg(selfPeer, sendElements, true);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.logError.bind(this.core.context.logger)(e?.stack, '克隆转发消息失败,将忽略本条消息', msg);
|
this.core.context.logger.logError(e?.stack, '克隆转发消息失败,将忽略本条消息', msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -66,31 +66,31 @@ export class OneBotGroupApi {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseGroupIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
|
// async parseGroupIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
|
||||||
this.core.context.logger.logDebug('收到新人被邀请进群消息', grayTipElement);
|
// this.core.context.logger.logDebug('收到新人被邀请进群消息', grayTipElement);
|
||||||
const xmlElement = grayTipElement.xmlElement;
|
// const xmlElement = grayTipElement.xmlElement;
|
||||||
if (xmlElement?.content) {
|
// if (xmlElement?.content) {
|
||||||
const regex = /jp="(\d+)"/g;
|
// const regex = /jp="(\d+)"/g;
|
||||||
|
|
||||||
const matches = [];
|
// const matches = [];
|
||||||
let match = null;
|
// let match = null;
|
||||||
|
|
||||||
while ((match = regex.exec(xmlElement.content)) !== null) {
|
// while ((match = regex.exec(xmlElement.content)) !== null) {
|
||||||
matches.push(match[1]);
|
// matches.push(match[1]);
|
||||||
}
|
// }
|
||||||
if (matches.length === 2) {
|
// if (matches.length === 2) {
|
||||||
const [inviter, invitee] = matches;
|
// const [inviter, invitee] = matches;
|
||||||
return new OB11GroupIncreaseEvent(
|
// return new OB11GroupIncreaseEvent(
|
||||||
this.core,
|
// this.core,
|
||||||
parseInt(GroupCode),
|
// parseInt(GroupCode),
|
||||||
parseInt(invitee),
|
// parseInt(invitee),
|
||||||
parseInt(inviter),
|
// parseInt(inviter),
|
||||||
'invite',
|
// 'invite',
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return undefined;
|
// return undefined;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// async parseGroupMemberIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
|
// async parseGroupMemberIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
|
||||||
// const groupElement = grayTipElement?.groupElement;
|
// const groupElement = grayTipElement?.groupElement;
|
||||||
@@ -159,7 +159,7 @@ export class OneBotGroupApi {
|
|||||||
}
|
}
|
||||||
const replyMsg = replyMsgList[0];
|
const replyMsg = replyMsgList[0];
|
||||||
if (!replyMsg) {
|
if (!replyMsg) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('解析表情回应消息失败: 未找到回应消息');
|
this.core.context.logger.logError('解析表情回应消息失败: 未找到回应消息');
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return new OB11GroupMsgEmojiLikeEvent(
|
return new OB11GroupMsgEmojiLikeEvent(
|
||||||
@@ -304,9 +304,8 @@ export class OneBotGroupApi {
|
|||||||
// 筛选出表情回应 事件
|
// 筛选出表情回应 事件
|
||||||
if (grayTipElement.xmlElement?.templId === '10382') {
|
if (grayTipElement.xmlElement?.templId === '10382') {
|
||||||
return await this.obContext.apis.GroupApi.parseGroupEmojiLikeEventByGrayTip(msg.peerUid, grayTipElement);
|
return await this.obContext.apis.GroupApi.parseGroupEmojiLikeEventByGrayTip(msg.peerUid, grayTipElement);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return await this.obContext.apis.GroupApi.parseGroupIncreaseEvent(msg.peerUid, grayTipElement);
|
//return await this.obContext.apis.GroupApi.parseGroupIncreaseEvent(msg.peerUid, grayTipElement);
|
||||||
}
|
}
|
||||||
} else if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
} else if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
||||||
// 解析json事件
|
// 解析json事件
|
||||||
@@ -315,7 +314,7 @@ export class OneBotGroupApi {
|
|||||||
} else if (grayTipElement.jsonGrayTipElement.busiId == JsonGrayBusiId.AIO_GROUP_ESSENCE_MSG_TIP) {
|
} else if (grayTipElement.jsonGrayTipElement.busiId == JsonGrayBusiId.AIO_GROUP_ESSENCE_MSG_TIP) {
|
||||||
return await this.parseEssenceMsg(msg, grayTipElement.jsonGrayTipElement.jsonStr);
|
return await this.parseEssenceMsg(msg, grayTipElement.jsonGrayTipElement.jsonStr);
|
||||||
} else {
|
} else {
|
||||||
return await this.parseOtherJsonEvent(msg, grayTipElement.jsonGrayTipElement.jsonStr, this.core.context)
|
return await this.parseOtherJsonEvent(msg, grayTipElement.jsonGrayTipElement.jsonStr, this.core.context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { OneBotFriendApi } from '@/onebot/api/friend';
|
import type { OneBotFriendApi } from '@/onebot/api/friend';
|
||||||
import { OneBotUserApi } from '@/onebot/api/user';
|
import type { OneBotUserApi } from '@/onebot/api/user';
|
||||||
import { OneBotGroupApi } from '@/onebot/api/group';
|
import type { OneBotGroupApi } from '@/onebot/api/group';
|
||||||
import { OneBotMsgApi } from '@/onebot/api/msg';
|
import type { OneBotMsgApi } from '@/onebot/api/msg';
|
||||||
import { OneBotQuickActionApi } from '@/onebot/api/quick-action';
|
import type { OneBotQuickActionApi } from '@/onebot/api/quick-action';
|
||||||
|
|
||||||
export * from './friend';
|
export * from './friend';
|
||||||
export * from './group';
|
export * from './group';
|
||||||
|
@@ -118,7 +118,6 @@ export class OneBotMsgApi {
|
|||||||
return {
|
return {
|
||||||
type: OB11MessageDataType.image,
|
type: OB11MessageDataType.image,
|
||||||
data: {
|
data: {
|
||||||
pic_type: element.picType,
|
|
||||||
summary: element.summary,
|
summary: element.summary,
|
||||||
file: encodedFileId,
|
file: encodedFileId,
|
||||||
sub_type: element.picSubType,
|
sub_type: element.picSubType,
|
||||||
@@ -130,7 +129,7 @@ export class OneBotMsgApi {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('获取图片url失败', e.stack);
|
this.core.context.logger.logError('获取图片url失败', e.stack);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -213,7 +212,7 @@ export class OneBotMsgApi {
|
|||||||
guildId: '',
|
guildId: '',
|
||||||
};
|
};
|
||||||
if (!records || !element.replyMsgTime || !element.senderUidStr) {
|
if (!records || !element.replyMsgTime || !element.senderUidStr) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('似乎是旧版客户端,获取不到引用的消息', element.replayMsgSeq);
|
this.core.context.logger.logError('似乎是旧版客户端,获取不到引用的消息', element.replayMsgSeq);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +230,7 @@ export class OneBotMsgApi {
|
|||||||
let replyMsg = replyMsgList.find(msg => msg.msgRandom === records.msgRandom);
|
let replyMsg = replyMsgList.find(msg => msg.msgRandom === records.msgRandom);
|
||||||
|
|
||||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)(
|
this.core.context.logger.logError(
|
||||||
'筛选结果,筛选消息失败,将使用Fallback-1 Seq: ',
|
'筛选结果,筛选消息失败,将使用Fallback-1 Seq: ',
|
||||||
element.replayMsgSeq,
|
element.replayMsgSeq,
|
||||||
',消息长度:',
|
',消息长度:',
|
||||||
@@ -242,7 +241,7 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||||
this.core.context.logger.logWarn.bind(this.core.context.logger)(
|
this.core.context.logger.logWarn(
|
||||||
'筛选消息失败,将使用Fallback-2 Seq:',
|
'筛选消息失败,将使用Fallback-2 Seq:',
|
||||||
element.replayMsgSeq,
|
element.replayMsgSeq,
|
||||||
',消息长度:',
|
',消息长度:',
|
||||||
@@ -256,7 +255,7 @@ export class OneBotMsgApi {
|
|||||||
|
|
||||||
// 丢弃该消息段
|
// 丢弃该消息段
|
||||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)(
|
this.core.context.logger.logError(
|
||||||
'最终筛选结果,筛选消息失败,获取不到引用的消息 Seq: ',
|
'最终筛选结果,筛选消息失败,获取不到引用的消息 Seq: ',
|
||||||
element.replayMsgSeq,
|
element.replayMsgSeq,
|
||||||
',消息长度:',
|
',消息长度:',
|
||||||
@@ -458,7 +457,7 @@ export class OneBotMsgApi {
|
|||||||
const sysFaces = faceConfig.sysface;
|
const sysFaces = faceConfig.sysface;
|
||||||
const face: any = sysFaces.find((systemFace) => systemFace.QSid === parsedFaceId.toString());
|
const face: any = sysFaces.find((systemFace) => systemFace.QSid === parsedFaceId.toString());
|
||||||
if (!face) {
|
if (!face) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('不支持的ID', id);
|
this.core.context.logger.logError('不支持的ID', id);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
parsedFaceId = parseInt(parsedFaceId.toString());
|
parsedFaceId = parseInt(parsedFaceId.toString());
|
||||||
@@ -581,20 +580,20 @@ export class OneBotMsgApi {
|
|||||||
// 保留, 直到...找到更好的解决方案
|
// 保留, 直到...找到更好的解决方案
|
||||||
if (data.id !== undefined) {
|
if (data.id !== undefined) {
|
||||||
if (!['qq', '163', 'kugou', 'kuwo', 'migu'].includes(data.type)) {
|
if (!['qq', '163', 'kugou', 'kuwo', 'migu'].includes(data.type)) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('音乐卡片type错误, 只支持qq、163、kugou、kuwo、migu,当前type:', data.type);
|
this.core.context.logger.logError('音乐卡片type错误, 只支持qq、163、kugou、kuwo、migu,当前type:', data.type);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!['qq', '163', 'kugou', 'kuwo', 'migu', 'custom'].includes(data.type)) {
|
if (!['qq', '163', 'kugou', 'kuwo', 'migu', 'custom'].includes(data.type)) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('音乐卡片type错误, 只支持qq、163、kugou、kuwo、migu、custom,当前type:', data.type);
|
this.core.context.logger.logError('音乐卡片type错误, 只支持qq、163、kugou、kuwo、migu、custom,当前type:', data.type);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (!data.url) {
|
if (!data.url) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('自定义音卡缺少参数url');
|
this.core.context.logger.logError('自定义音卡缺少参数url');
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (!data.image) {
|
if (!data.image) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('自定义音卡缺少参数image');
|
this.core.context.logger.logError('自定义音卡缺少参数image');
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -618,7 +617,7 @@ export class OneBotMsgApi {
|
|||||||
type: OB11MessageDataType.json
|
type: OB11MessageDataType.json
|
||||||
}, context);
|
}, context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('生成音乐消息失败', e);
|
this.core.context.logger.logError('生成音乐消息失败', e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -673,7 +672,7 @@ export class OneBotMsgApi {
|
|||||||
if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
||||||
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||||
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement);
|
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement);
|
||||||
if (PokeEvent) { return PokeEvent };
|
if (PokeEvent) { return PokeEvent; };
|
||||||
} else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
|
} else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
|
||||||
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
||||||
}
|
}
|
||||||
@@ -841,14 +840,14 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async convertArrayToStringMessage(originMsg: OB11Message): Promise<OB11Message> {
|
private async convertArrayToStringMessage(originMsg: OB11Message): Promise<OB11Message> {
|
||||||
let msg = structuredClone(originMsg);
|
const msg = structuredClone(originMsg);
|
||||||
msg.message_format = 'string';
|
msg.message_format = 'string';
|
||||||
msg.message = msg.raw_message;
|
msg.message = msg.raw_message;
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
async importArrayTostringMsg(originMsg: OB11Message) {
|
async importArrayTostringMsg(originMsg: OB11Message) {
|
||||||
let msg = structuredClone(originMsg);
|
const msg = structuredClone(originMsg);
|
||||||
msg.message_format = 'string';
|
msg.message_format = 'string';
|
||||||
msg.message = msg.raw_message;
|
msg.message = msg.raw_message;
|
||||||
return msg;
|
return msg;
|
||||||
@@ -907,7 +906,7 @@ export class OneBotMsgApi {
|
|||||||
timeout += PredictTime;// 10S Basic Timeout + PredictTime( For File 512kb/s )
|
timeout += PredictTime;// 10S Basic Timeout + PredictTime( For File 512kb/s )
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('发送消息计算预计时间异常', e);
|
this.core.context.logger.logError('发送消息计算预计时间异常', e);
|
||||||
}
|
}
|
||||||
const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
|
const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
|
||||||
if (!returnMsg) throw new Error('发送消息失败');
|
if (!returnMsg) throw new Error('发送消息失败');
|
||||||
@@ -921,10 +920,10 @@ export class OneBotMsgApi {
|
|||||||
deleteAfterSentFiles.forEach(file => {
|
deleteAfterSentFiles.forEach(file => {
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(file)) {
|
if (fs.existsSync(file)) {
|
||||||
fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', e));
|
fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError('发送消息删除文件失败', e));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', (error as Error).message)
|
this.core.context.logger.logError('发送消息删除文件失败', (error as Error).message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 60000);
|
}, 60000);
|
||||||
@@ -938,7 +937,7 @@ export class OneBotMsgApi {
|
|||||||
) {
|
) {
|
||||||
const realUri = inputdata.url || inputdata.file || inputdata.path || '';
|
const realUri = inputdata.url || inputdata.file || inputdata.path || '';
|
||||||
if (realUri.length === 0) {
|
if (realUri.length === 0) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('文件消息缺少参数', inputdata);
|
this.core.context.logger.logError('文件消息缺少参数', inputdata);
|
||||||
throw Error('文件消息缺少参数');
|
throw Error('文件消息缺少参数');
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
@@ -949,7 +948,7 @@ export class OneBotMsgApi {
|
|||||||
} = (await uri2local(this.core.NapCatTempPath, realUri));
|
} = (await uri2local(this.core.NapCatTempPath, realUri));
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
this.core.context.logger.logError.bind(this.core.context.logger)('文件下载失败', errMsg);
|
this.core.context.logger.logError('文件下载失败', errMsg);
|
||||||
throw Error('文件下载失败' + errMsg);
|
throw Error('文件下载失败' + errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -959,29 +958,20 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
groupChangDecreseType2String(type: number): GroupDecreaseSubType {
|
groupChangDecreseType2String(type: number): GroupDecreaseSubType {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 130:
|
case 130:
|
||||||
return 'kick';
|
return 'leave';
|
||||||
case 131:
|
case 131:
|
||||||
return 'leave';
|
return 'kick';
|
||||||
case 3:
|
case 3:
|
||||||
return 'kick_me';
|
return 'kick_me';
|
||||||
default:
|
default:
|
||||||
return 'kick';
|
return 'kick';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseSysMessage(msg: number[]) {
|
async parseSysMessage(msg: number[]) {
|
||||||
// Todo Refactor
|
// Todo Refactor
|
||||||
// const sysMsg = decodeSysMessage(Uint8Array.from(msg));
|
const SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg));
|
||||||
// if (sysMsg.msgSpec.length === 0) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
|
|
||||||
// if (msgType === 528 && subType === 39 && subSubType === 39) {
|
|
||||||
// if (!sysMsg.bodyWrapper) return;
|
|
||||||
// return await this.obContext.apis.UserApi.parseLikeEvent(sysMsg.bodyWrapper.wrappedBody);
|
|
||||||
// }
|
|
||||||
let SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg));
|
|
||||||
if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) {
|
if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) {
|
||||||
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
|
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
|
||||||
console.log(JSON.stringify(groupChange));
|
console.log(JSON.stringify(groupChange));
|
||||||
@@ -994,15 +984,17 @@ export class OneBotMsgApi {
|
|||||||
);
|
);
|
||||||
} else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) {
|
} else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) {
|
||||||
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
|
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
|
||||||
// console.log(JSON.stringify(groupChange),JSON.stringify(SysMessage));
|
|
||||||
return new OB11GroupDecreaseEvent(
|
return new OB11GroupDecreaseEvent(
|
||||||
this.core,
|
this.core,
|
||||||
groupChange.groupUin,
|
groupChange.groupUin,
|
||||||
+this.core.selfInfo.uin,
|
groupChange.memberUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.memberUid) : 0,
|
||||||
groupChange.operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.operatorUid) : 0,
|
groupChange.operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.operatorUid) : 0,
|
||||||
this.groupChangDecreseType2String(groupChange.decreaseType),
|
this.groupChangDecreseType2String(groupChange.decreaseType),
|
||||||
);
|
);
|
||||||
|
} else if (SysMessage.contentHead.type == 528 && SysMessage.contentHead.subType == 39 && SysMessage.body?.msgContent) {
|
||||||
|
return await this.obContext.apis.UserApi.parseLikeEvent(SysMessage.body?.msgContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (msgType === 732 && subType === 16 && subSubType === 16) {
|
if (msgType === 732 && subType === 16 && subSubType === 16) {
|
||||||
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
|
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import {
|
import type {
|
||||||
NapCatOneBot11Adapter,
|
NapCatOneBot11Adapter,
|
||||||
OB11Message,
|
OB11Message,
|
||||||
OB11MessageAt,
|
OB11MessageAt,
|
||||||
@@ -10,34 +10,35 @@ import {
|
|||||||
QuickActionGroupMessage,
|
QuickActionGroupMessage,
|
||||||
QuickActionGroupRequest,
|
QuickActionGroupRequest,
|
||||||
} from '@/onebot';
|
} from '@/onebot';
|
||||||
import { NTGroupRequestOperateTypes, NapCatCore, Peer } from '@/core';
|
import { NTGroupRequestOperateTypes, type NapCatCore, type Peer } from '@/core';
|
||||||
import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
|
import type { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
|
||||||
import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
|
import type { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
|
||||||
|
|
||||||
import { ContextMode, createContext, normalize } from '@/onebot/action/msg/SendMsg';
|
import { ContextMode, createContext, normalize } from '@/onebot/action/msg/SendMsg';
|
||||||
import { isNull } from '@/common/helper';
|
import { isNull } from '@/common/helper';
|
||||||
|
|
||||||
export class OneBotQuickActionApi {
|
export class OneBotQuickActionApi {
|
||||||
constructor(
|
private obContext: NapCatOneBot11Adapter;
|
||||||
public obContext: NapCatOneBot11Adapter,
|
private core: NapCatCore;
|
||||||
public core: NapCatCore,
|
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
|
||||||
) {
|
this.obContext = obContext;
|
||||||
|
this.core = core;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleQuickOperation(eventContext: QuickActionEvent, quickAction: QuickAction) {
|
async handleQuickOperation(eventContext: QuickActionEvent, quickAction: QuickAction) {
|
||||||
const logger = this.core.context.logger;
|
|
||||||
if (eventContext.post_type === 'message') {
|
if (eventContext.post_type === 'message') {
|
||||||
await this.handleMsg(eventContext as OB11Message, quickAction)
|
await this.handleMsg(eventContext as OB11Message, quickAction)
|
||||||
.catch(logger.logError.bind(logger));
|
.catch(e => this.core.context.logger.logError(e));
|
||||||
}
|
}
|
||||||
if (eventContext.post_type === 'request') {
|
if (eventContext.post_type === 'request') {
|
||||||
const friendRequest = eventContext as OB11FriendRequestEvent;
|
const friendRequest = eventContext as OB11FriendRequestEvent;
|
||||||
const groupRequest = eventContext as OB11GroupRequestEvent;
|
const groupRequest = eventContext as OB11GroupRequestEvent;
|
||||||
if ((friendRequest).request_type === 'friend') {
|
if ((friendRequest).request_type === 'friend') {
|
||||||
await this.handleFriendRequest(friendRequest, quickAction)
|
await this.handleFriendRequest(friendRequest, quickAction)
|
||||||
.catch(logger.logError.bind(logger));
|
.catch(e => this.core.context.logger.logError(e));
|
||||||
} else if (groupRequest.request_type === 'group') {
|
} else if (groupRequest.request_type === 'group') {
|
||||||
await this.handleGroupRequest(groupRequest, quickAction)
|
await this.handleGroupRequest(groupRequest, quickAction)
|
||||||
.catch(logger.logError.bind(logger));
|
.catch(e => this.core.context.logger.logError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ export class OneBotQuickActionApi {
|
|||||||
group_id: msg.group_id?.toString(),
|
group_id: msg.group_id?.toString(),
|
||||||
user_id: msg.user_id?.toString(),
|
user_id: msg.user_id?.toString(),
|
||||||
}, peerContextMode);
|
}, peerContextMode);
|
||||||
|
|
||||||
if (reply) {
|
if (reply) {
|
||||||
// let group: Group | undefined;
|
// let group: Group | undefined;
|
||||||
let replyMessage: OB11MessageData[] = [];
|
let replyMessage: OB11MessageData[] = [];
|
||||||
@@ -78,7 +79,7 @@ export class OneBotQuickActionApi {
|
|||||||
sendElements,
|
sendElements,
|
||||||
deleteAfterSentFiles,
|
deleteAfterSentFiles,
|
||||||
} = await this.obContext.apis.MsgApi.createSendElements(replyMessage, peer);
|
} = await this.obContext.apis.MsgApi.createSendElements(replyMessage, peer);
|
||||||
this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles, false).then().catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles, false).then().catch(e => this.core.context.logger.logError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,13 +89,13 @@ export class OneBotQuickActionApi {
|
|||||||
request.flag,
|
request.flag,
|
||||||
quickAction.approve ? NTGroupRequestOperateTypes.KAGREE : NTGroupRequestOperateTypes.KREFUSE,
|
quickAction.approve ? NTGroupRequestOperateTypes.KAGREE : NTGroupRequestOperateTypes.KREFUSE,
|
||||||
quickAction.reason,
|
quickAction.reason,
|
||||||
).catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
).catch(e => this.core.context.logger.logError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleFriendRequest(request: OB11FriendRequestEvent, quickAction: QuickActionFriendRequest) {
|
async handleFriendRequest(request: OB11FriendRequestEvent, quickAction: QuickActionFriendRequest) {
|
||||||
if (!isNull(quickAction.approve)) {
|
if (!isNull(quickAction.approve)) {
|
||||||
this.core.apis.FriendApi.handleFriendRequest(request.flag, !!quickAction.approve).then().catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
this.core.apis.FriendApi.handleFriendRequest(request.flag, !!quickAction.approve).then().catch(e => this.core.context.logger.logError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ export class OneBotUserApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> {
|
async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> {
|
||||||
const likeTip = decodeProfileLikeTip(Uint8Array.from(wrappedBody));
|
const likeTip = decodeProfileLikeTip(wrappedBody);
|
||||||
if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return;
|
if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return;
|
||||||
this.core.context.logger.logDebug("收到点赞通知消息");
|
this.core.context.logger.logDebug("收到点赞通知消息");
|
||||||
const likeMsg = likeTip.content.msg;
|
const likeMsg = likeTip.content.msg;
|
||||||
|
@@ -58,7 +58,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
readonly context: InstanceContext;
|
readonly context: InstanceContext;
|
||||||
|
|
||||||
configLoader: OB11ConfigLoader;
|
configLoader: OB11ConfigLoader;
|
||||||
apis: StableOneBotApiWrapper;
|
public readonly apis: StableOneBotApiWrapper;
|
||||||
networkManager: OB11NetworkManager;
|
networkManager: OB11NetworkManager;
|
||||||
actions: ActionMap;
|
actions: ActionMap;
|
||||||
private readonly bootTime = Date.now() / 1000;
|
private readonly bootTime = Date.now() / 1000;
|
||||||
@@ -76,7 +76,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
FriendApi: new OneBotFriendApi(this, core),
|
FriendApi: new OneBotFriendApi(this, core),
|
||||||
MsgApi: new OneBotMsgApi(this, core),
|
MsgApi: new OneBotMsgApi(this, core),
|
||||||
QuickActionApi: new OneBotQuickActionApi(this, core),
|
QuickActionApi: new OneBotQuickActionApi(this, core),
|
||||||
};
|
} as const;
|
||||||
this.actions = createActionMap(this, core);
|
this.actions = createActionMap(this, core);
|
||||||
this.networkManager = new OB11NetworkManager();
|
this.networkManager = new OB11NetworkManager();
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
selfInfo.nick = user.nick;
|
selfInfo.nick = user.nick;
|
||||||
this.context.logger.setLogSelfInfo(selfInfo);
|
this.context.logger.setLogSelfInfo(selfInfo);
|
||||||
})
|
})
|
||||||
.catch(this.context.logger.logError.bind(this.context.logger));
|
.catch(e => this.context.logger.logError(e));
|
||||||
|
|
||||||
const serviceInfo = await this.creatOneBotLog(ob11Config);
|
const serviceInfo = await this.creatOneBotLog(ob11Config);
|
||||||
this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`);
|
this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`);
|
||||||
@@ -213,7 +213,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
if (networkChange === OB11NetworkReloadType.NetWorkClose) {
|
if (networkChange === OB11NetworkReloadType.NetWorkClose) {
|
||||||
await this.networkManager.closeSomeAdaterWhenOpen([existingAdapter]);
|
await this.networkManager.closeSomeAdaterWhenOpen([existingAdapter]);
|
||||||
}
|
}
|
||||||
} else if(adapterConfig.enable) {
|
} else if (adapterConfig.enable) {
|
||||||
const newAdapter = new adapterClass(adapterConfig.name, adapterConfig, this.core, this.actions);
|
const newAdapter = new adapterClass(adapterConfig.name, adapterConfig, this.core, this.actions);
|
||||||
await this.networkManager.registerAdapterAndOpen(newAdapter);
|
await this.networkManager.registerAdapterAndOpen(newAdapter);
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
if (event) this.networkManager.emitEvent(event);
|
if (event) this.networkManager.emitEvent(event);
|
||||||
})
|
})
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
this.context.logger.logError.bind(this.context.logger)(
|
this.context.logger.logError(
|
||||||
'constructSysMessage error: ',
|
'constructSysMessage error: ',
|
||||||
e,
|
e,
|
||||||
'\n Parse Hex:',
|
'\n Parse Hex:',
|
||||||
@@ -260,29 +260,36 @@ export class NapCatOneBot11Adapter {
|
|||||||
m.msgId
|
m.msgId
|
||||||
);
|
);
|
||||||
await this.emitMsg(m).catch((e) =>
|
await this.emitMsg(m).catch((e) =>
|
||||||
this.context.logger.logError.bind(this.context.logger)('处理消息失败', e)
|
this.context.logger.logError('处理消息失败', e)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
msgListener.onAddSendMsg = async (msg) => {
|
msgListener.onAddSendMsg = async (msg) => {
|
||||||
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) {
|
try {
|
||||||
await this.core.eventWrapper.registerListen('NodeIKernelMsgListener/onMsgInfoListUpdate', (msgList: RawMessage[]) => {
|
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) {
|
||||||
const report = msgList.find((e) =>
|
const [updatemsgs] = await this.core.eventWrapper.registerListen('NodeIKernelMsgListener/onMsgInfoListUpdate', (msgList: RawMessage[]) => {
|
||||||
e.senderUin == this.core.selfInfo.uin && e.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && e.msgId === msg.msgId
|
const report = msgList.find((e) =>
|
||||||
);
|
e.senderUin == this.core.selfInfo.uin && e.sendStatus !== SendStatusType.KSEND_STATUS_SENDING && e.msgId === msg.msgId
|
||||||
return !!report;
|
);
|
||||||
}, 1, 300);
|
return !!report;
|
||||||
msg.id = MessageUnique.createUniqueMsgId(
|
}, 1, 10 * 60 * 1000);
|
||||||
{
|
// 10分钟 超时
|
||||||
chatType: msg.chatType,
|
const updatemsg = updatemsgs.find((e) => e.msgId === msg.msgId);
|
||||||
peerUid: msg.peerUid,
|
if (updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS || updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS_NOSEQ) {
|
||||||
guildId: '',
|
updatemsg.id = MessageUnique.createUniqueMsgId(
|
||||||
},
|
{
|
||||||
msg.msgId
|
chatType: updatemsg.chatType,
|
||||||
);
|
peerUid: updatemsg.peerUid,
|
||||||
//此时上报的seq不是对的 不过对onebot业务无影响
|
guildId: '',
|
||||||
this.emitMsg(msg);
|
},
|
||||||
|
updatemsg.msgId
|
||||||
|
);
|
||||||
|
this.emitMsg(updatemsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.context.logger.logError('处理发送消息失败', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
msgListener.onMsgRecall = async (chatType: ChatType, uid: string, msgSeq: string) => {
|
msgListener.onMsgRecall = async (chatType: ChatType, uid: string, msgSeq: string) => {
|
||||||
@@ -291,10 +298,10 @@ export class NapCatOneBot11Adapter {
|
|||||||
peerUid: uid,
|
peerUid: uid,
|
||||||
guildId: ''
|
guildId: ''
|
||||||
};
|
};
|
||||||
let msg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, msgSeq)).msgList.find(e => e.msgType == NTMsgType.KMSGTYPEGRAYTIPS);
|
const msg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, msgSeq)).msgList.find(e => e.msgType == NTMsgType.KMSGTYPEGRAYTIPS);
|
||||||
let element = msg?.elements[0];
|
const element = msg?.elements[0];
|
||||||
if (msg && element) {
|
if (msg && element) {
|
||||||
let recallEvent = await this.emitRecallMsg(msg, element);
|
const recallEvent = await this.emitRecallMsg(msg, element);
|
||||||
try {
|
try {
|
||||||
if (recallEvent) {
|
if (recallEvent) {
|
||||||
await this.networkManager.emitEvent(recallEvent);
|
await this.networkManager.emitEvent(recallEvent);
|
||||||
@@ -303,12 +310,12 @@ export class NapCatOneBot11Adapter {
|
|||||||
this.context.logger.logError('处理消息撤回失败', e);
|
this.context.logger.logError('处理消息撤回失败', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
msgListener.onKickedOffLine = async (kick) => {
|
msgListener.onKickedOffLine = async (kick) => {
|
||||||
const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc);
|
const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc);
|
||||||
this.networkManager
|
this.networkManager
|
||||||
.emitEvent(event)
|
.emitEvent(event)
|
||||||
.catch((e) => this.context.logger.logError.bind(this.context.logger)('处理Bot掉线失败', e));
|
.catch((e) => this.context.logger.logError('处理Bot掉线失败', e));
|
||||||
};
|
};
|
||||||
this.context.session.getMsgService().addKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger));
|
this.context.session.getMsgService().addKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger));
|
||||||
}
|
}
|
||||||
@@ -397,7 +404,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
this.networkManager
|
this.networkManager
|
||||||
.emitEvent(groupAdminNoticeEvent)
|
.emitEvent(groupAdminNoticeEvent)
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
this.context.logger.logError.bind(this.context.logger)('处理群管理员变动失败', e)
|
this.context.logger.logError('处理群管理员变动失败', e)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.context.logger.logDebug(
|
this.context.logger.logDebug(
|
||||||
@@ -433,7 +440,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
// this.networkManager
|
// this.networkManager
|
||||||
// .emitEvent(groupDecreaseEvent)
|
// .emitEvent(groupDecreaseEvent)
|
||||||
// .catch((e) =>
|
// .catch((e) =>
|
||||||
// this.context.logger.logError.bind(this.context.logger)('处理群成员退出失败', e)
|
// this.context.logger.logError('处理群成员退出失败', e)
|
||||||
// );
|
// );
|
||||||
// // notify.status == 1 表示未处理 2表示处理完成
|
// // notify.status == 1 表示未处理 2表示处理完成
|
||||||
// } else
|
// } else
|
||||||
@@ -458,10 +465,10 @@ export class NapCatOneBot11Adapter {
|
|||||||
this.networkManager
|
this.networkManager
|
||||||
.emitEvent(groupRequestEvent)
|
.emitEvent(groupRequestEvent)
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
this.context.logger.logError.bind(this.context.logger)('处理加群请求失败', e)
|
this.context.logger.logError('处理加群请求失败', e)
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.context.logger.logError.bind(this.context.logger)(
|
this.context.logger.logError(
|
||||||
'获取加群人QQ号失败 Uid:',
|
'获取加群人QQ号失败 Uid:',
|
||||||
notify.user1.uid,
|
notify.user1.uid,
|
||||||
e
|
e
|
||||||
@@ -483,7 +490,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
this.networkManager
|
this.networkManager
|
||||||
.emitEvent(groupInviteEvent)
|
.emitEvent(groupInviteEvent)
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
this.context.logger.logError.bind(this.context.logger)('处理邀请本人加群失败', e)
|
this.context.logger.logError('处理邀请本人加群失败', e)
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS &&
|
notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS &&
|
||||||
@@ -501,7 +508,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
this.networkManager
|
this.networkManager
|
||||||
.emitEvent(groupInviteEvent)
|
.emitEvent(groupInviteEvent)
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
this.context.logger.logError.bind(this.context.logger)('处理邀请本人加群失败', e)
|
this.context.logger.logError('处理邀请本人加群失败', e)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -526,10 +533,10 @@ export class NapCatOneBot11Adapter {
|
|||||||
this.networkManager
|
this.networkManager
|
||||||
.emitEvent(groupAdminNoticeEvent)
|
.emitEvent(groupAdminNoticeEvent)
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
this.context.logger.logError.bind(this.context.logger)('处理群管理员变动失败', e)
|
this.context.logger.logError('处理群管理员变动失败', e)
|
||||||
);
|
);
|
||||||
existMember.isChangeRole = false;
|
existMember.isChangeRole = false;
|
||||||
this.context.logger.logDebug.bind(this.context.logger)('群管理员变动处理完毕');
|
this.context.logger.logDebug('群管理员变动处理完毕');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -657,7 +664,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
|
|
||||||
private async emitRecallMsg(message: RawMessage, element: MessageElement) {
|
private async emitRecallMsg(message: RawMessage, element: MessageElement) {
|
||||||
const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' };
|
const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' };
|
||||||
let oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId) ?? MessageUnique.createUniqueMsgId(peer, message.msgId);
|
const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId) ?? MessageUnique.createUniqueMsgId(peer, message.msgId);
|
||||||
if (message.chatType == ChatType.KCHATTYPEC2C) {
|
if (message.chatType == ChatType.KCHATTYPEC2C) {
|
||||||
return await this.emitFriendRecallMsg(message, oriMessageId, element);
|
return await this.emitFriendRecallMsg(message, oriMessageId, element);
|
||||||
} else if (message.chatType == ChatType.KCHATTYPEGROUP) {
|
} else if (message.chatType == ChatType.KCHATTYPEGROUP) {
|
||||||
@@ -677,7 +684,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) {
|
private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) {
|
||||||
const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
|
const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
|
||||||
if (!operatorUid) return undefined;
|
if (!operatorUid) return undefined;
|
||||||
let operatorId = message.senderUin ?? await this.core.apis.UserApi.getUinByUidV2(operatorUid);
|
const operatorId = message.senderUin ?? await this.core.apis.UserApi.getUinByUidV2(operatorUid);
|
||||||
return new OB11GroupRecallNoticeEvent(
|
return new OB11GroupRecallNoticeEvent(
|
||||||
this.core,
|
this.core,
|
||||||
+message.peerUin,
|
+message.peerUin,
|
||||||
|
@@ -50,12 +50,12 @@ export class OB11ActiveHttpAdapter implements IOB11NetworkAdapter {
|
|||||||
try {
|
try {
|
||||||
this.obContext.apis.QuickActionApi
|
this.obContext.apis.QuickActionApi
|
||||||
.handleQuickOperation(event as QuickActionEvent, resJson)
|
.handleQuickOperation(event as QuickActionEvent, resJson)
|
||||||
.catch(this.logger.logError.bind(this.logger));
|
.catch(e => this.logger.logError(e));
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [Http Client] 新消息事件HTTP上报返回快速操作失败', e);
|
this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报返回快速操作失败', e);
|
||||||
}
|
}
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [Http Client] 新消息事件HTTP上报失败', e);
|
this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报失败', e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -95,7 +95,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
try {
|
try {
|
||||||
this.connectEvent(this.core);
|
this.connectEvent(this.core);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Client] 发送连接生命周期失败', e);
|
this.logger.logError('[OneBot] [WebSocket Client] 发送连接生命周期失败', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -104,8 +104,8 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
});
|
});
|
||||||
this.connection.once('close', () => {
|
this.connection.once('close', () => {
|
||||||
if (!isClosedByError) {
|
if (!isClosedByError) {
|
||||||
this.logger.logError.bind(this.logger)(`[OneBot] [WebSocket Client] 反向WebSocket (${this.config.url}) 连接意外关闭`);
|
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.config.url}) 连接意外关闭`);
|
||||||
this.logger.logError.bind(this.logger)(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.config.reconnectInterval / 1000)} 秒后尝试重新连接`);
|
this.logger.logError(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.config.reconnectInterval / 1000)} 秒后尝试重新连接`);
|
||||||
if (this.isEnable) {
|
if (this.isEnable) {
|
||||||
this.connection = null;
|
this.connection = null;
|
||||||
setTimeout(() => this.tryConnect(), this.config.reconnectInterval);
|
setTimeout(() => this.tryConnect(), this.config.reconnectInterval);
|
||||||
@@ -114,8 +114,8 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
});
|
});
|
||||||
this.connection.on('error', (err) => {
|
this.connection.on('error', (err) => {
|
||||||
isClosedByError = true;
|
isClosedByError = true;
|
||||||
this.logger.logError.bind(this.logger)(`[OneBot] [WebSocket Client] 反向WebSocket (${this.config.url}) 连接错误`, err);
|
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.config.url}) 连接错误`, err);
|
||||||
this.logger.logError.bind(this.logger)(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.config.reconnectInterval / 1000)} 秒后尝试重新连接`);
|
this.logger.logError(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.config.reconnectInterval / 1000)} 秒后尝试重新连接`);
|
||||||
if (this.isEnable) {
|
if (this.isEnable) {
|
||||||
this.connection = null;
|
this.connection = null;
|
||||||
setTimeout(() => this.tryConnect(), this.config.reconnectInterval);
|
setTimeout(() => this.tryConnect(), this.config.reconnectInterval);
|
||||||
@@ -128,7 +128,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
try {
|
try {
|
||||||
this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT));
|
this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Client] 发送生命周期失败', e);
|
this.logger.logError('[OneBot] [WebSocket Client] 发送生命周期失败', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
receiveData.params = (receiveData?.params) ? receiveData.params : {};// 兼容类型验证
|
receiveData.params = (receiveData?.params) ? receiveData.params : {};// 兼容类型验证
|
||||||
const action = this.actions.get(receiveData.action);
|
const action = this.actions.get(receiveData.action);
|
||||||
if (!action) {
|
if (!action) {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Client] 发生错误', '不支持的Api ' + receiveData.action);
|
this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的Api ' + receiveData.action);
|
||||||
this.checkStateAndReply<any>(OB11Response.error('不支持的Api ' + receiveData.action, 1404, echo));
|
this.checkStateAndReply<any>(OB11Response.error('不支持的Api ' + receiveData.action, 1404, echo));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -102,7 +102,7 @@ export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter {
|
|||||||
if (req.path === '' || req.path === '/') {
|
if (req.path === '' || req.path === '/') {
|
||||||
const hello = OB11Response.ok({});
|
const hello = OB11Response.ok({});
|
||||||
hello.message = 'NapCat4 Ss Running';
|
hello.message = 'NapCat4 Ss Running';
|
||||||
return res.json(hello)
|
return res.json(hello);
|
||||||
}
|
}
|
||||||
const actionName = req.path.split('/')[1];
|
const actionName = req.path.split('/')[1];
|
||||||
const action = this.actions.get(actionName);
|
const action = this.actions.get(actionName);
|
||||||
|
@@ -51,7 +51,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
|
|
||||||
wsClient.on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Client Error:', err.message));
|
wsClient.on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Client Error:', err.message));
|
||||||
wsClient.on('message', (message) => {
|
wsClient.on('message', (message) => {
|
||||||
this.handleMessage(wsClient, message).then().catch(this.logger.logError.bind(this.logger));
|
this.handleMessage(wsClient, message).then().catch(e => this.logger.logError(e));
|
||||||
});
|
});
|
||||||
wsClient.on('ping', () => {
|
wsClient.on('ping', () => {
|
||||||
wsClient.pong();
|
wsClient.pong();
|
||||||
@@ -85,7 +85,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
try {
|
try {
|
||||||
this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT), wsClient);
|
this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT), wsClient);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Server] 发送生命周期失败', e);
|
this.logger.logError('[OneBot] [WebSocket Server] 发送生命周期失败', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
|
|
||||||
open() {
|
open() {
|
||||||
if (this.isEnable) {
|
if (this.isEnable) {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Server] Cannot open a opened WebSocket server');
|
this.logger.logError('[OneBot] [WebSocket Server] Cannot open a opened WebSocket server');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const addressInfo = this.wsServer.address();
|
const addressInfo = this.wsServer.address();
|
||||||
@@ -116,9 +116,9 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
this.isEnable = false;
|
this.isEnable = false;
|
||||||
this.wsServer.close((err) => {
|
this.wsServer.close((err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Server] Error closing server:', err.message);
|
this.logger.logError('[OneBot] [WebSocket Server] Error closing server:', err.message);
|
||||||
} else {
|
} else {
|
||||||
this.logger.log.bind(this.logger)('[OneBot] [WebSocket Server] Server Closed');
|
this.logger.log('[OneBot] [WebSocket Server] Server Closed');
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -179,7 +179,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 不然类型校验爆炸
|
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 不然类型校验爆炸
|
||||||
const action = this.actions.get(receiveData.action);
|
const action = this.actions.get(receiveData.action);
|
||||||
if (!action) {
|
if (!action) {
|
||||||
this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Client] 发生错误', '不支持的API ' + receiveData.action);
|
this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的API ' + receiveData.action);
|
||||||
this.checkStateAndReply<any>(OB11Response.error('不支持的API ' + receiveData.action, 1404, echo), wsClient);
|
this.checkStateAndReply<any>(OB11Response.error('不支持的API ' + receiveData.action, 1404, echo), wsClient);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
366
src/shell/base.ts
Normal file
366
src/shell/base.ts
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
import type { SelfInfo } from '@/core/types';
|
||||||
|
|
||||||
|
import { LogWrapper } from '@/common/log';
|
||||||
|
import { NodeIKernelLoginListener, NodeIKernelSessionListener } from '@/core/listeners';
|
||||||
|
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '@/core/adapters';
|
||||||
|
import { NapCatPathWrapper } from '@/common/path';
|
||||||
|
import {
|
||||||
|
genSessionConfig,
|
||||||
|
InstanceContext,
|
||||||
|
loadQQWrapper,
|
||||||
|
NapCatCore,
|
||||||
|
NapCatCoreWorkingEnv,
|
||||||
|
NodeIQQNTWrapperSession,
|
||||||
|
PlatformType,
|
||||||
|
WrapperNodeApi,
|
||||||
|
} from '@/core';
|
||||||
|
import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
|
||||||
|
import { hostname, systemVersion } from '@/common/system';
|
||||||
|
import { proxiedListenerOf } from '@/common/proxy-handler';
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import { NodeIKernelLoginService } from '@/core/services';
|
||||||
|
import { program } from 'commander';
|
||||||
|
import qrcode from 'qrcode-terminal';
|
||||||
|
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||||
|
import { InitWebUi } from '@/webui';
|
||||||
|
import { WebUiDataRuntime } from '@/webui/src/helper/Data';
|
||||||
|
import { napCatVersion } from '@/common/version';
|
||||||
|
import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener';
|
||||||
|
// NapCat Shell App ES 入口文件
|
||||||
|
async function handleUncaughtExceptions(logger: LogWrapper) {
|
||||||
|
process.on('uncaughtException', (err) => {
|
||||||
|
logger.logError('[NapCat] [Error] Unhandled Exception:', err.message);
|
||||||
|
});
|
||||||
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
|
logger.logError('[NapCat] [Error] unhandledRejection:', reason);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDataPaths(wrapper: WrapperNodeApi): [string, string] {
|
||||||
|
if (os.platform() === 'darwin') {
|
||||||
|
const userPath = os.homedir();
|
||||||
|
const appDataPath = path.resolve(userPath, './Library/Application Support/QQ');
|
||||||
|
return [appDataPath, path.join(appDataPath, 'global')];
|
||||||
|
}
|
||||||
|
let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
|
||||||
|
if (!dataPath) {
|
||||||
|
dataPath = path.resolve(os.homedir(), './.config/QQ');
|
||||||
|
fs.mkdirSync(dataPath, { recursive: true });
|
||||||
|
}
|
||||||
|
const dataPathGlobal = path.resolve(dataPath, './nt_qq/global');
|
||||||
|
return [dataPath, dataPathGlobal];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlatformType(): PlatformType {
|
||||||
|
const platformMapping: Partial<Record<NodeJS.Platform, PlatformType>> = {
|
||||||
|
win32: PlatformType.KWINDOWS,
|
||||||
|
darwin: PlatformType.KMAC,
|
||||||
|
linux: PlatformType.KLINUX,
|
||||||
|
};
|
||||||
|
return platformMapping[os.platform()] ?? PlatformType.KWINDOWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initializeEngine(
|
||||||
|
engine: any,
|
||||||
|
basicInfoWrapper: QQBasicInfoWrapper,
|
||||||
|
dataPathGlobal: string,
|
||||||
|
systemPlatform: PlatformType,
|
||||||
|
systemVersion: string
|
||||||
|
) {
|
||||||
|
engine.initWithDeskTopConfig(
|
||||||
|
{
|
||||||
|
base_path_prefix: '',
|
||||||
|
platform_type: systemPlatform,
|
||||||
|
app_type: 4,
|
||||||
|
app_version: basicInfoWrapper.getFullQQVesion(),
|
||||||
|
os_version: systemVersion,
|
||||||
|
use_xlog: false,
|
||||||
|
qua: basicInfoWrapper.QQVersionQua,
|
||||||
|
global_path_config: {
|
||||||
|
desktopGlobalPath: dataPathGlobal,
|
||||||
|
},
|
||||||
|
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },
|
||||||
|
},
|
||||||
|
new NodeIGlobalAdapter(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initializeLoginService(
|
||||||
|
loginService: NodeIKernelLoginService,
|
||||||
|
basicInfoWrapper: QQBasicInfoWrapper,
|
||||||
|
dataPathGlobal: string,
|
||||||
|
systemVersion: string,
|
||||||
|
hostname: string
|
||||||
|
) {
|
||||||
|
loginService.initConfig({
|
||||||
|
machineId: '',
|
||||||
|
appid: basicInfoWrapper.QQVersionAppid ?? '',
|
||||||
|
platVer: systemVersion,
|
||||||
|
commonPath: dataPathGlobal,
|
||||||
|
clientVer: basicInfoWrapper.getFullQQVesion(),
|
||||||
|
hostName: hostname,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleLogin(
|
||||||
|
loginService: NodeIKernelLoginService,
|
||||||
|
logger: LogWrapper,
|
||||||
|
pathWrapper: NapCatPathWrapper,
|
||||||
|
quickLoginUin: string | undefined,
|
||||||
|
historyLoginList: any[]
|
||||||
|
): Promise<SelfInfo> {
|
||||||
|
return new Promise<SelfInfo>((resolve) => {
|
||||||
|
const loginListener = new NodeIKernelLoginListener();
|
||||||
|
let isLogined = false;
|
||||||
|
|
||||||
|
loginListener.onUserLoggedIn = (userid: string) => {
|
||||||
|
logger.logError(`当前账号(${userid})已登录,无法重复登录`);
|
||||||
|
};
|
||||||
|
|
||||||
|
loginListener.onQRCodeLoginSucceed = async (loginResult) => {
|
||||||
|
isLogined = true;
|
||||||
|
resolve({
|
||||||
|
uid: loginResult.uid,
|
||||||
|
uin: loginResult.uin,
|
||||||
|
nick: '',
|
||||||
|
online: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {
|
||||||
|
WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl);
|
||||||
|
|
||||||
|
const realBase64 = pngBase64QrcodeData.replace(/^data:image\/\w+;base64,/, '');
|
||||||
|
const buffer = Buffer.from(realBase64, 'base64');
|
||||||
|
logger.logWarn('请扫描下面的二维码,然后在手Q上授权登录:');
|
||||||
|
const qrcodePath = path.join(pathWrapper.cachePath, 'qrcode.png');
|
||||||
|
qrcode.generate(qrcodeUrl, { small: true }, (res) => {
|
||||||
|
logger.logWarn([
|
||||||
|
'\n',
|
||||||
|
res,
|
||||||
|
'二维码解码URL: ' + qrcodeUrl,
|
||||||
|
'如果控制台二维码无法扫码,可以复制解码url到二维码生成网站生成二维码再扫码,也可以打开下方的二维码路径图片进行扫码。',
|
||||||
|
].join('\n'));
|
||||||
|
fs.writeFile(qrcodePath, buffer, {}, () => {
|
||||||
|
logger.logWarn('二维码已保存到', qrcodePath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
|
||||||
|
if (!isLogined) {
|
||||||
|
logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg);
|
||||||
|
if (errType == 1 && errCode == 3) {
|
||||||
|
// 二维码过期刷新
|
||||||
|
}
|
||||||
|
loginService.getQRCodePicture();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loginListener.onLoginFailed = (args) => {
|
||||||
|
logger.logError('[Core] [Login] Login Error , ErrInfo: ', args);
|
||||||
|
};
|
||||||
|
|
||||||
|
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger));
|
||||||
|
const isConnect = loginService.connect();
|
||||||
|
if (!isConnect) {
|
||||||
|
logger.logError('核心登录服务连接失败!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('核心登录服务连接成功!');
|
||||||
|
|
||||||
|
loginService.getLoginList().then((res) => {
|
||||||
|
// 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList
|
||||||
|
WebUiDataRuntime.setQQQuickLoginList(res.LocalLoginInfoList.filter((item) => item.isQuickLogin).map((item) => item.uin.toString()));
|
||||||
|
});
|
||||||
|
|
||||||
|
WebUiDataRuntime.setQuickLoginCall(async (uin: string) => {
|
||||||
|
return await new Promise((resolve) => {
|
||||||
|
if (uin) {
|
||||||
|
logger.log('正在快速登录 ', uin);
|
||||||
|
loginService.quickLoginWithUin(uin).then(res => {
|
||||||
|
if (res.loginErrorInfo.errMsg) {
|
||||||
|
resolve({ result: false, message: res.loginErrorInfo.errMsg });
|
||||||
|
}
|
||||||
|
resolve({ result: true, message: '' });
|
||||||
|
}).catch((e) => {
|
||||||
|
logger.logError(e);
|
||||||
|
resolve({ result: false, message: '快速登录发生错误' });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve({ result: false, message: '快速登录失败' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (quickLoginUin) {
|
||||||
|
if (historyLoginList.some(u => u.uin === quickLoginUin)) {
|
||||||
|
logger.log('正在快速登录 ', quickLoginUin);
|
||||||
|
setTimeout(() => {
|
||||||
|
loginService.quickLoginWithUin(quickLoginUin)
|
||||||
|
.then(result => {
|
||||||
|
if (result.loginErrorInfo.errMsg) {
|
||||||
|
logger.logError('快速登录错误:', result.loginErrorInfo.errMsg);
|
||||||
|
if (!isLogined) loginService.getQRCodePicture();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch();
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式');
|
||||||
|
if (!isLogined) loginService.getQRCodePicture();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式');
|
||||||
|
if (historyLoginList.length > 0) {
|
||||||
|
logger.log(`可用于快速登录的 QQ:\n${historyLoginList
|
||||||
|
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
|
||||||
|
.join('\n')
|
||||||
|
}`);
|
||||||
|
}
|
||||||
|
loginService.getQRCodePicture();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initializeSession(
|
||||||
|
session: NodeIQQNTWrapperSession,
|
||||||
|
sessionConfig: any,
|
||||||
|
logger: LogWrapper
|
||||||
|
) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
const sessionListener = new NodeIKernelSessionListener();
|
||||||
|
sessionListener.onSessionInitComplete = (r: unknown) => {
|
||||||
|
if (r === 0) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(new Error('登录异常' + r?.toString()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
session.init(
|
||||||
|
sessionConfig,
|
||||||
|
new NodeIDependsAdapter(),
|
||||||
|
new NodeIDispatcherAdapter(),
|
||||||
|
sessionListener,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
session.startNT(0);
|
||||||
|
} catch (_) {
|
||||||
|
try {
|
||||||
|
session.startNT();
|
||||||
|
} catch (e: unknown) {
|
||||||
|
reject(new Error('init failed ' + (e as Error).message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function NCoreInitShell() {
|
||||||
|
console.log('NapCat Shell App Loading...');
|
||||||
|
const pathWrapper = new NapCatPathWrapper();
|
||||||
|
const logger = new LogWrapper(pathWrapper.logsPath);
|
||||||
|
handleUncaughtExceptions(logger);
|
||||||
|
|
||||||
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
|
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
|
||||||
|
|
||||||
|
const o3Service = wrapper.NodeIO3MiscService.get();
|
||||||
|
o3Service.addO3MiscListener(new NodeIO3MiscListener());
|
||||||
|
|
||||||
|
logger.log(`[NapCat] [Core] NapCat.Core Version: ` + napCatVersion);
|
||||||
|
InitWebUi(logger, pathWrapper).then().catch(e => logger.logError(e));
|
||||||
|
|
||||||
|
const engine = wrapper.NodeIQQNTWrapperEngine.get();
|
||||||
|
const loginService = wrapper.NodeIKernelLoginService.get();
|
||||||
|
const session = wrapper.NodeIQQNTWrapperSession.create();
|
||||||
|
|
||||||
|
const [dataPath, dataPathGlobal] = getDataPaths(wrapper);
|
||||||
|
const systemPlatform = getPlatformType();
|
||||||
|
|
||||||
|
if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined');
|
||||||
|
|
||||||
|
await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion);
|
||||||
|
await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
|
||||||
|
|
||||||
|
program.option('-q, --qq [number]', 'QQ号').parse(process.argv);
|
||||||
|
const cmdOptions = program.opts();
|
||||||
|
const quickLoginUin = cmdOptions.qq;
|
||||||
|
const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList;
|
||||||
|
|
||||||
|
const dataTimestape = new Date().getTime().toString();
|
||||||
|
o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']);
|
||||||
|
|
||||||
|
const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList);
|
||||||
|
|
||||||
|
const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
|
||||||
|
o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));
|
||||||
|
|
||||||
|
let guid = loginService.getMachineGuid();
|
||||||
|
guid = guid.slice(0, 8) + '-' + guid.slice(8, 12) + '-' + guid.slice(12, 16) + '-' + guid.slice(16, 20) + '-' + guid.slice(20);
|
||||||
|
o3Service.reportAmgomWeather('login', 'a6', [dataTimestape, '184', '329']);
|
||||||
|
|
||||||
|
const sessionConfig = await genSessionConfig(
|
||||||
|
guid,
|
||||||
|
basicInfoWrapper.QQVersionAppid,
|
||||||
|
basicInfoWrapper.getFullQQVesion(),
|
||||||
|
selfInfo.uin,
|
||||||
|
selfInfo.uid,
|
||||||
|
dataPath,
|
||||||
|
);
|
||||||
|
|
||||||
|
await initializeSession(session, sessionConfig, logger);
|
||||||
|
|
||||||
|
const accountDataPath = path.resolve(dataPath, './NapCat/data');
|
||||||
|
fs.mkdirSync(dataPath, { recursive: true });
|
||||||
|
logger.logDebug('本账号数据/缓存目录:', accountDataPath);
|
||||||
|
|
||||||
|
await new NapCatShell(
|
||||||
|
wrapper,
|
||||||
|
session,
|
||||||
|
logger,
|
||||||
|
loginService,
|
||||||
|
selfInfo,
|
||||||
|
basicInfoWrapper,
|
||||||
|
pathWrapper,
|
||||||
|
).InitNapCat();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class NapCatShell {
|
||||||
|
readonly core: NapCatCore;
|
||||||
|
readonly context: InstanceContext;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
wrapper: WrapperNodeApi,
|
||||||
|
session: NodeIQQNTWrapperSession,
|
||||||
|
logger: LogWrapper,
|
||||||
|
loginService: NodeIKernelLoginService,
|
||||||
|
selfInfo: SelfInfo,
|
||||||
|
basicInfoWrapper: QQBasicInfoWrapper,
|
||||||
|
pathWrapper: NapCatPathWrapper,
|
||||||
|
) {
|
||||||
|
this.context = {
|
||||||
|
workingEnv: NapCatCoreWorkingEnv.Shell,
|
||||||
|
wrapper,
|
||||||
|
session,
|
||||||
|
logger,
|
||||||
|
loginService,
|
||||||
|
basicInfoWrapper,
|
||||||
|
pathWrapper,
|
||||||
|
};
|
||||||
|
this.core = new NapCatCore(this.context, selfInfo);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
async InitNapCat() {
|
||||||
|
await this.core.initCore();
|
||||||
|
new NapCatOneBot11Adapter(this.core, this.context, this.context.pathWrapper).InitOneBot()
|
||||||
|
.catch(e => this.context.logger.logError('初始化OneBot失败', e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -1,369 +1,3 @@
|
|||||||
import type { SelfInfo } from '@/core/types';
|
import { NCoreInitShell } from "./base";
|
||||||
|
|
||||||
import { LogWrapper } from '@/common/log';
|
|
||||||
import { NodeIKernelLoginListener, NodeIKernelSessionListener } from '@/core/listeners';
|
|
||||||
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '@/core/adapters';
|
|
||||||
import { NapCatPathWrapper } from '@/common/path';
|
|
||||||
import {
|
|
||||||
genSessionConfig,
|
|
||||||
InstanceContext,
|
|
||||||
loadQQWrapper,
|
|
||||||
NapCatCore,
|
|
||||||
NapCatCoreWorkingEnv,
|
|
||||||
NodeIQQNTWrapperSession,
|
|
||||||
PlatformType,
|
|
||||||
WrapperNodeApi,
|
|
||||||
} from '@/core';
|
|
||||||
import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
|
|
||||||
import { hostname, systemVersion } from '@/common/system';
|
|
||||||
import { proxiedListenerOf } from '@/common/proxy-handler';
|
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import os from 'os';
|
|
||||||
import { NodeIKernelLoginService } from '@/core/services';
|
|
||||||
import { program } from 'commander';
|
|
||||||
import qrcode from 'qrcode-terminal';
|
|
||||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
|
||||||
import { InitWebUi } from '@/webui';
|
|
||||||
import { WebUiDataRuntime } from '@/webui/src/helper/Data';
|
|
||||||
import { napCatVersion } from '@/common/version';
|
|
||||||
import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener';
|
|
||||||
|
|
||||||
program.option('-q, --qq [number]', 'QQ号').parse(process.argv);
|
|
||||||
const cmdOptions = program.opts();
|
|
||||||
|
|
||||||
// NapCat Shell App ES 入口文件
|
|
||||||
async function handleUncaughtExceptions(logger: LogWrapper) {
|
|
||||||
process.on('uncaughtException', (err) => {
|
|
||||||
logger.logError('[NapCat] [Error] Unhandled Exception:', err.message);
|
|
||||||
});
|
|
||||||
process.on('unhandledRejection', (reason, promise) => {
|
|
||||||
logger.logError('[NapCat] [Error] unhandledRejection:', reason);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDataPaths(wrapper: WrapperNodeApi): [string, string] {
|
|
||||||
if (os.platform() === 'darwin') {
|
|
||||||
const userPath = os.homedir();
|
|
||||||
const appDataPath = path.resolve(userPath, './Library/Application Support/QQ');
|
|
||||||
return [appDataPath, path.join(appDataPath, 'global')];
|
|
||||||
}
|
|
||||||
let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
|
|
||||||
if (!dataPath) {
|
|
||||||
dataPath = path.resolve(os.homedir(), './.config/QQ');
|
|
||||||
fs.mkdirSync(dataPath, { recursive: true });
|
|
||||||
}
|
|
||||||
const dataPathGlobal = path.resolve(dataPath, './nt_qq/global');
|
|
||||||
return [dataPath, dataPathGlobal];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPlatformType(): PlatformType {
|
|
||||||
const platformMapping: Partial<Record<NodeJS.Platform, PlatformType>> = {
|
|
||||||
win32: PlatformType.KWINDOWS,
|
|
||||||
darwin: PlatformType.KMAC,
|
|
||||||
linux: PlatformType.KLINUX,
|
|
||||||
};
|
|
||||||
return platformMapping[os.platform()] ?? PlatformType.KWINDOWS;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initializeEngine(
|
|
||||||
engine: any,
|
|
||||||
basicInfoWrapper: QQBasicInfoWrapper,
|
|
||||||
dataPathGlobal: string,
|
|
||||||
systemPlatform: PlatformType,
|
|
||||||
systemVersion: string
|
|
||||||
) {
|
|
||||||
engine.initWithDeskTopConfig(
|
|
||||||
{
|
|
||||||
base_path_prefix: '',
|
|
||||||
platform_type: systemPlatform,
|
|
||||||
app_type: 4,
|
|
||||||
app_version: basicInfoWrapper.getFullQQVesion(),
|
|
||||||
os_version: systemVersion,
|
|
||||||
use_xlog: false,
|
|
||||||
qua: basicInfoWrapper.QQVersionQua,
|
|
||||||
global_path_config: {
|
|
||||||
desktopGlobalPath: dataPathGlobal,
|
|
||||||
},
|
|
||||||
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },
|
|
||||||
},
|
|
||||||
new NodeIGlobalAdapter(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initializeLoginService(
|
|
||||||
loginService: NodeIKernelLoginService,
|
|
||||||
basicInfoWrapper: QQBasicInfoWrapper,
|
|
||||||
dataPathGlobal: string,
|
|
||||||
systemVersion: string,
|
|
||||||
hostname: string
|
|
||||||
) {
|
|
||||||
loginService.initConfig({
|
|
||||||
machineId: '',
|
|
||||||
appid: basicInfoWrapper.QQVersionAppid ?? '',
|
|
||||||
platVer: systemVersion,
|
|
||||||
commonPath: dataPathGlobal,
|
|
||||||
clientVer: basicInfoWrapper.getFullQQVesion(),
|
|
||||||
hostName: hostname,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleLogin(
|
|
||||||
loginService: NodeIKernelLoginService,
|
|
||||||
logger: LogWrapper,
|
|
||||||
pathWrapper: NapCatPathWrapper,
|
|
||||||
quickLoginUin: string | undefined,
|
|
||||||
historyLoginList: any[]
|
|
||||||
): Promise<SelfInfo> {
|
|
||||||
return new Promise<SelfInfo>((resolve) => {
|
|
||||||
const loginListener = new NodeIKernelLoginListener();
|
|
||||||
let isLogined = false;
|
|
||||||
|
|
||||||
loginListener.onUserLoggedIn = (userid: string) => {
|
|
||||||
logger.logError(`当前账号(${userid})已登录,无法重复登录`);
|
|
||||||
};
|
|
||||||
|
|
||||||
loginListener.onQRCodeLoginSucceed = async (loginResult) => {
|
|
||||||
isLogined = true;
|
|
||||||
resolve({
|
|
||||||
uid: loginResult.uid,
|
|
||||||
uin: loginResult.uin,
|
|
||||||
nick: '',
|
|
||||||
online: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {
|
|
||||||
WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl);
|
|
||||||
|
|
||||||
const realBase64 = pngBase64QrcodeData.replace(/^data:image\/\w+;base64,/, '');
|
|
||||||
const buffer = Buffer.from(realBase64, 'base64');
|
|
||||||
logger.logWarn('请扫描下面的二维码,然后在手Q上授权登录:');
|
|
||||||
const qrcodePath = path.join(pathWrapper.cachePath, 'qrcode.png');
|
|
||||||
qrcode.generate(qrcodeUrl, { small: true }, (res) => {
|
|
||||||
logger.logWarn([
|
|
||||||
'\n',
|
|
||||||
res,
|
|
||||||
'二维码解码URL: ' + qrcodeUrl,
|
|
||||||
'如果控制台二维码无法扫码,可以复制解码url到二维码生成网站生成二维码再扫码,也可以打开下方的二维码路径图片进行扫码。',
|
|
||||||
].join('\n'));
|
|
||||||
fs.writeFile(qrcodePath, buffer, {}, () => {
|
|
||||||
logger.logWarn('二维码已保存到', qrcodePath);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
|
|
||||||
if (!isLogined) {
|
|
||||||
logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg);
|
|
||||||
if (errType == 1 && errCode == 3) {
|
|
||||||
// 二维码过期刷新
|
|
||||||
}
|
|
||||||
loginService.getQRCodePicture();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loginListener.onLoginFailed = (args) => {
|
|
||||||
logger.logError('[Core] [Login] Login Error , ErrInfo: ', args);
|
|
||||||
};
|
|
||||||
|
|
||||||
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger));
|
|
||||||
const isConnect = loginService.connect();
|
|
||||||
if (!isConnect) {
|
|
||||||
logger.logError('核心登录服务连接失败!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log('核心登录服务连接成功!');
|
|
||||||
|
|
||||||
loginService.getLoginList().then((res) => {
|
|
||||||
// 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList
|
|
||||||
WebUiDataRuntime.setQQQuickLoginList(res.LocalLoginInfoList.filter((item) => item.isQuickLogin).map((item) => item.uin.toString()));
|
|
||||||
});
|
|
||||||
|
|
||||||
WebUiDataRuntime.setQuickLoginCall(async (uin: string) => {
|
|
||||||
return await new Promise((resolve) => {
|
|
||||||
if (uin) {
|
|
||||||
logger.log('正在快速登录 ', uin);
|
|
||||||
loginService.quickLoginWithUin(uin).then(res => {
|
|
||||||
if (res.loginErrorInfo.errMsg) {
|
|
||||||
resolve({ result: false, message: res.loginErrorInfo.errMsg });
|
|
||||||
}
|
|
||||||
resolve({ result: true, message: '' });
|
|
||||||
}).catch((e) => {
|
|
||||||
logger.logError(e);
|
|
||||||
resolve({ result: false, message: '快速登录发生错误' });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resolve({ result: false, message: '快速登录失败' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (quickLoginUin) {
|
|
||||||
if (historyLoginList.some(u => u.uin === quickLoginUin)) {
|
|
||||||
logger.log('正在快速登录 ', quickLoginUin);
|
|
||||||
setTimeout(() => {
|
|
||||||
loginService.quickLoginWithUin(quickLoginUin)
|
|
||||||
.then(result => {
|
|
||||||
if (result.loginErrorInfo.errMsg) {
|
|
||||||
logger.logError('快速登录错误:', result.loginErrorInfo.errMsg);
|
|
||||||
if (!isLogined) loginService.getQRCodePicture();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch();
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式');
|
|
||||||
if (!isLogined) loginService.getQRCodePicture();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式');
|
|
||||||
if (historyLoginList.length > 0) {
|
|
||||||
logger.log(`可用于快速登录的 QQ:\n${historyLoginList
|
|
||||||
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
|
|
||||||
.join('\n')
|
|
||||||
}`);
|
|
||||||
}
|
|
||||||
loginService.getQRCodePicture();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initializeSession(
|
|
||||||
session: NodeIQQNTWrapperSession,
|
|
||||||
sessionConfig: any,
|
|
||||||
logger: LogWrapper
|
|
||||||
) {
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
const sessionListener = new NodeIKernelSessionListener();
|
|
||||||
sessionListener.onSessionInitComplete = (r: unknown) => {
|
|
||||||
if (r === 0) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject(new Error('登录异常' + r?.toString()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
session.init(
|
|
||||||
sessionConfig,
|
|
||||||
new NodeIDependsAdapter(),
|
|
||||||
new NodeIDispatcherAdapter(),
|
|
||||||
sessionListener,
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
session.startNT(0);
|
|
||||||
} catch (_) {
|
|
||||||
try {
|
|
||||||
session.startNT();
|
|
||||||
} catch (e: unknown) {
|
|
||||||
reject(new Error('init failed ' + (e as Error).message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function NCoreInitShell() {
|
|
||||||
console.log('NapCat Shell App Loading...');
|
|
||||||
const pathWrapper = new NapCatPathWrapper();
|
|
||||||
const logger = new LogWrapper(pathWrapper.logsPath);
|
|
||||||
handleUncaughtExceptions(logger);
|
|
||||||
|
|
||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
|
||||||
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
|
|
||||||
|
|
||||||
const o3Service = wrapper.NodeIO3MiscService.get();
|
|
||||||
o3Service.addO3MiscListener(new NodeIO3MiscListener());
|
|
||||||
|
|
||||||
logger.log(`[NapCat] [Core] NapCat.Core Version: ` + napCatVersion);
|
|
||||||
InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger));
|
|
||||||
|
|
||||||
const engine = wrapper.NodeIQQNTWrapperEngine.get();
|
|
||||||
const loginService = wrapper.NodeIKernelLoginService.get();
|
|
||||||
const session = wrapper.NodeIQQNTWrapperSession.create();
|
|
||||||
|
|
||||||
const [dataPath, dataPathGlobal] = getDataPaths(wrapper);
|
|
||||||
const systemPlatform = getPlatformType();
|
|
||||||
|
|
||||||
if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined');
|
|
||||||
|
|
||||||
await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion);
|
|
||||||
await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
|
|
||||||
|
|
||||||
const quickLoginUin = cmdOptions.qq;
|
|
||||||
const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList;
|
|
||||||
|
|
||||||
const dataTimestape = new Date().getTime().toString();
|
|
||||||
o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']);
|
|
||||||
|
|
||||||
const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList);
|
|
||||||
|
|
||||||
const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
|
|
||||||
o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));
|
|
||||||
|
|
||||||
let guid = loginService.getMachineGuid();
|
|
||||||
guid = guid.slice(0, 8) + '-' + guid.slice(8, 12) + '-' + guid.slice(12, 16) + '-' + guid.slice(16, 20) + '-' + guid.slice(20);
|
|
||||||
o3Service.reportAmgomWeather('login', 'a6', [dataTimestape, '184', '329']);
|
|
||||||
|
|
||||||
const sessionConfig = await genSessionConfig(
|
|
||||||
guid,
|
|
||||||
basicInfoWrapper.QQVersionAppid,
|
|
||||||
basicInfoWrapper.getFullQQVesion(),
|
|
||||||
selfInfo.uin,
|
|
||||||
selfInfo.uid,
|
|
||||||
dataPath,
|
|
||||||
);
|
|
||||||
|
|
||||||
await initializeSession(session, sessionConfig, logger);
|
|
||||||
|
|
||||||
const accountDataPath = path.resolve(dataPath, './NapCat/data');
|
|
||||||
fs.mkdirSync(dataPath, { recursive: true });
|
|
||||||
logger.logDebug('本账号数据/缓存目录:', accountDataPath);
|
|
||||||
|
|
||||||
await new NapCatShell(
|
|
||||||
wrapper,
|
|
||||||
session,
|
|
||||||
logger,
|
|
||||||
loginService,
|
|
||||||
selfInfo,
|
|
||||||
basicInfoWrapper,
|
|
||||||
pathWrapper,
|
|
||||||
).InitNapCat();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class NapCatShell {
|
|
||||||
readonly core: NapCatCore;
|
|
||||||
readonly context: InstanceContext;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
wrapper: WrapperNodeApi,
|
|
||||||
session: NodeIQQNTWrapperSession,
|
|
||||||
logger: LogWrapper,
|
|
||||||
loginService: NodeIKernelLoginService,
|
|
||||||
selfInfo: SelfInfo,
|
|
||||||
basicInfoWrapper: QQBasicInfoWrapper,
|
|
||||||
pathWrapper: NapCatPathWrapper,
|
|
||||||
) {
|
|
||||||
this.context = {
|
|
||||||
workingEnv: NapCatCoreWorkingEnv.Shell,
|
|
||||||
wrapper,
|
|
||||||
session,
|
|
||||||
logger,
|
|
||||||
loginService,
|
|
||||||
basicInfoWrapper,
|
|
||||||
pathWrapper,
|
|
||||||
};
|
|
||||||
this.core = new NapCatCore(this.context, selfInfo);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
async InitNapCat() {
|
|
||||||
await this.core.initCore();
|
|
||||||
new NapCatOneBot11Adapter(this.core, this.context, this.context.pathWrapper).InitOneBot()
|
|
||||||
.catch(e => this.context.logger.logError.bind(this.context.logger)('初始化OneBot失败', e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NCoreInitShell();
|
NCoreInitShell();
|
||||||
|
7
src/universal/napcat.ts
Normal file
7
src/universal/napcat.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { NCoreInitShell } from "@/shell/base";
|
||||||
|
|
||||||
|
export * from "@/framework/napcat";
|
||||||
|
export * from "@/shell/base";
|
||||||
|
if ((global as any).LiteLoader == undefined) {
|
||||||
|
NCoreInitShell();
|
||||||
|
}
|
@@ -3,11 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
import { LogWrapper } from '@/common/log';
|
import { LogWrapper } from '@/common/log';
|
||||||
import { NapCatPathWrapper } from '@/common/path';
|
import { NapCatPathWrapper } from '@/common/path';
|
||||||
import { RequestUtil } from '@/common/request';
|
|
||||||
|
|
||||||
import { WebUiConfigWrapper } from '@webapi/helper/config';
|
import { WebUiConfigWrapper } from '@webapi/helper/config';
|
||||||
import { ALLRouter } from '@webapi/router';
|
import { ALLRouter } from '@webapi/router';
|
||||||
import { cors } from '@webapi/middleware/cors';
|
import { cors } from '@webapi/middleware/cors';
|
||||||
@@ -29,10 +26,9 @@ export let webUiPathWrapper: NapCatPathWrapper;
|
|||||||
export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapper) {
|
export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapper) {
|
||||||
webUiPathWrapper = pathWrapper;
|
webUiPathWrapper = pathWrapper;
|
||||||
WebUiConfig = new WebUiConfigWrapper();
|
WebUiConfig = new WebUiConfigWrapper();
|
||||||
const log = logger.log.bind(logger);
|
|
||||||
const config = await WebUiConfig.GetWebUIConfig();
|
const config = await WebUiConfig.GetWebUIConfig();
|
||||||
if (config.port == 0) {
|
if (config.port == 0) {
|
||||||
log('[NapCat] [WebUi] Current WebUi is not run.');
|
logger.log('[NapCat] [WebUi] Current WebUi is not run.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,25 +57,15 @@ export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapp
|
|||||||
// ------------启动服务------------
|
// ------------启动服务------------
|
||||||
app.listen(config.port, config.host, async () => {
|
app.listen(config.port, config.host, async () => {
|
||||||
// 启动后打印出相关地址
|
// 启动后打印出相关地址
|
||||||
|
|
||||||
const port = config.port.toString(),
|
const port = config.port.toString(),
|
||||||
searchParams = { token: config.token },
|
searchParams = { token: config.token },
|
||||||
path = `${config.prefix}/webui`;
|
path = `${config.prefix}/webui`;
|
||||||
|
if (config.host !== '' && config.host !== '0.0.0.0') {
|
||||||
// 打印日志(地址、token)
|
logger.log(`[NapCat] [WebUi] WebUi User Panel Url: ${createUrl(config.host, port, path, searchParams)}`);
|
||||||
log(`[NapCat] [WebUi] Current WebUi is running at http://${config.host}:${config.port}${config.prefix}`);
|
logger.log(`[NapCat] [WebUi] WebUi User Panel Url: https://napcat.152710.xyz/web_login?back=http://${config.host}:${config.port}${config.prefix}&token=${config.token}`);
|
||||||
log(`[NapCat] [WebUi] Login Token is ${config.token}`);
|
|
||||||
log(`[NapCat] [WebUi] WebUi User Panel Url: ${createUrl(config.host, port, path, searchParams)}`);
|
|
||||||
log(`[NapCat] [WebUi] WebUi Local Panel Url: ${createUrl('127.0.0.1', port, path, searchParams)}`);
|
|
||||||
|
|
||||||
// 获取公网地址
|
|
||||||
try {
|
|
||||||
const publishUrl = 'https://ip.011102.xyz/';
|
|
||||||
const data = await RequestUtil.HttpGetJson<{ IP: { IP: string } }>(publishUrl, 'GET', {}, {}, true, true);
|
|
||||||
log(`[NapCat] [WebUi] WebUi Publish Panel Url: ${createUrl(data.IP.IP, port, path, searchParams)}`);
|
|
||||||
} catch (err) {
|
|
||||||
logger.logError(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`);
|
|
||||||
}
|
}
|
||||||
|
logger.log(`[NapCat] [WebUi] WebUi Local Panel Url: ${createUrl('127.0.0.1', port, path, searchParams)}`);
|
||||||
|
logger.log(`[NapCat] [WebUi] WebUi Local Panel Url: https://napcat.152710.xyz/web_login?back=http://127.0.0.1:${config.port}${config.prefix}&token=${config.token}`);
|
||||||
});
|
});
|
||||||
// ------------Over!------------
|
// ------------Over!------------
|
||||||
}
|
}
|
||||||
|
19
src/webui/src/api/Log.ts
Normal file
19
src/webui/src/api/Log.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { RequestHandler } from 'express';
|
||||||
|
import { sendError, sendSuccess } from '../utils/response';
|
||||||
|
import { WebUiConfigWrapper } from '../helper/config';
|
||||||
|
|
||||||
|
// 日志记录
|
||||||
|
export const LogHandler: RequestHandler = async (req, res) => {
|
||||||
|
const filename = req.query.id as string;
|
||||||
|
if (filename.includes('..')) {
|
||||||
|
return sendError(res, 'ID不合法');
|
||||||
|
}
|
||||||
|
const logContent = WebUiConfigWrapper.GetLogContent(filename);
|
||||||
|
return sendSuccess(res, logContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 日志列表
|
||||||
|
export const LogListHandler: RequestHandler = async (_, res) => {
|
||||||
|
const logList = WebUiConfigWrapper.GetLogsList();
|
||||||
|
return sendSuccess(res, logList);
|
||||||
|
};
|
@@ -1,5 +1,5 @@
|
|||||||
import { webUiPathWrapper } from '@/webui';
|
import { webUiPathWrapper } from '@/webui';
|
||||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
import { existsSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
||||||
import * as net from 'node:net';
|
import * as net from 'node:net';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
@@ -131,4 +131,26 @@ export class WebUiConfigWrapper {
|
|||||||
}
|
}
|
||||||
return defaultconfig; // 理论上这行代码到不了,到了只能返回默认配置了
|
return defaultconfig; // 理论上这行代码到不了,到了只能返回默认配置了
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取日志文件夹路径
|
||||||
|
public static async GetLogsPath(): Promise<string> {
|
||||||
|
return resolve(webUiPathWrapper.logsPath);
|
||||||
|
}
|
||||||
|
// 获取日志列表
|
||||||
|
public static GetLogsList(): string[] {
|
||||||
|
if (existsSync(webUiPathWrapper.logsPath)) {
|
||||||
|
return readdirSync(webUiPathWrapper.logsPath)
|
||||||
|
.filter((file) => file.endsWith('.log'))
|
||||||
|
.map((file) => file.replace('.log', ''));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// 获取指定日志文件内容
|
||||||
|
public static GetLogContent(filename: string): string {
|
||||||
|
const logPath = resolve(webUiPathWrapper.logsPath, `${filename}.log`);
|
||||||
|
if (existsSync(logPath)) {
|
||||||
|
return readFileSync(logPath, 'utf-8');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
src/webui/src/router/Log.ts
Normal file
9
src/webui/src/router/Log.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { LogHandler, LogListHandler } from '../api/Log';
|
||||||
|
const router = Router();
|
||||||
|
// router:读取日志内容
|
||||||
|
router.get('/GetLog', LogHandler);
|
||||||
|
// router:读取日志列表
|
||||||
|
router.get('/GetLogList', LogListHandler);
|
||||||
|
|
||||||
|
export { router as LogRouter };
|
@@ -10,6 +10,7 @@ import { sendSuccess } from '@webapi/utils/response';
|
|||||||
|
|
||||||
import { QQLoginRouter } from '@webapi/router/QQLogin';
|
import { QQLoginRouter } from '@webapi/router/QQLogin';
|
||||||
import { AuthRouter } from '@webapi/router/auth';
|
import { AuthRouter } from '@webapi/router/auth';
|
||||||
|
import { LogRouter } from '@webapi/router/Log';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@@ -26,5 +27,7 @@ router.use('/auth', AuthRouter);
|
|||||||
router.use('/QQLogin', QQLoginRouter);
|
router.use('/QQLogin', QQLoginRouter);
|
||||||
// router:OB11配置相关路由
|
// router:OB11配置相关路由
|
||||||
router.use('/OB11Config', OB11ConfigRouter);
|
router.use('/OB11Config', OB11ConfigRouter);
|
||||||
|
// router:日志相关路由
|
||||||
|
router.use('/Log', LogRouter);
|
||||||
|
|
||||||
export { router as ALLRouter };
|
export { router as ALLRouter };
|
||||||
|
@@ -6,9 +6,7 @@ import { builtinModules } from 'module';
|
|||||||
//依赖排除
|
//依赖排除
|
||||||
const external = ['silk-wasm', 'ws', 'express', 'qrcode-terminal', 'fluent-ffmpeg', 'piscina'];
|
const external = ['silk-wasm', 'ws', 'express', 'qrcode-terminal', 'fluent-ffmpeg', 'piscina'];
|
||||||
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
|
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
|
||||||
function genCpModule(module: string) {
|
|
||||||
return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false };
|
|
||||||
}
|
|
||||||
let startScripts: string[] | undefined = undefined;
|
let startScripts: string[] | undefined = undefined;
|
||||||
if (process.env.NAPCAT_BUILDSYS == 'linux') {
|
if (process.env.NAPCAT_BUILDSYS == 'linux') {
|
||||||
startScripts = [];
|
startScripts = [];
|
||||||
@@ -17,6 +15,29 @@ if (process.env.NAPCAT_BUILDSYS == 'linux') {
|
|||||||
} else {
|
} else {
|
||||||
startScripts = ['./script/KillQQ.bat'];
|
startScripts = ['./script/KillQQ.bat'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UniversalBaseConfigPlugin: PluginOption[] = [
|
||||||
|
cp({
|
||||||
|
targets: [
|
||||||
|
{ src: './manifest.json', dest: 'dist' },
|
||||||
|
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
||||||
|
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false },
|
||||||
|
{ src: './napcat.webui/dist/', dest: 'dist/static/', flatten: false },
|
||||||
|
{ src: './src/framework/liteloader.cjs', dest: 'dist' },
|
||||||
|
{ src: './src/framework/napcat.cjs', dest: 'dist' },
|
||||||
|
{ src: './src/framework/preload.cjs', dest: 'dist' },
|
||||||
|
{ src: './src/framework/renderer.js', dest: 'dist' },
|
||||||
|
{ src: './package.json', dest: 'dist' },
|
||||||
|
{ src: './logo.png', dest: 'dist' },
|
||||||
|
{ src: './launcher/', dest: 'dist', flatten: true },
|
||||||
|
...startScripts.map((startScript) => {
|
||||||
|
return { src: startScript, dest: 'dist' };
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
nodeResolve(),
|
||||||
|
];
|
||||||
|
|
||||||
const FrameworkBaseConfigPlugin: PluginOption[] = [
|
const FrameworkBaseConfigPlugin: PluginOption[] = [
|
||||||
cp({
|
cp({
|
||||||
targets: [
|
targets: [
|
||||||
@@ -34,6 +55,8 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
|
|||||||
}),
|
}),
|
||||||
nodeResolve(),
|
nodeResolve(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
const ShellBaseConfigPlugin: PluginOption[] = [
|
const ShellBaseConfigPlugin: PluginOption[] = [
|
||||||
cp({
|
cp({
|
||||||
targets: [
|
targets: [
|
||||||
@@ -49,6 +72,35 @@ const ShellBaseConfigPlugin: PluginOption[] = [
|
|||||||
}),
|
}),
|
||||||
nodeResolve(),
|
nodeResolve(),
|
||||||
];
|
];
|
||||||
|
const UniversalBaseConfig = () =>
|
||||||
|
defineConfig({
|
||||||
|
resolve: {
|
||||||
|
conditions: ['node', 'default'],
|
||||||
|
alias: {
|
||||||
|
'@/core': resolve(__dirname, './src/core'),
|
||||||
|
'@': resolve(__dirname, './src'),
|
||||||
|
'./lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg',
|
||||||
|
'@webapi': resolve(__dirname, './src/webui/src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
sourcemap: false,
|
||||||
|
target: 'esnext',
|
||||||
|
minify: false,
|
||||||
|
lib: {
|
||||||
|
entry: {
|
||||||
|
napcat: 'src/universal/napcat.ts',
|
||||||
|
'audio-worker': 'src/common/audio-worker.ts',
|
||||||
|
},
|
||||||
|
formats: ['es'],
|
||||||
|
fileName: (_, entryName) => `${entryName}.mjs`,
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: [...nodeModules, ...external],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const ShellBaseConfig = () =>
|
const ShellBaseConfig = () =>
|
||||||
defineConfig({
|
defineConfig({
|
||||||
@@ -114,6 +166,11 @@ export default defineConfig(({ mode }): UserConfig => {
|
|||||||
...ShellBaseConfig(),
|
...ShellBaseConfig(),
|
||||||
plugins: [...ShellBaseConfigPlugin],
|
plugins: [...ShellBaseConfigPlugin],
|
||||||
};
|
};
|
||||||
|
} else if (mode == 'universal') {
|
||||||
|
return {
|
||||||
|
...UniversalBaseConfig(),
|
||||||
|
plugins: [...UniversalBaseConfigPlugin],
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
...FrameworkBaseConfig(),
|
...FrameworkBaseConfig(),
|
||||||
|
Reference in New Issue
Block a user