mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
feat: http post quick operation
This commit is contained in:
parent
89c3f07cba
commit
1735babb7d
@ -6,7 +6,7 @@ import path from "node:path";
|
|||||||
import {selfInfo} from "./data";
|
import {selfInfo} from "./data";
|
||||||
import {DATA_DIR} from "./utils";
|
import {DATA_DIR} from "./utils";
|
||||||
|
|
||||||
export const HOOK_LOG = true;
|
export const HOOK_LOG = false;
|
||||||
|
|
||||||
export const ALLOW_SEND_TEMP_MSG = false;
|
export const ALLOW_SEND_TEMP_MSG = false;
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import {ReceiveCmdS} from "../hook";
|
|||||||
import {Group, GroupMember, GroupMemberRole, GroupNotifies, GroupNotify, GroupRequestOperateTypes} from "../types";
|
import {Group, GroupMember, GroupMemberRole, GroupNotifies, GroupNotify, GroupRequestOperateTypes} from "../types";
|
||||||
import {callNTQQApi, GeneralCallResult, NTQQApiClass, NTQQApiMethod} from "../ntcall";
|
import {callNTQQApi, GeneralCallResult, NTQQApiClass, NTQQApiMethod} from "../ntcall";
|
||||||
import {uidMaps} from "../../common/data";
|
import {uidMaps} from "../../common/data";
|
||||||
import {BrowserWindow} from "electron";
|
|
||||||
import {dbUtil} from "../../common/db";
|
import {dbUtil} from "../../common/db";
|
||||||
import {log} from "../../common/utils/log";
|
import {log} from "../../common/utils/log";
|
||||||
import {NTQQWindowApi, NTQQWindows} from "./window";
|
import {NTQQWindowApi, NTQQWindows} from "./window";
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
import BaseAction from "../BaseAction";
|
import BaseAction from "../BaseAction";
|
||||||
import * as ntqqApi from "../../../ntqqapi/api";
|
// import * as ntqqApi from "../../../ntqqapi/api";
|
||||||
|
import {
|
||||||
|
NTQQMsgApi,
|
||||||
|
NTQQFriendApi,
|
||||||
|
NTQQGroupApi,
|
||||||
|
NTQQUserApi,
|
||||||
|
NTQQFileApi,
|
||||||
|
NTQQFileCacheApi,
|
||||||
|
NTQQWindowApi,
|
||||||
|
} from "../../../ntqqapi/api";
|
||||||
import {ActionName} from "../types";
|
import {ActionName} from "../types";
|
||||||
import {log} from "../../../common/utils/log";
|
import {log} from "../../../common/utils/log";
|
||||||
|
|
||||||
@ -13,8 +22,10 @@ export default class Debug extends BaseAction<Payload, any> {
|
|||||||
|
|
||||||
protected async _handle(payload: Payload): Promise<any> {
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
log("debug call ntqq api", payload);
|
log("debug call ntqq api", payload);
|
||||||
for (const ntqqApiClass in ntqqApi) {
|
const ntqqApi = [NTQQMsgApi, NTQQFriendApi, NTQQGroupApi, NTQQUserApi, NTQQFileApi, NTQQFileCacheApi, NTQQWindowApi]
|
||||||
const method = ntqqApi[ntqqApiClass][payload.method]
|
for (const ntqqApiClass of ntqqApi) {
|
||||||
|
log("ntqqApiClass", ntqqApiClass)
|
||||||
|
const method = ntqqApiClass[payload.method]
|
||||||
if (method) {
|
if (method) {
|
||||||
const result = method(...payload.args);
|
const result = method(...payload.args);
|
||||||
if (method.constructor.name === "AsyncFunction") {
|
if (method.constructor.name === "AsyncFunction") {
|
||||||
|
@ -75,278 +75,25 @@ export interface ReturnDataType {
|
|||||||
message_id: number
|
message_id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
export function convertMessage2List(message: OB11MessageMixType, autoEscape = false) {
|
||||||
actionName = ActionName.SendMsg
|
|
||||||
|
|
||||||
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
|
|
||||||
const messages = this.convertMessage2List(payload.message);
|
|
||||||
const fmNum = this.getSpecialMsgNum(payload, OB11MessageDataType.node)
|
|
||||||
if (fmNum && fmNum != messages.length) {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
message: "转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (payload.group_id && !(await getGroup(payload.group_id))) {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
message: `群${payload.group_id}不存在`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (payload.user_id && payload.message_type !== "group") {
|
|
||||||
if (!(await getFriend(payload.user_id))) {
|
|
||||||
if (!ALLOW_SEND_TEMP_MSG && !(await dbUtil.getReceivedTempUinMap())[payload.user_id.toString()]) {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
message: `不能发送临时消息`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
valid: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async _handle(payload: OB11PostSendMsg) {
|
|
||||||
|
|
||||||
const peer: Peer = {
|
|
||||||
chatType: ChatType.friend,
|
|
||||||
peerUid: ""
|
|
||||||
}
|
|
||||||
let isTempMsg = false;
|
|
||||||
let group: Group | undefined = undefined;
|
|
||||||
const genGroupPeer = async () => {
|
|
||||||
group = await getGroup(payload.group_id.toString())
|
|
||||||
peer.chatType = ChatType.group
|
|
||||||
// peer.name = group.name
|
|
||||||
peer.peerUid = group.groupCode
|
|
||||||
}
|
|
||||||
|
|
||||||
const genFriendPeer = () => {
|
|
||||||
const friend = friends.find(f => f.uin == payload.user_id.toString())
|
|
||||||
if (friend) {
|
|
||||||
// peer.name = friend.nickName
|
|
||||||
peer.peerUid = friend.uid
|
|
||||||
} else {
|
|
||||||
peer.chatType = ChatType.temp
|
|
||||||
const tempUserUid = getUidByUin(payload.user_id.toString())
|
|
||||||
if (!tempUserUid) {
|
|
||||||
throw (`找不到私聊对象${payload.user_id}`)
|
|
||||||
}
|
|
||||||
// peer.name = tempUser.nickName
|
|
||||||
isTempMsg = true;
|
|
||||||
peer.peerUid = tempUserUid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (payload?.group_id && payload.message_type === "group") {
|
|
||||||
await genGroupPeer()
|
|
||||||
|
|
||||||
} else if (payload?.user_id) {
|
|
||||||
genFriendPeer()
|
|
||||||
} else if (payload.group_id) {
|
|
||||||
await genGroupPeer()
|
|
||||||
} else {
|
|
||||||
throw ("发送消息参数错误, 请指定group_id或user_id")
|
|
||||||
}
|
|
||||||
const messages = this.convertMessage2List(payload.message);
|
|
||||||
if (this.getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
|
||||||
try {
|
|
||||||
const returnMsg = await this.handleForwardNode(peer, messages as OB11MessageNode[], group)
|
|
||||||
return {message_id: returnMsg.msgShortId}
|
|
||||||
} catch (e) {
|
|
||||||
throw ("发送转发消息失败 " + e.toString())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
|
||||||
const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic
|
|
||||||
if (music) {
|
|
||||||
const {url, audio, title, content, image} = music.data;
|
|
||||||
const selfPeer: Peer = {peerUid: selfInfo.uid, chatType: ChatType.friend}
|
|
||||||
// 搞不定!
|
|
||||||
// const musicMsg = await this.send(selfPeer, [this.genMusicElement(url, audio, title, content, image)], [], false)
|
|
||||||
// 转发
|
|
||||||
// const res = await NTQQApi.forwardMsg(selfPeer, peer, [musicMsg.msgId])
|
|
||||||
// log("转发音乐消息成功", res);
|
|
||||||
// return {message_id: musicMsg.msgShortId}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// log("send msg:", peer, sendElements)
|
|
||||||
const {sendElements, deleteAfterSentFiles} = await this.createSendElements(messages, group)
|
|
||||||
const returnMsg = await this.send(peer, sendElements, deleteAfterSentFiles)
|
|
||||||
deleteAfterSentFiles.map(f => fs.unlink(f, () => {
|
|
||||||
}));
|
|
||||||
return {message_id: returnMsg.msgShortId}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected convertMessage2List(message: OB11MessageMixType) {
|
|
||||||
if (typeof message === "string") {
|
if (typeof message === "string") {
|
||||||
// message = [{
|
if (!autoEscape) {
|
||||||
// type: OB11MessageDataType.text,
|
|
||||||
// data: {
|
|
||||||
// text: message
|
|
||||||
// }
|
|
||||||
// }] as OB11MessageData[]
|
|
||||||
message = decodeCQCode(message.toString())
|
message = decodeCQCode(message.toString())
|
||||||
|
} else {
|
||||||
|
message = [{
|
||||||
|
type: OB11MessageDataType.text,
|
||||||
|
data: {
|
||||||
|
text: message
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
} else if (!Array.isArray(message)) {
|
} else if (!Array.isArray(message)) {
|
||||||
message = [message]
|
message = [message]
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType): number {
|
export async function createSendElements(messageData: OB11MessageData[], group: Group | undefined, ignoreTypes: OB11MessageDataType[] = []) {
|
||||||
if (Array.isArray(payload.message)) {
|
|
||||||
return payload.message.filter(msg => msg.type == msgType).length
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private async cloneMsg(msg: RawMessage): Promise<RawMessage> {
|
|
||||||
log("克隆的目标消息", msg)
|
|
||||||
let sendElements: SendMessageElement[] = [];
|
|
||||||
for (const ele of msg.elements) {
|
|
||||||
sendElements.push(ele as SendMessageElement)
|
|
||||||
// Object.keys(ele).forEach((eleKey) => {
|
|
||||||
// if (eleKey.endsWith("Element")) {
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
if (sendElements.length === 0) {
|
|
||||||
log("需要clone的消息无法解析,将会忽略掉", msg)
|
|
||||||
}
|
|
||||||
log("克隆消息", sendElements)
|
|
||||||
try {
|
|
||||||
const nodeMsg = await NTQQMsgApi.sendMsg({
|
|
||||||
chatType: ChatType.friend,
|
|
||||||
peerUid: selfInfo.uid
|
|
||||||
}, sendElements, true);
|
|
||||||
await sleep(500);
|
|
||||||
return nodeMsg
|
|
||||||
} catch (e) {
|
|
||||||
log(e, "克隆转发消息失败,将忽略本条消息", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回一个合并转发的消息id
|
|
||||||
private async handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[], group: Group | undefined) {
|
|
||||||
|
|
||||||
const selfPeer = {
|
|
||||||
chatType: ChatType.friend,
|
|
||||||
peerUid: selfInfo.uid
|
|
||||||
}
|
|
||||||
let nodeMsgIds: string[] = []
|
|
||||||
// 先判断一遍是不是id和自定义混用
|
|
||||||
let needClone = messageNodes.filter(node => node.data.id).length && messageNodes.filter(node => !node.data.id).length
|
|
||||||
for (const messageNode of messageNodes) {
|
|
||||||
// 一个node表示一个人的消息
|
|
||||||
let nodeId = messageNode.data.id;
|
|
||||||
// 有nodeId表示一个子转发消息卡片
|
|
||||||
if (nodeId) {
|
|
||||||
let nodeMsg = await dbUtil.getMsgByShortId(parseInt(nodeId));
|
|
||||||
if (!needClone) {
|
|
||||||
nodeMsgIds.push(nodeMsg.msgId)
|
|
||||||
} else {
|
|
||||||
if (nodeMsg.peerUid !== selfInfo.uid) {
|
|
||||||
const cloneMsg = await this.cloneMsg(nodeMsg)
|
|
||||||
if (cloneMsg) {
|
|
||||||
nodeMsgIds.push(cloneMsg.msgId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 自定义的消息
|
|
||||||
// 提取消息段,发给自己生成消息id
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
sendElements,
|
|
||||||
deleteAfterSentFiles
|
|
||||||
} = await this.createSendElements(this.convertMessage2List(messageNode.data.content), group);
|
|
||||||
log("开始生成转发节点", sendElements);
|
|
||||||
let sendElementsSplit: SendMessageElement[][] = []
|
|
||||||
let splitIndex = 0;
|
|
||||||
for (const ele of sendElements) {
|
|
||||||
if (!sendElementsSplit[splitIndex]) {
|
|
||||||
sendElementsSplit[splitIndex] = []
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ele.elementType === ElementType.FILE || ele.elementType === ElementType.VIDEO) {
|
|
||||||
if (sendElementsSplit[splitIndex].length > 0) {
|
|
||||||
splitIndex++;
|
|
||||||
}
|
|
||||||
sendElementsSplit[splitIndex] = [ele]
|
|
||||||
splitIndex++;
|
|
||||||
} else {
|
|
||||||
sendElementsSplit[splitIndex].push(ele)
|
|
||||||
}
|
|
||||||
log(sendElementsSplit)
|
|
||||||
}
|
|
||||||
// log("分割后的转发节点", sendElementsSplit)
|
|
||||||
for (const eles of sendElementsSplit) {
|
|
||||||
const nodeMsg = await this.send(selfPeer, eles, [], true);
|
|
||||||
nodeMsgIds.push(nodeMsg.msgId)
|
|
||||||
await sleep(500);
|
|
||||||
log("转发节点生成成功", nodeMsg.msgId);
|
|
||||||
}
|
|
||||||
deleteAfterSentFiles.map(f => fs.unlink(f, () => {
|
|
||||||
}));
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
log("生成转发消息节点失败", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查srcPeer是否一致,不一致则需要克隆成自己的消息, 让所有srcPeer都变成自己的,使其保持一致才能够转发
|
|
||||||
let nodeMsgArray: Array<RawMessage> = []
|
|
||||||
let srcPeer: Peer = null;
|
|
||||||
let needSendSelf = false;
|
|
||||||
for (const [index, msgId] of nodeMsgIds.entries()) {
|
|
||||||
const nodeMsg = await dbUtil.getMsgByLongId(msgId)
|
|
||||||
if (nodeMsg) {
|
|
||||||
nodeMsgArray.push(nodeMsg)
|
|
||||||
if (!srcPeer) {
|
|
||||||
srcPeer = {chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid}
|
|
||||||
} else if (srcPeer.peerUid !== nodeMsg.peerUid) {
|
|
||||||
needSendSelf = true
|
|
||||||
srcPeer = selfPeer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log("nodeMsgArray", nodeMsgArray);
|
|
||||||
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
|
|
||||||
if (needSendSelf) {
|
|
||||||
log("需要克隆转发消息");
|
|
||||||
for (const [index, msg] of nodeMsgArray.entries()) {
|
|
||||||
if (msg.peerUid !== selfInfo.uid) {
|
|
||||||
const cloneMsg = await this.cloneMsg(msg)
|
|
||||||
if (cloneMsg) {
|
|
||||||
nodeMsgIds[index] = cloneMsg.msgId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// elements之间用换行符分隔
|
|
||||||
// let _sendForwardElements: SendMessageElement[] = []
|
|
||||||
// for(let i = 0; i < sendForwardElements.length; i++){
|
|
||||||
// _sendForwardElements.push(sendForwardElements[i])
|
|
||||||
// _sendForwardElements.push(SendMsgElementConstructor.text("\n\n"))
|
|
||||||
// }
|
|
||||||
// const nodeMsg = await NTQQApi.sendMsg(selfPeer, _sendForwardElements, true);
|
|
||||||
// nodeIds.push(nodeMsg.msgId)
|
|
||||||
// await sleep(500);
|
|
||||||
// 开发转发
|
|
||||||
try {
|
|
||||||
log("开发转发", nodeMsgIds)
|
|
||||||
return await NTQQMsgApi.multiForwardMsg(srcPeer, destPeer, nodeMsgIds)
|
|
||||||
} catch (e) {
|
|
||||||
log("forward failed", e)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createSendElements(messageData: OB11MessageData[], group: Group | undefined, ignoreTypes: OB11MessageDataType[] = []) {
|
|
||||||
let sendElements: SendMessageElement[] = []
|
let sendElements: SendMessageElement[] = []
|
||||||
let deleteAfterSentFiles: string[] = []
|
let deleteAfterSentFiles: string[] = []
|
||||||
for (let sendMsg of messageData) {
|
for (let sendMsg of messageData) {
|
||||||
@ -445,10 +192,12 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
case OB11MessageDataType.json: {
|
case OB11MessageDataType.json: {
|
||||||
sendElements.push(SendMsgElementConstructor.ark(sendMsg.data.data))
|
sendElements.push(SendMsgElementConstructor.ark(sendMsg.data.data))
|
||||||
}break
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -459,7 +208,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async send(peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[], waitComplete = true) {
|
export async function sendMsg(peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[], waitComplete = true) {
|
||||||
if (!sendElements.length) {
|
if (!sendElements.length) {
|
||||||
throw ("消息体无法解析")
|
throw ("消息体无法解析")
|
||||||
}
|
}
|
||||||
@ -471,6 +220,266 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
return returnMsg
|
return returnMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||||
|
actionName = ActionName.SendMsg
|
||||||
|
|
||||||
|
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
|
||||||
|
const messages = convertMessage2List(payload.message);
|
||||||
|
const fmNum = this.getSpecialMsgNum(payload, OB11MessageDataType.node)
|
||||||
|
if (fmNum && fmNum != messages.length) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (payload.group_id && !(await getGroup(payload.group_id))) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: `群${payload.group_id}不存在`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (payload.user_id && payload.message_type !== "group") {
|
||||||
|
if (!(await getFriend(payload.user_id))) {
|
||||||
|
if (!ALLOW_SEND_TEMP_MSG && !(await dbUtil.getReceivedTempUinMap())[payload.user_id.toString()]) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: `不能发送临时消息`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _handle(payload: OB11PostSendMsg) {
|
||||||
|
|
||||||
|
const peer: Peer = {
|
||||||
|
chatType: ChatType.friend,
|
||||||
|
peerUid: ""
|
||||||
|
}
|
||||||
|
let isTempMsg = false;
|
||||||
|
let group: Group | undefined = undefined;
|
||||||
|
const genGroupPeer = async () => {
|
||||||
|
group = await getGroup(payload.group_id.toString())
|
||||||
|
peer.chatType = ChatType.group
|
||||||
|
// peer.name = group.name
|
||||||
|
peer.peerUid = group.groupCode
|
||||||
|
}
|
||||||
|
|
||||||
|
const genFriendPeer = () => {
|
||||||
|
const friend = friends.find(f => f.uin == payload.user_id.toString())
|
||||||
|
if (friend) {
|
||||||
|
// peer.name = friend.nickName
|
||||||
|
peer.peerUid = friend.uid
|
||||||
|
} else {
|
||||||
|
peer.chatType = ChatType.temp
|
||||||
|
const tempUserUid = getUidByUin(payload.user_id.toString())
|
||||||
|
if (!tempUserUid) {
|
||||||
|
throw (`找不到私聊对象${payload.user_id}`)
|
||||||
|
}
|
||||||
|
// peer.name = tempUser.nickName
|
||||||
|
isTempMsg = true;
|
||||||
|
peer.peerUid = tempUserUid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (payload?.group_id && payload.message_type === "group") {
|
||||||
|
await genGroupPeer()
|
||||||
|
|
||||||
|
} else if (payload?.user_id) {
|
||||||
|
genFriendPeer()
|
||||||
|
} else if (payload.group_id) {
|
||||||
|
await genGroupPeer()
|
||||||
|
} else {
|
||||||
|
throw ("发送消息参数错误, 请指定group_id或user_id")
|
||||||
|
}
|
||||||
|
const messages = convertMessage2List(payload.message);
|
||||||
|
if (this.getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
||||||
|
try {
|
||||||
|
const returnMsg = await this.handleForwardNode(peer, messages as OB11MessageNode[], group)
|
||||||
|
return {message_id: returnMsg.msgShortId}
|
||||||
|
} catch (e) {
|
||||||
|
throw ("发送转发消息失败 " + e.toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
||||||
|
const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic
|
||||||
|
if (music) {
|
||||||
|
const {url, audio, title, content, image} = music.data;
|
||||||
|
const selfPeer: Peer = {peerUid: selfInfo.uid, chatType: ChatType.friend}
|
||||||
|
// 搞不定!
|
||||||
|
// const musicMsg = await this.send(selfPeer, [this.genMusicElement(url, audio, title, content, image)], [], false)
|
||||||
|
// 转发
|
||||||
|
// const res = await NTQQApi.forwardMsg(selfPeer, peer, [musicMsg.msgId])
|
||||||
|
// log("转发音乐消息成功", res);
|
||||||
|
// return {message_id: musicMsg.msgShortId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// log("send msg:", peer, sendElements)
|
||||||
|
const {sendElements, deleteAfterSentFiles} = await createSendElements(messages, group)
|
||||||
|
const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles)
|
||||||
|
deleteAfterSentFiles.map(f => fs.unlink(f, () => {
|
||||||
|
}));
|
||||||
|
return {message_id: returnMsg.msgShortId}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType): number {
|
||||||
|
if (Array.isArray(payload.message)) {
|
||||||
|
return payload.message.filter(msg => msg.type == msgType).length
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private async cloneMsg(msg: RawMessage): Promise<RawMessage> {
|
||||||
|
log("克隆的目标消息", msg)
|
||||||
|
let sendElements: SendMessageElement[] = [];
|
||||||
|
for (const ele of msg.elements) {
|
||||||
|
sendElements.push(ele as SendMessageElement)
|
||||||
|
// Object.keys(ele).forEach((eleKey) => {
|
||||||
|
// if (eleKey.endsWith("Element")) {
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
if (sendElements.length === 0) {
|
||||||
|
log("需要clone的消息无法解析,将会忽略掉", msg)
|
||||||
|
}
|
||||||
|
log("克隆消息", sendElements)
|
||||||
|
try {
|
||||||
|
const nodeMsg = await NTQQMsgApi.sendMsg({
|
||||||
|
chatType: ChatType.friend,
|
||||||
|
peerUid: selfInfo.uid
|
||||||
|
}, sendElements, true);
|
||||||
|
await sleep(500);
|
||||||
|
return nodeMsg
|
||||||
|
} catch (e) {
|
||||||
|
log(e, "克隆转发消息失败,将忽略本条消息", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回一个合并转发的消息id
|
||||||
|
private async handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[], group: Group | undefined) {
|
||||||
|
|
||||||
|
const selfPeer = {
|
||||||
|
chatType: ChatType.friend,
|
||||||
|
peerUid: selfInfo.uid
|
||||||
|
}
|
||||||
|
let nodeMsgIds: string[] = []
|
||||||
|
// 先判断一遍是不是id和自定义混用
|
||||||
|
let needClone = messageNodes.filter(node => node.data.id).length && messageNodes.filter(node => !node.data.id).length
|
||||||
|
for (const messageNode of messageNodes) {
|
||||||
|
// 一个node表示一个人的消息
|
||||||
|
let nodeId = messageNode.data.id;
|
||||||
|
// 有nodeId表示一个子转发消息卡片
|
||||||
|
if (nodeId) {
|
||||||
|
let nodeMsg = await dbUtil.getMsgByShortId(parseInt(nodeId));
|
||||||
|
if (!needClone) {
|
||||||
|
nodeMsgIds.push(nodeMsg.msgId)
|
||||||
|
} else {
|
||||||
|
if (nodeMsg.peerUid !== selfInfo.uid) {
|
||||||
|
const cloneMsg = await this.cloneMsg(nodeMsg)
|
||||||
|
if (cloneMsg) {
|
||||||
|
nodeMsgIds.push(cloneMsg.msgId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 自定义的消息
|
||||||
|
// 提取消息段,发给自己生成消息id
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
sendElements,
|
||||||
|
deleteAfterSentFiles
|
||||||
|
} = await createSendElements(convertMessage2List(messageNode.data.content), group);
|
||||||
|
log("开始生成转发节点", sendElements);
|
||||||
|
let sendElementsSplit: SendMessageElement[][] = []
|
||||||
|
let splitIndex = 0;
|
||||||
|
for (const ele of sendElements) {
|
||||||
|
if (!sendElementsSplit[splitIndex]) {
|
||||||
|
sendElementsSplit[splitIndex] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ele.elementType === ElementType.FILE || ele.elementType === ElementType.VIDEO) {
|
||||||
|
if (sendElementsSplit[splitIndex].length > 0) {
|
||||||
|
splitIndex++;
|
||||||
|
}
|
||||||
|
sendElementsSplit[splitIndex] = [ele]
|
||||||
|
splitIndex++;
|
||||||
|
} else {
|
||||||
|
sendElementsSplit[splitIndex].push(ele)
|
||||||
|
}
|
||||||
|
log(sendElementsSplit)
|
||||||
|
}
|
||||||
|
// log("分割后的转发节点", sendElementsSplit)
|
||||||
|
for (const eles of sendElementsSplit) {
|
||||||
|
const nodeMsg = await sendMsg(selfPeer, eles, [], true);
|
||||||
|
nodeMsgIds.push(nodeMsg.msgId)
|
||||||
|
await sleep(500);
|
||||||
|
log("转发节点生成成功", nodeMsg.msgId);
|
||||||
|
}
|
||||||
|
deleteAfterSentFiles.map(f => fs.unlink(f, () => {
|
||||||
|
}));
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
log("生成转发消息节点失败", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查srcPeer是否一致,不一致则需要克隆成自己的消息, 让所有srcPeer都变成自己的,使其保持一致才能够转发
|
||||||
|
let nodeMsgArray: Array<RawMessage> = []
|
||||||
|
let srcPeer: Peer = null;
|
||||||
|
let needSendSelf = false;
|
||||||
|
for (const [index, msgId] of nodeMsgIds.entries()) {
|
||||||
|
const nodeMsg = await dbUtil.getMsgByLongId(msgId)
|
||||||
|
if (nodeMsg) {
|
||||||
|
nodeMsgArray.push(nodeMsg)
|
||||||
|
if (!srcPeer) {
|
||||||
|
srcPeer = {chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid}
|
||||||
|
} else if (srcPeer.peerUid !== nodeMsg.peerUid) {
|
||||||
|
needSendSelf = true
|
||||||
|
srcPeer = selfPeer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("nodeMsgArray", nodeMsgArray);
|
||||||
|
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
|
||||||
|
if (needSendSelf) {
|
||||||
|
log("需要克隆转发消息");
|
||||||
|
for (const [index, msg] of nodeMsgArray.entries()) {
|
||||||
|
if (msg.peerUid !== selfInfo.uid) {
|
||||||
|
const cloneMsg = await this.cloneMsg(msg)
|
||||||
|
if (cloneMsg) {
|
||||||
|
nodeMsgIds[index] = cloneMsg.msgId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// elements之间用换行符分隔
|
||||||
|
// let _sendForwardElements: SendMessageElement[] = []
|
||||||
|
// for(let i = 0; i < sendForwardElements.length; i++){
|
||||||
|
// _sendForwardElements.push(sendForwardElements[i])
|
||||||
|
// _sendForwardElements.push(SendMsgElementConstructor.text("\n\n"))
|
||||||
|
// }
|
||||||
|
// const nodeMsg = await NTQQApi.sendMsg(selfPeer, _sendForwardElements, true);
|
||||||
|
// nodeIds.push(nodeMsg.msgId)
|
||||||
|
// await sleep(500);
|
||||||
|
// 开发转发
|
||||||
|
try {
|
||||||
|
log("开发转发", nodeMsgIds)
|
||||||
|
return await NTQQMsgApi.multiForwardMsg(srcPeer, destPeer, nodeMsgIds)
|
||||||
|
} catch (e) {
|
||||||
|
log("forward failed", e)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private genMusicElement(url: string, audio: string, title: string, content: string, image: string): SendArkElement {
|
private genMusicElement(url: string, audio: string, title: string, content: string, image: string): SendArkElement {
|
||||||
const musicJson = {
|
const musicJson = {
|
||||||
app: 'com.tencent.structmsg',
|
app: 'com.tencent.structmsg',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {OB11Message} from "../types";
|
import {OB11Message, OB11MessageAt, OB11MessageData} from "../types";
|
||||||
import {selfInfo} from "../../common/data";
|
import {getGroup, 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";
|
||||||
@ -7,9 +7,47 @@ import {wsReply} from "./ws/reply";
|
|||||||
import {log} from "../../common/utils/log";
|
import {log} from "../../common/utils/log";
|
||||||
import {getConfigUtil} from "../../common/config";
|
import {getConfigUtil} from "../../common/config";
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
import {NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, Peer} from "../../ntqqapi/api";
|
||||||
|
import {ChatType, Group, GroupRequestOperateTypes} from "../../ntqqapi/types";
|
||||||
|
import {convertMessage2List, createSendElements, sendMsg} from "../action/msg/SendMsg";
|
||||||
|
import {dbUtil} from "../../common/db";
|
||||||
|
import {OB11FriendRequestEvent} from "../event/request/OB11FriendRequest";
|
||||||
|
import {OB11GroupRequestEvent} from "../event/request/OB11GroupRequest";
|
||||||
|
import {isNull} from "../../common/utils";
|
||||||
|
|
||||||
export type PostEventType = OB11Message | OB11BaseMetaEvent | OB11BaseNoticeEvent
|
export type PostEventType = OB11Message | OB11BaseMetaEvent | OB11BaseNoticeEvent
|
||||||
|
|
||||||
|
interface QuickActionPrivateMessage {
|
||||||
|
reply?: string;
|
||||||
|
auto_escape?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QuickActionGroupMessage extends QuickActionPrivateMessage {
|
||||||
|
// 回复群消息
|
||||||
|
at_sender?: boolean
|
||||||
|
delete?: boolean
|
||||||
|
kick?: boolean
|
||||||
|
ban?: boolean
|
||||||
|
ban_duration?: number
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QuickActionFriendRequest {
|
||||||
|
approve?: boolean
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QuickActionGroupRequest {
|
||||||
|
approve?: boolean
|
||||||
|
reason?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuickAction =
|
||||||
|
QuickActionPrivateMessage
|
||||||
|
& QuickActionGroupMessage
|
||||||
|
& QuickActionFriendRequest
|
||||||
|
& QuickActionGroupRequest
|
||||||
|
|
||||||
const eventWSList: WebSocketClass[] = [];
|
const eventWSList: WebSocketClass[] = [];
|
||||||
|
|
||||||
export function registerWsEventSender(ws: WebSocketClass) {
|
export function registerWsEventSender(ws: WebSocketClass) {
|
||||||
@ -56,8 +94,77 @@ export function postOB11Event(msg: PostEventType, reportSelf = false) {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers,
|
headers,
|
||||||
body: msgStr
|
body: msgStr
|
||||||
}).then((res: any) => {
|
}).then(async (res) => {
|
||||||
log(`新消息事件HTTP上报成功: ${host} ` + msgStr);
|
log(`新消息事件HTTP上报成功: ${host} `, msgStr);
|
||||||
|
// todo: 处理不够优雅,应该使用高级泛型进行QuickAction类型识别
|
||||||
|
let resJson: QuickAction;
|
||||||
|
try {
|
||||||
|
resJson = await res.json();
|
||||||
|
log(`新消息事件HTTP上报返回快速操作: `, JSON.stringify(resJson))
|
||||||
|
} catch (e) {
|
||||||
|
log(`新消息事件HTTP上报没有返回快速操作,不需要处理`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (msg.post_type === "message") {
|
||||||
|
msg = msg as OB11Message;
|
||||||
|
const rawMessage = await dbUtil.getMsgByShortId(msg.message_id)
|
||||||
|
resJson = resJson as QuickActionPrivateMessage | QuickActionGroupMessage
|
||||||
|
const reply = resJson.reply
|
||||||
|
let peer: Peer = {
|
||||||
|
chatType: ChatType.friend,
|
||||||
|
peerUid: msg.user_id.toString()
|
||||||
|
}
|
||||||
|
if (msg.message_type == "private") {
|
||||||
|
if (msg.sub_type === "group") {
|
||||||
|
peer.chatType = ChatType.temp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
peer.chatType = ChatType.group
|
||||||
|
peer.peerUid = msg.group_id.toString()
|
||||||
|
}
|
||||||
|
if (reply) {
|
||||||
|
let group: Group = null
|
||||||
|
let replyMessage: OB11MessageData[] = []
|
||||||
|
|
||||||
|
if (msg.message_type == "group") {
|
||||||
|
group = await getGroup(msg.group_id.toString())
|
||||||
|
if ((resJson as QuickActionGroupMessage).at_sender) {
|
||||||
|
replyMessage.push({
|
||||||
|
type: "at",
|
||||||
|
data: {
|
||||||
|
qq: msg.user_id.toString()
|
||||||
|
}
|
||||||
|
} as OB11MessageAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replyMessage = replyMessage.concat(convertMessage2List(reply, resJson.auto_escape))
|
||||||
|
const {sendElements, deleteAfterSentFiles} = await createSendElements(replyMessage, group)
|
||||||
|
sendMsg(peer, sendElements, deleteAfterSentFiles, false).then()
|
||||||
|
} else if (resJson.delete) {
|
||||||
|
NTQQMsgApi.recallMsg(peer, [rawMessage.msgId]).then()
|
||||||
|
} else if (resJson.kick) {
|
||||||
|
NTQQGroupApi.kickMember(peer.peerUid, [rawMessage.senderUid]).then()
|
||||||
|
} else if (resJson.ban) {
|
||||||
|
NTQQGroupApi.banMember(peer.peerUid, [{
|
||||||
|
uid: rawMessage.senderUid,
|
||||||
|
timeStamp: resJson.ban_duration || 60 * 30
|
||||||
|
}],).then()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (msg.post_type === "request") {
|
||||||
|
if ((msg as OB11FriendRequestEvent).request_type === "friend") {
|
||||||
|
resJson = resJson as QuickActionFriendRequest
|
||||||
|
if (!isNull(resJson.approve)) {
|
||||||
|
// todo: set remark
|
||||||
|
NTQQFriendApi.handleFriendRequest(parseInt((msg as OB11FriendRequestEvent).flag), resJson.approve).then()
|
||||||
|
}
|
||||||
|
} else if ((msg as OB11GroupRequestEvent).request_type === "group") {
|
||||||
|
resJson = resJson as QuickActionGroupRequest
|
||||||
|
if (!isNull(resJson.approve)) {
|
||||||
|
NTQQGroupApi.handleGroupRequest((msg as OB11FriendRequestEvent).flag, resJson.approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject, resJson.reason).then()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}, (err: any) => {
|
}, (err: any) => {
|
||||||
log(`新消息事件HTTP上报失败: ${host} `, err, msg);
|
log(`新消息事件HTTP上报失败: ${host} `, err, msg);
|
||||||
});
|
});
|
||||||
|
29
test/quick_action/server.py
Normal file
29
test/quick_action/server.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import uvicorn
|
||||||
|
from fastapi import FastAPI, Request
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.post("/")
|
||||||
|
async def root(request: Request):
|
||||||
|
data = await request.json()
|
||||||
|
print(data)
|
||||||
|
if (data["post_type"] == "message"):
|
||||||
|
text = list(filter(lambda x: x["type"] == "text", data["message"]))[0]["data"]["text"]
|
||||||
|
if text == "禁言":
|
||||||
|
return {"ban": True, "ban_duration": 10}
|
||||||
|
elif text == "踢我":
|
||||||
|
return {"kick": True}
|
||||||
|
elif text == "撤回":
|
||||||
|
return {"delete": True}
|
||||||
|
# print(data["message"])
|
||||||
|
return {"reply": "Hello World"}
|
||||||
|
elif data["post_type"] == "request":
|
||||||
|
if data["request_type"] == "group":
|
||||||
|
return {"approve": False, "reason": "不让你进群"}
|
||||||
|
else:
|
||||||
|
# 加好友
|
||||||
|
return {"approve": True}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
uvicorn.run(app, host="", port=8000)
|
Loading…
x
Reference in New Issue
Block a user