fix: send empty forward msg

fix: ignore post history msg before login
fix: quit group not sync to groups of data
feat: support post url params
feat: support port http heart
This commit is contained in:
linyuchen 2024-04-16 23:15:33 +08:00
parent f8890b309b
commit c2b3316603
15 changed files with 555 additions and 458 deletions

View File

@ -39,6 +39,7 @@ export class ConfigUtil {
enableWs: true, enableWs: true,
enableWsReverse: false, enableWsReverse: false,
messagePostFormat: "array", messagePostFormat: "array",
enableHttpHeart: false
} }
let defaultConfig: Config = { let defaultConfig: Config = {
ob11: ob11Default, ob11: ob11Default,

View File

@ -58,6 +58,15 @@ export async function getGroup(qq: string): Promise<Group | undefined> {
return group return group
} }
export function deleteGroup(groupCode: string) {
const groupIndex = groups.findIndex(group => group.groupCode === groupCode.toString())
// log(groups, groupCode, groupIndex);
if (groupIndex !== -1) {
log("删除群", groupCode);
groups.splice(groupIndex, 1)
}
}
export async function getGroupMember(groupQQ: string | number, memberUinOrUid: string | number) { export async function getGroupMember(groupQQ: string | number, memberUinOrUid: string | number) {
groupQQ = groupQQ.toString() groupQQ = groupQQ.toString()
memberUinOrUid = memberUinOrUid.toString() memberUinOrUid = memberUinOrUid.toString()

View File

@ -95,6 +95,9 @@ export abstract class HttpServerBase {
if (method == "get") { if (method == "get") {
payload = req.query payload = req.query
} }
else if (req.query){
payload = {...req.query, ...req.body}
}
log("收到http请求", url, payload); log("收到http请求", url, payload);
try { try {
res.send(await handler(res, payload)) res.send(await handler(res, payload))

View File

@ -9,6 +9,7 @@ export interface OB11Config {
enableWs?: boolean enableWs?: boolean
enableWsReverse?: boolean enableWsReverse?: boolean
messagePostFormat?: 'array' | 'string' messagePostFormat?: 'array' | 'string'
enableHttpHeart?: boolean
} }
export interface CheckVersion { export interface CheckVersion {
result: boolean, result: boolean,

View File

@ -34,7 +34,7 @@ import {
GroupNotifyTypes, GroupNotifyTypes,
RawMessage RawMessage
} from "../ntqqapi/types"; } from "../ntqqapi/types";
import {ob11HTTPServer} from "../onebot11/server/http"; import {httpHeart, ob11HTTPServer} from "../onebot11/server/http";
import {OB11FriendRecallNoticeEvent} from "../onebot11/event/notice/OB11FriendRecallNoticeEvent"; import {OB11FriendRecallNoticeEvent} from "../onebot11/event/notice/OB11FriendRecallNoticeEvent";
import {OB11GroupRecallNoticeEvent} from "../onebot11/event/notice/OB11GroupRecallNoticeEvent"; import {OB11GroupRecallNoticeEvent} from "../onebot11/event/notice/OB11GroupRecallNoticeEvent";
import {postOB11Event} from "../onebot11/server/postOB11Event"; import {postOB11Event} from "../onebot11/server/postOB11Event";
@ -148,7 +148,10 @@ function onLoad() {
async function postReceiveMsg(msgList: RawMessage[]) { async function postReceiveMsg(msgList: RawMessage[]) {
const {debug, reportSelfMessage} = getConfigUtil().getConfig(); const {debug, reportSelfMessage} = getConfigUtil().getConfig();
for (let message of msgList) { for (let message of msgList) {
// 过滤启动之前的消息
if (parseInt(message.msgTime) < startTime / 1000) {
continue;
}
// log("收到新消息", message.msgId, message.msgSeq) // log("收到新消息", message.msgId, message.msgSeq)
// if (message.senderUin !== selfInfo.uin){ // if (message.senderUin !== selfInfo.uin){
message.msgShortId = await dbUtil.addMsg(message); message.msgShortId = await dbUtil.addMsg(message);
@ -379,7 +382,7 @@ function onLoad() {
}) })
} }
let startTime = 0; let startTime = 0; // 毫秒
async function start() { async function start() {
log("llonebot pid", process.pid) log("llonebot pid", process.pid)
@ -402,6 +405,9 @@ function onLoad() {
if (config.ob11.enableWsReverse) { if (config.ob11.enableWsReverse) {
ob11ReverseWebsockets.start(); ob11ReverseWebsockets.start();
} }
if (config.ob11.enableHttpHeart){
httpHeart.start();
}
log("LLOneBot start") log("LLOneBot start")
} }

View File

@ -1,5 +1,5 @@
import {Config} from "../common/types"; import {Config} from "../common/types";
import {ob11HTTPServer} from "../onebot11/server/http"; import {httpHeart, ob11HTTPServer} from "../onebot11/server/http";
import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer"; import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer";
import {ob11ReverseWebsockets} from "../onebot11/server/ws/ReverseWebsocket"; import {ob11ReverseWebsockets} from "../onebot11/server/ws/ReverseWebsocket";
import {llonebotError} from "../common/data"; import {llonebotError} from "../common/data";
@ -54,6 +54,14 @@ export async function setConfig(config: Config) {
} }
} }
} }
if (config.ob11.enableHttpHeart){
// 启动http心跳
httpHeart.start();
}
else{
// 关闭http心跳
httpHeart.stop();
}
log("old config", oldConfig) log("old config", oldConfig)
log("配置已更新", config) log("配置已更新", config)
checkFfmpeg(config.ffmpeg).then() checkFfmpeg(config.ffmpeg).then()

View File

@ -1,7 +1,7 @@
import {ReceiveCmdS} from "../hook"; 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 {deleteGroup, uidMaps} from "../../common/data";
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";
@ -102,13 +102,17 @@ export class NTQQGroupApi{
}); });
} }
static async quitGroup(groupQQ: string) { static async quitGroup(groupQQ: string) {
await callNTQQApi<GeneralCallResult>({ const result = await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.QUIT_GROUP, methodName: NTQQApiMethod.QUIT_GROUP,
args: [ args: [
{"groupCode": groupQQ}, {"groupCode": groupQQ},
null null
] ]
}) })
if (result.result === 0){
deleteGroup(groupQQ);
}
return result;
} }
static async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') { static async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') {
return await callNTQQApi<GeneralCallResult>( return await callNTQQApi<GeneralCallResult>(

View File

@ -2,7 +2,16 @@ import {BrowserWindow} from 'electron';
import {NTQQApiClass, NTQQApiMethod} 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, getFriend, getGroupMember, groups, selfInfo, tempGroupCodeMap, uidMaps} from "../common/data"; import {
deleteGroup,
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";
@ -233,6 +242,11 @@ 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);
if (group.privilegeFlag === 0){
deleteGroup(group.groupCode);
continue;
}
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) => {
@ -294,7 +308,9 @@ async function processGroupEvent(payload: { groupList: Group[] }) {
} }
} }
} }
if (group.privilegeFlag === 0){
deleteGroup(group.groupCode);
}
} }
} }

View File

@ -6,15 +6,25 @@ import {ActionName} from "../types";
import {NTQQGroupApi} from "../../../ntqqapi/api"; import {NTQQGroupApi} from "../../../ntqqapi/api";
import {log} from "../../../common/utils"; import {log} from "../../../common/utils";
interface Payload {
no_cache: boolean
}
class GetGroupList extends BaseAction<null, OB11Group[]> { class GetGroupList extends BaseAction<Payload, OB11Group[]> {
actionName = ActionName.GetGroupList actionName = ActionName.GetGroupList
protected async _handle(payload: null) { protected async _handle(payload: Payload) {
// if (groups.length === 0) { if (groups.length === 0
// const groups = await NTQQGroupApi.getGroups(true) // || payload.no_cache === true
) {
try {
const groups = await NTQQGroupApi.getGroups(true)
// log("get groups", groups) // log("get groups", groups)
// } return OB11Constructor.groups(groups);
} catch (e) {
}
}
return OB11Constructor.groups(groups); return OB11Constructor.groups(groups);
} }
} }

View File

@ -91,6 +91,8 @@ function initActionMap() {
const actionMap = new Map<string, BaseAction<any, any>>(); const actionMap = new Map<string, BaseAction<any, any>>();
for (const action of actionHandlers) { for (const action of actionHandlers) {
actionMap.set(action.actionName, action); actionMap.set(action.actionName, action);
actionMap.set(action.actionName + '_async', action);
actionMap.set(action.actionName + '_rate_limited', action);
} }
return actionMap return actionMap

View File

@ -74,15 +74,15 @@ export interface ReturnDataType {
export function convertMessage2List(message: OB11MessageMixType, autoEscape = false) { export function convertMessage2List(message: OB11MessageMixType, autoEscape = false) {
if (typeof message === "string") { if (typeof message === "string") {
if (!autoEscape) { if (autoEscape === true) {
message = decodeCQCode(message.toString())
} else {
message = [{ message = [{
type: OB11MessageDataType.text, type: OB11MessageDataType.text,
data: { data: {
text: message text: message
} }
}] }]
} else {
message = decodeCQCode(message.toString())
} }
} else if (!Array.isArray(message)) { } else if (!Array.isArray(message)) {
message = [message] message = [message]
@ -329,7 +329,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
} else { } else {
throw ("发送消息参数错误, 请指定group_id或user_id") throw ("发送消息参数错误, 请指定group_id或user_id")
} }
const messages = convertMessage2List(payload.message, !!payload.auto_escape); const messages = convertMessage2List(payload.message, payload.auto_escape);
if (this.getSpecialMsgNum(payload, OB11MessageDataType.node)) { if (this.getSpecialMsgNum(payload, OB11MessageDataType.node)) {
try { try {
const returnMsg = await this.handleForwardNode(peer, messages as OB11MessageNode[], group) const returnMsg = await this.handleForwardNode(peer, messages as OB11MessageNode[], group)
@ -508,6 +508,9 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
// nodeIds.push(nodeMsg.msgId) // nodeIds.push(nodeMsg.msgId)
// await sleep(500); // await sleep(500);
// 开发转发 // 开发转发
if (nodeMsgIds.length === 0) {
throw Error("转发消息失败,节点为空")
}
try { try {
log("开发转发", nodeMsgIds) log("开发转发", nodeMsgIds)
return await NTQQMsgApi.multiForwardMsg(srcPeer, destPeer, nodeMsgIds) return await NTQQMsgApi.multiForwardMsg(srcPeer, destPeer, nodeMsgIds)

View File

@ -22,7 +22,7 @@ import {
User, User,
VideoElement VideoElement
} from '../ntqqapi/types'; } from '../ntqqapi/types';
import {getFriend, getGroupMember, selfInfo, tempGroupCodeMap} from '../common/data'; import {deleteGroup, getFriend, getGroupMember, groups, selfInfo, tempGroupCodeMap} from '../common/data';
import {EventType} from "./event/OB11BaseEvent"; import {EventType} from "./event/OB11BaseEvent";
import {encodeCQCode} from "./cqcode"; import {encodeCQCode} from "./cqcode";
import {dbUtil} from "../common/db"; import {dbUtil} from "../common/db";
@ -39,6 +39,7 @@ import {getConfigUtil} from "../common/config";
import {OB11GroupTitleEvent} from "./event/notice/OB11GroupTitleEvent"; import {OB11GroupTitleEvent} from "./event/notice/OB11GroupTitleEvent";
import {OB11GroupCardEvent} from "./event/notice/OB11GroupCardEvent"; import {OB11GroupCardEvent} from "./event/notice/OB11GroupCardEvent";
import {OB11GroupDecreaseEvent} from "./event/notice/OB11GroupDecreaseEvent"; import {OB11GroupDecreaseEvent} from "./event/notice/OB11GroupDecreaseEvent";
import {NTQQGroupApi} from "../ntqqapi/api";
let lastRKeyUpdateTime = 0; let lastRKeyUpdateTime = 0;
@ -158,8 +159,7 @@ export class OB11Constructor {
// } // }
// } // }
message_data["data"]["url"] = IMAGE_HTTP_HOST + url message_data["data"]["url"] = IMAGE_HTTP_HOST + url
} } else {
else{
// 有可能会碰到appid为1406的这个不能使用新的NT域名并且需要把appid改为1407才可访问 // 有可能会碰到appid为1406的这个不能使用新的NT域名并且需要把appid改为1407才可访问
message_data["data"]["url"] = `${IMAGE_HTTP_HOST}/download?appid=1407&fileid=${fileUuid}&rkey=${currentRKey}&spec=0` message_data["data"]["url"] = `${IMAGE_HTTP_HOST}/download?appid=1407&fileid=${fileUuid}&rkey=${currentRKey}&spec=0`
} }
@ -228,25 +228,23 @@ export class OB11Constructor {
message_data["data"]["data"] = element.arkElement.bytesData; message_data["data"]["data"] = element.arkElement.bytesData;
} else if (element.faceElement) { } else if (element.faceElement) {
const faceId = element.faceElement.faceIndex; const faceId = element.faceElement.faceIndex;
if (faceId === FaceIndex.dice){ if (faceId === FaceIndex.dice) {
message_data["type"] = OB11MessageDataType.dice message_data["type"] = OB11MessageDataType.dice
message_data["data"]["result"] = element.faceElement.resultId; message_data["data"]["result"] = element.faceElement.resultId;
} } else if (faceId === FaceIndex.RPS) {
else if (faceId === FaceIndex.RPS){
message_data["type"] = OB11MessageDataType.RPS message_data["type"] = OB11MessageDataType.RPS
message_data["data"]["result"] = element.faceElement.resultId; message_data["data"]["result"] = element.faceElement.resultId;
} } else {
else{
message_data["type"] = OB11MessageDataType.face; message_data["type"] = OB11MessageDataType.face;
message_data["data"]["id"] = element.faceElement.faceIndex.toString(); 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;
} else if (element.markdownElement){ } else if (element.markdownElement) {
message_data["type"] = OB11MessageDataType.markdown; message_data["type"] = OB11MessageDataType.markdown;
message_data["data"]["data"] = element.markdownElement.content; message_data["data"]["data"] = element.markdownElement.content;
} else if (element.multiForwardMsgElement){ } else if (element.multiForwardMsgElement) {
message_data["type"] = OB11MessageDataType.forward; message_data["type"] = OB11MessageDataType.forward;
message_data["data"]["id"] = msg.msgId message_data["data"]["id"] = msg.msgId
} }
@ -267,7 +265,7 @@ export class OB11Constructor {
if (msg.chatType !== ChatType.group) { if (msg.chatType !== ChatType.group) {
return; return;
} }
if (msg.senderUin){ if (msg.senderUin) {
let member = await getGroupMember(msg.peerUid, msg.senderUin); let member = await getGroupMember(msg.peerUid, msg.senderUin);
if (member && member.cardName !== msg.sendMemberName) { if (member && member.cardName !== msg.sendMemberName) {
const event = new OB11GroupCardEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), msg.sendMemberName, member.cardName) const event = new OB11GroupCardEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), msg.sendMemberName, member.cardName)
@ -317,13 +315,18 @@ export class OB11Constructor {
if (memberUin && adminUin) { if (memberUin && adminUin) {
return new OB11GroupBanEvent(parseInt(msg.peerUid), parseInt(memberUin), parseInt(adminUin), duration, sub_type); return new OB11GroupBanEvent(parseInt(msg.peerUid), parseInt(memberUin), parseInt(adminUin), duration, sub_type);
} }
} } else if (groupElement.type == TipGroupElementType.kicked) {
else if (groupElement.type == TipGroupElementType.kicked){ log(`收到我被踢出或退群提示, 群${msg.peerUid}`, groupElement)
log("收到我被踢出提示", groupElement) deleteGroup(msg.peerUid);
NTQQGroupApi.quitGroup(msg.peerUid).then()
try {
const adminUin = (await getGroupMember(msg.peerUid, groupElement.adminUid))?.uin || (await NTQQUserApi.getUserDetailInfo(groupElement.adminUid))?.uin const adminUin = (await getGroupMember(msg.peerUid, groupElement.adminUid))?.uin || (await NTQQUserApi.getUserDetailInfo(groupElement.adminUid))?.uin
if (adminUin) { if (adminUin) {
return new OB11GroupDecreaseEvent(parseInt(msg.peerUid), parseInt(selfInfo.uin), parseInt(adminUin), "kick_me"); return new OB11GroupDecreaseEvent(parseInt(msg.peerUid), parseInt(selfInfo.uin), parseInt(adminUin), "kick_me");
} }
} catch (e) {
return new OB11GroupDecreaseEvent(parseInt(msg.peerUid), parseInt(selfInfo.uin), 0, "leave");
}
} }
} else if (element.fileElement) { } else if (element.fileElement) {
return new OB11GroupUploadNoticeEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), { return new OB11GroupUploadNoticeEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), {

View File

@ -1,8 +1,11 @@
import {Response} from "express"; import {Response} from "express";
import {OB11Response} from "../action/OB11Response"; import {OB11Response} from "../action/OB11Response";
import {HttpServerBase} from "../../common/server/http"; import {HttpServerBase} from "../../common/server/http";
import {actionHandlers} from "../action"; import {actionHandlers, actionMap} from "../action";
import {getConfigUtil} from "../../common/config"; import {getConfigUtil} from "../../common/config";
import {postOB11Event} from "./postOB11Event";
import {OB11HeartbeatEvent} from "../event/meta/OB11HeartbeatEvent";
import {selfInfo} from "../../common/data";
class OB11HTTPServer extends HttpServerBase { class OB11HTTPServer extends HttpServerBase {
name = "OneBot V11 server" name = "OneBot V11 server"
@ -21,9 +24,32 @@ class OB11HTTPServer extends HttpServerBase {
export const ob11HTTPServer = new OB11HTTPServer(); export const ob11HTTPServer = new OB11HTTPServer();
setTimeout(() => { setTimeout(() => {
for (const action of actionHandlers) { for (const [actionName, action] of actionMap) {
for (const method of ["post", "get"]) { for (const method of ["post", "get"]) {
ob11HTTPServer.registerRouter(method, action.actionName, (res, payload) => action.handle(payload)) ob11HTTPServer.registerRouter(method, actionName, (res, payload) => action.handle(payload))
} }
} }
}, 0) }, 0)
class HTTPHeart{
intervalId: NodeJS.Timeout | null = null
start(){
const {heartInterval} = getConfigUtil().getConfig();
if (this.intervalId) {
clearInterval(this.intervalId);
}
this.intervalId = setInterval(() => {
// ws的心跳是ws自己维护的
postOB11Event(new OB11HeartbeatEvent(selfInfo.online, true, heartInterval), false, false)
}, heartInterval)
}
stop(){
if (this.intervalId){
clearInterval(this.intervalId)
}
}
}
export const httpHeart = new HTTPHeart();

View File

@ -69,7 +69,7 @@ export function postWsEvent(event: PostEventType) {
} }
} }
export function postOB11Event(msg: PostEventType, reportSelf = false) { export function postOB11Event(msg: PostEventType, reportSelf = false, postWs = true) {
const config = getConfigUtil().getConfig(); const config = getConfigUtil().getConfig();
// 判断msg是否是event // 判断msg是否是event
if (!config.reportSelfMessage && !reportSelf) { if (!config.reportSelfMessage && !reportSelf) {
@ -172,5 +172,7 @@ export function postOB11Event(msg: PostEventType, reportSelf = false) {
}); });
} }
} }
if (postWs){
postWsEvent(msg); postWsEvent(msg);
}
} }

View File

@ -68,6 +68,9 @@ async function onSettingWindowCreated(view: Element) {
`<div class="q-input"><input class="q-input__inner" data-config-key="ob11.httpPort" type="number" min="1" max="65534" value="${config.ob11.httpPort}" placeholder="${config.ob11.httpPort}" /></div>`, `<div class="q-input"><input class="q-input__inner" data-config-key="ob11.httpPort" type="number" min="1" max="65534" value="${config.ob11.httpPort}" placeholder="${config.ob11.httpPort}" /></div>`,
'config-ob11-httpPort', config.ob11.enableHttp 'config-ob11-httpPort', config.ob11.enableHttp
), ),
SettingItem('启用 HTTP 心跳', null,
SettingSwitch('ob11.enableHttpHeart', config.ob11.enableHttpHeart, {'control-display-id': 'config-ob11-enableHttpHeart'}),
),
SettingItem('启用 HTTP 事件上报', null, SettingItem('启用 HTTP 事件上报', null,
SettingSwitch('ob11.enableHttpPost', config.ob11.enableHttpPost, {'control-display-id': 'config-ob11-httpHosts'}), SettingSwitch('ob11.enableHttpPost', config.ob11.enableHttpPost, {'control-display-id': 'config-ob11-httpHosts'}),
), ),