qq-official-bot
Version:
265 lines (264 loc) • 10.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Sender = void 0;
const utils_1 = require("../utils");
const crypto_1 = require("crypto");
const promises_1 = __importDefault(require("node:fs/promises"));
const formdata_node_1 = require("formdata-node");
class Sender {
constructor(bot, baseUrl, message, source = {}) {
this.bot = bot;
this.baseUrl = baseUrl;
this.message = message;
this.source = source;
this.brief = '';
this.isFile = false;
this.messagePayload = {
msg_seq: (0, crypto_1.randomInt)(1, 1000000),
content: ''
};
this.buttons = [];
this.filePayload = {
srv_send_msg: true
};
this.contentType = 'application/json';
this.messagePayload.msg_id = source.id;
}
getType(type) {
return ['image', 'video', 'audio'].indexOf(type) + 1;
}
parseFromTemplate(template) {
const result = [];
const reg = /(<[^>]+>)/;
while (template.length) {
const match = template.match(reg)?.[0];
if (!match)
break;
const index = template.indexOf(match);
const prevText = template.slice(0, index);
if (prevText) {
result.push({
type: 'text',
text: prevText
});
}
template = template.slice(index + match.length);
const [type, ...attrArr] = match.slice(1, -1).split(',');
const attrs = Object.fromEntries(attrArr.map((attr) => {
const [key, value] = attr.split('=');
try {
return [key, JSON.parse(value)];
}
catch {
return [key, value];
}
}));
result.push({ type, ...attrs });
}
if (template.length) {
result.push({
type: 'text',
text: template
});
}
return result;
}
async fixGuildMediaData(elem) {
if (elem.url)
return elem.url;
if (typeof elem.file === "string" && elem.file.startsWith('http')) {
return elem.file;
}
this.contentType = 'multipart/form-data';
if (Buffer.isBuffer(elem.file)) {
return new formdata_node_1.Blob([elem.file]);
}
else if (typeof elem.file !== "string") {
throw new Error("bad file param: " + elem.file);
}
else if (elem.file.startsWith("base64://")) {
return new formdata_node_1.Blob([Buffer.from(elem.file.slice(9), 'base64')]);
}
else if (/^data:[^/]+\/[^;]+;base64,/.test(elem.file)) {
return new formdata_node_1.Blob([Buffer.from(elem.file.replace(/^data:[^/]+\/[^;]+;base64,/, ''), 'base64')]);
}
else
try {
return new formdata_node_1.Blob([await promises_1.default.readFile(elem.file.replace("file://", ""))]);
}
catch {
}
throw new Error("bad file param: " + elem.file);
}
async processMessage() {
if (!Array.isArray(this.message))
this.message = [this.message];
const message = [...this.message];
while (message.length) {
const elem = message.shift();
if (typeof elem === 'string') {
const index = message.findIndex((item) => item === elem);
message.splice(index, 0, ...this.parseFromTemplate(elem));
continue;
}
const { type, ...data } = elem;
switch (elem.type) {
case 'reply':
if (elem.event_id) {
this.messagePayload.event_id = elem.event_id;
this.brief += `<reply,event_id=${elem.event_id}>`;
}
else {
this.messagePayload.msg_id = elem.id;
this.messagePayload.message_reference = {
message_id: elem.id
};
this.brief += `<reply,msg_id=${elem.id}>`;
}
break;
case "at":
this.messagePayload.content += `<@${elem.user_id === 'all' ? 'everyone' : elem.user_id}>`;
this.brief += `<at,user=${elem.user_id === 'all' ? 'everyone' : elem.user_id}>`;
break;
case 'link':
this.messagePayload.content += `<#${elem.channel_id}>`;
this.brief += `<link,channel=${elem.channel_id}>`;
break;
case 'text':
this.messagePayload.content += elem.text;
this.brief += elem.text;
break;
case 'face':
this.messagePayload.content += `<emoji:${elem.id}>`;
this.brief += `<face,id=${elem.id}>`;
break;
case 'image':
case 'audio':
case 'video':
if (this.messagePayload.msg_id || this.messagePayload.event_id) {
if (!this.baseUrl.startsWith('/v2')) {
const fileData = await this.fixGuildMediaData(elem);
if (typeof fileData !== 'string') {
this.messagePayload.file_image = fileData;
}
else {
this.messagePayload.image = elem.file;
}
}
else {
this.messagePayload.msg_type = 7;
const [_, _version, target_type, target_id] = this.baseUrl.split('/');
const result = await this.bot.uploadMedia(target_id, target_type.slice(0, -1), elem.file, this.getType(elem.type));
this.messagePayload.media = { file_info: result.file_info };
}
}
else {
if (!this.baseUrl.startsWith('/v2')) {
const fileData = await this.fixGuildMediaData(elem);
if (typeof fileData !== 'string') {
this.messagePayload.file_image = fileData;
}
else {
this.messagePayload.image = elem.file;
}
}
else {
this.filePayload.file_type = this.getType(elem.type);
const [_, _version, target_type, target_id] = this.baseUrl.split('/');
this.filePayload.file_data = await this.bot.uploadMedia(target_id, target_type.slice(0, -1), elem.file, this.getType(elem.type));
this.isFile = true;
}
}
this.brief += `<${elem.type}:${(0, utils_1.md5)(elem.file)}>`;
break;
case 'markdown':
this.messagePayload.markdown = data;
this.messagePayload.msg_type = 2;
this.brief += `<markdown,${elem.content ? `content=${elem.content}` : `template_id=${elem.custom_template_id}`}>`;
break;
case 'keyboard':
this.messagePayload.msg_type = 2;
this.messagePayload.keyboard = data;
this.messagePayload.bot_appid = this.bot.config.appid;
break;
case 'button':
this.buttons.push(data);
this.brief += `<button,data=${JSON.stringify(data)}>`;
break;
case "embed":
if (this.baseUrl.startsWith('/v2'))
break;
case "ark":
this.messagePayload.msg_type = type === 'ark' ? 3 : 4;
this.messagePayload[type] = data;
this.brief += `<${type}>`;
break;
}
}
if (this.buttons.length) {
const rows = [];
let row = [];
for (let i = 0; i < this.buttons.length; i++) {
if (row.length > 5) {
rows.push(row);
row = [];
}
if (Array.isArray(this.buttons[i].buttons)) {
rows.push(this.buttons[i].buttons);
continue;
}
row.push(this.buttons[i]);
}
this.messagePayload.keyboard = {
content: {
rows: rows.map(row => {
return {
buttons: row
};
})
},
bot_appid: this.bot.config.appid
};
}
}
async waitAudit(id) {
if (!this.bot.config.intents?.includes('MESSAGE_AUDIT'))
throw new Error(`message is waiting for audit:${id}`);
return new Promise(resolve => {
const listener = async (event) => {
if (event.audit_id !== id)
return;
this.bot.off('message.audit', listener);
resolve({
id: event.message_id,
timestamp: event.create_time
});
};
this.bot.on('message.audit', listener);
});
}
async sendMsg() {
await this.processMessage();
if (this.isFile) {
const { data: result } = await this.bot.request.post(this.baseUrl + '/files', this.filePayload);
return result;
}
const { data: result } = await this.bot.request.post(this.baseUrl + '/messages', this.messagePayload, {
headers: {
'Content-Type': this.contentType
}
});
if (Reflect.has(result, 'message_audit'))
return this.waitAudit(result.message_audit.audit_id);
const { id, timestamp, ...other } = result;
return {
id,
timestamp: new Date(timestamp).getTime() / 1000,
...other
};
}
}
exports.Sender = Sender;