style: format

This commit is contained in:
linyuchen
2024-03-03 00:22:07 +08:00
parent f7b9d599c3
commit d1e4135442
55 changed files with 5229 additions and 3088 deletions

4434
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,6 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"electron-vite": "^2.0.0",
"express": "^4.18.2", "express": "^4.18.2",
"fluent-ffmpeg": "^2.1.2", "fluent-ffmpeg": "^2.1.2",
"music-metadata": "^8.1.4", "music-metadata": "^8.1.4",
@@ -29,9 +28,15 @@
"@types/node": "^20.11.24", "@types/node": "^20.11.24",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"electron": "^29.0.1", "electron": "^29.0.1",
"electron-vite": "^2.0.0",
"eslint": "^8.0.1",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
"eslint-plugin-promise": "^6.0.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.2.2", "typescript": "*",
"vite": "^5.1.4", "vite": "^5.1.4",
"vite-plugin-cp": "^4.0.8" "vite-plugin-cp": "^4.0.8"
} }

View File

@@ -1,20 +1,22 @@
import fs from "fs"; import fs from 'fs'
import path from"path"; import path from 'path'
import {version} from "../src/version"; import { version } from '../src/version'
const manifestPath = path.join(__dirname, "../manifest.json");
function readManifest(): any{ const manifestPath = path.join(__dirname, '../manifest.json')
if (fs.existsSync(manifestPath)){
return JSON.parse(fs.readFileSync(manifestPath, "utf-8")); function readManifest (): any {
if (fs.existsSync(manifestPath)) {
return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'))
} }
} }
function writeManifest(manifest: any){ function writeManifest (manifest: any) {
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
} }
let manifest = readManifest(); const manifest = readManifest()
if (version !== manifest.version){ if (version !== manifest.version) {
manifest.version = version; manifest.version = version
manifest.name = `LLOneBot v${version}`; manifest.name = `LLOneBot v${version}`
writeManifest(manifest); writeManifest(manifest)
} }

View File

@@ -1,7 +1,5 @@
import {Peer} from "../ntqqapi/ntcall"; export const CHANNEL_GET_CONFIG = 'llonebot_get_config'
export const CHANNEL_SET_CONFIG = 'llonebot_set_config'
export const CHANNEL_GET_CONFIG = "llonebot_get_config" export const CHANNEL_LOG = 'llonebot_log'
export const CHANNEL_SET_CONFIG = "llonebot_set_config" export const CHANNEL_ERROR = 'llonebot_error'
export const CHANNEL_LOG = "llonebot_log" export const CHANNEL_SELECT_FILE = 'llonebot_select_ffmpeg'
export const CHANNEL_ERROR = "llonebot_error"
export const CHANNEL_SELECT_FILE = "llonebot_select_ffmpeg"

View File

@@ -1,8 +1,8 @@
import fs from "fs"; import fs from "fs";
import {Config, OB11Config} from "./types"; import {Config, OB11Config} from './types';
import {mergeNewProperties} from "./utils"; import {mergeNewProperties} from "./utils";
export const HOOK_LOG= false; export const HOOK_LOG = false;
export class ConfigUtil { export class ConfigUtil {
private readonly configPath: string; private readonly configPath: string;
@@ -12,7 +12,7 @@ export class ConfigUtil {
this.configPath = configPath; this.configPath = configPath;
} }
getConfig(cache=true) { getConfig(cache = true) {
if (this.config && cache) { if (this.config && cache) {
return this.config; return this.config;
} }

View File

@@ -1,40 +1,39 @@
import {NTQQApi} from '../ntqqapi/ntcall'; import {NTQQApi} from '../ntqqapi/ntcall'
import { import {
FileElement, type Friend,
Friend, type FriendRequest,
FriendRequest, type Group,
Group, type GroupMember,
GroupMember, type GroupNotify,
GroupNotify, type RawMessage,
PicElement, PttElement, type SelfInfo
RawMessage, } from '../ntqqapi/types'
SelfInfo, VideoElement import {type FileCache, type LLOneBotError} from './types'
} from "../ntqqapi/types";
import {FileCache, LLOneBotError} from "./types";
export let selfInfo: SelfInfo = {
uid: "",
uin: "",
nick: "",
online: true,
}
export let groups: Group[] = []
export let friends: Friend[] = []
export let msgHistory: Record<string, RawMessage> = {} // msgId: RawMessage
export let groupNotifies: Map<string, GroupNotify> = new Map<string, GroupNotify>();
export let friendRequests: Map<number, FriendRequest> = new Map<number, FriendRequest>();
export let llonebotError: LLOneBotError = {
ffmpegError: "",
otherError: ""
}
let globalMsgId = Math.floor(Date.now() / 1000);
export let fileCache: Map<string, FileCache> = new Map(); export const selfInfo: SelfInfo = {
uid: '',
uin: '',
nick: '',
online: true
}
export const groups: Group[] = []
export const friends: Friend[] = []
export const msgHistory: Record<string, RawMessage> = {} // msgId: RawMessage
export const groupNotifies: Map<string, GroupNotify> = new Map<string, GroupNotify>()
export const friendRequests: Map<number, FriendRequest> = new Map<number, FriendRequest>()
export const llonebotError: LLOneBotError = {
ffmpegError: '',
otherError: ''
}
let globalMsgId = Math.floor(Date.now() / 1000)
export const fileCache = new Map<string, FileCache>()
export function addHistoryMsg(msg: RawMessage): boolean { export function addHistoryMsg(msg: RawMessage): boolean {
let existMsg = msgHistory[msg.msgId] const existMsg = msgHistory[msg.msgId]
if (existMsg) { if (existMsg) {
Object.assign(existMsg, msg) Object.assign(existMsg, msg)
msg.msgShortId = existMsg.msgShortId; msg.msgShortId = existMsg.msgShortId
return false return false
} }
msg.msgShortId = ++globalMsgId msg.msgShortId = ++globalMsgId
@@ -47,9 +46,8 @@ export function getHistoryMsgByShortId(shortId: number | string) {
return Object.values(msgHistory).find(msg => msg.msgShortId.toString() == shortId.toString()) return Object.values(msgHistory).find(msg => msg.msgShortId.toString() == shortId.toString())
} }
export async function getFriend(qq: string): Promise<Friend | undefined> { export async function getFriend(qq: string): Promise<Friend | undefined> {
let friend = friends.find(friend => friend.uin === qq) const friend = friends.find(friend => friend.uin === qq)
// if (!friend){ // if (!friend){
// friends = (await NTQQApi.getFriends(true)) // friends = (await NTQQApi.getFriends(true))
// friend = friends.find(friend => friend.uin === qq) // friend = friends.find(friend => friend.uin === qq)
@@ -58,7 +56,7 @@ export async function getFriend(qq: string): Promise<Friend | undefined> {
} }
export async function getGroup(qq: string): Promise<Group | undefined> { export async function getGroup(qq: string): Promise<Group | undefined> {
let group = groups.find(group => group.groupCode === qq) const group = groups.find(group => group.groupCode === qq)
// if (!group){ // if (!group){
// groups = await NTQQApi.getGroups(true); // groups = await NTQQApi.getGroups(true);
// group = groups.find(group => group.groupCode === qq) // group = groups.find(group => group.groupCode === qq)
@@ -67,9 +65,9 @@ export async function getGroup(qq: string): Promise<Group | undefined> {
} }
export async function getGroupMember(groupQQ: string | number, memberQQ: string | number, memberUid: string = null) { export async function getGroupMember(groupQQ: string | number, memberQQ: string | number, memberUid: string = null) {
groupQQ = groupQQ.toString(); groupQQ = groupQQ.toString()
if (memberQQ){ if (memberQQ) {
memberQQ = memberQQ.toString(); memberQQ = memberQQ.toString()
} }
const group = await getGroup(groupQQ) const group = await getGroup(groupQQ)
if (group) { if (group) {
@@ -82,7 +80,7 @@ export async function getGroupMember(groupQQ: string | number, memberQQ: string
let member = group.members?.find(filterFunc) let member = group.members?.find(filterFunc)
if (!member) { if (!member) {
const _members = await NTQQApi.getGroupMembers(groupQQ) const _members = await NTQQApi.getGroupMembers(groupQQ)
if (_members.length) { if (_members.length > 0) {
group.members = _members group.members = _members
} }
member = group.members?.find(filterFunc) member = group.members?.find(filterFunc)
@@ -91,21 +89,16 @@ export async function getGroupMember(groupQQ: string | number, memberQQ: string
} }
} }
export function getHistoryMsgBySeq(seq: string) { export function getHistoryMsgBySeq(seq: string) {
return Object.values(msgHistory).find(msg => msg.msgSeq === seq) return Object.values(msgHistory).find(msg => msg.msgSeq === seq)
} }
export const uidMaps: Record<string, string> = {} // 一串加密的字符串(uid) -> qq号
export let uidMaps: Record<string, string> = {} // 一串加密的字符串(uid) -> qq号
export function getUidByUin(uin: string) { export function getUidByUin(uin: string) {
for (const key in uidMaps) { for (const key in uidMaps) {
if (uidMaps[key] === uin) { if (uidMaps[key] === uin) {
return key; return key
} }
} }
} }

View File

@@ -1,4 +1,4 @@
import express, {Express, Request, Response, json} from "express"; import express, {Express, json, Request, Response} from "express";
import {getConfigUtil, log} from "../utils"; import {getConfigUtil, log} from "../utils";
import http from "http"; import http from "http";
@@ -45,13 +45,13 @@ export abstract class HttpServerBase {
} }
stop() { stop() {
if (this.server){ if (this.server) {
this.server.close() this.server.close()
this.server = null; this.server = null;
} }
} }
restart(port: number){ restart(port: number) {
this.stop() this.stop()
this.start(port) this.start(port)
} }
@@ -63,20 +63,20 @@ export abstract class HttpServerBase {
url = "/" + url url = "/" + url
} }
if (!this.expressAPP[method]){ if (!this.expressAPP[method]) {
const err = `${this.name} register router failed${method} not exist`; const err = `${this.name} register router failed${method} not exist`;
log(err); log(err);
throw err; throw err;
} }
this.expressAPP[method](url, this.authorize, async (req: Request, res: Response) => { this.expressAPP[method](url, this.authorize, async (req: Request, res: Response) => {
let payload = req.body; let payload = req.body;
if (method == "get"){ if (method == "get") {
payload = req.query payload = req.query
} }
log("收到http请求", url, payload); log("收到http请求", url, payload);
try{ try {
res.send(await handler(res, payload)) res.send(await handler(res, payload))
}catch (e) { } catch (e) {
this.handleFailed(res, payload, e.stack.toString()) this.handleFailed(res, payload, e.stack.toString())
} }
}); });

View File

@@ -15,7 +15,7 @@ class WebsocketClientBase {
} }
} }
onMessage(msg: string){ onMessage(msg: string) {
} }
} }
@@ -29,11 +29,11 @@ export class WebsocketServerBase {
start(port: number) { start(port: number) {
this.ws = new WebSocketServer({port}); this.ws = new WebSocketServer({port});
this.ws.on("connection", (wsClient, req)=>{ this.ws.on("connection", (wsClient, req) => {
const url = req.url.split("?").shift() const url = req.url.split("?").shift()
this.authorize(wsClient, req); this.authorize(wsClient, req);
this.onConnect(wsClient, url, req); this.onConnect(wsClient, url, req);
wsClient.on("message", async (msg)=>{ wsClient.on("message", async (msg) => {
this.onMessage(wsClient, url, msg.toString()) this.onMessage(wsClient, url, msg.toString())
}) })
}) })
@@ -45,7 +45,8 @@ export class WebsocketServerBase {
}); });
this.ws = null; this.ws = null;
} }
restart(port: number){
restart(port: number) {
this.stop(); this.stop();
this.start(port); this.start(port);
} }
@@ -85,7 +86,7 @@ export class WebsocketServerBase {
} }
onMessage(wsClient: WebSocket, url: string, msg: string) { onMessage(wsClient: WebSocket, url: string, msg: string) {
} }

View File

@@ -1,40 +1,37 @@
import {FileElement, PicElement, PttElement, VideoElement} from "../ntqqapi/types";
export interface OB11Config { export interface OB11Config {
httpPort: number httpPort: number
httpHosts: string[] httpHosts: string[]
wsPort: number wsPort: number
wsHosts: string[] wsHosts: string[]
enableHttp?: boolean enableHttp?: boolean
enableHttpPost?: boolean enableHttpPost?: boolean
enableWs?: boolean enableWs?: boolean
enableWsReverse?: boolean enableWsReverse?: boolean
messagePostFormat?: 'array' | 'string' messagePostFormat?: 'array' | 'string'
} }
export interface Config { export interface Config {
ob11: OB11Config ob11: OB11Config
token?: string token?: string
heartInterval?: number // ms heartInterval?: number // ms
enableLocalFile2Url?: boolean // 开启后本地文件路径图片会转成http链接, 语音会转成base64 enableLocalFile2Url?: boolean // 开启后本地文件路径图片会转成http链接, 语音会转成base64
debug?: boolean debug?: boolean
reportSelfMessage?: boolean reportSelfMessage?: boolean
log?: boolean log?: boolean
autoDeleteFile?: boolean autoDeleteFile?: boolean
autoDeleteFileSecond?: number autoDeleteFileSecond?: number
ffmpeg?: string // ffmpeg路径 ffmpeg?: string // ffmpeg路径
} }
export type LLOneBotError = { export interface LLOneBotError {
ffmpegError?: string ffmpegError?: string
otherError?: string otherError?: string
} }
export interface FileCache {
export interface FileCache{ fileName: string
fileName: string, filePath: string
filePath: string, fileSize: string
fileSize: string, url?: string
url?: string, downloadFunc?: () => Promise<void>
downloadFunc?: () => Promise<void>; }
}

View File

@@ -33,7 +33,7 @@ function truncateString(obj: any, maxLength = 500) {
export function log(...msg: any[]) { export function log(...msg: any[]) {
if (!getConfigUtil().getConfig().log) { if (!getConfigUtil().getConfig().log) {
return return //console.log(...msg);
} }
let currentDateTime = new Date().toLocaleString(); let currentDateTime = new Date().toLocaleString();
const date = new Date(); const date = new Date();
@@ -195,9 +195,9 @@ export async function encodeSilk(filePath: string) {
const pttPath = path.join(CONFIG_DIR, uuidv4()); const pttPath = path.join(CONFIG_DIR, uuidv4());
if (getFileHeader(filePath) !== "02232153494c4b") { if (getFileHeader(filePath) !== "02232153494c4b") {
log(`语音文件${filePath}需要转换成silk`) log(`语音文件${filePath}需要转换成silk`)
const isWav = await isWavFile(filePath); const _isWav = await isWavFile(filePath);
const wavPath = pttPath + ".wav" const wavPath = pttPath + ".wav"
if (!isWav) { if (!_isWav) {
log(`语音文件${filePath}正在转换成wav`) log(`语音文件${filePath}正在转换成wav`)
// let voiceData = await fsp.readFile(filePath) // let voiceData = await fsp.readFile(filePath)
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {

14
src/global.d.ts vendored
View File

@@ -1,10 +1,8 @@
import {LLOneBot} from "./preload"; import { type LLOneBot } from './preload'
declare global { declare global {
interface Window { interface Window {
llonebot: LLOneBot; llonebot: LLOneBot
LiteLoader: any; LiteLoader: any
} }
} }

View File

@@ -141,7 +141,7 @@ function onLoad() {
// 检查ffmpeg // 检查ffmpeg
if (arg.ffmpeg) { if (arg.ffmpeg) {
checkFfmpeg(arg.ffmpeg).then(success => { checkFfmpeg(arg.ffmpeg).then(success => {
if (success){ if (success) {
llonebotError.ffmpegError = '' llonebotError.ffmpegError = ''
} }
}) })
@@ -254,8 +254,8 @@ function onLoad() {
continue; continue;
} }
let existNotify = groupNotifies[notify.seq]; let existNotify = groupNotifies[notify.seq];
if (existNotify){ if (existNotify) {
if (Date.now() - existNotify.time < 3000){ if (Date.now() - existNotify.time < 3000) {
continue continue
} }
} }
@@ -298,8 +298,7 @@ function onLoad() {
groupRequestEvent.comment = notify.postscript; groupRequestEvent.comment = notify.postscript;
groupRequestEvent.flag = notify.seq; groupRequestEvent.flag = notify.seq;
postOB11Event(groupRequestEvent); postOB11Event(groupRequestEvent);
} } else if (notify.type == GroupNotifyTypes.INVITE_ME) {
else if(notify.type == GroupNotifyTypes.INVITE_ME){
let groupInviteEvent = new OB11GroupRequestEvent(); let groupInviteEvent = new OB11GroupRequestEvent();
groupInviteEvent.group_id = parseInt(notify.group.groupCode); groupInviteEvent.group_id = parseInt(notify.group.groupCode);
let user_id = (await NTQQApi.getUserDetailInfo(notify.user2.uid))?.uin let user_id = (await NTQQApi.getUserDetailInfo(notify.user2.uid))?.uin
@@ -368,16 +367,18 @@ function onLoad() {
let getSelfNickCount = 0; let getSelfNickCount = 0;
const init = async () => { const init = async () => {
try { try {
log("start get self info")
const _ = await NTQQApi.getSelfInfo(); const _ = await NTQQApi.getSelfInfo();
log("get self info api result:", _);
Object.assign(selfInfo, _); Object.assign(selfInfo, _);
selfInfo.nick = selfInfo.uin; selfInfo.nick = selfInfo.uin;
log("get self simple info", _);
} catch (e) { } catch (e) {
log("retry get self info"); log("retry get self info", e);
} }
log("self info", selfInfo);
if (selfInfo.uin) { if (selfInfo.uin) {
try { try {
const userInfo = (await NTQQApi.getUserInfo(selfInfo.uid)); const userInfo = (await NTQQApi.getUserDetailInfo(selfInfo.uid));
log("self info", userInfo); log("self info", userInfo);
if (userInfo) { if (userInfo) {
selfInfo.nick = userInfo.nick; selfInfo.nick = userInfo.nick;

View File

@@ -1,71 +1,71 @@
import {BrowserWindow} from 'electron'; import {type BrowserWindow} from 'electron'
import {getConfigUtil, log, sleep} from "../common/utils"; import {getConfigUtil, log, sleep} from '../common/utils'
import {NTQQApi, NTQQApiClass, sendMessagePool} from "./ntcall"; import {NTQQApi, type NTQQApiClass, sendMessagePool} from './ntcall'
import {Group, RawMessage, User} from "./types"; import {type Group, type RawMessage, type User} from './types'
import {addHistoryMsg, friends, groups, msgHistory, selfInfo} from "../common/data"; import {addHistoryMsg, friends, groups, msgHistory, selfInfo} from '../common/data'
import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent"; import {OB11GroupDecreaseEvent} from '../onebot11/event/notice/OB11GroupDecreaseEvent'
import {OB11GroupIncreaseEvent} from "../onebot11/event/notice/OB11GroupIncreaseEvent"; import {OB11GroupIncreaseEvent} from '../onebot11/event/notice/OB11GroupIncreaseEvent'
import {v4 as uuidv4} from "uuid" import {v4 as uuidv4} from 'uuid'
import {postOB11Event} from "../onebot11/server/postOB11Event"; import {postOB11Event} from '../onebot11/server/postOB11Event'
import {HOOK_LOG} from "../common/config"; import {HOOK_LOG} from '../common/config'
import fs from "fs"; import fs from 'fs'
export let hookApiCallbacks: Record<string, (apiReturn: any) => void> = {} export const hookApiCallbacks: Record<string, (apiReturn: any) => void> = {}
export enum ReceiveCmd { export enum ReceiveCmd {
UPDATE_MSG = "nodeIKernelMsgListener/onMsgInfoListUpdate", UPDATE_MSG = 'nodeIKernelMsgListener/onMsgInfoListUpdate',
NEW_MSG = "nodeIKernelMsgListener/onRecvMsg", NEW_MSG = 'nodeIKernelMsgListener/onRecvMsg',
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_UNIX = "onGroupListUpdate", GROUPS_UNIX = 'onGroupListUpdate',
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',
} }
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:
{ Array<{
cmdName: ReceiveCmd, cmdName: ReceiveCmd
cmdType: "event", cmdType: 'event'
payload: PayloadType payload: PayloadType
}[] }>
} }
let receiveHooks: Array<{ const 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) { export function hookNTQQApiReceive(window: BrowserWindow) {
const originalSend = window.webContents.send; const originalSend = window.webContents.send
const patchSend = (channel: string, ...args: NTQQApiReturnData) => { const patchSend = (channel: string, ...args: NTQQApiReturnData) => {
HOOK_LOG && 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) { if (args?.[1] instanceof Array) {
for (let receiveData of args?.[1]) { for (const receiveData of args?.[1]) {
const ntQQApiMethodName = receiveData.cmdName; const ntQQApiMethodName = receiveData.cmdName
// log(`received ntqq api message: ${channel} ${ntQQApiMethodName}`, JSON.stringify(receiveData)) // log(`received ntqq api message: ${channel} ${ntQQApiMethodName}`, JSON.stringify(receiveData))
for (let hook of receiveHooks) { for (const hook of receiveHooks) {
if (hook.method === ntQQApiMethodName) { if (hook.method === ntQQApiMethodName) {
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
try { try {
let _ = hook.hookFunc(receiveData.payload) const _ = hook.hookFunc(receiveData.payload)
if (hook.hookFunc.constructor.name === "AsyncFunction") { if (hook.hookFunc.constructor.name === 'AsyncFunction') {
(_ as Promise<void>).then() (_ as Promise<void>).then()
} }
} catch (e) { } catch (e) {
log("hook error", e, receiveData.payload) log('hook error', e, receiveData.payload)
} }
}).then() }).then()
} }
@@ -74,35 +74,35 @@ export function hookNTQQApiReceive(window: BrowserWindow) {
} }
if (args[0]?.callbackId) { if (args[0]?.callbackId) {
// log("hookApiCallback", hookApiCallbacks, args) // log("hookApiCallback", hookApiCallbacks, args)
const callbackId = args[0].callbackId; const callbackId = args[0].callbackId
if (hookApiCallbacks[callbackId]) { if (hookApiCallbacks[callbackId]) {
// log("callback found") // log("callback found")
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
hookApiCallbacks[callbackId](args[1]); hookApiCallbacks[callbackId](args[1])
}).then() }).then()
delete hookApiCallbacks[callbackId]; delete hookApiCallbacks[callbackId]
} }
} }
return originalSend.call(window.webContents, channel, ...args); return originalSend.call(window.webContents, channel, ...args)
} }
window.webContents.send = patchSend; window.webContents.send = patchSend
} }
export function hookNTQQApiCall(window: BrowserWindow) { export function hookNTQQApiCall(window: BrowserWindow) {
// 监听调用NTQQApi // 监听调用NTQQApi
let webContents = window.webContents as any; const 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) {
HOOK_LOG && log("call NTQQ api", thisArg, args); HOOK_LOG && log('call NTQQ api', thisArg, args)
return target.apply(thisArg, args); return target.apply(thisArg, args)
}, }
}); })
if (webContents._events["-ipc-message"]?.[0]) { if (webContents._events['-ipc-message']?.[0]) {
webContents._events["-ipc-message"][0] = proxyIpcMsg; webContents._events['-ipc-message'][0] = proxyIpcMsg
} else { } else {
webContents._events["-ipc-message"] = proxyIpcMsg; webContents._events['-ipc-message'] = proxyIpcMsg
} }
} }
@@ -113,29 +113,29 @@ export function registerReceiveHook<PayloadType>(method: ReceiveCmd, hookFunc: (
hookFunc, hookFunc,
id id
}) })
return id; return id
} }
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)
} }
async function updateGroups(_groups: Group[], needUpdate: boolean = true) { async function updateGroups(_groups: Group[], needUpdate: boolean = true) {
for (let group of _groups) { for (const group of _groups) {
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) { if (needUpdate) {
const members = await NTQQApi.getGroupMembers(group.groupCode); const members = await NTQQApi.getGroupMembers(group.groupCode)
if (members) { if (members) {
existGroup.members = members; existGroup.members = members
} }
} }
} }
@@ -143,84 +143,83 @@ async function updateGroups(_groups: Group[], needUpdate: boolean = true) {
async function processGroupEvent(payload) { async function processGroupEvent(payload) {
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); const existGroup = groups.find(g => g.groupCode == group.groupCode)
if (existGroup) { if (existGroup) {
if (existGroup.memberCount > group.memberCount) { if (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 NTQQApi.getGroupMembers(group.groupCode); const newMembers = await NTQQApi.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)
} }
for (const member of oldMembers) { for (const member of oldMembers) {
if (!newMembersSet.has(member.uin)) { if (!newMembersSet.has(member.uin)) {
postOB11Event(new OB11GroupDecreaseEvent(group.groupCode, parseInt(member.uin))); postOB11Event(new OB11GroupDecreaseEvent(group.groupCode, parseInt(member.uin)))
break; break
} }
} }
} else if (existGroup.memberCount < group.memberCount) { } else if (existGroup.memberCount < group.memberCount) {
const oldMembers = existGroup.members; const oldMembers = existGroup.members
const oldMembersSet = new Set<string>(); const oldMembersSet = new Set<string>()
for (const member of oldMembers) { for (const member of oldMembers) {
oldMembersSet.add(member.uin); oldMembersSet.add(member.uin)
} }
await sleep(200); await sleep(200)
const newMembers = await NTQQApi.getGroupMembers(group.groupCode); const newMembers = await NTQQApi.getGroupMembers(group.groupCode)
group.members = newMembers; group.members = newMembers
for (const member of newMembers) { for (const member of newMembers) {
if (!oldMembersSet.has(member.uin)) { if (!oldMembersSet.has(member.uin)) {
postOB11Event(new OB11GroupIncreaseEvent(group.groupCode, parseInt(member.uin))); postOB11Event(new OB11GroupIncreaseEvent(group.groupCode, parseInt(member.uin)))
break; break
} }
} }
} }
} }
} }
updateGroups(newGroupList, false).then(); updateGroups(newGroupList, false).then()
} catch (e) { } catch (e) {
updateGroups(payload.groupList).then(); updateGroups(payload.groupList).then()
console.log(e); console.log(e)
} }
} }
registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmd.GROUPS, (payload) => { registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmd.GROUPS, (payload) => {
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 }>(ReceiveCmd.GROUPS_UNIX, (payload) => { registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmd.GROUPS_UNIX, (payload) => {
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<{
data: { categoryId: number, categroyName: string, categroyMbCount: number, buddyList: User[] }[] data: Array<{ categoryId: number, categroyName: string, categroyMbCount: number, buddyList: User[] }>
}>(ReceiveCmd.FRIENDS, payload => { }>(ReceiveCmd.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 (const friend of _friends) {
let existFriend = friends.find(f => f.uin == friend.uin) const existFriend = friends.find(f => f.uin == friend.uin)
if (!existFriend) { if (!existFriend) {
friends.push(friend) friends.push(friend)
} else { } else {
@@ -230,8 +229,8 @@ registerReceiveHook<{
} }
}) })
registerReceiveHook<{ msgList: Array<RawMessage> }>(ReceiveCmd.NEW_MSG, (payload) => { registerReceiveHook<{ msgList: RawMessage[] }>(ReceiveCmd.NEW_MSG, (payload) => {
const {autoDeleteFile, autoDeleteFileSecond} = getConfigUtil().getConfig(); const {autoDeleteFile, autoDeleteFileSecond} = getConfigUtil().getConfig()
for (const message of payload.msgList) { for (const message of payload.msgList) {
// log("收到新消息push到历史记录", message) // log("收到新消息push到历史记录", message)
addHistoryMsg(message) addHistoryMsg(message)
@@ -241,43 +240,43 @@ registerReceiveHook<{ msgList: Array<RawMessage> }>(ReceiveCmd.NEW_MSG, (payload
} }
for (const msgElement of message.elements) { for (const msgElement of message.elements) {
setTimeout(() => { setTimeout(() => {
const picPath = msgElement.picElement?.sourcePath; const picPath = msgElement.picElement?.sourcePath
const pttPath = msgElement.pttElement?.filePath; const pttPath = msgElement.pttElement?.filePath
const pathList = [picPath, pttPath]; const pathList = [picPath, pttPath]
if (msgElement.picElement){ if (msgElement.picElement) {
pathList.push(...Object.values(msgElement.picElement.thumbPath)); pathList.push(...Object.values(msgElement.picElement.thumbPath))
} }
// log("需要清理的文件", pathList); // log("需要清理的文件", pathList);
for (const path of pathList) { for (const path of pathList) {
if (path) { if (path) {
fs.unlink(picPath, () => { fs.unlink(picPath, () => {
log("删除文件成功", path) log('删除文件成功', path)
}); })
} }
} }
}, autoDeleteFileSecond * 1000) }, autoDeleteFileSecond * 1000)
} }
} }
const msgIds = Object.keys(msgHistory); const msgIds = Object.keys(msgHistory)
if (msgIds.length > 30000) { if (msgIds.length > 30000) {
delete msgHistory[msgIds.sort()[0]] delete msgHistory[msgIds.sort()[0]]
} }
}) })
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmd.SELF_SEND_MSG, ({msgRecord}) => { registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmd.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);
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}}>(ReceiveCmd.SELF_STATUS, (info)=>{ registerReceiveHook<{ info: { status: number } }>(ReceiveCmd.SELF_STATUS, (info) => {
selfInfo.online = info.info.status !== 20; selfInfo.online = info.info.status !== 20
}) })

View File

@@ -1,26 +1,25 @@
import {ipcMain} from "electron"; import {ipcMain} from 'electron'
import {hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook} from "./hook"; import {hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook} from './hook'
import {log, sleep} from "../common/utils"; import {log, sleep} from '../common/utils'
import { import {
ChatType, type ChatType,
ElementType, ElementType,
Friend, type Friend,
FriendRequest, type FriendRequest,
Group, type Group, GroupMember,
GroupMember, type GroupMemberRole,
GroupMemberRole, type GroupNotifies,
GroupNotifies, type GroupNotify,
GroupNotify, type GroupRequestOperateTypes,
GroupRequestOperateTypes, type RawMessage,
RawMessage, type SelfInfo,
SelfInfo, type SendMessageElement,
SendMessageElement, type User
User } from './types'
} from "./types"; import * as fs from 'node:fs'
import * as fs from "node:fs"; import {addHistoryMsg, friendRequests, groupNotifies, msgHistory, selfInfo} from '../common/data'
import {addHistoryMsg, friendRequests, groupNotifies, msgHistory, selfInfo} from "../common/data"; import {v4 as uuidv4} from 'uuid'
import {v4 as uuidv4} from "uuid" import path from 'path'
import path from "path";
interface IPCReceiveEvent { interface IPCReceiveEvent {
eventName: string eventName: string
@@ -35,88 +34,88 @@ export type IPCReceiveDetail = [
] ]
export enum NTQQApiClass { export enum NTQQApiClass {
NT_API = "ns-ntApi", NT_API = 'ns-ntApi',
FS_API = "ns-FsApi", FS_API = 'ns-FsApi',
GLOBAL_DATA = "ns-GlobalDataApi" GLOBAL_DATA = 'ns-GlobalDataApi'
} }
export enum NTQQApiMethod { export enum NTQQApiMethod {
LIKE_FRIEND = "nodeIKernelProfileLikeService/setBuddyProfileLike", LIKE_FRIEND = 'nodeIKernelProfileLikeService/setBuddyProfileLike',
SELF_INFO = "fetchAuthData", SELF_INFO = 'fetchAuthData',
FRIENDS = "nodeIKernelBuddyService/getBuddyList", FRIENDS = 'nodeIKernelBuddyService/getBuddyList',
GROUPS = "nodeIKernelGroupService/getGroupList", GROUPS = 'nodeIKernelGroupService/getGroupList',
GROUP_MEMBER_SCENE = "nodeIKernelGroupService/createMemberListScene", GROUP_MEMBER_SCENE = 'nodeIKernelGroupService/createMemberListScene',
GROUP_MEMBERS = "nodeIKernelGroupService/getNextMemberList", GROUP_MEMBERS = 'nodeIKernelGroupService/getNextMemberList',
USER_INFO = "nodeIKernelProfileService/getUserSimpleInfo", USER_INFO = 'nodeIKernelProfileService/getUserSimpleInfo',
USER_DETAIL_INFO = "nodeIKernelProfileService/getUserDetailInfo", USER_DETAIL_INFO = 'nodeIKernelProfileService/getUserDetailInfo',
FILE_TYPE = "getFileType", FILE_TYPE = 'getFileType',
FILE_MD5 = "getFileMd5", FILE_MD5 = 'getFileMd5',
FILE_COPY = "copyFile", FILE_COPY = 'copyFile',
IMAGE_SIZE = "getImageSizeFromPath", IMAGE_SIZE = 'getImageSizeFromPath',
FILE_SIZE = "getFileSize", FILE_SIZE = 'getFileSize',
MEDIA_FILE_PATH = "nodeIKernelMsgService/getRichMediaFilePathForGuild", MEDIA_FILE_PATH = 'nodeIKernelMsgService/getRichMediaFilePathForGuild',
RECALL_MSG = "nodeIKernelMsgService/recallMsg", RECALL_MSG = 'nodeIKernelMsgService/recallMsg',
SEND_MSG = "nodeIKernelMsgService/sendMsg", SEND_MSG = 'nodeIKernelMsgService/sendMsg',
DOWNLOAD_MEDIA = "nodeIKernelMsgService/downloadRichMedia", DOWNLOAD_MEDIA = 'nodeIKernelMsgService/downloadRichMedia',
MULTI_FORWARD_MSG = "nodeIKernelMsgService/multiForwardMsgWithComment", // 合并转发 MULTI_FORWARD_MSG = 'nodeIKernelMsgService/multiForwardMsgWithComment', // 合并转发
GET_GROUP_NOTICE = "nodeIKernelGroupService/getSingleScreenNotifies", GET_GROUP_NOTICE = 'nodeIKernelGroupService/getSingleScreenNotifies',
HANDLE_GROUP_REQUEST = "nodeIKernelGroupService/operateSysNotify", HANDLE_GROUP_REQUEST = 'nodeIKernelGroupService/operateSysNotify',
QUIT_GROUP = "nodeIKernelGroupService/quitGroup", QUIT_GROUP = 'nodeIKernelGroupService/quitGroup',
// READ_FRIEND_REQUEST = "nodeIKernelBuddyListener/onDoubtBuddyReqUnreadNumChange" // READ_FRIEND_REQUEST = "nodeIKernelBuddyListener/onDoubtBuddyReqUnreadNumChange"
HANDLE_FRIEND_REQUEST = "nodeIKernelBuddyService/approvalFriendRequest", HANDLE_FRIEND_REQUEST = 'nodeIKernelBuddyService/approvalFriendRequest',
KICK_MEMBER = "nodeIKernelGroupService/kickMember", KICK_MEMBER = 'nodeIKernelGroupService/kickMember',
MUTE_MEMBER = "nodeIKernelGroupService/setMemberShutUp", MUTE_MEMBER = 'nodeIKernelGroupService/setMemberShutUp',
MUTE_GROUP = "nodeIKernelGroupService/setGroupShutUp", MUTE_GROUP = 'nodeIKernelGroupService/setGroupShutUp',
SET_MEMBER_CARD = "nodeIKernelGroupService/modifyMemberCardName", SET_MEMBER_CARD = 'nodeIKernelGroupService/modifyMemberCardName',
SET_MEMBER_ROLE = "nodeIKernelGroupService/modifyMemberRole", SET_MEMBER_ROLE = 'nodeIKernelGroupService/modifyMemberRole',
PUBLISH_GROUP_BULLETIN = "nodeIKernelGroupService/publishGroupBulletinBulletin", PUBLISH_GROUP_BULLETIN = 'nodeIKernelGroupService/publishGroupBulletinBulletin',
SET_GROUP_NAME = "nodeIKernelGroupService/modifyGroupName", SET_GROUP_NAME = 'nodeIKernelGroupService/modifyGroupName',
} }
enum NTQQApiChannel { enum NTQQApiChannel {
IPC_UP_2 = "IPC_UP_2", IPC_UP_2 = 'IPC_UP_2',
IPC_UP_3 = "IPC_UP_3", IPC_UP_3 = 'IPC_UP_3',
IPC_UP_1 = "IPC_UP_1", IPC_UP_1 = 'IPC_UP_1',
} }
export interface Peer { export interface Peer {
chatType: ChatType chatType: ChatType
peerUid: string // 如果是群聊uid为群号私聊uid就是加密的字符串 peerUid: string // 如果是群聊uid为群号私聊uid就是加密的字符串
guildId?: "" guildId?: ''
} }
interface NTQQApiParams { interface NTQQApiParams {
methodName: NTQQApiMethod | string, methodName: NTQQApiMethod | string
className?: NTQQApiClass, className?: NTQQApiClass
channel?: NTQQApiChannel, channel?: NTQQApiChannel
classNameIsRegister?: boolean classNameIsRegister?: boolean
args?: unknown[], args?: unknown[]
cbCmd?: ReceiveCmd | null, cbCmd?: ReceiveCmd | null
cmdCB?: (payload: any) => boolean; cmdCB?: (payload: any) => boolean
afterFirstCmd?: boolean, // 是否在methodName调用完之后再去hook cbCmd afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd
timeoutSecond?: number, timeoutSecond?: number
} }
function callNTQQApi<ReturnType>(params: NTQQApiParams) { async function callNTQQApi<ReturnType>(params: NTQQApiParams) {
let { let {
className, methodName, channel, args, className, methodName, channel, args,
cbCmd, timeoutSecond: timeout, cbCmd, timeoutSecond: timeout,
classNameIsRegister, cmdCB, afterFirstCmd classNameIsRegister, cmdCB, afterFirstCmd
} = params; } = params
className = className ?? NTQQApiClass.NT_API; className = className ?? NTQQApiClass.NT_API
channel = channel ?? NTQQApiChannel.IPC_UP_2; channel = channel ?? NTQQApiChannel.IPC_UP_2
args = args ?? []; args = args ?? []
timeout = timeout ?? 5; timeout = timeout ?? 5
afterFirstCmd = afterFirstCmd ?? true; afterFirstCmd = afterFirstCmd ?? true
const uuid = uuidv4(); const uuid = uuidv4()
// log("callNTQQApi", channel, className, methodName, args, uuid) // log("callNTQQApi", channel, className, methodName, args, uuid)
return new Promise((resolve: (data: ReturnType) => void, reject) => { return await new Promise((resolve: (data: ReturnType) => void, reject) => {
// log("callNTQQApiPromise", channel, className, methodName, args, uuid) // log("callNTQQApiPromise", channel, className, methodName, args, uuid)
const _timeout = timeout * 1000 const _timeout = timeout * 1000
let success = false let success = false
let eventName = className + "-" + channel[channel.length - 1]; let eventName = className + '-' + channel[channel.length - 1]
if (classNameIsRegister) { if (classNameIsRegister) {
eventName += "-register"; eventName += '-register'
} }
const apiArgs = [methodName, ...args] const apiArgs = [methodName, ...args]
if (!cbCmd) { if (!cbCmd) {
@@ -124,40 +123,40 @@ function callNTQQApi<ReturnType>(params: NTQQApiParams) {
hookApiCallbacks[uuid] = (r: ReturnType) => { hookApiCallbacks[uuid] = (r: ReturnType) => {
success = true success = true
resolve(r) resolve(r)
}; }
} else { } else {
// 这里的callback比较特殊QQ后端先返回是否调用成功再返回一条结果数据 // 这里的callback比较特殊QQ后端先返回是否调用成功再返回一条结果数据
const secondCallback = () => { const secondCallback = () => {
const hookId = registerReceiveHook<ReturnType>(cbCmd, (payload) => { const hookId = registerReceiveHook<ReturnType>(cbCmd, (payload) => {
// log(methodName, "second callback", cbCmd, payload, cmdCB); // log(methodName, "second callback", cbCmd, payload, cmdCB);
if (!!cmdCB) { if (cmdCB) {
if (cmdCB(payload)) { if (cmdCB(payload)) {
removeReceiveHook(hookId); removeReceiveHook(hookId)
success = true success = true
resolve(payload); resolve(payload)
} }
} else { } else {
removeReceiveHook(hookId); removeReceiveHook(hookId)
success = true success = true
resolve(payload); resolve(payload)
} }
}) })
} }
!afterFirstCmd && secondCallback(); !afterFirstCmd && secondCallback()
hookApiCallbacks[uuid] = (result: GeneralCallResult) => { hookApiCallbacks[uuid] = (result: GeneralCallResult) => {
log(`${methodName} callback`, result) log(`${methodName} callback`, result)
if (result?.result == 0 || result === undefined) { if (result?.result == 0 || result === undefined) {
afterFirstCmd && secondCallback(); afterFirstCmd && secondCallback()
} else { } else {
success = true success = true
reject(`ntqq api call failed, ${result.errMsg}`); reject(`ntqq api call failed, ${result.errMsg}`)
} }
} }
} }
setTimeout(() => { setTimeout(() => {
// log("ntqq api timeout", success, channel, className, methodName) // log("ntqq api timeout", success, channel, className, methodName)
if (!success) { if (!success) {
log(`ntqq api timeout ${channel}, ${eventName}, ${methodName}`, apiArgs); log(`ntqq api timeout ${channel}, ${eventName}, ${methodName}`, apiArgs)
reject(`ntqq api timeout ${channel}, ${eventName}, ${methodName}, ${apiArgs}`) reject(`ntqq api timeout ${channel}, ${eventName}, ${methodName}, ${apiArgs}`)
} }
}, _timeout) }, _timeout)
@@ -171,19 +170,17 @@ function callNTQQApi<ReturnType>(params: NTQQApiParams) {
}) })
} }
export const sendMessagePool: Record<string, ((sendSuccessMsg: RawMessage) => void) | null> = {}// peerUid: callbackFunnc
export let sendMessagePool: Record<string, ((sendSuccessMsg: RawMessage) => void) | null> = {}// peerUid: callbackFunnc
interface GeneralCallResult { interface GeneralCallResult {
result: number, // 0: success result: number // 0: success
errMsg: string errMsg: string
} }
export class NTQQApi { export class NTQQApi {
// static likeFriend = defineNTQQApi<void>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.LIKE_FRIEND) // static likeFriend = defineNTQQApi<void>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.LIKE_FRIEND)
static likeFriend(uid: string, count = 1) { static async likeFriend(uid: string, count = 1) {
return callNTQQApi<GeneralCallResult>({ return await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.LIKE_FRIEND, methodName: NTQQApiMethod.LIKE_FRIEND,
args: [{ args: [{
doLikeUserInfo: { doLikeUserInfo: {
@@ -196,10 +193,12 @@ export class NTQQApi {
}) })
} }
static getSelfInfo() { static async getSelfInfo() {
return callNTQQApi<SelfInfo>({ return await callNTQQApi<SelfInfo>({
className: NTQQApiClass.GLOBAL_DATA, className: NTQQApiClass.GLOBAL_DATA,
methodName: NTQQApiMethod.SELF_INFO, timeoutSecond: 2 // channel: NTQQApiChannel.IPC_UP_3,
methodName: NTQQApiMethod.SELF_INFO,
timeoutSecond: 2
}) })
} }
@@ -234,19 +233,19 @@ export class NTQQApi {
static async getFriends(forced = false) { static async getFriends(forced = false) {
const data = await callNTQQApi<{ const data = await callNTQQApi<{
data: { data: Array<{
categoryId: number, categoryId: number
categroyName: string, categroyName: string
categroyMbCount: number, categroyMbCount: number
buddyList: Friend[] buddyList: Friend[]
}[] }>
}>( }>(
{ {
methodName: NTQQApiMethod.FRIENDS, methodName: NTQQApiMethod.FRIENDS,
args: [{force_update: forced}, undefined], args: [{force_update: forced}, undefined],
cbCmd: ReceiveCmd.FRIENDS cbCmd: ReceiveCmd.FRIENDS
}) })
let _friends: Friend[] = []; const _friends: Friend[] = []
for (const fData of data.data) { for (const fData of data.data) {
_friends.push(...fData.buddyList) _friends.push(...fData.buddyList)
} }
@@ -255,22 +254,22 @@ export class NTQQApi {
static async getGroups(forced = false) { static async getGroups(forced = false) {
let cbCmd = ReceiveCmd.GROUPS let cbCmd = ReceiveCmd.GROUPS
if (process.platform != "win32") { if (process.platform != 'win32') {
cbCmd = ReceiveCmd.GROUPS_UNIX cbCmd = ReceiveCmd.GROUPS_UNIX
} }
const result = await callNTQQApi<{ const result = await callNTQQApi<{
updateType: number, updateType: number
groupList: Group[] groupList: Group[]
}>({methodName: NTQQApiMethod.GROUPS, args: [{force_update: forced}, undefined], cbCmd}) }>({methodName: NTQQApiMethod.GROUPS, args: [{force_update: forced}, undefined], cbCmd})
return result.groupList return result.groupList
} }
static async getGroupMembers(groupQQ: string, num = 3000) { static async getGroupMembers(groupQQ: string, num = 3000): Promise<GroupMember[]> {
const sceneId = await callNTQQApi({ const sceneId = await callNTQQApi({
methodName: NTQQApiMethod.GROUP_MEMBER_SCENE, methodName: NTQQApiMethod.GROUP_MEMBER_SCENE,
args: [{ args: [{
groupCode: groupQQ, groupCode: groupQQ,
scene: "groupMemberList_MainWindow" scene: 'groupMemberList_MainWindow'
}] }]
}) })
// log("get group member sceneId", sceneId); // log("get group member sceneId", sceneId);
@@ -280,16 +279,16 @@ export class NTQQApi {
}>({ }>({
methodName: NTQQApiMethod.GROUP_MEMBERS, methodName: NTQQApiMethod.GROUP_MEMBERS,
args: [{ args: [{
sceneId: sceneId, sceneId,
num: num num
}, },
null null
] ]
}) })
// log("members info", typeof result.result.infos, Object.keys(result.result.infos)) // log("members info", typeof result.result.infos, Object.keys(result.result.infos))
let values = result.result.infos.values() const values = result.result.infos.values()
let members = Array.from(values) as GroupMember[] const members: GroupMember[] = Array.from(values)
for (const member of members) { for (const member of members) {
// uidMaps[member.uid] = member.uin; // uidMaps[member.uid] = member.uin;
} }
@@ -303,73 +302,74 @@ export class NTQQApi {
} }
} }
static async getFileType(filePath: string) {
static getFileType(filePath: string) { return await callNTQQApi<{ ext: string }>({
return callNTQQApi<{ ext: string }>({
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_TYPE, args: [filePath] className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_TYPE, args: [filePath]
}) })
} }
static getFileMd5(filePath: string) { static async getFileMd5(filePath: string) {
return callNTQQApi<string>({ return await callNTQQApi<string>({
className: NTQQApiClass.FS_API, className: NTQQApiClass.FS_API,
methodName: NTQQApiMethod.FILE_MD5, methodName: NTQQApiMethod.FILE_MD5,
args: [filePath] args: [filePath]
}) })
} }
static copyFile(filePath: string, destPath: string) { static async copyFile(filePath: string, destPath: string) {
return callNTQQApi<string>({ return await callNTQQApi<string>({
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_COPY, args: [{ className: NTQQApiClass.FS_API,
methodName: NTQQApiMethod.FILE_COPY,
args: [{
fromPath: filePath, fromPath: filePath,
toPath: destPath toPath: destPath
}] }]
}) })
} }
static getImageSize(filePath: string) { static async getImageSize(filePath: string) {
return callNTQQApi<{ width: number, height: number }>({ return await callNTQQApi<{ width: number, height: number }>({
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.IMAGE_SIZE, args: [filePath] className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.IMAGE_SIZE, args: [filePath]
}) })
} }
static getFileSize(filePath: string) { static async getFileSize(filePath: string) {
return callNTQQApi<number>({ return await callNTQQApi<number>({
className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_SIZE, args: [filePath] className: NTQQApiClass.FS_API, methodName: NTQQApiMethod.FILE_SIZE, args: [filePath]
}) })
} }
// 上传文件到QQ的文件夹 // 上传文件到QQ的文件夹
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC) { static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC) {
const md5 = await NTQQApi.getFileMd5(filePath); const md5 = await NTQQApi.getFileMd5(filePath)
let ext = (await NTQQApi.getFileType(filePath))?.ext let ext = (await NTQQApi.getFileType(filePath))?.ext
if (ext) { if (ext) {
ext = "." + ext ext = '.' + ext
} else { } else {
ext = "" ext = ''
} }
let fileName = `${path.basename(filePath)}`; let fileName = `${path.basename(filePath)}`
if (fileName.indexOf(".") === -1) { if (!fileName.includes('.')) {
fileName += ext; fileName += ext
} }
const mediaPath = await callNTQQApi<string>({ const mediaPath = await callNTQQApi<string>({
methodName: NTQQApiMethod.MEDIA_FILE_PATH, methodName: NTQQApiMethod.MEDIA_FILE_PATH,
args: [{ args: [{
path_info: { path_info: {
md5HexStr: md5, md5HexStr: md5,
fileName: fileName, fileName,
elementType: elementType, elementType,
elementSubType: 0, elementSubType: 0,
thumbSize: 0, thumbSize: 0,
needCreate: true, needCreate: true,
downloadType: 1, downloadType: 1,
file_uuid: "" file_uuid: ''
} }
}] }]
}) })
log("media path", mediaPath) log('media path', mediaPath)
await NTQQApi.copyFile(filePath, mediaPath); await NTQQApi.copyFile(filePath, mediaPath)
const fileSize = await NTQQApi.getFileSize(filePath); const fileSize = await NTQQApi.getFileSize(filePath)
return { return {
md5, md5,
fileName, fileName,
@@ -386,16 +386,16 @@ export class NTQQApi {
const apiParams = [ const apiParams = [
{ {
getReq: { getReq: {
msgId: msgId, msgId,
chatType: chatType, chatType,
peerUid: peerUid, peerUid,
elementId: elementId, elementId,
thumbSize: 0, thumbSize: 0,
downloadType: 1, downloadType: 1,
filePath: thumbPath, filePath: thumbPath
}, }
}, },
undefined, undefined
] ]
// log("需要下载media", sourcePath); // log("需要下载media", sourcePath);
await callNTQQApi({ await callNTQQApi({
@@ -404,15 +404,16 @@ export class NTQQApi {
cbCmd: ReceiveCmd.MEDIA_DOWNLOAD_COMPLETE, cbCmd: ReceiveCmd.MEDIA_DOWNLOAD_COMPLETE,
cmdCB: (payload: { notifyInfo: { filePath: string } }) => { cmdCB: (payload: { notifyInfo: { filePath: string } }) => {
// log("media 下载完成判断", payload.notifyInfo.filePath, sourcePath); // log("media 下载完成判断", payload.notifyInfo.filePath, sourcePath);
return payload.notifyInfo.filePath == sourcePath; return payload.notifyInfo.filePath == sourcePath
} }
}) })
return sourcePath return sourcePath
} }
static recallMsg(peer: Peer, msgIds: string[]) { static async recallMsg(peer: Peer, msgIds: string[]) {
return callNTQQApi({ return await callNTQQApi({
methodName: NTQQApiMethod.RECALL_MSG, args: [{ methodName: NTQQApiMethod.RECALL_MSG,
args: [{
peer, peer,
msgIds msgIds
}, null] }, null]
@@ -420,43 +421,43 @@ export class NTQQApi {
} }
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = false, timeout = 10000) { static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = false, timeout = 10000) {
const peerUid = peer.peerUid; const peerUid = peer.peerUid
// 等待上一个相同的peer发送完 // 等待上一个相同的peer发送完
let checkLastSendUsingTime = 0; let checkLastSendUsingTime = 0
const waitLastSend = async () => { const waitLastSend = async () => {
if (checkLastSendUsingTime > timeout) { if (checkLastSendUsingTime > timeout) {
throw ("发送超时") throw ('发送超时')
} }
let lastSending = sendMessagePool[peer.peerUid] const lastSending = sendMessagePool[peer.peerUid]
if (lastSending) { if (lastSending) {
// log("有正在发送的消息,等待中...") // log("有正在发送的消息,等待中...")
await sleep(500); await sleep(500)
checkLastSendUsingTime += 500; checkLastSendUsingTime += 500
return await waitLastSend(); return await waitLastSend()
} else { } else {
return;
} }
} }
await waitLastSend(); await waitLastSend()
let sentMessage: RawMessage = null; let sentMessage: RawMessage = null
sendMessagePool[peerUid] = async (rawMessage: RawMessage) => { sendMessagePool[peerUid] = async (rawMessage: RawMessage) => {
delete sendMessagePool[peerUid]; delete sendMessagePool[peerUid]
sentMessage = rawMessage; sentMessage = rawMessage
} }
let checkSendCompleteUsingTime = 0; let checkSendCompleteUsingTime = 0
const checkSendComplete = async (): Promise<RawMessage> => { const checkSendComplete = async (): Promise<RawMessage> => {
if (sentMessage && msgHistory[sentMessage.msgId]?.sendStatus == 2) { if (sentMessage && msgHistory[sentMessage.msgId]?.sendStatus == 2) {
// log(`给${peerUid}发送消息成功`) // log(`给${peerUid}发送消息成功`)
return sentMessage; return sentMessage
} else { } else {
checkSendCompleteUsingTime += 500; checkSendCompleteUsingTime += 500
if (checkSendCompleteUsingTime > timeout) { if (checkSendCompleteUsingTime > timeout) {
throw ("发送超时") throw ('发送超时')
} }
await sleep(500); await sleep(500)
return await checkSendComplete() return await checkSendComplete()
} }
} }
@@ -464,16 +465,17 @@ export class NTQQApi {
callNTQQApi({ callNTQQApi({
methodName: NTQQApiMethod.SEND_MSG, methodName: NTQQApiMethod.SEND_MSG,
args: [{ args: [{
msgId: "0", msgId: '0',
peer, msgElements, peer,
msgAttributeInfos: new Map(), msgElements,
msgAttributeInfos: new Map()
}, null] }, null]
}).then() }).then()
return checkSendComplete(); return await checkSendComplete()
} }
static multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) { static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
let msgInfos = msgIds.map(id => { const msgInfos = msgIds.map(id => {
return {msgId: id, senderShowName: selfInfo.nick} return {msgId: id, senderShowName: selfInfo.nick}
}) })
const apiArgs = [ const apiArgs = [
@@ -484,42 +486,42 @@ export class NTQQApi {
commentElements: [], commentElements: [],
msgAttributeInfos: new Map() msgAttributeInfos: new Map()
}, },
null, null
] ]
return new Promise<RawMessage>((resolve, reject) => { return await new Promise<RawMessage>((resolve, reject) => {
let complete = false let complete = false
setTimeout(() => { setTimeout(() => {
if (!complete) { if (!complete) {
reject("转发消息超时"); reject('转发消息超时')
} }
}, 5000) }, 5000)
registerReceiveHook(ReceiveCmd.SELF_SEND_MSG, (payload: { msgRecord: RawMessage }) => { registerReceiveHook(ReceiveCmd.SELF_SEND_MSG, (payload: { msgRecord: RawMessage }) => {
const msg = payload.msgRecord; const msg = payload.msgRecord
// 需要判断它是转发的消息,并且识别到是当前转发的这一条 // 需要判断它是转发的消息,并且识别到是当前转发的这一条
const arkElement = msg.elements.find(ele => ele.arkElement) const arkElement = msg.elements.find(ele => ele.arkElement)
if (!arkElement) { if (!arkElement) {
// log("收到的不是转发消息") // log("收到的不是转发消息")
return return
} }
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData); const forwardData: any = JSON.parse(arkElement.arkElement.bytesData)
if (forwardData.app != "com.tencent.multimsg") { if (forwardData.app != 'com.tencent.multimsg') {
return return
} }
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) { if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
complete = true; complete = true
addHistoryMsg(msg) addHistoryMsg(msg)
resolve(msg); resolve(msg)
log("转发消息成功:", payload) log('转发消息成功:', payload)
} }
}) })
callNTQQApi<GeneralCallResult>({ callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.MULTI_FORWARD_MSG, methodName: NTQQApiMethod.MULTI_FORWARD_MSG,
args: apiArgs args: apiArgs
}).then(result => { }).then(result => {
log("转发消息结果:", result, apiArgs) log('转发消息结果:', result, apiArgs)
if (result.result !== 0) { if (result.result !== 0) {
complete = true; complete = true
reject("转发消息失败," + JSON.stringify(result)); reject('转发消息失败,' + JSON.stringify(result))
} }
}) })
}) })
@@ -530,56 +532,56 @@ export class NTQQApi {
// 加群通知,退出通知,需要管理员权限 // 加群通知,退出通知,需要管理员权限
callNTQQApi<GeneralCallResult>({ callNTQQApi<GeneralCallResult>({
methodName: ReceiveCmd.GROUP_NOTIFY, methodName: ReceiveCmd.GROUP_NOTIFY,
classNameIsRegister: true, classNameIsRegister: true
}).then() }).then()
return await callNTQQApi<GroupNotifies>({ return await callNTQQApi<GroupNotifies>({
methodName: NTQQApiMethod.GET_GROUP_NOTICE, methodName: NTQQApiMethod.GET_GROUP_NOTICE,
cbCmd: ReceiveCmd.GROUP_NOTIFY, cbCmd: ReceiveCmd.GROUP_NOTIFY,
afterFirstCmd: false, afterFirstCmd: false,
args: [ args: [
{"doubt": false, "startSeq": "", "number": 14}, {doubt: false, startSeq: '', number: 14},
null null
] ]
}); })
} }
static async handleGroupRequest(seq: string, operateType: GroupRequestOperateTypes, reason?: string) { static async handleGroupRequest(seq: string, operateType: GroupRequestOperateTypes, reason?: string) {
const notify: GroupNotify = groupNotifies[seq]; const notify: GroupNotify = groupNotifies[seq]
if (!notify) { if (!notify) {
throw `${seq}对应的加群通知不存在` throw `${seq}对应的加群通知不存在`
} }
delete groupNotifies[seq]; delete groupNotifies[seq]
return await callNTQQApi<GeneralCallResult>({ return await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.HANDLE_GROUP_REQUEST, methodName: NTQQApiMethod.HANDLE_GROUP_REQUEST,
args: [ args: [
{ {
"doubt": false, doubt: false,
"operateMsg": { operateMsg: {
"operateType": operateType, // 2 拒绝 operateType, // 2 拒绝
"targetMsg": { targetMsg: {
"seq": seq, // 通知序列号 seq, // 通知序列号
"type": notify.type, type: notify.type,
"groupCode": notify.group.groupCode, groupCode: notify.group.groupCode,
"postscript": reason postscript: reason
} }
} }
}, },
null null
] ]
}); })
} }
static async quitGroup(groupQQ: string) { static async quitGroup(groupQQ: string) {
await callNTQQApi<GeneralCallResult>({ await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.QUIT_GROUP, methodName: NTQQApiMethod.QUIT_GROUP,
args: [ args: [
{"groupCode": groupQQ}, {groupCode: groupQQ},
null null
] ]
}) })
} }
static async handleFriendRequest(sourceId: number, accept: boolean,) { static async handleFriendRequest(sourceId: number, accept: boolean) {
const request: FriendRequest = friendRequests[sourceId] const request: FriendRequest = friendRequests[sourceId]
if (!request) { if (!request) {
throw `sourceId ${sourceId}, 对应的好友请求不存在` throw `sourceId ${sourceId}, 对应的好友请求不存在`
@@ -588,20 +590,20 @@ export class NTQQApi {
methodName: NTQQApiMethod.HANDLE_FRIEND_REQUEST, methodName: NTQQApiMethod.HANDLE_FRIEND_REQUEST,
args: [ args: [
{ {
"approvalInfo": { approvalInfo: {
"friendUid": request.friendUid, friendUid: request.friendUid,
"reqTime": request.reqTime, reqTime: request.reqTime,
accept accept
} }
} }
] ]
}) })
delete friendRequests[sourceId]; delete friendRequests[sourceId]
return result; return result
} }
static kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = "") { static async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') {
return callNTQQApi<GeneralCallResult>( return await callNTQQApi<GeneralCallResult>(
{ {
methodName: NTQQApiMethod.KICK_MEMBER, methodName: NTQQApiMethod.KICK_MEMBER,
args: [ args: [
@@ -609,30 +611,30 @@ export class NTQQApi {
groupCode: groupQQ, groupCode: groupQQ,
kickUids, kickUids,
refuseForever, refuseForever,
kickReason, kickReason
} }
] ]
} }
) )
} }
static banMember(groupQQ: string, memList: { uid: string, timeStamp: number }[]) { static async banMember(groupQQ: string, memList: Array<{ uid: string, timeStamp: number }>) {
// timeStamp为秒数, 0为解除禁言 // timeStamp为秒数, 0为解除禁言
return callNTQQApi<GeneralCallResult>( return await callNTQQApi<GeneralCallResult>(
{ {
methodName: NTQQApiMethod.MUTE_MEMBER, methodName: NTQQApiMethod.MUTE_MEMBER,
args: [ args: [
{ {
groupCode: groupQQ, groupCode: groupQQ,
memList, memList
} }
] ]
} }
) )
} }
static banGroup(groupQQ: string, shutUp: boolean) { static async banGroup(groupQQ: string, shutUp: boolean) {
return callNTQQApi<GeneralCallResult>({ return await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.MUTE_GROUP, methodName: NTQQApiMethod.MUTE_GROUP,
args: [ args: [
{ {
@@ -643,8 +645,8 @@ export class NTQQApi {
}) })
} }
static setMemberCard(groupQQ: string, memberUid: string, cardName: string) { static async setMemberCard(groupQQ: string, memberUid: string, cardName: string) {
return callNTQQApi<GeneralCallResult>({ return await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.SET_MEMBER_CARD, methodName: NTQQApiMethod.SET_MEMBER_CARD,
args: [ args: [
{ {
@@ -656,8 +658,8 @@ export class NTQQApi {
}) })
} }
static setMemberRole(groupQQ: string, memberUid: string, role: GroupMemberRole) { static async setMemberRole(groupQQ: string, memberUid: string, role: GroupMemberRole) {
return callNTQQApi<GeneralCallResult>({ return await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.SET_MEMBER_ROLE, methodName: NTQQApiMethod.SET_MEMBER_ROLE,
args: [ args: [
{ {
@@ -669,8 +671,8 @@ export class NTQQApi {
}) })
} }
static setGroupName(groupQQ: string, groupName: string) { static async setGroupName(groupQQ: string, groupName: string) {
return callNTQQApi<GeneralCallResult>({ return await callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.SET_GROUP_NAME, methodName: NTQQApiMethod.SET_GROUP_NAME,
args: [ args: [
{ {
@@ -684,4 +686,4 @@ export class NTQQApi {
static publishGroupBulletin(groupQQ: string, title: string, content: string) { static publishGroupBulletin(groupQQ: string, title: string, content: string) {
} }
} }

View File

@@ -249,7 +249,7 @@ export interface VideoElement {
"thumbHeight": number, "thumbHeight": number,
"busiType": 0, // 未知 "busiType": 0, // 未知
"subBusiType": 0, // 未知 "subBusiType": 0, // 未知
"thumbPath": Map<number,any>, "thumbPath": Map<number, any>,
"transferStatus": 0, // 未知 "transferStatus": 0, // 未知
"progress": 0, // 下载进度? "progress": 0, // 下载进度?
"invalidState": 0, // 未知 "invalidState": 0, // 未知

View File

@@ -4,6 +4,7 @@ import {OB11Return} from "../types";
class BaseAction<PayloadType, ReturnDataType> { class BaseAction<PayloadType, ReturnDataType> {
actionName: ActionName actionName: ActionName
protected async check(payload: PayloadType): Promise<BaseCheckResult> { protected async check(payload: PayloadType): Promise<BaseCheckResult> {
return { return {
valid: true, valid: true,

View File

@@ -1,10 +1,10 @@
import {ActionName} from "./types"; import {ActionName} from "./types";
import CanSendRecord from "./CanSendRecord"; import CanSendRecord from "./CanSendRecord";
interface ReturnType{ interface ReturnType {
yes: boolean yes: boolean
} }
export default class CanSendImage extends CanSendRecord{ export default class CanSendImage extends CanSendRecord {
actionName = ActionName.CanSendImage actionName = ActionName.CanSendImage
} }

View File

@@ -1,14 +1,14 @@
import BaseAction from "./BaseAction"; import BaseAction from "./BaseAction";
import {ActionName} from "./types"; import {ActionName} from "./types";
interface ReturnType{ interface ReturnType {
yes: boolean yes: boolean
} }
export default class CanSendRecord extends BaseAction<any, ReturnType>{ export default class CanSendRecord extends BaseAction<any, ReturnType> {
actionName = ActionName.CanSendRecord actionName = ActionName.CanSendRecord
protected async _handle(payload): Promise<ReturnType>{ protected async _handle(payload): Promise<ReturnType> {
return { return {
yes: true yes: true
} }

View File

@@ -1,24 +1,24 @@
import BaseAction from "./BaseAction"; import BaseAction from "./BaseAction";
import {NTQQApi} from "../../ntqqapi/ntcall"; import {NTQQApi} from "../../ntqqapi/ntcall";
import {friends} from "../../common/data";
import {ActionName} from "./types"; import {ActionName} from "./types";
import {log} from "../../common/utils"; import {log} from "../../common/utils";
interface Payload{ interface Payload {
method: string, method: string,
args: any[], args: any[],
} }
export default class Debug extends BaseAction<Payload, any>{ export default class Debug extends BaseAction<Payload, any> {
actionName = ActionName.Debug actionName = ActionName.Debug
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);
const method = NTQQApi[payload.method] const method = NTQQApi[payload.method]
if (!method){ if (!method) {
throw `${method} 不存在` throw `${method} 不存在`
} }
const result = method(...payload.args); const result = method(...payload.args);
if (method.constructor.name === "AsyncFunction"){ if (method.constructor.name === "AsyncFunction") {
return await result return await result
} }
return result return result

View File

@@ -10,7 +10,7 @@ interface Payload {
class DeleteMsg extends BaseAction<Payload, void> { class DeleteMsg extends BaseAction<Payload, void> {
actionName = ActionName.DeleteMsg actionName = ActionName.DeleteMsg
protected async _handle(payload:Payload){ protected async _handle(payload: Payload) {
let msg = getHistoryMsgByShortId(payload.message_id) let msg = getHistoryMsgByShortId(payload.message_id)
await NTQQApi.recallMsg({ await NTQQApi.recallMsg({
chatType: msg.chatType, chatType: msg.chatType,

View File

@@ -3,11 +3,11 @@ import {fileCache} from "../../common/data";
import {getConfigUtil} from "../../common/utils"; import {getConfigUtil} from "../../common/utils";
import fs from "fs/promises"; import fs from "fs/promises";
export interface GetFilePayload{ export interface GetFilePayload {
file: string // 文件名 file: string // 文件名
} }
export interface GetFileResponse{ export interface GetFileResponse {
file?: string // path file?: string // path
url?: string url?: string
file_size?: string file_size?: string
@@ -16,7 +16,7 @@ export interface GetFileResponse{
} }
export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse>{ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> { protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
const cache = fileCache.get(payload.file) const cache = fileCache.get(payload.file)
const {autoDeleteFile, enableLocalFile2Url, autoDeleteFileSecond} = getConfigUtil().getConfig() const {autoDeleteFile, enableLocalFile2Url, autoDeleteFileSecond} = getConfigUtil().getConfig()
@@ -26,7 +26,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse>{
if (cache.downloadFunc) { if (cache.downloadFunc) {
await cache.downloadFunc() await cache.downloadFunc()
} }
let res : GetFileResponse= { let res: GetFileResponse = {
file: cache.filePath, file: cache.filePath,
url: cache.url, url: cache.url,
file_size: cache.fileSize, file_size: cache.fileSize,

View File

@@ -8,7 +8,7 @@ import {ActionName} from "./types";
class GetFriendList extends BaseAction<null, OB11User[]> { class GetFriendList extends BaseAction<null, OB11User[]> {
actionName = ActionName.GetFriendList actionName = ActionName.GetFriendList
protected async _handle(payload: null){ protected async _handle(payload: null) {
return OB11Constructor.friends(friends); return OB11Constructor.friends(friends);
} }
} }

View File

@@ -8,7 +8,7 @@ import {ActionName} from "./types";
class GetGroupList extends BaseAction<null, OB11Group[]> { class GetGroupList extends BaseAction<null, OB11Group[]> {
actionName = ActionName.GetGroupList actionName = ActionName.GetGroupList
protected async _handle(payload: null){ protected async _handle(payload: null) {
return OB11Constructor.groups(groups); return OB11Constructor.groups(groups);
} }
} }

View File

@@ -13,13 +13,12 @@ export interface PayloadType {
class GetGroupMemberInfo extends BaseAction<PayloadType, OB11GroupMember> { class GetGroupMemberInfo extends BaseAction<PayloadType, OB11GroupMember> {
actionName = ActionName.GetGroupMemberInfo actionName = ActionName.GetGroupMemberInfo
protected async _handle(payload: PayloadType){ protected async _handle(payload: PayloadType) {
const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString()) const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString())
if (member) { if (member) {
return OB11Constructor.groupMember(payload.group_id.toString(), member) return OB11Constructor.groupMember(payload.group_id.toString(), member)
} } else {
else { throw (`群成员${payload.user_id}不存在`)
throw(`群成员${payload.user_id}不存在`)
} }
} }
} }

View File

@@ -13,15 +13,14 @@ export interface PayloadType {
class GetGroupMemberList extends BaseAction<PayloadType, OB11GroupMember[]> { class GetGroupMemberList extends BaseAction<PayloadType, OB11GroupMember[]> {
actionName = ActionName.GetGroupMemberList actionName = ActionName.GetGroupMemberList
protected async _handle(payload: PayloadType){ protected async _handle(payload: PayloadType) {
const group = await getGroup(payload.group_id.toString()); const group = await getGroup(payload.group_id.toString());
if (group) { if (group) {
if (!group.members?.length) { if (!group.members?.length) {
group.members = await NTQQApi.getGroupMembers(payload.group_id.toString()) group.members = await NTQQApi.getGroupMembers(payload.group_id.toString())
} }
return OB11Constructor.groupMembers(group); return OB11Constructor.groupMembers(group);
} } else {
else {
throw (`${payload.group_id}不存在`) throw (`${payload.group_id}不存在`)
} }
} }

View File

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

View File

@@ -2,6 +2,6 @@ import {GetFileBase} from "./GetFile";
import {ActionName} from "./types"; import {ActionName} from "./types";
export default class GetImage extends GetFileBase{ export default class GetImage extends GetFileBase {
actionName = ActionName.GetImage actionName = ActionName.GetImage
} }

View File

@@ -8,7 +8,7 @@ import {ActionName} from "./types";
class GetLoginInfo extends BaseAction<null, OB11User> { class GetLoginInfo extends BaseAction<null, OB11User> {
actionName = ActionName.GetLoginInfo actionName = ActionName.GetLoginInfo
protected async _handle(payload: null){ protected async _handle(payload: null) {
return OB11Constructor.selfInfo(selfInfo); return OB11Constructor.selfInfo(selfInfo);
} }
} }

View File

@@ -14,17 +14,17 @@ export type ReturnDataType = OB11Message
class GetMsg extends BaseAction<PayloadType, OB11Message> { class GetMsg extends BaseAction<PayloadType, OB11Message> {
actionName = ActionName.GetMsg actionName = ActionName.GetMsg
protected async _handle(payload: PayloadType){ protected async _handle(payload: PayloadType) {
// log("history msg ids", Object.keys(msgHistory)); // log("history msg ids", Object.keys(msgHistory));
if (!payload.message_id){ if (!payload.message_id) {
throw("参数message_id不能为空") throw ("参数message_id不能为空")
} }
const msg = getHistoryMsgByShortId(payload.message_id) const msg = getHistoryMsgByShortId(payload.message_id)
if (msg) { if (msg) {
const msgData = await OB11Constructor.message(msg); const msgData = await OB11Constructor.message(msg);
return msgData return msgData
} else { } else {
throw("消息不存在") throw ("消息不存在")
} }
} }
} }

View File

@@ -1,15 +1,15 @@
import {GetFileBase, GetFilePayload, GetFileResponse} from "./GetFile"; import {GetFileBase, GetFilePayload, GetFileResponse} from "./GetFile";
import {ActionName} from "./types"; import {ActionName} from "./types";
interface Payload extends GetFilePayload{ interface Payload extends GetFilePayload {
out_format: 'mp3' | 'amr' | 'wma' | 'm4a' | 'spx' | 'ogg' | 'wav' | 'flac' out_format: 'mp3' | 'amr' | 'wma' | 'm4a' | 'spx' | 'ogg' | 'wav' | 'flac'
} }
export default class GetRecord extends GetFileBase{ export default class GetRecord extends GetFileBase {
actionName = ActionName.GetRecord actionName = ActionName.GetRecord
protected async _handle(payload: Payload): Promise<GetFileResponse> { protected async _handle(payload: Payload): Promise<GetFileResponse> {
let res = super._handle(payload); let res = super._handle(payload);
return res; return res;
} }
} }

View File

@@ -6,6 +6,7 @@ import {selfInfo} from "../../common/data";
export default class GetStatus extends BaseAction<any, OB11Status> { export default class GetStatus extends BaseAction<any, OB11Status> {
actionName = ActionName.GetStatus actionName = ActionName.GetStatus
protected async _handle(payload: any): Promise<OB11Status> { protected async _handle(payload: any): Promise<OB11Status> {
return { return {
online: selfInfo.online, online: selfInfo.online,

View File

@@ -3,8 +3,9 @@ import {OB11Version} from "../types";
import {ActionName} from "./types"; import {ActionName} from "./types";
import {version} from "../../version"; import {version} from "../../version";
export default class GetVersionInfo extends BaseAction<any, OB11Version>{ export default class GetVersionInfo extends BaseAction<any, OB11Version> {
actionName = ActionName.GetVersionInfo actionName = ActionName.GetVersionInfo
protected async _handle(payload: any): Promise<OB11Version> { protected async _handle(payload: any): Promise<OB11Version> {
return { return {
app_name: "LLOneBot", app_name: "LLOneBot",

View File

@@ -2,7 +2,7 @@ import SendMsg from "./SendMsg";
import {ActionName} from "./types"; import {ActionName} from "./types";
class SendGroupMsg extends SendMsg{ class SendGroupMsg extends SendMsg {
actionName = ActionName.SendGroupMsg actionName = ActionName.SendGroupMsg
} }

View File

@@ -2,7 +2,6 @@ import BaseAction from "./BaseAction";
import {getFriend} from "../../common/data"; import {getFriend} from "../../common/data";
import {NTQQApi} from "../../ntqqapi/ntcall"; import {NTQQApi} from "../../ntqqapi/ntcall";
import {ActionName} from "./types"; import {ActionName} from "./types";
import { log } from "../../common/utils";
interface Payload { interface Payload {
user_id: number, user_id: number,
@@ -20,7 +19,7 @@ export default class SendLike extends BaseAction<Payload, null> {
} }
try { try {
let result = await NTQQApi.likeFriend(friend.uid, parseInt(payload.times.toString()) || 1); let result = await NTQQApi.likeFriend(friend.uid, parseInt(payload.times.toString()) || 1);
if (result.result !== 0){ if (result.result !== 0) {
throw result.errMsg throw result.errMsg
} }
} catch (e) { } catch (e) {

View File

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

View File

@@ -4,17 +4,18 @@ import {getGroupMember} from "../../common/data";
import {GroupMemberRole} from "../../ntqqapi/types"; import {GroupMemberRole} from "../../ntqqapi/types";
import {ActionName} from "./types"; import {ActionName} from "./types";
interface Payload{ interface Payload {
group_id: number, group_id: number,
user_id: number, user_id: number,
enable: boolean enable: boolean
} }
export default class SetGroupAdmin extends BaseAction<Payload, null>{ export default class SetGroupAdmin extends BaseAction<Payload, null> {
actionName = ActionName.SetGroupAdmin actionName = ActionName.SetGroupAdmin
protected async _handle(payload: Payload): Promise<null> { protected async _handle(payload: Payload): Promise<null> {
const member = await getGroupMember(payload.group_id, payload.user_id) const member = await getGroupMember(payload.group_id, payload.user_id)
if(!member){ if (!member) {
throw `群成员${payload.user_id}不存在` throw `群成员${payload.user_id}不存在`
} }
await NTQQApi.setMemberRole(payload.group_id.toString(), member.uid, payload.enable ? GroupMemberRole.admin : GroupMemberRole.normal) await NTQQApi.setMemberRole(payload.group_id.toString(), member.uid, payload.enable ? GroupMemberRole.admin : GroupMemberRole.normal)

View File

@@ -3,21 +3,22 @@ import {NTQQApi} from "../../ntqqapi/ntcall";
import {getGroupMember} from "../../common/data"; import {getGroupMember} from "../../common/data";
import {ActionName} from "./types"; import {ActionName} from "./types";
interface Payload{ interface Payload {
group_id: number, group_id: number,
user_id: number, user_id: number,
duration: number duration: number
} }
export default class SetGroupBan extends BaseAction<Payload, null>{ export default class SetGroupBan extends BaseAction<Payload, null> {
actionName = ActionName.SetGroupBan actionName = ActionName.SetGroupBan
protected async _handle(payload: Payload): Promise<null> { protected async _handle(payload: Payload): Promise<null> {
const member = await getGroupMember(payload.group_id, payload.user_id) const member = await getGroupMember(payload.group_id, payload.user_id)
if(!member){ if (!member) {
throw `群成员${payload.user_id}不存在` throw `群成员${payload.user_id}不存在`
} }
await NTQQApi.banMember(payload.group_id.toString(), await NTQQApi.banMember(payload.group_id.toString(),
[{uid:member.uid, timeStamp: parseInt(payload.duration.toString())}]) [{uid: member.uid, timeStamp: parseInt(payload.duration.toString())}])
return null return null
} }
} }

View File

@@ -1,20 +1,20 @@
import BaseAction from "./BaseAction"; import BaseAction from "./BaseAction";
import {NTQQApi} from "../../ntqqapi/ntcall"; import {NTQQApi} from "../../ntqqapi/ntcall";
import {getGroupMember} from "../../common/data"; import {getGroupMember} from "../../common/data";
import {GroupMemberRole} from "../../ntqqapi/types";
import {ActionName} from "./types"; import {ActionName} from "./types";
interface Payload{ interface Payload {
group_id: number, group_id: number,
user_id: number, user_id: number,
card: string card: string
} }
export default class SetGroupCard extends BaseAction<Payload, null>{ export default class SetGroupCard extends BaseAction<Payload, null> {
actionName = ActionName.SetGroupCard actionName = ActionName.SetGroupCard
protected async _handle(payload: Payload): Promise<null> { protected async _handle(payload: Payload): Promise<null> {
const member = await getGroupMember(payload.group_id, payload.user_id) const member = await getGroupMember(payload.group_id, payload.user_id)
if(!member){ if (!member) {
throw `群成员${payload.user_id}不存在` throw `群成员${payload.user_id}不存在`
} }
await NTQQApi.setMemberCard(payload.group_id.toString(), member.uid, payload.card || "") await NTQQApi.setMemberCard(payload.group_id.toString(), member.uid, payload.card || "")

View File

@@ -3,17 +3,18 @@ import {NTQQApi} from "../../ntqqapi/ntcall";
import {getGroupMember} from "../../common/data"; import {getGroupMember} from "../../common/data";
import {ActionName} from "./types"; import {ActionName} from "./types";
interface Payload{ interface Payload {
group_id: number, group_id: number,
user_id: number, user_id: number,
reject_add_request: boolean reject_add_request: boolean
} }
export default class SetGroupKick extends BaseAction<Payload, null>{ export default class SetGroupKick extends BaseAction<Payload, null> {
actionName = ActionName.SetGroupKick actionName = ActionName.SetGroupKick
protected async _handle(payload: Payload): Promise<null> { protected async _handle(payload: Payload): Promise<null> {
const member = await getGroupMember(payload.group_id, payload.user_id) const member = await getGroupMember(payload.group_id, payload.user_id)
if(!member){ if (!member) {
throw `群成员${payload.user_id}不存在` throw `群成员${payload.user_id}不存在`
} }
await NTQQApi.kickMember(payload.group_id.toString(), [member.uid], !!payload.reject_add_request); await NTQQApi.kickMember(payload.group_id.toString(), [member.uid], !!payload.reject_add_request);

View File

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

View File

@@ -1,21 +1,22 @@
import BaseAction from "../BaseAction"; import BaseAction from "../BaseAction";
import {OB11GroupMember, OB11User} from "../../types"; import {OB11User} from "../../types";
import {friends, getFriend, getGroupMember, groups} from "../../../common/data"; import {getFriend, getGroupMember, groups} from "../../../common/data";
import {OB11Constructor} from "../../constructor"; import {OB11Constructor} from "../../constructor";
import {ActionName} from "../types"; import {ActionName} from "../types";
export default class GoCQHTTPGetStrangerInfo extends BaseAction<{user_id: number}, OB11User>{ export default class GoCQHTTPGetStrangerInfo extends BaseAction<{ user_id: number }, OB11User> {
actionName = ActionName.GoCQHTTP_GetStrangerInfo actionName = ActionName.GoCQHTTP_GetStrangerInfo
protected async _handle(payload: { user_id: number }): Promise<OB11User> { protected async _handle(payload: { user_id: number }): Promise<OB11User> {
const user_id = payload.user_id.toString() const user_id = payload.user_id.toString()
const friend = await getFriend(user_id) const friend = await getFriend(user_id)
if (friend){ if (friend) {
return OB11Constructor.friend(friend); return OB11Constructor.friend(friend);
} }
for(const group of groups){ for (const group of groups) {
const member = await getGroupMember(group.groupCode, user_id) const member = await getGroupMember(group.groupCode, user_id)
if (member){ if (member) {
return OB11Constructor.groupMember(group.groupCode, member) as OB11User return OB11Constructor.groupMember(group.groupCode, member) as OB11User
} }
} }

View File

@@ -1,15 +1,16 @@
import SendMsg, {ReturnDataType} from "../SendMsg"; import SendMsg from "../SendMsg";
import {OB11MessageMixType, OB11PostSendMsg} from "../../types"; import {OB11PostSendMsg} from "../../types";
import {ActionName, BaseCheckResult} from "../types"; import {ActionName} from "../types";
export class GoCQHTTPSendGroupForwardMsg extends SendMsg{ export class GoCQHTTPSendGroupForwardMsg extends SendMsg {
actionName = ActionName.GoCQHTTP_SendGroupForwardMsg; actionName = ActionName.GoCQHTTP_SendGroupForwardMsg;
protected async check(payload: OB11PostSendMsg){
protected async check(payload: OB11PostSendMsg) {
payload.message = this.convertMessage2List(payload.messages); payload.message = this.convertMessage2List(payload.messages);
return super.check(payload); return super.check(payload);
} }
} }
export class GoCQHTTPSendPrivateForwardMsg extends GoCQHTTPSendGroupForwardMsg{ export class GoCQHTTPSendPrivateForwardMsg extends GoCQHTTPSendGroupForwardMsg {
actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg; actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg;
} }

View File

@@ -1,15 +1,15 @@
import GetGuildList from "./GetGuildList";
export type BaseCheckResult = ValidCheckResult | InvalidCheckResult export type BaseCheckResult = ValidCheckResult | InvalidCheckResult
export interface ValidCheckResult { export interface ValidCheckResult {
valid: true valid: true
[k: string | number]: any [k: string | number]: any
} }
export interface InvalidCheckResult { export interface InvalidCheckResult {
valid: false valid: false
message: string message: string
[k: string | number]: any [k: string | number]: any
} }

View File

@@ -7,18 +7,18 @@ import {
OB11MessageDataType, OB11MessageDataType,
OB11User OB11User
} from "./types"; } from "./types";
import { AtType, ChatType, Group, GroupMember, IMAGE_HTTP_HOST, RawMessage, SelfInfo, User } from '../ntqqapi/types'; import {AtType, ChatType, Group, GroupMember, IMAGE_HTTP_HOST, RawMessage, SelfInfo, User} from '../ntqqapi/types';
import {fileCache, getFriend, getGroupMember, getHistoryMsgBySeq, selfInfo} from '../common/data'; import {fileCache, getFriend, getGroupMember, getHistoryMsgBySeq, selfInfo} from '../common/data';
import { file2base64, getConfigUtil, log } from "../common/utils"; import {getConfigUtil, log} from "../common/utils";
import { NTQQApi } from "../ntqqapi/ntcall"; import {NTQQApi} from "../ntqqapi/ntcall";
import { EventType } from "./event/OB11BaseEvent"; import {EventType} from "./event/OB11BaseEvent";
import { encodeCQCode } from "./cqcode"; import {encodeCQCode} from "./cqcode";
export class OB11Constructor { export class OB11Constructor {
static async message(msg: RawMessage): Promise<OB11Message> { static async message(msg: RawMessage): Promise<OB11Message> {
const { enableLocalFile2Url, ob11: { messagePostFormat } } = getConfigUtil().getConfig() const {enableLocalFile2Url, ob11: {messagePostFormat}} = getConfigUtil().getConfig()
const message_type = msg.chatType == ChatType.group ? "group" : "private"; const message_type = msg.chatType == ChatType.group ? "group" : "private";
const resMsg: OB11Message = { const resMsg: OB11Message = {
self_id: parseInt(selfInfo.uin), self_id: parseInt(selfInfo.uin),
@@ -110,9 +110,10 @@ export class OB11Constructor {
fileSize: element.picElement.fileSize.toString(), fileSize: element.picElement.fileSize.toString(),
url: IMAGE_HTTP_HOST + element.picElement.originImageUrl, url: IMAGE_HTTP_HOST + element.picElement.originImageUrl,
downloadFunc: async () => { downloadFunc: async () => {
await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
element.elementId, element.picElement.thumbPath.get(0), element.picElement.sourcePath) element.elementId, element.picElement.thumbPath.get(0), element.picElement.sourcePath)
}}) }
})
// 不在自动下载图片 // 不在自动下载图片
} else if (element.videoElement) { } else if (element.videoElement) {
@@ -128,7 +129,8 @@ export class OB11Constructor {
downloadFunc: async () => { downloadFunc: async () => {
await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
element.elementId, element.videoElement.thumbPath.get(0), element.videoElement.filePath) element.elementId, element.videoElement.thumbPath.get(0), element.videoElement.filePath)
}}) }
})
// 怎么拿到url呢 // 怎么拿到url呢
} else if (element.fileElement) { } else if (element.fileElement) {
message_data["type"] = OB11MessageDataType.file; message_data["type"] = OB11MessageDataType.file;
@@ -143,10 +145,10 @@ export class OB11Constructor {
downloadFunc: async () => { downloadFunc: async () => {
await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, await NTQQApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
element.elementId, null, element.fileElement.filePath) element.elementId, null, element.fileElement.filePath)
}}) }
})
// 怎么拿到url呢 // 怎么拿到url呢
} } else if (element.pttElement) {
else if (element.pttElement) {
message_data["type"] = OB11MessageDataType.voice; message_data["type"] = OB11MessageDataType.voice;
message_data["data"]["file"] = element.pttElement.fileName message_data["data"]["file"] = element.pttElement.fileName
message_data["data"]["path"] = element.pttElement.filePath message_data["data"]["path"] = element.pttElement.filePath

View File

@@ -1,4 +1,4 @@
import { selfInfo } from "../../common/data"; import {selfInfo} from "../../common/data";
export enum EventType { export enum EventType {
META = "meta_event", META = "meta_event",

View File

@@ -1,4 +1,3 @@
import {OB11BaseNoticeEvent} from "./OB11BaseNoticeEvent";
import {OB11GroupNoticeEvent} from "./OB11GroupNoticeEvent"; import {OB11GroupNoticeEvent} from "./OB11GroupNoticeEvent";
export class OB11GroupAdminNoticeEvent extends OB11GroupNoticeEvent { export class OB11GroupAdminNoticeEvent extends OB11GroupNoticeEvent {

View File

@@ -2,7 +2,7 @@ import {OB11GroupNoticeEvent} from "../notice/OB11GroupNoticeEvent";
import {EventType} from "../OB11BaseEvent"; import {EventType} from "../OB11BaseEvent";
export class OB11GroupRequestEvent extends OB11GroupNoticeEvent{ export class OB11GroupRequestEvent extends OB11GroupNoticeEvent {
post_type = EventType.REQUEST; post_type = EventType.REQUEST;
request_type: "group" = "group"; request_type: "group" = "group";
sub_type: "add" | "invite" = "add"; sub_type: "add" | "invite" = "add";

View File

@@ -6,6 +6,7 @@ import {actionHandlers} from "../action";
class OB11HTTPServer extends HttpServerBase { class OB11HTTPServer extends HttpServerBase {
name = "OneBot V11 server" name = "OneBot V11 server"
handleFailed(res: Response, payload: any, e: any) { handleFailed(res: Response, payload: any, e: any) {
res.send(OB11Response.error(e.stack.toString(), 200)) res.send(OB11Response.error(e.stack.toString(), 200))
} }
@@ -20,7 +21,7 @@ class OB11HTTPServer extends HttpServerBase {
export const ob11HTTPServer = new OB11HTTPServer(); export const ob11HTTPServer = new OB11HTTPServer();
for (const action of actionHandlers) { for (const action of actionHandlers) {
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, action.actionName, (res, payload) => action.handle(payload))
} }
} }

View File

@@ -3,7 +3,7 @@ import {OB11Message} from "../types";
import {selfInfo} from "../../common/data"; import {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";
import {wsReply} from "./ws/reply"; import {wsReply} from "./ws/reply";
export type PostEventType = OB11Message | OB11BaseMetaEvent | OB11BaseNoticeEvent export type PostEventType = OB11Message | OB11BaseMetaEvent | OB11BaseNoticeEvent
@@ -29,7 +29,7 @@ export function postWsEvent(event: PostEventType) {
} }
} }
export function postOB11Event(msg: PostEventType, reportSelf=false) { export function postOB11Event(msg: PostEventType, reportSelf = false) {
const config = getConfigUtil().getConfig(); const config = getConfigUtil().getConfig();
// 判断msg是否是event // 判断msg是否是event
if (!config.reportSelfMessage && !reportSelf) { if (!config.reportSelfMessage && !reportSelf) {

View File

@@ -9,7 +9,7 @@ import BaseAction from "../../action/BaseAction";
import {actionMap} from "../../action"; import {actionMap} from "../../action";
import {registerWsEventSender, unregisterWsEventSender} from "../postOB11Event"; import {registerWsEventSender, unregisterWsEventSender} from "../postOB11Event";
import {wsReply} from "./reply"; import {wsReply} from "./reply";
import { WebSocket as WebSocketClass } from "ws"; import {WebSocket as WebSocketClass} from "ws";
export let rwsList: ReverseWebsocket[] = []; export let rwsList: ReverseWebsocket[] = [];

View File

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

View File

@@ -54,13 +54,11 @@ export async function uri2local(uri: string, fileName: string = null) {
} else { } else {
filePath = pathname filePath = pathname
} }
} } else {
else{
const cache = fileCache.get(uri) const cache = fileCache.get(uri)
if (cache) { if (cache) {
filePath = cache.filePath filePath = cache.filePath
} } else {
else{
filePath = uri; filePath = uri;
} }
} }

View File

@@ -1,20 +1,20 @@
/// <reference path="./global.d.ts" /> /// <reference path="./global.d.ts" />
// 打开设置界面时触发 // 打开设置界面时触发
async function onSettingWindowCreated(view: Element) { async function onSettingWindowCreated (view: Element) {
window.llonebot.log("setting window created"); window.llonebot.log('setting window created')
const isEmpty = (value: any) => value === undefined || value === null || value === ''; const isEmpty = (value: any) => value === undefined || value === null || value === ''
let config = await window.llonebot.getConfig() const config = await window.llonebot.getConfig()
const httpClass = "http"; const httpClass = 'http'
const httpPostClass = "http-post"; const httpPostClass = 'http-post'
const wsClass = "ws"; const wsClass = 'ws'
const reverseWSClass = "reverse-ws"; const reverseWSClass = 'reverse-ws'
const llonebotError = await window.llonebot.getError(); const llonebotError = await window.llonebot.getError()
window.llonebot.log("获取error" + JSON.stringify(llonebotError)); window.llonebot.log('获取error' + JSON.stringify(llonebotError))
function createHttpHostEleStr(host: string) {
let eleStr = ` function createHttpHostEleStr (host: string) {
const eleStr = `
<setting-item data-direction="row" class="hostItem vertical-list-item ${httpPostClass}"> <setting-item data-direction="row" class="hostItem vertical-list-item ${httpPostClass}">
<h2>HTTP事件上报地址(http)</h2> <h2>HTTP事件上报地址(http)</h2>
<input class="httpHost input-text" type="text" value="${host}" <input class="httpHost input-text" type="text" value="${host}"
@@ -22,11 +22,11 @@ async function onSettingWindowCreated(view: Element) {
placeholder="如:http://127.0.0.1:8080/onebot/v11/http"/> placeholder="如:http://127.0.0.1:8080/onebot/v11/http"/>
</setting-item> </setting-item>
` `
return eleStr return eleStr
} }
function createWsHostEleStr(host: string) { function createWsHostEleStr (host: string) {
let eleStr = ` const eleStr = `
<setting-item data-direction="row" class="hostItem vertical-list-item ${reverseWSClass}"> <setting-item data-direction="row" class="hostItem vertical-list-item ${reverseWSClass}">
<h2>反向websocket地址:</h2> <h2>反向websocket地址:</h2>
<input class="wsHost input-text" type="text" value="${host}" <input class="wsHost input-text" type="text" value="${host}"
@@ -34,20 +34,20 @@ async function onSettingWindowCreated(view: Element) {
placeholder="如: ws://127.0.0.1:5410/onebot"/> placeholder="如: ws://127.0.0.1:5410/onebot"/>
</setting-item> </setting-item>
` `
return eleStr return eleStr
} }
let httpHostsEleStr = "" let httpHostsEleStr = ''
for (const host of config.ob11.httpHosts) { for (const host of config.ob11.httpHosts) {
httpHostsEleStr += createHttpHostEleStr(host); httpHostsEleStr += createHttpHostEleStr(host)
} }
let wsHostsEleStr = "" let wsHostsEleStr = ''
for (const host of config.ob11.wsHosts) { for (const host of config.ob11.wsHosts) {
wsHostsEleStr += createWsHostEleStr(host); wsHostsEleStr += createWsHostEleStr(host)
} }
let html = ` const html = `
<div class="config_view llonebot"> <div class="config_view llonebot">
<setting-section> <setting-section>
<setting-panel id="llonebotError" style="display:${llonebotError.ffmpegError || llonebotError.otherError ? '' : 'none'}"> <setting-panel id="llonebotError" style="display:${llonebotError.ffmpegError || llonebotError.otherError ? '' : 'none'}">
@@ -68,7 +68,7 @@ async function onSettingWindowCreated(view: Element) {
<div> <div>
<div>启用HTTP服务</div> <div>启用HTTP服务</div>
</div> </div>
<setting-switch id="http" ${config.ob11.enableHttp ? "is-active" : ""}></setting-switch> <setting-switch id="http" ${config.ob11.enableHttp ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
<setting-item class="vertical-list-item ${httpClass}" data-direction="row" style="display: ${config.ob11.enableHttp ? '' : 'none'}"> <setting-item class="vertical-list-item ${httpClass}" data-direction="row" style="display: ${config.ob11.enableHttp ? '' : 'none'}">
<setting-text>HTTP监听端口</setting-text> <setting-text>HTTP监听端口</setting-text>
@@ -78,7 +78,7 @@ async function onSettingWindowCreated(view: Element) {
<div> <div>
<div>启用HTTP事件上报</div> <div>启用HTTP事件上报</div>
</div> </div>
<setting-switch id="httpPost" ${config.ob11.enableHttpPost ? "is-active" : ""}></setting-switch> <setting-switch id="httpPost" ${config.ob11.enableHttpPost ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
<div class="${httpPostClass}" style="display: ${config.ob11.enableHttpPost ? '' : 'none'}"> <div class="${httpPostClass}" style="display: ${config.ob11.enableHttpPost ? '' : 'none'}">
<div > <div >
@@ -92,7 +92,7 @@ async function onSettingWindowCreated(view: Element) {
<div> <div>
<div>启用正向Websocket协议</div> <div>启用正向Websocket协议</div>
</div> </div>
<setting-switch id="websocket" ${config.ob11.enableWs ? "is-active" : ""}></setting-switch> <setting-switch id="websocket" ${config.ob11.enableWs ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
<setting-item class="vertical-list-item ${wsClass}" data-direction="row" style="display: ${config.ob11.enableWs ? '' : 'none'}"> <setting-item class="vertical-list-item ${wsClass}" data-direction="row" style="display: ${config.ob11.enableWs ? '' : 'none'}">
<setting-text>正向Websocket监听端口</setting-text> <setting-text>正向Websocket监听端口</setting-text>
@@ -103,7 +103,7 @@ async function onSettingWindowCreated(view: Element) {
<div> <div>
<div>启用反向Websocket协议</div> <div>启用反向Websocket协议</div>
</div> </div>
<setting-switch id="websocketReverse" ${config.ob11.enableWsReverse ? "is-active" : ""}></setting-switch> <setting-switch id="websocketReverse" ${config.ob11.enableWsReverse ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
<div class="${reverseWSClass}" style="display: ${config.ob11.enableWsReverse ? '' : 'none'}"> <div class="${reverseWSClass}" style="display: ${config.ob11.enableWsReverse ? '' : 'none'}">
<div> <div>
@@ -137,8 +137,8 @@ async function onSettingWindowCreated(view: Element) {
<setting-text data-type="secondary">如客户端无特殊需求推荐保持默认设置,两者的详细差异可参考 <a href="javascript:LiteLoader.api.openExternal('https://github.com/botuniverse/onebot-11/tree/master/message#readme');">OneBot v11 文档</a></setting-text> <setting-text data-type="secondary">如客户端无特殊需求推荐保持默认设置,两者的详细差异可参考 <a href="javascript:LiteLoader.api.openExternal('https://github.com/botuniverse/onebot-11/tree/master/message#readme');">OneBot v11 文档</a></setting-text>
</div> </div>
<setting-select id="messagePostFormat"> <setting-select id="messagePostFormat">
<setting-option data-value="array" ${config.ob11.messagePostFormat !== "string" ? "is-selected" : ""}>消息段</setting-option> <setting-option data-value="array" ${config.ob11.messagePostFormat !== 'string' ? 'is-selected' : ''}>消息段</setting-option>
<setting-option data-value="string" ${config.ob11.messagePostFormat === "string" ? "is-selected" : ""}>CQ码</setting-option> <setting-option data-value="string" ${config.ob11.messagePostFormat === 'string' ? 'is-selected' : ''}>CQ码</setting-option>
</setting-select> </setting-select>
</setting-item> </setting-item>
<setting-item data-direction="row" class="vertical-list-item"> <setting-item data-direction="row" class="vertical-list-item">
@@ -146,28 +146,28 @@ async function onSettingWindowCreated(view: Element) {
<div>获取文件使用base64编码</div> <div>获取文件使用base64编码</div>
<div class="tips">开启后,调用/get_image、/get_record时获取不到url时添加一个base64字段</div> <div class="tips">开启后,调用/get_image、/get_record时获取不到url时添加一个base64字段</div>
</div> </div>
<setting-switch id="switchFileUrl" ${config.enableLocalFile2Url ? "is-active" : ""}></setting-switch> <setting-switch id="switchFileUrl" ${config.enableLocalFile2Url ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
<setting-item data-direction="row" class="vertical-list-item"> <setting-item data-direction="row" class="vertical-list-item">
<div> <div>
<div>debug模式</div> <div>debug模式</div>
<div class="tips">开启后上报消息添加raw字段附带原始消息</div> <div class="tips">开启后上报消息添加raw字段附带原始消息</div>
</div> </div>
<setting-switch id="debug" ${config.debug ? "is-active" : ""}></setting-switch> <setting-switch id="debug" ${config.debug ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
<setting-item data-direction="row" class="vertical-list-item"> <setting-item data-direction="row" class="vertical-list-item">
<div> <div>
<div>上报自身消息</div> <div>上报自身消息</div>
<div class="tips">慎用,不然会自己和自己聊个不停</div> <div class="tips">慎用,不然会自己和自己聊个不停</div>
</div> </div>
<setting-switch id="reportSelfMessage" ${config.reportSelfMessage ? "is-active" : ""}></setting-switch> <setting-switch id="reportSelfMessage" ${config.reportSelfMessage ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
<setting-item data-direction="row" class="vertical-list-item"> <setting-item data-direction="row" class="vertical-list-item">
<div> <div>
<div>日志</div> <div>日志</div>
<div class="tips">目录:${window.LiteLoader.plugins["LLOneBot"].path.data}</div> <div class="tips">目录:${window.LiteLoader.plugins.LLOneBot.path.data}</div>
</div> </div>
<setting-switch id="log" ${config.log ? "is-active" : ""}></setting-switch> <setting-switch id="log" ${config.log ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
<setting-item data-direction="row" class="vertical-list-item"> <setting-item data-direction="row" class="vertical-list-item">
<div> <div>
@@ -179,7 +179,7 @@ async function onSettingWindowCreated(view: Element) {
value="${config.autoDeleteFileSecond || 60}" type="number"/>秒后自动删除 value="${config.autoDeleteFileSecond || 60}" type="number"/>秒后自动删除
</div> </div>
</div> </div>
<setting-switch id="autoDeleteFile" ${config.autoDeleteFile ? "is-active" : ""}></setting-switch> <setting-switch id="autoDeleteFile" ${config.autoDeleteFile ? 'is-active' : ''}></setting-switch>
</setting-item> </setting-item>
</setting-panel> </setting-panel>
</setting-section> </setting-section>
@@ -199,185 +199,182 @@ async function onSettingWindowCreated(view: Element) {
</style> </style>
` `
const parser = new DOMParser(); const parser = new DOMParser()
const doc = parser.parseFromString(html, "text/html"); const doc = parser.parseFromString(html, 'text/html')
const getError = async ()=> { const getError = async () => {
const llonebotError = await window.llonebot.getError(); const llonebotError = await window.llonebot.getError()
console.log(llonebotError); console.log(llonebotError)
const llonebotErrorEle = document.getElementById("llonebotError"); const llonebotErrorEle = document.getElementById('llonebotError')
const ffmpegErrorEle = document.getElementById("ffmpegError"); const ffmpegErrorEle = document.getElementById('ffmpegError')
const otherErrorEle = document.getElementById("otherError"); const otherErrorEle = document.getElementById('otherError')
if (llonebotError.otherError || llonebotError.ffmpegError){ if (llonebotError.otherError || llonebotError.ffmpegError) {
llonebotErrorEle.style.display = '' llonebotErrorEle.style.display = ''
} } else {
else{ llonebotErrorEle.style.display = 'none'
llonebotErrorEle.style.display = 'none' }
} if (llonebotError.ffmpegError) {
if (llonebotError.ffmpegError) { const errContentEle = doc.querySelector('#ffmpegError .err-content')
const errContentEle = doc.querySelector("#ffmpegError .err-content") // const errContent = ffmpegErrorEle.getElementsByClassName("err-content")[0];
// const errContent = ffmpegErrorEle.getElementsByClassName("err-content")[0]; errContentEle.textContent = llonebotError.ffmpegError;
errContentEle.textContent = llonebotError.ffmpegError; (ffmpegErrorEle).style.display = ''
(ffmpegErrorEle as HTMLElement).style.display = '' } else {
} ffmpegErrorEle.style.display = ''
else{ }
ffmpegErrorEle.style.display = '' if (llonebotError.otherError) {
} const errContentEle = doc.querySelector('#otherError .err-content')
if (llonebotError.otherError) { errContentEle.textContent = llonebotError.otherError
const errContentEle = doc.querySelector("#otherError .err-content") otherErrorEle.style.display = ''
errContentEle.textContent = llonebotError.otherError; } else {
otherErrorEle.style.display = '' otherErrorEle.style.display = 'none'
} }
else{ }
otherErrorEle.style.display = 'none'
} function addHostEle (type: string, initValue: string = '') {
let addressEle, hostItemsEle
if (type === 'ws') {
const addressDoc = parser.parseFromString(createWsHostEleStr(initValue), 'text/html')
addressEle = addressDoc.querySelector('setting-item')
hostItemsEle = document.getElementById('wsHostItems')
} else {
const addressDoc = parser.parseFromString(createHttpHostEleStr(initValue), 'text/html')
addressEle = addressDoc.querySelector('setting-item')
hostItemsEle = document.getElementById('httpHostItems')
} }
function addHostEle(type: string, initValue: string = "") { hostItemsEle.appendChild(addressEle)
let addressEle, hostItemsEle; }
if (type === "ws") {
let addressDoc = parser.parseFromString(createWsHostEleStr(initValue), "text/html");
addressEle = addressDoc.querySelector("setting-item")
hostItemsEle = document.getElementById("wsHostItems");
} else {
let addressDoc = parser.parseFromString(createHttpHostEleStr(initValue), "text/html");
addressEle = addressDoc.querySelector("setting-item")
hostItemsEle = document.getElementById("httpHostItems");
}
hostItemsEle.appendChild(addressEle); doc.getElementById('addHttpHost').addEventListener('click', () => {
addHostEle('http')
})
doc.getElementById('addWsHost').addEventListener('click', () => {
addHostEle('ws')
})
doc.getElementById('messagePostFormat').addEventListener('selected', (e: CustomEvent) => {
config.ob11.messagePostFormat = e.detail && !isEmpty(e.detail.value) ? e.detail.value : 'array'
window.llonebot.setConfig(config)
})
function switchClick (eleId: string, configKey: string, _config = null) {
if (!_config) {
_config = config
} }
doc.getElementById(eleId)?.addEventListener('click', (e) => {
const switchEle = e.target as HTMLInputElement
if (_config[configKey]) {
_config[configKey] = false
switchEle.removeAttribute('is-active')
} else {
_config[configKey] = true
switchEle.setAttribute('is-active', '')
}
// 妈蛋手动操作DOM越写越麻烦要不用vue算了
const keyClassMap = {
enableHttp: httpClass,
enableHttpPost: httpPostClass,
enableWs: wsClass,
enableWsReverse: reverseWSClass
}
for (const e of document.getElementsByClassName(keyClassMap[configKey])) {
(e as HTMLElement).style.display = _config[configKey] ? '' : 'none'
}
window.llonebot.setConfig(config)
})
}
doc.getElementById("addHttpHost").addEventListener("click", () => addHostEle("http")) switchClick('http', 'enableHttp', config.ob11)
doc.getElementById("addWsHost").addEventListener("click", () => addHostEle("ws")) switchClick('httpPost', 'enableHttpPost', config.ob11)
doc.getElementById("messagePostFormat").addEventListener("selected", (e: CustomEvent) => { switchClick('websocket', 'enableWs', config.ob11)
config.ob11.messagePostFormat = e.detail && !isEmpty(e.detail.value) ? e.detail.value : 'array'; switchClick('websocketReverse', 'enableWsReverse', config.ob11)
window.llonebot.setConfig(config); switchClick('debug', 'debug')
switchClick('switchFileUrl', 'enableLocalFile2Url')
switchClick('reportSelfMessage', 'reportSelfMessage')
switchClick('log', 'log')
switchClick('autoDeleteFile', 'autoDeleteFile')
doc.getElementById('save')?.addEventListener('click',
() => {
const httpPortEle: HTMLInputElement = document.getElementById('httpPort') as HTMLInputElement
const httpHostEles: HTMLCollectionOf<HTMLInputElement> = document.getElementsByClassName('httpHost') as HTMLCollectionOf<HTMLInputElement>
const wsPortEle: HTMLInputElement = document.getElementById('wsPort') as HTMLInputElement
const wsHostEles: HTMLCollectionOf<HTMLInputElement> = document.getElementsByClassName('wsHost') as HTMLCollectionOf<HTMLInputElement>
const tokenEle = document.getElementById('token') as HTMLInputElement
const ffmpegPathEle = document.getElementById('ffmpegPath') as HTMLInputElement
// 获取端口和host
const httpPort = httpPortEle.value
const httpHosts: string[] = []
for (const hostEle of httpHostEles) {
const value = hostEle.value.trim()
value && httpHosts.push(value)
}
const wsPort = wsPortEle.value
const token = tokenEle.value.trim()
const wsHosts: string[] = []
for (const hostEle of wsHostEles) {
const value = hostEle.value.trim()
value && wsHosts.push(value)
}
config.ob11.httpPort = parseInt(httpPort)
config.ob11.httpHosts = httpHosts
config.ob11.wsPort = parseInt(wsPort)
config.ob11.wsHosts = wsHosts
config.token = token
config.ffmpeg = ffmpegPathEle.value.trim()
window.llonebot.setConfig(config)
setTimeout(() => {
getError().then()
}, 1000)
alert('保存成功')
}) })
function switchClick(eleId: string, configKey: string, _config = null) { doc.getElementById('selectFFMPEG')?.addEventListener('click', () => {
if (!_config) { window.llonebot.selectFile().then(selectPath => {
_config = config if (selectPath) {
} config.ffmpeg = (document.getElementById('ffmpegPath') as HTMLInputElement).value = selectPath
doc.getElementById(eleId)?.addEventListener("click", (e) => { // window.llonebot.setConfig(config);
const switchEle = e.target as HTMLInputElement }
if (_config[configKey]) { })
_config[configKey] = false })
switchEle.removeAttribute("is-active")
} else {
_config[configKey] = true
switchEle.setAttribute("is-active", "")
}
// 妈蛋手动操作DOM越写越麻烦要不用vue算了
const keyClassMap = {
"enableHttp": httpClass,
"enableHttpPost": httpPostClass,
"enableWs": wsClass,
"enableWsReverse": reverseWSClass,
}
for (let e of document.getElementsByClassName(keyClassMap[configKey])) {
e["style"].display = _config[configKey] ? "" : "none"
}
window.llonebot.setConfig(config) // 自动保存删除文件延时时间
}) const autoDeleteMinEle = doc.getElementById('autoDeleteMin') as HTMLInputElement
let st = null
autoDeleteMinEle.addEventListener('change', () => {
if (st) {
clearTimeout(st)
} }
st = setTimeout(() => {
console.log('auto delete file minute change')
config.autoDeleteFileSecond = parseInt(autoDeleteMinEle.value) || 1
window.llonebot.setConfig(config)
}, 1000)
})
switchClick("http", "enableHttp", config.ob11); doc.body.childNodes.forEach(node => {
switchClick("httpPost", "enableHttpPost", config.ob11); view.appendChild(node)
switchClick("websocket", "enableWs", config.ob11); })
switchClick("websocketReverse", "enableWsReverse", config.ob11);
switchClick("debug", "debug");
switchClick("switchFileUrl", "enableLocalFile2Url");
switchClick("reportSelfMessage", "reportSelfMessage");
switchClick("log", "log");
switchClick("autoDeleteFile", "autoDeleteFile");
doc.getElementById("save")?.addEventListener("click",
() => {
const httpPortEle: HTMLInputElement = document.getElementById("httpPort") as HTMLInputElement;
const httpHostEles: HTMLCollectionOf<HTMLInputElement> = document.getElementsByClassName("httpHost") as HTMLCollectionOf<HTMLInputElement>;
const wsPortEle: HTMLInputElement = document.getElementById("wsPort") as HTMLInputElement;
const wsHostEles: HTMLCollectionOf<HTMLInputElement> = document.getElementsByClassName("wsHost") as HTMLCollectionOf<HTMLInputElement>;
const tokenEle = document.getElementById("token") as HTMLInputElement;
const ffmpegPathEle = document.getElementById("ffmpegPath") as HTMLInputElement;
// 获取端口和host
const httpPort = httpPortEle.value
let httpHosts: string[] = [];
for (const hostEle of httpHostEles) {
const value = hostEle.value.trim();
value && httpHosts.push(value);
}
const wsPort = wsPortEle.value;
const token = tokenEle.value.trim();
let wsHosts: string[] = [];
for (const hostEle of wsHostEles) {
const value = hostEle.value.trim();
value && wsHosts.push(value);
}
config.ob11.httpPort = parseInt(httpPort);
config.ob11.httpHosts = httpHosts;
config.ob11.wsPort = parseInt(wsPort);
config.ob11.wsHosts = wsHosts;
config.token = token;
config.ffmpeg = ffmpegPathEle.value.trim();
window.llonebot.setConfig(config);
setTimeout(()=>{
getError().then();
}, 1000);
alert("保存成功");
})
doc.getElementById("selectFFMPEG")?.addEventListener("click", ()=>{
window.llonebot.selectFile().then(selectPath=>{
if (selectPath){
config.ffmpeg = (document.getElementById("ffmpegPath") as HTMLInputElement).value = selectPath;
// window.llonebot.setConfig(config);
}
});
})
// 自动保存删除文件延时时间
const autoDeleteMinEle = doc.getElementById("autoDeleteMin") as HTMLInputElement;
let st = null;
autoDeleteMinEle.addEventListener("change", ()=>{
if (st){
clearTimeout(st)
}
st = setTimeout(()=>{
console.log("auto delete file minute change");
config.autoDeleteFileSecond = parseInt(autoDeleteMinEle.value) || 1;
window.llonebot.setConfig(config);
}, 1000)
})
doc.body.childNodes.forEach(node => {
view.appendChild(node);
});
} }
function init () {
const hash = location.hash
if (hash === '#/blank') {
function init() { }
let hash = location.hash;
if (hash === "#/blank") {
return;
}
} }
if (location.hash === '#/blank') {
if (location.hash === "#/blank") { (window as any).navigation.addEventListener('navigatesuccess', init, { once: true })
(window as any).navigation.addEventListener("navigatesuccess", init, {once: true});
} else { } else {
init(); init()
} }
export { export {
onSettingWindowCreated onSettingWindowCreated
} }

View File

@@ -12,10 +12,10 @@
}, },
"include": [ "include": [
"src/*", "src/*",
"src/**/*",
"scripts/*" "scripts/*"
], ],
"exclude": [ "exclude": [
"node_modules", "node_modules",
"src/common/types.ts"
] ]
} }

2338
yarn.lock

File diff suppressed because it is too large Load Diff