mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
430 lines
19 KiB
TypeScript
430 lines
19 KiB
TypeScript
import {
|
||
OB11Group,
|
||
OB11GroupMember,
|
||
OB11GroupMemberRole,
|
||
OB11Message,
|
||
OB11MessageData,
|
||
OB11MessageDataType,
|
||
OB11User,
|
||
OB11UserSex
|
||
} from "./types";
|
||
import {
|
||
AtType,
|
||
ChatType,
|
||
GrayTipElementSubType,
|
||
Group,
|
||
GroupMember,
|
||
IMAGE_HTTP_HOST,
|
||
RawMessage,
|
||
SelfInfo,
|
||
Sex,
|
||
TipGroupElementType,
|
||
User, VideoElement
|
||
} from '../ntqqapi/types';
|
||
import {getFriend, getGroupMember, selfInfo, tempGroupCodeMap} from '../common/data';
|
||
import {EventType} from "./event/OB11BaseEvent";
|
||
import {encodeCQCode} from "./cqcode";
|
||
import {dbUtil} from "../common/db";
|
||
import {OB11GroupIncreaseEvent} from "./event/notice/OB11GroupIncreaseEvent";
|
||
import {OB11GroupBanEvent} from "./event/notice/OB11GroupBanEvent";
|
||
import {OB11GroupUploadNoticeEvent} from "./event/notice/OB11GroupUploadNoticeEvent";
|
||
import {OB11GroupNoticeEvent} from "./event/notice/OB11GroupNoticeEvent";
|
||
import {NTQQUserApi} from "../ntqqapi/api/user";
|
||
import {NTQQFileApi} from "../ntqqapi/api/file";
|
||
import {calcQQLevel} from "../common/utils/qqlevel";
|
||
import {log} from "../common/utils/log";
|
||
import {sleep} from "../common/utils/helper";
|
||
import {getConfigUtil} from "../common/config";
|
||
import {OB11GroupTitleEvent} from "./event/notice/OB11GroupTitleEvent";
|
||
import {OB11GroupCardEvent} from "./event/notice/OB11GroupCardEvent";
|
||
|
||
|
||
export class OB11Constructor {
|
||
static async message(msg: RawMessage): Promise<OB11Message> {
|
||
|
||
const {enableLocalFile2Url, ob11: {messagePostFormat}} = getConfigUtil().getConfig()
|
||
const message_type = msg.chatType == ChatType.group ? "group" : "private";
|
||
const resMsg: OB11Message = {
|
||
self_id: parseInt(selfInfo.uin),
|
||
user_id: parseInt(msg.senderUin),
|
||
time: parseInt(msg.msgTime) || Date.now(),
|
||
message_id: msg.msgShortId,
|
||
real_id: msg.msgShortId,
|
||
message_type: msg.chatType == ChatType.group ? "group" : "private",
|
||
sender: {
|
||
user_id: parseInt(msg.senderUin),
|
||
nickname: msg.sendNickName,
|
||
card: msg.sendMemberName || "",
|
||
},
|
||
raw_message: "",
|
||
font: 14,
|
||
sub_type: "friend",
|
||
message: messagePostFormat === 'string' ? '' : [],
|
||
message_format: messagePostFormat === 'string' ? 'string' : 'array',
|
||
post_type: selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
|
||
}
|
||
if (msg.chatType == ChatType.group) {
|
||
resMsg.sub_type = "normal" // 这里go-cqhttp是group,而onebot11标准是normal, 蛋疼
|
||
resMsg.group_id = parseInt(msg.peerUin)
|
||
const member = await getGroupMember(msg.peerUin, msg.senderUin);
|
||
if (member) {
|
||
resMsg.sender.role = OB11Constructor.groupMemberRole(member.role);
|
||
resMsg.sender.nickname = member.nick
|
||
}
|
||
} else if (msg.chatType == ChatType.friend) {
|
||
resMsg.sub_type = "friend"
|
||
const friend = await getFriend(msg.senderUin);
|
||
if (friend) {
|
||
resMsg.sender.nickname = friend.nick;
|
||
}
|
||
} else if (msg.chatType == ChatType.temp) {
|
||
resMsg.sub_type = "group"
|
||
const tempGroupCode = tempGroupCodeMap[msg.peerUin]
|
||
if (tempGroupCode) {
|
||
resMsg.group_id = parseInt(tempGroupCode)
|
||
}
|
||
}
|
||
|
||
for (let element of msg.elements) {
|
||
let message_data: OB11MessageData | any = {
|
||
data: {},
|
||
type: "unknown"
|
||
}
|
||
if (element.textElement && element.textElement?.atType !== AtType.notAt) {
|
||
message_data["type"] = OB11MessageDataType.at
|
||
if (element.textElement.atType == AtType.atAll) {
|
||
// message_data["data"]["mention"] = "all"
|
||
message_data["data"]["qq"] = "all"
|
||
} else {
|
||
let atUid = element.textElement.atNtUid
|
||
let atQQ = element.textElement.atUid
|
||
if (!atQQ || atQQ === "0") {
|
||
const atMember = await getGroupMember(msg.peerUin, atUid)
|
||
if (atMember) {
|
||
atQQ = atMember.uin
|
||
}
|
||
}
|
||
if (atQQ) {
|
||
// message_data["data"]["mention"] = atQQ
|
||
message_data["data"]["qq"] = atQQ
|
||
}
|
||
}
|
||
} else if (element.textElement) {
|
||
message_data["type"] = "text"
|
||
let text = element.textElement.content
|
||
if (!text.trim()) {
|
||
continue;
|
||
}
|
||
message_data["data"]["text"] = text
|
||
} else if (element.replyElement) {
|
||
message_data["type"] = "reply"
|
||
// log("收到回复消息", element.replyElement.replayMsgSeq)
|
||
try {
|
||
const replyMsg = await dbUtil.getMsgBySeqId(element.replyElement.replayMsgSeq)
|
||
// log("找到回复消息", replyMsg.msgShortId, replyMsg.msgId)
|
||
if (replyMsg) {
|
||
message_data["data"]["id"] = replyMsg.msgShortId.toString()
|
||
} else {
|
||
continue
|
||
}
|
||
} catch (e) {
|
||
log("获取不到引用的消息", e.stack, element.replyElement.replayMsgSeq)
|
||
}
|
||
|
||
} else if (element.picElement) {
|
||
message_data["type"] = "image"
|
||
// message_data["data"]["file"] = element.picElement.sourcePath
|
||
message_data["data"]["file"] = element.picElement.fileName
|
||
// message_data["data"]["path"] = element.picElement.sourcePath
|
||
const url = element.picElement.originImageUrl
|
||
const fileMd5 = element.picElement.md5HexStr
|
||
if (url) {
|
||
message_data["data"]["url"] = IMAGE_HTTP_HOST + url
|
||
} else if (fileMd5 && element.picElement.fileUuid.indexOf("_") === -1) { // fileuuid有下划线的是Linux发送的,这个url是另外的格式,目前尚未得知如何组装
|
||
message_data["data"]["url"] = `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${fileMd5.toUpperCase()}/0`
|
||
}
|
||
// message_data["data"]["file_id"] = element.picElement.fileUuid
|
||
message_data["data"]["file_size"] = element.picElement.fileSize
|
||
dbUtil.addFileCache(element.picElement.fileName, {
|
||
fileName: element.picElement.fileName,
|
||
filePath: element.picElement.sourcePath,
|
||
fileSize: element.picElement.fileSize.toString(),
|
||
url: message_data["data"]["url"],
|
||
downloadFunc: async () => {
|
||
await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
|
||
element.elementId, element.picElement.thumbPath?.get(0) || "", element.picElement.sourcePath)
|
||
}
|
||
}).then()
|
||
// 不在自动下载图片
|
||
|
||
} else if (element.videoElement || element.fileElement) {
|
||
const videoOrFileElement = element.videoElement || element.fileElement
|
||
const ob11MessageDataType = element.videoElement ? OB11MessageDataType.video : OB11MessageDataType.file
|
||
message_data["type"] = ob11MessageDataType;
|
||
message_data["data"]["file"] = videoOrFileElement.fileName
|
||
message_data["data"]["path"] = videoOrFileElement.filePath
|
||
message_data["data"]["file_id"] = videoOrFileElement.fileUuid
|
||
message_data["data"]["file_size"] = videoOrFileElement.fileSize
|
||
dbUtil.addFileCache(videoOrFileElement.fileUuid, {
|
||
msgId: msg.msgId,
|
||
fileName: videoOrFileElement.fileName,
|
||
filePath: videoOrFileElement.filePath,
|
||
fileSize: videoOrFileElement.fileSize,
|
||
downloadFunc: async () => {
|
||
await NTQQFileApi.downloadMedia(
|
||
msg.msgId, msg.chatType, msg.peerUid,
|
||
element.elementId,
|
||
ob11MessageDataType == OB11MessageDataType.video ? (videoOrFileElement as VideoElement).thumbPath.get(0) : null,
|
||
videoOrFileElement.filePath)
|
||
}
|
||
}).then()
|
||
// 怎么拿到url呢
|
||
} else if (element.pttElement) {
|
||
message_data["type"] = OB11MessageDataType.voice;
|
||
message_data["data"]["file"] = element.pttElement.fileName
|
||
message_data["data"]["path"] = element.pttElement.filePath
|
||
// message_data["data"]["file_id"] = element.pttElement.fileUuid
|
||
message_data["data"]["file_size"] = element.pttElement.fileSize
|
||
dbUtil.addFileCache(element.pttElement.fileName, {
|
||
fileName: element.pttElement.fileName,
|
||
filePath: element.pttElement.filePath,
|
||
fileSize: element.pttElement.fileSize,
|
||
}).then()
|
||
|
||
// log("收到语音消息", msg)
|
||
// window.LLAPI.Ptt2Text(message.raw.msgId, message.peer, messages).then(text => {
|
||
// console.log("语音转文字结果", text);
|
||
// }).catch(err => {
|
||
// console.log("语音转文字失败", err);
|
||
// })
|
||
} else if (element.arkElement) {
|
||
message_data["type"] = OB11MessageDataType.json;
|
||
message_data["data"]["data"] = element.arkElement.bytesData;
|
||
} else if (element.faceElement) {
|
||
message_data["type"] = OB11MessageDataType.face;
|
||
message_data["data"]["id"] = element.faceElement.faceIndex.toString();
|
||
} else if (element.marketFaceElement) {
|
||
message_data["type"] = OB11MessageDataType.mface;
|
||
message_data["data"]["text"] = element.marketFaceElement.faceName;
|
||
}
|
||
if (message_data.type !== "unknown" && message_data.data) {
|
||
const cqCode = encodeCQCode(message_data);
|
||
if (messagePostFormat === 'string') {
|
||
(resMsg.message as string) += cqCode;
|
||
} else (resMsg.message as OB11MessageData[]).push(message_data);
|
||
|
||
resMsg.raw_message += cqCode;
|
||
}
|
||
}
|
||
resMsg.raw_message = resMsg.raw_message.trim();
|
||
return resMsg;
|
||
}
|
||
|
||
static async GroupEvent(msg: RawMessage): Promise<OB11GroupNoticeEvent> {
|
||
if (msg.chatType !== ChatType.group) {
|
||
return;
|
||
}
|
||
if (msg.senderUin){
|
||
let member = await getGroupMember(msg.peerUid, msg.senderUin);
|
||
if (member && member.cardName !== msg.sendMemberName) {
|
||
member.cardName = msg.sendMemberName;
|
||
return new OB11GroupCardEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), msg.sendMemberName, member.cardName)
|
||
}
|
||
}
|
||
// log("group msg", msg);
|
||
for (let element of msg.elements) {
|
||
const grayTipElement = element.grayTipElement
|
||
const groupElement = grayTipElement?.groupElement
|
||
if (groupElement) {
|
||
// log("收到群提示消息", groupElement)
|
||
if (groupElement.type == TipGroupElementType.memberIncrease) {
|
||
log("收到群成员增加消息", groupElement)
|
||
await sleep(1000);
|
||
const member = await getGroupMember(msg.peerUid, groupElement.memberUid);
|
||
let memberUin = member?.uin;
|
||
if (!memberUin) {
|
||
memberUin = (await NTQQUserApi.getUserDetailInfo(groupElement.memberUid)).uin
|
||
}
|
||
// log("获取新群成员QQ", memberUin)
|
||
const adminMember = await getGroupMember(msg.peerUid, groupElement.adminUid);
|
||
// log("获取同意新成员入群的管理员", adminMember)
|
||
if (memberUin) {
|
||
const operatorUin = adminMember?.uin || memberUin
|
||
let event = new OB11GroupIncreaseEvent(parseInt(msg.peerUid), parseInt(memberUin), parseInt(operatorUin));
|
||
// log("构造群增加事件", event)
|
||
return event;
|
||
}
|
||
} else if (groupElement.type === TipGroupElementType.ban) {
|
||
log("收到群群员禁言提示", groupElement)
|
||
const memberUid = groupElement.shutUp.member.uid
|
||
const adminUid = groupElement.shutUp.admin.uid
|
||
let memberUin: string = ""
|
||
let duration = parseInt(groupElement.shutUp.duration)
|
||
let sub_type: "ban" | "lift_ban" = duration > 0 ? "ban" : "lift_ban"
|
||
if (memberUid) {
|
||
memberUin = (await getGroupMember(msg.peerUid, memberUid))?.uin || (await NTQQUserApi.getUserDetailInfo(memberUid))?.uin
|
||
} else {
|
||
memberUin = "0"; // 0表示全员禁言
|
||
if (duration > 0) {
|
||
duration = -1
|
||
}
|
||
}
|
||
const adminUin = (await getGroupMember(msg.peerUid, adminUid))?.uin || (await NTQQUserApi.getUserDetailInfo(adminUid))?.uin
|
||
if (memberUin && adminUin) {
|
||
return new OB11GroupBanEvent(parseInt(msg.peerUid), parseInt(memberUin), parseInt(adminUin), duration, sub_type);
|
||
}
|
||
}
|
||
} else if (element.fileElement) {
|
||
return new OB11GroupUploadNoticeEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), {
|
||
id: element.fileElement.fileUuid,
|
||
name: element.fileElement.fileName,
|
||
size: parseInt(element.fileElement.fileSize),
|
||
busid: element.fileElement.fileBizId || 0
|
||
})
|
||
}
|
||
|
||
if (grayTipElement) {
|
||
if (grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER) {
|
||
log("收到新人被邀请进群消息", grayTipElement)
|
||
const xmlElement = grayTipElement.xmlElement
|
||
if (xmlElement?.content) {
|
||
const regex = /jp="(\d+)"/g;
|
||
|
||
let matches = [];
|
||
let match = null
|
||
|
||
while ((match = regex.exec(xmlElement.content)) !== null) {
|
||
matches.push(match[1]);
|
||
}
|
||
if (matches.length === 2) {
|
||
const [inviter, invitee] = matches;
|
||
return new OB11GroupIncreaseEvent(parseInt(msg.peerUid), parseInt(invitee), parseInt(inviter), "invite");
|
||
}
|
||
}
|
||
} else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
|
||
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr)
|
||
/*
|
||
{
|
||
align: 'center',
|
||
items: [
|
||
{ txt: '恭喜', type: 'nor' },
|
||
{
|
||
col: '3',
|
||
jp: '5',
|
||
param: ["QQ号"],
|
||
txt: '林雨辰',
|
||
type: 'url'
|
||
},
|
||
{ txt: '获得群主授予的', type: 'nor' },
|
||
{
|
||
col: '3',
|
||
jp: '',
|
||
txt: '好好好',
|
||
type: 'url'
|
||
},
|
||
{ txt: '头衔', type: 'nor' }
|
||
]
|
||
}
|
||
|
||
* */
|
||
const memberUin = json.items[1].param[0]
|
||
const title = json.items[3].txt
|
||
log("收到群成员新头衔消息", json)
|
||
return new OB11GroupTitleEvent(parseInt(msg.peerUid), parseInt(memberUin), title)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static friend(friend: User): OB11User {
|
||
return {
|
||
user_id: parseInt(friend.uin),
|
||
nickname: friend.nick,
|
||
remark: friend.remark,
|
||
sex: OB11Constructor.sex(friend.sex),
|
||
level: friend.qqLevel && calcQQLevel(friend.qqLevel) || 0
|
||
}
|
||
}
|
||
|
||
static selfInfo(selfInfo: SelfInfo): OB11User {
|
||
return {
|
||
user_id: parseInt(selfInfo.uin),
|
||
nickname: selfInfo.nick,
|
||
}
|
||
}
|
||
|
||
static friends(friends: User[]): OB11User[] {
|
||
return friends.map(OB11Constructor.friend)
|
||
}
|
||
|
||
static groupMemberRole(role: number): OB11GroupMemberRole | undefined {
|
||
return {
|
||
4: OB11GroupMemberRole.owner,
|
||
3: OB11GroupMemberRole.admin,
|
||
2: OB11GroupMemberRole.member
|
||
}[role]
|
||
}
|
||
|
||
static sex(sex: Sex): OB11UserSex {
|
||
const sexMap = {
|
||
[Sex.male]: OB11UserSex.male,
|
||
[Sex.female]: OB11UserSex.female,
|
||
[Sex.unknown]: OB11UserSex.unknown
|
||
}
|
||
return sexMap[sex] || OB11UserSex.unknown
|
||
}
|
||
|
||
static groupMember(group_id: string, member: GroupMember): OB11GroupMember {
|
||
return {
|
||
group_id: parseInt(group_id),
|
||
user_id: parseInt(member.uin),
|
||
nickname: member.nick,
|
||
card: member.cardName,
|
||
sex: OB11Constructor.sex(member.sex),
|
||
age: 0,
|
||
area: "",
|
||
level: 0,
|
||
qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0,
|
||
join_time: 0, // 暂时没法获取
|
||
last_sent_time: 0, // 暂时没法获取
|
||
title_expire_time: 0,
|
||
unfriendly: false,
|
||
card_changeable: true,
|
||
is_robot: member.isRobot,
|
||
shut_up_timestamp: member.shutUpTime,
|
||
role: OB11Constructor.groupMemberRole(member.role),
|
||
}
|
||
}
|
||
|
||
static stranger(user: User): OB11User {
|
||
return {
|
||
...user,
|
||
user_id: parseInt(user.uin),
|
||
nickname: user.nick,
|
||
sex: OB11Constructor.sex(user.sex),
|
||
age: 0,
|
||
qid: user.qid,
|
||
login_days: 0,
|
||
level: user.qqLevel && calcQQLevel(user.qqLevel) || 0,
|
||
}
|
||
}
|
||
|
||
static groupMembers(group: Group): OB11GroupMember[] {
|
||
log("construct ob11 group members", group)
|
||
return group.members.map(m => OB11Constructor.groupMember(group.groupCode, m))
|
||
}
|
||
|
||
static group(group: Group): OB11Group {
|
||
return {
|
||
group_id: parseInt(group.groupCode),
|
||
group_name: group.groupName,
|
||
member_count: group.memberCount,
|
||
max_member_count: group.maxMember
|
||
}
|
||
}
|
||
|
||
static groups(groups: Group[]): OB11Group[] {
|
||
return groups.map(OB11Constructor.group)
|
||
}
|
||
}
|