Skip to content

Commit

Permalink
optimize: send dynamicMsg
Browse files Browse the repository at this point in the history
  • Loading branch information
snowtafir committed Jan 26, 2025
1 parent be51a2a commit 593d3ee
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 119 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# 2.0.6
* 优化消息发送
* 优化文字动态图片资源的发送
* 依赖升级
* 新增哔哩直播动态@全体成员功能,开启前请检查机器人管理员权限和所在聊天类型是否支持
Expand Down
3 changes: 3 additions & 0 deletions defaultConfig/bilibili/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,6 @@ liveAtAllGroupList:

# 直播动态@全体成员的共享冷却时间CD,单位秒,默认 1800 秒(30分钟),即每个群聊30分钟内不论多少条直播动态,只会@一次。注意,qq群有 @全体成员 10次/日 的限制,所以请合理设置。
liveAtAllCD: 1800

# 直播动态@全体成员失败时是否发送错误消息,默认 1 发送,0 不发送。开启liveAtAll后才会生效。
liveAtAllErrMsg: 1
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "yz-yuki-plugin",
"version": "2.0.6-10",
"version": "2.0.6-11",
"description": "优纪插件,yunzaijs 关于 微博推送、B站推送 等功能的拓展插件",
"author": "snowtafir",
"type": "module",
Expand Down
12 changes: 9 additions & 3 deletions src/models/bilibili/bilibili.main.query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,15 @@ export class BiliQuery {
content: any,
dynamicTitle: any;
let title = `B站【${upName}】动态推送:\n`;
let dynamicType: string = data.type;

switch (data.type) {
let dynamicType:
| 'DYNAMIC_TYPE_AV'
| 'DYNAMIC_TYPE_WORD'
| 'DYNAMIC_TYPE_DRAW'
| 'DYNAMIC_TYPE_ARTICLE'
| 'DYNAMIC_TYPE_FORWARD'
| 'DYNAMIC_TYPE_LIVE_RCMD' = data.type;

switch (dynamicType) {
case 'DYNAMIC_TYPE_AV':
// 处理视频动态
desc = data?.modules?.module_dynamic?.major?.archive;
Expand Down
236 changes: 163 additions & 73 deletions src/models/bilibili/bilibili.main.task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export class BiliTask {
return resjson;
}

/**
* 执行动态推送任务
*/
async runTask() {
let biliConfigData = await Config.getUserConfig('bilibili', 'config');
let biliPushData = await Config.getUserConfig('bilibili', 'push');
Expand All @@ -50,7 +53,16 @@ export class BiliTask {
await this.processBiliData(biliPushData, biliConfigData, uidMap, dynamicList);

let now: number = Date.now() / 1000; // 时间戳(秒)
await this.pushDynamicMessages(uidMap, dynamicList, now, interval, biliConfigData);

// 定义待推送动态消息映射
const messageMap: Map<
string,
Map<string | number, Map<string | number, { sendMode: string; dynamicUUid_str: string; dynamicType: string; messages: any[] }[]>>
> = new Map();

await this.makeUidDynamicDataMap(uidMap, dynamicList, now, interval, biliConfigData, messageMap);

await this.sendDynamicMessage(messageMap, biliConfigData);
}

/**
Expand Down Expand Up @@ -133,18 +145,25 @@ export class BiliTask {
}
}
}
requestedDataOfUids.clear(); // 清空已请求的映射
requestedDataOfUids.clear(); // 清空已请求的 uid 映射
}

/**
* 推送动态消息
* 构建uid对应动态数据映射
* @param uidMap uid 映射
* @param dynamicList 动态列表
* @param now 当前时间戳
* @param interval 推送间隔时间
* @param biliConfigData Bilibili配置数据
*/
async pushDynamicMessages(uidMap: Map<any, Map<string, any>>, dynamicList: any, now: number, interval: number, biliConfigData: any) {
async makeUidDynamicDataMap(
uidMap: Map<any, Map<string, any>>,
dynamicList: any,
now: number,
interval: number,
biliConfigData: any,
messageMap: Map<string, Map<string | number, Map<string | number, { sendMode: string; dynamicUUid_str: string; dynamicType: string; messages: any[] }[]>>>
) {
for (let [chatType, chatTypeMap] of uidMap) {
for (let [key, value] of chatTypeMap) {
const tempDynamicList = dynamicList[key] || [];
Expand Down Expand Up @@ -176,7 +195,7 @@ export class BiliTask {
if (chatIds && chatIds.length) {
for (let chatId of chatIds) {
if (type && type.length && !type.includes(pushDynamicData.type)) continue; // 如果禁用了某类型的动态推送,跳过当前循环
await this.sendDynamic(chatId, bot_id, upName, pushDynamicData, biliConfigData, chatType); // 发送动态消息
await this.makeDynamicMessageMap(chatId, bot_id, upName, pushDynamicData, biliConfigData, chatType, messageMap); // 发送动态消息
await this.randomDelay(1000, 2000); // 随机延时1-2秒
}
}
Expand All @@ -186,15 +205,23 @@ export class BiliTask {
}

/**
* 发送动态消息
* 渲染构建待发送的动态消息数据的映射数组
* @param chatId 聊天 ID
* @param bot_id 机器人 ID
* @param upName 用户名
* @param upName up主用户名
* @param pushDynamicData 推送动态数据
* @param biliConfigData 哔哩配置数据
* @param chatType 聊天类型
*/
async sendDynamic(chatId: string | number, bot_id: string | number, upName: string, pushDynamicData: any, biliConfigData: any, chatType: string) {
async makeDynamicMessageMap(
chatId: string | number,
bot_id: string | number,
upName: string,
pushDynamicData: any,
biliConfigData: any,
chatType: string,
messageMap: Map<string, Map<string | number, Map<string | number, { sendMode: string; dynamicUUid_str: string; dynamicType: string; messages: any[] }[]>>>
) {
const id_str = pushDynamicData.id_str;

let sended: string | null = null,
Expand All @@ -208,15 +235,9 @@ export class BiliTask {
}
if (sended) return; // 如果已经发送过,则直接返回

let liveAtAll: boolean = !!biliConfigData.liveAtAll === true ? true : false; // 直播动态是否@全体成员,默认false
let liveAtAllCD: number = biliConfigData.liveAtAllCD || 1800; // 直播动态@全体成员 冷却时间CD,默认 30 分钟
let liveAtAllMark: number | string | null = await Redis.get(`${markKey}${chatId}:liveAtAllMark`); // 直播动态@全体成员标记,默认 0
// 直播动态@全体成员的群组列表,默认空数组,为空则不进行@全体成员操作
let liveAtAllGroupList = new Set(
Array.isArray(biliConfigData?.liveAtAllGroupList) ? Array.from(biliConfigData.liveAtAllGroupList).map(item => String(item)) : []
);

if (!!biliConfigData.pushMsgMode) {
// 判断推送内容模式
let pushMsgMode: string = !!biliConfigData.pushMsgMode === false ? 'TEXT' : 'PIC'; // 是否启用图文模式,默认为 PIC 截图模式
if (pushMsgMode === 'PIC') {
const { data, uid } = await BiliQuery.formatDynamicData(pushDynamicData); // 处理动态数据
const extentData = { ...data };

Expand Down Expand Up @@ -248,34 +269,21 @@ export class BiliTask {
};

let imgs: Buffer[] | null = await this.renderDynamicCard(uid, renderData, ScreenshotOptionsData);
if (!imgs) return;

Redis.set(`${markKey}${chatId}:${id_str}`, '1', { EX: 3600 * 72 }); // 设置已发送标记

global?.logger?.mark('优纪插件:B站动态执行推送');

if (liveAtAll && !liveAtAllMark && extentData?.type === 'DYNAMIC_TYPE_LIVE_RCMD' && liveAtAllGroupList.has(String(chatId))) {
try {
await this.sendMessage(chatId, bot_id, chatType, Segment.at('all'));
await Redis.set(`${markKey}${chatId}:liveAtAllMark`, 1, { EX: liveAtAllCD }); // 设置直播动态@全体成员标记为 1
} catch (error) {
logger.error(`直播动态发送@全体成员失败,请检查 <机器人> 是否有 [管理员权限] 或 [聊天平台是否支持] :${error}`);
await this.sendMessage(chatId, bot_id, chatType, ['直播动态发送@全体成员失败,请检查权限或平台是否支持']);
}
}

for (let i = 0; i < imgs.length; i++) {
const image: Buffer = imgs[i];
await this.sendMessage(chatId, bot_id, chatType, Segment.image(image));
await this.randomDelay(1000, 2000); // 随机延时1-2秒
}

await new Promise(resolve => setTimeout(resolve, 1000)); // 休眠1秒
if (!imgs) return; // 如果渲染失败,则直接返回

await this.addMessageToMap(
messageMap,
chatType,
bot_id,
chatId,
'SINGLE',
id_str,
extentData?.type,
imgs.map(img => Segment.image(img))
);
} else {
const dynamicMsg = await BiliQuery.formatTextDynamicData(upName, pushDynamicData, false, biliConfigData); // 构建图文动态消息

Redis.set(`${markKey}${chatId}:${id_str}`, '1', { EX: 3600 * 72 }); // 设置已发送标记

if (dynamicMsg === undefined || dynamicMsg === 'continue') {
return 'return'; // 如果动态消息构建失败,则直接返回
}
Expand All @@ -289,41 +297,17 @@ export class BiliTask {
}

let mergeTextPic: boolean = !!biliConfigData.mergeTextPic === false ? false : true; // 是否合并文本和图片,默认为 true
//开启了合并文本和图片
if (mergeTextPic === true) {
const mergeMsg = [...dynamicMsg.msg, ...dynamicMsg.pics];
if (liveAtAll && !liveAtAllMark && dynamicMsg.dynamicType === 'DYNAMIC_TYPE_LIVE_RCMD' && liveAtAllGroupList.has(String(chatId))) {
try {
await this.sendMessage(chatId, bot_id, chatType, Segment.at('all'));
await Redis.set(`${markKey}${chatId}:liveAtAllMark`, 1, { EX: liveAtAllCD }); // 设置直播动态@全体成员标记为 1
} catch (error) {
global?.logger.error(`直播动态发送@全体成员失败,请检查 <机器人> 是否有 [管理员权限] 或 [聊天平台是否支持] :${error}`);
await this.sendMessage(chatId, bot_id, chatType, ['直播动态发送@全体成员失败,请检查权限或平台是否支持']);
}
}
await this.sendMessage(chatId, bot_id, chatType, mergeMsg);
await this.addMessageToMap(messageMap, chatType, bot_id, chatId, 'MERGE', id_str, dynamicMsg.dynamicType, mergeMsg);
} else {
if (liveAtAll && !liveAtAllMark && dynamicMsg.dynamicType === 'DYNAMIC_TYPE_LIVE_RCMD' && liveAtAllGroupList.has(String(chatId))) {
try {
await this.sendMessage(chatId, bot_id, chatType, Segment.at('all'));
await Redis.set(`${markKey}${chatId}:liveAtAllMark`, 1, { EX: liveAtAllCD }); // 设置直播动态@全体成员标记为 1
} catch (error) {
global?.logger.error(`直播动态发送@全体成员失败,请检查 <机器人> 是否有 [管理员权限] 或 [聊天平台是否支持] :${error}`);
await this.sendMessage(chatId, bot_id, chatType, ['直播动态发送@全体成员失败,请检查权限或平台是否支持']);
}
}
await this.sendMessage(chatId, bot_id, chatType, dynamicMsg.msg);
const pics = dynamicMsg.pics;
if (pics && pics.length > 0) {
for (let i = 0; i < pics.length; i++) {
await this.sendMessage(chatId, bot_id, chatType, pics[i]);
await this.randomDelay(1000, 2000); // 随机延时1-2秒
}
}
await new Promise(resolve => setTimeout(resolve, 1000));
//不合并文本和图片
await this.addMessageToMap(messageMap, chatType, bot_id, chatId, 'MERGE', id_str, dynamicMsg.dynamicType, dynamicMsg.msg);
await this.addMessageToMap(messageMap, chatType, bot_id, chatId, 'SINGLE', id_str, dynamicMsg.dynamicType, dynamicMsg.pics);
}
}
}

/**
* 构建渲染数据
* @param extentData 扩展数据
Expand Down Expand Up @@ -401,13 +385,119 @@ export class BiliTask {
}

/**
* 发送消息
* 收集消息映射
* @param messageMap 消息映射
* @param chatType 聊天类型
* @param bot_id 机器人 ID
* @param chatId 聊天 ID
* @param sendMode 发送模式: SINGLE 逐条发送,MERGE 合并发送
* @param dynamicUUid_str 动态 UUID
* @param dynamicType 动态类型
* @param message 消息内容
*/
async addMessageToMap(
messageMap: Map<string, Map<string | number, Map<string | number, { sendMode: string; dynamicUUid_str: string; dynamicType: string; messages: any[] }[]>>>,
chatType: string,
bot_id: string | number,
chatId: string | number,
sendMode: string,
dynamicUUid_str: string,
dynamicType: string,
messages: any
) {
if (!messageMap.has(chatType)) {
messageMap.set(chatType, new Map());
}
const botMap = messageMap.get(chatType);
if (!botMap?.has(bot_id)) {
botMap?.set(bot_id, new Map());
}
const chatMap = botMap?.get(bot_id);
if (!chatMap?.has(chatId)) {
chatMap?.set(chatId, []);
}
chatMap?.get(chatId)?.push({ sendMode, dynamicUUid_str, dynamicType, messages });
}

/**
* 推送动态消息
* @param messageMap 消息映射
* @param biliConfigData 哔哩配置数据
*/
async sendDynamicMessage(
messageMap: Map<string, Map<string | number, Map<string | number, { sendMode: string; dynamicUUid_str: string; dynamicType: string; messages: any[] }[]>>>,
biliConfigData: { [key: string]: any }
) {
let liveAtAll: boolean = !!biliConfigData.liveAtAll === true ? true : false; // 直播动态是否@全体成员,默认false
let liveAtAllCD: number = biliConfigData.liveAtAllCD || 1800; // 直播动态@全体成员 冷却时间CD,默认 30 分钟
// 直播动态@全体成员的群组/好友列表,默认空数组,为空则不进行@全体成员操作
let liveAtAllGroupList = new Set(
Array.isArray(biliConfigData?.liveAtAllGroupList) ? Array.from(biliConfigData.liveAtAllGroupList).map(item => String(item)) : []
);
const LogMark = new Set(); // 日志mark
for (const [chatType, botMap] of messageMap) {
for (const [bot_id, chatMap] of botMap) {
for (const [chatId, messageCombinationList] of chatMap) {
// 遍历组合消息
for (const messageCombination of messageCombinationList) {
const { sendMode, dynamicUUid_str, dynamicType, messages } = messageCombination;
let sended: string | null = null;
let markKey: string = '';
if (chatType === 'group') {
markKey = this.groupKey;
sended = await Redis.get(`${markKey}${chatId}:${dynamicUUid_str}`);
} else if (chatType === 'private') {
markKey = this.privateKey;
sended = await Redis.get(`${markKey}${chatId}:${dynamicUUid_str}`);
}
const sendMarkKey = `${markKey}${chatId}:${dynamicUUid_str}`;
if (sended) {
continue; // 如果已经发送过,则直接跳过
}
if (!LogMark.has('1')) {
global?.logger?.mark('优纪插件: B站动态执行推送');
LogMark.add('1');
}
let liveAtAllMark: number | string | null = await Redis.get(`${markKey}${chatId}:liveAtAllMark`); // 直播动态@全体成员标记,默认 0
// 如果开启了直播动态@全体成员
if (liveAtAll && !liveAtAllMark && dynamicType === 'DYNAMIC_TYPE_LIVE_RCMD' && liveAtAllGroupList.has(String(chatId))) {
try {
await this.sendMessageApi(chatId, bot_id, chatType, [Segment.at('all')]);
await Redis.set(`${markKey}${chatId}:liveAtAllMark`, 1, { EX: liveAtAllCD }); // 设置直播动态@全体成员标记为 1
} catch (error) {
logger.error(`直播动态发送@全体成员失败,请检查 <机器人> 是否有 [管理员权限] 或 [聊天平台是否支持] :${error}`);
let liveAtAllErrMsg: boolean = !!biliConfigData.liveAtAllErrMsg === false ? false : true; // 直播动态@全体成员失败是否发送错误提示消息,默认 false
if (liveAtAllErrMsg) {
await this.sendMessageApi(chatId, bot_id, chatType, ['直播动态发送@全体成员失败,请检查权限或平台是否支持']);
}
}
}

if (sendMode === 'SINGLE') {
for (let i = 0; i < messages.length; i++) {
await this.sendMessageApi(chatId, bot_id, chatType, messages[i]);
}
await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
await this.randomDelay(1000, 2000); // 随机延时1-2秒
} else if (sendMode === 'MERGE') {
await this.sendMessageApi(chatId, bot_id, chatType, messages);
await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
}
}
}
}
}
LogMark.clear(); // 清空日志mark
}

/**
* 发送消息api
* @param chatId 聊天 ID
* @param bot_id 机器人 ID
* @param chatType 聊天类型
* @param message 消息内容
*/
async sendMessage(chatId: string | number, bot_id: string | number, chatType: string, message: any) {
async sendMessageApi(chatId: string | number, bot_id: string | number, chatType: string, message: any) {
if (chatType === 'group') {
await (Bot[bot_id] ?? Bot)
?.pickGroup(String(chatId))
Expand Down
Loading

0 comments on commit 593d3ee

Please sign in to comment.