mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
122a9ca2cc | ||
![]() |
48aaddd32b | ||
![]() |
47401af856 | ||
![]() |
709adfd812 | ||
![]() |
038d0c5412 | ||
![]() |
6bb4362ed4 | ||
![]() |
e617f9452d | ||
![]() |
6d8bb49a37 | ||
![]() |
4f6073ee86 | ||
![]() |
2e7176304b | ||
![]() |
e36cf11004 | ||
![]() |
0e49e17f68 | ||
![]() |
524de45f6b | ||
![]() |
85741a4b60 | ||
![]() |
f9ccb8c978 | ||
![]() |
ea3d069e49 | ||
![]() |
3e6024f183 | ||
![]() |
337871693a | ||
![]() |
2d921c4577 | ||
![]() |
9accff7323 | ||
![]() |
88b1ee8c31 | ||
![]() |
3ac618bb4e | ||
![]() |
0051df3741 | ||
![]() |
7eb4e010b0 | ||
![]() |
33cc23ada3 | ||
![]() |
e5aee372e3 | ||
![]() |
6b6ce4a761 | ||
![]() |
8c4ea7f8f2 | ||
![]() |
c8b268b806 | ||
![]() |
cf5e0e0f14 | ||
![]() |
7b79f9cc17 | ||
![]() |
708d599966 | ||
![]() |
1ecd5b78e6 | ||
![]() |
fca2e3c51a | ||
![]() |
95ea761b2d | ||
![]() |
6b3bfa1ee9 | ||
![]() |
df3e302a9d | ||
![]() |
c88a68c9a8 | ||
![]() |
92d01b9cdd | ||
![]() |
fe04fa5986 | ||
![]() |
c382f541b4 | ||
![]() |
f420527207 | ||
![]() |
e0c83ebf79 | ||
![]() |
c7fb18fc08 | ||
![]() |
2db8ab937d | ||
![]() |
819f5dd8e5 | ||
![]() |
d4a8ed735e | ||
![]() |
f07e3bb4d5 | ||
![]() |
fa5ef0c221 | ||
![]() |
da7499ec0b | ||
![]() |
d2f4327e44 | ||
![]() |
29ae55f340 |
115
.vscode/launch.json
vendored
Normal file
115
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "dev:shell",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"dev:shell"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "build:shell",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"build:shell"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "build:universal",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"build:universal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "build:framework",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"build:framework"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "build:webui",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"build:webui"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "dev:universal",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"dev:universal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "dev:framework",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"dev:framework"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "dev:webui",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"dev:webui"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "lint",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"lint"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "depend",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"depend"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "dev:depend",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"run",
|
||||||
|
"dev:depend"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "4.5.20",
|
"version": "4.6.2",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -1,19 +1,21 @@
|
|||||||
import { PlayMode } from '@/const/enum'
|
import { PlayMode } from '@/const/enum'
|
||||||
|
|
||||||
|
import WebUIManager from '@/controllers/webui_manager'
|
||||||
import type {
|
import type {
|
||||||
FinalMusic,
|
FinalMusic,
|
||||||
Music163ListResponse,
|
Music163ListResponse,
|
||||||
Music163URLResponse
|
Music163URLResponse
|
||||||
} from '@/types/music'
|
} from '@/types/music'
|
||||||
|
|
||||||
import WebUIManager from '@/controllers/webui_manager'
|
|
||||||
/**
|
/**
|
||||||
* 获取网易云音乐歌单
|
* 获取网易云音乐歌单
|
||||||
* @param id 歌单id
|
* @param id 歌单id
|
||||||
* @returns 歌单信息
|
* @returns 歌单信息
|
||||||
*/
|
*/
|
||||||
export const get163MusicList = async (id: string) => {
|
export const get163MusicList = async (id: string) => {
|
||||||
let res = await WebUIManager.proxy<Music163ListResponse>('https://wavesgame.top/playlist/track/all?id=' + id);
|
let res = await WebUIManager.proxy<Music163ListResponse>(
|
||||||
|
'https://wavesgame.top/playlist/track/all?id=' + id
|
||||||
|
)
|
||||||
// const res = await request.get<Music163ListResponse>(
|
// const res = await request.get<Music163ListResponse>(
|
||||||
// `https://wavesgame.top/playlist/track/all?id=${id}`
|
// `https://wavesgame.top/playlist/track/all?id=${id}`
|
||||||
// )
|
// )
|
||||||
@@ -71,7 +73,7 @@ export const get163MusicListSongs = async (id: string) => {
|
|||||||
if (songURL) {
|
if (songURL) {
|
||||||
finalMusic.push({
|
finalMusic.push({
|
||||||
id: song.id,
|
id: song.id,
|
||||||
url: songURL,
|
url: songURL.replace(/http:\/\//, '//').replace(/https:\/\//, '//'),
|
||||||
title: song.name,
|
title: song.name,
|
||||||
artist: song.ar.map((p) => p.name).join('/'),
|
artist: song.ar.map((p) => p.name).join('/'),
|
||||||
cover: song.al.picUrl
|
cover: song.al.picUrl
|
||||||
|
12
package.json
12
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.5.20",
|
"version": "4.6.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
|
"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",
|
||||||
@@ -43,14 +43,14 @@
|
|||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
"commander": "^13.0.0",
|
"commander": "^13.0.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"esbuild": "0.24.0",
|
"esbuild": "0.25.0",
|
||||||
"eslint": "^9.14.0",
|
"eslint": "^9.14.0",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"express-rate-limit": "^7.5.0",
|
"express-rate-limit": "^7.5.0",
|
||||||
"fast-xml-parser": "^4.3.6",
|
"fast-xml-parser": "^4.3.6",
|
||||||
"file-type": "^20.0.0",
|
"file-type": "^20.0.0",
|
||||||
"globals": "^15.12.0",
|
"globals": "^16.0.0",
|
||||||
"image-size": "^1.1.1",
|
"image-size": "^1.1.1",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
@@ -59,13 +59,13 @@
|
|||||||
"vite": "^6.0.1",
|
"vite": "^6.0.1",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-tsconfig-paths": "^5.1.0",
|
"vite-tsconfig-paths": "^5.1.0",
|
||||||
"winston": "^3.17.0"
|
"napcat.protobuf": "^1.1.3",
|
||||||
|
"winston": "^3.17.0",
|
||||||
|
"compressing": "^1.10.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ffmpeg.wasm/core-mt": "^0.13.2",
|
"@ffmpeg.wasm/core-mt": "^0.13.2",
|
||||||
"compressing": "^1.10.1",
|
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
"piscina": "^4.7.0",
|
|
||||||
"silk-wasm": "^3.6.1",
|
"silk-wasm": "^3.6.1",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,20 @@
|
|||||||
import { encode } from 'silk-wasm';
|
import { encode } from 'silk-wasm';
|
||||||
|
import { parentPort } from 'worker_threads';
|
||||||
|
|
||||||
export interface EncodeArgs {
|
export interface EncodeArgs {
|
||||||
input: ArrayBufferView | ArrayBuffer
|
input: ArrayBufferView | ArrayBuffer
|
||||||
sampleRate: number
|
sampleRate: number
|
||||||
}
|
}
|
||||||
export default async ({ input, sampleRate }: EncodeArgs) => {
|
export function recvTask<T>(cb: (taskData: T) => Promise<unknown>) {
|
||||||
|
parentPort?.on('message', async (taskData: T) => {
|
||||||
|
try {
|
||||||
|
let ret = await cb(taskData);
|
||||||
|
parentPort?.postMessage(ret);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
parentPort?.postMessage({ error: (error as Error).message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
recvTask<EncodeArgs>(async ({ input, sampleRate }) => {
|
||||||
return await encode(input, sampleRate);
|
return await encode(input, sampleRate);
|
||||||
};
|
});
|
@@ -1,4 +1,3 @@
|
|||||||
import Piscina from 'piscina';
|
|
||||||
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';
|
||||||
@@ -6,16 +5,16 @@ import { EncodeResult, getDuration, getWavFileInfo, isSilk, isWav } from 'silk-w
|
|||||||
import { LogWrapper } from '@/common/log';
|
import { LogWrapper } from '@/common/log';
|
||||||
import { EncodeArgs } from '@/common/audio-worker';
|
import { EncodeArgs } from '@/common/audio-worker';
|
||||||
import { FFmpegService } from '@/common/ffmpeg';
|
import { FFmpegService } from '@/common/ffmpeg';
|
||||||
|
import { runTask } from './worker';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
const ALLOW_SAMPLE_RATE = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
const ALLOW_SAMPLE_RATE = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
||||||
|
|
||||||
async function getWorkerPath() {
|
function getWorkerPath() {
|
||||||
return new URL(/* @vite-ignore */ './audio-worker.mjs', import.meta.url).href;
|
//return new URL(/* @vite-ignore */ './audio-worker.mjs', import.meta.url).href;
|
||||||
|
return path.join(path.dirname(fileURLToPath(import.meta.url)), 'audio-worker.mjs');
|
||||||
}
|
}
|
||||||
|
|
||||||
const piscina = new Piscina<EncodeArgs, EncodeResult>({
|
|
||||||
filename: await getWorkerPath(),
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
||||||
@@ -46,7 +45,7 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
|
|||||||
const { input, sampleRate } = isWav(file)
|
const { input, sampleRate } = isWav(file)
|
||||||
? await handleWavFile(file, filePath, pcmPath)
|
? await handleWavFile(file, filePath, pcmPath)
|
||||||
: { input: await FFmpegService.convert(filePath, pcmPath), sampleRate: 24000 };
|
: { input: await FFmpegService.convert(filePath, pcmPath), sampleRate: 24000 };
|
||||||
const silk = await piscina.run({ input: input, sampleRate: sampleRate });
|
const silk = await runTask<EncodeArgs, EncodeResult>(getWorkerPath(), { input: input, sampleRate: sampleRate });
|
||||||
fsPromise.unlink(pcmPath).catch((e) => logger.logError('删除临时文件失败', pcmPath, e));
|
fsPromise.unlink(pcmPath).catch((e) => logger.logError('删除临时文件失败', pcmPath, e));
|
||||||
await fsPromise.writeFile(pttPath, Buffer.from(silk.data));
|
await fsPromise.writeFile(pttPath, Buffer.from(silk.data));
|
||||||
logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);
|
logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);
|
||||||
|
@@ -5,6 +5,17 @@ import { readFileSync, statSync, writeFileSync } from 'fs';
|
|||||||
import type { VideoInfo } from './video';
|
import type { VideoInfo } from './video';
|
||||||
import { fileTypeFromFile } from 'file-type';
|
import { fileTypeFromFile } from 'file-type';
|
||||||
import imageSize from 'image-size';
|
import imageSize from 'image-size';
|
||||||
|
import { parentPort } from 'worker_threads';
|
||||||
|
export function recvTask<T>(cb: (taskData: T) => Promise<unknown>) {
|
||||||
|
parentPort?.on('message', async (taskData: T) => {
|
||||||
|
try {
|
||||||
|
let ret = await cb(taskData);
|
||||||
|
parentPort?.postMessage(ret);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
parentPort?.postMessage({ error: (error as Error).message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
class FFmpegService {
|
class FFmpegService {
|
||||||
public static async extractThumbnail(videoPath: string, thumbnailPath: string): Promise<void> {
|
public static async extractThumbnail(videoPath: string, thumbnailPath: string): Promise<void> {
|
||||||
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
|
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
|
||||||
@@ -149,3 +160,6 @@ export default async function handleFFmpegTask({ method, args }: FFmpegTask): Pr
|
|||||||
throw new Error(`Unknown method: ${method}`);
|
throw new Error(`Unknown method: ${method}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
recvTask<FFmpegTask>(async ({ method, args }: FFmpegTask) => {
|
||||||
|
return await handleFFmpegTask({ method, args });
|
||||||
|
});
|
@@ -1,6 +1,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import Piscina from 'piscina';
|
|
||||||
import { VideoInfo } from './video';
|
import { VideoInfo } from './video';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { runTask } from './worker';
|
||||||
|
|
||||||
type EncodeArgs = {
|
type EncodeArgs = {
|
||||||
method: 'extractThumbnail' | 'convertFile' | 'convert' | 'getVideoInfo';
|
method: 'extractThumbnail' | 'convertFile' | 'convert' | 'getVideoInfo';
|
||||||
@@ -9,42 +11,26 @@ type EncodeArgs = {
|
|||||||
|
|
||||||
type EncodeResult = any;
|
type EncodeResult = any;
|
||||||
|
|
||||||
async function getWorkerPath() {
|
function getWorkerPath() {
|
||||||
return new URL(/* @vite-ignore */ './ffmpeg-worker.mjs', import.meta.url).href;
|
return path.join(path.dirname(fileURLToPath(import.meta.url)), './ffmpeg-worker.mjs');
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FFmpegService {
|
export class FFmpegService {
|
||||||
public static async extractThumbnail(videoPath: string, thumbnailPath: string): Promise<void> {
|
public static async extractThumbnail(videoPath: string, thumbnailPath: string): Promise<void> {
|
||||||
const piscina = new Piscina<EncodeArgs, EncodeResult>({
|
await runTask<EncodeArgs, EncodeResult>(getWorkerPath(), { method: 'extractThumbnail', args: [videoPath, thumbnailPath] });
|
||||||
filename: await getWorkerPath(),
|
|
||||||
});
|
|
||||||
await piscina.run({ method: 'extractThumbnail', args: [videoPath, thumbnailPath] });
|
|
||||||
await piscina.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async convertFile(inputFile: string, outputFile: string, format: string): Promise<void> {
|
public static async convertFile(inputFile: string, outputFile: string, format: string): Promise<void> {
|
||||||
const piscina = new Piscina<EncodeArgs, EncodeResult>({
|
await runTask<EncodeArgs, EncodeResult>(getWorkerPath(), { method: 'convertFile', args: [inputFile, outputFile, format] });
|
||||||
filename: await getWorkerPath(),
|
|
||||||
});
|
|
||||||
await piscina.run({ method: 'convertFile', args: [inputFile, outputFile, format] });
|
|
||||||
await piscina.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async convert(filePath: string, pcmPath: string): Promise<Buffer> {
|
public static async convert(filePath: string, pcmPath: string): Promise<Buffer> {
|
||||||
const piscina = new Piscina<EncodeArgs, EncodeResult>({
|
const result = await runTask<EncodeArgs, EncodeResult>(getWorkerPath(), { method: 'convert', args: [filePath, pcmPath] });
|
||||||
filename: await getWorkerPath(),
|
|
||||||
});
|
|
||||||
const result = await piscina.run({ method: 'convert', args: [filePath, pcmPath] });
|
|
||||||
await piscina.destroy();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getVideoInfo(videoPath: string, thumbnailPath: string): Promise<VideoInfo> {
|
public static async getVideoInfo(videoPath: string, thumbnailPath: string): Promise<VideoInfo> {
|
||||||
const piscina = new Piscina<EncodeArgs, EncodeResult>({
|
const result = await await runTask<EncodeArgs, EncodeResult>(getWorkerPath(), { method: 'getVideoInfo', args: [videoPath, thumbnailPath] });
|
||||||
filename: await getWorkerPath(),
|
|
||||||
});
|
|
||||||
const result = await piscina.run({ method: 'getVideoInfo', args: [videoPath, thumbnailPath] });
|
|
||||||
await piscina.destroy();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -232,7 +232,7 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
|||||||
tokens.push(`群聊 [${msg.peerName}(${msg.peerUin})]`);
|
tokens.push(`群聊 [${msg.peerName}(${msg.peerUin})]`);
|
||||||
}
|
}
|
||||||
if (msg.senderUin !== '0') {
|
if (msg.senderUin !== '0') {
|
||||||
tokens.push(`[${msg.sendMemberName ?? msg.sendRemarkName ?? msg.sendNickName}(${msg.senderUin})]`);
|
tokens.push(`[${msg.sendMemberName || msg.sendRemarkName || msg.sendNickName}(${msg.senderUin})]`);
|
||||||
}
|
}
|
||||||
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
|
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
|
||||||
tokens.push('移动设备');
|
tokens.push('移动设备');
|
||||||
|
@@ -163,7 +163,7 @@ class Store {
|
|||||||
const current = this.get<StoreValueType>(key);
|
const current = this.get<StoreValueType>(key);
|
||||||
|
|
||||||
if (current === null) {
|
if (current === null) {
|
||||||
this.set(key, 1);
|
this.set(key, 1, 60);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ class Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newValue = numericValue + 1;
|
const newValue = numericValue + 1;
|
||||||
this.set(key, newValue);
|
this.set(key, newValue, 60);
|
||||||
return newValue;
|
return newValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '4.5.20';
|
export const napCatVersion = '4.6.2';
|
||||||
|
29
src/common/worker.ts
Normal file
29
src/common/worker.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Worker } from 'worker_threads';
|
||||||
|
|
||||||
|
export async function runTask<T, R>(workerScript: string, taskData: T): Promise<R> {
|
||||||
|
let worker = new Worker(workerScript);
|
||||||
|
try {
|
||||||
|
return await new Promise<R>((resolve, reject) => {
|
||||||
|
worker.on('message', (result: R) => {
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.on('error', (error) => {
|
||||||
|
reject(new Error(`Worker error: ${error.message}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.on('exit', (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
reject(new Error(`Worker stopped with exit code ${code}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
worker.postMessage(taskData);
|
||||||
|
});
|
||||||
|
} catch (error: unknown) {
|
||||||
|
throw new Error(`Failed to run task: ${(error as Error).message}`);
|
||||||
|
} finally {
|
||||||
|
// Ensure the worker is terminated after the promise is settled
|
||||||
|
worker.terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -41,7 +41,8 @@ export class NTQQFileApi {
|
|||||||
this.context = context;
|
this.context = context;
|
||||||
this.core = core;
|
this.core = core;
|
||||||
this.rkeyManager = new RkeyManager([
|
this.rkeyManager = new RkeyManager([
|
||||||
'https://rkey.napneko.icu/rkeys'
|
'https://ss.xingzhige.com/music_card/rkey', // 国内
|
||||||
|
'https://secret-service.bietiaop.com/rkeys',//国内
|
||||||
],
|
],
|
||||||
this.context.logger
|
this.context.logger
|
||||||
);
|
);
|
||||||
|
@@ -165,7 +165,13 @@ export class NTQQGroupApi {
|
|||||||
|
|
||||||
return this.groupMemberCache.get(groupCode);
|
return this.groupMemberCache.get(groupCode);
|
||||||
}
|
}
|
||||||
|
async refreshGroupMemberCachePartial(groupCode: string, uid: string) {
|
||||||
|
const member = await this.getGroupMemberEx(groupCode, uid, true);
|
||||||
|
if (member) {
|
||||||
|
this.groupMemberCache.get(groupCode)?.set(uid, member);
|
||||||
|
}
|
||||||
|
return member;
|
||||||
|
}
|
||||||
async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) {
|
async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) {
|
||||||
const groupCodeStr = groupCode.toString();
|
const groupCodeStr = groupCode.toString();
|
||||||
const memberUinOrUidStr = memberUinOrUid.toString();
|
const memberUinOrUidStr = memberUinOrUid.toString();
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement, SendStatusType } from '@/core/types';
|
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement, SendStatusType } from '@/core/types';
|
||||||
import { GroupFileInfoUpdateItem, InstanceContext, NapCatCore } from '@/core';
|
import { GroupFileInfoUpdateItem, InstanceContext, NapCatCore, NodeIKernelMsgService } from '@/core';
|
||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
export class NTQQMsgApi {
|
export class NTQQMsgApi {
|
||||||
@@ -12,6 +12,11 @@ export class NTQQMsgApi {
|
|||||||
this.context = context;
|
this.context = context;
|
||||||
this.core = core;
|
this.core = core;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clickInlineKeyboardButton(...params: Parameters<NodeIKernelMsgService['clickInlineKeyboardButton']>) {
|
||||||
|
return this.context.session.getMsgService().clickInlineKeyboardButton(...params);
|
||||||
|
}
|
||||||
|
|
||||||
getMsgByClientSeqAndTime(peer: Peer, replyMsgClientSeq: string, replyMsgTime: string) {
|
getMsgByClientSeqAndTime(peer: Peer, replyMsgClientSeq: string, replyMsgTime: string) {
|
||||||
// https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType 可以用过特殊方式拉取
|
// https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType 可以用过特殊方式拉取
|
||||||
return this.context.session.getMsgService().getMsgByClientSeqAndTime(peer, replyMsgClientSeq, replyMsgTime);
|
return this.context.session.getMsgService().getMsgByClientSeqAndTime(peer, replyMsgClientSeq, replyMsgTime);
|
||||||
@@ -131,6 +136,20 @@ export class NTQQMsgApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async queryFirstMsgBySender(peer: Peer, SendersUid: string[]) {
|
||||||
|
console.log(peer, SendersUid);
|
||||||
|
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: peer,
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: SendersUid,
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: true,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 20000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async setMsgRead(peer: Peer) {
|
async setMsgRead(peer: Peer) {
|
||||||
return this.context.session.getMsgService().setMsgRead(peer);
|
return this.context.session.getMsgService().setMsgRead(peer);
|
||||||
}
|
}
|
||||||
|
16
src/core/external/appid.json
vendored
16
src/core/external/appid.json
vendored
@@ -186,5 +186,21 @@
|
|||||||
"9.9.17-31363": {
|
"9.9.17-31363": {
|
||||||
"appid": 537266500,
|
"appid": 537266500,
|
||||||
"qua": "V1_WIN_NQ_9.9.17_31363_GW_B"
|
"qua": "V1_WIN_NQ_9.9.17_31363_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.16-32690": {
|
||||||
|
"appid": 537271229,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.16_32690_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.18-32690": {
|
||||||
|
"appid": 537271194,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.18_32690_GW_B"
|
||||||
|
},
|
||||||
|
"6.9.66-32690": {
|
||||||
|
"appid": 537271218,
|
||||||
|
"qua": "V1_MAC_NQ_6.9.66_32690_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.16-32721": {
|
||||||
|
"appid": 537271229,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.16_32721_GW_B"
|
||||||
}
|
}
|
||||||
}
|
}
|
5
src/core/external/napcat.json
vendored
5
src/core/external/napcat.json
vendored
@@ -4,5 +4,6 @@
|
|||||||
"fileLogLevel": "debug",
|
"fileLogLevel": "debug",
|
||||||
"consoleLogLevel": "info",
|
"consoleLogLevel": "info",
|
||||||
"packetBackend": "auto",
|
"packetBackend": "auto",
|
||||||
"packetServer": ""
|
"packetServer": "",
|
||||||
}
|
"o3HookMode": 1
|
||||||
|
}
|
22
src/core/external/offset.json
vendored
22
src/core/external/offset.json
vendored
@@ -175,7 +175,7 @@
|
|||||||
"send": "713A318",
|
"send": "713A318",
|
||||||
"recv": "713DB50"
|
"recv": "713DB50"
|
||||||
},
|
},
|
||||||
"6.9.63.30851-x64": {
|
"6.9.63-30851-x64": {
|
||||||
"send": "46C8040",
|
"send": "46C8040",
|
||||||
"recv": "46CA8AC"
|
"recv": "46CA8AC"
|
||||||
},
|
},
|
||||||
@@ -246,5 +246,25 @@
|
|||||||
"6.9.65-31363-arm64": {
|
"6.9.65-31363-arm64": {
|
||||||
"send": "422CEF8",
|
"send": "422CEF8",
|
||||||
"recv": "422F710"
|
"recv": "422F710"
|
||||||
|
},
|
||||||
|
"9.9.18-32690-x64": {
|
||||||
|
"send": "39F9630",
|
||||||
|
"recv": "39FDE30"
|
||||||
|
},
|
||||||
|
"3.2.16-32690-x64": {
|
||||||
|
"send": "A5E24C0",
|
||||||
|
"recv": "A5E5EE0"
|
||||||
|
},
|
||||||
|
"3.2.16-32690-arm64": {
|
||||||
|
"send": "7226630",
|
||||||
|
"recv": "7229F60"
|
||||||
|
},
|
||||||
|
"3.2.16-32721-x64": {
|
||||||
|
"send": "A5E24C0",
|
||||||
|
"recv": "A5E5EE0"
|
||||||
|
},
|
||||||
|
"3.2.16-32721-arm64": {
|
||||||
|
"send": "7226630",
|
||||||
|
"recv": "7229F60"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -10,6 +10,7 @@ export const NapcatConfigSchema = Type.Object({
|
|||||||
consoleLogLevel: Type.String({ default: 'info' }),
|
consoleLogLevel: Type.String({ default: 'info' }),
|
||||||
packetBackend: Type.String({ default: 'auto' }),
|
packetBackend: Type.String({ default: 'auto' }),
|
||||||
packetServer: Type.String({ default: '' }),
|
packetServer: Type.String({ default: '' }),
|
||||||
|
o3HookMode: Type.Number({ default: 0 }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type NapcatConfig = Static<typeof NapcatConfigSchema>;
|
export type NapcatConfig = Static<typeof NapcatConfigSchema>;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChatType } from '@/core';
|
import { ChatType, RawMessage } from '@/core';
|
||||||
export interface SearchGroupInfo {
|
export interface SearchGroupInfo {
|
||||||
groupCode: string;
|
groupCode: string;
|
||||||
ownerUid: string;
|
ownerUid: string;
|
||||||
@@ -94,4 +94,27 @@ export interface NodeIKernelSearchListener {
|
|||||||
}[]
|
}[]
|
||||||
}[]
|
}[]
|
||||||
}): any;
|
}): any;
|
||||||
|
|
||||||
|
onSearchMsgKeywordsResult(params: {
|
||||||
|
searchId: string,
|
||||||
|
hasMore: boolean,
|
||||||
|
resultItems: Array<{
|
||||||
|
msgId: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
senderUid: string,
|
||||||
|
senderUin: string,
|
||||||
|
senderNick: string,
|
||||||
|
senderNickHits: unknown[],
|
||||||
|
senderRemark: string,
|
||||||
|
senderRemarkHits: unknown[],
|
||||||
|
senderCard: string,
|
||||||
|
senderCardHits: unknown[],
|
||||||
|
fieldType: number,
|
||||||
|
fieldText: string,
|
||||||
|
msgRecord: RawMessage;
|
||||||
|
hitsInfo: Array<unknown>,
|
||||||
|
msgAbstract: unknown,
|
||||||
|
}>
|
||||||
|
}): void | Promise<void>;
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@ import { PacketLogger } from '@/core/packet/context/loggerContext';
|
|||||||
|
|
||||||
// 0 send 1 recv
|
// 0 send 1 recv
|
||||||
export interface NativePacketExportType {
|
export interface NativePacketExportType {
|
||||||
InitHook?: (send: string, recv: string, callback: (type: number, uin: string, cmd: string, seq: number, hex_data: string) => void) => boolean;
|
InitHook?: (send: string, recv: string, callback: (type: number, uin: string, cmd: string, seq: number, hex_data: string) => void, o3_hook: boolean) => boolean;
|
||||||
SendPacket?: (cmd: string, data: string, trace_id: string) => void;
|
SendPacket?: (cmd: string, data: string, trace_id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ export class NativePacketClient extends IPacketClient {
|
|||||||
const platform = process.platform + '.' + process.arch;
|
const platform = process.platform + '.' + process.arch;
|
||||||
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node');
|
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node');
|
||||||
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
|
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
|
||||||
|
|
||||||
this.MoeHooExport.exports.InitHook?.(send, recv, (type: number, uin: string, cmd: string, seq: number, hex_data: string) => {
|
this.MoeHooExport.exports.InitHook?.(send, recv, (type: number, uin: string, cmd: string, seq: number, hex_data: string) => {
|
||||||
const trace_id = createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex');
|
const trace_id = createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex');
|
||||||
if (type === 0 && this.cb.get(trace_id + 'recv')) {
|
if (type === 0 && this.cb.get(trace_id + 'recv')) {
|
||||||
@@ -55,7 +56,7 @@ export class NativePacketClient extends IPacketClient {
|
|||||||
// console.log('callback:', callback, trace_id);
|
// console.log('callback:', callback, trace_id);
|
||||||
callback?.({ seq, cmd, hex_data });
|
callback?.({ seq, cmd, hex_data });
|
||||||
}
|
}
|
||||||
});
|
}, this.napcore.config.o3HookMode == 1);
|
||||||
this.available = true;
|
this.available = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,22 +1,22 @@
|
|||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import {PacketContext} from '@/core/packet/context/packetContext';
|
import { PacketContext } from '@/core/packet/context/packetContext';
|
||||||
import * as trans from '@/core/packet/transformer';
|
import * as trans from '@/core/packet/transformer';
|
||||||
import {PacketMsg} from '@/core/packet/message/message';
|
import { PacketMsg } from '@/core/packet/message/message';
|
||||||
import {
|
import {
|
||||||
PacketMsgFileElement,
|
PacketMsgFileElement,
|
||||||
PacketMsgPicElement,
|
PacketMsgPicElement,
|
||||||
PacketMsgPttElement,
|
PacketMsgPttElement,
|
||||||
PacketMsgVideoElement
|
PacketMsgVideoElement
|
||||||
} from '@/core/packet/message/element';
|
} from '@/core/packet/message/element';
|
||||||
import {ChatType, MsgSourceType, NTMsgType, RawMessage} from '@/core';
|
import { ChatType, MsgSourceType, NTMsgType, RawMessage } from '@/core';
|
||||||
import {MiniAppRawData, MiniAppReqParams} from '@/core/packet/entities/miniApp';
|
import { MiniAppRawData, MiniAppReqParams } from '@/core/packet/entities/miniApp';
|
||||||
import {AIVoiceChatType} from '@/core/packet/entities/aiChat';
|
import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
|
||||||
import {NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg} from '@napneko/nap-proto-core';
|
import { NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
import {IndexNode, LongMsgResult, MsgInfo} from '@/core/packet/transformer/proto';
|
import { IndexNode, LongMsgResult, MsgInfo } from '@/core/packet/transformer/proto';
|
||||||
import {OidbPacket} from '@/core/packet/transformer/base';
|
import { OidbPacket } from '@/core/packet/transformer/base';
|
||||||
import {ImageOcrResult} from '@/core/packet/entities/ocrResult';
|
import { ImageOcrResult } from '@/core/packet/entities/ocrResult';
|
||||||
import {gunzipSync} from 'zlib';
|
import { gunzipSync } from 'zlib';
|
||||||
import {PacketMsgConverter} from '@/core/packet/message/converter';
|
import { PacketMsgConverter } from '@/core/packet/message/converter';
|
||||||
|
|
||||||
export class PacketOperationContext {
|
export class PacketOperationContext {
|
||||||
private readonly context: PacketContext;
|
private readonly context: PacketContext;
|
||||||
@@ -59,10 +59,10 @@ export class PacketOperationContext {
|
|||||||
const res = trans.GetStrangerInfo.parse(resp);
|
const res = trans.GetStrangerInfo.parse(resp);
|
||||||
const extBigInt = BigInt(res.data.status.value);
|
const extBigInt = BigInt(res.data.status.value);
|
||||||
if (extBigInt <= 10n) {
|
if (extBigInt <= 10n) {
|
||||||
return {status: Number(extBigInt) * 10, ext_status: 0};
|
return { status: Number(extBigInt) * 10, ext_status: 0 };
|
||||||
}
|
}
|
||||||
status = Number((extBigInt & 0xff00n) + ((extBigInt >> 16n) & 0xffn));
|
status = Number((extBigInt & 0xff00n) + ((extBigInt >> 16n) & 0xffn));
|
||||||
return {status: 10, ext_status: status};
|
return { status: 10, ext_status: status };
|
||||||
} catch {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -79,13 +79,13 @@ export class PacketOperationContext {
|
|||||||
const reqList = msg.flatMap(m =>
|
const reqList = msg.flatMap(m =>
|
||||||
m.msg.map(e => {
|
m.msg.map(e => {
|
||||||
if (e instanceof PacketMsgPicElement) {
|
if (e instanceof PacketMsgPicElement) {
|
||||||
return this.context.highway.uploadImage({chatType, peerUid}, e);
|
return this.context.highway.uploadImage({ chatType, peerUid }, e);
|
||||||
} else if (e instanceof PacketMsgVideoElement) {
|
} else if (e instanceof PacketMsgVideoElement) {
|
||||||
return this.context.highway.uploadVideo({chatType, peerUid}, e);
|
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
|
||||||
} else if (e instanceof PacketMsgPttElement) {
|
} else if (e instanceof PacketMsgPttElement) {
|
||||||
return this.context.highway.uploadPtt({chatType, peerUid}, e);
|
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
|
||||||
} else if (e instanceof PacketMsgFileElement) {
|
} else if (e instanceof PacketMsgFileElement) {
|
||||||
return this.context.highway.uploadFile({chatType, peerUid}, e);
|
return this.context.highway.uploadFile({ chatType, peerUid }, e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}).filter(Boolean)
|
}).filter(Boolean)
|
||||||
@@ -160,6 +160,12 @@ export class PacketOperationContext {
|
|||||||
const res = trans.DownloadGroupFile.parse(resp);
|
const res = trans.DownloadGroupFile.parse(resp);
|
||||||
return `https://${res.download.downloadDns}/ftn_handler/${Buffer.from(res.download.downloadUrl).toString('hex')}/?fname=`;
|
return `https://${res.download.downloadDns}/ftn_handler/${Buffer.from(res.download.downloadUrl).toString('hex')}/?fname=`;
|
||||||
}
|
}
|
||||||
|
async GetPrivateFileUrl(self_id: string, fileUUID: string, md5: string) {
|
||||||
|
const req = trans.DownloadPrivateFile.build(self_id, fileUUID, md5);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.DownloadPrivateFile.parse(resp);
|
||||||
|
return `http://${res.body?.result?.server}:${res.body?.result?.port}${res.body?.result?.url?.slice(8)}&isthumb=0`;
|
||||||
|
}
|
||||||
|
|
||||||
async GetGroupPttUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
async GetGroupPttUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||||
const req = trans.DownloadGroupPtt.build(groupUin, node);
|
const req = trans.DownloadGroupPtt.build(groupUin, node);
|
||||||
|
@@ -144,7 +144,7 @@ export class PacketHighwayContext {
|
|||||||
const ukey = preRespData.upload.uKey;
|
const ukey = preRespData.upload.uKey;
|
||||||
if (ukey && ukey != '') {
|
if (ukey && ukey != '') {
|
||||||
this.logger.debug(`[Highway] uploadGroupImageReq get upload ukey: ${ukey}, need upload!`);
|
this.logger.debug(`[Highway] uploadGroupImageReq get upload ukey: ${ukey}, need upload!`);
|
||||||
const index = preRespData.upload.msgInfo.msgInfoBody[0].index;
|
const index = preRespData.upload.msgInfo.msgInfoBody[0]!.index;
|
||||||
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
||||||
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
||||||
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
||||||
@@ -181,7 +181,7 @@ export class PacketHighwayContext {
|
|||||||
const ukey = preRespData.upload.uKey;
|
const ukey = preRespData.upload.uKey;
|
||||||
if (ukey && ukey != '') {
|
if (ukey && ukey != '') {
|
||||||
this.logger.debug(`[Highway] uploadC2CImageReq get upload ukey: ${ukey}, need upload!`);
|
this.logger.debug(`[Highway] uploadC2CImageReq get upload ukey: ${ukey}, need upload!`);
|
||||||
const index = preRespData.upload.msgInfo.msgInfoBody[0].index;
|
const index = preRespData.upload.msgInfo.msgInfoBody[0]!.index;
|
||||||
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
||||||
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
||||||
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
||||||
@@ -219,7 +219,7 @@ export class PacketHighwayContext {
|
|||||||
const ukey = preRespData.upload.uKey;
|
const ukey = preRespData.upload.uKey;
|
||||||
if (ukey && ukey != '') {
|
if (ukey && ukey != '') {
|
||||||
this.logger.debug(`[Highway] uploadGroupVideoReq get upload video ukey: ${ukey}, need upload!`);
|
this.logger.debug(`[Highway] uploadGroupVideoReq get upload video ukey: ${ukey}, need upload!`);
|
||||||
const index = preRespData.upload.msgInfo.msgInfoBody[0].index;
|
const index = preRespData.upload.msgInfo.msgInfoBody[0]!.index;
|
||||||
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
||||||
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
||||||
fileUuid: index.fileUuid,
|
fileUuid: index.fileUuid,
|
||||||
@@ -244,16 +244,16 @@ export class PacketHighwayContext {
|
|||||||
this.logger.debug(`[Highway] uploadGroupVideoReq get upload invalid ukey ${ukey}, don't need upload!`);
|
this.logger.debug(`[Highway] uploadGroupVideoReq get upload invalid ukey ${ukey}, don't need upload!`);
|
||||||
}
|
}
|
||||||
const subFile = preRespData.upload.subFileInfos[0];
|
const subFile = preRespData.upload.subFileInfos[0];
|
||||||
if (subFile.uKey && subFile.uKey != '') {
|
if (subFile!.uKey && subFile!.uKey != '') {
|
||||||
this.logger.debug(`[Highway] uploadGroupVideoReq get upload video thumb ukey: ${subFile.uKey}, need upload!`);
|
this.logger.debug(`[Highway] uploadGroupVideoReq get upload video thumb ukey: ${subFile!.uKey}, need upload!`);
|
||||||
const index = preRespData.upload.msgInfo.msgInfoBody[1].index;
|
const index = preRespData.upload.msgInfo.msgInfoBody[1]!.index;
|
||||||
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
||||||
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
||||||
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
||||||
fileUuid: index.fileUuid,
|
fileUuid: index.fileUuid,
|
||||||
uKey: subFile.uKey,
|
uKey: subFile!.uKey,
|
||||||
network: {
|
network: {
|
||||||
ipv4S: oidbIpv4s2HighwayIpv4s(subFile.ipv4S)
|
ipv4S: oidbIpv4s2HighwayIpv4s(subFile!.ipv4S)
|
||||||
},
|
},
|
||||||
msgInfoBody: preRespData.upload.msgInfo.msgInfoBody,
|
msgInfoBody: preRespData.upload.msgInfo.msgInfoBody,
|
||||||
blockSize: BlockSize,
|
blockSize: BlockSize,
|
||||||
@@ -269,7 +269,7 @@ export class PacketHighwayContext {
|
|||||||
extend
|
extend
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`[Highway] uploadGroupVideoReq get upload invalid thumb ukey ${subFile.uKey}, don't need upload!`);
|
this.logger.debug(`[Highway] uploadGroupVideoReq get upload invalid thumb ukey ${subFile!.uKey}, don't need upload!`);
|
||||||
}
|
}
|
||||||
video.msgInfo = preRespData.upload.msgInfo;
|
video.msgInfo = preRespData.upload.msgInfo;
|
||||||
}
|
}
|
||||||
@@ -284,7 +284,7 @@ export class PacketHighwayContext {
|
|||||||
const ukey = preRespData.upload.uKey;
|
const ukey = preRespData.upload.uKey;
|
||||||
if (ukey && ukey != '') {
|
if (ukey && ukey != '') {
|
||||||
this.logger.debug(`[Highway] uploadC2CVideoReq get upload video ukey: ${ukey}, need upload!`);
|
this.logger.debug(`[Highway] uploadC2CVideoReq get upload video ukey: ${ukey}, need upload!`);
|
||||||
const index = preRespData.upload.msgInfo.msgInfoBody[0].index;
|
const index = preRespData.upload.msgInfo.msgInfoBody[0]!.index;
|
||||||
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
||||||
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
||||||
fileUuid: index.fileUuid,
|
fileUuid: index.fileUuid,
|
||||||
@@ -309,16 +309,16 @@ export class PacketHighwayContext {
|
|||||||
this.logger.debug(`[Highway] uploadC2CVideoReq get upload invalid ukey ${ukey}, don't need upload!`);
|
this.logger.debug(`[Highway] uploadC2CVideoReq get upload invalid ukey ${ukey}, don't need upload!`);
|
||||||
}
|
}
|
||||||
const subFile = preRespData.upload.subFileInfos[0];
|
const subFile = preRespData.upload.subFileInfos[0];
|
||||||
if (subFile.uKey && subFile.uKey != '') {
|
if (subFile!.uKey && subFile!.uKey != '') {
|
||||||
this.logger.debug(`[Highway] uploadC2CVideoReq get upload video thumb ukey: ${subFile.uKey}, need upload!`);
|
this.logger.debug(`[Highway] uploadC2CVideoReq get upload video thumb ukey: ${subFile!.uKey}, need upload!`);
|
||||||
const index = preRespData.upload.msgInfo.msgInfoBody[1].index;
|
const index = preRespData.upload.msgInfo.msgInfoBody[1]!.index;
|
||||||
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
||||||
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
||||||
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
||||||
fileUuid: index.fileUuid,
|
fileUuid: index.fileUuid,
|
||||||
uKey: subFile.uKey,
|
uKey: subFile!.uKey,
|
||||||
network: {
|
network: {
|
||||||
ipv4S: oidbIpv4s2HighwayIpv4s(subFile.ipv4S)
|
ipv4S: oidbIpv4s2HighwayIpv4s(subFile!.ipv4S)
|
||||||
},
|
},
|
||||||
msgInfoBody: preRespData.upload.msgInfo.msgInfoBody,
|
msgInfoBody: preRespData.upload.msgInfo.msgInfoBody,
|
||||||
blockSize: BlockSize,
|
blockSize: BlockSize,
|
||||||
@@ -334,7 +334,7 @@ export class PacketHighwayContext {
|
|||||||
extend
|
extend
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`[Highway] uploadC2CVideoReq get upload invalid thumb ukey ${subFile.uKey}, don't need upload!`);
|
this.logger.debug(`[Highway] uploadC2CVideoReq get upload invalid thumb ukey ${subFile!.uKey}, don't need upload!`);
|
||||||
}
|
}
|
||||||
video.msgInfo = preRespData.upload.msgInfo;
|
video.msgInfo = preRespData.upload.msgInfo;
|
||||||
}
|
}
|
||||||
@@ -347,7 +347,7 @@ export class PacketHighwayContext {
|
|||||||
const ukey = preRespData.upload.uKey;
|
const ukey = preRespData.upload.uKey;
|
||||||
if (ukey && ukey != '') {
|
if (ukey && ukey != '') {
|
||||||
this.logger.debug(`[Highway] uploadGroupPttReq get upload ptt ukey: ${ukey}, need upload!`);
|
this.logger.debug(`[Highway] uploadGroupPttReq get upload ptt ukey: ${ukey}, need upload!`);
|
||||||
const index = preRespData.upload.msgInfo.msgInfoBody[0].index;
|
const index = preRespData.upload.msgInfo.msgInfoBody[0]!.index;
|
||||||
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
||||||
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
||||||
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
||||||
@@ -383,7 +383,7 @@ export class PacketHighwayContext {
|
|||||||
const ukey = preRespData.upload.uKey;
|
const ukey = preRespData.upload.uKey;
|
||||||
if (ukey && ukey != '') {
|
if (ukey && ukey != '') {
|
||||||
this.logger.debug(`[Highway] uploadC2CPttReq get upload ptt ukey: ${ukey}, need upload!`);
|
this.logger.debug(`[Highway] uploadC2CPttReq get upload ptt ukey: ${ukey}, need upload!`);
|
||||||
const index = preRespData.upload.msgInfo.msgInfoBody[0].index;
|
const index = preRespData.upload.msgInfo.msgInfoBody[0]!.index;
|
||||||
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
const md5 = Buffer.from(index.info.fileHash, 'hex');
|
||||||
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
const sha1 = Buffer.from(index.info.fileSha1, 'hex');
|
||||||
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
const extend = new NapProtoMsg(proto.NTV2RichMediaHighwayExt).encode({
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { DownloadBaseEmojiByIdReq, DownloadBaseEmojiByUrlReq, GetBaseEmojiPathReq, PullSysEmojisReq } from '../types';
|
import { DownloadBaseEmojiByIdReq, DownloadBaseEmojiByUrlReq, GetBaseEmojiPathReq, PullSysEmojisReq } from '../types';
|
||||||
|
import { GeneralCallResult } from './common';
|
||||||
|
|
||||||
export interface NodeIKernelBaseEmojiService {
|
export interface NodeIKernelBaseEmojiService {
|
||||||
removeKernelBaseEmojiListener(listenerId: number): void;
|
removeKernelBaseEmojiListener(listenerId: number): void;
|
||||||
@@ -7,7 +8,26 @@ export interface NodeIKernelBaseEmojiService {
|
|||||||
|
|
||||||
isBaseEmojiPathExist(args: Array<string>): unknown;
|
isBaseEmojiPathExist(args: Array<string>): unknown;
|
||||||
|
|
||||||
fetchFullSysEmojis(pullSysEmojisReq: PullSysEmojisReq): unknown;
|
fetchFullSysEmojis(pullSysEmojisReq: PullSysEmojisReq): Promise<GeneralCallResult & {
|
||||||
|
rsp: {
|
||||||
|
otherPanelResult: {
|
||||||
|
SysEmojiGroupList: Array<unknown>,
|
||||||
|
downloadInfo: Array<unknown>
|
||||||
|
},
|
||||||
|
normalPanelResult: {
|
||||||
|
SysEmojiGroupList: Array<unknown>,
|
||||||
|
downloadInfo: Array<unknown>
|
||||||
|
},
|
||||||
|
superPanelResult: {
|
||||||
|
SysEmojiGroupList: Array<unknown>,
|
||||||
|
downloadInfo: Array<unknown>
|
||||||
|
},
|
||||||
|
redHeartPanelResult: {
|
||||||
|
SysEmojiGroupList: Array<unknown>,
|
||||||
|
downloadInfo: Array<unknown>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
getBaseEmojiPathByIds(getBaseEmojiPathReqs: Array<GetBaseEmojiPathReq>): unknown;
|
getBaseEmojiPathByIds(getBaseEmojiPathReqs: Array<GetBaseEmojiPathReq>): unknown;
|
||||||
|
|
||||||
|
@@ -253,7 +253,7 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
getGroupShutUpMemberList(groupCode: string): Promise<GeneralCallResult>;
|
getGroupShutUpMemberList(groupCode: string): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
setMemberShutUp(groupCode: string, memberTimes: { uid: string, timeStamp: number }[]): Promise<void>;
|
setMemberShutUp(groupCode: string, memberTimes: { uid: string, timeStamp: number }[]): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
getGroupRecommendContactArkJson(groupCode: string): Promise<GeneralCallResult & { arkJson: string }>;
|
getGroupRecommendContactArkJson(groupCode: string): Promise<GeneralCallResult & { arkJson: string }>;
|
||||||
|
|
||||||
|
@@ -464,11 +464,20 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
||||||
|
|
||||||
clickInlineKeyboardButton(...args: unknown[]): unknown;
|
clickInlineKeyboardButton(params: {
|
||||||
|
guildId?: string,
|
||||||
|
peerId: string,
|
||||||
|
botAppid: string,
|
||||||
|
msgSeq: string,
|
||||||
|
buttonId: string,
|
||||||
|
callback_data: string,
|
||||||
|
dmFlag: number,
|
||||||
|
chatType: number // 1私聊 2群
|
||||||
|
}): Promise<GeneralCallResult & { status: number, promptText: string, promptType: number, promptIcon: number }>;
|
||||||
|
|
||||||
setCurOnScreenMsg(...args: unknown[]): unknown;
|
setCurOnScreenMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
setCurOnScreenMsgForMsgEvent(...args: unknown[]): unknown;
|
setCurOnScreenMsgForMsgEvent(peer: Peer, msgRegList: Map<string, Uint8Array>): void;
|
||||||
|
|
||||||
getMiscData(key: string): unknown;
|
getMiscData(key: string): unknown;
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { NodeIKernelRobotListener } from '@/core/listeners';
|
import { NodeIKernelRobotListener } from '@/core/listeners';
|
||||||
|
import { GeneralCallResult, Peer } from '..';
|
||||||
|
|
||||||
export interface NodeIKernelRobotService {
|
export interface NodeIKernelRobotService {
|
||||||
fetchGroupRobotStoreDiscovery(arg: unknown): unknown;
|
fetchGroupRobotStoreDiscovery(arg: unknown): unknown;
|
||||||
@@ -31,5 +32,17 @@ export interface NodeIKernelRobotService {
|
|||||||
|
|
||||||
getRobotUinRange(data: unknown): Promise<{ response: { robotUinRanges: Array<unknown> } }>;
|
getRobotUinRange(data: unknown): Promise<{ response: { robotUinRanges: Array<unknown> } }>;
|
||||||
|
|
||||||
|
getRobotFunctions(peer: Peer, params: {
|
||||||
|
uins: Array<string>,
|
||||||
|
num: 0,
|
||||||
|
client_info: { platform: 4, version: '', build_num: 9999 },
|
||||||
|
tinyids: [],
|
||||||
|
page: 0,
|
||||||
|
full_fetch: false,
|
||||||
|
scene: 4,
|
||||||
|
filter: 1,
|
||||||
|
bkn: ''
|
||||||
|
}): Promise<GeneralCallResult & { response: { bot_features: Array<unknown>, next_page: number } }>;
|
||||||
|
|
||||||
isNull(): boolean;
|
isNull(): boolean;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChatType } from '@/core/types';
|
import { ChatType, Peer } from '@/core/types';
|
||||||
import { GeneralCallResult } from './common';
|
import { GeneralCallResult } from './common';
|
||||||
|
|
||||||
export interface NodeIKernelSearchService {
|
export interface NodeIKernelSearchService {
|
||||||
@@ -54,7 +54,7 @@ export interface NodeIKernelSearchService {
|
|||||||
|
|
||||||
cancelSearchChatMsgs(...args: unknown[]): unknown;// needs 3 arguments
|
cancelSearchChatMsgs(...args: unknown[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchMsgWithKeywords(...args: unknown[]): unknown;// needs 2 arguments
|
searchMsgWithKeywords(keyWords: string[], param: Peer & { searchFields: number, pageLimit: number }): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
searchMoreMsgWithKeywords(...args: unknown[]): unknown;// needs 1 arguments
|
searchMoreMsgWithKeywords(...args: unknown[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
10
src/onebot/action/extends/BotExit.ts
Normal file
10
src/onebot/action/extends/BotExit.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { ActionName } from '@/onebot/action/router';
|
||||||
|
import { OneBotAction } from '../OneBotAction';
|
||||||
|
|
||||||
|
export class BotExit extends OneBotAction<void, void> {
|
||||||
|
override actionName = ActionName.Exit;
|
||||||
|
|
||||||
|
async _handle() {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
30
src/onebot/action/extends/ClickInlineKeyboardButton.ts
Normal file
30
src/onebot/action/extends/ClickInlineKeyboardButton.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { ActionName } from '@/onebot/action/router';
|
||||||
|
import { OneBotAction } from '../OneBotAction';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
|
const SchemaData = Type.Object({
|
||||||
|
group_id: Type.Union([Type.Number(), Type.String()]),
|
||||||
|
bot_appid: Type.String(),
|
||||||
|
button_id: Type.String({ default: '' }),
|
||||||
|
callback_data: Type.String({ default: '' }),
|
||||||
|
msg_seq: Type.String({ default: '10086' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
type Payload = Static<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class ClickInlineKeyboardButton extends OneBotAction<Payload, unknown> {
|
||||||
|
override actionName = ActionName.ClickInlineKeyboardButton;
|
||||||
|
override payloadSchema = SchemaData;
|
||||||
|
|
||||||
|
async _handle(payload: Payload) {
|
||||||
|
return await this.core.apis.MsgApi.clickInlineKeyboardButton({
|
||||||
|
buttonId: payload.button_id,
|
||||||
|
peerId: payload.group_id.toString(),
|
||||||
|
botAppid: payload.bot_appid,
|
||||||
|
msgSeq: payload.msg_seq,
|
||||||
|
callback_data: payload.callback_data,
|
||||||
|
dmFlag: 0,
|
||||||
|
chatType: 2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
56
src/onebot/action/extends/GetUnidirectionalFriendList.ts
Normal file
56
src/onebot/action/extends/GetUnidirectionalFriendList.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { PacketHexStr } from '@/core/packet/transformer/base';
|
||||||
|
import { OneBotAction } from '@/onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/onebot/action/router';
|
||||||
|
import { ProtoBuf, ProtoBufBase, PBUint32, PBString } from 'napcat.protobuf';
|
||||||
|
|
||||||
|
interface Friend {
|
||||||
|
uin: number;
|
||||||
|
uid: string;
|
||||||
|
nick_name: string;
|
||||||
|
age: number;
|
||||||
|
source: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Block {
|
||||||
|
str_uid: string;
|
||||||
|
bytes_source: string;
|
||||||
|
uint32_sex: number;
|
||||||
|
uint32_age: number;
|
||||||
|
bytes_nick: string;
|
||||||
|
uint64_uin: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetUnidirectionalFriendList extends OneBotAction<void, Friend[]> {
|
||||||
|
override actionName = ActionName.GetUnidirectionalFriendList;
|
||||||
|
|
||||||
|
async pack_data(data: string): Promise<Uint8Array> {
|
||||||
|
return ProtoBuf(class extends ProtoBufBase {
|
||||||
|
type = PBUint32(2, false, 0);
|
||||||
|
data = PBString(3, false, data);
|
||||||
|
}).encode();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handle(): Promise<Friend[]> {
|
||||||
|
const self_id = this.core.selfInfo.uin;
|
||||||
|
const req_json = {
|
||||||
|
uint64_uin: self_id,
|
||||||
|
uint64_top: 0,
|
||||||
|
uint32_req_num: 99,
|
||||||
|
bytes_cookies: ""
|
||||||
|
};
|
||||||
|
const packed_data = await this.pack_data(JSON.stringify(req_json));
|
||||||
|
const data = Buffer.from(packed_data).toString('hex');
|
||||||
|
const rsq = { cmd: 'MQUpdateSvc_com_qq_ti.web.OidbSvc.0xe17_0', data: data as PacketHexStr };
|
||||||
|
const rsp_data = await this.core.apis.PacketApi.pkt.operation.sendPacket(rsq, true);
|
||||||
|
const block_json = ProtoBuf(class extends ProtoBufBase { data = PBString(4); }).decode(rsp_data);
|
||||||
|
const block_list: Block[] = JSON.parse(block_json.data).rpt_block_list;
|
||||||
|
|
||||||
|
return block_list.map((block) => ({
|
||||||
|
uin: block.uint64_uin,
|
||||||
|
uid: block.str_uid,
|
||||||
|
nick_name: Buffer.from(block.bytes_nick, 'base64').toString(),
|
||||||
|
age: block.uint32_age,
|
||||||
|
source: Buffer.from(block.bytes_source, 'base64').toString()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@@ -5,7 +5,7 @@ import { Static, Type } from '@sinclair/typebox';
|
|||||||
const SchemaData = Type.Object({
|
const SchemaData = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.Union([Type.Number(), Type.String()]),
|
||||||
user_id: Type.Union([Type.Number(), Type.String()]),
|
user_id: Type.Union([Type.Number(), Type.String()]),
|
||||||
special_title: Type.String(),
|
special_title: Type.String({ default: '' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type Payload = Static<typeof SchemaData>;
|
||||||
@@ -16,7 +16,7 @@ export class SetSpecialTittle extends GetPacketStatusDepends<Payload, void> {
|
|||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
if(!uid) throw new Error('User not found');
|
if (!uid) throw new Error('User not found');
|
||||||
await this.core.apis.PacketApi.pkt.operation.SetGroupSpecialTitle(+payload.group_id, uid, payload.special_title);
|
await this.core.apis.PacketApi.pkt.operation.SetGroupSpecialTitle(+payload.group_id, uid, payload.special_title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
src/onebot/action/file/GetPrivateFileUrl.ts
Normal file
36
src/onebot/action/file/GetPrivateFileUrl.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { ActionName } from '@/onebot/action/router';
|
||||||
|
import { FileNapCatOneBotUUID } from '@/common/file-uuid';
|
||||||
|
import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
|
const SchemaData = Type.Object({
|
||||||
|
file_id: Type.String(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type Payload = Static<typeof SchemaData>;
|
||||||
|
|
||||||
|
interface GetPrivateFileUrlResponse {
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetPrivateFileUrl extends GetPacketStatusDepends<Payload, GetPrivateFileUrlResponse> {
|
||||||
|
override actionName = ActionName.NapCat_GetPrivateFileUrl;
|
||||||
|
override payloadSchema = SchemaData;
|
||||||
|
|
||||||
|
async _handle(payload: Payload) {
|
||||||
|
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id);
|
||||||
|
|
||||||
|
if (contextMsgFile?.fileUUID && contextMsgFile.msgId) {
|
||||||
|
let msg = await this.core.apis.MsgApi.getMsgsByMsgId(contextMsgFile.peer, [contextMsgFile.msgId]);
|
||||||
|
let self_id = this.core.selfInfo.uid;
|
||||||
|
let file_hash = msg.msgList[0]?.elements.map(ele => ele.fileElement?.file10MMd5)[0];
|
||||||
|
if (file_hash) {
|
||||||
|
return {
|
||||||
|
url: await this.core.apis.PacketApi.pkt.operation.GetPrivateFileUrl(self_id, contextMsgFile.fileUUID, file_hash)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
throw new Error('real fileUUID not found!');
|
||||||
|
}
|
||||||
|
}
|
@@ -13,12 +13,16 @@ type Payload = Static<typeof SchemaData>;
|
|||||||
export default class SetGroupBan extends OneBotAction<Payload, null> {
|
export default class SetGroupBan extends OneBotAction<Payload, null> {
|
||||||
override actionName = ActionName.SetGroupBan;
|
override actionName = ActionName.SetGroupBan;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload): Promise<null> {
|
async _handle(payload: Payload): Promise<null> {
|
||||||
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
if (!uid) throw new Error('uid error');
|
if (!uid) throw new Error('uid error');
|
||||||
await this.core.apis.GroupApi.banMember(payload.group_id.toString(),
|
let member_role = (await this.core.apis.GroupApi.getGroupMemberEx(payload.group_id.toString(), uid, true))?.role;
|
||||||
|
if (member_role === 4) throw new Error('cannot ban owner');
|
||||||
|
if (member_role === 3) throw new Error('cannot ban admin');
|
||||||
|
// 例如无管理员权限时 result为 120101005 errMsg为 'ERR_NOT_GROUP_ADMIN'
|
||||||
|
let ret = await this.core.apis.GroupApi.banMember(payload.group_id.toString(),
|
||||||
[{ uid: uid, timeStamp: +payload.duration }]);
|
[{ uid: uid, timeStamp: +payload.duration }]);
|
||||||
|
if (ret.result !== 0) throw new Error(ret.errMsg);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -104,6 +104,10 @@ import { GetClientkey } from './extends/GetClientkey';
|
|||||||
import { SendPacket } from './extends/SendPacket';
|
import { SendPacket } from './extends/SendPacket';
|
||||||
import { SendPoke } from '@/onebot/action/packet/SendPoke';
|
import { SendPoke } from '@/onebot/action/packet/SendPoke';
|
||||||
import { SetDiyOnlineStatus } from './extends/SetDiyOnlineStatus';
|
import { SetDiyOnlineStatus } from './extends/SetDiyOnlineStatus';
|
||||||
|
import { BotExit } from './extends/BotExit';
|
||||||
|
import { ClickInlineKeyboardButton } from './extends/ClickInlineKeyboardButton';
|
||||||
|
import { GetPrivateFileUrl } from './file/GetPrivateFileUrl';
|
||||||
|
import { GetUnidirectionalFriendList } from './extends/GetUnidirectionalFriendList';
|
||||||
|
|
||||||
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
|
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
|
||||||
|
|
||||||
@@ -221,6 +225,10 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
|||||||
new SendPacket(obContext, core),
|
new SendPacket(obContext, core),
|
||||||
new SendPoke(obContext, core),
|
new SendPoke(obContext, core),
|
||||||
new GetGroupSystemMsg(obContext, core),
|
new GetGroupSystemMsg(obContext, core),
|
||||||
|
new BotExit(obContext, core),
|
||||||
|
new ClickInlineKeyboardButton(obContext, core),
|
||||||
|
new GetPrivateFileUrl(obContext,core),
|
||||||
|
new GetUnidirectionalFriendList(obContext,core),
|
||||||
];
|
];
|
||||||
|
|
||||||
type HandlerUnion = typeof actionHandlers[number];
|
type HandlerUnion = typeof actionHandlers[number];
|
||||||
|
@@ -10,6 +10,9 @@ export interface InvalidCheckResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ActionName = {
|
export const ActionName = {
|
||||||
|
NapCat_GetPrivateFileUrl: 'get_private_file_url',
|
||||||
|
ClickInlineKeyboardButton: 'click_inline_keyboard_button',
|
||||||
|
GetUnidirectionalFriendList: 'get_unidirectional_friend_list',
|
||||||
// onebot 11
|
// onebot 11
|
||||||
SendPrivateMsg: 'send_private_msg',
|
SendPrivateMsg: 'send_private_msg',
|
||||||
SendGroupMsg: 'send_group_msg',
|
SendGroupMsg: 'send_group_msg',
|
||||||
@@ -49,7 +52,7 @@ export const ActionName = {
|
|||||||
GetVersionInfo: 'get_version_info',
|
GetVersionInfo: 'get_version_info',
|
||||||
// Reboot : 'set_restart',
|
// Reboot : 'set_restart',
|
||||||
// CleanCache : 'clean_cache',
|
// CleanCache : 'clean_cache',
|
||||||
|
Exit: 'bot_exit',
|
||||||
// go-cqhttp
|
// go-cqhttp
|
||||||
SetQQProfile: 'set_qq_profile',
|
SetQQProfile: 'set_qq_profile',
|
||||||
// QidianGetAccountInfo : 'qidian_get_account_info',
|
// QidianGetAccountInfo : 'qidian_get_account_info',
|
||||||
|
@@ -49,6 +49,7 @@ export class OneBotGroupApi {
|
|||||||
duration = -1;
|
duration = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await this.core.apis.GroupApi.refreshGroupMemberCachePartial(GroupCode, memberUid);
|
||||||
const adminUin = (await this.core.apis.GroupApi.getGroupMember(GroupCode, adminUid))?.uin;
|
const adminUin = (await this.core.apis.GroupApi.getGroupMember(GroupCode, adminUid))?.uin;
|
||||||
if (memberUin && adminUin) {
|
if (memberUin && adminUin) {
|
||||||
return new OB11GroupBanEvent(
|
return new OB11GroupBanEvent(
|
||||||
@@ -113,12 +114,16 @@ export class OneBotGroupApi {
|
|||||||
async parseCardChangedEvent(msg: RawMessage) {
|
async parseCardChangedEvent(msg: RawMessage) {
|
||||||
if (msg.senderUin && msg.senderUin !== '0') {
|
if (msg.senderUin && msg.senderUin !== '0') {
|
||||||
const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUid, msg.senderUin);
|
const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUid, msg.senderUin);
|
||||||
|
await this.core.apis.GroupApi.refreshGroupMemberCachePartial(msg.peerUid, msg.senderUid);
|
||||||
if (member && member.cardName !== msg.sendMemberName) {
|
if (member && member.cardName !== msg.sendMemberName) {
|
||||||
const newCardName = msg.sendMemberName ?? '';
|
const newCardName = msg.sendMemberName ?? '';
|
||||||
const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
|
const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
|
||||||
member.cardName = newCardName;
|
member.cardName = newCardName;
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
if (member && member.nick !== msg.sendNickName) {
|
||||||
|
await this.core.apis.GroupApi.refreshGroupMemberCachePartial(msg.peerUid, msg.senderUid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@@ -132,7 +132,6 @@ export class OneBotMsgApi {
|
|||||||
file: element.fileName,
|
file: element.fileName,
|
||||||
sub_type: element.picSubType,
|
sub_type: element.picSubType,
|
||||||
url: await this.core.apis.FileApi.getImageUrl(element),
|
url: await this.core.apis.FileApi.getImageUrl(element),
|
||||||
path: element.filePath,
|
|
||||||
file_size: element.fileSize,
|
file_size: element.fileSize,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -148,13 +147,13 @@ export class OneBotMsgApi {
|
|||||||
peerUid: msg.peerUid,
|
peerUid: msg.peerUid,
|
||||||
guildId: '',
|
guildId: '',
|
||||||
};
|
};
|
||||||
const file = FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileName);
|
FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileUuid);
|
||||||
|
FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileName);
|
||||||
return {
|
return {
|
||||||
type: OB11MessageDataType.file,
|
type: OB11MessageDataType.file,
|
||||||
data: {
|
data: {
|
||||||
file: file,
|
file: element.fileName,
|
||||||
path: element.filePath,
|
file_id: element.fileUuid,
|
||||||
file_id: file,
|
|
||||||
file_size: element.fileSize,
|
file_size: element.fileSize,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -216,7 +215,6 @@ export class OneBotMsgApi {
|
|||||||
data: {
|
data: {
|
||||||
summary: _.faceName, // 商城表情名称
|
summary: _.faceName, // 商城表情名称
|
||||||
file: filename,
|
file: filename,
|
||||||
path: url,
|
|
||||||
url: url,
|
url: url,
|
||||||
key: _.key,
|
key: _.key,
|
||||||
emoji_id: _.emojiId,
|
emoji_id: _.emojiId,
|
||||||
@@ -339,7 +337,6 @@ export class OneBotMsgApi {
|
|||||||
type: OB11MessageDataType.video,
|
type: OB11MessageDataType.video,
|
||||||
data: {
|
data: {
|
||||||
file: fileCode,
|
file: fileCode,
|
||||||
path: videoDownUrl,
|
|
||||||
url: videoDownUrl,
|
url: videoDownUrl,
|
||||||
file_size: element.fileSize,
|
file_size: element.fileSize,
|
||||||
},
|
},
|
||||||
@@ -357,8 +354,8 @@ export class OneBotMsgApi {
|
|||||||
type: OB11MessageDataType.voice,
|
type: OB11MessageDataType.voice,
|
||||||
data: {
|
data: {
|
||||||
file: fileCode,
|
file: fileCode,
|
||||||
path: element.filePath,
|
|
||||||
file_size: element.fileSize,
|
file_size: element.fileSize,
|
||||||
|
path: element.filePath,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -658,6 +655,19 @@ export class OneBotMsgApi {
|
|||||||
[OB11MessageDataType.node]: async () => undefined,
|
[OB11MessageDataType.node]: async () => undefined,
|
||||||
|
|
||||||
[OB11MessageDataType.forward]: async ({ data }, context) => {
|
[OB11MessageDataType.forward]: async ({ data }, context) => {
|
||||||
|
// let id = data.id.toString();
|
||||||
|
// let peer: Peer | undefined = context.peer;
|
||||||
|
// if (isNumeric(id)) {
|
||||||
|
// let msgid = '';
|
||||||
|
// if (BigInt(data.id) > 2147483647n) {
|
||||||
|
// peer = MessageUnique.getPeerByMsgId(id)?.Peer;
|
||||||
|
// msgid = id;
|
||||||
|
// } else {
|
||||||
|
// let data = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
|
||||||
|
// msgid = data?.MsgId ?? '';
|
||||||
|
// peer = data?.Peer;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
const jsonData = ForwardMsgBuilder.fromResId(data.id);
|
const jsonData = ForwardMsgBuilder.fromResId(data.id);
|
||||||
return this.ob11ToRawConverters.json({
|
return this.ob11ToRawConverters.json({
|
||||||
data: { data: JSON.stringify(jsonData) },
|
data: { data: JSON.stringify(jsonData) },
|
||||||
@@ -996,24 +1006,17 @@ export class OneBotMsgApi {
|
|||||||
this.core.context.logger.logError('文件消息缺少参数', inputdata);
|
this.core.context.logger.logError('文件消息缺少参数', inputdata);
|
||||||
throw new Error('文件消息缺少参数');
|
throw new Error('文件消息缺少参数');
|
||||||
}
|
}
|
||||||
|
realUri = await this.handleObfuckName(realUri) ?? realUri;
|
||||||
const downloadFile = async (uri: string) => {
|
|
||||||
const { path, fileName, errMsg, success } = await uriToLocalFile(this.core.NapCatTempPath, uri);
|
|
||||||
if (!success) {
|
|
||||||
this.core.context.logger.logError('文件下载失败', errMsg);
|
|
||||||
throw new Error('文件下载失败: ' + errMsg);
|
|
||||||
}
|
|
||||||
return { path, fileName };
|
|
||||||
};
|
|
||||||
try {
|
try {
|
||||||
const { path, fileName } = await downloadFile(realUri);
|
const { path, fileName, errMsg, success } = await uriToLocalFile(this.core.NapCatTempPath, realUri);
|
||||||
deleteAfterSentFiles.push(path);
|
if (!success) {
|
||||||
return { path, fileName: inputdata.name ?? fileName };
|
this.core.context.logger.logError('文件处理失败', errMsg);
|
||||||
} catch {
|
throw new Error('文件处理失败: ' + errMsg);
|
||||||
realUri = await this.handleObfuckName(realUri);
|
}
|
||||||
const { path, fileName } = await downloadFile(realUri);
|
|
||||||
deleteAfterSentFiles.push(path);
|
deleteAfterSentFiles.push(path);
|
||||||
return { path, fileName: inputdata.name ?? fileName };
|
return { path, fileName: inputdata.name ?? fileName };
|
||||||
|
} catch (e: unknown) {
|
||||||
|
throw new Error((e as Error).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1038,7 +1041,7 @@ export class OneBotMsgApi {
|
|||||||
}
|
}
|
||||||
return url !== '' ? url : await this.core.apis.FileApi.downloadMedia(msgId, peer.chatType, peer.peerUid, elementId, '', '');
|
return url !== '' ? url : await this.core.apis.FileApi.downloadMedia(msgId, peer.chatType, peer.peerUid, elementId, '', '');
|
||||||
}
|
}
|
||||||
throw new Error('文件名解析失败');
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
groupChangDecreseType2String(type: number): GroupDecreaseSubType {
|
groupChangDecreseType2String(type: number): GroupDecreaseSubType {
|
||||||
|
@@ -8,8 +8,7 @@ const external = [
|
|||||||
'silk-wasm',
|
'silk-wasm',
|
||||||
'ws',
|
'ws',
|
||||||
'express',
|
'express',
|
||||||
'@ffmpeg.wasm/core-mt',
|
'@ffmpeg.wasm/core-mt'
|
||||||
'piscina'
|
|
||||||
];
|
];
|
||||||
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
|
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user