refactor: 统一时间戳为毫秒,优化发送消息逻辑代码

fix: 发送文件的文件名保持原样
This commit is contained in:
linyuchen 2024-02-27 19:47:17 +08:00
parent c636af0b0e
commit 9faa56ec32
7 changed files with 89 additions and 82 deletions

View File

@ -47,6 +47,8 @@ TG群<https://t.me/+nLZEnpne-pQ1OWFl>
- [x] 处理添加好友请求 - [x] 处理添加好友请求
- [x] 处理加群请求 - [x] 处理加群请求
- [x] 退群 - [x] 退群
- [ ] 上报加群邀请
- [ ] 同意加群邀请
- [x] 上报好友消息 - [x] 上报好友消息
- [x] 上报添加好友请求 - [x] 上报添加好友请求
- [x] 上报群消息 - [x] 上报群消息
@ -159,6 +161,8 @@ TG群<https://t.me/+nLZEnpne-pQ1OWFl>
- [x] 群管理功能,禁言、踢人,改群名片等 - [x] 群管理功能,禁言、踢人,改群名片等
- [x] 视频消息 - [x] 视频消息
- [x] 文件消息 - [x] 文件消息
- [ ] 上报加群邀请
- [ ] 同意加群邀请
- [ ] 音乐卡片 - [ ] 音乐卡片
- [ ] 无头模式 - [ ] 无头模式

View File

@ -9,7 +9,7 @@ import {
SendTextElement SendTextElement
} from "./types"; } from "./types";
import {NTQQApi} from "./ntcall"; import {NTQQApi} from "./ntcall";
import {encodeSilk, log} from "../common/utils"; import {encodeSilk} from "../common/utils";
import fs from "fs"; import fs from "fs";
@ -56,7 +56,7 @@ export class SendMsgElementConstructor {
} }
static async pic(picPath: string): Promise<SendPicElement> { static async pic(picPath: string): Promise<SendPicElement> {
const {md5, fileName, path, fileSize} = await NTQQApi.uploadFile(picPath); const {md5, fileName, path, fileSize} = await NTQQApi.uploadFile(picPath, ElementType.PIC);
const imageSize = await NTQQApi.getImageSize(picPath); const imageSize = await NTQQApi.getImageSize(picPath);
const picElement = { const picElement = {
md5HexStr: md5, md5HexStr: md5,
@ -82,7 +82,13 @@ export class SendMsgElementConstructor {
} }
static async file(filePath: string, isVideo: boolean = false): Promise<SendFileElement> { static async file(filePath: string, isVideo: boolean = false): Promise<SendFileElement> {
const {md5, fileName, path, fileSize} = await NTQQApi.uploadFile(filePath); let picHeight = 0;
let picWidth = 0;
if (isVideo) {
picHeight = 1024;
picWidth = 768;
}
const {md5, fileName, path, fileSize} = await NTQQApi.uploadFile(filePath, ElementType.FILE);
let element: SendFileElement = { let element: SendFileElement = {
elementType: ElementType.FILE, elementType: ElementType.FILE,
elementId: "", elementId: "",
@ -90,18 +96,18 @@ export class SendMsgElementConstructor {
fileName, fileName,
"filePath": path, "filePath": path,
"fileSize": (fileSize).toString(), "fileSize": (fileSize).toString(),
picHeight,
picWidth
} }
} }
if (isVideo){
element.fileElement.picHeight = 1024;
element.fileElement.picWidth = 768;
}
return element; return element;
} }
static video(filePath: string): Promise<SendFileElement> { static video(filePath: string): Promise<SendFileElement> {
return SendMsgElementConstructor.file(filePath, true); return SendMsgElementConstructor.file(filePath, true);
} }
static async ptt(pttPath: string): Promise<SendPttElement> { static async ptt(pttPath: string): Promise<SendPttElement> {
const {converted, path: silkPath, duration} = await encodeSilk(pttPath); const {converted, path: silkPath, duration} = await encodeSilk(pttPath);
// log("生成语音", silkPath, duration); // log("生成语音", silkPath, duration);

View File

@ -1,13 +1,14 @@
import {ipcMain} from "electron"; import {ipcMain} from "electron";
import {hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook} from "./hook"; import {hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook} from "./hook";
import {log} from "../common/utils"; import {log, sleep} from "../common/utils";
import { import {
ChatType, ChatType,
ElementType, ElementType,
Friend, Friend,
FriendRequest, FriendRequest,
Group, Group,
GroupMember, GroupMemberRole, GroupMember,
GroupMemberRole,
GroupNotifies, GroupNotifies,
GroupNotify, GroupNotify,
GroupRequestOperateTypes, GroupRequestOperateTypes,
@ -19,6 +20,7 @@ import {
import * as fs from "fs"; import * as fs from "fs";
import {addHistoryMsg, friendRequests, groupNotifies, msgHistory, selfInfo} from "../common/data"; import {addHistoryMsg, friendRequests, groupNotifies, msgHistory, selfInfo} from "../common/data";
import {v4 as uuidv4} from "uuid" import {v4 as uuidv4} from "uuid"
import path from "path";
interface IPCReceiveEvent { interface IPCReceiveEvent {
eventName: string eventName: string
@ -346,7 +348,10 @@ export class NTQQApi {
} else { } else {
ext = "" ext = ""
} }
const fileName = `${md5}${ext}`; let fileName = `${path.basename(filePath)}`;
if (fileName.indexOf(".") === -1) {
fileName += ext;
}
const mediaPath = await callNTQQApi<string>({ const mediaPath = await callNTQQApi<string>({
methodName: NTQQApiMethod.MEDIA_FILE_PATH, methodName: NTQQApiMethod.MEDIA_FILE_PATH,
args: [{ args: [{
@ -414,62 +419,48 @@ export class NTQQApi {
}) })
} }
static sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = false, timeout = 10000) { static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = false, timeout = 10000) {
const sendTimeout = timeout
return new Promise<RawMessage>((resolve, reject) => {
const peerUid = peer.peerUid; const peerUid = peer.peerUid;
let usingTime = 0;
let success = false;
let isTimeout = false;
const checkSuccess = () => { // 等待上一个相同的peer发送完
if (!success) { let checkLastSendUsingTime = 0;
sendMessagePool[peerUid] = null; const waitLastSend = async () => {
isTimeout = true; if (checkLastSendUsingTime > timeout) {
reject("发送超时") throw ("发送超时")
} }
} let lastSending = sendMessagePool[peer.peerUid]
setTimeout(checkSuccess, sendTimeout); if (lastSending) {
const checkLastSend = () => {
let lastSending = sendMessagePool[peerUid]
if (sendTimeout < usingTime) {
sendMessagePool[peerUid] = null;
isTimeout = true;
reject("发送超时")
}
if (!!lastSending) {
// log("有正在发送的消息,等待中...") // log("有正在发送的消息,等待中...")
usingTime += 500; await sleep(500);
setTimeout(checkLastSend, 500); checkLastSendUsingTime += 500;
return await waitLastSend();
} else { } else {
log("可以进行发送消息,设置发送成功回调", sendMessagePool) return;
sendMessagePool[peerUid] = (rawMessage: RawMessage) => {
sendMessagePool[peerUid] = null;
const checkSendComplete = () => {
if (isTimeout) {
return reject("发送超时")
} }
if (msgHistory[rawMessage.msgId]?.sendStatus == 2) { }
log(`${peerUid}发送消息成功`) await waitLastSend();
success = true;
resolve(rawMessage); let sentMessage: RawMessage = null;
sendMessagePool[peerUid] = async (rawMessage: RawMessage) => {
delete sendMessagePool[peerUid];
sentMessage = rawMessage;
}
let checkSendCompleteUsingTime = 0;
const checkSendComplete = async (): Promise<RawMessage> => {
if (sentMessage && msgHistory[sentMessage.msgId]?.sendStatus == 2) {
// log(`给${peerUid}发送消息成功`)
return sentMessage;
} else { } else {
setTimeout(checkSendComplete, 500) checkSendCompleteUsingTime += 500;
if (checkSendCompleteUsingTime > timeout) {
throw ("发送超时")
}
await sleep(500);
return await checkSendComplete()
} }
} }
if (waitComplete) {
checkSendComplete();
} else {
success = true;
log(`${peerUid}发送消息成功`)
resolve(rawMessage);
}
}
}
}
checkLastSend()
callNTQQApi({ callNTQQApi({
methodName: NTQQApiMethod.SEND_MSG, methodName: NTQQApiMethod.SEND_MSG,
args: [{ args: [{
@ -478,7 +469,7 @@ export class NTQQApi {
msgAttributeInfos: new Map(), msgAttributeInfos: new Map(),
}, null] }, null]
}).then() }).then()
}) return checkSendComplete();
} }
static multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) { static multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
@ -638,6 +629,7 @@ export class NTQQApi {
} }
) )
} }
static banGroup(groupQQ: string, shutUp: boolean) { static banGroup(groupQQ: string, shutUp: boolean) {
return callNTQQApi<GeneralCallResult>({ return callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.MUTE_GROUP, methodName: NTQQApiMethod.MUTE_GROUP,

View File

@ -236,7 +236,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
case OB11MessageDataType.voice: { case OB11MessageDataType.voice: {
const file = sendMsg.data?.file const file = sendMsg.data?.file
if (file) { if (file) {
const {path, isLocal} = (await uri2local(uuidv4(), file)) const {path, isLocal} = (await uri2local(file))
if (path) { if (path) {
if (!isLocal) { // 只删除http和base64转过来的文件 if (!isLocal) { // 只删除http和base64转过来的文件
deleteAfterSentFiles.push(path) deleteAfterSentFiles.push(path)

View File

@ -23,7 +23,7 @@ export class OB11Constructor {
const resMsg: OB11Message = { const resMsg: OB11Message = {
self_id: parseInt(selfInfo.uin), self_id: parseInt(selfInfo.uin),
user_id: parseInt(msg.senderUin), user_id: parseInt(msg.senderUin),
time: parseInt(msg.msgTime) || 0, time: parseInt(msg.msgTime) * 1000 || Date.now(), // 13位时间戳毫秒
message_id: msg.msgShortId, message_id: msg.msgShortId,
real_id: msg.msgId, real_id: msg.msgId,
message_type: msg.chatType == ChatType.group ? "group" : "private", message_type: msg.chatType == ChatType.group ? "group" : "private",

View File

@ -10,7 +10,7 @@ export enum EventType {
export abstract class OB11BaseEvent { export abstract class OB11BaseEvent {
time = new Date().getTime(); time = Date.now();
self_id = parseInt(selfInfo.uin); self_id = parseInt(selfInfo.uin);
post_type: EventType; post_type: EventType;
} }

View File

@ -1,10 +1,13 @@
import {CONFIG_DIR, isGIF} from "../common/utils"; import {CONFIG_DIR, isGIF} from "../common/utils";
import {v4 as uuidv4} from "uuid";
import * as path from 'path'; import * as path from 'path';
import {OB11MessageData} from "./types";
const fs = require("fs").promises; const fs = require("fs").promises;
export async function uri2local(fileName: string, uri: string){ export async function uri2local(uri: string, fileName: string=null){
if (!fileName){
fileName = uuidv4();
}
let filePath = path.join(CONFIG_DIR, fileName) let filePath = path.join(CONFIG_DIR, fileName)
let url = new URL(uri); let url = new URL(uri);
let res = { let res = {
@ -33,6 +36,8 @@ export async function uri2local(fileName: string, uri: string){
let blob = await fetchRes.blob(); let blob = await fetchRes.blob();
let buffer = await blob.arrayBuffer(); let buffer = await blob.arrayBuffer();
try { try {
fileName = path.basename(url.pathname) || fileName
filePath = path.join(CONFIG_DIR, fileName)
await fs.writeFile(filePath, Buffer.from(buffer)); await fs.writeFile(filePath, Buffer.from(buffer));
} catch (e: any) { } catch (e: any) {
res.errMsg = `${url}下载失败,` + e.toString() res.errMsg = `${url}下载失败,` + e.toString()