This commit is contained in:
idranme 2024-09-08 15:56:05 +08:00
parent 9ecfb6ea0c
commit 1bcdbba29a
No known key found for this signature in database
GPG Key ID: 926F7B5B668E495F
8 changed files with 85 additions and 23 deletions

View File

@ -4,6 +4,7 @@ import path from 'node:path'
import { TEMP_DIR } from '../globalVars' import { TEMP_DIR } from '../globalVars'
import { randomUUID, createHash } from 'node:crypto' import { randomUUID, createHash } from 'node:crypto'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import { fileTypeFromFile } from 'file-type'
export function isGIF(path: string) { export function isGIF(path: string) {
const buffer = Buffer.alloc(4) const buffer = Buffer.alloc(4)
@ -116,7 +117,7 @@ type Uri2LocalRes = {
isLocal: boolean isLocal: boolean
} }
export async function uri2local(uri: string, filename?: string): Promise<Uri2LocalRes> { export async function uri2local(uri: string, filename?: string, needExt?: boolean): Promise<Uri2LocalRes> {
const { type } = checkUriType(uri) const { type } = checkUriType(uri)
if (type === FileUriType.FileURL) { if (type === FileUriType.FileURL) {
@ -139,8 +140,14 @@ export async function uri2local(uri: string, filename?: string): Promise<Uri2Loc
} else { } else {
filename ??= randomUUID() filename ??= randomUUID()
} }
const filePath = path.join(TEMP_DIR, filename) let filePath = path.join(TEMP_DIR, filename)
await fsPromise.writeFile(filePath, res.data) await fsPromise.writeFile(filePath, res.data)
if (needExt && !path.extname(filePath)) {
const ext = (await fileTypeFromFile(filePath))?.ext
filename += `.${ext}`
await fsPromise.rename(filePath, `${filePath}.${ext}`)
filePath = `${filePath}.${ext}`
}
return { success: true, errMsg: '', fileName: filename, path: filePath, isLocal: false } return { success: true, errMsg: '', fileName: filename, path: filePath, isLocal: false }
} catch (e) { } catch (e) {
const errMsg = `${uri} 下载失败, ${(e as Error).message}` const errMsg = `${uri} 下载失败, ${(e as Error).message}`
@ -150,9 +157,15 @@ export async function uri2local(uri: string, filename?: string): Promise<Uri2Loc
if (type === FileUriType.OneBotBase64) { if (type === FileUriType.OneBotBase64) {
filename ??= randomUUID() filename ??= randomUUID()
const filePath = path.join(TEMP_DIR, filename) let filePath = path.join(TEMP_DIR, filename)
const base64 = uri.replace(/^base64:\/\//, '') const base64 = uri.replace(/^base64:\/\//, '')
await fsPromise.writeFile(filePath, base64, 'base64') await fsPromise.writeFile(filePath, base64, 'base64')
if (needExt) {
const ext = (await fileTypeFromFile(filePath))?.ext
filename += `.${ext}`
await fsPromise.rename(filePath, `${filePath}.${ext}`)
filePath = `${filePath}.${ext}`
}
return { success: true, errMsg: '', fileName: filename, path: filePath, isLocal: false } return { success: true, errMsg: '', fileName: filename, path: filePath, isLocal: false }
} }
@ -162,8 +175,14 @@ export async function uri2local(uri: string, filename?: string): Promise<Uri2Loc
if (capture) { if (capture) {
filename ??= randomUUID() filename ??= randomUUID()
const [, _type, base64] = capture const [, _type, base64] = capture
const filePath = path.join(TEMP_DIR, filename) let filePath = path.join(TEMP_DIR, filename)
await fsPromise.writeFile(filePath, base64, 'base64') await fsPromise.writeFile(filePath, base64, 'base64')
if (needExt) {
const ext = (await fileTypeFromFile(filePath))?.ext
filename += `.${ext}`
await fsPromise.rename(filePath, `${filePath}.${ext}`)
filePath = `${filePath}.${ext}`
}
return { success: true, errMsg: '', fileName: filename, path: filePath, isLocal: false } return { success: true, errMsg: '', fileName: filename, path: filePath, isLocal: false }
} }
} }

View File

@ -202,7 +202,7 @@ function onLoad() {
// 创建窗口时触发 // 创建窗口时触发
function onBrowserWindowCreated(window: BrowserWindow) { function onBrowserWindowCreated(window: BrowserWindow) {
if (![2, 4].includes(window.id)) { if (![2, 4, 6].includes(window.id)) {
return return
} }
if (window.id === 2) { if (window.id === 2) {

View File

@ -1,5 +1,5 @@
import { ReceiveCmdS } from '../hook' import { ReceiveCmdS } from '../hook'
import { Group, GroupMember, GroupMemberRole, GroupNotifies, GroupRequestOperateTypes, GetFileListParam } from '../types' import { Group, GroupMember, GroupMemberRole, GroupNotifies, GroupRequestOperateTypes, GetFileListParam, PublishGroupBulletinReq } from '../types'
import { invoke, NTClass, NTMethod } from '../ntcall' import { invoke, NTClass, NTMethod } from '../ntcall'
import { GeneralCallResult } from '../services' import { GeneralCallResult } from '../services'
import { NTQQWindows } from './window' import { NTQQWindows } from './window'
@ -288,4 +288,16 @@ export class NTQQGroupApi extends Service {
) )
return data.fileInfo.item return data.fileInfo.item
} }
async publishGroupBulletin(groupCode: string, req: PublishGroupBulletinReq) {
const ntUserApi = this.ctx.get('ntUserApi')!
const psKey = (await ntUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!
return await invoke('nodeIKernelGroupService/publishGroupBulletin', [{ groupCode, psKey, req }, null])
}
async uploadGroupBulletinPic(groupCode: string, path: string) {
const ntUserApi = this.ctx.get('ntUserApi')!
const psKey = (await ntUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!
return await invoke('nodeIKernelGroupService/uploadGroupBulletinPic', [{ groupCode, psKey, path }, null])
}
} }

View File

@ -109,6 +109,10 @@ export class NTQQUserApi extends Service {
return cookies return cookies
} }
async getPSkey(domains: string[]) {
return await invoke('nodeIKernelTipOffService/getPskey', [{ domains, isForNewPCQQ: true }, null])
}
genBkn(sKey: string) { genBkn(sKey: string) {
sKey = sKey || '' sKey = sKey || ''
let hash = 5381 let hash = 5381

View File

@ -30,7 +30,7 @@ declare module 'cordis' {
'nt/message-sent': (input: RawMessage[]) => void 'nt/message-sent': (input: RawMessage[]) => void
'nt/group-notify': (input: GroupNotify[]) => void 'nt/group-notify': (input: GroupNotify[]) => void
'nt/friend-request': (input: FriendRequest[]) => void 'nt/friend-request': (input: FriendRequest[]) => void
'nt/group-member-info-updated': (input: { groupCode: string; members: GroupMember[] }) => void 'nt/group-member-info-updated': (input: { groupCode: string, members: GroupMember[] }) => void
} }
} }

View File

@ -202,11 +202,12 @@ export interface NodeIKernelGroupService {
publishInstructionForNewcomers(groupCode: string, arg: unknown): void publishInstructionForNewcomers(groupCode: string, arg: unknown): void
uploadGroupBulletinPic(groupCode: string, pskey: string, imagePath: string): Promise<GeneralCallResult & { uploadGroupBulletinPic(groupCode: string, pskey: string, imagePath: string): Promise<{
errCode: number errCode: number
errMsg: string
picInfo?: { picInfo?: {
id: string, id: string
width: number, width: number
height: number height: number
} }
}> }>

View File

@ -65,3 +65,15 @@ export interface GroupMember {
joinTime: string joinTime: string
lastSpeakTime: string lastSpeakTime: string
} }
export interface PublishGroupBulletinReq {
text: string
picInfo?: {
id: string
width: number
height: number
}
oldFeedsId: ''
pinned: number
confirmRequired: number
}

View File

@ -1,5 +1,7 @@
import BaseAction from '../BaseAction' import BaseAction from '../BaseAction'
import { ActionName } from '../types' import { ActionName } from '../types'
import { unlink } from 'fs/promises'
import { checkFileReceived, uri2local } from '@/common/utils/file'
interface Payload { interface Payload {
group_id: number | string group_id: number | string
@ -13,24 +15,36 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_SendGroupNotice actionName = ActionName.GoCQHTTP_SendGroupNotice
async _handle(payload: Payload) { async _handle(payload: Payload) {
const type = 1 const groupCode = payload.group_id.toString()
const isShowEditCard = 0
const tipWindowType = 0
const pinned = Number(payload.pinned ?? 0) const pinned = Number(payload.pinned ?? 0)
const confirmRequired = Number(payload.confirm_required ?? 1) const confirmRequired = Number(payload.confirm_required ?? 1)
const result = await this.ctx.ntWebApi.setGroupNotice({ let picInfo: { id: string, width: number, height: number } | undefined
groupCode: payload.group_id.toString(), if (payload.image) {
content: payload.content, const { path, isLocal, success, errMsg } = await uri2local(payload.image, undefined, true)
if (!success) {
throw new Error(`设置群公告失败, 错误信息: uri2local: ${errMsg}`)
}
await checkFileReceived(path, 5000) // 文件不存在QQ会崩溃需要提前判断
const result = await this.ctx.ntGroupApi.uploadGroupBulletinPic(groupCode, path)
if (result.errCode !== 0) {
throw new Error(`设置群公告失败, 错误信息: uploadGroupBulletinPic: ${result.errMsg}`)
}
if (!isLocal) {
unlink(path)
}
picInfo = result.picInfo
}
const res = await this.ctx.ntGroupApi.publishGroupBulletin(groupCode, {
text: encodeURIComponent(payload.content),
oldFeedsId: '',
pinned, pinned,
type,
isShowEditCard,
tipWindowType,
confirmRequired, confirmRequired,
picId: '' picInfo
}) })
if (result.ec !== 0) { if (res.result !== 0) {
throw new Error(`设置群公告失败, 错误信息: ${result.em}`) throw new Error(`设置群公告失败, 错误信息: ${res.errMsg}`)
} }
return null return null
} }