mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
40b2f6bfd6 | ||
![]() |
911e4921e2 | ||
![]() |
1db9bb419d | ||
![]() |
c6241a94e3 | ||
![]() |
1cbf75ca36 | ||
![]() |
8f85c897c8 | ||
![]() |
29c31b7aba | ||
![]() |
402919d6f2 | ||
![]() |
82608dd5ff | ||
![]() |
f312368df2 | ||
![]() |
374fc64427 | ||
![]() |
95bd74bb0d | ||
![]() |
a9f5069649 | ||
![]() |
957f7ffd8d | ||
![]() |
336dd3ce10 | ||
![]() |
47a7295477 | ||
![]() |
341a0e1c2a |
@@ -5,7 +5,7 @@ if %errorLevel% == 0 (
|
|||||||
echo Administrator mode detected.
|
echo Administrator mode detected.
|
||||||
) else (
|
) else (
|
||||||
echo Please run this script in administrator mode.
|
echo Please run this script in administrator mode.
|
||||||
powershell -Command "Start-Process 'cmd.exe' -ArgumentList '/c cd /d \"%cd%\" && \"%~f0\"' -Verb runAs"
|
powershell -Command "Start-Process 'cmd.exe' -ArgumentList '/c cd /d \"%cd%\" && \"%~f0\" %1' -Verb runAs"
|
||||||
exit
|
exit
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,6 +35,6 @@ if not exist "%QQpath%" (
|
|||||||
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
||||||
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > %NAPCAT_LOAD_PATH%
|
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > %NAPCAT_LOAD_PATH%
|
||||||
|
|
||||||
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%"
|
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1
|
||||||
|
|
||||||
REM "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" 123456
|
REM "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" 123456
|
@@ -5,7 +5,7 @@ if %errorLevel% == 0 (
|
|||||||
echo Administrator mode detected.
|
echo Administrator mode detected.
|
||||||
) else (
|
) else (
|
||||||
echo Please run this script in administrator mode.
|
echo Please run this script in administrator mode.
|
||||||
powershell -Command "Start-Process 'wt.exe' -ArgumentList 'cmd /c cd /d \"%cd%\" && \"%~f0\"' -Verb runAs"
|
powershell -Command "Start-Process 'wt.exe' -ArgumentList 'cmd /c cd /d \"%cd%\" && \"%~f0\" %1' -Verb runAs"
|
||||||
exit
|
exit
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,6 +36,4 @@ if not exist "%QQpath%" (
|
|||||||
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
||||||
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > %NAPCAT_LOAD_PATH%
|
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > %NAPCAT_LOAD_PATH%
|
||||||
|
|
||||||
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%"
|
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1
|
||||||
|
|
||||||
REM "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" 123456
|
|
4
launcher/quickLoginExample.bat
Normal file
4
launcher/quickLoginExample.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
REM ./launcher.bat 123456
|
||||||
|
REM ./launcher-win10.bat 123456
|
||||||
|
REM 带有REM的为注释 删掉你需要的系统的那行REM这三个单词 修改QQ本脚本启动即可
|
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "2.4.5",
|
"version": "2.4.7",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.4.5",
|
"version": "2.4.7",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:framework": "vite build --mode framework",
|
"build:framework": "vite build --mode framework",
|
||||||
"build:shell": "vite build --mode shell",
|
"build:shell": "vite build --mode shell",
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import fsPromise from 'fs/promises';
|
import fsPromise from 'fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
@@ -12,7 +11,7 @@ const FFMPEG_PATH = process.env.FFMPEG_PATH || 'ffmpeg';
|
|||||||
|
|
||||||
async function guessDuration(pttPath: string, logger: LogWrapper) {
|
async function guessDuration(pttPath: string, logger: LogWrapper) {
|
||||||
const pttFileInfo = await fsPromise.stat(pttPath);
|
const pttFileInfo = await fsPromise.stat(pttPath);
|
||||||
let duration = Math.max(1, Math.floor(pttFileInfo.size / 1024 / 3)); // 3kb/s
|
const duration = Math.max(1, Math.floor(pttFileInfo.size / 1024 / 3)); // 3kb/s
|
||||||
logger.log('通过文件大小估算语音的时长:', duration);
|
logger.log('通过文件大小估算语音的时长:', duration);
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
@@ -20,7 +19,7 @@ async function guessDuration(pttPath: string, logger: LogWrapper) {
|
|||||||
async function convert(filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> {
|
async function convert(filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> {
|
||||||
return new Promise<Buffer>((resolve, reject) => {
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
const cp = spawn(FFMPEG_PATH, ['-y', '-i', filePath, '-ar', '24000', '-ac', '1', '-f', 's16le', pcmPath]);
|
const cp = spawn(FFMPEG_PATH, ['-y', '-i', filePath, '-ar', '24000', '-ac', '1', '-f', 's16le', pcmPath]);
|
||||||
cp.on('error', err => {
|
cp.on('error', (err: Error) => {
|
||||||
logger.log('FFmpeg处理转换出错: ', err.message);
|
logger.log('FFmpeg处理转换出错: ', err.message);
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
@@ -160,8 +160,7 @@ type Uri2LocalRes = {
|
|||||||
errMsg: string,
|
errMsg: string,
|
||||||
fileName: string,
|
fileName: string,
|
||||||
ext: string,
|
ext: string,
|
||||||
path: string,
|
path: string
|
||||||
isLocal: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkFileV2(filePath: string) {
|
export async function checkFileV2(filePath: string) {
|
||||||
@@ -194,7 +193,6 @@ export async function checkUriType(Uri: string) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}, 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://')) {
|
||||||
@@ -206,13 +204,13 @@ export async function checkUriType(Uri: string) {
|
|||||||
}
|
}
|
||||||
if (uri.startsWith('file://')) {
|
if (uri.startsWith('file://')) {
|
||||||
let filePath: string;
|
let filePath: string;
|
||||||
// await fs.copyFile(url.pathname, filePath);
|
|
||||||
const pathname = decodeURIComponent(new URL(uri).pathname);
|
const pathname = decodeURIComponent(new URL(uri).pathname);
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
filePath = pathname.slice(1);
|
filePath = pathname.slice(1);
|
||||||
} else {
|
} else {
|
||||||
filePath = pathname;
|
filePath = pathname;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { Uri: filePath, Type: FileUriType.Local };
|
return { Uri: filePath, Type: FileUriType.Local };
|
||||||
}
|
}
|
||||||
if (uri.startsWith('data:')) {
|
if (uri.startsWith('data:')) {
|
||||||
@@ -228,35 +226,42 @@ export async function checkUriType(Uri: string) {
|
|||||||
export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise<Uri2LocalRes> {
|
export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise<Uri2LocalRes> {
|
||||||
const { Uri: HandledUri, Type: UriType } = await checkUriType(uri);
|
const { Uri: HandledUri, Type: UriType } = await checkUriType(uri);
|
||||||
//解析失败
|
//解析失败
|
||||||
|
const tempName = randomUUID();
|
||||||
|
if (!filename) filename = randomUUID();
|
||||||
|
//解析Http和Https协议
|
||||||
|
|
||||||
if (UriType == FileUriType.Unknown) {
|
if (UriType == FileUriType.Unknown) {
|
||||||
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
|
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '' };
|
||||||
}
|
}
|
||||||
//解析File协议和本地文件
|
//解析File协议和本地文件
|
||||||
if (UriType == FileUriType.Local) {
|
if (UriType == FileUriType.Local) {
|
||||||
const fileExt = path.extname(HandledUri);
|
const fileExt = path.extname(HandledUri);
|
||||||
let filename = path.basename(HandledUri, fileExt);
|
let filename = path.basename(HandledUri, fileExt);
|
||||||
filename += fileExt;
|
filename += fileExt;
|
||||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: HandledUri, isLocal: true };
|
//复制文件到临时文件并保持后缀
|
||||||
|
const filenameTemp = tempName + fileExt;
|
||||||
|
const filePath = path.join(dir, filenameTemp);
|
||||||
|
fs.copyFileSync(HandledUri, filePath);
|
||||||
|
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
|
||||||
}
|
}
|
||||||
//接下来都要有文件名
|
//接下来都要有文件名
|
||||||
if (!filename) filename = randomUUID();
|
|
||||||
//解析Http和Https协议
|
|
||||||
|
|
||||||
if (UriType == FileUriType.Remote) {
|
if (UriType == FileUriType.Remote) {
|
||||||
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
|
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
|
||||||
if (pathInfo.name) {
|
if (pathInfo.name) {
|
||||||
filename = pathInfo.name;
|
const pathlen = 200 - dir.length - pathInfo.name.length;
|
||||||
|
filename = pathlen > 0 ? pathInfo.name.substring(0, pathlen) : pathInfo.name.substring(pathInfo.name.length, pathInfo.name.length - 10);//过长截断
|
||||||
if (pathInfo.ext) {
|
if (pathInfo.ext) {
|
||||||
filename += pathInfo.ext;
|
filename += pathInfo.ext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filename = filename.replace(/[/\\:*?"<>|]/g, '_');
|
filename = filename.replace(/[/\\:*?"<>|]/g, '_');
|
||||||
const fileExt = path.extname(HandledUri);
|
const fileExt = path.extname(HandledUri).replace(/[/\\:*?"<>|]/g, '_').substring(0, 10);
|
||||||
const filePath = path.join(dir, filename);
|
const filePath = path.join(dir, tempName + fileExt);
|
||||||
const buffer = await httpDownload(HandledUri);
|
const buffer = await httpDownload(HandledUri);
|
||||||
fs.writeFileSync(filePath, buffer);
|
//没有文件就创建
|
||||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: false };
|
fs.writeFileSync(filePath, buffer, { flag: 'wx' });
|
||||||
|
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
|
||||||
}
|
}
|
||||||
//解析Base64
|
//解析Base64
|
||||||
if (UriType == FileUriType.Base64) {
|
if (UriType == FileUriType.Base64) {
|
||||||
@@ -271,7 +276,7 @@ export async function uri2local(dir: string, uri: string, filename: string | und
|
|||||||
fileExt = ext;
|
fileExt = ext;
|
||||||
filename = filename + '.' + ext;
|
filename = filename + '.' + ext;
|
||||||
}
|
}
|
||||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: false };
|
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
|
||||||
}
|
}
|
||||||
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
|
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '' };
|
||||||
}
|
}
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '2.4.5';
|
export const napCatVersion = '2.4.7';
|
||||||
|
@@ -24,6 +24,7 @@ import pathLib from 'node:path';
|
|||||||
import { defaultVideoThumbB64, getVideoInfo } from '@/common/video';
|
import { defaultVideoThumbB64, getVideoInfo } from '@/common/video';
|
||||||
import ffmpeg from 'fluent-ffmpeg';
|
import ffmpeg from 'fluent-ffmpeg';
|
||||||
import { encodeSilk } from '@/common/audio';
|
import { encodeSilk } from '@/common/audio';
|
||||||
|
import { MessageContext } from '@/onebot/api';
|
||||||
|
|
||||||
export class NTQQFileApi {
|
export class NTQQFileApi {
|
||||||
context: InstanceContext;
|
context: InstanceContext;
|
||||||
@@ -33,7 +34,7 @@ export class NTQQFileApi {
|
|||||||
constructor(context: InstanceContext, core: NapCatCore) {
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.core = core;
|
this.core = core;
|
||||||
this.rkeyManager = new RkeyManager('https://llob.linyuchen.net/rkey', this.context.logger);
|
this.rkeyManager = new RkeyManager(['https://llob.linyuchen.net/rkey', 'http://napcat-sign.wumiao.wang:2082/rkey'], this.context.logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
async copyFile(filePath: string, destPath: string) {
|
async copyFile(filePath: string, destPath: string) {
|
||||||
@@ -71,7 +72,7 @@ export class NTQQFileApi {
|
|||||||
file_uuid: '',
|
file_uuid: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.copyFile(filePath, mediaPath!);
|
await this.copyFile(filePath, mediaPath);
|
||||||
const fileSize = await this.getFileSize(filePath);
|
const fileSize = await this.getFileSize(filePath);
|
||||||
return {
|
return {
|
||||||
md5: fileMd5,
|
md5: fileMd5,
|
||||||
@@ -82,7 +83,7 @@ export class NTQQFileApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendFileElement(filePath: string, fileName: string = '', folderId: string = '',): Promise<SendFileElement> {
|
async createValidSendFileElement(context: MessageContext, filePath: string, fileName: string = '', folderId: string = '',): Promise<SendFileElement> {
|
||||||
const {
|
const {
|
||||||
fileName: _fileName,
|
fileName: _fileName,
|
||||||
path,
|
path,
|
||||||
@@ -91,6 +92,7 @@ export class NTQQFileApi {
|
|||||||
if (fileSize === 0) {
|
if (fileSize === 0) {
|
||||||
throw new Error('文件异常,大小为0');
|
throw new Error('文件异常,大小为0');
|
||||||
}
|
}
|
||||||
|
context.deleteAfterSentFiles.push(path);
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.FILE,
|
elementType: ElementType.FILE,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
@@ -103,12 +105,13 @@ export class NTQQFileApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendPicElement(picPath: string, summary: string = '', subType: 0 | 1 = 0,): Promise<SendPicElement> {
|
async createValidSendPicElement(context: MessageContext, picPath: string, summary: string = '', subType: 0 | 1 = 0,): Promise<SendPicElement> {
|
||||||
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
|
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
|
||||||
if (fileSize === 0) {
|
if (fileSize === 0) {
|
||||||
throw new Error('文件异常,大小为0');
|
throw new Error('文件异常,大小为0');
|
||||||
}
|
}
|
||||||
const imageSize = await this.core.apis.FileApi.getImageSize(picPath);
|
const imageSize = await this.core.apis.FileApi.getImageSize(picPath);
|
||||||
|
context.deleteAfterSentFiles.push(path);
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.PIC,
|
elementType: ElementType.PIC,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
@@ -130,26 +133,32 @@ export class NTQQFileApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendVideoElement(filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
|
async createValidSendVideoElement(context: MessageContext, filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
|
||||||
const logger = this.core.context.logger;
|
const logger = this.core.context.logger;
|
||||||
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
|
|
||||||
if (fileSize === 0) {
|
|
||||||
throw new Error('文件异常,大小为0');
|
|
||||||
}
|
|
||||||
let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
|
|
||||||
thumb = pathLib.dirname(thumb);
|
|
||||||
let videoInfo = {
|
let videoInfo = {
|
||||||
width: 1920, height: 1080,
|
width: 1920, height: 1080,
|
||||||
time: 15,
|
time: 15,
|
||||||
format: 'mp4',
|
format: 'mp4',
|
||||||
size: fileSize,
|
size: 0,
|
||||||
filePath,
|
filePath,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
videoInfo = await getVideoInfo(path, logger);
|
videoInfo = await getVideoInfo(filePath, logger);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.logError('获取视频信息失败,将使用默认值', e);
|
logger.logError('获取视频信息失败,将使用默认值', e);
|
||||||
}
|
}
|
||||||
|
const newFilePath = filePath + '.mp4';
|
||||||
|
fs.copyFileSync(filePath, newFilePath);
|
||||||
|
context.deleteAfterSentFiles.push(newFilePath);
|
||||||
|
filePath = newFilePath;
|
||||||
|
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
|
||||||
|
if (fileSize === 0) {
|
||||||
|
throw new Error('文件异常,大小为0');
|
||||||
|
}
|
||||||
|
videoInfo.size = fileSize;
|
||||||
|
let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
|
||||||
|
thumb = pathLib.dirname(thumb);
|
||||||
|
|
||||||
const thumbPath = new Map();
|
const thumbPath = new Map();
|
||||||
const _thumbPath = await new Promise<string | undefined>((resolve, reject) => {
|
const _thumbPath = await new Promise<string | undefined>((resolve, reject) => {
|
||||||
const thumbFileName = `${md5}_0.png`;
|
const thumbFileName = `${md5}_0.png`;
|
||||||
@@ -179,6 +188,7 @@ export class NTQQFileApi {
|
|||||||
const thumbSize = _thumbPath ? (await fsPromises.stat(_thumbPath)).size : 0;
|
const thumbSize = _thumbPath ? (await fsPromises.stat(_thumbPath)).size : 0;
|
||||||
thumbPath.set(0, _thumbPath);
|
thumbPath.set(0, _thumbPath);
|
||||||
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : '';
|
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : '';
|
||||||
|
context.deleteAfterSentFiles.push(path);
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.VIDEO,
|
elementType: ElementType.VIDEO,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
|
@@ -32,10 +32,6 @@ export class NTQQGroupApi {
|
|||||||
for (const group of this.groups) {
|
for (const group of this.groups) {
|
||||||
this.groupCache.set(group.groupCode, group);
|
this.groupCache.set(group.groupCode, group);
|
||||||
}
|
}
|
||||||
// let text = await this.context.session.getMsgService().sendSsoCmdReqByContend(
|
|
||||||
// 'LightAppSvc.mini_app_share.AdaptShareInfo',
|
|
||||||
// JSON.stringify({ data: 'test' }));
|
|
||||||
// console.log(text);
|
|
||||||
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
|
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { GroupMemberRole, Peer } from '@/core';
|
import { GroupMemberRole } from '@/core';
|
||||||
|
|
||||||
export interface Peer {
|
export interface Peer {
|
||||||
chatType: ChatType;
|
chatType: ChatType;
|
||||||
|
@@ -8,7 +8,7 @@ interface ServerRkeyData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class RkeyManager {
|
export class RkeyManager {
|
||||||
serverUrl: string = '';
|
serverUrl: string[] = [];
|
||||||
logger: LogWrapper;
|
logger: LogWrapper;
|
||||||
private rkeyData: ServerRkeyData = {
|
private rkeyData: ServerRkeyData = {
|
||||||
group_rkey: '',
|
group_rkey: '',
|
||||||
@@ -16,7 +16,7 @@ export class RkeyManager {
|
|||||||
expired_time: 0,
|
expired_time: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(serverUrl: string, logger: LogWrapper) {
|
constructor(serverUrl: string[], logger: LogWrapper) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.serverUrl = serverUrl;
|
this.serverUrl = serverUrl;
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,13 @@ export class RkeyManager {
|
|||||||
|
|
||||||
async refreshRkey(): Promise<any> {
|
async refreshRkey(): Promise<any> {
|
||||||
//刷新rkey
|
//刷新rkey
|
||||||
this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(this.serverUrl, 'GET');
|
for (const url of this.serverUrl) {
|
||||||
|
try {
|
||||||
|
this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET');
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.logError(`[Rkey] Get Rkey ${url} Error `, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -145,7 +145,6 @@ export class NapCatCore {
|
|||||||
if (Info.status == 20) {
|
if (Info.status == 20) {
|
||||||
this.selfInfo.online = false;
|
this.selfInfo.online = false;
|
||||||
this.context.logger.log("账号状态变更为离线");
|
this.context.logger.log("账号状态变更为离线");
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
this.selfInfo.online = true;
|
this.selfInfo.online = true;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import * as pb from 'protobufjs';
|
import * as pb from 'protobufjs';
|
||||||
|
|
||||||
// Proto: from src/core/proto/ProfileLike.proto
|
// Proto: from src/core/proto/ProfileLike.proto
|
||||||
// Auther: Mlikiowa
|
// Author: Mlikiowa
|
||||||
|
|
||||||
export interface LikeDetailType {
|
export interface LikeDetailType {
|
||||||
txt: string;
|
txt: string;
|
||||||
@@ -80,4 +80,4 @@ export const likeMsg = new pb.Type("likeMsg")
|
|||||||
|
|
||||||
export const profileLikeTip = new pb.Type("profileLikeTip")
|
export const profileLikeTip = new pb.Type("profileLikeTip")
|
||||||
.add(likeMsg)
|
.add(likeMsg)
|
||||||
.add(new pb.Field("msg", 14, "likeMsg"));
|
.add(new pb.Field("msg", 14, "likeMsg"));
|
||||||
|
@@ -19,26 +19,21 @@ export class OCRImage extends BaseAction<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
const { path, isLocal, success } = (await uri2local(this.core.NapCatTempPath, payload.image));
|
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.image));
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw `OCR ${payload.image}失败,image字段可能格式不正确`;
|
throw `OCR ${payload.image}失败,image字段可能格式不正确`;
|
||||||
}
|
}
|
||||||
if (path) {
|
if (path) {
|
||||||
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
const ret = await this.core.apis.SystemApi.ocrImage(path);
|
const ret = await this.core.apis.SystemApi.ocrImage(path);
|
||||||
if (!isLocal) {
|
fs.unlink(path, () => { });
|
||||||
fs.unlink(path, () => {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
throw `OCR ${payload.file}失败`;
|
throw `OCR ${payload.file}失败`;
|
||||||
}
|
}
|
||||||
return ret.result;
|
return ret.result;
|
||||||
}
|
}
|
||||||
if (!isLocal) {
|
fs.unlink(path, () => { });
|
||||||
fs.unlink(path, () => {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
throw `OCR ${payload.file}失败,文件可能不存在`;
|
throw `OCR ${payload.file}失败,文件可能不存在`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,17 +24,16 @@ export default class SetAvatar extends BaseAction<Payload, null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _handle(payload: Payload): Promise<null> {
|
async _handle(payload: Payload): Promise<null> {
|
||||||
const { path, isLocal, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
|
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
||||||
}
|
}
|
||||||
if (path) {
|
if (path) {
|
||||||
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
const ret = await this.core.apis.UserApi.setQQAvatar(path);
|
const ret = await this.core.apis.UserApi.setQQAvatar(path);
|
||||||
if (!isLocal) {
|
fs.unlink(path, () => {
|
||||||
fs.unlink(path, () => {
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
throw `头像${payload.file}设置失败,api无返回`;
|
throw `头像${payload.file}设置失败,api无返回`;
|
||||||
}
|
}
|
||||||
@@ -45,10 +44,8 @@ export default class SetAvatar extends BaseAction<Payload, null> {
|
|||||||
throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isLocal) {
|
fs.unlink(path, () => { });
|
||||||
fs.unlink(path, () => {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
|
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@@ -31,7 +31,6 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
|
|||||||
//公告图逻辑
|
//公告图逻辑
|
||||||
const {
|
const {
|
||||||
path,
|
path,
|
||||||
isLocal,
|
|
||||||
success,
|
success,
|
||||||
} = (await uri2local(this.core.NapCatTempPath, payload.image));
|
} = (await uri2local(this.core.NapCatTempPath, payload.image));
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@@ -45,10 +44,10 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
|
|||||||
if (ImageUploadResult.errCode != 0) {
|
if (ImageUploadResult.errCode != 0) {
|
||||||
throw `群公告${payload.image}设置失败,图片上传失败`;
|
throw `群公告${payload.image}设置失败,图片上传失败`;
|
||||||
}
|
}
|
||||||
if (!isLocal) {
|
|
||||||
unlink(path, () => {
|
unlink(path, () => {
|
||||||
});
|
});
|
||||||
}
|
|
||||||
UploadImage = ImageUploadResult.picInfo;
|
UploadImage = ImageUploadResult.picInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +57,6 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
|
|||||||
const noticeShowEditCard = +(payload.is_show_edit_card ?? 0);
|
const noticeShowEditCard = +(payload.is_show_edit_card ?? 0);
|
||||||
const noticeTipWindowType = +(payload.tip_window_type ?? 0);
|
const noticeTipWindowType = +(payload.tip_window_type ?? 0);
|
||||||
const noticeConfirmRequired = +(payload.confirm_required ?? 1);
|
const noticeConfirmRequired = +(payload.confirm_required ?? 1);
|
||||||
//const publishGroupBulletinResult = await this.core.apis.GroupApi.publishGroupBulletin(payload.group_id.toString(), payload.content, UploadImage, noticePinned, noticeConfirmRequired);
|
|
||||||
const publishGroupBulletinResult = await this.core.apis.WebApi.setGroupNotice(
|
const publishGroupBulletinResult = await this.core.apis.WebApi.setGroupNotice(
|
||||||
payload.group_id.toString(),
|
payload.group_id.toString(),
|
||||||
payload.content,
|
payload.content,
|
||||||
@@ -72,7 +70,7 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
|
|||||||
UploadImage?.height
|
UploadImage?.height
|
||||||
);
|
);
|
||||||
if (!publishGroupBulletinResult || publishGroupBulletinResult.ec != 0) {
|
if (!publishGroupBulletinResult || publishGroupBulletinResult.ec != 0) {
|
||||||
throw `设置群公告失败,错误信息:${publishGroupBulletinResult?.em}`;
|
throw new Error(`设置群公告失败,错误信息:${publishGroupBulletinResult?.em}`);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -25,17 +25,14 @@ export default class SetGroupPortrait extends BaseAction<Payload, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _handle(payload: Payload): Promise<any> {
|
async _handle(payload: Payload): Promise<any> {
|
||||||
const { path, isLocal, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
|
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
||||||
}
|
}
|
||||||
if (path) {
|
if (path) {
|
||||||
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
const ret = await this.core.apis.GroupApi.setGroupAvatar(payload.group_id.toString(), path) as any;
|
const ret = await this.core.apis.GroupApi.setGroupAvatar(payload.group_id.toString(), path) as any;
|
||||||
if (!isLocal) {
|
fs.unlink(path, () => { });
|
||||||
fs.unlink(path, () => {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
throw `头像${payload.file}设置失败,api无返回`;
|
throw `头像${payload.file}设置失败,api无返回`;
|
||||||
}
|
}
|
||||||
@@ -46,10 +43,7 @@ export default class SetGroupPortrait extends BaseAction<Payload, any> {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
if (!isLocal) {
|
fs.unlink(path, () => {});
|
||||||
fs.unlink(path, () => {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
|
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { ChatType } from '@/core/entities';
|
import { ChatType, Peer } from '@/core/entities';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { uri2local } from '@/common/file';
|
import { uri2local } from '@/common/file';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageContext } from '@/onebot/api';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -29,14 +30,19 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
|
|||||||
file = `file://${file}`;
|
file = `file://${file}`;
|
||||||
}
|
}
|
||||||
const downloadResult = await uri2local(this.core.NapCatTempPath, file);
|
const downloadResult = await uri2local(this.core.NapCatTempPath, file);
|
||||||
|
const peer: Peer = {
|
||||||
|
chatType: ChatType.KCHATTYPEGROUP,
|
||||||
|
peerUid: payload.group_id.toString(),
|
||||||
|
};
|
||||||
if (!downloadResult.success) {
|
if (!downloadResult.success) {
|
||||||
throw new Error(downloadResult.errMsg);
|
throw new Error(downloadResult.errMsg);
|
||||||
}
|
}
|
||||||
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name, payload.folder_id);
|
const msgContext: MessageContext = {
|
||||||
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId({
|
peer: peer,
|
||||||
chatType: ChatType.KCHATTYPEGROUP,
|
deleteAfterSentFiles: []
|
||||||
peerUid: payload.group_id.toString(),
|
};
|
||||||
}, [sendFileEle], [], true);
|
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder_id);
|
||||||
|
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], [], true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,8 @@ import { ChatType, Peer, SendFileElement } from '@/core/entities';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { uri2local } from '@/common/file';
|
import { uri2local } from '@/common/file';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageContext } from '@/onebot/api';
|
||||||
|
import { ContextMode, createContext } from '../msg/SendMsg';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -42,7 +44,15 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
|
|||||||
if (!downloadResult.success) {
|
if (!downloadResult.success) {
|
||||||
throw new Error(downloadResult.errMsg);
|
throw new Error(downloadResult.errMsg);
|
||||||
}
|
}
|
||||||
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name);
|
|
||||||
|
const msgContext: MessageContext = {
|
||||||
|
peer: await createContext(this.core, {
|
||||||
|
user_id: payload.user_id.toString(),
|
||||||
|
group_id: undefined,
|
||||||
|
}, ContextMode.Private),
|
||||||
|
deleteAfterSentFiles: []
|
||||||
|
};
|
||||||
|
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
|
||||||
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], [], true);
|
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], [], true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ import {
|
|||||||
OB11MessageDataType,
|
OB11MessageDataType,
|
||||||
OB11MessageMixType,
|
OB11MessageMixType,
|
||||||
OB11MessageNode,
|
OB11MessageNode,
|
||||||
|
OB11PostContext,
|
||||||
OB11PostSendMsg,
|
OB11PostSendMsg,
|
||||||
} from '@/onebot/types';
|
} from '@/onebot/types';
|
||||||
import { ActionName, BaseCheckResult } from '@/onebot/action/types';
|
import { ActionName, BaseCheckResult } from '@/onebot/action/types';
|
||||||
@@ -30,7 +31,7 @@ export function normalize(message: OB11MessageMixType, autoEscape = false): OB11
|
|||||||
) : Array.isArray(message) ? message : [message];
|
) : Array.isArray(message) ? message : [message];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createContext(core: NapCatCore, payload: OB11PostSendMsg, contextMode: ContextMode): Promise<Peer> {
|
export async function createContext(core: NapCatCore, payload: OB11PostContext, contextMode: ContextMode): Promise<Peer> {
|
||||||
// This function determines the type of message by the existence of user_id / group_id,
|
// This function determines the type of message by the existence of user_id / group_id,
|
||||||
// not message_type.
|
// not message_type.
|
||||||
// This redundant design of Ob11 here should be blamed.
|
// This redundant design of Ob11 here should be blamed.
|
||||||
|
@@ -142,22 +142,22 @@ export class OneBotGroupApi {
|
|||||||
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
|
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
|
||||||
const type = json.items[json.items.length - 1]?.txt;
|
const type = json.items[json.items.length - 1]?.txt;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "头衔": {
|
case "头衔": {
|
||||||
const memberUin = json.items[1].param[0];
|
const memberUin = json.items[1].param[0];
|
||||||
const title = json.items[3].txt;
|
const title = json.items[3].txt;
|
||||||
logger.logDebug('收到群成员新头衔消息', json);
|
logger.logDebug('收到群成员新头衔消息', json);
|
||||||
return new OB11GroupTitleEvent(
|
return new OB11GroupTitleEvent(
|
||||||
this.core,
|
this.core,
|
||||||
parseInt(msg.peerUid),
|
parseInt(msg.peerUid),
|
||||||
parseInt(memberUin),
|
parseInt(memberUin),
|
||||||
title,
|
title,
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
case "移出":
|
case "移出":
|
||||||
logger.logDebug('收到机器人被踢消息', json);
|
logger.logDebug('收到机器人被踢消息', json);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
logger.logWarn('收到未知的灰条消息', json);
|
logger.logWarn('收到未知的灰条消息', json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@ import { RequestUtil } from '@/common/request';
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import fsPromise from 'node:fs/promises';
|
import fsPromise from 'node:fs/promises';
|
||||||
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
|
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
|
||||||
|
import { SysMessage, SysMessageType } from '@/core/proto/ProfileLike';
|
||||||
|
|
||||||
type RawToOb11Converters = {
|
type RawToOb11Converters = {
|
||||||
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
|
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
|
||||||
@@ -185,7 +186,7 @@ export class OneBotMsgApi {
|
|||||||
data: {
|
data: {
|
||||||
file: 'marketface',
|
file: 'marketface',
|
||||||
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, "." + _.key + ".jpg"),
|
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, "." + _.key + ".jpg"),
|
||||||
path: elementWrapper.elementId,
|
path: url,
|
||||||
url: url,
|
url: url,
|
||||||
file_unique: _.key
|
file_unique: _.key
|
||||||
},
|
},
|
||||||
@@ -460,6 +461,7 @@ export class OneBotMsgApi {
|
|||||||
// File service
|
// File service
|
||||||
[OB11MessageDataType.image]: async (sendMsg, context) => {
|
[OB11MessageDataType.image]: async (sendMsg, context) => {
|
||||||
const sendPicElement = await this.core.apis.FileApi.createValidSendPicElement(
|
const sendPicElement = await this.core.apis.FileApi.createValidSendPicElement(
|
||||||
|
context,
|
||||||
(await this.handleOb11FileLikeMessage(sendMsg, context)).path,
|
(await this.handleOb11FileLikeMessage(sendMsg, context)).path,
|
||||||
sendMsg.data.summary,
|
sendMsg.data.summary,
|
||||||
sendMsg.data.sub_type,
|
sendMsg.data.sub_type,
|
||||||
@@ -470,7 +472,7 @@ export class OneBotMsgApi {
|
|||||||
|
|
||||||
[OB11MessageDataType.file]: async (sendMsg, context) => {
|
[OB11MessageDataType.file]: async (sendMsg, context) => {
|
||||||
const { path, fileName } = await this.handleOb11FileLikeMessage(sendMsg, context);
|
const { path, fileName } = await this.handleOb11FileLikeMessage(sendMsg, context);
|
||||||
return await this.core.apis.FileApi.createValidSendFileElement(path, fileName);
|
return await this.core.apis.FileApi.createValidSendFileElement(context, path, fileName);
|
||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.video]: async (sendMsg, context) => {
|
[OB11MessageDataType.video]: async (sendMsg, context) => {
|
||||||
@@ -481,9 +483,7 @@ export class OneBotMsgApi {
|
|||||||
const uri2LocalRes = await uri2local(this.core.NapCatTempPath, thumb);
|
const uri2LocalRes = await uri2local(this.core.NapCatTempPath, thumb);
|
||||||
if (uri2LocalRes.success) thumb = uri2LocalRes.path;
|
if (uri2LocalRes.success) thumb = uri2LocalRes.path;
|
||||||
}
|
}
|
||||||
const videoEle = await this.core.apis.FileApi.createValidSendVideoElement(path, fileName, thumb);
|
const videoEle = await this.core.apis.FileApi.createValidSendVideoElement(context, path, fileName, thumb);
|
||||||
|
|
||||||
context.deleteAfterSentFiles.push(videoEle.videoElement.filePath);
|
|
||||||
return videoEle;
|
return videoEle;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -805,7 +805,6 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
path,
|
path,
|
||||||
isLocal,
|
|
||||||
fileName,
|
fileName,
|
||||||
errMsg,
|
errMsg,
|
||||||
success,
|
success,
|
||||||
@@ -816,10 +815,42 @@ export class OneBotMsgApi {
|
|||||||
throw Error('文件下载失败' + errMsg);
|
throw Error('文件下载失败' + errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isLocal) { // 只删除http和base64转过来的文件
|
deleteAfterSentFiles.push(path);
|
||||||
deleteAfterSentFiles.push(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { path, fileName: inputdata.name ?? fileName };
|
return { path, fileName: inputdata.name ?? fileName };
|
||||||
}
|
}
|
||||||
|
async parseSysMessage(msg: number[]) {
|
||||||
|
const sysMsg = SysMessage.decode(Uint8Array.from(msg)) as unknown as SysMessageType;
|
||||||
|
if (sysMsg.msgSpec.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
|
||||||
|
if (msgType === 528 && subType === 39 && subSubType === 39) {
|
||||||
|
if (!sysMsg.bodyWrapper) return;
|
||||||
|
const event = await this.obContext.apis.UserApi.parseLikeEvent(sysMsg.bodyWrapper.wrappedBody);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (msgType === 732 && subType === 16 && subSubType === 16) {
|
||||||
|
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
|
||||||
|
if (greyTip.subTypeId === 36) {
|
||||||
|
const emojiLikeToOthers = EmojiLikeToOthersWrapper1
|
||||||
|
.fromBinary(greyTip.rest)
|
||||||
|
.wrapper!
|
||||||
|
.body!;
|
||||||
|
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
|
||||||
|
greyTip.groupCode.toString(),
|
||||||
|
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
|
||||||
|
emojiLikeToOthers.msgSpec!.msgSeq.toString(),
|
||||||
|
emojiLikeToOthers.attributes!.emojiId,
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
import { NapCatCore } from '@/core';
|
import { NapCatCore } from '@/core';
|
||||||
|
import { profileLikeTip, ProfileLikeTipType } from '@/core/proto/ProfileLike';
|
||||||
|
|
||||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||||
|
import { OB11ProfileLikeEvent } from '../event/notice/OB11ProfileLikeEvent';
|
||||||
|
|
||||||
export class OneBotUserApi {
|
export class OneBotUserApi {
|
||||||
obContext: NapCatOneBot11Adapter;
|
obContext: NapCatOneBot11Adapter;
|
||||||
@@ -10,4 +12,20 @@ export class OneBotUserApi {
|
|||||||
this.obContext = obContext;
|
this.obContext = obContext;
|
||||||
this.core = core;
|
this.core = core;
|
||||||
}
|
}
|
||||||
|
async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> {
|
||||||
|
const likeTip = profileLikeTip.decode(Uint8Array.from(wrappedBody.slice(12))) as unknown as ProfileLikeTipType;
|
||||||
|
this.core.context.logger.logDebug("收到点赞通知消息");
|
||||||
|
const likeMsg = likeTip.msg;
|
||||||
|
if (!likeMsg) return;
|
||||||
|
const detail = likeMsg.detail;
|
||||||
|
if (!detail) return;
|
||||||
|
const times = detail.txt.match(/\d+/) ?? "0";
|
||||||
|
return new OB11ProfileLikeEvent(
|
||||||
|
this.core,
|
||||||
|
Number(detail.uin),
|
||||||
|
detail.nickname,
|
||||||
|
parseInt(times[0], 10),
|
||||||
|
likeMsg.time,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -44,9 +44,7 @@ import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecal
|
|||||||
import { LRUCache } from '@/common/lru-cache';
|
import { LRUCache } from '@/common/lru-cache';
|
||||||
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
|
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
|
||||||
import { OB11ProfileLikeEvent } from './event/notice/OB11ProfileLikeEvent';
|
import { OB11ProfileLikeEvent } from './event/notice/OB11ProfileLikeEvent';
|
||||||
import { profileLikeTip, ProfileLikeTipType, SysMessage, SysMessageMsgSpecType, SysMessageType } from '@/core/proto/ProfileLike';
|
import { profileLikeTip, ProfileLikeTipType, SysMessage, SysMessageType } from '@/core/proto/ProfileLike';
|
||||||
import { Message } from 'protobufjs';
|
|
||||||
|
|
||||||
//OneBot实现类
|
//OneBot实现类
|
||||||
export class NapCatOneBot11Adapter {
|
export class NapCatOneBot11Adapter {
|
||||||
readonly core: NapCatCore;
|
readonly core: NapCatCore;
|
||||||
@@ -240,50 +238,10 @@ export class NapCatOneBot11Adapter {
|
|||||||
|
|
||||||
private initMsgListener() {
|
private initMsgListener() {
|
||||||
const msgListener = new NodeIKernelMsgListener();
|
const msgListener = new NodeIKernelMsgListener();
|
||||||
msgListener.onRecvSysMsg = async (msg) => {
|
msgListener.onRecvSysMsg = (msg) => {
|
||||||
const sysMsg = SysMessage.decode(Uint8Array.from(msg)) as unknown as SysMessageType;
|
this.apis.MsgApi.parseSysMessage(msg).then((event) => {
|
||||||
if (sysMsg.msgSpec.length === 0) {
|
if (event) this.networkManager.emitEvent(event);
|
||||||
return;
|
}).catch(e => this.context.logger.logError('constructSysMessage error: ', e));
|
||||||
}
|
|
||||||
const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
|
|
||||||
if (msgType === 528 && subType === 39 && subSubType === 39) {
|
|
||||||
const likeTip = profileLikeTip.decode(Uint8Array.from(sysMsg.bodyWrapper.wrappedBody.slice(12))) as unknown as ProfileLikeTipType;
|
|
||||||
this.core.context.logger.logDebug("收到点赞通知消息");
|
|
||||||
const likeMsg = likeTip.msg;
|
|
||||||
if (!likeMsg) return;
|
|
||||||
const detail = likeMsg.detail;
|
|
||||||
if (!detail) return;
|
|
||||||
const times = detail.txt.match(/\d+/) ?? "0";
|
|
||||||
await this.networkManager.emitEvent(new OB11ProfileLikeEvent(
|
|
||||||
this.core,
|
|
||||||
Number(detail.uin),
|
|
||||||
detail.nickname,
|
|
||||||
parseInt(times[0], 10),
|
|
||||||
likeMsg.time,
|
|
||||||
)).catch(e => this.context.logger.logError('处理被点赞事件失败', e));
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
if (msgType === 732 && subType === 16 && subSubType === 16) {
|
|
||||||
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
|
|
||||||
if (greyTip.subTypeId === 36) {
|
|
||||||
const emojiLikeToOthers = EmojiLikeToOthersWrapper1
|
|
||||||
.fromBinary(greyTip.rest)
|
|
||||||
.wrapper!
|
|
||||||
.body!;
|
|
||||||
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
|
|
||||||
greyTip.groupCode.toString(),
|
|
||||||
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
|
|
||||||
emojiLikeToOthers.msgSpec!.msgSeq.toString(),
|
|
||||||
emojiLikeToOthers.attributes!.emojiId,
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
||||||
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
msgListener.onInputStatusPush = async data => {
|
msgListener.onInputStatusPush = async data => {
|
||||||
|
@@ -47,7 +47,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
}
|
}
|
||||||
//鉴权
|
//鉴权
|
||||||
this.authorize(token, wsClient, wsReq);
|
this.authorize(token, wsClient, wsReq);
|
||||||
let paramUrl = wsReq.url?.indexOf('?') !== -1 ? wsReq.url?.substring(0, wsReq.url?.indexOf('?')) : wsReq.url;
|
const paramUrl = wsReq.url?.indexOf('?') !== -1 ? wsReq.url?.substring(0, wsReq.url?.indexOf('?')) : wsReq.url;
|
||||||
const isEventConnect = paramUrl === '/event' || paramUrl === '' || paramUrl === '/';
|
const isEventConnect = paramUrl === '/event' || paramUrl === '' || paramUrl === '/';
|
||||||
if (isEventConnect) {
|
if (isEventConnect) {
|
||||||
this.connectEvent(core, wsClient);
|
this.connectEvent(core, wsClient);
|
||||||
|
@@ -208,3 +208,8 @@ export interface OB11PostSendMsg {
|
|||||||
messages?: OB11MessageMixType; // 兼容 go-cqhttp
|
messages?: OB11MessageMixType; // 兼容 go-cqhttp
|
||||||
auto_escape?: boolean | string
|
auto_escape?: boolean | string
|
||||||
}
|
}
|
||||||
|
export interface OB11PostContext {
|
||||||
|
message_type?: 'private' | 'group'
|
||||||
|
user_id?: string,
|
||||||
|
group_id?: string,
|
||||||
|
}
|
||||||
|
@@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
undefined,
|
undefined,
|
||||||
SettingButton('V2.4.5', 'napcat-update-button', 'secondary'),
|
SettingButton('V2.4.7', 'napcat-update-button', 'secondary'),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
void 0,
|
void 0,
|
||||||
SettingButton("V2.4.5", "napcat-update-button", "secondary")
|
SettingButton("V2.4.7", "napcat-update-button", "secondary")
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
Reference in New Issue
Block a user