mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f8890b309b | ||
![]() |
b5e578733f | ||
![]() |
51602b987e | ||
![]() |
b501af6e0e | ||
![]() |
81821e74d8 |
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 4,
|
"manifest_version": 4,
|
||||||
"type": "extension",
|
"type": "extension",
|
||||||
"name": "LLOneBot v3.21.0",
|
"name": "LLOneBot v3.22.1",
|
||||||
"slug": "LLOneBot",
|
"slug": "LLOneBot",
|
||||||
"description": "使你的NTQQ支持OneBot11协议进行QQ机器人开发, 不支持商店在线更新",
|
"description": "使你的NTQQ支持OneBot11协议进行QQ机器人开发, 不支持商店在线更新",
|
||||||
"version": "3.21.0",
|
"version": "3.22.1",
|
||||||
"icon": "./icon.jpg",
|
"icon": "./icon.jpg",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -1,15 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
AtType,
|
AtType,
|
||||||
ElementType,
|
ElementType, FaceIndex,
|
||||||
PicType,
|
FaceType,
|
||||||
SendArkElement,
|
PicType,
|
||||||
SendFaceElement,
|
SendArkElement,
|
||||||
SendFileElement,
|
SendFaceElement,
|
||||||
SendPicElement,
|
SendFileElement,
|
||||||
SendPttElement,
|
SendPicElement,
|
||||||
SendReplyElement,
|
SendPttElement,
|
||||||
SendTextElement,
|
SendReplyElement,
|
||||||
SendVideoElement
|
SendTextElement,
|
||||||
|
SendVideoElement
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import {promises as fs} from "node:fs";
|
import {promises as fs} from "node:fs";
|
||||||
import ffmpeg from "fluent-ffmpeg"
|
import ffmpeg from "fluent-ffmpeg"
|
||||||
@@ -18,239 +19,301 @@ import {calculateFileMD5, isGIF} from "../common/utils/file";
|
|||||||
import {log} from "../common/utils/log";
|
import {log} from "../common/utils/log";
|
||||||
import {defaultVideoThumb, getVideoInfo} from "../common/utils/video";
|
import {defaultVideoThumb, getVideoInfo} from "../common/utils/video";
|
||||||
import {encodeSilk} from "../common/utils/audio";
|
import {encodeSilk} from "../common/utils/audio";
|
||||||
|
import {isNull} from "../common/utils";
|
||||||
|
|
||||||
|
|
||||||
export class SendMsgElementConstructor {
|
export class SendMsgElementConstructor {
|
||||||
|
|
||||||
static poke(groupCode: string, uin: string){
|
static poke(groupCode: string, uin: string) {
|
||||||
return null
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
static text(content: string): SendTextElement {
|
||||||
|
return {
|
||||||
|
elementType: ElementType.TEXT,
|
||||||
|
elementId: "",
|
||||||
|
textElement: {
|
||||||
|
content,
|
||||||
|
atType: AtType.notAt,
|
||||||
|
atUid: "",
|
||||||
|
atTinyId: "",
|
||||||
|
atNtUid: "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static at(atUid: string, atNtUid: string, atType: AtType, atName: string): SendTextElement {
|
||||||
|
return {
|
||||||
|
elementType: ElementType.TEXT,
|
||||||
|
elementId: "",
|
||||||
|
textElement: {
|
||||||
|
content: `@${atName}`,
|
||||||
|
atType,
|
||||||
|
atUid,
|
||||||
|
atTinyId: "",
|
||||||
|
atNtUid,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static reply(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 text(content: string): SendTextElement {
|
}
|
||||||
return {
|
|
||||||
elementType: ElementType.TEXT,
|
static async pic(picPath: string, summary: string = "", subType: 0 | 1 = 0): Promise<SendPicElement> {
|
||||||
elementId: "",
|
const {md5, fileName, path, fileSize} = await NTQQFileApi.uploadFile(picPath, ElementType.PIC, subType);
|
||||||
textElement: {
|
if (fileSize === 0) {
|
||||||
content,
|
throw "文件异常,大小为0";
|
||||||
atType: AtType.notAt,
|
}
|
||||||
atUid: "",
|
const imageSize = await NTQQFileApi.getImageSize(picPath);
|
||||||
atTinyId: "",
|
const picElement = {
|
||||||
atNtUid: "",
|
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
|
||||||
|
};
|
||||||
|
log("图片信息", picElement)
|
||||||
|
return {
|
||||||
|
elementType: ElementType.PIC,
|
||||||
|
elementId: "",
|
||||||
|
picElement,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static async file(filePath: string, fileName: string = ""): Promise<SendFileElement> {
|
||||||
|
const {md5, fileName: _fileName, path, fileSize} = await NTQQFileApi.uploadFile(filePath, ElementType.FILE);
|
||||||
|
if (fileSize === 0) {
|
||||||
|
throw "文件异常,大小为0";
|
||||||
|
}
|
||||||
|
let element: SendFileElement = {
|
||||||
|
elementType: ElementType.FILE,
|
||||||
|
elementId: "",
|
||||||
|
fileElement: {
|
||||||
|
fileName: fileName || _fileName,
|
||||||
|
"filePath": path,
|
||||||
|
"fileSize": (fileSize).toString(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static at(atUid: string, atNtUid: string, atType: AtType, atName: string): SendTextElement {
|
return element;
|
||||||
return {
|
}
|
||||||
elementType: ElementType.TEXT,
|
|
||||||
elementId: "",
|
static async video(filePath: string, fileName: string = "", diyThumbPath: string = ""): Promise<SendVideoElement> {
|
||||||
textElement: {
|
let {fileName: _fileName, path, fileSize, md5} = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
|
||||||
content: `@${atName}`,
|
if (fileSize === 0) {
|
||||||
atType,
|
throw "文件异常,大小为0";
|
||||||
atUid,
|
|
||||||
atTinyId: "",
|
|
||||||
atNtUid,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
const pathLib = require("path");
|
||||||
static reply(msgSeq: string, msgId: string, senderUin: string, senderUinStr: string): SendReplyElement {
|
let thumbDir = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`)
|
||||||
return {
|
thumbDir = pathLib.dirname(thumbDir)
|
||||||
elementType: ElementType.REPLY,
|
// log("thumb 目录", thumb)
|
||||||
elementId: "",
|
let videoInfo = {
|
||||||
replyElement: {
|
width: 1920, height: 1080,
|
||||||
replayMsgSeq: msgSeq, // raw.msgSeq
|
time: 15,
|
||||||
replayMsgId: msgId, // raw.msgId
|
format: "mp4",
|
||||||
senderUin: senderUin,
|
size: fileSize,
|
||||||
senderUinStr: senderUinStr,
|
filePath
|
||||||
}
|
};
|
||||||
}
|
try {
|
||||||
|
videoInfo = await getVideoInfo(path);
|
||||||
|
log("视频信息", videoInfo)
|
||||||
|
} catch (e) {
|
||||||
|
log("获取视频信息失败", e)
|
||||||
}
|
}
|
||||||
|
const createThumb = new Promise<string>((resolve, reject) => {
|
||||||
|
const thumbFileName = `${md5}_0.png`
|
||||||
|
const thumbPath = pathLib.join(thumbDir, thumbFileName)
|
||||||
|
log("开始生成视频缩略图", filePath);
|
||||||
|
let completed = false;
|
||||||
|
|
||||||
static async pic(picPath: string, summary: string = "", subType: 0|1=0): Promise<SendPicElement> {
|
function useDefaultThumb() {
|
||||||
const {md5, fileName, path, fileSize} = await NTQQFileApi.uploadFile(picPath, ElementType.PIC, subType);
|
if (completed) return;
|
||||||
if (fileSize === 0) {
|
log("获取视频封面失败,使用默认封面");
|
||||||
throw "文件异常,大小为0";
|
fs.writeFile(thumbPath, defaultVideoThumb).then(() => {
|
||||||
}
|
resolve(thumbPath);
|
||||||
const imageSize = await NTQQFileApi.getImageSize(picPath);
|
}).catch(reject)
|
||||||
const picElement = {
|
}
|
||||||
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
|
|
||||||
};
|
|
||||||
log("图片信息", picElement)
|
|
||||||
return {
|
|
||||||
elementType: ElementType.PIC,
|
|
||||||
elementId: "",
|
|
||||||
picElement,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static async file(filePath: string, fileName: string = ""): Promise<SendFileElement> {
|
setTimeout(useDefaultThumb, 5000);
|
||||||
const {md5, fileName: _fileName, path, fileSize} = await NTQQFileApi.uploadFile(filePath, ElementType.FILE);
|
ffmpeg(filePath)
|
||||||
if (fileSize === 0) {
|
.on("end", () => {
|
||||||
throw "文件异常,大小为0";
|
|
||||||
}
|
|
||||||
let element: SendFileElement = {
|
|
||||||
elementType: ElementType.FILE,
|
|
||||||
elementId: "",
|
|
||||||
fileElement: {
|
|
||||||
fileName: fileName || _fileName,
|
|
||||||
"filePath": path,
|
|
||||||
"fileSize": (fileSize).toString(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async video(filePath: string, fileName: string = "", diyThumbPath: string = ""): Promise<SendVideoElement> {
|
|
||||||
let {fileName: _fileName, path, fileSize, md5} = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
|
|
||||||
if (fileSize === 0) {
|
|
||||||
throw "文件异常,大小为0";
|
|
||||||
}
|
|
||||||
const pathLib = require("path");
|
|
||||||
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);
|
|
||||||
log("视频信息", videoInfo)
|
|
||||||
} catch (e) {
|
|
||||||
log("获取视频信息失败", 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) => {
|
|
||||||
log("获取视频封面失败,使用默认封面", 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);
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
let thumbPath = new Map()
|
.on("error", (err) => {
|
||||||
const _thumbPath = await createThumb;
|
if (diyThumbPath) {
|
||||||
const thumbSize = (await fs.stat(_thumbPath)).size;
|
fs.copyFile(diyThumbPath, thumbPath).then(() => {
|
||||||
// log("生成缩略图", _thumbPath)
|
completed = true;
|
||||||
thumbPath.set(0, _thumbPath)
|
resolve(thumbPath);
|
||||||
const thumbMd5 = await calculateFileMD5(_thumbPath);
|
}).catch(reject)
|
||||||
let element: SendVideoElement = {
|
} else {
|
||||||
elementType: ElementType.VIDEO,
|
useDefaultThumb()
|
||||||
elementId: "",
|
}
|
||||||
videoElement: {
|
})
|
||||||
fileName: fileName || _fileName,
|
.screenshots({
|
||||||
filePath: path,
|
timestamps: [0],
|
||||||
videoMd5: md5,
|
filename: thumbFileName,
|
||||||
thumbMd5,
|
folder: thumbDir,
|
||||||
fileTime: videoInfo.time,
|
size: videoInfo.width + "x" + videoInfo.height
|
||||||
thumbPath: thumbPath,
|
}).on("end", () => {
|
||||||
thumbSize,
|
log("生成视频缩略图", thumbPath)
|
||||||
thumbWidth: videoInfo.width,
|
completed = true;
|
||||||
thumbHeight: videoInfo.height,
|
resolve(thumbPath);
|
||||||
fileSize: "" + fileSize,
|
})
|
||||||
// fileUuid: "",
|
})
|
||||||
// transferStatus: 0,
|
let thumbPath = new Map()
|
||||||
// progress: 0,
|
const _thumbPath = await createThumb;
|
||||||
// invalidState: 0,
|
log("生成缩略图", _thumbPath)
|
||||||
// fileSubId: "",
|
const thumbSize = (await fs.stat(_thumbPath)).size;
|
||||||
// fileBizId: null,
|
// log("生成缩略图", _thumbPath)
|
||||||
// originVideoMd5: "",
|
thumbPath.set(0, _thumbPath)
|
||||||
// fileFormat: 2,
|
const thumbMd5 = await calculateFileMD5(_thumbPath);
|
||||||
// import_rich_media_context: null,
|
let element: SendVideoElement = {
|
||||||
// sourceVideoCodecFormat: 2
|
elementType: ElementType.VIDEO,
|
||||||
}
|
elementId: "",
|
||||||
}
|
videoElement: {
|
||||||
return element;
|
fileName: fileName || _fileName,
|
||||||
|
filePath: path,
|
||||||
|
videoMd5: md5,
|
||||||
|
thumbMd5,
|
||||||
|
fileTime: videoInfo.time,
|
||||||
|
thumbPath: thumbPath,
|
||||||
|
thumbSize,
|
||||||
|
thumbWidth: videoInfo.width,
|
||||||
|
thumbHeight: videoInfo.height,
|
||||||
|
fileSize: "" + fileSize,
|
||||||
|
// fileUuid: "",
|
||||||
|
// transferStatus: 0,
|
||||||
|
// progress: 0,
|
||||||
|
// invalidState: 0,
|
||||||
|
// fileSubId: "",
|
||||||
|
// fileBizId: null,
|
||||||
|
// originVideoMd5: "",
|
||||||
|
// fileFormat: 2,
|
||||||
|
// import_rich_media_context: null,
|
||||||
|
// sourceVideoCodecFormat: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
log("videoElement", element)
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
static async ptt(pttPath: string): Promise<SendPttElement> {
|
static async ptt(pttPath: string): Promise<SendPttElement> {
|
||||||
const {converted, path: silkPath, duration} = await encodeSilk(pttPath);
|
const {converted, path: silkPath, duration} = await encodeSilk(pttPath);
|
||||||
// log("生成语音", silkPath, duration);
|
// log("生成语音", silkPath, duration);
|
||||||
const {md5, fileName, path, fileSize} = await NTQQFileApi.uploadFile(silkPath, ElementType.PTT);
|
const {md5, fileName, path, fileSize} = await NTQQFileApi.uploadFile(silkPath, ElementType.PTT);
|
||||||
if (fileSize === 0) {
|
if (fileSize === 0) {
|
||||||
throw "文件异常,大小为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,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static face(faceId: number): SendFaceElement {
|
static face(faceId: number): SendFaceElement {
|
||||||
return {
|
faceId = parseInt(faceId.toString());
|
||||||
elementType: ElementType.FACE,
|
return {
|
||||||
elementId: "",
|
elementType: ElementType.FACE,
|
||||||
faceElement: {
|
elementId: "",
|
||||||
faceIndex: faceId,
|
faceElement: {
|
||||||
faceType: 1
|
faceIndex: faceId,
|
||||||
}
|
faceType: faceId < 222 ? FaceType.normal : FaceType.normal2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static ark(data: any): SendArkElement {
|
static dice(resultId: number | null): SendFaceElement {
|
||||||
return {
|
// 实际测试并不能控制结果
|
||||||
elementType: ElementType.ARK,
|
|
||||||
elementId: "",
|
// 随机1到6
|
||||||
arkElement: {
|
if (isNull(resultId)) resultId = Math.floor(Math.random() * 6) + 1;
|
||||||
bytesData: data,
|
return {
|
||||||
linkInfo: null,
|
elementType: ElementType.FACE,
|
||||||
subElementType: null
|
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(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(data: any): SendArkElement {
|
||||||
|
return {
|
||||||
|
elementType: ElementType.ARK,
|
||||||
|
elementId: "",
|
||||||
|
arkElement: {
|
||||||
|
bytesData: data,
|
||||||
|
linkInfo: null,
|
||||||
|
subElementType: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
import {BrowserWindow} from 'electron';
|
import {BrowserWindow} from 'electron';
|
||||||
import {NTQQApiClass} from "./ntcall";
|
import {NTQQApiClass, NTQQApiMethod} from "./ntcall";
|
||||||
import {NTQQMsgApi, sendMessagePool} from "./api/msg"
|
import {NTQQMsgApi, sendMessagePool} from "./api/msg"
|
||||||
import {ChatType, Group, GroupMember, GroupMemberRole, RawMessage, User} from "./types";
|
import {ChatType, Group, GroupMember, GroupMemberRole, RawMessage, User} from "./types";
|
||||||
import {friends, getGroupMember, groups, selfInfo, tempGroupCodeMap, uidMaps} from "../common/data";
|
import {friends, getFriend, getGroupMember, groups, selfInfo, tempGroupCodeMap, uidMaps} from "../common/data";
|
||||||
import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent";
|
import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent";
|
||||||
import {v4 as uuidv4} from "uuid"
|
import {v4 as uuidv4} from "uuid"
|
||||||
import {postOB11Event} from "../onebot11/server/postOB11Event";
|
import {postOB11Event} from "../onebot11/server/postOB11Event";
|
||||||
@@ -11,446 +11,503 @@ import fs from "fs";
|
|||||||
import {dbUtil} from "../common/db";
|
import {dbUtil} from "../common/db";
|
||||||
import {NTQQGroupApi} from "./api/group";
|
import {NTQQGroupApi} from "./api/group";
|
||||||
import {log} from "../common/utils/log";
|
import {log} from "../common/utils/log";
|
||||||
import {sleep} from "../common/utils/helper";
|
import {isNumeric, sleep} from "../common/utils/helper";
|
||||||
import {OB11Constructor} from "../onebot11/constructor";
|
import {OB11Constructor} from "../onebot11/constructor";
|
||||||
|
|
||||||
export let hookApiCallbacks: Record<string, (apiReturn: any) => void> = {}
|
export let hookApiCallbacks: Record<string, (apiReturn: any) => void> = {}
|
||||||
|
|
||||||
export let ReceiveCmdS = {
|
export let ReceiveCmdS = {
|
||||||
RECENT_CONTACT: "nodeIKernelRecentContactListener/onRecentContactListChangedVer2",
|
RECENT_CONTACT: "nodeIKernelRecentContactListener/onRecentContactListChangedVer2",
|
||||||
UPDATE_MSG: "nodeIKernelMsgListener/onMsgInfoListUpdate",
|
UPDATE_MSG: "nodeIKernelMsgListener/onMsgInfoListUpdate",
|
||||||
UPDATE_ACTIVE_MSG: "nodeIKernelMsgListener/onActiveMsgInfoUpdate",
|
UPDATE_ACTIVE_MSG: "nodeIKernelMsgListener/onActiveMsgInfoUpdate",
|
||||||
NEW_MSG: `nodeIKernelMsgListener/onRecvMsg`,
|
NEW_MSG: `nodeIKernelMsgListener/onRecvMsg`,
|
||||||
NEW_ACTIVE_MSG: `nodeIKernelMsgListener/onRecvActiveMsg`,
|
NEW_ACTIVE_MSG: `nodeIKernelMsgListener/onRecvActiveMsg`,
|
||||||
SELF_SEND_MSG: "nodeIKernelMsgListener/onAddSendMsg",
|
SELF_SEND_MSG: "nodeIKernelMsgListener/onAddSendMsg",
|
||||||
USER_INFO: "nodeIKernelProfileListener/onProfileSimpleChanged",
|
USER_INFO: "nodeIKernelProfileListener/onProfileSimpleChanged",
|
||||||
USER_DETAIL_INFO: "nodeIKernelProfileListener/onProfileDetailInfoChanged",
|
USER_DETAIL_INFO: "nodeIKernelProfileListener/onProfileDetailInfoChanged",
|
||||||
GROUPS: "nodeIKernelGroupListener/onGroupListUpdate",
|
GROUPS: "nodeIKernelGroupListener/onGroupListUpdate",
|
||||||
GROUPS_STORE: "onGroupListUpdate",
|
GROUPS_STORE: "onGroupListUpdate",
|
||||||
GROUP_MEMBER_INFO_UPDATE: "nodeIKernelGroupListener/onMemberInfoChange",
|
GROUP_MEMBER_INFO_UPDATE: "nodeIKernelGroupListener/onMemberInfoChange",
|
||||||
FRIENDS: "onBuddyListChange",
|
FRIENDS: "onBuddyListChange",
|
||||||
MEDIA_DOWNLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaDownloadComplete",
|
MEDIA_DOWNLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaDownloadComplete",
|
||||||
UNREAD_GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated",
|
UNREAD_GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated",
|
||||||
GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupSingleScreenNotifies",
|
GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupSingleScreenNotifies",
|
||||||
FRIEND_REQUEST: "nodeIKernelBuddyListener/onBuddyReqChange",
|
FRIEND_REQUEST: "nodeIKernelBuddyListener/onBuddyReqChange",
|
||||||
SELF_STATUS: 'nodeIKernelProfileListener/onSelfStatusChanged',
|
SELF_STATUS: 'nodeIKernelProfileListener/onSelfStatusChanged',
|
||||||
CACHE_SCAN_FINISH: "nodeIKernelStorageCleanListener/onFinishScan",
|
CACHE_SCAN_FINISH: "nodeIKernelStorageCleanListener/onFinishScan",
|
||||||
MEDIA_UPLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaUploadComplete",
|
MEDIA_UPLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaUploadComplete",
|
||||||
SKEY_UPDATE: "onSkeyUpdate"
|
SKEY_UPDATE: "onSkeyUpdate"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReceiveCmd = typeof ReceiveCmdS[keyof typeof ReceiveCmdS]
|
export type ReceiveCmd = typeof ReceiveCmdS[keyof typeof ReceiveCmdS]
|
||||||
|
|
||||||
interface NTQQApiReturnData<PayloadType = unknown> extends Array<any> {
|
interface NTQQApiReturnData<PayloadType = unknown> extends Array<any> {
|
||||||
0: {
|
0: {
|
||||||
"type": "request",
|
"type": "request",
|
||||||
"eventName": NTQQApiClass,
|
"eventName": NTQQApiClass,
|
||||||
"callbackId"?: string
|
"callbackId"?: string
|
||||||
},
|
},
|
||||||
1:
|
1:
|
||||||
{
|
{
|
||||||
cmdName: ReceiveCmd,
|
cmdName: ReceiveCmd,
|
||||||
cmdType: "event",
|
cmdType: "event",
|
||||||
payload: PayloadType
|
payload: PayloadType
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
let receiveHooks: Array<{
|
let receiveHooks: Array<{
|
||||||
method: ReceiveCmd[],
|
method: ReceiveCmd[],
|
||||||
hookFunc: ((payload: any) => void | Promise<void>)
|
hookFunc: ((payload: any) => void | Promise<void>)
|
||||||
id: string
|
id: string
|
||||||
}> = []
|
}> = []
|
||||||
|
|
||||||
export function hookNTQQApiReceive(window: BrowserWindow) {
|
let callHooks: Array<{
|
||||||
const originalSend = window.webContents.send;
|
method: NTQQApiMethod[],
|
||||||
const patchSend = (channel: string, ...args: NTQQApiReturnData) => {
|
hookFunc: ((callParams: unknown[]) => void | Promise<void>)
|
||||||
// console.log("hookNTQQApiReceive", channel, args)
|
}> = []
|
||||||
let isLogger = false
|
|
||||||
try {
|
|
||||||
isLogger = args[0]?.eventName?.startsWith("ns-LoggerApi")
|
export function hookNTQQApiReceive(window: BrowserWindow) {
|
||||||
} catch (e) {
|
const originalSend = window.webContents.send;
|
||||||
|
const patchSend = (channel: string, ...args: NTQQApiReturnData) => {
|
||||||
|
// console.log("hookNTQQApiReceive", channel, args)
|
||||||
|
let isLogger = false
|
||||||
|
try {
|
||||||
|
isLogger = args[0]?.eventName?.startsWith("ns-LoggerApi")
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
}
|
|
||||||
if (!isLogger) {
|
|
||||||
try {
|
|
||||||
HOOK_LOG && log(`received ntqq api message: ${channel}`, args)
|
|
||||||
} catch (e) {
|
|
||||||
log("hook log error", e, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (args?.[1] instanceof Array) {
|
|
||||||
for (let receiveData of args?.[1]) {
|
|
||||||
const ntQQApiMethodName = receiveData.cmdName;
|
|
||||||
// log(`received ntqq api message: ${channel} ${ntQQApiMethodName}`, JSON.stringify(receiveData))
|
|
||||||
for (let hook of receiveHooks) {
|
|
||||||
if (hook.method.includes(ntQQApiMethodName)) {
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
let _ = hook.hookFunc(receiveData.payload)
|
|
||||||
if (hook.hookFunc.constructor.name === "AsyncFunction") {
|
|
||||||
(_ as Promise<void>).then()
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log("hook error", e, receiveData.payload)
|
|
||||||
}
|
|
||||||
}).then()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (args[0]?.callbackId) {
|
|
||||||
// log("hookApiCallback", hookApiCallbacks, args)
|
|
||||||
const callbackId = args[0].callbackId;
|
|
||||||
if (hookApiCallbacks[callbackId]) {
|
|
||||||
// log("callback found")
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
hookApiCallbacks[callbackId](args[1]);
|
|
||||||
}).then()
|
|
||||||
delete hookApiCallbacks[callbackId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log("hookNTQQApiReceive error", e.stack.toString(), args)
|
|
||||||
}
|
|
||||||
originalSend.call(window.webContents, channel, ...args);
|
|
||||||
}
|
}
|
||||||
window.webContents.send = patchSend;
|
if (!isLogger) {
|
||||||
|
try {
|
||||||
|
HOOK_LOG && log(`received ntqq api message: ${channel}`, args)
|
||||||
|
} catch (e) {
|
||||||
|
log("hook log error", e, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (args?.[1] instanceof Array) {
|
||||||
|
for (let receiveData of args?.[1]) {
|
||||||
|
const ntQQApiMethodName = receiveData.cmdName;
|
||||||
|
// log(`received ntqq api message: ${channel} ${ntQQApiMethodName}`, JSON.stringify(receiveData))
|
||||||
|
for (let hook of receiveHooks) {
|
||||||
|
if (hook.method.includes(ntQQApiMethodName)) {
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let _ = hook.hookFunc(receiveData.payload)
|
||||||
|
if (hook.hookFunc.constructor.name === "AsyncFunction") {
|
||||||
|
(_ as Promise<void>).then()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log("hook error", e, receiveData.payload)
|
||||||
|
}
|
||||||
|
}).then()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (args[0]?.callbackId) {
|
||||||
|
// log("hookApiCallback", hookApiCallbacks, args)
|
||||||
|
const callbackId = args[0].callbackId;
|
||||||
|
if (hookApiCallbacks[callbackId]) {
|
||||||
|
// log("callback found")
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
hookApiCallbacks[callbackId](args[1]);
|
||||||
|
}).then()
|
||||||
|
delete hookApiCallbacks[callbackId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log("hookNTQQApiReceive error", e.stack.toString(), args)
|
||||||
|
}
|
||||||
|
originalSend.call(window.webContents, channel, ...args);
|
||||||
|
}
|
||||||
|
window.webContents.send = patchSend;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hookNTQQApiCall(window: BrowserWindow) {
|
export function hookNTQQApiCall(window: BrowserWindow) {
|
||||||
// 监听调用NTQQApi
|
// 监听调用NTQQApi
|
||||||
let webContents = window.webContents as any;
|
let webContents = window.webContents as any;
|
||||||
const ipc_message_proxy = webContents._events["-ipc-message"]?.[0] || webContents._events["-ipc-message"];
|
const ipc_message_proxy = webContents._events["-ipc-message"]?.[0] || webContents._events["-ipc-message"];
|
||||||
|
|
||||||
const proxyIpcMsg = new Proxy(ipc_message_proxy, {
|
const proxyIpcMsg = new Proxy(ipc_message_proxy, {
|
||||||
apply(target, thisArg, args) {
|
apply(target, thisArg, args) {
|
||||||
// console.log(thisArg, args);
|
// console.log(thisArg, args);
|
||||||
let isLogger = false
|
let isLogger = false
|
||||||
try {
|
try {
|
||||||
isLogger = args[3][0].eventName.startsWith("ns-LoggerApi")
|
isLogger = args[3][0].eventName.startsWith("ns-LoggerApi")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!isLogger) {
|
if (!isLogger) {
|
||||||
try {
|
try {
|
||||||
HOOK_LOG && log("call NTQQ api", thisArg, args);
|
HOOK_LOG && log("call NTQQ api", thisArg, args);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target.apply(thisArg, args);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (webContents._events["-ipc-message"]?.[0]) {
|
|
||||||
webContents._events["-ipc-message"][0] = proxyIpcMsg;
|
|
||||||
} else {
|
|
||||||
webContents._events["-ipc-message"] = proxyIpcMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ipc_invoke_proxy = webContents._events["-ipc-invoke"]?.[0] || webContents._events["-ipc-invoke"];
|
|
||||||
const proxyIpcInvoke = new Proxy(ipc_invoke_proxy, {
|
|
||||||
apply(target, thisArg, args) {
|
|
||||||
// console.log(args);
|
|
||||||
HOOK_LOG && log("call NTQQ invoke api", thisArg, args)
|
|
||||||
args[0]["_replyChannel"]["sendReply"] = new Proxy(args[0]["_replyChannel"]["sendReply"], {
|
|
||||||
apply(sendtarget, sendthisArg, sendargs) {
|
|
||||||
sendtarget.apply(sendthisArg, sendargs);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let ret = target.apply(thisArg, args);
|
|
||||||
try {
|
|
||||||
HOOK_LOG && log("call NTQQ invoke api return", ret)
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
});
|
try {
|
||||||
if (webContents._events["-ipc-invoke"]?.[0]) {
|
const _args: unknown[] = args[3][1];
|
||||||
webContents._events["-ipc-invoke"][0] = proxyIpcInvoke;
|
const cmdName: NTQQApiMethod = _args[0] as NTQQApiMethod;
|
||||||
} else {
|
const callParams = _args.slice(1);
|
||||||
webContents._events["-ipc-invoke"] = proxyIpcInvoke;
|
callHooks.forEach(hook => {
|
||||||
|
if (hook.method.includes(cmdName)) {
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let _ = hook.hookFunc(callParams)
|
||||||
|
if (hook.hookFunc.constructor.name === "AsyncFunction") {
|
||||||
|
(_ as Promise<void>).then()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log("hook call error", e, _args)
|
||||||
|
}
|
||||||
|
}).then()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target.apply(thisArg, args);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (webContents._events["-ipc-message"]?.[0]) {
|
||||||
|
webContents._events["-ipc-message"][0] = proxyIpcMsg;
|
||||||
|
} else {
|
||||||
|
webContents._events["-ipc-message"] = proxyIpcMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ipc_invoke_proxy = webContents._events["-ipc-invoke"]?.[0] || webContents._events["-ipc-invoke"];
|
||||||
|
const proxyIpcInvoke = new Proxy(ipc_invoke_proxy, {
|
||||||
|
apply(target, thisArg, args) {
|
||||||
|
// console.log(args);
|
||||||
|
HOOK_LOG && log("call NTQQ invoke api", thisArg, args)
|
||||||
|
args[0]["_replyChannel"]["sendReply"] = new Proxy(args[0]["_replyChannel"]["sendReply"], {
|
||||||
|
apply(sendtarget, sendthisArg, sendargs) {
|
||||||
|
sendtarget.apply(sendthisArg, sendargs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let ret = target.apply(thisArg, args);
|
||||||
|
try {
|
||||||
|
HOOK_LOG && log("call NTQQ invoke api return", ret)
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
if (webContents._events["-ipc-invoke"]?.[0]) {
|
||||||
|
webContents._events["-ipc-invoke"][0] = proxyIpcInvoke;
|
||||||
|
} else {
|
||||||
|
webContents._events["-ipc-invoke"] = proxyIpcInvoke;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerReceiveHook<PayloadType>(method: ReceiveCmd | ReceiveCmd[], hookFunc: (payload: PayloadType) => void): string {
|
export function registerReceiveHook<PayloadType>(method: ReceiveCmd | ReceiveCmd[], hookFunc: (payload: PayloadType) => void): string {
|
||||||
const id = uuidv4()
|
const id = uuidv4()
|
||||||
if (!Array.isArray(method)) {
|
if (!Array.isArray(method)) {
|
||||||
method = [method]
|
method = [method]
|
||||||
}
|
}
|
||||||
receiveHooks.push({
|
receiveHooks.push({
|
||||||
method,
|
method,
|
||||||
hookFunc,
|
hookFunc,
|
||||||
id
|
id
|
||||||
})
|
})
|
||||||
return id;
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerCallHook(method: NTQQApiMethod | NTQQApiMethod[], hookFunc: (callParams: unknown[]) => void | Promise<void>): void {
|
||||||
|
if (!Array.isArray(method)) {
|
||||||
|
method = [method]
|
||||||
|
}
|
||||||
|
callHooks.push({
|
||||||
|
method,
|
||||||
|
hookFunc
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeReceiveHook(id: string) {
|
export function removeReceiveHook(id: string) {
|
||||||
const index = receiveHooks.findIndex(h => h.id === id)
|
const index = receiveHooks.findIndex(h => h.id === id)
|
||||||
receiveHooks.splice(index, 1);
|
receiveHooks.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let activatedGroups: string[] = [];
|
let activatedGroups: string[] = [];
|
||||||
|
|
||||||
async function updateGroups(_groups: Group[], needUpdate: boolean = true) {
|
async function updateGroups(_groups: Group[], needUpdate: boolean = true) {
|
||||||
for (let group of _groups) {
|
for (let group of _groups) {
|
||||||
log("update group", group)
|
log("update group", group)
|
||||||
// if (!activatedGroups.includes(group.groupCode)) {
|
// if (!activatedGroups.includes(group.groupCode)) {
|
||||||
NTQQMsgApi.activateChat({peerUid: group.groupCode, chatType: ChatType.group}).then((r) => {
|
NTQQMsgApi.activateChat({peerUid: group.groupCode, chatType: ChatType.group}).then((r) => {
|
||||||
// activatedGroups.push(group.groupCode);
|
// activatedGroups.push(group.groupCode);
|
||||||
// log(`激活群聊天窗口${group.groupName}(${group.groupCode})`, r)
|
// log(`激活群聊天窗口${group.groupName}(${group.groupCode})`, r)
|
||||||
// if (r.result !== 0) {
|
// if (r.result !== 0) {
|
||||||
// setTimeout(() => NTQQMsgApi.activateGroupChat(group.groupCode).then(r => log(`再次激活群聊天窗口${group.groupName}(${group.groupCode})`, r)), 500);
|
// setTimeout(() => NTQQMsgApi.activateGroupChat(group.groupCode).then(r => log(`再次激活群聊天窗口${group.groupName}(${group.groupCode})`, r)), 500);
|
||||||
// }else {
|
// }else {
|
||||||
// }
|
// }
|
||||||
}).catch(log)
|
}).catch(log)
|
||||||
// }
|
// }
|
||||||
let existGroup = groups.find(g => g.groupCode == group.groupCode);
|
let existGroup = groups.find(g => g.groupCode == group.groupCode);
|
||||||
if (existGroup) {
|
if (existGroup) {
|
||||||
Object.assign(existGroup, group);
|
Object.assign(existGroup, group);
|
||||||
} else {
|
} else {
|
||||||
groups.push(group);
|
groups.push(group);
|
||||||
existGroup = group;
|
existGroup = group;
|
||||||
}
|
|
||||||
|
|
||||||
if (needUpdate) {
|
|
||||||
const members = await NTQQGroupApi.getGroupMembers(group.groupCode);
|
|
||||||
|
|
||||||
if (members) {
|
|
||||||
existGroup.members = members;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needUpdate) {
|
||||||
|
const members = await NTQQGroupApi.getGroupMembers(group.groupCode);
|
||||||
|
|
||||||
|
if (members) {
|
||||||
|
existGroup.members = members;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processGroupEvent(payload: { groupList: Group[] }) {
|
async function processGroupEvent(payload: { groupList: Group[] }) {
|
||||||
try {
|
try {
|
||||||
const newGroupList = payload.groupList;
|
const newGroupList = payload.groupList;
|
||||||
for (const group of newGroupList) {
|
for (const group of newGroupList) {
|
||||||
let existGroup = groups.find(g => g.groupCode == group.groupCode);
|
let existGroup = groups.find(g => g.groupCode == group.groupCode);
|
||||||
if (existGroup) {
|
if (existGroup) {
|
||||||
if (existGroup.memberCount > group.memberCount) {
|
if (existGroup.memberCount > group.memberCount) {
|
||||||
log(`群(${group.groupCode})成员数量减少${existGroup.memberCount} -> ${group.memberCount}`);
|
log(`群(${group.groupCode})成员数量减少${existGroup.memberCount} -> ${group.memberCount}`);
|
||||||
const oldMembers = existGroup.members;
|
const oldMembers = existGroup.members;
|
||||||
|
|
||||||
await sleep(200); // 如果请求QQ API的速度过快,通常无法正确拉取到最新的群信息,因此这里人为引入一个延时
|
await sleep(200); // 如果请求QQ API的速度过快,通常无法正确拉取到最新的群信息,因此这里人为引入一个延时
|
||||||
const newMembers = await NTQQGroupApi.getGroupMembers(group.groupCode);
|
const newMembers = await NTQQGroupApi.getGroupMembers(group.groupCode);
|
||||||
|
|
||||||
group.members = newMembers;
|
group.members = newMembers;
|
||||||
const newMembersSet = new Set<string>(); // 建立索引降低时间复杂度
|
const newMembersSet = new Set<string>(); // 建立索引降低时间复杂度
|
||||||
|
|
||||||
for (const member of newMembers) {
|
for (const member of newMembers) {
|
||||||
newMembersSet.add(member.uin);
|
newMembersSet.add(member.uin);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断bot是否是管理员,如果是管理员不需要从这里得知有人退群,这里的退群无法得知是主动退群还是被踢
|
|
||||||
let bot = await getGroupMember(group.groupCode, selfInfo.uin)
|
|
||||||
if (bot.role == GroupMemberRole.admin || bot.role == GroupMemberRole.owner) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for (const member of oldMembers) {
|
|
||||||
if (!newMembersSet.has(member.uin) && member.uin != selfInfo.uin) {
|
|
||||||
postOB11Event(new OB11GroupDecreaseEvent(parseInt(group.groupCode), parseInt(member.uin), parseInt(member.uin), "leave"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 判断bot是否是管理员,如果是管理员不需要从这里得知有人退群,这里的退群无法得知是主动退群还是被踢
|
||||||
|
let bot = await getGroupMember(group.groupCode, selfInfo.uin)
|
||||||
|
if (bot.role == GroupMemberRole.admin || bot.role == GroupMemberRole.owner) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (const member of oldMembers) {
|
||||||
|
if (!newMembersSet.has(member.uin) && member.uin != selfInfo.uin) {
|
||||||
|
postOB11Event(new OB11GroupDecreaseEvent(parseInt(group.groupCode), parseInt(member.uin), parseInt(member.uin), "leave"));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGroups(newGroupList, false).then();
|
}
|
||||||
} catch (e) {
|
|
||||||
updateGroups(payload.groupList).then();
|
|
||||||
log("更新群信息错误", e.stack.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateGroups(newGroupList, false).then();
|
||||||
|
} catch (e) {
|
||||||
|
updateGroups(payload.groupList).then();
|
||||||
|
log("更新群信息错误", e.stack.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 群列表变动
|
// 群列表变动
|
||||||
registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmdS.GROUPS, (payload) => {
|
registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmdS.GROUPS, (payload) => {
|
||||||
// updateType 3是群列表变动,2是群成员变动
|
// updateType 3是群列表变动,2是群成员变动
|
||||||
// log("群列表变动", payload.updateType, payload.groupList)
|
// log("群列表变动", payload.updateType, payload.groupList)
|
||||||
if (payload.updateType != 2) {
|
if (payload.updateType != 2) {
|
||||||
updateGroups(payload.groupList).then();
|
updateGroups(payload.groupList).then();
|
||||||
} else {
|
} else {
|
||||||
if (process.platform == "win32") {
|
if (process.platform == "win32") {
|
||||||
processGroupEvent(payload).then();
|
processGroupEvent(payload).then();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmdS.GROUPS_STORE, (payload) => {
|
registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmdS.GROUPS_STORE, (payload) => {
|
||||||
// updateType 3是群列表变动,2是群成员变动
|
// updateType 3是群列表变动,2是群成员变动
|
||||||
// log("群列表变动", payload.updateType, payload.groupList)
|
// log("群列表变动", payload.updateType, payload.groupList)
|
||||||
if (payload.updateType != 2) {
|
if (payload.updateType != 2) {
|
||||||
updateGroups(payload.groupList).then();
|
updateGroups(payload.groupList).then();
|
||||||
} else {
|
} else {
|
||||||
if (process.platform != "win32") {
|
if (process.platform != "win32") {
|
||||||
processGroupEvent(payload).then();
|
processGroupEvent(payload).then();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
registerReceiveHook<{
|
registerReceiveHook<{
|
||||||
groupCode: string,
|
groupCode: string,
|
||||||
dataSource: number,
|
dataSource: number,
|
||||||
members: Set<GroupMember>
|
members: Set<GroupMember>
|
||||||
}>(ReceiveCmdS.GROUP_MEMBER_INFO_UPDATE, async (payload) => {
|
}>(ReceiveCmdS.GROUP_MEMBER_INFO_UPDATE, async (payload) => {
|
||||||
const groupCode = payload.groupCode;
|
const groupCode = payload.groupCode;
|
||||||
const members = Array.from(payload.members.values());
|
const members = Array.from(payload.members.values());
|
||||||
// log("群成员信息变动", groupCode, members)
|
// log("群成员信息变动", groupCode, members)
|
||||||
for (const member of members) {
|
for (const member of members) {
|
||||||
const existMember = await getGroupMember(groupCode, member.uin);
|
const existMember = await getGroupMember(groupCode, member.uin);
|
||||||
if (existMember) {
|
if (existMember) {
|
||||||
Object.assign(existMember, member);
|
Object.assign(existMember, member);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// const existGroup = groups.find(g => g.groupCode == groupCode);
|
}
|
||||||
// if (existGroup) {
|
// const existGroup = groups.find(g => g.groupCode == groupCode);
|
||||||
// log("对比群成员", existGroup.members, members)
|
// if (existGroup) {
|
||||||
// for (const member of members) {
|
// log("对比群成员", existGroup.members, members)
|
||||||
// const existMember = existGroup.members.find(m => m.uin == member.uin);
|
// for (const member of members) {
|
||||||
// if (existMember) {
|
// const existMember = existGroup.members.find(m => m.uin == member.uin);
|
||||||
// log("对比群名片", existMember.cardName, member.cardName)
|
// if (existMember) {
|
||||||
// if (existMember.cardName != member.cardName) {
|
// log("对比群名片", existMember.cardName, member.cardName)
|
||||||
// postOB11Event(new OB11GroupCardEvent(parseInt(existGroup.groupCode), parseInt(member.uin), member.cardName, existMember.cardName));
|
// if (existMember.cardName != member.cardName) {
|
||||||
// }
|
// postOB11Event(new OB11GroupCardEvent(parseInt(existGroup.groupCode), parseInt(member.uin), member.cardName, existMember.cardName));
|
||||||
// Object.assign(existMember, member);
|
// }
|
||||||
// }
|
// Object.assign(existMember, member);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
// }
|
||||||
})
|
})
|
||||||
|
|
||||||
// 好友列表变动
|
// 好友列表变动
|
||||||
registerReceiveHook<{
|
registerReceiveHook<{
|
||||||
data: { categoryId: number, categroyName: string, categroyMbCount: number, buddyList: User[] }[]
|
data: { categoryId: number, categroyName: string, categroyMbCount: number, buddyList: User[] }[]
|
||||||
}>(ReceiveCmdS.FRIENDS, payload => {
|
}>(ReceiveCmdS.FRIENDS, payload => {
|
||||||
for (const fData of payload.data) {
|
for (const fData of payload.data) {
|
||||||
const _friends = fData.buddyList;
|
const _friends = fData.buddyList;
|
||||||
for (let friend of _friends) {
|
for (let friend of _friends) {
|
||||||
NTQQMsgApi.activateChat({peerUid: friend.uid, chatType: ChatType.friend}).then()
|
NTQQMsgApi.activateChat({peerUid: friend.uid, chatType: ChatType.friend}).then()
|
||||||
let existFriend = friends.find(f => f.uin == friend.uin)
|
let existFriend = friends.find(f => f.uin == friend.uin)
|
||||||
if (!existFriend) {
|
if (!existFriend) {
|
||||||
friends.push(friend)
|
friends.push(friend)
|
||||||
} else {
|
} else {
|
||||||
Object.assign(existFriend, friend)
|
Object.assign(existFriend, friend)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], (payload) => {
|
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], (payload) => {
|
||||||
// 保存一下uid
|
// 保存一下uid
|
||||||
for (const message of payload.msgList) {
|
for (const message of payload.msgList) {
|
||||||
const uid = message.senderUid;
|
const uid = message.senderUid;
|
||||||
const uin = message.senderUin;
|
const uin = message.senderUin;
|
||||||
if (uid && uin) {
|
if (uid && uin) {
|
||||||
if (message.chatType === ChatType.temp) {
|
if (message.chatType === ChatType.temp) {
|
||||||
dbUtil.getReceivedTempUinMap().then(receivedTempUinMap => {
|
dbUtil.getReceivedTempUinMap().then(receivedTempUinMap => {
|
||||||
if (!receivedTempUinMap[uin]) {
|
if (!receivedTempUinMap[uin]) {
|
||||||
receivedTempUinMap[uin] = uid;
|
receivedTempUinMap[uin] = uid;
|
||||||
dbUtil.setReceivedTempUinMap(receivedTempUinMap)
|
dbUtil.setReceivedTempUinMap(receivedTempUinMap)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
uidMaps[uid] = uin;
|
uidMaps[uid] = uin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 自动清理新消息文件
|
||||||
|
const {autoDeleteFile} = getConfigUtil().getConfig();
|
||||||
|
if (!autoDeleteFile) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const message of payload.msgList) {
|
||||||
|
// log("收到新消息,push到历史记录", message.msgId)
|
||||||
|
// dbUtil.addMsg(message).then()
|
||||||
|
// 清理文件
|
||||||
|
|
||||||
|
for (const msgElement of message.elements) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const picPath = msgElement.picElement?.sourcePath
|
||||||
|
const picThumbPath = [...msgElement.picElement?.thumbPath.values()]
|
||||||
|
const pttPath = msgElement.pttElement?.filePath
|
||||||
|
const filePath = msgElement.fileElement?.filePath
|
||||||
|
const videoPath = msgElement.videoElement?.filePath
|
||||||
|
const videoThumbPath: string[] = [...msgElement.videoElement?.thumbPath.values()]
|
||||||
|
const pathList = [picPath, ...picThumbPath, pttPath, filePath, videoPath, ...videoThumbPath]
|
||||||
|
if (msgElement.picElement) {
|
||||||
|
pathList.push(...Object.values(msgElement.picElement.thumbPath))
|
||||||
}
|
}
|
||||||
}
|
const aioOpGrayTipElement = msgElement.grayTipElement?.aioOpGrayTipElement
|
||||||
|
if (aioOpGrayTipElement) {
|
||||||
|
tempGroupCodeMap[aioOpGrayTipElement.peerUid] = aioOpGrayTipElement.fromGrpCodeOfTmpChat;
|
||||||
// 自动清理新消息文件
|
|
||||||
const {autoDeleteFile} = getConfigUtil().getConfig();
|
|
||||||
if (!autoDeleteFile) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for (const message of payload.msgList) {
|
|
||||||
// log("收到新消息,push到历史记录", message.msgId)
|
|
||||||
// dbUtil.addMsg(message).then()
|
|
||||||
// 清理文件
|
|
||||||
|
|
||||||
for (const msgElement of message.elements) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const picPath = msgElement.picElement?.sourcePath
|
|
||||||
const picThumbPath = [...msgElement.picElement?.thumbPath.values()]
|
|
||||||
const pttPath = msgElement.pttElement?.filePath
|
|
||||||
const filePath = msgElement.fileElement?.filePath
|
|
||||||
const videoPath = msgElement.videoElement?.filePath
|
|
||||||
const videoThumbPath: string[] = [...msgElement.videoElement?.thumbPath.values()]
|
|
||||||
const pathList = [picPath, ...picThumbPath, pttPath, filePath, videoPath, ...videoThumbPath]
|
|
||||||
if (msgElement.picElement) {
|
|
||||||
pathList.push(...Object.values(msgElement.picElement.thumbPath))
|
|
||||||
}
|
|
||||||
const aioOpGrayTipElement = msgElement.grayTipElement?.aioOpGrayTipElement
|
|
||||||
if (aioOpGrayTipElement) {
|
|
||||||
tempGroupCodeMap[aioOpGrayTipElement.peerUid] = aioOpGrayTipElement.fromGrpCodeOfTmpChat;
|
|
||||||
}
|
|
||||||
|
|
||||||
// log("需要清理的文件", pathList);
|
|
||||||
for (const path of pathList) {
|
|
||||||
if (path) {
|
|
||||||
fs.unlink(picPath, () => {
|
|
||||||
log("删除文件成功", path)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, getConfigUtil().getConfig().autoDeleteFileSecond * 1000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log("需要清理的文件", pathList);
|
||||||
|
for (const path of pathList) {
|
||||||
|
if (path) {
|
||||||
|
fs.unlink(picPath, () => {
|
||||||
|
log("删除文件成功", path)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, getConfigUtil().getConfig().autoDeleteFileSecond * 1000)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({msgRecord}) => {
|
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({msgRecord}) => {
|
||||||
const message = msgRecord;
|
const message = msgRecord;
|
||||||
const peerUid = message.peerUid;
|
const peerUid = message.peerUid;
|
||||||
// log("收到自己发送成功的消息", Object.keys(sendMessagePool), message);
|
// log("收到自己发送成功的消息", Object.keys(sendMessagePool), message);
|
||||||
// log("收到自己发送成功的消息", message.msgId, message.msgSeq);
|
// log("收到自己发送成功的消息", message.msgId, message.msgSeq);
|
||||||
dbUtil.addMsg(message).then()
|
dbUtil.addMsg(message).then()
|
||||||
const sendCallback = sendMessagePool[peerUid]
|
const sendCallback = sendMessagePool[peerUid]
|
||||||
if (sendCallback) {
|
if (sendCallback) {
|
||||||
try {
|
try {
|
||||||
sendCallback(message);
|
sendCallback(message);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("receive self msg error", e.stack)
|
log("receive self msg error", e.stack)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
|
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
|
||||||
selfInfo.online = info.info.status !== 20
|
selfInfo.online = info.info.status !== 20
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
let activatedPeerUids: string[] = []
|
let activatedPeerUids: string[] = []
|
||||||
registerReceiveHook<{
|
registerReceiveHook<{
|
||||||
changedRecentContactLists: {
|
changedRecentContactLists: {
|
||||||
listType: number, sortedContactList: string[],
|
listType: number, sortedContactList: string[],
|
||||||
changedList: {
|
changedList: {
|
||||||
id: string, // peerUid
|
id: string, // peerUid
|
||||||
chatType: ChatType
|
chatType: ChatType
|
||||||
}[]
|
|
||||||
}[]
|
}[]
|
||||||
|
}[]
|
||||||
}>(ReceiveCmdS.RECENT_CONTACT, async (payload) => {
|
}>(ReceiveCmdS.RECENT_CONTACT, async (payload) => {
|
||||||
for (const recentContact of payload.changedRecentContactLists) {
|
for (const recentContact of payload.changedRecentContactLists) {
|
||||||
for (const changedContact of recentContact.changedList) {
|
for (const changedContact of recentContact.changedList) {
|
||||||
if (activatedPeerUids.includes(changedContact.id)) continue;
|
if (activatedPeerUids.includes(changedContact.id)) continue;
|
||||||
activatedPeerUids.push(changedContact.id)
|
activatedPeerUids.push(changedContact.id)
|
||||||
const peer = {peerUid: changedContact.id, chatType: changedContact.chatType}
|
const peer = {peerUid: changedContact.id, chatType: changedContact.chatType}
|
||||||
if (changedContact.chatType === ChatType.temp) {
|
if (changedContact.chatType === ChatType.temp) {
|
||||||
log("收到临时会话消息", peer)
|
log("收到临时会话消息", peer)
|
||||||
NTQQMsgApi.activateChatAndGetHistory(peer).then(
|
NTQQMsgApi.activateChatAndGetHistory(peer).then(
|
||||||
() => {
|
() => {
|
||||||
NTQQMsgApi.getMsgHistory(peer, "", 20).then(({msgList}) => {
|
NTQQMsgApi.getMsgHistory(peer, "", 20).then(({msgList}) => {
|
||||||
let lastTempMsg = msgList.pop()
|
let lastTempMsg = msgList.pop()
|
||||||
log("激活窗口之前的第一条临时会话消息:", lastTempMsg)
|
log("激活窗口之前的第一条临时会话消息:", lastTempMsg)
|
||||||
if ((Date.now() / 1000) - parseInt(lastTempMsg.msgTime) < 5) {
|
if ((Date.now() / 1000) - parseInt(lastTempMsg.msgTime) < 5) {
|
||||||
OB11Constructor.message(lastTempMsg).then(r => postOB11Event(r))
|
OB11Constructor.message(lastTempMsg).then(r => postOB11Event(r))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
NTQQMsgApi.activateChat(peer).then()
|
NTQQMsgApi.activateChat(peer).then()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
registerCallHook(NTQQApiMethod.DELETE_ACTIVE_CHAT, async (payload) => {
|
||||||
|
const peerUid = payload[0] as string;
|
||||||
|
log("激活的聊天窗口被删除,准备重新激活", peerUid);
|
||||||
|
let chatType = ChatType.friend;
|
||||||
|
if (isNumeric(peerUid)) {
|
||||||
|
chatType = ChatType.group;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// 检查是否好友
|
||||||
|
if (!(await getFriend(peerUid))){
|
||||||
|
chatType = ChatType.temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const peer = {peerUid, chatType}
|
||||||
|
await sleep(1000);
|
||||||
|
NTQQMsgApi.activateChat(peer).then((r) => {
|
||||||
|
log("重新激活聊天窗口", peer, {result: r.result, errMsg: r.errMsg})
|
||||||
|
});
|
||||||
})
|
})
|
@@ -26,6 +26,7 @@ export enum NTQQApiMethod {
|
|||||||
ACTIVE_CHAT_HISTORY = "nodeIKernelMsgService/getMsgsIncludeSelfAndAddActiveChat", // 激活聊天窗口,有时候必须这样才能收到消息, 并返回历史消息
|
ACTIVE_CHAT_HISTORY = "nodeIKernelMsgService/getMsgsIncludeSelfAndAddActiveChat", // 激活聊天窗口,有时候必须这样才能收到消息, 并返回历史消息
|
||||||
HISTORY_MSG = "nodeIKernelMsgService/getMsgsIncludeSelf",
|
HISTORY_MSG = "nodeIKernelMsgService/getMsgsIncludeSelf",
|
||||||
GET_MULTI_MSG = "nodeIKernelMsgService/getMultiMsg",
|
GET_MULTI_MSG = "nodeIKernelMsgService/getMultiMsg",
|
||||||
|
DELETE_ACTIVE_CHAT = "nodeIKernelMsgService/deleteActiveChatByUid",
|
||||||
|
|
||||||
LIKE_FRIEND = "nodeIKernelProfileLikeService/setBuddyProfileLike",
|
LIKE_FRIEND = "nodeIKernelProfileLikeService/setBuddyProfileLike",
|
||||||
SELF_INFO = "fetchAuthData",
|
SELF_INFO = "fetchAuthData",
|
||||||
|
@@ -212,9 +212,28 @@ export interface GrayTipElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum FaceType {
|
||||||
|
normal=1, // 小黄脸
|
||||||
|
normal2=2, // 新小黄脸, 从faceIndex 222开始?
|
||||||
|
dice=3 // 骰子
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FaceIndex {
|
||||||
|
dice = 358,
|
||||||
|
RPS = 359 // 石头剪刀布
|
||||||
|
}
|
||||||
|
|
||||||
export interface FaceElement {
|
export interface FaceElement {
|
||||||
faceIndex: number,
|
faceIndex: number,
|
||||||
faceType: 1
|
faceType: FaceType,
|
||||||
|
faceText?: string,
|
||||||
|
packId?: string,
|
||||||
|
stickerId?: string,
|
||||||
|
sourceType?: number,
|
||||||
|
stickerType?: number,
|
||||||
|
resultId?: string,
|
||||||
|
surpriseId?: string,
|
||||||
|
randomType?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketFaceElement {
|
export interface MarketFaceElement {
|
||||||
|
@@ -224,6 +224,14 @@ export async function createSendElements(messageData: OB11MessageData[], target:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case OB11MessageDataType.dice:{
|
||||||
|
const resultId = sendMsg.data?.result
|
||||||
|
sendElements.push(SendMsgElementConstructor.dice(resultId));
|
||||||
|
}break;
|
||||||
|
case OB11MessageDataType.RPS:{
|
||||||
|
const resultId = sendMsg.data?.result
|
||||||
|
sendElements.push(SendMsgElementConstructor.rps(resultId));
|
||||||
|
}break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
} from "./types";
|
} from "./types";
|
||||||
import {
|
import {
|
||||||
AtType,
|
AtType,
|
||||||
ChatType,
|
ChatType, FaceIndex,
|
||||||
GrayTipElementSubType,
|
GrayTipElementSubType,
|
||||||
Group,
|
Group,
|
||||||
GroupMember,
|
GroupMember,
|
||||||
@@ -227,8 +227,19 @@ export class OB11Constructor {
|
|||||||
message_data["type"] = OB11MessageDataType.json;
|
message_data["type"] = OB11MessageDataType.json;
|
||||||
message_data["data"]["data"] = element.arkElement.bytesData;
|
message_data["data"]["data"] = element.arkElement.bytesData;
|
||||||
} else if (element.faceElement) {
|
} else if (element.faceElement) {
|
||||||
message_data["type"] = OB11MessageDataType.face;
|
const faceId = element.faceElement.faceIndex;
|
||||||
message_data["data"]["id"] = element.faceElement.faceIndex.toString();
|
if (faceId === FaceIndex.dice){
|
||||||
|
message_data["type"] = OB11MessageDataType.dice
|
||||||
|
message_data["data"]["result"] = element.faceElement.resultId;
|
||||||
|
}
|
||||||
|
else if (faceId === FaceIndex.RPS){
|
||||||
|
message_data["type"] = OB11MessageDataType.RPS
|
||||||
|
message_data["data"]["result"] = element.faceElement.resultId;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
message_data["type"] = OB11MessageDataType.face;
|
||||||
|
message_data["data"]["id"] = element.faceElement.faceIndex.toString();
|
||||||
|
}
|
||||||
} else if (element.marketFaceElement) {
|
} else if (element.marketFaceElement) {
|
||||||
message_data["type"] = OB11MessageDataType.mface;
|
message_data["type"] = OB11MessageDataType.mface;
|
||||||
message_data["data"]["text"] = element.marketFaceElement.faceName;
|
message_data["data"]["text"] = element.marketFaceElement.faceName;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import {OB11Message, OB11MessageAt, OB11MessageData} from "../types";
|
import {OB11Message, OB11MessageAt, OB11MessageData} from "../types";
|
||||||
import {getGroup, selfInfo} from "../../common/data";
|
import {getFriend, getGroup, getUidByUin, selfInfo} from "../../common/data";
|
||||||
import {OB11BaseMetaEvent} from "../event/meta/OB11BaseMetaEvent";
|
import {OB11BaseMetaEvent} from "../event/meta/OB11BaseMetaEvent";
|
||||||
import {OB11BaseNoticeEvent} from "../event/notice/OB11BaseNoticeEvent";
|
import {OB11BaseNoticeEvent} from "../event/notice/OB11BaseNoticeEvent";
|
||||||
import {WebSocket as WebSocketClass} from "ws";
|
import {WebSocket as WebSocketClass} from "ws";
|
||||||
@@ -63,7 +63,6 @@ export function unregisterWsEventSender(ws: WebSocketClass) {
|
|||||||
|
|
||||||
export function postWsEvent(event: PostEventType) {
|
export function postWsEvent(event: PostEventType) {
|
||||||
for (const ws of eventWSList) {
|
for (const ws of eventWSList) {
|
||||||
log(ws)
|
|
||||||
new Promise(() => {
|
new Promise(() => {
|
||||||
wsReply(ws, event);
|
wsReply(ws, event);
|
||||||
}).then()
|
}).then()
|
||||||
@@ -116,6 +115,7 @@ export function postOB11Event(msg: PostEventType, reportSelf = false) {
|
|||||||
peerUid: msg.user_id.toString()
|
peerUid: msg.user_id.toString()
|
||||||
}
|
}
|
||||||
if (msg.message_type == "private") {
|
if (msg.message_type == "private") {
|
||||||
|
peer.peerUid = getUidByUin(msg.user_id.toString())
|
||||||
if (msg.sub_type === "group") {
|
if (msg.sub_type === "group") {
|
||||||
peer.chatType = ChatType.temp
|
peer.chatType = ChatType.temp
|
||||||
}
|
}
|
||||||
@@ -140,6 +140,7 @@ export function postOB11Event(msg: PostEventType, reportSelf = false) {
|
|||||||
}
|
}
|
||||||
replyMessage = replyMessage.concat(convertMessage2List(reply, resJson.auto_escape))
|
replyMessage = replyMessage.concat(convertMessage2List(reply, resJson.auto_escape))
|
||||||
const {sendElements, deleteAfterSentFiles} = await createSendElements(replyMessage, group)
|
const {sendElements, deleteAfterSentFiles} = await createSendElements(replyMessage, group)
|
||||||
|
log(`发送消息给`, peer, sendElements)
|
||||||
sendMsg(peer, sendElements, deleteAfterSentFiles, false).then()
|
sendMsg(peer, sendElements, deleteAfterSentFiles, false).then()
|
||||||
} else if (resJson.delete) {
|
} else if (resJson.delete) {
|
||||||
NTQQMsgApi.recallMsg(peer, [rawMessage.msgId]).then()
|
NTQQMsgApi.recallMsg(peer, [rawMessage.msgId]).then()
|
||||||
|
@@ -117,7 +117,9 @@ export enum OB11MessageDataType {
|
|||||||
node = "node", // 合并转发消息节点
|
node = "node", // 合并转发消息节点
|
||||||
forward = "forward", // 合并转发消息,用于上报
|
forward = "forward", // 合并转发消息,用于上报
|
||||||
xml = "xml",
|
xml = "xml",
|
||||||
poke = "poke"
|
poke = "poke",
|
||||||
|
dice = "dice",
|
||||||
|
RPS = "rps"
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OB11MessageMFace{
|
export interface OB11MessageMFace{
|
||||||
@@ -126,6 +128,20 @@ export interface OB11MessageMFace{
|
|||||||
text: string
|
text: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OB11MessageDice{
|
||||||
|
type: OB11MessageDataType.dice,
|
||||||
|
data: {
|
||||||
|
result: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface OB11MessageRPS{
|
||||||
|
type: OB11MessageDataType.RPS,
|
||||||
|
data: {
|
||||||
|
result: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface OB11MessageText {
|
export interface OB11MessageText {
|
||||||
type: OB11MessageDataType.text,
|
type: OB11MessageDataType.text,
|
||||||
data: {
|
data: {
|
||||||
@@ -226,7 +242,8 @@ export type OB11MessageData =
|
|||||||
OB11MessageFace | OB11MessageMFace |
|
OB11MessageFace | OB11MessageMFace |
|
||||||
OB11MessageAt | OB11MessageReply |
|
OB11MessageAt | OB11MessageReply |
|
||||||
OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo |
|
OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo |
|
||||||
OB11MessageNode | OB11MessageCustomMusic | OB11MessageJson | OB11MessagePoke
|
OB11MessageNode | OB11MessageCustomMusic | OB11MessageJson | OB11MessagePoke |
|
||||||
|
OB11MessageDice | OB11MessageRPS
|
||||||
|
|
||||||
export interface OB11PostSendMsg {
|
export interface OB11PostSendMsg {
|
||||||
message_type?: "private" | "group"
|
message_type?: "private" | "group"
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const version = "3.21.0"
|
export const version = "3.22.1"
|
Reference in New Issue
Block a user