diff --git a/src/AI/AI.ts b/src/AI/AI.ts index 3ccbd93..7978e28 100644 --- a/src/AI/AI.ts +++ b/src/AI/AI.ts @@ -15,7 +15,12 @@ export interface Privilege { counter: number, timer: number, prob: number, - standby: boolean + standby: boolean, + activeTimeInfo: { + start: number, + end: number, + segs: number + } } export class AI { @@ -51,7 +56,12 @@ export class AI { counter: -1, timer: -1, prob: -1, - standby: false + standby: false, + activeTimeInfo: { + start: 0, + end: 0, + segs: 0 + } }; this.stream = { id: '', @@ -309,6 +319,43 @@ export class AI { await this.stopCurrentChatStream(); } + // 若不在活动时间范围内,返回-1 + getCurSegIndex(): number { + const now = new Date(); + const cur = now.getHours() * 60 + now.getMinutes(); + const { start, end, segs } = this.privilege.activeTimeInfo; + const endReal = end >= start ? end : end + 24 * 60; + const curReal = cur >= start ? cur : cur + 24 * 60; + + if (curReal >= endReal) return -1; + + const segLen = (endReal - start) / segs; + const index = Math.floor((curReal - start) / segLen); + return Math.min(index, segs - 1); + } + + // 若没有下一个活跃时间点,返回-1 + getNextTimePoint(curSegIndex: number): number { + const { start, end, segs } = this.privilege.activeTimeInfo; + + if (start === 0 && end === 0) return -1; + + const endReal = end >= start ? end : end + 24 * 60; + const segLen = (endReal - start) / segs; + const nextSegIndex = (curSegIndex + 1) % segs; + const todayMin = Math.floor(start + nextSegIndex * segLen + Math.random() * segLen) % (24 * 60); + + const nextTime = new Date(); + nextTime.setHours(Math.floor(todayMin / 60), todayMin % 60, Math.floor(Math.random() * 60), 0); + + // 如果时间已过,设置为明天 + if (nextTime.getTime() <= Date.now()) { + nextTime.setDate(nextTime.getDate() + 1); + } + + return Math.floor(nextTime.getTime() / 1000); + } + async stopCurrentChatStream(): Promise { const { id, reply, toolCallStatus } = this.stream; this.stream = { @@ -329,7 +376,7 @@ export class AI { } export class AIManager { - static version = "1.0.0"; + static version = "1.0.1"; static cache: { [key: string]: AI } = {}; static usageMap: { [key: string]: { // 模型名 diff --git a/src/AI/context.ts b/src/AI/context.ts index 2846ac9..993583d 100644 --- a/src/AI/context.ts +++ b/src/AI/context.ts @@ -7,6 +7,12 @@ import { AI, AIManager } from "./AI"; import { logger } from "../logger"; import { transformMsgId } from "../utils/utils"; +export interface MessageInfo { + msgId: string; + time: number; // 秒 + content: string; +} + export interface Message { role: string; tool_calls?: ToolCall[]; @@ -14,9 +20,8 @@ export interface Message { uid: string; name: string; - contentArray: string[]; - msgIdArray: string[]; images: Image[]; + msgArray: MessageInfo[]; } export class Context { @@ -114,18 +119,23 @@ export class Context { const uid = role == 'user' ? ctx.player.userId : ctx.endPoint.userId; const length = messages.length; if (length !== 0 && messages[length - 1].uid === uid && !//.test(s)) { - messages[length - 1].contentArray.push(s); - messages[length - 1].msgIdArray.push(msgId); messages[length - 1].images.push(...images); + messages[length - 1].msgArray.push({ + msgId: msgId, + time: Math.floor(Date.now() / 1000), + content: s + }); } else { - const message = { + const message: Message = { role: role, - content: '', uid: uid, name: name, - contentArray: [s], - msgIdArray: [msgId], - images: images + images: images, + msgArray: [{ + msgId: msgId, + time: Math.floor(Date.now() / 1000), + content: s + }] }; messages.push(message); @@ -149,27 +159,29 @@ export class Context { } async addToolCallsMessage(tool_calls: ToolCall[]) { - const message = { + const message: Message = { role: 'assistant', tool_calls: tool_calls, uid: '', name: '', - contentArray: [], - msgIdArray: [], - images: [] + images: [], + msgArray: [] }; this.messages.push(message); } async addToolMessage(tool_call_id: string, s: string) { - const message = { + const message: Message = { role: 'tool', tool_call_id: tool_call_id, uid: '', name: '', - contentArray: [s], - msgIdArray: [''], - images: [] + images: [], + msgArray: [{ + msgId: '', + time: Math.floor(Date.now() / 1000), + content: s + }] }; for (let i = this.messages.length - 1; i >= 0; i--) { @@ -183,14 +195,16 @@ export class Context { } async addSystemUserMessage(name: string, s: string, images: Image[]) { - const message = { + const message: Message = { role: 'user', - content: s, uid: '', name: `_${name}`, - contentArray: [s], - msgIdArray: [''], - images: images + images: images, + msgArray: [{ + msgId: '', + time: Math.floor(Date.now() / 1000), + content: s + }] }; this.messages.push(message); } diff --git a/src/AI/memory.ts b/src/AI/memory.ts index 2013ab4..fe7cb9b 100644 --- a/src/AI/memory.ts +++ b/src/AI/memory.ts @@ -5,8 +5,9 @@ import { Context } from "./context"; import { generateId } from "../utils/utils"; import { logger } from "../logger"; import { fetchData } from "../service"; -import { parseBody } from "../utils/utils_message"; +import { buildContent, parseBody } from "../utils/utils_message"; import { ToolManager } from "../tool/tool"; +import { fmtTime } from "../utils/utils_string"; export interface MemoryInfo { id: string; @@ -75,7 +76,7 @@ export class Memory { groupId: ctx.group.groupId, groupName: ctx.group.groupName }, - time: new Date().toLocaleString(), + time: fmtTime(Math.floor(Date.now() / 1000)), createTime: Math.floor(Date.now() / 1000), lastMentionTime: Math.floor(Date.now() / 1000), keywords: kws || [], @@ -152,7 +153,7 @@ export class Memory { } const { url: chatUrl, apiKey: chatApiKey } = ConfigManager.request; - const { roleSettingTemplate, isPrefix, showNumber, showMsgId } = ConfigManager.message; + const { roleSettingTemplate, isPrefix, showNumber, showMsgId, showTime } = ConfigManager.message; const { shortMemorySummaryRound, memoryUrl, memoryApiKey, memoryBodyTemplate, memoryPromptTemplate } = ConfigManager.memory; const messages = ai.context.messages; @@ -195,18 +196,13 @@ export class Memory { "群聊号码": ctx.group.groupId.replace(/^.+:/, ''), "添加前缀": isPrefix, "展示消息ID": showMsgId, + "展示时间": showTime, "对话内容": isPrefix ? sumMessages.map(message => { if (message.role === 'assistant' && message?.tool_calls && message?.tool_calls.length > 0) { return `\n[function_call]: ${message.tool_calls.map((tool_call, index) => `${index + 1}. ${JSON.stringify(tool_call.function, null, 2)}`).join('\n')}`; } - const prefix = (isPrefix && message.name) ? ( - message.name.startsWith('_') ? - `<|${message.name}|>` : - `<|from:${message.name}${showNumber ? `(${message.uid.replace(/^.+:/, '')})` : ``}|>` - ) : ''; - const content = message.msgIdArray.map((msgId, index) => (showMsgId && msgId ? `<|msg_id:${msgId}|>` : '') + message.contentArray[index]).join('\f'); - - return `[${message.role}]: ${prefix}${content}`; + + return `[${message.role}]: ${buildContent(message)}`; }).join('\n') : JSON.stringify(sumMessages) }) @@ -361,7 +357,7 @@ export class Memory { buildMemoryPrompt(ctx: seal.MsgContext, context: Context): string { const userMessages = context.messages.filter(msg => msg.role === 'user' && !msg.name.startsWith('_')); - const lastMsg = userMessages.length > 0 ? userMessages[userMessages.length - 1].contentArray.join('') : ''; + const lastMsg = userMessages.length > 0 ? userMessages[userMessages.length - 1].msgArray.map(mi => mi.content).join('') : ''; const ai = AIManager.getAI(ctx.endPoint.userId); let s = ai.memory.buildMemory(true, seal.formatTmpl(ctx, "核心:骰子名字"), ctx.endPoint.userId, '', '', lastMsg); diff --git a/src/config/config_memory.ts b/src/config/config_memory.ts index 944a359..88b60df 100644 --- a/src/config/config_memory.ts +++ b/src/config/config_memory.ts @@ -60,6 +60,9 @@ export class MemoryConfig { {{#if 展示消息ID}} - <|msg_id:xxx|>表示消息ID,仅用于调用函数时使用,不要在生成的回复中提及或使用 - <|quote:xxx|>表示引用消息,xxx为对应的消息ID +{{/if}} +{{#if 展示时间}} + - <|time:xxxx-xx-xx xx:xx:xx|>表示消息发送时间,不要在生成的回复中提及或使用 {{/if}} - \\f用于分割多条消息 diff --git a/src/config/config_message.ts b/src/config/config_message.ts index bb460af..e68e3c2 100644 --- a/src/config/config_message.ts +++ b/src/config/config_message.ts @@ -86,6 +86,9 @@ export class MessageConfig { {{#if 展示消息ID}} - <|msg_id:xxx|>表示消息ID,仅用于调用函数时使用,不要在生成的回复中提及或使用 - <|quote:xxx|>表示引用消息,xxx为对应的消息ID +{{/if}} +{{#if 展示时间}} + - <|time:xxxx-xx-xx xx:xx:xx|>表示消息发送时间,不要在生成的回复中提及或使用 {{/if}} - \\f用于分割多条消息 {{#if 接收图片}} @@ -143,6 +146,7 @@ export class MessageConfig { seal.ext.registerBoolConfig(MessageConfig.ext, "是否在消息内添加前缀", true, "可用于辨别不同用户"); seal.ext.registerBoolConfig(MessageConfig.ext, "是否给AI展示数字号码", true, "例如QQ号和群号,能力较弱模型可能会出现幻觉"); seal.ext.registerBoolConfig(MessageConfig.ext, "是否在消息内添加消息ID", false, "可用于撤回等情况"); + seal.ext.registerBoolConfig(MessageConfig.ext, "是否在消息内添加发送时间", false, "将消息发送时间添加到上下文中"); seal.ext.registerBoolConfig(MessageConfig.ext, "是否合并user content", false, "在不支持连续多个role为user的情况下开启,可用于适配deepseek-reasoner"); seal.ext.registerIntConfig(MessageConfig.ext, "存储上下文对话限制轮数", 15, "出现一次user视作一轮"); seal.ext.registerIntConfig(MessageConfig.ext, "上下文插入system message间隔轮数", 0, "需要小于限制轮数的二分之一才能生效,为0时不生效,示例对话不计入轮数"); @@ -156,6 +160,7 @@ export class MessageConfig { isPrefix: seal.ext.getBoolConfig(MessageConfig.ext, "是否在消息内添加前缀"), showNumber: seal.ext.getBoolConfig(MessageConfig.ext, "是否给AI展示数字号码"), showMsgId: seal.ext.getBoolConfig(MessageConfig.ext, "是否在消息内添加消息ID"), + showTime: seal.ext.getBoolConfig(MessageConfig.ext, "是否在消息内添加发送时间"), isMerge: seal.ext.getBoolConfig(MessageConfig.ext, "是否合并user content"), maxRounds: seal.ext.getIntConfig(MessageConfig.ext, "存储上下文对话限制轮数"), insertCount: seal.ext.getIntConfig(MessageConfig.ext, "上下文插入system message间隔轮数") diff --git a/src/index.ts b/src/index.ts index f46fa9f..f6e3b44 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import { ConfigManager, CQTYPESALLOW } from "./config/config"; import { buildSystemMessage } from "./utils/utils_message"; import { triggerConditionMap } from "./tool/tool_trigger"; import { logger } from "./logger"; -import { transformTextToArray } from "./utils/utils_string"; +import { fmtTime, transformTextToArray } from "./utils/utils_string"; import { checkUpdate } from "./utils/utils_update"; import { get_chart_url } from "./service"; import { TimerManager } from "./timer"; @@ -27,6 +27,7 @@ function main() { 【.ai prompt】检查当前prompt(仅骰主可用) 【.ai status】查看当前AI状态 【.ai ctxn】查看上下文里的名字 +【.ai timer】查看当前聊天定时器 【.ai on】开启AI 【.ai sb】开启待机模式,此时AI将记忆聊天内容 【.ai off】关闭AI,此时仍能用关键词触发 @@ -129,7 +130,7 @@ function main() { const systemMessage = buildSystemMessage(ctx, ai); - seal.replyToSender(ctx, msg, systemMessage.contentArray[0]); + seal.replyToSender(ctx, msg, systemMessage.msgArray[0].content); return ret; } case 'status': { @@ -139,12 +140,16 @@ function main() { return ret; } + const { start, end, segs } = pr.activeTimeInfo; + seal.replyToSender(ctx, msg, `${id} 权限限制: ${pr.limit} 上下文轮数: ${ai.context.messages.filter(m => m.role === 'user').length} 计数器模式(c): ${pr.counter > -1 ? `${pr.counter}条` : '关闭'} 计时器模式(t): ${pr.timer > -1 ? `${pr.timer}秒` : '关闭'} 概率模式(p): ${pr.prob > -1 ? `${pr.prob}%` : '关闭'} +活跃时间段: ${(start !== 0 || end !== 0) ? `${Math.floor(start / 60).toString().padStart(2, '0')}:${(start % 60).toString().padStart(2, '0')}至${Math.floor(end / 60).toString().padStart(2, '0')}:${(end % 60).toString().padStart(2, '0')}` : '未设置'} +活跃次数: ${segs > 0 ? segs : '未设置'} 待机模式: ${pr.standby ? '开启' : '关闭'}`); return ret; } @@ -160,6 +165,28 @@ function main() { seal.replyToSender(ctx, msg, s); return ret; } + case 'timer': { + const pr = ai.privilege; + if (ctx.privilegeLevel < pr.limit) { + seal.replyToSender(ctx, msg, seal.formatTmpl(ctx, "核心:提示_无权限")); + return ret; + } + + const timers = TimerManager.timerQueue.filter(t => t.id === ai.id); + + if (timers.length === 0) { + seal.replyToSender(ctx, msg, '当前对话没有定时器'); + return ret; + } + + const s = timers.map((t, i) => { + return `${i + 1}. 触发内容:${t.content} +${t.setTime} => ${fmtTime(t.timestamp)} +类型:${t.type}`; + }).join('\n'); + seal.replyToSender(ctx, msg, s); + return ret; + } case 'on': { const pr = ai.privilege; if (ctx.privilegeLevel < pr.limit) { @@ -179,38 +206,91 @@ function main() { 单位/秒,默认60秒 【p】概率模式,每条消息按概率触发 单位/%,默认10% +【a】活跃时间段和活跃次数 +格式为"开始时间-结束时间-活跃次数"(如"09:00-18:00-5") 【.ai on --t --p=42】使用示例`); return ret; } let text = `AI已开启:`; - kwargs.forEach(kwarg => { + for (const kwarg of kwargs) { const name = kwarg.name; const exist = kwarg.valueExists; - const value = parseFloat(kwarg.value); + const valInt = parseInt(kwarg.value); + const valFloat = parseFloat(kwarg.value); + const valStr = kwarg.value.trim(); switch (name) { case 'c': case 'counter': { - pr.counter = exist && !isNaN(value) ? value : 10; + ai.context.counter = 0; + pr.counter = exist && !isNaN(valInt) ? valInt : 10; text += `\n计数器模式:${pr.counter}条`; break; } case 't': case 'timer': { - pr.timer = exist && !isNaN(value) ? value : 60; + clearTimeout(ai.context.timer); + ai.context.timer = null; + pr.timer = exist && !isNaN(valFloat) ? valFloat : 60; text += `\n计时器模式:${pr.timer}秒`; break; } case 'p': case 'prob': { - pr.prob = exist && !isNaN(value) ? value : 10; + pr.prob = exist && !isNaN(valFloat) ? valFloat : 10; text += `\n概率模式:${pr.prob}%`; break; } + case 'a': + case 'active': { + if (!exist) { + seal.replyToSender(ctx, msg, '请输入活跃时间段'); + return ret; + } + + const arr = valStr.split('-').map((item, index) => { + const parts = item.split(/[::,,]+/).map(Number).map(i => isNaN(i) ? 0 : i); + if (index < 2) { + return (parts[0] * 60 + (parts[1] || 0)) % (24 * 60); + } else { + return parts[0]; + } + }) + + const [start = 0, end = 0, segs = 1] = arr; + + if (start === end) { + seal.replyToSender(ctx, msg, '活跃时间段开始时间和结束时间不能相同'); + return ret; + } + + const endReal = end >= start ? end : end + 24 * 60; + if (segs > endReal - start) { + seal.replyToSender(ctx, msg, '活跃次数不能大于活跃时间段分钟数'); + return ret; + } + + TimerManager.removeTimer(id, '', 'activeTime', []); + pr.activeTimeInfo = { + start, + end, + segs, + } + + text += `\n活跃时间段:${Math.floor(start / 60).toString().padStart(2, '0')}:${(start % 60).toString().padStart(2, '0')}至${Math.floor(end / 60).toString().padStart(2, '0')}:${(end % 60).toString().padStart(2, '0')}`; + text += `\n活跃次数:${segs}`; + + const curSegIndex = ai.getCurSegIndex(); + const nextTimePoint = ai.getNextTimePoint(curSegIndex); + if (nextTimePoint !== -1) { + TimerManager.addTimer(ctx, msg, ai, nextTimePoint, '', 'activeTime'); + } + break; + } } - }); + }; pr.standby = true; @@ -225,12 +305,18 @@ function main() { return ret; } + ai.resetState(); + TimerManager.removeTimer(id, '', 'activeTime', []); + pr.counter = -1; pr.timer = -1; pr.prob = -1; pr.standby = true; - - ai.resetState(); + pr.activeTimeInfo = { + start: 0, + end: 0, + segs: 0, + } seal.replyToSender(ctx, msg, 'AI已开启待机模式'); AIManager.saveAI(id); @@ -245,12 +331,18 @@ function main() { const kwargs = cmdArgs.kwargs; if (kwargs.length == 0) { + ai.resetState(); + TimerManager.removeTimer(id, '', 'activeTime', []); + pr.counter = -1; pr.timer = -1; pr.prob = -1; pr.standby = false; - - ai.resetState(); + pr.activeTimeInfo = { + start: 0, + end: 0, + segs: 0, + } seal.replyToSender(ctx, msg, 'AI已关闭'); AIManager.saveAI(id); @@ -264,12 +356,15 @@ function main() { switch (name) { case 'c': case 'counter': { + ai.context.counter = 0; pr.counter = -1; text += `\n计数器模式`; break; } case 't': case 'timer': { + clearTimeout(ai.context.timer); + ai.context.timer = null; pr.timer = -1; text += `\n计时器模式`; break; @@ -280,11 +375,20 @@ function main() { text += `\n概率模式`; break; } + case 'a': + case 'active': { + TimerManager.removeTimer(id, '', 'activeTime', []); + pr.activeTimeInfo = { + start: 0, + end: 0, + segs: 0, + } + text += `\n活跃时间段`; + break; + } } }); - ai.resetState(); - seal.replyToSender(ctx, msg, text); AIManager.saveAI(id); return ret; diff --git a/src/timer.ts b/src/timer.ts index 13f8515..9a38c8e 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -2,6 +2,7 @@ import { ConfigManager } from "./config/config"; import { createCtx, createMsg } from "./utils/utils_seal"; import { AI, AIManager } from "./AI/AI"; import { logger } from "./logger"; +import { fmtTime } from "./utils/utils_string"; export interface TimerInfo { id: string, @@ -9,9 +10,10 @@ export interface TimerInfo { uid: string, gid: string, epId: string, - timestamp: number, + timestamp: number, // 定时器触发时间,单位秒 setTime: string, - content: string + content: string, + type: 'timer' | 'activeTime' }; export class TimerManager { @@ -34,16 +36,17 @@ export class TimerManager { ConfigManager.ext.storageSet(`timerQueue`, JSON.stringify(this.timerQueue)); } - static addTimer(ctx: seal.MsgContext, msg: seal.Message, ai: AI, t: number, content: string) { + static addTimer(ctx: seal.MsgContext, msg: seal.Message, ai: AI, timestamp: number, content: string, reason: 'timer' | 'activeTime') { this.timerQueue.push({ id: ai.id, messageType: msg.messageType, uid: ctx.player.userId, gid: ctx.group.groupId, epId: ctx.endPoint.userId, - timestamp: Math.floor(Date.now() / 1000) + t * 60, - setTime: new Date().toLocaleString(), - content: content + timestamp: timestamp, + setTime: fmtTime(Math.floor(Date.now() / 1000)), + content: content, + type: reason }) this.saveTimerQueue(); @@ -54,6 +57,39 @@ export class TimerManager { } } + static removeTimer(id: string = '', content: string = '', reason: 'timer' | 'activeTime' = 'timer', index_list: number[] = []) { + if (index_list.length > 0) { + const timers = TimerManager.timerQueue.filter(t => + (id && t.id === id) && + (content && t.content === content) && + (reason && t.type === reason) + ); + + for (const index of index_list) { + if (index < 1 || index > timers.length) { + logger.warning(`序号${index}超出范围`); + continue; + } + + const i = TimerManager.timerQueue.indexOf(timers[index - 1]); + if (i === -1) { + logger.warning(`出错了:找不到序号${index}的定时器`); + continue; + } + + TimerManager.timerQueue.splice(i, 1); + } + } else { + this.timerQueue = this.timerQueue.filter(timer => + (id && timer.id !== id) && + (content && timer.content !== content) && + (reason && timer.type !== reason) + ); + } + + this.saveTimerQueue(); + } + static async task() { try { if (this.isTaskRunning) { @@ -72,18 +108,42 @@ export class TimerManager { continue; } - const { id, messageType, uid, gid, epId, setTime, content } = timer; + const { id, messageType, uid, gid, epId, setTime, content, type: reason } = timer; const msg = createMsg(messageType, uid, gid); const ctx = createCtx(epId, msg); const ai = AIManager.getAI(id); - const s = `你设置的定时器触发了,请按照以下内容发送回复: + switch (reason) { + case 'timer': { + const s = `你设置的定时器触发了,请按照以下内容发送回复: 定时器设定时间:${setTime} -当前触发时间:${new Date().toLocaleString()} +当前触发时间:${fmtTime(Math.floor(Date.now() / 1000))} 提示内容:${content}`; - await ai.context.addSystemUserMessage("定时器触发提示", s, []); - await ai.chat(ctx, msg, '定时任务'); + await ai.context.addSystemUserMessage("定时器触发提示", s, []); + await ai.chat(ctx, msg, '定时任务'); + break; + } + case 'activeTime': { + const curSegIndex = ai.getCurSegIndex(); + const nextTimePoint = ai.getNextTimePoint(curSegIndex); + if (nextTimePoint !== -1) { + this.addTimer(ctx, msg, ai, nextTimePoint, '', 'activeTime'); + } + + if (curSegIndex === -1) { + logger.error(`${id} 不在活跃时间内,触发了 activeTime 定时器,真奇怪`); + continue; + } + + const s = `现在是你的活跃时间:${fmtTime(Math.floor(Date.now() / 1000))},请说点什么`; + + await ai.context.addSystemUserMessage("活跃时间触发提示", s, []); + await ai.chat(ctx, msg, '活跃时间'); + break; + } + } + changed = true; await new Promise(resolve => setTimeout(resolve, 2000)); diff --git a/src/tool/tool_ban.ts b/src/tool/tool_ban.ts index 0ae8d7f..0393a31 100644 --- a/src/tool/tool_ban.ts +++ b/src/tool/tool_ban.ts @@ -1,6 +1,7 @@ import { logger } from "../logger"; import { ConfigManager } from "../config/config"; import { Tool, ToolInfo, ToolManager } from "./tool"; +import { fmtTime } from "../utils/utils_string"; export function registerBan() { const info: ToolInfo = { @@ -160,7 +161,7 @@ export function registerGetBanList() { const data = await globalThis.http.getData(epId, `get_group_shut_list?group_id=${gid.replace(/^.+:/, '')}`); const s = `被禁言成员数量: ${data.length}\n` + data.slice(0, 50).map((item: any, index: number) => { - return `${index + 1}. ${item.nick}(${item.uin}) ${item.cardName && item.cardName !== item.nick ? `群名片: ${item.cardName}` : ''} 禁言结束时间: ${new Date(item.shutUpTime * 1000).toLocaleString()}`; + return `${index + 1}. ${item.nick}(${item.uin}) ${item.cardName && item.cardName !== item.nick ? `群名片: ${item.cardName}` : ''} 禁言结束时间: ${fmtTime(item.shutUpTime)}`; }).join('\n'); return s; diff --git a/src/tool/tool_context.ts b/src/tool/tool_context.ts index 7bce3cc..f88bba8 100644 --- a/src/tool/tool_context.ts +++ b/src/tool/tool_context.ts @@ -1,5 +1,6 @@ import { AIManager } from "../AI/AI"; import { ConfigManager } from "../config/config"; +import { buildContent } from "../utils/utils_message"; import { createCtx, createMsg } from "../utils/utils_seal"; import { Tool, ToolInfo, ToolManager } from "./tool"; @@ -66,8 +67,6 @@ export function registerGetContext() { return `未知的上下文类型<${ctx_type}>`; } - const { isPrefix, showNumber, showMsgId } = ConfigManager.message; - const messages = ai.context.messages; const images = []; const s = messages.map(message => { @@ -77,14 +76,7 @@ export function registerGetContext() { return `\n[function_call]: ${message.tool_calls.map((tool_call, index) => `${index + 1}. ${JSON.stringify(tool_call.function, null, 2)}`).join('\n')}`; } - const prefix = (isPrefix && message.name) ? ( - message.name.startsWith('_') ? - `<|${message.name}|>` : - `<|from:${message.name}${showNumber ? `(${message.uid.replace(/^.+:/, '')})` : ``}|>` - ) : ''; - const content = message.msgIdArray.map((msgId, index) => (showMsgId && msgId ? `<|msg_id:${msgId}|>` : '') + message.contentArray[index]).join('\f'); - - return `[${message.role}]: ${prefix}${content}`; + return `[${message.role}]: ${buildContent(message)}`; }).join('\n'); // 将images添加到最后一条消息,以便使用 diff --git a/src/tool/tool_roll_check.ts b/src/tool/tool_roll_check.ts index 2924343..b318953 100644 --- a/src/tool/tool_roll_check.ts +++ b/src/tool/tool_roll_check.ts @@ -142,7 +142,7 @@ export function registerSanCheck() { msg = createMsg(msg.messageType, uid, ctx.group.groupId); ctx = createCtx(ctx.endPoint.userId, msg); - const value = seal.vars.intGet(ctx, 'san')[0]; console.log(value) + const value = seal.vars.intGet(ctx, 'san')[0]; if (value === 0) { seal.vars.intSet(ctx, 'san', 60); } diff --git a/src/tool/tool_time.ts b/src/tool/tool_time.ts index 73e0f98..f3c2e34 100644 --- a/src/tool/tool_time.ts +++ b/src/tool/tool_time.ts @@ -1,5 +1,5 @@ import { TimerManager } from "../timer"; -import { ConfigManager } from "../config/config"; +import { fmtTime } from "../utils/utils_string"; import { Tool, ToolInfo, ToolManager } from "./tool"; export function registerGetTime() { @@ -19,7 +19,7 @@ export function registerGetTime() { const tool = new Tool(info); tool.solve = async (_, __, ___, ____) => { - return new Date().toLocaleString(); + return fmtTime(Math.floor(Date.now() / 1000)); } ToolManager.toolMap[info.function.name] = tool; @@ -65,7 +65,7 @@ export function registerSetTimer() { return '时间应为数字'; } - TimerManager.addTimer(ctx, msg, ai, t, content); + TimerManager.addTimer(ctx, msg, ai, Math.floor(Date.now() / 1000) + t * 60, content, 'timer'); return `设置定时器成功,请等待`; } @@ -90,7 +90,7 @@ export function registerShowTimerList() { const tool = new Tool(info); tool.solve = async (_, __, ai, ___) => { - const timers = TimerManager.timerQueue.filter(t => t.id === ai.id); + const timers = TimerManager.timerQueue.filter(t => t.id === ai.id && t.type === 'timer'); if (timers.length === 0) { return '当前对话没有定时器'; @@ -98,7 +98,7 @@ export function registerShowTimerList() { const s = timers.map((t, i) => { return `${i + 1}. 触发内容:${t.content} -${t.setTime} => ${new Date(t.timestamp * 1000).toLocaleString()}`; +${t.setTime} => ${fmtTime(t.timestamp)}`; }).join('\n'); return s; @@ -132,7 +132,7 @@ export function registerCancelTimer() { const tool = new Tool(info); tool.solve = async (_, __, ai, args) => { const { index_list } = args; - const timers = TimerManager.timerQueue.filter(t => t.id === ai.id); + const timers = TimerManager.timerQueue.filter(t => t.id === ai.id && t.type === 'timer'); if (timers.length === 0) { return '当前对话没有定时器'; @@ -142,20 +142,7 @@ export function registerCancelTimer() { return '请输入要取消的定时器序号'; } - for (const index of index_list) { - if (index < 1 || index > timers.length) { - return `序号${index}超出范围`; - } - - const i = TimerManager.timerQueue.indexOf(timers[index - 1]); - if (i === -1) { - return `出错了:找不到序号${index}的定时器`; - } - - TimerManager.timerQueue.splice(i, 1); - } - - ConfigManager.ext.storageSet(`TimerMatimerQueue`, JSON.stringify(TimerManager.timerQueue)); + TimerManager.removeTimer(ai.id, '', 'timer', index_list); return '定时器取消成功'; } diff --git a/src/update.ts b/src/update.ts index 1cd6790..60b5294 100644 --- a/src/update.ts +++ b/src/update.ts @@ -2,7 +2,8 @@ export const updateInfo = { "4.10.2":`- 新增请求超时相关 - 修复addMemory时,keywords可以为null的问题 -- 新增表情包制作工具`, +- 新增表情包制作工具 +- 新增活跃时间`, "4.10.1": `- 可能修复了非指令无法响应的问题 - 修复了构建ctx时,isPrivate始终为0的问题 - 新增保存图片功能 diff --git a/src/utils/utils_message.ts b/src/utils/utils_message.ts index 5ac1378..7e13ccd 100644 --- a/src/utils/utils_message.ts +++ b/src/utils/utils_message.ts @@ -4,9 +4,10 @@ import { Message } from "../AI/context"; import { logger } from "../logger"; import { ConfigManager } from "../config/config"; import { ToolInfo } from "../tool/tool"; +import { fmtTime } from "./utils_string"; export function buildSystemMessage(ctx: seal.MsgContext, ai: AI): Message { - const { roleSettingTemplate, systemMessageTemplate, isPrefix, showNumber, showMsgId } = ConfigManager.message; + const { roleSettingTemplate, systemMessageTemplate, isPrefix, showNumber, showMsgId, showTime } = ConfigManager.message; const { isTool, usePromptEngineering } = ConfigManager.tool; const { localImagePaths, receiveImage, condition } = ConfigManager.image; const { isMemory, isShortMemory } = ConfigManager.memory; @@ -66,6 +67,7 @@ export function buildSystemMessage(ctx: seal.MsgContext, ai: AI): Message { "群聊号码": ctx.group.groupId.replace(/^.+:/, ''), "添加前缀": isPrefix, "展示消息ID": showMsgId, + "展示时间": showTime, "接收图片": receiveImage, "图片条件不为零": condition !== '0', "可发送图片不为空": sandableImagesPrompt, @@ -85,9 +87,12 @@ export function buildSystemMessage(ctx: seal.MsgContext, ai: AI): Message { role: "system", uid: '', name: '', - contentArray: [content], - msgIdArray: [''], - images: [] + images: [], + msgArray: [{ + msgId: '', + time: Math.floor(Date.now() / 1000), + content: content + }] }; return systemMessage; @@ -105,18 +110,24 @@ function buildSamplesMessages(ctx: seal.MsgContext): Message[] { role: "user", uid: '', name: "用户", - contentArray: [item], - msgIdArray: [''], - images: [] + images: [], + msgArray: [{ + msgId: '', + time: Math.floor(Date.now() / 1000), + content: item + }] }; } else { return { role: "assistant", uid: ctx.endPoint.userId, name: seal.formatTmpl(ctx, "核心:骰子名字"), - contentArray: [item], - msgIdArray: [''], - images: [] + images: [], + msgArray: [{ + msgId: '', + time: Math.floor(Date.now() / 1000), + content: item + }] }; } }) @@ -157,7 +168,7 @@ function buildContextMessages(systemMessage: Message, messages: Message[]): Mess } export function handleMessages(ctx: seal.MsgContext, ai: AI) { - const { isPrefix, showNumber, showMsgId, isMerge } = ConfigManager.message; + const { isMerge } = ConfigManager.message; const systemMessage = buildSystemMessage(ctx, ai); const samplesMessages = buildSamplesMessages(ctx); @@ -202,20 +213,13 @@ export function handleMessages(ctx: seal.MsgContext, ai: AI) { let last_role = ''; for (let i = 0; i < messages.length; i++) { const message = messages[i]; - const prefix = (isPrefix && message.name) ? ( - message.name.startsWith('_') ? - `<|${message.name}|>` : - `<|from:${message.name}${showNumber ? `(${message.uid.replace(/^.+:/, '')})` : ``}|>` - ) : ''; - - const content = message.msgIdArray.map((msgId, index) => (showMsgId && msgId ? `<|msg_id:${msgId}|>` : '') + message.contentArray[index]).join('\f'); if (isMerge && message.role === last_role && message.role !== 'tool') { - processedMessages[processedMessages.length - 1].content += '\f' + prefix + content; + processedMessages[processedMessages.length - 1].content += '\f' + buildContent(message); } else { processedMessages.push({ role: message.role, - content: prefix + content, + content: buildContent(message), tool_calls: message?.tool_calls, tool_call_id: message?.tool_call_id }); @@ -267,4 +271,19 @@ export function parseBody(template: string[], messages: any[], tools: ToolInfo[] } return bodyObject; +} + +export function buildContent(message: Message): string { + const { isPrefix, showNumber, showMsgId, showTime } = ConfigManager.message; + const prefix = (isPrefix && message.name) ? ( + message.name.startsWith('_') ? + `<|${message.name}|>` : + `<|from:${message.name}${showNumber ? `(${message.uid.replace(/^.+:/, '')})` : ``}|>` + ) : ''; + const content = message.msgArray.map(mi => + ((showMsgId && mi.msgId) ? `<|msg_id:${mi.msgId}|>` : '') + + (showTime ? `<|time:${fmtTime(mi.time)}|>` : '') + + mi.content + ).join('\f'); + return prefix + content; } \ No newline at end of file diff --git a/src/utils/utils_string.ts b/src/utils/utils_string.ts index 1c109ed..ddea46e 100644 --- a/src/utils/utils_string.ts +++ b/src/utils/utils_string.ts @@ -147,7 +147,7 @@ export function checkRepeat(context: Context, s: string) { const message = messages[i]; // 寻找最后一条文本消息 if (message.role === 'assistant' && !message?.tool_calls) { - const content = message.contentArray[message.contentArray.length - 1] || ''; + const content = message.msgArray[message.msgArray.length - 1].content || ''; const similarity = calculateSimilarity(content.trim(), s.trim()); logger.info(`复读相似度:${similarity}`); @@ -455,4 +455,15 @@ function advancedSplit(s: string, r: RegExp) { } return parts; +} + +export function fmtTime(timestamp: number) { + const date = new Date(timestamp * 1000); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hour = String(date.getHours()).padStart(2, '0'); + const minute = String(date.getMinutes()).padStart(2, '0'); + const second = String(date.getSeconds()).padStart(2, '0'); + return `${year}-${month}-${day} ${hour}:${minute}:${second}`; } \ No newline at end of file