Compare commits

..

13 Commits

Author SHA1 Message Date
linyuchen
2eb0ad589a chore: ver 3.7.0 2024-02-23 19:57:20 +08:00
linyuchen
829aba18f8 feat: 管理员变动事件
feat: 加群事件
feat: 加群请求处理api
feat: 退群api
fix: 回复消息id改为string
2024-02-23 19:56:20 +08:00
linyuchen
67dfd7c22f Merge branch 'main' into dev 2024-02-23 14:06:36 +08:00
linyuchen
27745087ad Merge pull request #69 from MisaLiu/fix_app_version 2024-02-23 11:50:37 +08:00
linyuchen
4ba333b6f5 Merge pull request #68 from MisaLiu/fix_echo 2024-02-23 11:50:28 +08:00
Misa Liu
f4fe26fbe1 fix: Fix app_version in get_version_info 2024-02-23 10:33:15 +08:00
Misa Liu
30e488aeaf fix: Fix var type of echo 2024-02-23 10:22:42 +08:00
linyuchen
1f0dad786c feat: group admin change notice 2024-02-23 04:08:20 +08:00
linyuchen
8dfc71ab6d fix: message id int32 2024-02-22 23:05:07 +08:00
linyuchen
12d1f87ad5 fix: message id int32 2024-02-22 23:02:23 +08:00
linyuchen
b27dadbbca temp save 2024-02-22 22:55:52 +08:00
linyuchen
688624500f docs: tg 2024-02-22 17:35:14 +08:00
linyuchen
eefb919f0f docs: update readme 2024-02-21 22:21:32 +08:00
25 changed files with 364 additions and 71 deletions

View File

@@ -1,7 +1,9 @@
# LLOneBot API
# LLOneBot API
LiteLoaderQQNT的OneBot11协议插件
TG群<https://t.me/+nLZEnpne-pQ1OWFl>
*注意:本文档对应的是 LiteLoader 1.0.0及以上版本如果你使用的是旧版本请切换到本项目v1分支查看文档*
*V3之后不再需要LLAPI*
@@ -31,9 +33,13 @@ LiteLoaderQQNT的OneBot11协议插件
- [x] 获取群列表
- [x] 获取群成员列表
- [x] 撤回消息
- [x] 处理加群请求
- [x] 退群
- [x] 上报好友消息
- [x] 上报群消息
- [x] 上报好友、群消息撤回
- [x] 上报加群请求
- [x] 上报群员人数变动
消息格式支持:
- [x] cq码
@@ -60,6 +66,9 @@ LiteLoaderQQNT的OneBot11协议插件
- [x] get_group_member_info
- [x] get_friend_list
- [x] get_msg
- [x] send_like
- [x] set_group_add_request
- [x] set_group_leave
- [x] get_version_info
- [x] get_status
- [x] can_send_image
@@ -68,6 +77,7 @@ LiteLoaderQQNT的OneBot11协议插件
支持的go-cqhtp api:
- [x] send_private_forward_msg
- [x] send_group_forward_msg
- [x] get_stranger_info
## 示例
@@ -108,7 +118,7 @@ LiteLoaderQQNT的OneBot11协议插件
- [x] 重构摆脱LLAPI目前调用LLAPI只能在renderer进程调用需重构成在main进程调用
- [x] 支持正、反向websocket感谢@disymayufei的PR
- [x] 转发消息记录
- [ ] 好友点赞api
- [x] 好友点赞api
## onebot11文档
<https://11.onebot.dev/>

View File

@@ -4,7 +4,7 @@
"name": "LLOneBot",
"slug": "LLOneBot",
"description": "LiteLoaderQQNT的OneBotApi",
"version": "3.6.0",
"version": "3.7.0",
"thumbnail": "./icon.png",
"authors": [
{

View File

@@ -2,6 +2,8 @@ import fs from "fs";
import {Config, OB11Config} from "./types";
import {mergeNewProperties} from "./utils";
export const HOOK_LOG = false;
export class ConfigUtil {
private readonly configPath: string;
private config: Config | null = null;

View File

@@ -1,15 +1,15 @@
import {NTQQApi} from '../ntqqapi/ntcall';
import {Friend, Group, GroupMember, RawMessage, SelfInfo} from "../ntqqapi/types";
import {Friend, Group, GroupMember, GroupNotify, RawMessage, SelfInfo} from "../ntqqapi/types";
export let groups: Group[] = []
export let friends: Friend[] = []
export let msgHistory: Record<string, RawMessage> = {} // msgId: RawMessage
let globalMsgId = Date.now()
let globalMsgId = Math.floor(Date.now() / 1000);
export function addHistoryMsg(msg: RawMessage): boolean{
export function addHistoryMsg(msg: RawMessage): boolean {
let existMsg = msgHistory[msg.msgId]
if (existMsg){
if (existMsg) {
Object.assign(existMsg, msg)
msg.msgShortId = existMsg.msgShortId;
return false
@@ -19,7 +19,7 @@ export function addHistoryMsg(msg: RawMessage): boolean{
return true
}
export function getHistoryMsgByShortId(shortId: number | string){
export function getHistoryMsgByShortId(shortId: number | string) {
// log("getHistoryMsgByShortId", shortId, Object.values(msgHistory).map(m=>m.msgShortId))
return Object.values(msgHistory).find(msg => msg.msgShortId.toString() == shortId.toString())
}
@@ -43,20 +43,19 @@ export async function getGroup(qq: string): Promise<Group | undefined> {
return group
}
export async function getGroupMember(groupQQ: string, memberQQ: string=null, memberUid: string=null) {
export async function getGroupMember(groupQQ: string, memberQQ: string, memberUid: string = null) {
const group = await getGroup(groupQQ)
if (group) {
let filterFunc: (member: GroupMember) => boolean
if (memberQQ){
if (memberQQ) {
filterFunc = member => member.uin === memberQQ
}
else if (memberUid){
} else if (memberUid) {
filterFunc = member => member.uid === memberUid
}
let member = group.members?.find(filterFunc)
if (!member){
if (!member) {
const _members = await NTQQApi.getGroupMembers(groupQQ)
if (_members.length){
if (_members.length) {
group.members = _members
}
member = group.members?.find(filterFunc)
@@ -77,7 +76,7 @@ export function getHistoryMsgBySeq(seq: string) {
}
export let uidMaps:Record<string, string> = {} // 一串加密的字符串(uid) -> qq号
export let uidMaps: Record<string, string> = {} // 一串加密的字符串(uid) -> qq号
export function getUidByUin(uin: string) {
for (const key in uidMaps) {
@@ -87,4 +86,6 @@ export function getUidByUin(uin: string) {
}
}
export const version = "v3.6.0"
export const version = "3.7.0"
export let groupNotifies: Map<string, GroupNotify> = new Map();

View File

@@ -193,4 +193,8 @@ export async function encodeSilk(filePath: string) {
log("convert silk failed", error.stack);
return {};
}
}
export function isNull(value: any) {
return value === undefined || value === null;
}

View File

@@ -6,17 +6,19 @@ import {Config} from "../common/types";
import {CHANNEL_GET_CONFIG, CHANNEL_LOG, CHANNEL_SET_CONFIG,} from "../common/channels";
import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer";
import {CONFIG_DIR, getConfigUtil, log} from "../common/utils";
import {addHistoryMsg, getGroupMember, msgHistory, selfInfo} from "../common/data";
import {addHistoryMsg, getGroup, getGroupMember, groupNotifies, msgHistory, selfInfo} from "../common/data";
import {hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmd, registerReceiveHook} from "../ntqqapi/hook";
import {OB11Constructor} from "../onebot11/constructor";
import {NTQQApi} from "../ntqqapi/ntcall";
import {ChatType, RawMessage} from "../ntqqapi/types";
import {ChatType, GroupMember, GroupNotifies, GroupNotifyTypes, RawMessage} from "../ntqqapi/types";
import {ob11HTTPServer} from "../onebot11/server/http";
import {OB11FriendRecallNoticeEvent} from "../onebot11/event/notice/OB11FriendRecallNoticeEvent";
import {OB11GroupRecallNoticeEvent} from "../onebot11/event/notice/OB11GroupRecallNoticeEvent";
import {postEvent} from "../onebot11/server/postevent";
import {ob11ReverseWebsockets} from "../onebot11/server/ws/ReverseWebsocket";
import {EventType} from "../onebot11/event/OB11BaseEvent";
import {OB11GroupAdminNoticeEvent} from "../onebot11/event/notice/OB11GroupAdminNoticeEvent";
import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent";
import {OB11GroupRequestEvent} from "../onebot11/event/request/OB11GroupRequest";
let running = false;
@@ -105,8 +107,7 @@ function onLoad() {
}
}
async function start() {
async function startReceiveHook() {
registerReceiveHook<{ msgList: Array<RawMessage> }>(ReceiveCmd.NEW_MSG, (payload) => {
try {
postReceiveMsg(payload.msgList);
@@ -159,6 +160,81 @@ function onLoad() {
log("report self message error: ", e.toString());
}
})
registerReceiveHook<{
"doubt": boolean,
"oldestUnreadSeq": string,
"unreadCount": number
}>(ReceiveCmd.UNREAD_GROUP_NOTIFY, async (payload) => {
if (payload.unreadCount) {
log("开始获取群通知详情")
let notify: GroupNotifies;
try {
notify = await NTQQApi.getGroupNotifies();
}catch (e) {
// log("获取群通知详情失败", e);
return
}
const notifies = notify.notifies.slice(0, payload.unreadCount)
log("获取群通知详情完成", notifies, payload);
try {
for (const notify of notifies) {
if (parseInt(notify.seq) / 1000 < startTime){
continue;
}
const member1 = await getGroupMember(notify.group.groupCode, null, notify.user1.uid);
let member2: GroupMember;
if (notify.user2.uid){
member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
}
if ([GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET].includes(notify.type)) {
log("有管理员变动通知");
let groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent()
groupAdminNoticeEvent.group_id = parseInt(notify.group.groupCode);
log("开始获取变动的管理员")
if(member1){
log("变动管理员获取成功")
groupAdminNoticeEvent.user_id = parseInt(member1.uin);
groupAdminNoticeEvent.sub_type = notify.type == GroupNotifyTypes.ADMIN_UNSET ? "unset" : "set";
postEvent(groupAdminNoticeEvent, true);
}
else{
log("获取群通知的成员信息失败", notify, getGroup(notify.group.groupCode));
}
}
else if (notify.type == GroupNotifyTypes.MEMBER_EXIT){
log("有成员退出通知");
let groupDecreaseEvent = new OB11GroupDecreaseEvent(parseInt(notify.group.groupCode), parseInt(member1.uin))
// postEvent(groupDecreaseEvent, true);
}
else if ([GroupNotifyTypes.JOIN_REQUEST].includes(notify.type)){
log("有加群请求");
groupNotifies[notify.seq] = notify;
let groupRequestEvent = new OB11GroupRequestEvent();
groupRequestEvent.group_id = parseInt(notify.group.groupCode);
let requestQQ = ""
try {
requestQQ = (await NTQQApi.getUserInfo(notify.user1.uid)).uin;
}catch (e) {
log("获取加群人QQ号失败", e)
}
groupRequestEvent.user_id = parseInt(requestQQ) || 0;
groupRequestEvent.sub_type = "add"
groupRequestEvent.comment = notify.postscript;
groupRequestEvent.flag = notify.seq;
postEvent(groupRequestEvent);
}
}
}catch (e) {
log("解析群通知失败", e.stack);
}
}
})
}
let startTime = 0;
async function start() {
startTime = Date.now();
startReceiveHook().then();
NTQQApi.getGroups(true).then()
const config = getConfigUtil().getConfig()
if (config.ob11.enableHttp) {
@@ -168,16 +244,17 @@ function onLoad() {
log("http server start failed", e);
}
}
if (config.ob11.enableWs){
if (config.ob11.enableWs) {
ob11WebsocketServer.start(config.ob11.wsPort);
}
if (config.ob11.enableWsReverse){
if (config.ob11.enableWsReverse) {
ob11ReverseWebsockets.start();
}
log("LLOneBot start")
}
let getSelfNickCount = 0;
const init = async () => {
try {
const _ = await NTQQApi.getSelfInfo();
@@ -194,7 +271,10 @@ function onLoad() {
if (userInfo) {
selfInfo.nick = userInfo.nick;
} else {
return setTimeout(init, 1000);
getSelfNickCount++;
if (getSelfNickCount < 10){
return setTimeout(init, 1000);
}
}
} catch (e) {
log("get self nickname failed", e.toString());

View File

@@ -7,6 +7,7 @@ import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecrease
import {OB11GroupIncreaseEvent} from "../onebot11/event/notice/OB11GroupIncreaseEvent";
import {v4 as uuidv4} from "uuid"
import {postEvent} from "../onebot11/server/postevent";
import {HOOK_LOG} from "../common/config";
export let hookApiCallbacks: Record<string, (apiReturn: any) => void> = {}
@@ -18,7 +19,9 @@ export enum ReceiveCmd {
GROUPS = "nodeIKernelGroupListener/onGroupListUpdate",
GROUPS_UNIX = "onGroupListUpdate",
FRIENDS = "onBuddyListChange",
MEDIA_DOWNLOAD_COMPLETE = "nodeIKernelMsgListener/onRichMediaDownloadComplete"
MEDIA_DOWNLOAD_COMPLETE = "nodeIKernelMsgListener/onRichMediaDownloadComplete",
UNREAD_GROUP_NOTIFY = "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated",
GROUP_NOTIFY = "nodeIKernelGroupListener/onGroupSingleScreenNotifies"
}
interface NTQQApiReturnData<PayloadType = unknown> extends Array<any> {
@@ -44,7 +47,7 @@ let receiveHooks: Array<{
export function hookNTQQApiReceive(window: BrowserWindow) {
const originalSend = window.webContents.send;
const patchSend = (channel: string, ...args: NTQQApiReturnData) => {
// log(`received ntqq api message: ${channel}`, JSON.stringify(args))
HOOK_LOG && log(`received ntqq api message: ${channel}`, JSON.stringify(args))
if (args?.[1] instanceof Array) {
for (let receiveData of args?.[1]) {
const ntQQApiMethodName = receiveData.cmdName;
@@ -88,15 +91,15 @@ export function hookNTQQApiCall(window: BrowserWindow) {
const proxyIpcMsg = new Proxy(ipc_message_proxy, {
apply(target, thisArg, args) {
log("call NTQQ api", thisArg, args);
HOOK_LOG && log("call NTQQ api", thisArg, args);
return target.apply(thisArg, args);
},
});
// if (webContents._events["-ipc-message"]?.[0]) {
// webContents._events["-ipc-message"][0] = proxyIpcMsg;
// } else {
// webContents._events["-ipc-message"] = proxyIpcMsg;
// }
if (webContents._events["-ipc-message"]?.[0]) {
webContents._events["-ipc-message"][0] = proxyIpcMsg;
} else {
webContents._events["-ipc-message"] = proxyIpcMsg;
}
}
export function registerReceiveHook<PayloadType>(method: ReceiveCmd, hookFunc: (payload: PayloadType) => void): string {
@@ -251,3 +254,4 @@ registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmd.SELF_SEND_MSG, ({msgRe
}
}
})

View File

@@ -1,9 +1,19 @@
import {ipcMain} from "electron";
import {hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook} from "./hook";
import {log} from "../common/utils";
import {ChatType, Friend, Group, GroupMember, RawMessage, SelfInfo, SendMessageElement, User} from "./types";
import {
ChatType,
Friend,
Group,
GroupMember,
GroupNotifies, GroupNotify, GroupRequestOperateTypes,
RawMessage,
SelfInfo,
SendMessageElement,
User
} from "./types";
import * as fs from "fs";
import {addHistoryMsg, msgHistory, selfInfo} from "../common/data";
import {addHistoryMsg, groupNotifies, msgHistory, selfInfo} from "../common/data";
import {v4 as uuidv4} from "uuid"
interface IPCReceiveEvent {
@@ -41,7 +51,10 @@ export enum NTQQApiMethod {
RECALL_MSG = "nodeIKernelMsgService/recallMsg",
SEND_MSG = "nodeIKernelMsgService/sendMsg",
DOWNLOAD_MEDIA = "nodeIKernelMsgService/downloadRichMedia",
MULTI_FORWARD_MSG = "nodeIKernelMsgService/multiForwardMsgWithComment" // 合并转发
MULTI_FORWARD_MSG = "nodeIKernelMsgService/multiForwardMsgWithComment", // 合并转发
GET_GROUP_NOTICE = "nodeIKernelGroupService/getSingleScreenNotifies",
HANDLE_GROUP_REQUEST = "nodeIKernelGroupService/operateSysNotify",
QUIT_GROUP = "nodeIKernelGroupService/quitGroup",
}
enum NTQQApiChannel {
@@ -56,15 +69,11 @@ export interface Peer {
guildId?: ""
}
enum CallBackType {
UUID,
METHOD
}
interface NTQQApiParams {
methodName: NTQQApiMethod,
methodName: NTQQApiMethod | string,
className?: NTQQApiClass,
channel?: NTQQApiChannel,
classNameIsRegister?: boolean
args?: unknown[],
cbCmd?: ReceiveCmd | null,
cmdCB?: (payload: any) => boolean;
@@ -75,7 +84,7 @@ function callNTQQApi<ReturnType>(params: NTQQApiParams) {
let {
className, methodName, channel, args,
cbCmd, timeoutSecond: timeout,
cmdCB
classNameIsRegister, cmdCB
} = params;
className = className ?? NTQQApiClass.NT_API;
channel = channel ?? NTQQApiChannel.IPC_UP_2;
@@ -87,6 +96,11 @@ function callNTQQApi<ReturnType>(params: NTQQApiParams) {
// log("callNTQQApiPromise", channel, className, methodName, args, uuid)
const _timeout = timeout * 1000
let success = false
let eventName = className + "-" + channel[channel.length - 1];
if (classNameIsRegister) {
eventName += "-register";
}
const apiArgs = [methodName, ...args]
if (!cbCmd) {
// QQ后端会返回结果并且可以插根据uuid识别
hookApiCallbacks[uuid] = (r: ReturnType) => {
@@ -121,12 +135,11 @@ function callNTQQApi<ReturnType>(params: NTQQApiParams) {
setTimeout(() => {
// log("ntqq api timeout", success, channel, className, methodName)
if (!success) {
log(`ntqq api timeout ${channel}, ${className}, ${methodName}`)
reject(`ntqq api timeout ${channel}, ${className}, ${methodName}`)
log(`ntqq api timeout ${channel}, ${eventName}, ${methodName}`, apiArgs);
reject(`ntqq api timeout ${channel}, ${eventName}, ${methodName}, ${apiArgs}`)
}
}, _timeout)
const eventName = className + "-" + channel[channel.length - 1];
const apiArgs = [methodName, ...args]
ipcMain.emit(
channel,
{},
@@ -240,6 +253,7 @@ export class NTQQApi {
}
// log(uidMaps);
// log("members info", values);
log(`get group ${groupQQ} members success`)
return members
} catch (e) {
log(`get group ${groupQQ} members failed`, e)
@@ -343,10 +357,11 @@ export class NTQQApi {
methodName: NTQQApiMethod.DOWNLOAD_MEDIA,
args: apiParams,
cbCmd: ReceiveCmd.MEDIA_DOWNLOAD_COMPLETE,
cmdCB:(payload: {notifyInfo: {filePath: string}})=>{
cmdCB: (payload: { notifyInfo: { filePath: string } }) => {
// log("media 下载完成判断", payload.notifyInfo.filePath, sourcePath);
return payload.notifyInfo.filePath == sourcePath;
}})
}
})
return sourcePath
}
@@ -397,6 +412,7 @@ export class NTQQApi {
return reject("发送超时")
}
if (msgHistory[rawMessage.msgId]?.sendStatus == 2) {
log(`${peerUid}发送消息成功`)
success = true;
resolve(rawMessage);
} else {
@@ -407,6 +423,7 @@ export class NTQQApi {
checkSendComplete();
} else {
success = true;
log(`${peerUid}发送消息成功`)
resolve(rawMessage);
}
}
@@ -476,4 +493,56 @@ export class NTQQApi {
})
})
}
static async getGroupNotifies() {
// 获取管理员变更
// 加群通知,退出通知,需要管理员权限
await callNTQQApi<GeneralCallResult>({
methodName: ReceiveCmd.GROUP_NOTIFY,
classNameIsRegister: true,
})
return await callNTQQApi<GroupNotifies>({
methodName: NTQQApiMethod.GET_GROUP_NOTICE,
cbCmd: ReceiveCmd.GROUP_NOTIFY,
args: [
{"doubt": false, "startSeq": "", "number": 14},
null
]
});
}
static async handleGroupRequest(seq: string, operateType: GroupRequestOperateTypes, reason?: string) {
const notify: GroupNotify = groupNotifies[seq];
if (!notify){
throw `${seq}对应的加群通知不存在`
}
return await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.HANDLE_GROUP_REQUEST,
args: [
{
"doubt": false,
"operateMsg": {
"operateType": operateType, // 2 拒绝
"targetMsg": {
"seq": seq, // 通知序列号
"type": notify.type,
"groupCode": notify.group.groupCode,
"postscript": reason
}
}
},
null
]
});
}
static async quitGroup(groupQQ: string){
await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.QUIT_GROUP,
args:[
{"groupCode": groupQQ},
null
]
})
}
}

View File

@@ -241,3 +241,42 @@ export interface RawMessage {
faceElement: FaceElement;
}[];
}
export enum GroupNotifyTypes {
INVITED_JOIN = 4, // 有人接受了邀请入群
JOIN_REQUEST = 7,
ADMIN_SET = 8,
ADMIN_UNSET = 12,
MEMBER_EXIT = 11, // 主动退出?
}
export interface GroupNotifies {
doubt: boolean,
nextStartSeq: string,
notifies: GroupNotify[],
}
export interface GroupNotify {
seq: string, // 转成数字再除以1000应该就是时间戳
type: GroupNotifyTypes,
status: 0, // 未知
group: { groupCode: string, groupName: string },
user1: { uid: string, nickName: string }, // 被设置管理员的人
user2: { uid: string, nickName: string }, // 操作者
actionUser: { uid: string, nickName: string }, //未知
actionTime: string,
invitationExt: {
srcType: number, // 0?未知
groupCode: string, waitStatus: number
},
postscript: string, // 加群用户填写的验证信息
repeatSeqs: [],
warningTips: string
}
export enum GroupRequestOperateTypes{
approve = 1,
reject = 2
}

View File

@@ -23,7 +23,7 @@ class BaseAction<PayloadType, ReturnDataType> {
}
}
public async websocketHandle(payload: PayloadType, echo: string): Promise<OB11Return<ReturnDataType | null>> {
public async websocketHandle(payload: PayloadType, echo: any): Promise<OB11Return<ReturnDataType | null>> {
const result = await this.check(payload)
if (!result.valid) {
return OB11Response.error(result.message, 1400)

View File

@@ -0,0 +1,9 @@
import BaseAction from "./BaseAction";
import {ActionName} from "./types";
export default class GetGuildList extends BaseAction<null, null>{
actionName = ActionName.GetGuildList
protected async _handle(payload: null): Promise<null> {
return null;
}
}

View File

@@ -0,0 +1,30 @@
import BaseAction from "./BaseAction";
import {groupNotifies} from "../../common/data";
import {GroupNotify, GroupRequestOperateTypes} from "../../ntqqapi/types";
import {NTQQApi} from "../../ntqqapi/ntcall";
import {ActionName} from "./types";
interface Payload{
flag: string,
// sub_type: "add" | "invite",
// type: "add" | "invite"
approve: boolean,
reason: string
}
export default class SetGroupAddRequest extends BaseAction<Payload, null>{
actionName = ActionName.SetGroupAddRequest
protected async _handle(payload: Payload): Promise<null> {
const seq = payload.flag.toString();
const notify: GroupNotify = groupNotifies[seq]
try{
await NTQQApi.handleGroupRequest(seq,
payload.approve ? GroupRequestOperateTypes.approve: GroupRequestOperateTypes.reject,
payload.reason
)
}catch (e) {
throw e
}
return null
}
}

View File

@@ -0,0 +1,22 @@
import BaseAction from "./BaseAction";
import {NTQQApi} from "../../ntqqapi/ntcall";
import {log} from "../../common/utils";
import {ActionName} from "./types";
interface Payload{
group_id: number,
is_dismiss: boolean
}
export default class SetGroupLeave extends BaseAction<Payload, any>{
actionName = ActionName.SetGroupLeave
protected async _handle(payload: Payload): Promise<any> {
try{
await NTQQApi.quitGroup(payload.group_id.toString())
}
catch (e) {
log("退群失败", e)
throw e
}
}
}

View File

@@ -17,6 +17,9 @@ import GetStatus from "./GetStatus";
import {GoCQHTTPSendGroupForwardMsg, GoCQHTTPSendPrivateForwardMsg} from "./go-cqhttp/SendForwardMsg";
import GoCQHTTPGetStrangerInfo from "./go-cqhttp/GetStrangerInfo";
import SendLike from "./SendLike";
import SetGroupAddRequest from "./SetGroupAddRequest";
import SetGroupLeave from "./SetGroupLeave";
import GetGuildList from "./GetGuildList";
export const actionHandlers = [
new SendLike(),
@@ -26,6 +29,8 @@ export const actionHandlers = [
new GetGroupList(), new GetGroupInfo(), new GetGroupMemberList(), new GetGroupMemberInfo(),
new SendGroupMsg(), new SendPrivateMsg(), new SendMsg(),
new DeleteMsg(),
new SetGroupAddRequest(),
new SetGroupLeave(),
new GetVersionInfo(),
new CanSendRecord(),
new CanSendImage(),
@@ -34,7 +39,8 @@ export const actionHandlers = [
//以下为go-cqhttp api
new GoCQHTTPSendGroupForwardMsg(),
new GoCQHTTPSendPrivateForwardMsg(),
new GoCQHTTPGetStrangerInfo()
new GoCQHTTPGetStrangerInfo(),
new GetGuildList()
]

View File

@@ -1,3 +1,5 @@
import GetGuildList from "./GetGuildList";
export type BaseCheckResult = ValidCheckResult | InvalidCheckResult
export interface ValidCheckResult {
@@ -25,6 +27,8 @@ export enum ActionName {
SendGroupMsg = "send_group_msg",
SendPrivateMsg = "send_private_msg",
DeleteMsg = "delete_msg",
SetGroupAddRequest = "set_group_add_request",
SetGroupLeave = "set_group_leave",
GetVersionInfo = "get_version_info",
GetStatus = "get_status",
CanSendRecord = "can_send_record",
@@ -32,5 +36,6 @@ export enum ActionName {
// 以下为go-cqhttp api
GoCQHTTP_SendGroupForwardMsg = "send_group_forward_msg",
GoCQHTTP_SendPrivateForwardMsg = "send_private_forward_msg",
GoCQHTTP_GetStrangerInfo = "get_stranger_info"
GoCQHTTP_GetStrangerInfo = "get_stranger_info",
GetGuildList = "get_guild_list",
}

View File

@@ -1,4 +1,5 @@
import {OB11Return} from '../types';
import {isNull} from '../../common/utils';
export class OB11Response {
static res<T>(data: T, status: string, retcode: number, message: string = ""): OB11Return<T> {
@@ -8,21 +9,21 @@ export class OB11Response {
data: data,
message: message,
wording: message,
echo: ""
echo: null
}
}
static ok<T>(data: T, echo: string = "") {
static ok<T>(data: T, echo: any = null) {
let res = OB11Response.res<T>(data, "ok", 0)
if (echo) {
if (!isNull(echo)) {
res.echo = echo;
}
return res;
}
static error(err: string, retcode: number, echo: string = "") {
static error(err: string, retcode: number, echo: any = null) {
let res = OB11Response.res(null, "failed", retcode, err)
if (echo) {
if (!isNull(echo)) {
res.echo = echo;
}
return res;

View File

@@ -114,7 +114,7 @@ export class OB11Constructor {
message_data["type"] = "reply"
const replyMsg = getHistoryMsgBySeq(element.replyElement.replayMsgSeq)
if (replyMsg) {
message_data["data"]["id"] = replyMsg.msgShortId
message_data["data"]["id"] = replyMsg.msgShortId.toString()
} else {
continue
}
@@ -194,7 +194,8 @@ export class OB11Constructor {
group_id: parseInt(group_id),
user_id: parseInt(member.uin),
nickname: member.nick,
card: member.cardName
card: member.cardName,
role: OB11Constructor.groupMemberRole(member.role),
}
}

View File

@@ -1,6 +1,7 @@
import {OB11BaseNoticeEvent} from "./OB11BaseNoticeEvent";
import {OB11GroupNoticeEvent} from "./OB11GroupNoticeEvent";
export class OB11GroupAdminNoticeEvent extends OB11BaseNoticeEvent {
export class OB11GroupAdminNoticeEvent extends OB11GroupNoticeEvent {
notice_type = "group_admin"
sub_type: string // "set" | "unset"
sub_type: "set" | "unset" // "set" | "unset"
}

View File

@@ -2,7 +2,7 @@ import {OB11GroupNoticeEvent} from "./OB11GroupNoticeEvent";
export class OB11GroupDecreaseEvent extends OB11GroupNoticeEvent {
notice_type = "group_decrease";
sub_type = "leave"; // TODO: 实现其他几种子类型的识别 ("leave" | "kick" | "kick_me")
sub_type: "leave" | "kick" | "kick_me" = "leave"; // TODO: 实现其他几种子类型的识别 ("leave" | "kick" | "kick_me")
operate_id: number;
constructor(groupId: number, userId: number) {

View File

@@ -0,0 +1,9 @@
import {OB11GroupNoticeEvent} from "../notice/OB11GroupNoticeEvent";
export class OB11GroupRequestEvent extends OB11GroupNoticeEvent{
request_type: "group" = "group";
sub_type: "add" | "invite" = "add";
comment: string;
flag: string;
}

View File

@@ -29,10 +29,10 @@ export function postWsEvent(event: PostEventType) {
}
}
export function postEvent(msg: PostEventType) {
export function postEvent(msg: PostEventType, reportSelf=false) {
const config = getConfigUtil().getConfig();
// 判断msg是否是event
if (!config.reportSelfMessage) {
if (!config.reportSelfMessage && !reportSelf) {
if ((msg as OB11Message).user_id.toString() == selfInfo.uin) {
return
}

View File

@@ -33,8 +33,8 @@ export class ReverseWebsocket {
}
public async onmessage(msg: string) {
let receiveData: { action: ActionName, params: any, echo?: string } = {action: null, params: {}}
let echo = ""
let receiveData: { action: ActionName, params: any, echo?: any } = {action: null, params: {}}
let echo = null
try {
receiveData = JSON.parse(msg.toString())
echo = receiveData.echo

View File

@@ -18,7 +18,7 @@ class OB11WebsocketServer extends WebsocketServerBase {
wsClient.send(JSON.stringify(OB11Response.res(null, "failed", 1403, "token验证失败")))
}
async handleAction(wsClient: WebSocket, actionName: string, params: any, echo?: string) {
async handleAction(wsClient: WebSocket, actionName: string, params: any, echo?: any) {
const action: BaseAction<any, any> = actionMap.get(actionName);
if (!action) {
return wsReply(wsClient, OB11Response.error("不支持的api " + actionName, 1404, echo))
@@ -34,8 +34,8 @@ class OB11WebsocketServer extends WebsocketServerBase {
onConnect(wsClient: WebSocket, url: string, req: IncomingMessage) {
if (url == "/api" || url == "/api/" || url == "/") {
wsClient.on("message", async (msg) => {
let receiveData: { action: ActionName, params: any, echo?: string } = {action: null, params: {}}
let echo = ""
let receiveData: { action: ActionName, params: any, echo?: any } = {action: null, params: {}}
let echo = null
try {
receiveData = JSON.parse(msg.toString())
echo = receiveData.echo

View File

@@ -1,13 +1,13 @@
import * as websocket from "ws";
import {OB11Response} from "../../action/utils";
import {PostEventType} from "../postevent";
import {log} from "../../../common/utils";
import {isNull, log} from "../../../common/utils";
export function wsReply(wsClient: websocket.WebSocket, data: OB11Response | PostEventType) {
try {
let packet = Object.assign({
}, data);
if (!packet["echo"]){
if (isNull(packet["echo"])){
delete packet["echo"];
}
wsClient.send(JSON.stringify(packet))

View File

@@ -77,7 +77,7 @@ export interface OB11Return<DataType> {
retcode: number
data: DataType
message: string,
echo?: string, // ws调用api才有此字段
echo?: any, // ws调用api才有此字段
wording?: string, // go-cqhttp字段错误信息
}