mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1ecd5b78e6 | ||
![]() |
fca2e3c51a | ||
![]() |
95ea761b2d | ||
![]() |
6b3bfa1ee9 | ||
![]() |
df3e302a9d | ||
![]() |
c88a68c9a8 | ||
![]() |
92d01b9cdd |
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "4.5.21",
|
"version": "4.5.22",
|
||||||
"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
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.5.21",
|
"version": "4.5.22",
|
||||||
"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",
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '4.5.21';
|
export const napCatVersion = '4.5.22';
|
||||||
|
2
src/core/external/offset.json
vendored
2
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"
|
||||||
},
|
},
|
||||||
|
@@ -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);
|
||||||
|
@@ -465,14 +465,14 @@ export interface NodeIKernelMsgService {
|
|||||||
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
||||||
|
|
||||||
clickInlineKeyboardButton(params: {
|
clickInlineKeyboardButton(params: {
|
||||||
guildId: string,
|
guildId?: string,
|
||||||
peerId: string,
|
peerId: string,
|
||||||
botAppid: string,
|
botAppid: string,
|
||||||
msgSeq: string,
|
msgSeq: string,
|
||||||
buttonId: string,
|
buttonId: string,
|
||||||
callback_data: string,
|
callback_data: string,
|
||||||
dmFlag: number,
|
dmFlag: number,
|
||||||
chatType: number
|
chatType: number // 1私聊 2群
|
||||||
}): Promise<GeneralCallResult & { status: number, promptText: string, promptType: number, promptIcon: number }>;
|
}): Promise<GeneralCallResult & { status: number, promptText: string, promptType: number, promptIcon: number }>;
|
||||||
|
|
||||||
setCurOnScreenMsg(...args: unknown[]): unknown;
|
setCurOnScreenMsg(...args: unknown[]): unknown;
|
||||||
|
@@ -7,6 +7,7 @@ const SchemaData = Type.Object({
|
|||||||
bot_appid: Type.String(),
|
bot_appid: Type.String(),
|
||||||
button_id: Type.String({ default: '' }),
|
button_id: Type.String({ default: '' }),
|
||||||
callback_data: Type.String({ default: '' }),
|
callback_data: Type.String({ default: '' }),
|
||||||
|
msg_seq: Type.String({ default: '10086' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type Payload = Static<typeof SchemaData>;
|
||||||
@@ -18,13 +19,12 @@ export class ClickInlineKeyboardButton extends OneBotAction<Payload, unknown> {
|
|||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
return await this.core.apis.MsgApi.clickInlineKeyboardButton({
|
return await this.core.apis.MsgApi.clickInlineKeyboardButton({
|
||||||
buttonId: payload.button_id,
|
buttonId: payload.button_id,
|
||||||
guildId: '',// 频道使用
|
|
||||||
peerId: payload.group_id.toString(),
|
peerId: payload.group_id.toString(),
|
||||||
botAppid: payload.bot_appid,
|
botAppid: payload.bot_appid,
|
||||||
msgSeq: '10086',
|
msgSeq: payload.msg_seq,
|
||||||
callback_data: payload.callback_data,
|
callback_data: payload.callback_data,
|
||||||
dmFlag: 0,
|
dmFlag: 0,
|
||||||
chatType: 1
|
chatType: 2
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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!');
|
||||||
|
}
|
||||||
|
}
|
@@ -106,6 +106,7 @@ import { SendPoke } from '@/onebot/action/packet/SendPoke';
|
|||||||
import { SetDiyOnlineStatus } from './extends/SetDiyOnlineStatus';
|
import { SetDiyOnlineStatus } from './extends/SetDiyOnlineStatus';
|
||||||
import { BotExit } from './extends/BotExit';
|
import { BotExit } from './extends/BotExit';
|
||||||
import { ClickInlineKeyboardButton } from './extends/ClickInlineKeyboardButton';
|
import { ClickInlineKeyboardButton } from './extends/ClickInlineKeyboardButton';
|
||||||
|
import { GetPrivateFileUrl } from './file/GetPrivateFileUrl';
|
||||||
|
|
||||||
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
|
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
|
||||||
|
|
||||||
@@ -225,6 +226,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
|||||||
new GetGroupSystemMsg(obContext, core),
|
new GetGroupSystemMsg(obContext, core),
|
||||||
new BotExit(obContext, core),
|
new BotExit(obContext, core),
|
||||||
new ClickInlineKeyboardButton(obContext, core),
|
new ClickInlineKeyboardButton(obContext, core),
|
||||||
|
new GetPrivateFileUrl(obContext,core)
|
||||||
];
|
];
|
||||||
|
|
||||||
type HandlerUnion = typeof actionHandlers[number];
|
type HandlerUnion = typeof actionHandlers[number];
|
||||||
|
@@ -10,6 +10,7 @@ export interface InvalidCheckResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ActionName = {
|
export const ActionName = {
|
||||||
|
NapCat_GetPrivateFileUrl: 'get_private_file_url',
|
||||||
ClickInlineKeyboardButton: 'click_inline_keyboard_button',
|
ClickInlineKeyboardButton: 'click_inline_keyboard_button',
|
||||||
GetUnidirectionalFriendList: 'get_unidirectional_friend_list',
|
GetUnidirectionalFriendList: 'get_unidirectional_friend_list',
|
||||||
// onebot 11
|
// onebot 11
|
||||||
|
@@ -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,7 +354,6 @@ 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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user