chore: gocq接口完成

This commit is contained in:
手瓜一十雪 2024-08-09 17:27:44 +08:00
parent b016268fdb
commit a1a378d6f5
22 changed files with 579 additions and 51 deletions

88
src/common/utils/audio.ts Normal file
View File

@ -0,0 +1,88 @@
import fs from 'fs';
import { encode, getDuration, getWavFileInfo, isWav, isSilk } from 'silk-wasm';
import fsPromise from 'fs/promises';
import path from 'node:path';
import { randomUUID } from 'crypto';
import { spawn } from 'node:child_process';
import { LogWrapper } from './log';
export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: LogWrapper) {
async function guessDuration(pttPath: string) {
const pttFileInfo = await fsPromise.stat(pttPath);
let duration = pttFileInfo.size / 1024 / 3; // 3kb/s
duration = Math.floor(duration);
duration = Math.max(1, duration);
logger.log('通过文件大小估算语音的时长:', duration);
return duration;
}
try {
const file = await fsPromise.readFile(filePath);
const pttPath = path.join(TEMP_DIR, randomUUID());
if (!isSilk(file)) {
logger.log(`语音文件${filePath}需要转换成silk`);
const _isWav = isWav(file);
const pcmPath = pttPath + '.pcm';
let sampleRate = 0;
const convert = () => {
return new Promise<Buffer>((resolve, reject) => {
// todo: 通过配置文件获取ffmpeg路径
const ffmpegPath = process.env.FFMPEG_PATH || 'ffmpeg';
const cp = spawn(ffmpegPath, ['-y', '-i', filePath, '-ar', '24000', '-ac', '1', '-f', 's16le', pcmPath]);
cp.on('error', err => {
logger.log('FFmpeg处理转换出错: ', err.message);
return reject(err);
});
cp.on('exit', (code, signal) => {
const EXIT_CODES = [0, 255];
if (code == null || EXIT_CODES.includes(code)) {
sampleRate = 24000;
const data = fs.readFileSync(pcmPath);
fs.unlink(pcmPath, (err) => {
});
return resolve(data);
}
logger.log(`FFmpeg exit: code=${code ?? 'unknown'} sig=${signal ?? 'unknown'}`);
reject(Error('FFmpeg处理转换失败'));
});
});
};
let input: Buffer;
if (!_isWav) {
input = await convert();
} else {
input = file;
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
const { fmt } = getWavFileInfo(input);
// log(`wav文件信息`, fmt)
if (!allowSampleRate.includes(fmt.sampleRate)) {
input = await convert();
}
}
const silk = await encode(input, sampleRate);
fs.writeFileSync(pttPath, silk.data);
logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);
return {
converted: true,
path: pttPath,
duration: silk.duration / 1000
};
} else {
const silk = file;
let duration = 0;
try {
duration = getDuration(silk) / 1000;
} catch (e: any) {
logger.log('获取语音文件时长失败, 使用文件大小推测时长', filePath, e.stack);
duration = await guessDuration(filePath);
}
return {
converted: false,
path: filePath,
duration,
};
}
} catch (error: any) {
logger.logError('convert silk failed', error.stack);
return {};
}
}

62
src/common/utils/video.ts Normal file

File diff suppressed because one or more lines are too long

View File

@ -32,7 +32,7 @@ export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileRespon
protected async _handle(payload: Payload): Promise<FileResponse> {
const isRandomName = !payload.name;
const name = payload.name || randomUUID();
const filePath = joinPath(getTempDir(), name);
const filePath = joinPath(this.CoreContext.NapCatTempPath, name);
if (payload.base64) {
fs.writeFileSync(filePath, payload.base64, 'base64');
@ -48,7 +48,7 @@ export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileRespon
if (isRandomName) {
// 默认实现要名称未填写时文件名为文件 md5
const md5 = await calculateFileMD5(filePath);
const newPath = joinPath(getTempDir(), md5);
const newPath = joinPath(this.CoreContext.NapCatTempPath, md5);
fs.renameSync(filePath, newPath);
return { file: newPath };
}

View File

@ -1,7 +1,7 @@
import BaseAction from '../BaseAction';
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
import { NTQQMsgApi } from '@/core/apis';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import { ActionName, BaseCheckResult } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
@ -41,7 +41,7 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
const msgList = data.msgList;
const messages = await Promise.all(msgList.map(async msg => {
const resMsg = await OB11Constructor.message(msg);
resMsg.message_id = await MessageUnique.createMsg({ guildId:'',chatType:msg.chatType,peerUid:msg.peerUid },msg.msgId)!;
resMsg.message_id = MessageUnique.createMsg({ guildId:'',chatType:msg.chatType,peerUid:msg.peerUid },msg.msgId)!;
return resMsg;
}));
messages.map(msg => {

View File

@ -3,7 +3,7 @@ import { OB11Message, OB11User } from '../../types';
import { ActionName } from '../types';
import { ChatType, RawMessage } from '@/core/entities';
import { NTQQMsgApi } from '@/core/apis/msg';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';

View File

@ -7,7 +7,7 @@ const SchemaData = {
type: 'object',
properties: {
group_id: { type: [ 'number' , 'string' ] },
type: { enum: [WebHonorType.ALL, WebHonorType.EMOTION, WebHonorType.LEGEND, WebHonorType.PERFROMER, WebHonorType.STORONGE_NEWBI, WebHonorType.TALKACTIVE] }
type: { enum: [WebHonorType.ALL, WebHonorType.EMOTION, WebHonorType.LEGEND, WebHonorType.PERFORMER, WebHonorType.STRONG_NEWBIE, WebHonorType.TALKATIVE] }
},
required: ['group_id']
} as const satisfies JSONSchema;

View File

@ -1,9 +1,8 @@
import BaseAction from '../BaseAction';
import { OB11Message, OB11User } from '../../types';
import { OB11Message } from '../../types';
import { ActionName } from '../types';
import { ChatType, Peer, RawMessage } from '@/core/entities';
import { NTQQMsgApi } from '@/core/apis/msg';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
interface Response {
@ -41,7 +40,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
if (!startMsgId) throw `消息${payload.message_seq}不存在`;
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList;
}
if(isReverseOrder) msgList.reverse();
if (isReverseOrder) msgList.reverse();
await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
}));

View File

@ -1,8 +1,6 @@
import { DeviceList } from '@/onebot11/main';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { JSONSchema } from 'json-schema-to-ts';
import { NTQQSystemApi } from '@/core';
import { sleep } from '@/common/utils/helper';
const SchemaData = {
@ -16,8 +14,11 @@ export class GetOnlineClient extends BaseAction<void, Array<any>> {
actionName = ActionName.GetOnlineClient;
protected async _handle(payload: void) {
//注册监听
const NTQQSystemApi = this.CoreContext.getApiContext().SystemApi;
NTQQSystemApi.getOnlineDev();
await sleep(500);
return DeviceList;
return [];
}
}

View File

@ -1,8 +1,7 @@
import BaseAction from '../BaseAction';
import { OB11User, OB11UserSex } from '../../types';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import { ActionName } from '../types';
import { NTQQUserApi } from '@/core/apis/user';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { calcQQLevel } from '@/common/utils/qqlevel';
const SchemaData = {
@ -20,24 +19,24 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
protected async _handle(payload: Payload): Promise<OB11User> {
const NTQQUserApi = this.CoreContext.getApiContext().UserApi;
const user_id = payload.user_id.toString();
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
if (!uid || uid.indexOf('*') != -1) {
const ret = {
...extendData,
user_id: parseInt(extendData.info.uin) || 0,
nickname: extendData.info.nick,
sex: OB11UserSex.unknown,
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
qid: extendData.info.qid,
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
login_days: 0,
uid: ''
};
return ret;
}
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
return OB11Constructor.stranger(data);
const user_id = payload.user_id.toString();
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
if (!uid || uid.indexOf('*') != -1) {
const ret = {
...extendData,
user_id: parseInt(extendData.info.uin) || 0,
nickname: extendData.info.nick,
sex: OB11UserSex.unknown,
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
qid: extendData.info.qid,
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
login_days: 0,
uid: ''
};
return ret;
}
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
return OB11Constructor.stranger(data);
}
}

View File

@ -10,7 +10,7 @@ interface Payload{
export class GoCQHTTPHandleQuickAction extends BaseAction<Payload, null>{
actionName = ActionName.GoCQHTTP_HandleQuickAction;
protected async _handle(payload: Payload): Promise<null> {
handleQuickOperation(payload.context, payload.operation).then().catch(log);
handleQuickOperation(payload.context, payload.operation,this.CoreContext).then().catch(this.CoreContext.context.logger.logError);
return null;
}
}

View File

@ -1,7 +1,6 @@
import { checkFileReceived, uri2local } from '@/common/utils/file';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { NTQQGroupApi, WebApi } from '@/core/apis';
import { unlink } from 'node:fs';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
@ -25,7 +24,7 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
let UploadImage: { id: string, width: number, height: number } | undefined = undefined;
if (payload.image) {
//公告图逻辑
const { errMsg, path, isLocal, success } = (await uri2local(payload.image));
const { errMsg, path, isLocal, success } = (await uri2local(this.CoreContext.NapCatTempPath,payload.image));
if (!success) {
throw `群公告${payload.image}设置失败,image字段可能格式不正确`;
}

View File

@ -5,6 +5,7 @@ import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
const SchemaData = {
type: 'object',
properties: {
@ -27,12 +28,12 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
if (fs.existsSync(file)) {
file = `file://${file}`;
}
const downloadResult = await uri2local(file);
const downloadResult = await uri2local(this.CoreContext.NapCatTempPath, file);
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name, payload.folder_id);
await sendMsg({ chatType: ChatType.group, peerUid: group.groupCode }, [sendFileEle], [], true);
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name, payload.folder_id);
await sendMsg({ chatType: ChatType.group, peerUid: payload.group_id.toString() }, [sendFileEle], [], true);
return null;
}
}

View File

@ -2,10 +2,10 @@ import BaseAction from '../BaseAction';;
import { ActionName } from '../types';
import { ChatType, Peer, SendFileElement } from '@/core/entities';
import fs from 'fs';
import { SendMsg, sendMsg } from '@/onebot/action/msg/SendMsg';
import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { NTQQFriendApi, NTQQUserApi } from '@/core';
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
const SchemaData = {
type: 'object',
properties: {
@ -22,6 +22,8 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
actionName = ActionName.GOCQHTTP_UploadPrivateFile;
PayloadSchema = SchemaData;
async getPeer(payload: Payload): Promise<Peer> {
const NTQQUserApi = this.CoreContext.getApiContext().UserApi;
const NTQQFriendApi = this.CoreContext.getApiContext().FriendApi;
if (payload.user_id) {
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
if (!peerUid) {
@ -38,11 +40,11 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
if (fs.existsSync(file)) {
file = `file://${file}`;
}
const downloadResult = await uri2local(file);
const downloadResult = await uri2local(this.CoreContext.NapCatTempPath, file);
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name);
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name);
await sendMsg(peer, [sendFileEle], [], true);
return null;
}

View File

@ -1,5 +1,5 @@
import { OB11Group } from '../../types';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';

View File

@ -1,5 +1,5 @@
import { OB11Group } from '../../types';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { Group } from '@/core/entities';

View File

@ -1,5 +1,5 @@
import { OB11GroupMember } from '../../types';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';

View File

@ -1,5 +1,5 @@
import { OB11GroupMember } from '../../types';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { NTQQUserApi } from '@/core/apis/user';

View File

@ -1,6 +1,6 @@
import { OB11GroupMember } from '../../types';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';

View File

@ -1,5 +1,5 @@
import { OB11Message } from '../../types';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';

View File

@ -1,6 +1,6 @@
import { OB11User } from '../../types';
import { OB11Constructor } from '../../helper/constructor';
import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';

377
src/onebot/helper/msg.ts Normal file
View File

@ -0,0 +1,377 @@
import {
AtType,
ElementType, FaceIndex, FaceType, NapCatCore, PicElement,
PicType,
SendArkElement,
SendFaceElement,
SendFileElement, SendMarkdownElement, SendMarketFaceElement,
SendPicElement,
SendPttElement,
SendReplyElement,
sendShareLocationElement,
SendTextElement,
SendVideoElement,
viedo_type
} from '@/core';
import { promises as fs } from 'node:fs';
import ffmpeg from 'fluent-ffmpeg';
import { NTQQFileApi } from '@/core/apis/file';
import { calculateFileMD5, isGIF } from '@/common/utils/file';
import { defaultVideoThumb, getVideoInfo } from '@/common/utils/video';
import { encodeSilk } from '@/common/utils/audio';
import { isNull } from '@/common/utils/helper';
import faceConfig from '@/core/external/face_config.json';
import * as pathLib from 'node:path';
export class SendMsgElementConstructor {
static location(CoreContext: NapCatCore): sendShareLocationElement {
return {
elementType: ElementType.SHARELOCATION,
elementId: '',
shareLocationElement: {
text: "测试",
ext: ""
}
}
}
static text(CoreContext: NapCatCore, content: string): SendTextElement {
return {
elementType: ElementType.TEXT,
elementId: '',
textElement: {
content,
atType: AtType.notAt,
atUid: '',
atTinyId: '',
atNtUid: '',
},
};
}
static at(CoreContext: NapCatCore, atUid: string, atNtUid: string, atType: AtType, atName: string): SendTextElement {
return {
elementType: ElementType.TEXT,
elementId: '',
textElement: {
content: `@${atName}`,
atType,
atUid,
atTinyId: '',
atNtUid,
},
};
}
static reply(CoreContext: NapCatCore, msgSeq: string, msgId: string, senderUin: string, senderUinStr: string): SendReplyElement {
return {
elementType: ElementType.REPLY,
elementId: '',
replyElement: {
replayMsgSeq: msgSeq, // raw.msgSeq
replayMsgId: msgId, // raw.msgId
senderUin: senderUin,
senderUinStr: senderUinStr,
}
};
}
static async pic(CoreContext: NapCatCore, picPath: string, summary: string = '', subType: 0 | 1 = 0): Promise<SendPicElement> {
const { md5, fileName, path, fileSize } = await NTQQFileApi.uploadFile(picPath, ElementType.PIC, subType);
if (fileSize === 0) {
throw '文件异常大小为0';
}
const imageSize = await NTQQFileApi.getImageSize(picPath);
const picElement: any = {
md5HexStr: md5,
fileSize: fileSize.toString(),
picWidth: imageSize?.width,
picHeight: imageSize?.height,
fileName: fileName,
sourcePath: path,
original: true,
picType: isGIF(picPath) ? PicType.gif : PicType.jpg,
picSubType: subType,
fileUuid: '',
fileSubId: '',
thumbFileSize: 0,
summary
};
//logDebug('图片信息', picElement);
return {
elementType: ElementType.PIC,
elementId: '',
picElement,
};
}
static async file(CoreContext: NapCatCore, filePath: string, fileName: string = '', folderId: string = ''): Promise<SendFileElement> {
const { md5, fileName: _fileName, path, fileSize } = await NTQQFileApi.uploadFile(filePath, ElementType.FILE);
if (fileSize === 0) {
throw '文件异常大小为0';
}
const element: SendFileElement = {
elementType: ElementType.FILE,
elementId: '',
fileElement: {
fileName: fileName || _fileName,
folderId: folderId,
'filePath': path!,
'fileSize': (fileSize).toString(),
}
};
return element;
}
static async video(CoreContext: NapCatCore, filePath: string, fileName: string = '', diyThumbPath: string = '', videotype: viedo_type = viedo_type.VIDEO_FORMAT_MP4): Promise<SendVideoElement> {
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
if (fileSize === 0) {
throw '文件异常大小为0';
}
let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
thumb = pathLib.dirname(thumb);
// log("thumb 目录", thumb)
let videoInfo = {
width: 1920, height: 1080,
time: 15,
format: 'mp4',
size: fileSize,
filePath
};
try {
videoInfo = await getVideoInfo(path);
//logDebug('视频信息', videoInfo);
} catch (e) {
logError('获取视频信息失败', e);
}
const createThumb = new Promise<string>((resolve, reject) => {
const thumbFileName = `${md5}_0.png`;
const thumbPath = pathLib.join(thumb, thumbFileName);
ffmpeg(filePath)
.on('end', () => {
})
.on('error', (err) => {
logDebug('获取视频封面失败,使用默认封面', err);
if (diyThumbPath) {
fs.copyFile(diyThumbPath, thumbPath).then(() => {
resolve(thumbPath);
}).catch(reject);
} else {
fs.writeFile(thumbPath, defaultVideoThumb).then(() => {
resolve(thumbPath);
}).catch(reject);
}
})
.screenshots({
timestamps: [0],
filename: thumbFileName,
folder: thumb,
size: videoInfo.width + 'x' + videoInfo.height
}).on('end', () => {
resolve(thumbPath);
});
});
const thumbPath = new Map();
const _thumbPath = await createThumb;
const thumbSize = (await fs.stat(_thumbPath)).size;
// log("生成缩略图", _thumbPath)
thumbPath.set(0, _thumbPath);
const thumbMd5 = await calculateFileMD5(_thumbPath);
const element: SendVideoElement = {
elementType: ElementType.VIDEO,
elementId: '',
videoElement: {
fileName: fileName || _fileName,
filePath: path,
videoMd5: md5,
thumbMd5,
fileTime: videoInfo.time,
thumbPath: thumbPath,
thumbSize,
thumbWidth: videoInfo.width,
thumbHeight: videoInfo.height,
fileSize: '' + fileSize,
//fileFormat: videotype
// fileUuid: "",
// transferStatus: 0,
// progress: 0,
// invalidState: 0,
// fileSubId: "",
// fileBizId: null,
// originVideoMd5: "",
// fileFormat: 2,
// import_rich_media_context: null,
// sourceVideoCodecFormat: 2
}
};
return element;
}
static async ptt(CoreContext: NapCatCore, pttPath: string): Promise<SendPttElement> {
const { converted, path: silkPath, duration } = await encodeSilk(pttPath);
// log("生成语音", silkPath, duration);
if (!silkPath) {
throw '语音转换失败, 请检查语音文件是否正常';
}
const { md5, fileName, path, fileSize } = await NTQQFileApi.uploadFile(silkPath!, ElementType.PTT);
if (fileSize === 0) {
throw '文件异常大小为0';
}
if (converted) {
fs.unlink(silkPath).then();
}
return {
elementType: ElementType.PTT,
elementId: '',
pttElement: {
fileName: fileName,
filePath: path,
md5HexStr: md5,
fileSize: fileSize,
// duration: Math.max(1, Math.round(fileSize / 1024 / 3)), // 一秒钟大概是3kb大小, 小于1秒的按1秒算
duration: duration || 1,
formatType: 1,
voiceType: 1,
voiceChangeType: 0,
canConvert2Text: true,
waveAmplitudes: [
0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17,
],
fileSubId: '',
playState: 1,
autoConvertText: 0,
}
};
}
// NodeIQQNTWrapperSession sendMsg [
// "0",
// {
// "peerUid": "u_e_RIxgTs2NaJ68h0PwOPSg",
// "chatType": 1,
// "guildId": ""
// },
// [
// {
// "elementId": "0",
// "elementType": 6,
// "faceElement": {
// "faceIndex": 0,
// "faceType": 5,
// "msgType": 0,
// "pokeType": 1,
// "pokeStrength": 0
// }
// }
// ],
// {}
// ]
static face(CoreContext: NapCatCore, faceId: number): SendFaceElement {
// 从face_config.json中获取表情名称
const sysFaces = faceConfig.sysface;
const emojiFaces = faceConfig.emoji;
const face: any = sysFaces.find((face) => face.QSid === faceId.toString());
faceId = parseInt(faceId.toString());
// let faceType = parseInt(faceId.toString().substring(0, 1));
let faceType = 1;
if (faceId >= 222) {
faceType = 2;
}
if (face.AniStickerType) {
faceType = 3;
}
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
faceIndex: faceId,
faceType,
faceText: face.QDes,
stickerId: face.AniStickerId,
stickerType: face.AniStickerType,
packId: face.AniStickerPackId,
sourceType: 1,
},
};
}
static mface(CoreContext: NapCatCore, emojiPackageId: number, emojiId: string, key: string, faceName: string): SendMarketFaceElement {
return {
elementType: ElementType.MFACE,
marketFaceElement: {
emojiPackageId,
emojiId,
key,
faceName: faceName || '[商城表情]',
},
};
}
static dice(CoreContext: NapCatCore, resultId: number | null): SendFaceElement {
// 实际测试并不能控制结果
// 随机1到6
// if (isNull(resultId)) resultId = Math.floor(Math.random() * 6) + 1;
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
faceIndex: FaceIndex.dice,
faceType: FaceType.dice,
'faceText': '[骰子]',
'packId': '1',
'stickerId': '33',
'sourceType': 1,
'stickerType': 2,
// resultId: resultId.toString(),
'surpriseId': '',
// "randomType": 1,
}
};
}
// 猜拳(石头剪刀布)表情
static rps(CoreContext: NapCatCore, resultId: number | null): SendFaceElement {
// 实际测试并不能控制结果
// if (isNull(resultId)) resultId = Math.floor(Math.random() * 3) + 1;
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
'faceIndex': FaceIndex.RPS,
'faceText': '[包剪锤]',
'faceType': 3,
'packId': '1',
'stickerId': '34',
'sourceType': 1,
'stickerType': 2,
// 'resultId': resultId.toString(),
'surpriseId': '',
// "randomType": 1,
}
};
}
static ark(CoreContext: NapCatCore, data: any): SendArkElement {
if (typeof data !== 'string') {
data = JSON.stringify(data);
}
return {
elementType: ElementType.ARK,
elementId: '',
arkElement: {
bytesData: data,
linkInfo: null,
subElementType: null
}
};
}
static markdown(CoreContext: NapCatCore, content: string): SendMarkdownElement {
return {
elementType: ElementType.MARKDOWN,
elementId: '',
markdownElement: {
content
}
};
}
}