mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
874acdd7fe | ||
![]() |
b2b996df9c | ||
![]() |
4427774c2d | ||
![]() |
41c04faa05 | ||
![]() |
6ad4492f01 | ||
![]() |
d52f16bc88 | ||
![]() |
2b0179acd1 | ||
![]() |
f540f324a1 | ||
![]() |
128f40a51d | ||
![]() |
c815e0ca6b | ||
![]() |
1da720e0a7 | ||
![]() |
1472c9c949 | ||
![]() |
4678253815 |
@@ -4,7 +4,7 @@
|
|||||||
"name": "LLOneBot",
|
"name": "LLOneBot",
|
||||||
"slug": "LLOneBot",
|
"slug": "LLOneBot",
|
||||||
"description": "实现 OneBot 11 协议,用以 QQ 机器人开发",
|
"description": "实现 OneBot 11 协议,用以 QQ 机器人开发",
|
||||||
"version": "3.28.3",
|
"version": "3.28.6",
|
||||||
"icon": "./icon.webp",
|
"icon": "./icon.webp",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"repo": "linyuchen/LiteLoaderQQNT-OneBotApi",
|
"repo": "LLOneBot/LLOneBot",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"release": {
|
"release": {
|
||||||
"tag": "latest",
|
"tag": "latest",
|
||||||
|
@@ -16,7 +16,7 @@ const manifest = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
repository: {
|
repository: {
|
||||||
repo: 'linyuchen/LiteLoaderQQNT-OneBotApi',
|
repo: 'LLOneBot/LLOneBot',
|
||||||
branch: 'main',
|
branch: 'main',
|
||||||
release: {
|
release: {
|
||||||
tag: 'latest',
|
tag: 'latest',
|
||||||
|
@@ -2,7 +2,7 @@ import fs from 'node:fs'
|
|||||||
import { Config, OB11Config } from './types'
|
import { Config, OB11Config } from './types'
|
||||||
import { mergeNewProperties } from './utils/helper'
|
import { mergeNewProperties } from './utils/helper'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { selfInfo } from './data'
|
import { getSelfUin } from './data'
|
||||||
import { DATA_DIR } from './utils'
|
import { DATA_DIR } from './utils'
|
||||||
|
|
||||||
export const HOOK_LOG = false
|
export const HOOK_LOG = false
|
||||||
@@ -97,6 +97,6 @@ export class ConfigUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getConfigUtil() {
|
export function getConfigUtil() {
|
||||||
const configFilePath = path.join(DATA_DIR, `config_${selfInfo.uin}.json`)
|
const configFilePath = path.join(DATA_DIR, `config_${getSelfUin()}.json`)
|
||||||
return new ConfigUtil(configFilePath)
|
return new ConfigUtil(configFilePath)
|
||||||
}
|
}
|
||||||
|
@@ -8,14 +8,8 @@ import { type LLOneBotError } from './types'
|
|||||||
import { NTQQGroupApi } from '../ntqqapi/api/group'
|
import { NTQQGroupApi } from '../ntqqapi/api/group'
|
||||||
import { log } from './utils/log'
|
import { log } from './utils/log'
|
||||||
import { isNumeric } from './utils/helper'
|
import { isNumeric } from './utils/helper'
|
||||||
import { NTQQFriendApi } from '../ntqqapi/api'
|
import { NTQQFriendApi, NTQQUserApi } from '../ntqqapi/api'
|
||||||
|
|
||||||
export const selfInfo: SelfInfo = {
|
|
||||||
uid: '',
|
|
||||||
uin: '',
|
|
||||||
nick: '',
|
|
||||||
online: true,
|
|
||||||
}
|
|
||||||
export let groups: Group[] = []
|
export let groups: Group[] = []
|
||||||
export let friends: Friend[] = []
|
export let friends: Friend[] = []
|
||||||
export const llonebotError: LLOneBotError = {
|
export const llonebotError: LLOneBotError = {
|
||||||
@@ -98,4 +92,39 @@ export async function getGroupMember(groupQQ: string | number, memberUinOrUid: s
|
|||||||
member = getMember()
|
member = getMember()
|
||||||
}
|
}
|
||||||
return member
|
return member
|
||||||
|
}
|
||||||
|
|
||||||
|
const selfInfo: SelfInfo = {
|
||||||
|
uid: '',
|
||||||
|
uin: '',
|
||||||
|
nick: '',
|
||||||
|
online: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSelfNick(force = false): Promise<string> {
|
||||||
|
if (!selfInfo.nick || force) {
|
||||||
|
const userInfo = await NTQQUserApi.getUserDetailInfo(selfInfo.uid)
|
||||||
|
if (userInfo) {
|
||||||
|
selfInfo.nick = userInfo.nick
|
||||||
|
return userInfo.nick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selfInfo.nick
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSelfInfo() {
|
||||||
|
return selfInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setSelfInfo(data: Partial<SelfInfo>) {
|
||||||
|
Object.assign(selfInfo, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSelfUid() {
|
||||||
|
return selfInfo['uid']
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSelfUin() {
|
||||||
|
return selfInfo['uin']
|
||||||
}
|
}
|
@@ -1,7 +1,6 @@
|
|||||||
import { Level } from 'level'
|
import { Level } from 'level'
|
||||||
import { type GroupNotify, RawMessage } from '../ntqqapi/types'
|
import { type GroupNotify, RawMessage } from '../ntqqapi/types'
|
||||||
import { DATA_DIR } from './utils'
|
import { DATA_DIR } from './utils'
|
||||||
import { selfInfo } from './data'
|
|
||||||
import { FileCache } from './types'
|
import { FileCache } from './types'
|
||||||
import { log } from './utils/log'
|
import { log } from './utils/log'
|
||||||
|
|
||||||
@@ -27,33 +26,12 @@ class DBUtil {
|
|||||||
* */
|
* */
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
let initCount = 0
|
}
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
const initDB = () => {
|
|
||||||
initCount++
|
|
||||||
// if (initCount > 50) {
|
|
||||||
// return reject("init db fail")
|
|
||||||
// }
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!selfInfo.uin) {
|
|
||||||
setTimeout(initDB, 300)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const DB_PATH = DATA_DIR + `/msg_${selfInfo.uin}`
|
|
||||||
this.db = new Level(DB_PATH, { valueEncoding: 'json' })
|
|
||||||
console.log('llonebot init db success')
|
|
||||||
resolve(null)
|
|
||||||
} catch (e: any) {
|
|
||||||
console.log('init db fail', e.stack.toString())
|
|
||||||
setTimeout(initDB, 300)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setTimeout(initDB)
|
|
||||||
}).then()
|
|
||||||
|
|
||||||
|
init(uin: string) {
|
||||||
|
const DB_PATH = DATA_DIR + `/msg_${uin}`
|
||||||
|
this.db = new Level(DB_PATH, { valueEncoding: 'json' })
|
||||||
const expiredMilliSecond = 1000 * 60 * 60
|
const expiredMilliSecond = 1000 * 60 * 60
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
// this.cache = {}
|
// this.cache = {}
|
||||||
// 清理时间较久的缓存
|
// 清理时间较久的缓存
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import fs from 'node:fs'
|
|
||||||
import os from 'node:os'
|
import os from 'node:os'
|
||||||
import { systemPlatform } from './system'
|
import { systemPlatform } from './system'
|
||||||
|
|
||||||
@@ -38,32 +37,6 @@ type QQPkgInfo = {
|
|||||||
platform: string
|
platform: string
|
||||||
eleArch: string
|
eleArch: string
|
||||||
}
|
}
|
||||||
type QQVersionConfigInfo = {
|
|
||||||
baseVersion: string
|
|
||||||
curVersion: string
|
|
||||||
prevVersion: string
|
|
||||||
onErrorVersions: Array<any>
|
|
||||||
buildId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
|
||||||
'baseVersion': '9.9.9-23361',
|
|
||||||
'curVersion': '9.9.9-23361',
|
|
||||||
'prevVersion': '',
|
|
||||||
'onErrorVersions': [],
|
|
||||||
'buildId': '23361',
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fs.existsSync(configVersionInfoPath)) {
|
|
||||||
try {
|
|
||||||
const _ = JSON.parse(fs.readFileSync(configVersionInfoPath).toString())
|
|
||||||
_qqVersionConfigInfo = Object.assign(_qqVersionConfigInfo, _)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Load QQ version config info failed, Use default version', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo
|
|
||||||
|
|
||||||
export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath)
|
export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath)
|
||||||
// platform_type: 3,
|
// platform_type: 3,
|
||||||
@@ -74,14 +47,6 @@ export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath)
|
|||||||
// platVer: '10.0.26100',
|
// platVer: '10.0.26100',
|
||||||
// clientVer: '9.9.9-23159',
|
// clientVer: '9.9.9-23159',
|
||||||
|
|
||||||
let _appid: string = '537213803' // 默认为 Windows 平台的 appid
|
|
||||||
if (systemPlatform === 'linux') {
|
|
||||||
_appid = '537213827'
|
|
||||||
}
|
|
||||||
// todo: mac 平台的 appid
|
|
||||||
export const appid = _appid
|
|
||||||
export const isQQ998: boolean = qqPkgInfo.buildVersion >= '22106'
|
|
||||||
|
|
||||||
export function getBuildVersion(): number {
|
export function getBuildVersion(): number {
|
||||||
return +qqPkgInfo.buildVersion
|
return +qqPkgInfo.buildVersion
|
||||||
}
|
}
|
@@ -25,7 +25,7 @@ export function checkFileReceived(path: string, timeout: number = 3000): Promise
|
|||||||
} else if (Date.now() - startTime > timeout) {
|
} else if (Date.now() - startTime > timeout) {
|
||||||
reject(new Error(`文件不存在: ${path}`))
|
reject(new Error(`文件不存在: ${path}`))
|
||||||
} else {
|
} else {
|
||||||
setTimeout(check, 100)
|
setTimeout(check, 200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,5 +15,4 @@ if (!fs.existsSync(TEMP_DIR)) {
|
|||||||
}
|
}
|
||||||
export { getVideoInfo } from './video'
|
export { getVideoInfo } from './video'
|
||||||
export { checkFfmpeg } from './video'
|
export { checkFfmpeg } from './video'
|
||||||
export { encodeSilk } from './audio'
|
export { encodeSilk } from './audio'
|
||||||
export { isQQ998 } from './QQBasicInfo'
|
|
@@ -1,4 +1,4 @@
|
|||||||
import { selfInfo } from '../data'
|
import { getSelfInfo } from '../data'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { DATA_DIR, truncateString } from './index'
|
import { DATA_DIR, truncateString } from './index'
|
||||||
@@ -15,7 +15,7 @@ export function log(...msg: any[]) {
|
|||||||
if (!getConfigUtil().getConfig().log) {
|
if (!getConfigUtil().getConfig().log) {
|
||||||
return //console.log(...msg);
|
return //console.log(...msg);
|
||||||
}
|
}
|
||||||
|
const selfInfo = getSelfInfo()
|
||||||
const userInfo = selfInfo.uin ? `${selfInfo.nick}(${selfInfo.uin})` : ''
|
const userInfo = selfInfo.uin ? `${selfInfo.nick}(${selfInfo.uin})` : ''
|
||||||
let logMsg = ''
|
let logMsg = ''
|
||||||
for (let msgItem of msg) {
|
for (let msgItem of msg) {
|
||||||
@@ -31,5 +31,5 @@ export function log(...msg: any[]) {
|
|||||||
logMsg = `${currentDateTime} ${userInfo}: ${logMsg}\n\n`
|
logMsg = `${currentDateTime} ${userInfo}: ${logMsg}\n\n`
|
||||||
// sendLog(...msg);
|
// sendLog(...msg);
|
||||||
// console.log(msg)
|
// console.log(msg)
|
||||||
fs.appendFile(path.join(logDir, logFileName), logMsg, (err: any) => {})
|
fs.appendFile(path.join(logDir, logFileName), logMsg, () => {})
|
||||||
}
|
}
|
||||||
|
100
src/main/main.ts
100
src/main/main.ts
@@ -17,7 +17,10 @@ import { DATA_DIR } from '../common/utils'
|
|||||||
import {
|
import {
|
||||||
getGroupMember,
|
getGroupMember,
|
||||||
llonebotError,
|
llonebotError,
|
||||||
selfInfo,
|
setSelfInfo,
|
||||||
|
getSelfInfo,
|
||||||
|
getSelfUid,
|
||||||
|
getSelfUin
|
||||||
} from '../common/data'
|
} from '../common/data'
|
||||||
import { hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmdS, registerReceiveHook, startHook } from '../ntqqapi/hook'
|
import { hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmdS, registerReceiveHook, startHook } from '../ntqqapi/hook'
|
||||||
import { OB11Constructor } from '../onebot11/constructor'
|
import { OB11Constructor } from '../onebot11/constructor'
|
||||||
@@ -36,7 +39,7 @@ import { OB11FriendRequestEvent } from '../onebot11/event/request/OB11FriendRequ
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { dbUtil } from '../common/db'
|
import { dbUtil } from '../common/db'
|
||||||
import { setConfig } from './setConfig'
|
import { setConfig } from './setConfig'
|
||||||
import { NTQQUserApi, NTQQGroupApi, sentMessages } from '../ntqqapi/api'
|
import { NTQQUserApi, NTQQGroupApi } from '../ntqqapi/api'
|
||||||
import { checkNewVersion, upgradeLLOneBot } from '../common/utils/upgrade'
|
import { checkNewVersion, upgradeLLOneBot } from '../common/utils/upgrade'
|
||||||
import { log } from '../common/utils/log'
|
import { log } from '../common/utils/log'
|
||||||
import { getConfigUtil } from '../common/config'
|
import { getConfigUtil } from '../common/config'
|
||||||
@@ -50,7 +53,6 @@ let mainWindow: BrowserWindow | null = null
|
|||||||
|
|
||||||
// 加载插件时触发
|
// 加载插件时触发
|
||||||
function onLoad() {
|
function onLoad() {
|
||||||
log('llonebot main onLoad')
|
|
||||||
ipcMain.handle(CHANNEL_CHECK_VERSION, async (event, arg) => {
|
ipcMain.handle(CHANNEL_CHECK_VERSION, async (event, arg) => {
|
||||||
return checkNewVersion()
|
return checkNewVersion()
|
||||||
})
|
})
|
||||||
@@ -160,7 +162,7 @@ function onLoad() {
|
|||||||
if (!debug && msg.message.length === 0) {
|
if (!debug && msg.message.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const isSelfMsg = msg.user_id.toString() == selfInfo.uin
|
const isSelfMsg = msg.user_id.toString() === getSelfUin()
|
||||||
if (isSelfMsg && !reportSelfMessage) {
|
if (isSelfMsg && !reportSelfMessage) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -208,10 +210,6 @@ function onLoad() {
|
|||||||
const recallMsgIds: string[] = [] // 避免重复上报
|
const recallMsgIds: string[] = [] // 避免重复上报
|
||||||
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.UPDATE_MSG], async (payload) => {
|
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.UPDATE_MSG], async (payload) => {
|
||||||
for (const message of payload.msgList) {
|
for (const message of payload.msgList) {
|
||||||
const sentMessage = sentMessages[message.msgId]
|
|
||||||
if (sentMessage) {
|
|
||||||
Object.assign(sentMessage, message)
|
|
||||||
}
|
|
||||||
log('message update', message.msgId, message)
|
log('message update', message.msgId, message)
|
||||||
if (message.recallTime != '0') {
|
if (message.recallTime != '0') {
|
||||||
if (recallMsgIds.includes(message.msgId)) {
|
if (recallMsgIds.includes(message.msgId)) {
|
||||||
@@ -282,39 +280,6 @@ function onLoad() {
|
|||||||
log('收到群通知', notify)
|
log('收到群通知', notify)
|
||||||
await dbUtil.addGroupNotify(notify)
|
await dbUtil.addGroupNotify(notify)
|
||||||
const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type
|
const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type
|
||||||
// let member2: GroupMember;
|
|
||||||
// if (notify.user2.uid) {
|
|
||||||
// member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
|
|
||||||
// }
|
|
||||||
// 原本的群管变更通知事件处理
|
|
||||||
// if (
|
|
||||||
// [GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET, GroupNotifyTypes.ADMIN_UNSET_OTHER].includes(
|
|
||||||
// notify.type,
|
|
||||||
// )
|
|
||||||
// ) {
|
|
||||||
// const member1 = await getGroupMember(notify.group.groupCode, notify.user1.uid)
|
|
||||||
// log('有管理员变动通知')
|
|
||||||
// refreshGroupMembers(notify.group.groupCode).then()
|
|
||||||
// let groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent()
|
|
||||||
// groupAdminNoticeEvent.group_id = parseInt(notify.group.groupCode)
|
|
||||||
// log('开始获取变动的管理员')
|
|
||||||
// if (member1) {
|
|
||||||
// log('变动管理员获取成功')
|
|
||||||
// groupAdminNoticeEvent.user_id = parseInt(member1.uin)
|
|
||||||
// groupAdminNoticeEvent.sub_type = [
|
|
||||||
// GroupNotifyTypes.ADMIN_UNSET,
|
|
||||||
// GroupNotifyTypes.ADMIN_UNSET_OTHER,
|
|
||||||
// ].includes(notify.type)
|
|
||||||
// ? 'unset'
|
|
||||||
// : 'set'
|
|
||||||
// // member1.role = notify.type == GroupNotifyTypes.ADMIN_SET ? GroupMemberRole.admin : GroupMemberRole.normal;
|
|
||||||
// postOb11Event(groupAdminNoticeEvent, true)
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// log('获取群通知的成员信息失败', notify, getGroup(notify.group.groupCode))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
if (notify.type == GroupNotifyTypes.MEMBER_EXIT || notify.type == GroupNotifyTypes.KICK_MEMBER) {
|
if (notify.type == GroupNotifyTypes.MEMBER_EXIT || notify.type == GroupNotifyTypes.KICK_MEMBER) {
|
||||||
log('有成员退出通知', notify)
|
log('有成员退出通知', notify)
|
||||||
try {
|
try {
|
||||||
@@ -423,7 +388,7 @@ function onLoad() {
|
|||||||
|
|
||||||
let startTime = 0 // 毫秒
|
let startTime = 0 // 毫秒
|
||||||
|
|
||||||
async function start() {
|
async function start(uid: string, uin: string) {
|
||||||
log('llonebot pid', process.pid)
|
log('llonebot pid', process.pid)
|
||||||
const config = getConfigUtil().getConfig()
|
const config = getConfigUtil().getConfig()
|
||||||
if (!config.enableLLOB) {
|
if (!config.enableLLOB) {
|
||||||
@@ -433,6 +398,8 @@ function onLoad() {
|
|||||||
llonebotError.otherError = ''
|
llonebotError.otherError = ''
|
||||||
startTime = Date.now()
|
startTime = Date.now()
|
||||||
NTEventDispatch.init({ ListenerMap: wrapperConstructor, WrapperSession: getSession()! })
|
NTEventDispatch.init({ ListenerMap: wrapperConstructor, WrapperSession: getSession()! })
|
||||||
|
dbUtil.init(uin)
|
||||||
|
|
||||||
log('start activate group member info')
|
log('start activate group member info')
|
||||||
NTQQGroupApi.activateMemberInfoChange().then().catch(log)
|
NTQQGroupApi.activateMemberInfoChange().then().catch(log)
|
||||||
NTQQGroupApi.activateMemberListChange().then().catch(log)
|
NTQQGroupApi.activateMemberListChange().then().catch(log)
|
||||||
@@ -454,54 +421,29 @@ function onLoad() {
|
|||||||
log('LLOneBot start')
|
log('LLOneBot start')
|
||||||
}
|
}
|
||||||
|
|
||||||
let getSelfNickCount = 0
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
try {
|
const current = getSelfInfo()
|
||||||
log('start get self info')
|
if (!current.uin) {
|
||||||
const _ = await NTQQUserApi.getSelfInfo()
|
setSelfInfo({
|
||||||
log('get self info api result:', _)
|
uin: globalThis.authData?.uin,
|
||||||
Object.assign(selfInfo, _)
|
uid: globalThis.authData?.uid,
|
||||||
selfInfo.nick = selfInfo.uin
|
nick: current.uin,
|
||||||
} catch (e) {
|
})
|
||||||
log('retry get self info', e)
|
|
||||||
}
|
}
|
||||||
if (!selfInfo.uin) {
|
//log('self info', selfInfo, globalThis.authData)
|
||||||
selfInfo.uin = globalThis.authData?.uin
|
if (current.uin) {
|
||||||
selfInfo.uid = globalThis.authData?.uid
|
start(current.uid, current.uin)
|
||||||
selfInfo.nick = selfInfo.uin
|
|
||||||
}
|
|
||||||
log('self info', selfInfo, globalThis.authData)
|
|
||||||
if (selfInfo.uin) {
|
|
||||||
async function getUserNick() {
|
|
||||||
try {
|
|
||||||
getSelfNickCount++
|
|
||||||
const userInfo = await NTQQUserApi.getUserDetailInfo(selfInfo.uid)
|
|
||||||
log('self info', userInfo)
|
|
||||||
if (userInfo) {
|
|
||||||
selfInfo.nick = userInfo.nick
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
log('get self nickname failed', e.stack)
|
|
||||||
}
|
|
||||||
if (getSelfNickCount < 10) {
|
|
||||||
return setTimeout(getUserNick, 1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserNick().then()
|
|
||||||
start().then()
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setTimeout(init, 1000)
|
setTimeout(init, 1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTimeout(init, 1000)
|
init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建窗口时触发
|
// 创建窗口时触发
|
||||||
function onBrowserWindowCreated(window: BrowserWindow) {
|
function onBrowserWindowCreated(window: BrowserWindow) {
|
||||||
if (selfInfo.uid) {
|
if (getSelfUid()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mainWindow = window
|
mainWindow = window
|
||||||
|
@@ -9,15 +9,21 @@ import {
|
|||||||
ChatType,
|
ChatType,
|
||||||
ElementType,
|
ElementType,
|
||||||
IMAGE_HTTP_HOST,
|
IMAGE_HTTP_HOST,
|
||||||
IMAGE_HTTP_HOST_NT, PicElement,
|
IMAGE_HTTP_HOST_NT,
|
||||||
|
PicElement,
|
||||||
} from '../types'
|
} from '../types'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import { ReceiveCmdS } from '../hook'
|
import { ReceiveCmdS } from '../hook'
|
||||||
import { log } from '@/common/utils'
|
import { log, TEMP_DIR } from '@/common/utils'
|
||||||
import { rkeyManager } from '@/ntqqapi/api/rkey'
|
import { rkeyManager } from '@/ntqqapi/api/rkey'
|
||||||
import { getSession } from '@/ntqqapi/wrapper'
|
import { getSession } from '@/ntqqapi/wrapper'
|
||||||
import { Peer } from '@/ntqqapi/types/msg'
|
import { Peer } from '@/ntqqapi/types/msg'
|
||||||
|
import { calculateFileMD5 } from '@/common/utils/file'
|
||||||
|
import { fileTypeFromFile } from 'file-type'
|
||||||
|
import fsPromise from 'node:fs/promises'
|
||||||
|
import { NTEventDispatch } from '@/common/utils/EventTask'
|
||||||
|
import { OnRichMediaDownloadCompleteParams } from '@/ntqqapi/listeners'
|
||||||
|
|
||||||
export class NTQQFileApi {
|
export class NTQQFileApi {
|
||||||
static async getVideoUrl(peer: Peer, msgId: string, elementId: string): Promise<string> {
|
static async getVideoUrl(peer: Peer, msgId: string, elementId: string): Promise<string> {
|
||||||
@@ -30,19 +36,7 @@ export class NTQQFileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async getFileType(filePath: string) {
|
static async getFileType(filePath: string) {
|
||||||
return await callNTQQApi<{ ext: string }>({
|
return fileTypeFromFile(filePath)
|
||||||
className: NTQQApiClass.FS_API,
|
|
||||||
methodName: NTQQApiMethod.FILE_TYPE,
|
|
||||||
args: [filePath],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getFileMd5(filePath: string) {
|
|
||||||
return await callNTQQApi<string>({
|
|
||||||
className: NTQQApiClass.FS_API,
|
|
||||||
methodName: NTQQApiMethod.FILE_MD5,
|
|
||||||
args: [filePath],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async copyFile(filePath: string, destPath: string) {
|
static async copyFile(filePath: string, destPath: string) {
|
||||||
@@ -67,44 +61,35 @@ export class NTQQFileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 上传文件到QQ的文件夹
|
// 上传文件到QQ的文件夹
|
||||||
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
|
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType = 0) {
|
||||||
const md5 = await NTQQFileApi.getFileMd5(filePath)
|
const fileMd5 = await calculateFileMD5(filePath)
|
||||||
let ext = (await NTQQFileApi.getFileType(filePath))?.ext
|
let ext = (await NTQQFileApi.getFileType(filePath))?.ext || ''
|
||||||
if (ext) {
|
if (ext) {
|
||||||
ext = '.' + ext
|
ext = '.' + ext
|
||||||
} else {
|
|
||||||
ext = ''
|
|
||||||
}
|
}
|
||||||
let fileName = `${path.basename(filePath)}`
|
let fileName = `${path.basename(filePath)}`
|
||||||
if (fileName.indexOf('.') === -1) {
|
if (fileName.indexOf('.') === -1) {
|
||||||
fileName += ext
|
fileName += ext
|
||||||
}
|
}
|
||||||
const mediaPath = await callNTQQApi<string>({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.MEDIA_FILE_PATH,
|
const mediaPath = session?.getMsgService().getRichMediaFilePathForGuild({
|
||||||
args: [
|
md5HexStr: fileMd5,
|
||||||
{
|
fileName: fileName,
|
||||||
path_info: {
|
elementType: elementType,
|
||||||
md5HexStr: md5,
|
elementSubType,
|
||||||
fileName: fileName,
|
thumbSize: 0,
|
||||||
elementType: elementType,
|
needCreate: true,
|
||||||
elementSubType,
|
downloadType: 1,
|
||||||
thumbSize: 0,
|
file_uuid: ''
|
||||||
needCreate: true,
|
|
||||||
downloadType: 1,
|
|
||||||
file_uuid: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
log('media path', mediaPath)
|
await fsPromise.copyFile(filePath, mediaPath!)
|
||||||
await NTQQFileApi.copyFile(filePath, mediaPath)
|
const fileSize = (await fsPromise.stat(filePath)).size
|
||||||
const fileSize = await NTQQFileApi.getFileSize(filePath)
|
|
||||||
return {
|
return {
|
||||||
md5,
|
md5: fileMd5,
|
||||||
fileName,
|
fileName,
|
||||||
path: mediaPath,
|
path: mediaPath!,
|
||||||
fileSize,
|
fileSize,
|
||||||
ext,
|
ext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,44 +100,67 @@ export class NTQQFileApi {
|
|||||||
elementId: string,
|
elementId: string,
|
||||||
thumbPath: string,
|
thumbPath: string,
|
||||||
sourcePath: string,
|
sourcePath: string,
|
||||||
force: boolean = false,
|
timeout = 1000 * 60 * 2,
|
||||||
|
force = false
|
||||||
) {
|
) {
|
||||||
// 用于下载收到的消息中的图片等
|
// 用于下载收到的消息中的图片等
|
||||||
if (sourcePath && fs.existsSync(sourcePath)) {
|
if (sourcePath && fs.existsSync(sourcePath)) {
|
||||||
if (force) {
|
if (force) {
|
||||||
fs.unlinkSync(sourcePath)
|
try {
|
||||||
|
await fsPromise.unlink(sourcePath)
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return sourcePath
|
return sourcePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const apiParams = [
|
const data = await NTEventDispatch.CallNormalEvent<
|
||||||
|
(
|
||||||
|
params: {
|
||||||
|
fileModelId: string,
|
||||||
|
downloadSourceType: number,
|
||||||
|
triggerType: number,
|
||||||
|
msgId: string,
|
||||||
|
chatType: ChatType,
|
||||||
|
peerUid: string,
|
||||||
|
elementId: string,
|
||||||
|
thumbSize: number,
|
||||||
|
downloadType: number,
|
||||||
|
filePath: string
|
||||||
|
}) => Promise<unknown>,
|
||||||
|
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/downloadRichMedia',
|
||||||
|
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(arg: OnRichMediaDownloadCompleteParams) => {
|
||||||
|
if (arg.msgId === msgId) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
getReq: {
|
fileModelId: '0',
|
||||||
fileModelId: '0',
|
downloadSourceType: 0,
|
||||||
downloadSourceType: 0,
|
triggerType: 1,
|
||||||
triggerType: 1,
|
msgId: msgId,
|
||||||
msgId: msgId,
|
chatType: chatType,
|
||||||
chatType: chatType,
|
peerUid: peerUid,
|
||||||
peerUid: peerUid,
|
elementId: elementId,
|
||||||
elementId: elementId,
|
thumbSize: 0,
|
||||||
thumbSize: 0,
|
downloadType: 1,
|
||||||
downloadType: 1,
|
filePath: thumbPath
|
||||||
filePath: thumbPath,
|
}
|
||||||
},
|
)
|
||||||
},
|
let filePath = data[1].filePath
|
||||||
null,
|
if (filePath.startsWith('\\')) {
|
||||||
]
|
const downloadPath = TEMP_DIR
|
||||||
// log("需要下载media", sourcePath);
|
filePath = path.join(downloadPath, filePath)
|
||||||
await callNTQQApi({
|
// 下载路径是下载文件夹的相对路径
|
||||||
methodName: NTQQApiMethod.DOWNLOAD_MEDIA,
|
}
|
||||||
args: apiParams,
|
return filePath
|
||||||
cbCmd: ReceiveCmdS.MEDIA_DOWNLOAD_COMPLETE,
|
|
||||||
cmdCB: (payload: { notifyInfo: { filePath: string; msgId: string } }) => {
|
|
||||||
log('media 下载完成判断', payload.notifyInfo.msgId, msgId)
|
|
||||||
return payload.notifyInfo.msgId == msgId
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return sourcePath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getImageSize(filePath: string) {
|
static async getImageSize(filePath: string) {
|
||||||
@@ -163,22 +171,27 @@ export class NTQQFileApi {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getImageUrl(picElement: PicElement, chatType: ChatType) {
|
static async getImageUrl(element: PicElement) {
|
||||||
const isPrivateImage = chatType !== ChatType.group
|
if (!element) {
|
||||||
const url = picElement.originImageUrl // 没有域名
|
return ''
|
||||||
const md5HexStr = picElement.md5HexStr
|
}
|
||||||
const fileMd5 = picElement.md5HexStr
|
const url: string = element.originImageUrl! // 没有域名
|
||||||
const fileUuid = picElement.fileUuid
|
const md5HexStr = element.md5HexStr
|
||||||
|
const fileMd5 = element.md5HexStr
|
||||||
|
const fileUuid = element.fileUuid
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
if (url.startsWith('/download')) {
|
const UrlParse = new URL(IMAGE_HTTP_HOST + url) //临时解析拼接
|
||||||
// console.log('rkey', rkey);
|
const imageAppid = UrlParse.searchParams.get('appid')
|
||||||
if (url.includes('&rkey=')) {
|
const isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid)
|
||||||
|
if (isNewPic) {
|
||||||
|
let UrlRkey = UrlParse.searchParams.get('rkey')
|
||||||
|
if (UrlRkey) {
|
||||||
return IMAGE_HTTP_HOST_NT + url
|
return IMAGE_HTTP_HOST_NT + url
|
||||||
}
|
}
|
||||||
|
const rkeyData = await rkeyManager.getRkey()
|
||||||
const rkeyData = await rkeyManager.getRkey();
|
UrlRkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey
|
||||||
const existsRKey = isPrivateImage ? rkeyData.private_rkey : rkeyData.group_rkey;
|
return IMAGE_HTTP_HOST_NT + url + `${UrlRkey}`
|
||||||
return IMAGE_HTTP_HOST_NT + url + `${existsRKey}`
|
|
||||||
} else {
|
} else {
|
||||||
// 老的图片url,不需要rkey
|
// 老的图片url,不需要rkey
|
||||||
return IMAGE_HTTP_HOST + url
|
return IMAGE_HTTP_HOST + url
|
||||||
@@ -187,7 +200,7 @@ export class NTQQFileApi {
|
|||||||
// 没有url,需要自己拼接
|
// 没有url,需要自己拼接
|
||||||
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`
|
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`
|
||||||
}
|
}
|
||||||
log('图片url获取失败', picElement)
|
log('图片url获取失败', element)
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,93 +1,35 @@
|
|||||||
import { callNTQQApi, GeneralCallResult, NTQQApiMethod } from '../ntcall'
|
import { callNTQQApi, GeneralCallResult, NTQQApiMethod } from '../ntcall'
|
||||||
import { ChatType, RawMessage, SendMessageElement, Peer, ChatType2 } from '../types'
|
import { RawMessage, SendMessageElement, Peer, ChatType2 } from '../types'
|
||||||
import { dbUtil } from '../../common/db'
|
import { getSelfNick, getSelfUid } from '../../common/data'
|
||||||
import { selfInfo } from '../../common/data'
|
import { getBuildVersion } from '../../common/utils'
|
||||||
import { ReceiveCmdS, registerReceiveHook } from '../hook'
|
|
||||||
import { log } from '../../common/utils/log'
|
|
||||||
import { sleep } from '../../common/utils/helper'
|
|
||||||
import { isQQ998, getBuildVersion } from '../../common/utils'
|
|
||||||
import { getSession } from '@/ntqqapi/wrapper'
|
import { getSession } from '@/ntqqapi/wrapper'
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask'
|
import { NTEventDispatch } from '@/common/utils/EventTask'
|
||||||
|
|
||||||
export let sendMessagePool: Record<string, ((sendSuccessMsg: RawMessage) => void) | null> = {} // peerUid: callbackFunc
|
|
||||||
|
|
||||||
export let sentMessages: Record<string, RawMessage> = {} // msgId: RawMessage
|
|
||||||
|
|
||||||
async function sendWaiter(peer: Peer, waitComplete = true, timeout: number = 10000) {
|
|
||||||
// 等待上一个相同的peer发送完
|
|
||||||
const peerUid = peer.peerUid
|
|
||||||
let checkLastSendUsingTime = 0
|
|
||||||
const waitLastSend = async () => {
|
|
||||||
if (checkLastSendUsingTime > timeout) {
|
|
||||||
throw '发送超时'
|
|
||||||
}
|
|
||||||
let lastSending = sendMessagePool[peer.peerUid]
|
|
||||||
if (lastSending) {
|
|
||||||
// log("有正在发送的消息,等待中...")
|
|
||||||
await sleep(500)
|
|
||||||
checkLastSendUsingTime += 500
|
|
||||||
return await waitLastSend()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await waitLastSend()
|
|
||||||
|
|
||||||
let sentMessage: RawMessage | null = null
|
|
||||||
sendMessagePool[peerUid] = async (rawMessage: RawMessage) => {
|
|
||||||
delete sendMessagePool[peerUid]
|
|
||||||
sentMessage = rawMessage
|
|
||||||
sentMessages[rawMessage.msgId] = rawMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
let checkSendCompleteUsingTime = 0
|
|
||||||
const checkSendComplete = async (): Promise<RawMessage> => {
|
|
||||||
if (sentMessage) {
|
|
||||||
if (waitComplete) {
|
|
||||||
if (sentMessage.sendStatus == 2) {
|
|
||||||
delete sentMessages[sentMessage.msgId]
|
|
||||||
return sentMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
delete sentMessages[sentMessage.msgId]
|
|
||||||
return sentMessage
|
|
||||||
}
|
|
||||||
// log(`给${peerUid}发送消息成功`)
|
|
||||||
}
|
|
||||||
checkSendCompleteUsingTime += 500
|
|
||||||
if (checkSendCompleteUsingTime > timeout) {
|
|
||||||
throw '发送超时'
|
|
||||||
}
|
|
||||||
await sleep(500)
|
|
||||||
return await checkSendComplete()
|
|
||||||
}
|
|
||||||
return checkSendComplete()
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NTQQMsgApi {
|
export class NTQQMsgApi {
|
||||||
static async getTempChatInfo(chatType: ChatType2, peerUid: string) {
|
static async getTempChatInfo(chatType: ChatType2, peerUid: string) {
|
||||||
const session = getSession()
|
const session = getSession()
|
||||||
return session?.getMsgService().getTempChatInfo(chatType, peerUid)!
|
return session?.getMsgService().getTempChatInfo(chatType, peerUid)!
|
||||||
}
|
}
|
||||||
|
|
||||||
static enterOrExitAIO(peer: Peer, enter: boolean) {
|
static async prepareTempChat(toUserUid: string, GroupCode: string, nickname: string) {
|
||||||
return callNTQQApi<GeneralCallResult>({
|
//By Jadx/Ida Mlikiowa
|
||||||
methodName: NTQQApiMethod.ENTER_OR_EXIT_AIO,
|
let TempGameSession = {
|
||||||
args: [
|
nickname: '',
|
||||||
{
|
gameAppId: '',
|
||||||
"info_list": [
|
selfTinyId: '',
|
||||||
{
|
peerRoleId: '',
|
||||||
peer,
|
peerOpenId: '',
|
||||||
"option": enter ? 1 : 2
|
}
|
||||||
}
|
const session = getSession()
|
||||||
]
|
return session?.getMsgService().prepareTempChat({
|
||||||
},
|
chatType: ChatType2.KCHATTYPETEMPC2CFROMGROUP,
|
||||||
{
|
peerUid: toUserUid,
|
||||||
"send": true
|
peerNickname: nickname,
|
||||||
},
|
fromGroupCode: GroupCode,
|
||||||
],
|
sig: '',
|
||||||
|
selfPhone: '',
|
||||||
|
selfUid: getSelfUid(),
|
||||||
|
gameSession: TempGameSession
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,47 +38,13 @@ export class NTQQMsgApi {
|
|||||||
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
||||||
// 其实以官方文档为准是最好的,https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
|
// 其实以官方文档为准是最好的,https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
|
||||||
emojiId = emojiId.toString()
|
emojiId = emojiId.toString()
|
||||||
return await callNTQQApi<GeneralCallResult>({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.EMOJI_LIKE,
|
return session?.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set)
|
||||||
args: [
|
|
||||||
{
|
|
||||||
peer,
|
|
||||||
msgSeq,
|
|
||||||
emojiId,
|
|
||||||
emojiType: emojiId.length > 3 ? '2' : '1',
|
|
||||||
setEmoji: set,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string) {
|
static async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string) {
|
||||||
return await callNTQQApi<GeneralCallResult & { msgList: RawMessage[] }>({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.GET_MULTI_MSG,
|
return session?.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId)!
|
||||||
args: [
|
|
||||||
{
|
|
||||||
peer,
|
|
||||||
rootMsgId,
|
|
||||||
parentMsgId,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getMsgBoxInfo(peer: Peer) {
|
|
||||||
return await callNTQQApi<GeneralCallResult>({
|
|
||||||
methodName: NTQQApiMethod.GET_MSG_BOX_INFO,
|
|
||||||
args: [
|
|
||||||
{
|
|
||||||
contacts: [
|
|
||||||
peer
|
|
||||||
],
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async activateChat(peer: Peer) {
|
static async activateChat(peer: Peer) {
|
||||||
@@ -158,80 +66,80 @@ export class NTQQMsgApi {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getMsgHistory(peer: Peer, msgId: string, count: number) {
|
static async getMsgsByMsgId(peer: Peer | undefined, msgIds: string[] | undefined) {
|
||||||
// 消息时间从旧到新
|
if (!peer) throw new Error('peer is not allowed')
|
||||||
return await callNTQQApi<GeneralCallResult & { msgList: RawMessage[] }>({
|
if (!msgIds) throw new Error('msgIds is not allowed')
|
||||||
methodName: isQQ998 ? NTQQApiMethod.ACTIVE_CHAT_HISTORY : NTQQApiMethod.HISTORY_MSG,
|
const session = getSession()
|
||||||
args: [
|
//Mlikiowa: 参数不合规会导致NC异常崩溃 原因是TX未对进入参数判断 对应Android标记@NotNull AndroidJADX分析可得
|
||||||
{
|
return await session?.getMsgService().getMsgsByMsgId(peer, msgIds)!
|
||||||
peer,
|
|
||||||
msgId,
|
|
||||||
cnt: count,
|
|
||||||
queryOrder: true,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fetchRecentContact() {
|
static async getMsgHistory(peer: Peer, msgId: string, count: number, isReverseOrder: boolean = false) {
|
||||||
await callNTQQApi({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.RECENT_CONTACT,
|
// 消息时间从旧到新
|
||||||
args: [
|
return session?.getMsgService().getMsgsIncludeSelf(peer, msgId, count, isReverseOrder)!
|
||||||
{
|
|
||||||
fetchParam: {
|
|
||||||
anchorPointContact: {
|
|
||||||
contactId: '',
|
|
||||||
sortField: '',
|
|
||||||
pos: 0,
|
|
||||||
},
|
|
||||||
relativeMoveCount: 0,
|
|
||||||
listType: 2, // 1普通消息,2群助手内的消息
|
|
||||||
count: 200,
|
|
||||||
fetchOld: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async recallMsg(peer: Peer, msgIds: string[]) {
|
static async recallMsg(peer: Peer, msgIds: string[]) {
|
||||||
return await callNTQQApi({
|
const session = getSession()
|
||||||
methodName: NTQQApiMethod.RECALL_MSG,
|
return await session?.getMsgService().recallMsg({
|
||||||
args: [
|
chatType: peer.chatType,
|
||||||
{
|
peerUid: peer.peerUid
|
||||||
peer,
|
}, msgIds)
|
||||||
msgIds,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
if (getBuildVersion() >= 26702) {
|
function generateMsgId() {
|
||||||
return NTQQMsgApi.sendMsgV2(peer, msgElements, waitComplete, timeout)
|
const timestamp = Math.floor(Date.now() / 1000)
|
||||||
|
const random = Math.floor(Math.random() * Math.pow(2, 32))
|
||||||
|
const buffer = Buffer.alloc(8)
|
||||||
|
buffer.writeUInt32BE(timestamp, 0)
|
||||||
|
buffer.writeUInt32BE(random, 4)
|
||||||
|
const msgId = BigInt("0x" + buffer.toString('hex')).toString()
|
||||||
|
return msgId
|
||||||
}
|
}
|
||||||
const waiter = sendWaiter(peer, waitComplete, timeout)
|
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||||
callNTQQApi({
|
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||||
methodName: NTQQApiMethod.SEND_MSG,
|
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||||
args: [
|
let msgId: string
|
||||||
{
|
try {
|
||||||
msgId: '0',
|
msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime())
|
||||||
peer,
|
} catch (error) {
|
||||||
msgElements,
|
//if (!napCatCore.session.getMsgService()['generateMsgUniqueId'])
|
||||||
msgAttributeInfos: new Map(),
|
//兜底识别策略V2
|
||||||
},
|
msgId = generateMsgId()
|
||||||
null,
|
}
|
||||||
],
|
peer.guildId = msgId
|
||||||
}).then()
|
const data = await NTEventDispatch.CallNormalEvent<
|
||||||
return await waiter
|
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||||
|
(msgList: RawMessage[]) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/sendMsg',
|
||||||
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(msgRecords: RawMessage[]) => {
|
||||||
|
for (let msgRecord of msgRecords) {
|
||||||
|
if (msgRecord.guildId === msgId && msgRecord.sendStatus === 2) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
'0',
|
||||||
|
peer,
|
||||||
|
msgElements,
|
||||||
|
new Map()
|
||||||
|
)
|
||||||
|
const retMsg = data[1].find(msgRecord => {
|
||||||
|
if (msgRecord.guildId === msgId) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return retMsg!
|
||||||
}
|
}
|
||||||
|
|
||||||
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
if (peer.chatType === ChatType.temp) {
|
|
||||||
//await NTQQMsgApi.PrepareTempChat().then().catch()
|
|
||||||
}
|
|
||||||
function generateMsgId() {
|
function generateMsgId() {
|
||||||
const timestamp = Math.floor(Date.now() / 1000)
|
const timestamp = Math.floor(Date.now() / 1000)
|
||||||
const random = Math.floor(Math.random() * Math.pow(2, 32))
|
const random = Math.floor(Math.random() * Math.pow(2, 32))
|
||||||
@@ -295,74 +203,67 @@ export class NTQQMsgApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||||
const waiter = sendWaiter(destPeer, true, 10000)
|
const session = getSession()
|
||||||
callNTQQApi<GeneralCallResult>({
|
return session?.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], [])!
|
||||||
methodName: NTQQApiMethod.FORWARD_MSG,
|
|
||||||
args: [
|
|
||||||
{
|
|
||||||
msgIds: msgIds,
|
|
||||||
srcContact: srcPeer,
|
|
||||||
dstContacts: [destPeer],
|
|
||||||
commentElements: [],
|
|
||||||
msgAttributeInfos: new Map(),
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
}).then().catch(log)
|
|
||||||
return await waiter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
||||||
const msgInfos = msgIds.map((id) => {
|
const senderShowName = await getSelfNick()
|
||||||
return { msgId: id, senderShowName: selfInfo.nick }
|
const msgInfos = msgIds.map(id => {
|
||||||
|
return { msgId: id, senderShowName }
|
||||||
})
|
})
|
||||||
const apiArgs = [
|
const selfUid = getSelfUid()
|
||||||
{
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
msgInfos,
|
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>,) => Promise<unknown>,
|
||||||
srcContact: srcPeer,
|
(msgList: RawMessage[]) => void
|
||||||
dstContact: destPeer,
|
>(
|
||||||
commentElements: [],
|
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||||
msgAttributeInfos: new Map(),
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
(msgRecords: RawMessage[]) => {
|
||||||
|
for (let msgRecord of msgRecords) {
|
||||||
|
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == selfUid) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
null,
|
msgInfos,
|
||||||
]
|
srcPeer,
|
||||||
return await new Promise<RawMessage>((resolve, reject) => {
|
destPeer,
|
||||||
let complete = false
|
[],
|
||||||
setTimeout(() => {
|
new Map()
|
||||||
if (!complete) {
|
)
|
||||||
reject('转发消息超时')
|
for (let msg of data[1]) {
|
||||||
}
|
const arkElement = msg.elements.find(ele => ele.arkElement)
|
||||||
}, 5000)
|
if (!arkElement) {
|
||||||
registerReceiveHook(ReceiveCmdS.SELF_SEND_MSG, async (payload: { msgRecord: RawMessage }) => {
|
continue
|
||||||
const msg = payload.msgRecord
|
}
|
||||||
// 需要判断它是转发的消息,并且识别到是当前转发的这一条
|
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData)
|
||||||
const arkElement = msg.elements.find((ele) => ele.arkElement)
|
if (forwardData.app != 'com.tencent.multimsg') {
|
||||||
if (!arkElement) {
|
continue
|
||||||
// log("收到的不是转发消息")
|
}
|
||||||
return
|
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfUid) {
|
||||||
}
|
return msg
|
||||||
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData)
|
}
|
||||||
if (forwardData.app != 'com.tencent.multimsg') {
|
}
|
||||||
return
|
throw new Error('转发消息超时')
|
||||||
}
|
}
|
||||||
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
|
||||||
complete = true
|
static async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
|
||||||
await dbUtil.addMsg(msg)
|
const session = getSession()
|
||||||
resolve(msg)
|
const ret = await session?.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||||
log('转发消息成功:', payload)
|
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
|
||||||
}
|
filterMsgType: [],
|
||||||
})
|
filterSendersUid: [],
|
||||||
callNTQQApi<GeneralCallResult>({
|
filterMsgToTime: '0',
|
||||||
methodName: NTQQApiMethod.MULTI_FORWARD_MSG,
|
filterMsgFromTime: '0',
|
||||||
args: apiArgs,
|
isReverseOrder: false,
|
||||||
}).then((result) => {
|
isIncludeCurrent: true,
|
||||||
log('转发消息结果:', result, apiArgs)
|
pageLimit: 1,
|
||||||
if (result.result !== 0) {
|
|
||||||
complete = true
|
|
||||||
reject('转发消息失败,' + JSON.stringify(result))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
return ret!
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { callNTQQApi, GeneralCallResult, NTQQApiClass, NTQQApiMethod } from '../ntcall'
|
import { callNTQQApi, GeneralCallResult, NTQQApiClass, NTQQApiMethod } from '../ntcall'
|
||||||
import { SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '../types'
|
import { SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '../types'
|
||||||
import { ReceiveCmdS } from '../hook'
|
import { ReceiveCmdS } from '../hook'
|
||||||
import { selfInfo, friends, groupMembers } from '@/common/data'
|
import { friends, groupMembers, getSelfUin } from '@/common/data'
|
||||||
import { CacheClassFuncAsync, isQQ998, log, sleep, getBuildVersion } from '@/common/utils'
|
import { CacheClassFuncAsync, log, getBuildVersion } from '@/common/utils'
|
||||||
import { getSession } from '@/ntqqapi/wrapper'
|
import { getSession } from '@/ntqqapi/wrapper'
|
||||||
import { RequestUtil } from '@/common/utils/request'
|
import { RequestUtil } from '@/common/utils/request'
|
||||||
import { NodeIKernelProfileService, UserDetailSource, ProfileBizType } from '../services'
|
import { NodeIKernelProfileService, UserDetailSource, ProfileBizType } from '../services'
|
||||||
@@ -10,8 +10,6 @@ import { NodeIKernelProfileListener } from '../listeners'
|
|||||||
import { NTEventDispatch } from '@/common/utils/EventTask'
|
import { NTEventDispatch } from '@/common/utils/EventTask'
|
||||||
import { NTQQFriendApi } from './friend'
|
import { NTQQFriendApi } from './friend'
|
||||||
|
|
||||||
const userInfoCache: Record<string, User> = {} // uid: User
|
|
||||||
|
|
||||||
export class NTQQUserApi {
|
export class NTQQUserApi {
|
||||||
static async setQQAvatar(filePath: string) {
|
static async setQQAvatar(filePath: string) {
|
||||||
return await callNTQQApi<GeneralCallResult>({
|
return await callNTQQApi<GeneralCallResult>({
|
||||||
@@ -85,39 +83,25 @@ export class NTQQUserApi {
|
|||||||
if (getBuildVersion() >= 26702) {
|
if (getBuildVersion() >= 26702) {
|
||||||
return this.fetchUserDetailInfo(uid)
|
return this.fetchUserDetailInfo(uid)
|
||||||
}
|
}
|
||||||
// this.getUserInfo(uid)
|
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo']
|
||||||
let methodName = !isQQ998 ? NTQQApiMethod.USER_DETAIL_INFO : NTQQApiMethod.USER_DETAIL_INFO_WITH_BIZ_INFO
|
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged']
|
||||||
if (!withBizInfo) {
|
const [_retData, profile] = await NTEventDispatch.CallNormalEvent
|
||||||
methodName = NTQQApiMethod.USER_DETAIL_INFO
|
<EventService, EventListener>
|
||||||
}
|
(
|
||||||
const fetchInfo = async () => {
|
'NodeIKernelProfileService/getUserDetailInfoWithBizInfo',
|
||||||
const result = await callNTQQApi<{ info: User }>({
|
'NodeIKernelProfileListener/onProfileDetailInfoChanged',
|
||||||
methodName,
|
2,
|
||||||
cbCmd: ReceiveCmdS.USER_DETAIL_INFO,
|
5000,
|
||||||
afterFirstCmd: false,
|
(profile: User) => {
|
||||||
cmdCB: (payload) => {
|
if (profile.uid === uid) {
|
||||||
const success = payload.info.uid == uid
|
return true
|
||||||
// log("get user detail info", success, uid, payload)
|
}
|
||||||
return success
|
return false
|
||||||
},
|
},
|
||||||
args: [
|
uid,
|
||||||
{
|
[0]
|
||||||
uid,
|
)
|
||||||
},
|
return profile
|
||||||
null,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
const info = result.info
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
// 首次请求两次才能拿到的等级信息
|
|
||||||
if (!userInfoCache[uid] && getLevel) {
|
|
||||||
await fetchInfo()
|
|
||||||
await sleep(1000)
|
|
||||||
}
|
|
||||||
const userInfo = await fetchInfo()
|
|
||||||
userInfoCache[uid] = userInfo
|
|
||||||
return userInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return 'p_uin=o0xxx; p_skey=orXDssiGF8axxxxxxxxxxxxxx_; skey='
|
// return 'p_uin=o0xxx; p_skey=orXDssiGF8axxxxxxxxxxxxxx_; skey='
|
||||||
@@ -134,7 +118,8 @@ export class NTQQUserApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async getQzoneCookies() {
|
static async getQzoneCookies() {
|
||||||
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin + '&clientkey=' + (await this.getClientKey()).clientKey + '&u1=https%3A%2F%2Fuser.qzone.qq.com%2F' + selfInfo.uin + '%2Finfocenter&keyindex=19%27'
|
const uin = getSelfUin()
|
||||||
|
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + uin + '&clientkey=' + (await this.getClientKey()).clientKey + '&u1=https%3A%2F%2Fuser.qzone.qq.com%2F' + uin + '%2Finfocenter&keyindex=19%27'
|
||||||
let cookies: { [key: string]: string } = {}
|
let cookies: { [key: string]: string } = {}
|
||||||
try {
|
try {
|
||||||
cookies = await RequestUtil.HttpsGetCookies(requestUrl)
|
cookies = await RequestUtil.HttpsGetCookies(requestUrl)
|
||||||
@@ -149,7 +134,7 @@ export class NTQQUserApi {
|
|||||||
if (clientKeyData.result !== 0) {
|
if (clientKeyData.result !== 0) {
|
||||||
throw new Error('获取clientKey失败')
|
throw new Error('获取clientKey失败')
|
||||||
}
|
}
|
||||||
const url = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin
|
const url = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + getSelfUin()
|
||||||
+ '&clientkey=' + clientKeyData.clientKey
|
+ '&clientkey=' + clientKeyData.clientKey
|
||||||
+ '&u1=https%3A%2F%2Fh5.qzone.qq.com%2Fqqnt%2Fqzoneinpcqq%2Ffriend%3Frefresh%3D0%26clientuin%3D0%26darkMode%3D0&keyindex=' + clientKeyData.keyIndex
|
+ '&u1=https%3A%2F%2Fh5.qzone.qq.com%2Fqqnt%2Fqzoneinpcqq%2Ffriend%3Frefresh%3D0%26clientuin%3D0%26darkMode%3D0&keyindex=' + clientKeyData.keyIndex
|
||||||
return (await RequestUtil.HttpsGetCookies(url))?.skey
|
return (await RequestUtil.HttpsGetCookies(url))?.skey
|
||||||
@@ -158,7 +143,8 @@ export class NTQQUserApi {
|
|||||||
@CacheClassFuncAsync(1800 * 1000)
|
@CacheClassFuncAsync(1800 * 1000)
|
||||||
static async getCookies(domain: string) {
|
static async getCookies(domain: string) {
|
||||||
const ClientKeyData = await NTQQUserApi.forceFetchClientKey()
|
const ClientKeyData = await NTQQUserApi.forceFetchClientKey()
|
||||||
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin + '&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + selfInfo.uin + '%2Finfocenter&keyindex=19%27'
|
const uin = getSelfUin()
|
||||||
|
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + uin + '&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + uin + '%2Finfocenter&keyindex=19%27'
|
||||||
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl)
|
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl)
|
||||||
return cookies
|
return cookies
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { selfInfo } from '@/common/data'
|
import { getSelfUin } from '@/common/data'
|
||||||
import { log } from '@/common/utils/log'
|
import { log } from '@/common/utils/log'
|
||||||
import { NTQQUserApi } from './user'
|
import { NTQQUserApi } from './user'
|
||||||
import { RequestUtil } from '@/common/utils/request'
|
import { RequestUtil } from '@/common/utils/request'
|
||||||
@@ -192,49 +192,47 @@ export class WebApi {
|
|||||||
static async setGroupNotice(GroupCode: string, Content: string = '') {
|
static async setGroupNotice(GroupCode: string, Content: string = '') {
|
||||||
//https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn=${bkn}
|
//https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn=${bkn}
|
||||||
//qid=${群号}&bkn=${bkn}&text=${内容}&pinned=0&type=1&settings={"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}
|
//qid=${群号}&bkn=${bkn}&text=${内容}&pinned=0&type=1&settings={"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}
|
||||||
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com']))['qun.qq.com'];
|
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com']))['qun.qq.com']
|
||||||
const _Skey = await NTQQUserApi.getSkey();
|
const _Skey = await NTQQUserApi.getSkey()
|
||||||
const CookieValue = 'p_skey=' + _Pskey + '; skey=' + _Skey + '; p_uin=o' + selfInfo.uin;
|
const CookieValue = 'p_skey=' + _Pskey + '; skey=' + _Skey + '; p_uin=o' + getSelfUin()
|
||||||
let ret: any = undefined;
|
let ret: any = undefined
|
||||||
//console.log(CookieValue);
|
//console.log(CookieValue)
|
||||||
if (!_Skey || !_Pskey) {
|
if (!_Skey || !_Pskey) {
|
||||||
//获取Cookies失败
|
//获取Cookies失败
|
||||||
return undefined;
|
return undefined
|
||||||
}
|
}
|
||||||
const Bkn = WebApi.genBkn(_Skey);
|
const Bkn = WebApi.genBkn(_Skey)
|
||||||
const data = 'qid=' + GroupCode + '&bkn=' + Bkn + '&text=' + Content + '&pinned=0&type=1&settings={"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}';
|
const data = 'qid=' + GroupCode + '&bkn=' + Bkn + '&text=' + Content + '&pinned=0&type=1&settings={"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}'
|
||||||
const url = 'https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn=' + Bkn;
|
const url = 'https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn=' + Bkn
|
||||||
try {
|
try {
|
||||||
ret = await RequestUtil.HttpGetJson<any>(url, 'GET', '', { 'Cookie': CookieValue });
|
ret = await RequestUtil.HttpGetJson<any>(url, 'GET', '', { 'Cookie': CookieValue })
|
||||||
return ret;
|
return ret
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined;
|
return undefined
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getGrouptNotice(GroupCode: string): Promise<undefined | WebApiGroupNoticeRet> {
|
static async getGrouptNotice(GroupCode: string): Promise<undefined | WebApiGroupNoticeRet> {
|
||||||
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com']))['qun.qq.com'];
|
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com']))['qun.qq.com']
|
||||||
const _Skey = await NTQQUserApi.getSkey();
|
const _Skey = await NTQQUserApi.getSkey()
|
||||||
const CookieValue = 'p_skey=' + _Pskey + '; skey=' + _Skey + '; p_uin=o' + selfInfo.uin;
|
const CookieValue = 'p_skey=' + _Pskey + '; skey=' + _Skey + '; p_uin=o' + getSelfUin()
|
||||||
let ret: WebApiGroupNoticeRet | undefined = undefined;
|
let ret: WebApiGroupNoticeRet | undefined = undefined
|
||||||
//console.log(CookieValue);
|
//console.log(CookieValue)
|
||||||
if (!_Skey || !_Pskey) {
|
if (!_Skey || !_Pskey) {
|
||||||
//获取Cookies失败
|
//获取Cookies失败
|
||||||
return undefined;
|
return undefined
|
||||||
}
|
}
|
||||||
const Bkn = WebApi.genBkn(_Skey);
|
const Bkn = WebApi.genBkn(_Skey)
|
||||||
const url = 'https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=' + Bkn + '&qid=' + GroupCode + '&ft=23&ni=1&n=1&i=1&log_read=1&platform=1&s=-1&n=20';
|
const url = 'https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=' + Bkn + '&qid=' + GroupCode + '&ft=23&ni=1&n=1&i=1&log_read=1&platform=1&s=-1&n=20'
|
||||||
try {
|
try {
|
||||||
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(url, 'GET', '', { 'Cookie': CookieValue });
|
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(url, 'GET', '', { 'Cookie': CookieValue })
|
||||||
if (ret?.ec !== 0) {
|
if (ret?.ec !== 0) {
|
||||||
return undefined;
|
return undefined
|
||||||
}
|
}
|
||||||
return ret;
|
return ret
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined;
|
return undefined
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static genBkn(sKey: string) {
|
static genBkn(sKey: string) {
|
||||||
|
@@ -175,7 +175,6 @@ export class SendMsgElementConstructor {
|
|||||||
|
|
||||||
setTimeout(useDefaultThumb, 5000)
|
setTimeout(useDefaultThumb, 5000)
|
||||||
ffmpeg(filePath)
|
ffmpeg(filePath)
|
||||||
.on('end', () => { })
|
|
||||||
.on('error', (err) => {
|
.on('error', (err) => {
|
||||||
if (diyThumbPath) {
|
if (diyThumbPath) {
|
||||||
fs.copyFile(diyThumbPath, thumbPath)
|
fs.copyFile(diyThumbPath, thumbPath)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import type { BrowserWindow } from 'electron'
|
import type { BrowserWindow } from 'electron'
|
||||||
import { NTQQApiClass, NTQQApiMethod } from './ntcall'
|
import { NTQQApiClass, NTQQApiMethod } from './ntcall'
|
||||||
import { NTQQMsgApi, sendMessagePool } from './api/msg'
|
import { NTQQMsgApi } from './api/msg'
|
||||||
import { CategoryFriend, ChatType, Group, GroupMember, GroupMemberRole, RawMessage } from './types'
|
import { CategoryFriend, ChatType, Group, GroupMember, GroupMemberRole, RawMessage } from './types'
|
||||||
import {
|
import {
|
||||||
deleteGroup,
|
deleteGroup,
|
||||||
@@ -8,7 +8,8 @@ import {
|
|||||||
getFriend,
|
getFriend,
|
||||||
getGroupMember,
|
getGroupMember,
|
||||||
groups,
|
groups,
|
||||||
selfInfo,
|
getSelfUin,
|
||||||
|
setSelfInfo
|
||||||
} from '@/common/data'
|
} from '@/common/data'
|
||||||
import { OB11GroupDecreaseEvent } from '../onebot11/event/notice/OB11GroupDecreaseEvent'
|
import { OB11GroupDecreaseEvent } from '../onebot11/event/notice/OB11GroupDecreaseEvent'
|
||||||
import { postOb11Event } from '../onebot11/server/post-ob11-event'
|
import { postOb11Event } from '../onebot11/server/post-ob11-event'
|
||||||
@@ -242,18 +243,7 @@ async function updateGroups(_groups: Group[], needUpdate: boolean = true) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log('update group', group)
|
log('update group', group)
|
||||||
// if (!activatedGroups.includes(group.groupCode)) {
|
NTQQMsgApi.activateChat({ peerUid: group.groupCode, chatType: ChatType.group }).then().catch(log)
|
||||||
NTQQMsgApi.activateChat({ peerUid: group.groupCode, chatType: ChatType.group })
|
|
||||||
.then((r) => {
|
|
||||||
// activatedGroups.push(group.groupCode);
|
|
||||||
// log(`激活群聊天窗口${group.groupName}(${group.groupCode})`, r)
|
|
||||||
// if (r.result !== 0) {
|
|
||||||
// setTimeout(() => NTQQMsgApi.activateGroupChat(group.groupCode).then(r => log(`再次激活群聊天窗口${group.groupName}(${group.groupCode})`, r)), 500);
|
|
||||||
// }else {
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
.catch(log)
|
|
||||||
// }
|
|
||||||
let existGroup = groups.find((g) => g.groupCode == group.groupCode)
|
let existGroup = groups.find((g) => g.groupCode == group.groupCode)
|
||||||
if (existGroup) {
|
if (existGroup) {
|
||||||
Object.assign(existGroup, group)
|
Object.assign(existGroup, group)
|
||||||
@@ -293,12 +283,13 @@ async function processGroupEvent(payload: { groupList: Group[] }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 判断bot是否是管理员,如果是管理员不需要从这里得知有人退群,这里的退群无法得知是主动退群还是被踢
|
// 判断bot是否是管理员,如果是管理员不需要从这里得知有人退群,这里的退群无法得知是主动退群还是被踢
|
||||||
let bot = await getGroupMember(group.groupCode, selfInfo.uin)
|
const selfUin = getSelfUin()
|
||||||
|
const bot = await getGroupMember(group.groupCode, selfUin)
|
||||||
if (bot?.role == GroupMemberRole.admin || bot?.role == GroupMemberRole.owner) {
|
if (bot?.role == GroupMemberRole.admin || bot?.role == GroupMemberRole.owner) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (const member of oldMembers) {
|
for (const member of oldMembers) {
|
||||||
if (!newMembersSet.has(member.uin) && member.uin != selfInfo.uin) {
|
if (!newMembersSet.has(member.uin) && member.uin != selfUin) {
|
||||||
postOb11Event(
|
postOb11Event(
|
||||||
new OB11GroupDecreaseEvent(
|
new OB11GroupDecreaseEvent(
|
||||||
parseInt(group.groupCode),
|
parseInt(group.groupCode),
|
||||||
@@ -454,22 +445,13 @@ export async function startHook() {
|
|||||||
|
|
||||||
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({ msgRecord }) => {
|
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({ msgRecord }) => {
|
||||||
const message = msgRecord
|
const message = msgRecord
|
||||||
const peerUid = message.peerUid
|
|
||||||
// log("收到自己发送成功的消息", Object.keys(sendMessagePool), message);
|
|
||||||
// log("收到自己发送成功的消息", message.msgId, message.msgSeq);
|
|
||||||
dbUtil.addMsg(message).then()
|
dbUtil.addMsg(message).then()
|
||||||
const sendCallback = sendMessagePool[peerUid]
|
|
||||||
if (sendCallback) {
|
|
||||||
try {
|
|
||||||
sendCallback(message)
|
|
||||||
} catch (e: any) {
|
|
||||||
log('receive self msg error', e.stack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
|
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
|
||||||
selfInfo.online = info.info.status !== 20
|
setSelfInfo({
|
||||||
|
online: info.info.status !== 20
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
let activatedPeerUids: string[] = []
|
let activatedPeerUids: string[] = []
|
||||||
|
@@ -462,6 +462,7 @@ export interface RawMessage {
|
|||||||
senderUin?: string // 发送者QQ号
|
senderUin?: string // 发送者QQ号
|
||||||
peerUid: string // 群号 或者 QQ uid
|
peerUid: string // 群号 或者 QQ uid
|
||||||
peerUin: string // 群号 或者 发送者QQ号
|
peerUin: string // 群号 或者 发送者QQ号
|
||||||
|
guildId: string
|
||||||
sendNickName: string
|
sendNickName: string
|
||||||
sendMemberName?: string // 发送者群名片
|
sendMemberName?: string // 发送者群名片
|
||||||
chatType: ChatType
|
chatType: ChatType
|
||||||
|
@@ -37,7 +37,7 @@ export abstract class GetFileBase extends BaseAction<GetFilePayload, GetFileResp
|
|||||||
let element = this.getElement(msg, cache.elementId)
|
let element = this.getElement(msg, cache.elementId)
|
||||||
log('找到了文件 element', element)
|
log('找到了文件 element', element)
|
||||||
// 构建下载函数
|
// 构建下载函数
|
||||||
await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, cache.elementId, '', '', true)
|
await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, cache.elementId, '', '')
|
||||||
// 等待文件下载完成
|
// 等待文件下载完成
|
||||||
msg = await dbUtil.getMsgByLongId(cache.msgId)
|
msg = await dbUtil.getMsgByLongId(cache.msgId)
|
||||||
log('下载完成后的msg', msg)
|
log('下载完成后的msg', msg)
|
||||||
|
@@ -11,11 +11,7 @@ interface Payload {
|
|||||||
user_id?: number | string
|
user_id?: number | string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Response {
|
abstract class ForwardSingleMsg extends BaseAction<Payload, null> {
|
||||||
message_id: number
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class ForwardSingleMsg extends BaseAction<Payload, Response> {
|
|
||||||
protected async getTargetPeer(payload: Payload): Promise<Peer> {
|
protected async getTargetPeer(payload: Payload): Promise<Peer> {
|
||||||
if (payload.user_id) {
|
if (payload.user_id) {
|
||||||
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString())
|
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString())
|
||||||
@@ -27,13 +23,13 @@ abstract class ForwardSingleMsg extends BaseAction<Payload, Response> {
|
|||||||
return { chatType: ChatType.group, peerUid: payload.group_id!.toString() }
|
return { chatType: ChatType.group, peerUid: payload.group_id!.toString() }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _handle(payload: Payload): Promise<Response> {
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
const msg = await dbUtil.getMsgByShortId(payload.message_id)
|
const msg = await dbUtil.getMsgByShortId(payload.message_id)
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
throw new Error(`无法找到消息${payload.message_id}`)
|
throw new Error(`无法找到消息${payload.message_id}`)
|
||||||
}
|
}
|
||||||
const peer = await this.getTargetPeer(payload)
|
const peer = await this.getTargetPeer(payload)
|
||||||
const sentMsg = await NTQQMsgApi.forwardMsg(
|
const ret = await NTQQMsgApi.forwardMsg(
|
||||||
{
|
{
|
||||||
chatType: msg.chatType,
|
chatType: msg.chatType,
|
||||||
peerUid: msg.peerUid,
|
peerUid: msg.peerUid,
|
||||||
@@ -41,8 +37,10 @@ abstract class ForwardSingleMsg extends BaseAction<Payload, Response> {
|
|||||||
peer,
|
peer,
|
||||||
[msg.msgId],
|
[msg.msgId],
|
||||||
)
|
)
|
||||||
const ob11MsgId = await dbUtil.addMsg(sentMsg)
|
if (ret.result !== 0) {
|
||||||
return { message_id: ob11MsgId! }
|
throw new Error(`转发消息失败 ${ret.errMsg}`)
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ import {
|
|||||||
RawMessage,
|
RawMessage,
|
||||||
SendMessageElement,
|
SendMessageElement,
|
||||||
} from '../../../ntqqapi/types'
|
} from '../../../ntqqapi/types'
|
||||||
import { getGroup, getGroupMember, selfInfo } from '../../../common/data'
|
import { getGroup, getGroupMember, getSelfUid, getSelfUin } from '../../../common/data'
|
||||||
import {
|
import {
|
||||||
OB11MessageCustomMusic,
|
OB11MessageCustomMusic,
|
||||||
OB11MessageData,
|
OB11MessageData,
|
||||||
@@ -101,7 +101,7 @@ export async function createSendElements(
|
|||||||
remainAtAllCount = (await NTQQGroupApi.getGroupAtAllRemainCount(groupCode)).atInfo
|
remainAtAllCount = (await NTQQGroupApi.getGroupAtAllRemainCount(groupCode)).atInfo
|
||||||
.RemainAtAllCountForUin
|
.RemainAtAllCountForUin
|
||||||
log(`群${groupCode}剩余at全体次数`, remainAtAllCount)
|
log(`群${groupCode}剩余at全体次数`, remainAtAllCount)
|
||||||
const self = await getGroupMember(groupCode, selfInfo.uin)
|
const self = await getGroupMember(groupCode, getSelfUin())
|
||||||
isAdmin = self?.role === GroupMemberRole.admin || self?.role === GroupMemberRole.owner
|
isAdmin = self?.role === GroupMemberRole.admin || self?.role === GroupMemberRole.owner
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
@@ -457,7 +457,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
const nodeMsg = await NTQQMsgApi.sendMsg(
|
const nodeMsg = await NTQQMsgApi.sendMsg(
|
||||||
{
|
{
|
||||||
chatType: ChatType.friend,
|
chatType: ChatType.friend,
|
||||||
peerUid: selfInfo.uid,
|
peerUid: getSelfUid(),
|
||||||
},
|
},
|
||||||
sendElements,
|
sendElements,
|
||||||
true,
|
true,
|
||||||
@@ -473,7 +473,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
private async handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[]) {
|
private async handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[]) {
|
||||||
const selfPeer = {
|
const selfPeer = {
|
||||||
chatType: ChatType.friend,
|
chatType: ChatType.friend,
|
||||||
peerUid: selfInfo.uid,
|
peerUid: getSelfUid(),
|
||||||
}
|
}
|
||||||
let nodeMsgIds: string[] = []
|
let nodeMsgIds: string[] = []
|
||||||
// 先判断一遍是不是id和自定义混用
|
// 先判断一遍是不是id和自定义混用
|
||||||
@@ -489,7 +489,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
nodeMsgIds.push(nodeMsg?.msgId!)
|
nodeMsgIds.push(nodeMsg?.msgId!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (nodeMsg?.peerUid !== selfInfo.uid) {
|
if (nodeMsg?.peerUid !== selfPeer.peerUid) {
|
||||||
const cloneMsg = await this.cloneMsg(nodeMsg!)
|
const cloneMsg = await this.cloneMsg(nodeMsg!)
|
||||||
if (cloneMsg) {
|
if (cloneMsg) {
|
||||||
nodeMsgIds.push(cloneMsg.msgId)
|
nodeMsgIds.push(cloneMsg.msgId)
|
||||||
@@ -562,7 +562,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
if (needSendSelf) {
|
if (needSendSelf) {
|
||||||
log('需要克隆转发消息')
|
log('需要克隆转发消息')
|
||||||
for (const [index, msg] of nodeMsgArray.entries()) {
|
for (const [index, msg] of nodeMsgArray.entries()) {
|
||||||
if (msg.peerUid !== selfInfo.uid) {
|
if (msg.peerUid !== selfPeer.peerUid) {
|
||||||
const cloneMsg = await this.cloneMsg(msg)
|
const cloneMsg = await this.cloneMsg(msg)
|
||||||
if (cloneMsg) {
|
if (cloneMsg) {
|
||||||
nodeMsgIds[index] = cloneMsg.msgId
|
nodeMsgIds[index] = cloneMsg.msgId
|
||||||
@@ -583,13 +583,9 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
if (nodeMsgIds.length === 0) {
|
if (nodeMsgIds.length === 0) {
|
||||||
throw Error('转发消息失败,节点为空')
|
throw Error('转发消息失败,节点为空')
|
||||||
}
|
}
|
||||||
try {
|
const returnMsg = await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds)
|
||||||
log('开发转发', nodeMsgIds)
|
returnMsg.msgShortId = await dbUtil.addMsg(returnMsg)
|
||||||
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds)
|
return returnMsg
|
||||||
} catch (e) {
|
|
||||||
log('forward failed', e)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { OB11User } from '../../types'
|
import { OB11User } from '../../types'
|
||||||
import { OB11Constructor } from '../../constructor'
|
import { OB11Constructor } from '../../constructor'
|
||||||
import { selfInfo } from '../../../common/data'
|
import { getSelfInfo, getSelfNick } from '../../../common/data'
|
||||||
import BaseAction from '../BaseAction'
|
import BaseAction from '../BaseAction'
|
||||||
import { ActionName } from '../types'
|
import { ActionName } from '../types'
|
||||||
|
|
||||||
@@ -8,7 +8,10 @@ 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({
|
||||||
|
...getSelfInfo(),
|
||||||
|
nick: await getSelfNick(true)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
import BaseAction from '../BaseAction'
|
import BaseAction from '../BaseAction'
|
||||||
import { OB11Status } from '../../types'
|
import { OB11Status } from '../../types'
|
||||||
import { ActionName } from '../types'
|
import { ActionName } from '../types'
|
||||||
import { selfInfo } from '../../../common/data'
|
import { getSelfInfo } 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: getSelfInfo().online!,
|
||||||
good: true,
|
good: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,7 @@ import {
|
|||||||
FriendV2,
|
FriendV2,
|
||||||
ChatType2
|
ChatType2
|
||||||
} from '../ntqqapi/types'
|
} from '../ntqqapi/types'
|
||||||
import { deleteGroup, getGroupMember, selfInfo } from '../common/data'
|
import { deleteGroup, getGroupMember, getSelfUin } from '../common/data'
|
||||||
import { EventType } from './event/OB11BaseEvent'
|
import { EventType } from './event/OB11BaseEvent'
|
||||||
import { encodeCQCode } from './cqcode'
|
import { encodeCQCode } from './cqcode'
|
||||||
import { dbUtil } from '../common/db'
|
import { dbUtil } from '../common/db'
|
||||||
@@ -60,8 +60,9 @@ export class OB11Constructor {
|
|||||||
debug,
|
debug,
|
||||||
ob11: { messagePostFormat },
|
ob11: { messagePostFormat },
|
||||||
} = config
|
} = config
|
||||||
|
const selfUin = getSelfUin()
|
||||||
const resMsg: OB11Message = {
|
const resMsg: OB11Message = {
|
||||||
self_id: parseInt(selfInfo.uin),
|
self_id: parseInt(selfUin),
|
||||||
user_id: parseInt(msg.senderUin!),
|
user_id: parseInt(msg.senderUin!),
|
||||||
time: parseInt(msg.msgTime) || Date.now(),
|
time: parseInt(msg.msgTime) || Date.now(),
|
||||||
message_id: msg.msgShortId!,
|
message_id: msg.msgShortId!,
|
||||||
@@ -78,7 +79,7 @@ export class OB11Constructor {
|
|||||||
sub_type: 'friend',
|
sub_type: 'friend',
|
||||||
message: messagePostFormat === 'string' ? '' : [],
|
message: messagePostFormat === 'string' ? '' : [],
|
||||||
message_format: messagePostFormat === 'string' ? 'string' : 'array',
|
message_format: messagePostFormat === 'string' ? 'string' : 'array',
|
||||||
post_type: selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
|
post_type: selfUin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
|
||||||
}
|
}
|
||||||
if (debug) {
|
if (debug) {
|
||||||
resMsg.raw = msg
|
resMsg.raw = msg
|
||||||
@@ -179,7 +180,7 @@ export class OB11Constructor {
|
|||||||
// message_data["data"]["path"] = element.picElement.sourcePath
|
// message_data["data"]["path"] = element.picElement.sourcePath
|
||||||
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
||||||
|
|
||||||
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement, msg.chatType)
|
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement)
|
||||||
// message_data["data"]["file_id"] = element.picElement.fileUuid
|
// message_data["data"]["file_id"] = element.picElement.fileUuid
|
||||||
message_data['data']['file_size'] = element.picElement.fileSize
|
message_data['data']['file_size'] = element.picElement.fileSize
|
||||||
dbUtil
|
dbUtil
|
||||||
@@ -425,6 +426,7 @@ export class OB11Constructor {
|
|||||||
log(`收到我被踢出或退群提示, 群${msg.peerUid}`, groupElement)
|
log(`收到我被踢出或退群提示, 群${msg.peerUid}`, groupElement)
|
||||||
deleteGroup(msg.peerUid)
|
deleteGroup(msg.peerUid)
|
||||||
NTQQGroupApi.quitGroup(msg.peerUid).then()
|
NTQQGroupApi.quitGroup(msg.peerUid).then()
|
||||||
|
const selfUin = getSelfUin()
|
||||||
try {
|
try {
|
||||||
const adminUin =
|
const adminUin =
|
||||||
(await getGroupMember(msg.peerUid, groupElement.adminUid))?.uin ||
|
(await getGroupMember(msg.peerUid, groupElement.adminUid))?.uin ||
|
||||||
@@ -432,13 +434,13 @@ export class OB11Constructor {
|
|||||||
if (adminUin) {
|
if (adminUin) {
|
||||||
return new OB11GroupDecreaseEvent(
|
return new OB11GroupDecreaseEvent(
|
||||||
parseInt(msg.peerUid),
|
parseInt(msg.peerUid),
|
||||||
parseInt(selfInfo.uin),
|
parseInt(selfUin),
|
||||||
parseInt(adminUin),
|
parseInt(adminUin),
|
||||||
'kick_me',
|
'kick_me',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return new OB11GroupDecreaseEvent(parseInt(msg.peerUid), parseInt(selfInfo.uin), 0, 'leave')
|
return new OB11GroupDecreaseEvent(parseInt(msg.peerUid), parseInt(selfUin), 0, 'leave')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -565,7 +567,7 @@ export class OB11Constructor {
|
|||||||
const postMsg = await dbUtil.getMsgBySeqId(origMsg?.msgSeq!) ?? origMsg
|
const postMsg = await dbUtil.getMsgBySeqId(origMsg?.msgSeq!) ?? origMsg
|
||||||
// 如果 senderUin 为 0,可能是 历史消息 或 自身消息
|
// 如果 senderUin 为 0,可能是 历史消息 或 自身消息
|
||||||
if (msgList[0].senderUin === '0') {
|
if (msgList[0].senderUin === '0') {
|
||||||
msgList[0].senderUin = postMsg?.senderUin ?? selfInfo.uin
|
msgList[0].senderUin = postMsg?.senderUin ?? getSelfUin()
|
||||||
}
|
}
|
||||||
return new OB11GroupEssenceEvent(parseInt(msg.peerUid), postMsg?.msgShortId!, parseInt(msgList[0].senderUin!))
|
return new OB11GroupEssenceEvent(parseInt(msg.peerUid), postMsg?.msgShortId!, parseInt(msgList[0].senderUin!))
|
||||||
// 获取MsgSeq+Peer可获取具体消息
|
// 获取MsgSeq+Peer可获取具体消息
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { selfInfo } from '../../common/data'
|
import { getSelfUin } from '../../common/data'
|
||||||
|
|
||||||
export enum EventType {
|
export enum EventType {
|
||||||
META = 'meta_event',
|
META = 'meta_event',
|
||||||
@@ -10,6 +10,6 @@ export enum EventType {
|
|||||||
|
|
||||||
export abstract class OB11BaseEvent {
|
export abstract class OB11BaseEvent {
|
||||||
time = Math.floor(Date.now() / 1000)
|
time = Math.floor(Date.now() / 1000)
|
||||||
self_id = parseInt(selfInfo.uin)
|
self_id = parseInt(getSelfUin())
|
||||||
abstract post_type: EventType
|
abstract post_type: EventType
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { Response } from 'express'
|
import { Response } from 'express'
|
||||||
import { OB11Response } from '../action/OB11Response'
|
import { OB11Response } from '../action/OB11Response'
|
||||||
import { HttpServerBase } from '@/common/server/http'
|
import { HttpServerBase } from '@/common/server/http'
|
||||||
import { actionHandlers, actionMap } from '../action'
|
import { actionMap } from '../action'
|
||||||
import { getConfigUtil } from '@/common/config'
|
import { getConfigUtil } from '@/common/config'
|
||||||
import { postOb11Event } from './post-ob11-event'
|
import { postOb11Event } from './post-ob11-event'
|
||||||
import { OB11HeartbeatEvent } from '../event/meta/OB11HeartbeatEvent'
|
import { OB11HeartbeatEvent } from '../event/meta/OB11HeartbeatEvent'
|
||||||
import { selfInfo } from '@/common/data'
|
import { getSelfInfo } from '@/common/data'
|
||||||
|
|
||||||
class OB11HTTPServer extends HttpServerBase {
|
class OB11HTTPServer extends HttpServerBase {
|
||||||
name = 'LLOneBot server'
|
name = 'LLOneBot server'
|
||||||
@@ -40,7 +40,7 @@ class HTTPHeart {
|
|||||||
}
|
}
|
||||||
this.intervalId = setInterval(() => {
|
this.intervalId = setInterval(() => {
|
||||||
// ws的心跳是ws自己维护的
|
// ws的心跳是ws自己维护的
|
||||||
postOb11Event(new OB11HeartbeatEvent(selfInfo.online!, true, heartInterval!), false, false)
|
postOb11Event(new OB11HeartbeatEvent(getSelfInfo().online!, true, heartInterval!), false, false)
|
||||||
}, heartInterval)
|
}, heartInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { OB11Message } from '../types'
|
import { OB11Message } from '../types'
|
||||||
import { selfInfo } from '@/common/data'
|
import { getSelfUin } 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'
|
||||||
@@ -35,9 +35,10 @@ export function postWsEvent(event: PostEventType) {
|
|||||||
|
|
||||||
export function postOb11Event(msg: PostEventType, reportSelf = false, postWs = true) {
|
export function postOb11Event(msg: PostEventType, reportSelf = false, postWs = true) {
|
||||||
const config = getConfigUtil().getConfig()
|
const config = getConfigUtil().getConfig()
|
||||||
|
const selfUin = getSelfUin()
|
||||||
// 判断msg是否是event
|
// 判断msg是否是event
|
||||||
if (!config.reportSelfMessage && !reportSelf) {
|
if (!config.reportSelfMessage && !reportSelf) {
|
||||||
if (msg.post_type === 'message' && (msg as OB11Message).user_id.toString() == selfInfo.uin) {
|
if (msg.post_type === 'message' && (msg as OB11Message).user_id.toString() == selfUin) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +49,7 @@ export function postOb11Event(msg: PostEventType, reportSelf = false, postWs = t
|
|||||||
const sig = hmac.digest('hex')
|
const sig = hmac.digest('hex')
|
||||||
let headers = {
|
let headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'x-self-id': selfInfo.uin,
|
'x-self-id': selfUin,
|
||||||
}
|
}
|
||||||
if (config.ob11.httpSecret) {
|
if (config.ob11.httpSecret) {
|
||||||
headers['x-signature'] = 'sha1=' + sig
|
headers['x-signature'] = 'sha1=' + sig
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { selfInfo } from '../../../common/data'
|
import { getSelfInfo } from '../../../common/data'
|
||||||
import { LifeCycleSubType, OB11LifeCycleEvent } from '../../event/meta/OB11LifeCycleEvent'
|
import { LifeCycleSubType, OB11LifeCycleEvent } from '../../event/meta/OB11LifeCycleEvent'
|
||||||
import { ActionName } from '../../action/types'
|
import { ActionName } from '../../action/types'
|
||||||
import { OB11Response } from '../../action/OB11Response'
|
import { OB11Response } from '../../action/OB11Response'
|
||||||
@@ -78,6 +78,7 @@ export class ReverseWebsocket {
|
|||||||
|
|
||||||
private connect() {
|
private connect() {
|
||||||
const { token, heartInterval } = getConfigUtil().getConfig()
|
const { token, heartInterval } = getConfigUtil().getConfig()
|
||||||
|
const selfInfo = getSelfInfo()
|
||||||
this.websocket = new WebSocketClass(this.url, {
|
this.websocket = new WebSocketClass(this.url, {
|
||||||
maxPayload: 1024 * 1024 * 1024,
|
maxPayload: 1024 * 1024 * 1024,
|
||||||
handshakeTimeout: 2000,
|
handshakeTimeout: 2000,
|
||||||
|
@@ -9,7 +9,7 @@ import { OB11HeartbeatEvent } from '../../event/meta/OB11HeartbeatEvent'
|
|||||||
import { WebsocketServerBase } from '../../../common/server/websocket'
|
import { WebsocketServerBase } from '../../../common/server/websocket'
|
||||||
import { IncomingMessage } from 'node:http'
|
import { IncomingMessage } from 'node:http'
|
||||||
import { wsReply } from './reply'
|
import { wsReply } from './reply'
|
||||||
import { selfInfo } from '../../../common/data'
|
import { getSelfInfo } from '../../../common/data'
|
||||||
import { log } from '../../../common/utils/log'
|
import { log } from '../../../common/utils/log'
|
||||||
import { getConfigUtil } from '../../../common/config'
|
import { getConfigUtil } from '../../../common/config'
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ class OB11WebsocketServer extends WebsocketServerBase {
|
|||||||
}
|
}
|
||||||
const { heartInterval } = getConfigUtil().getConfig()
|
const { heartInterval } = getConfigUtil().getConfig()
|
||||||
const wsClientInterval = setInterval(() => {
|
const wsClientInterval = setInterval(() => {
|
||||||
postWsEvent(new OB11HeartbeatEvent(selfInfo.online!, true, heartInterval!))
|
postWsEvent(new OB11HeartbeatEvent(getSelfInfo().online!, true, heartInterval!))
|
||||||
}, heartInterval) // 心跳包
|
}, heartInterval) // 心跳包
|
||||||
wsClient.on('close', () => {
|
wsClient.on('close', () => {
|
||||||
log('event上报ws客户端已断开')
|
log('event上报ws客户端已断开')
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const version = '3.28.3'
|
export const version = '3.28.6'
|
||||||
|
Reference in New Issue
Block a user