mioku-plugin-chat 2.10.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/configs/base.ts +1 -1
- package/context.ts +2 -2
- package/core/base.ts +57 -62
- package/core/chat-engine.ts +1 -1
- package/core/chat-turn.ts +9 -9
- package/core/media/audio.ts +4 -4
- package/core/media/gif-extractor.ts +4 -4
- package/core/media/history-media.ts +9 -14
- package/core/media/image-analyzer.ts +1 -1
- package/core/media/image-compress.ts +1 -1
- package/core/media/segment.ts +10 -14
- package/core/multimodal.ts +15 -10
- package/core/prompt.ts +3 -3
- package/core/tools/info.ts +8 -7
- package/core/tools/web.ts +86 -56
- package/core/tools.ts +1 -1
- package/core/web/searxng.ts +1 -1
- package/core/web/web-reader.ts +2 -2
- package/handlers/idle-debug.ts +3 -3
- package/handlers/message.ts +44 -29
- package/handlers/poke.ts +22 -16
- package/humanize/emoji-agent.ts +1 -1
- package/humanize/emotion-agent.ts +1 -1
- package/humanize/expression.ts +1 -1
- package/humanize/memory.ts +1 -1
- package/humanize/planner.ts +1 -1
- package/humanize/topic.ts +1 -1
- package/index.ts +3 -3
- package/manage/cleanup.ts +1 -1
- package/manage/cooldown.ts +4 -4
- package/manage/idle-check.ts +4 -4
- package/manage/queue-processor.ts +4 -4
- package/manage/types.ts +37 -14
- package/package.json +3 -5
- package/types.ts +2 -2
- package/utils/message.ts +92 -60
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AIInstance } from "mioku";
|
|
2
|
-
import { logger } from "
|
|
2
|
+
import { logger } from "mioku";
|
|
3
3
|
import { extractJsonObject } from "../utils/json";
|
|
4
4
|
import type { ChatConfig, ChatMessage, TargetMessage } from "../types";
|
|
5
5
|
import { extractGroupIdFromSession } from "../utils/group-config";
|
package/humanize/expression.ts
CHANGED
package/humanize/memory.ts
CHANGED
package/humanize/planner.ts
CHANGED
package/humanize/topic.ts
CHANGED
package/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { definePlugin, type
|
|
1
|
+
import { definePlugin, type MiokuContext } from "mioku";
|
|
2
2
|
import type { AIInstance, AIModelRole, AIService } from "mioku";
|
|
3
3
|
import type { AudioServiceApi } from "mioku-service-audio";
|
|
4
4
|
import { getPluginRuntimeState, getService, Services } from "mioku";
|
|
@@ -117,7 +117,7 @@ export default definePlugin({
|
|
|
117
117
|
name: "chat",
|
|
118
118
|
version: "1.1.0",
|
|
119
119
|
description: "AI 智能聊天插件",
|
|
120
|
-
async setup(ctx:
|
|
120
|
+
async setup(ctx: MiokuContext) {
|
|
121
121
|
ctx.logger.info("聊天插件正在初始化...");
|
|
122
122
|
|
|
123
123
|
const aiService = getService(ctx, Services.AI);
|
|
@@ -497,7 +497,7 @@ roleModels = resolved.models;
|
|
|
497
497
|
processingSet,
|
|
498
498
|
},
|
|
499
499
|
matchMessageCommands: (
|
|
500
|
-
getPluginRuntimeState("
|
|
500
|
+
getPluginRuntimeState("core") as
|
|
501
501
|
| {
|
|
502
502
|
matchMessageCommands?: (
|
|
503
503
|
text: string,
|
package/manage/cleanup.ts
CHANGED
package/manage/cooldown.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MiokuContext } from "mioku";
|
|
2
2
|
import type { AIInstance } from "mioku";
|
|
3
3
|
import type { ChatConfig, TargetMessage } from "../types";
|
|
4
4
|
import type { ChatDatabase } from "../db";
|
|
@@ -39,7 +39,7 @@ export class CooldownManager {
|
|
|
39
39
|
private pluginCtx: ChatPluginContext;
|
|
40
40
|
private sessionTurnScheduler: SessionTurnScheduler;
|
|
41
41
|
|
|
42
|
-
private ctx:
|
|
42
|
+
private ctx: MiokuContext;
|
|
43
43
|
private configProvider: ChatConfigProvider;
|
|
44
44
|
private defaultConfig: ChatConfig;
|
|
45
45
|
private db: ChatDatabase;
|
|
@@ -206,7 +206,7 @@ export class CooldownManager {
|
|
|
206
206
|
this.buildHistoryMediaOptions(this.aiInstance, cfg),
|
|
207
207
|
);
|
|
208
208
|
const botNickname =
|
|
209
|
-
cfg.nicknames[0] || this.ctx.pickBot(selfId)
|
|
209
|
+
cfg.nicknames[0] || this.ctx.pickBot(selfId)?.nickname || "Bot";
|
|
210
210
|
const botRole = await getBotRole(groupId, this.ctx, selfId);
|
|
211
211
|
const { groupName, memberCount } = await this.getGroupInfoData(
|
|
212
212
|
this.ctx,
|
|
@@ -316,7 +316,7 @@ export class CooldownManager {
|
|
|
316
316
|
this.buildHistoryMediaOptions(this.aiInstance, cfg),
|
|
317
317
|
);
|
|
318
318
|
const botNickname =
|
|
319
|
-
cfg.nicknames[0] || this.ctx.pickBot(selfId)
|
|
319
|
+
cfg.nicknames[0] || this.ctx.pickBot(selfId)?.nickname || "Bot";
|
|
320
320
|
|
|
321
321
|
const planResult = await this.humanize.actionPlanner.plan(
|
|
322
322
|
groupSessionId,
|
package/manage/idle-check.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MiokuContext } from "mioku";
|
|
2
2
|
import type { AIInstance } from "mioku";
|
|
3
3
|
import type { ChatConfig, TargetMessage } from "../types";
|
|
4
4
|
import type { ChatDatabase } from "../db";
|
|
@@ -28,7 +28,7 @@ export class IdleCheckManager {
|
|
|
28
28
|
private groupBotsMapping = new Map<string, Set<number>>();
|
|
29
29
|
private intervalHandle: NodeJS.Timeout | null = null;
|
|
30
30
|
|
|
31
|
-
private ctx:
|
|
31
|
+
private ctx: MiokuContext;
|
|
32
32
|
private configProvider: ChatConfigProvider;
|
|
33
33
|
private defaultConfig: ChatConfig;
|
|
34
34
|
private db: ChatDatabase;
|
|
@@ -106,7 +106,7 @@ export class IdleCheckManager {
|
|
|
106
106
|
|
|
107
107
|
const now = Date.now();
|
|
108
108
|
const checkInterval = 60_000;
|
|
109
|
-
const allBotIds = Array.from(this.ctx.bots).map((bot) => bot.
|
|
109
|
+
const allBotIds = Array.from(this.ctx.bots).map((bot) => Number(bot.bot_id));
|
|
110
110
|
|
|
111
111
|
for (const [groupSessionId, lastTime] of this.groupLastActivityTime) {
|
|
112
112
|
const lastCheckTime =
|
|
@@ -216,7 +216,7 @@ export class IdleCheckManager {
|
|
|
216
216
|
this.buildHistoryMediaOptions(this.aiInstance, cfg),
|
|
217
217
|
);
|
|
218
218
|
const botNickname =
|
|
219
|
-
cfg.nicknames[0] || this.ctx.pickBot(selfId)
|
|
219
|
+
cfg.nicknames[0] || this.ctx.pickBot(selfId)?.nickname || "Bot";
|
|
220
220
|
const planResult = await this.humanize.actionPlanner.plan(
|
|
221
221
|
groupSessionId,
|
|
222
222
|
botNickname,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MiokuContext } from "mioku";
|
|
2
2
|
import type { AIInstance } from "mioku";
|
|
3
3
|
import type { ChatConfig, TargetMessage } from "../types";
|
|
4
4
|
import type { ChatDatabase } from "../db";
|
|
@@ -34,7 +34,7 @@ export class QueueProcessor {
|
|
|
34
34
|
private dynamicDelayQueues = new Map<string, DynamicDelayQueueData>();
|
|
35
35
|
private pluginCtx: ChatPluginContext;
|
|
36
36
|
|
|
37
|
-
private ctx:
|
|
37
|
+
private ctx: MiokuContext;
|
|
38
38
|
private configProvider: ChatConfigProvider;
|
|
39
39
|
private defaultConfig: ChatConfig;
|
|
40
40
|
private db: ChatDatabase;
|
|
@@ -177,7 +177,7 @@ export class QueueProcessor {
|
|
|
177
177
|
|
|
178
178
|
const cfg = this.configProvider(groupId);
|
|
179
179
|
const botRole = await getBotRole(groupId, this.ctx, selfId);
|
|
180
|
-
const botNickname = cfg.nicknames[0] || this.ctx.pickBot(selfId)
|
|
180
|
+
const botNickname = cfg.nicknames[0] || this.ctx.pickBot(selfId)?.nickname || "Bot";
|
|
181
181
|
const { groupName, memberCount } = await this.getGroupInfoData(this.ctx, groupId, selfId, String(groupId));
|
|
182
182
|
const { history } = await this.getGroupHistoryMessages(groupId, groupSessionId, this.ctx, cfg.historyCount, this.db, selfId, this.buildHistoryMediaOptions(this.aiInstance, cfg));
|
|
183
183
|
|
|
@@ -261,7 +261,7 @@ export class QueueProcessor {
|
|
|
261
261
|
};
|
|
262
262
|
|
|
263
263
|
const botRole = await getBotRole(groupId, this.ctx, selfId);
|
|
264
|
-
const botNickname = cfg.nicknames[0] || this.ctx.pickBot(selfId)
|
|
264
|
+
const botNickname = cfg.nicknames[0] || this.ctx.pickBot(selfId)?.nickname || "Bot";
|
|
265
265
|
const toolCtx = this.buildToolContext({
|
|
266
266
|
ctx: this.ctx, event: null, groupSessionId, groupId, userId: targetMessage.userId,
|
|
267
267
|
config: cfg, aiService: this.aiService, db: this.db, botRole, humanize: this.humanize, targetMessage, selfId,
|
package/manage/types.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Bot, MiokuContext } from "mioku";
|
|
2
2
|
import type { AIInstance, AIService } from "mioku";
|
|
3
3
|
import type { SkillPermissionRole } from "mioku";
|
|
4
4
|
import type { ChatRuntimePromptInjection } from "mioku";
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
ChatConfig,
|
|
7
|
+
ChatMessage,
|
|
8
|
+
TargetMessage,
|
|
9
|
+
ToolContext,
|
|
10
|
+
ChatResult,
|
|
11
|
+
} from "../types";
|
|
6
12
|
import type { ChatDatabase } from "../db";
|
|
7
13
|
import type { HumanizeEngine } from "../humanize";
|
|
8
14
|
import type { EmojiAgent } from "../humanize";
|
|
9
15
|
import type { SkillSessionManager } from "./skill-session";
|
|
10
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
GroupStructuredHistoryManager,
|
|
18
|
+
StructuredUserInput,
|
|
19
|
+
} from "./group-structured-history";
|
|
11
20
|
import type { SendAIResponseOptions } from "../core/base";
|
|
12
21
|
import type { GroupInfoResult, HumanizeContextsResult } from "../core/base";
|
|
13
22
|
|
|
@@ -32,7 +41,7 @@ export type RunRateLimitGuard = <T>(
|
|
|
32
41
|
export type GetGroupHistoryMessages = (
|
|
33
42
|
groupId: number,
|
|
34
43
|
groupSessionId: string,
|
|
35
|
-
ctx:
|
|
44
|
+
ctx: MiokuContext,
|
|
36
45
|
historyCount: number,
|
|
37
46
|
db: ChatDatabase,
|
|
38
47
|
selfId: number,
|
|
@@ -40,7 +49,7 @@ export type GetGroupHistoryMessages = (
|
|
|
40
49
|
) => Promise<{ history: ChatMessage[] }>;
|
|
41
50
|
|
|
42
51
|
export type GetGroupInfoData = (
|
|
43
|
-
ctx:
|
|
52
|
+
ctx: MiokuContext,
|
|
44
53
|
groupId: number,
|
|
45
54
|
selfId: number,
|
|
46
55
|
fallbackGroupName?: string,
|
|
@@ -54,7 +63,10 @@ export type GetHumanizeContexts = (
|
|
|
54
63
|
triggerUserId?: number,
|
|
55
64
|
) => Promise<HumanizeContextsResult>;
|
|
56
65
|
|
|
57
|
-
export type SendAIResponse = (
|
|
66
|
+
export type SendAIResponse = (
|
|
67
|
+
options: SendAIResponseOptions,
|
|
68
|
+
selfId: number,
|
|
69
|
+
) => Promise<void>;
|
|
58
70
|
|
|
59
71
|
export type SaveBotMessages = (
|
|
60
72
|
groupId: number,
|
|
@@ -63,14 +75,19 @@ export type SaveBotMessages = (
|
|
|
63
75
|
timestamp: number,
|
|
64
76
|
config: ChatConfig,
|
|
65
77
|
db: ChatDatabase,
|
|
66
|
-
ctx:
|
|
67
|
-
|
|
78
|
+
ctx: MiokuContext,
|
|
79
|
+
bot?: Bot,
|
|
68
80
|
) => void;
|
|
69
81
|
|
|
70
|
-
export type SendEmoji = (
|
|
82
|
+
export type SendEmoji = (
|
|
83
|
+
ctx: MiokuContext,
|
|
84
|
+
groupId: number,
|
|
85
|
+
emojiPath: string | null | undefined,
|
|
86
|
+
bot?: import("mioku").Bot,
|
|
87
|
+
) => Promise<void>;
|
|
71
88
|
|
|
72
89
|
export type SendMessage = (
|
|
73
|
-
ctx:
|
|
90
|
+
ctx: MiokuContext,
|
|
74
91
|
groupId: number | undefined,
|
|
75
92
|
userId: number,
|
|
76
93
|
text: string,
|
|
@@ -80,7 +97,7 @@ export type SendMessage = (
|
|
|
80
97
|
) => Promise<void>;
|
|
81
98
|
|
|
82
99
|
export type BuildToolContext = (options: {
|
|
83
|
-
ctx:
|
|
100
|
+
ctx: MiokuContext;
|
|
84
101
|
event: any;
|
|
85
102
|
selfId: number;
|
|
86
103
|
groupSessionId: string;
|
|
@@ -96,9 +113,15 @@ export type BuildToolContext = (options: {
|
|
|
96
113
|
audioService?: import("mioku-service-audio").AudioServiceApi;
|
|
97
114
|
}) => ToolContext;
|
|
98
115
|
|
|
99
|
-
export type BuildStructuredUserInput = (
|
|
116
|
+
export type BuildStructuredUserInput = (
|
|
117
|
+
event: any,
|
|
118
|
+
content: string,
|
|
119
|
+
fallbackTimestamp?: number,
|
|
120
|
+
) => StructuredUserInput;
|
|
100
121
|
|
|
101
|
-
export type BuildStructuredUserInputFromTarget = (
|
|
122
|
+
export type BuildStructuredUserInputFromTarget = (
|
|
123
|
+
targetMessage: TargetMessage,
|
|
124
|
+
) => StructuredUserInput;
|
|
102
125
|
|
|
103
126
|
export type PromptCtxForRunChat = {
|
|
104
127
|
config: ChatConfig;
|
|
@@ -143,4 +166,4 @@ export type RunChat = (
|
|
|
143
166
|
currentUserInputs: StructuredUserInput[];
|
|
144
167
|
},
|
|
145
168
|
runtimeOptions?: { extraTools?: any[] },
|
|
146
|
-
) => Promise<ChatResult>;
|
|
169
|
+
) => Promise<ChatResult>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mioku-plugin-chat",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "AI 智能聊天插件",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -54,12 +54,10 @@
|
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/bun": "^1.3.14",
|
|
57
|
-
"mioki": "^0.16.0",
|
|
58
57
|
"mioku": "workspace:*"
|
|
59
58
|
},
|
|
60
59
|
"peerDependencies": {
|
|
61
|
-
"mioku": "^0.
|
|
62
|
-
"mioku-service-audio": "workspace:*"
|
|
63
|
-
"mioki": "^0.16.0"
|
|
60
|
+
"mioku": "^1.0.0",
|
|
61
|
+
"mioku-service-audio": "workspace:*"
|
|
64
62
|
}
|
|
65
63
|
}
|
package/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChatCompletionMessageParam } from "openai/resources/chat/completions";
|
|
2
|
-
import type {
|
|
2
|
+
import type { MiokuContext } from "mioku";
|
|
3
3
|
import type { AITool, AIService, SkillPermissionRole } from "mioku";
|
|
4
4
|
import type { ChatDatabase } from "./db";
|
|
5
5
|
|
|
@@ -300,7 +300,7 @@ export interface SkillSession {
|
|
|
300
300
|
* 工具上下文
|
|
301
301
|
*/
|
|
302
302
|
export interface ToolContext {
|
|
303
|
-
ctx:
|
|
303
|
+
ctx: MiokuContext;
|
|
304
304
|
event: any;
|
|
305
305
|
sessionId: string;
|
|
306
306
|
groupId?: number;
|
package/utils/message.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { logger,
|
|
2
|
-
import type { AIInstance } from "mioku";
|
|
1
|
+
import { HistoryMessage, logger, type MiokuContext } from "mioku";
|
|
2
|
+
import type { AIInstance, Bot } from "mioku";
|
|
3
|
+
|
|
3
4
|
import type {
|
|
4
5
|
ChatConfig,
|
|
5
6
|
ChatMessage,
|
|
@@ -22,13 +23,15 @@ export function shouldTrigger(
|
|
|
22
23
|
e: any,
|
|
23
24
|
text: string,
|
|
24
25
|
cfg: ChatConfig,
|
|
25
|
-
ctx:
|
|
26
|
+
ctx: MiokuContext,
|
|
26
27
|
): boolean {
|
|
27
28
|
if (e.message_type === "private") return false;
|
|
28
29
|
|
|
29
|
-
// Only check if message @s the bot (seg format: {type: "at", qq: "123456"})
|
|
30
30
|
const atSeg = e.message?.find((seg: any) => seg.type === "at");
|
|
31
|
-
|
|
31
|
+
if (!atSeg) return false;
|
|
32
|
+
const data = (atSeg.data ?? {}) as { qq?: unknown; target?: unknown };
|
|
33
|
+
const qq = data.qq ?? data.target;
|
|
34
|
+
return String(qq ?? "") === String(e.self_id ?? "");
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
/**
|
|
@@ -37,15 +40,15 @@ export function shouldTrigger(
|
|
|
37
40
|
*/
|
|
38
41
|
export async function isQuotingBot(
|
|
39
42
|
e: any,
|
|
40
|
-
ctx:
|
|
43
|
+
ctx: MiokuContext,
|
|
41
44
|
): Promise<{ quoted: true; messageId: string; content: string } | null> {
|
|
42
45
|
if (e.quote_id) {
|
|
43
46
|
try {
|
|
44
|
-
const quoteMsg = await
|
|
45
|
-
if (quoteMsg && String(quoteMsg.sender
|
|
47
|
+
const quoteMsg = await fetchQuotedMessage(e, ctx);
|
|
48
|
+
if (quoteMsg && String(quoteMsg.sender?.user_id) === String(e.self_id)) {
|
|
46
49
|
const quotedText = quoteMsg.message
|
|
47
50
|
?.filter((s: any) => s.type === "text")
|
|
48
|
-
.map((s: any) => s.text)
|
|
51
|
+
.map((s: any) => String(s.data?.text ?? s.text ?? ""))
|
|
49
52
|
.join("");
|
|
50
53
|
if (quotedText) {
|
|
51
54
|
return { quoted: true, messageId: e.quote_id, content: quotedText };
|
|
@@ -65,7 +68,7 @@ export async function isQuotingBot(
|
|
|
65
68
|
*/
|
|
66
69
|
export async function getQuotedContent(
|
|
67
70
|
e: any,
|
|
68
|
-
ctx:
|
|
71
|
+
ctx: MiokuContext,
|
|
69
72
|
): Promise<
|
|
70
73
|
| {
|
|
71
74
|
messageId: string;
|
|
@@ -78,13 +81,13 @@ export async function getQuotedContent(
|
|
|
78
81
|
> {
|
|
79
82
|
if (e.quote_id) {
|
|
80
83
|
try {
|
|
81
|
-
const quotedMsg = await
|
|
84
|
+
const quotedMsg = await fetchQuotedMessage(e, ctx);
|
|
82
85
|
if (quotedMsg && quotedMsg.message) {
|
|
83
|
-
const senderName = quotedMsg.sender
|
|
86
|
+
const senderName = quotedMsg.sender?.nickname || "";
|
|
84
87
|
// 提取文本内容
|
|
85
88
|
const textContent = quotedMsg.message
|
|
86
89
|
.filter((s: any) => s.type === "text")
|
|
87
|
-
.map((s: any) => s.text
|
|
90
|
+
.map((s: any) => String(s.data?.text ?? s.text ?? ""))
|
|
88
91
|
.join("");
|
|
89
92
|
|
|
90
93
|
// 检测是否有图片
|
|
@@ -120,7 +123,7 @@ export function isGroupAllowed(groupId: number, cfg: ChatConfig): boolean {
|
|
|
120
123
|
export function extractContent(
|
|
121
124
|
e: any,
|
|
122
125
|
cfg: ChatConfig,
|
|
123
|
-
ctx:
|
|
126
|
+
ctx: MiokuContext,
|
|
124
127
|
): { text: string; multimodal: any[] | null } {
|
|
125
128
|
let text = "";
|
|
126
129
|
try {
|
|
@@ -129,9 +132,12 @@ export function extractContent(
|
|
|
129
132
|
|
|
130
133
|
// If text is empty but user @'d the bot, describe the action
|
|
131
134
|
if (!text.trim() && e.message) {
|
|
132
|
-
const hasAt = e.message.some(
|
|
133
|
-
(seg
|
|
134
|
-
|
|
135
|
+
const hasAt = e.message.some((seg: any) => {
|
|
136
|
+
if (seg.type !== "at") return false;
|
|
137
|
+
const data = (seg.data ?? {}) as { qq?: unknown; target?: unknown };
|
|
138
|
+
const qq = data.qq ?? data.target;
|
|
139
|
+
return String(qq ?? "") === String(e.self_id ?? "");
|
|
140
|
+
});
|
|
135
141
|
if (hasAt) {
|
|
136
142
|
text = "[@you with no text]";
|
|
137
143
|
}
|
|
@@ -190,14 +196,14 @@ async function mapWithConcurrency<T, R>(
|
|
|
190
196
|
|
|
191
197
|
export async function getBotRole(
|
|
192
198
|
groupId: number,
|
|
193
|
-
ctx:
|
|
199
|
+
ctx: MiokuContext,
|
|
194
200
|
selfId: number,
|
|
195
201
|
): Promise<"owner" | "admin" | "member"> {
|
|
196
202
|
try {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return (memberInfo
|
|
203
|
+
const bot = ctx.pickBot(selfId);
|
|
204
|
+
if (!bot) return "member";
|
|
205
|
+
const memberInfo = await bot.getMemberInfo(groupId, selfId);
|
|
206
|
+
return (memberInfo?.role as "owner" | "admin" | "member") || "member";
|
|
201
207
|
} catch {
|
|
202
208
|
return "member";
|
|
203
209
|
}
|
|
@@ -237,11 +243,11 @@ async function resolveMemberName(
|
|
|
237
243
|
}
|
|
238
244
|
ctx.memberNameCache.set(key, key);
|
|
239
245
|
try {
|
|
240
|
-
const bot = ctx.historyMediaOptions.bot as
|
|
241
|
-
if (!bot
|
|
242
|
-
const info = await bot.
|
|
243
|
-
ctx.historyMediaOptions.groupId
|
|
244
|
-
|
|
246
|
+
const bot = ctx.historyMediaOptions.bot as Bot | undefined;
|
|
247
|
+
if (!bot) return key;
|
|
248
|
+
const info = await bot.getMemberInfo(
|
|
249
|
+
ctx.historyMediaOptions.groupId!,
|
|
250
|
+
userId,
|
|
245
251
|
);
|
|
246
252
|
const name = info?.card || info?.nickname || key;
|
|
247
253
|
ctx.memberNameCache.set(key, name);
|
|
@@ -278,7 +284,10 @@ function extractQuotedText(messageSegs: any): string {
|
|
|
278
284
|
return parts.join(" ").trim();
|
|
279
285
|
}
|
|
280
286
|
|
|
281
|
-
function buildReplyAnnotation(
|
|
287
|
+
function buildReplyAnnotation(
|
|
288
|
+
source: any,
|
|
289
|
+
ctx: HistoryFormatContext,
|
|
290
|
+
): string | null {
|
|
282
291
|
if (!source || typeof source !== "object") return null;
|
|
283
292
|
const sourceId = source.id ?? source.message_id ?? source.message_seq;
|
|
284
293
|
const sourceUserId = source.user_id;
|
|
@@ -291,7 +300,8 @@ function buildReplyAnnotation(source: any, ctx: HistoryFormatContext): string |
|
|
|
291
300
|
const idStr = sourceId != null ? String(sourceId) : "?";
|
|
292
301
|
let displayName: string | undefined;
|
|
293
302
|
if (sourceUserId != null) {
|
|
294
|
-
displayName =
|
|
303
|
+
displayName =
|
|
304
|
+
ctx.memberNameCache.get(String(sourceUserId)) || String(sourceUserId);
|
|
295
305
|
}
|
|
296
306
|
if (!displayName) displayName = sourceNickname || "unknown";
|
|
297
307
|
|
|
@@ -334,7 +344,7 @@ async function getImageTagWithHashCache(
|
|
|
334
344
|
}
|
|
335
345
|
|
|
336
346
|
async function formatHistoryMessage(
|
|
337
|
-
msg:
|
|
347
|
+
msg: HistoryMessage,
|
|
338
348
|
ctx: HistoryFormatContext,
|
|
339
349
|
): Promise<FormattedHistoryMessage | null> {
|
|
340
350
|
if (String(msg.user_id) === String(ctx.botUin)) return null;
|
|
@@ -350,12 +360,12 @@ async function formatHistoryMessage(
|
|
|
350
360
|
if (!content.trim()) return null;
|
|
351
361
|
|
|
352
362
|
return {
|
|
353
|
-
userId: msg.user_id,
|
|
354
|
-
userName: msg.
|
|
355
|
-
userRole:
|
|
363
|
+
userId: Number(msg.user_id || 0),
|
|
364
|
+
userName: msg.nickname || String(msg.user_id || "unknown"),
|
|
365
|
+
userRole: "member",
|
|
356
366
|
content,
|
|
357
|
-
messageId: msg.message_id,
|
|
358
|
-
timestamp: msg.time
|
|
367
|
+
messageId: Number(msg.message_id) || 0,
|
|
368
|
+
timestamp: msg.time ?? Date.now(),
|
|
359
369
|
role: "user",
|
|
360
370
|
};
|
|
361
371
|
}
|
|
@@ -427,17 +437,24 @@ async function buildMessageContent(
|
|
|
427
437
|
)) {
|
|
428
438
|
const videoSources = getSegmentSourceCandidates(videoSeg);
|
|
429
439
|
if (videoSources.length > 0) {
|
|
430
|
-
parts.push(
|
|
440
|
+
parts.push(
|
|
441
|
+
await getCachedHistoryVideoTag(videoSources, historyMediaOptions),
|
|
442
|
+
);
|
|
431
443
|
} else {
|
|
432
444
|
parts.push("[video]");
|
|
433
445
|
}
|
|
434
446
|
}
|
|
435
447
|
|
|
436
|
-
for (const forwardSeg of msg.message.filter(
|
|
448
|
+
for (const forwardSeg of msg.message.filter(
|
|
449
|
+
(seg: any) => seg.type === "forward",
|
|
450
|
+
)) {
|
|
437
451
|
const forwardId = forwardSeg.id || forwardSeg.data?.id;
|
|
438
452
|
if (forwardId) {
|
|
439
453
|
parts.push(
|
|
440
|
-
await getCachedHistoryForwardTag(
|
|
454
|
+
await getCachedHistoryForwardTag(
|
|
455
|
+
String(forwardId),
|
|
456
|
+
historyMediaOptions,
|
|
457
|
+
),
|
|
441
458
|
);
|
|
442
459
|
} else {
|
|
443
460
|
parts.push("[forward]");
|
|
@@ -448,7 +465,11 @@ async function buildMessageContent(
|
|
|
448
465
|
["xml", "json", "lightapp", "ark"].includes(seg.type),
|
|
449
466
|
)) {
|
|
450
467
|
const cardData =
|
|
451
|
-
cardSeg.data?.data ||
|
|
468
|
+
cardSeg.data?.data ||
|
|
469
|
+
cardSeg.data?.xml ||
|
|
470
|
+
cardSeg.data ||
|
|
471
|
+
cardSeg.xml ||
|
|
472
|
+
"";
|
|
452
473
|
if (cardData) {
|
|
453
474
|
parts.push(
|
|
454
475
|
getCachedHistoryCardTag(
|
|
@@ -464,7 +485,9 @@ async function buildMessageContent(
|
|
|
464
485
|
if (parts.length > 0) return parts.join(" ");
|
|
465
486
|
|
|
466
487
|
const segTypes = msg.message.map((seg: any) => seg.type);
|
|
467
|
-
const nonTextTypes = segTypes.filter(
|
|
488
|
+
const nonTextTypes = segTypes.filter(
|
|
489
|
+
(t: string) => t !== "text" && t !== "at",
|
|
490
|
+
);
|
|
468
491
|
return nonTextTypes.length > 0 ? `[${nonTextTypes.join(", ")}]` : "";
|
|
469
492
|
}
|
|
470
493
|
|
|
@@ -474,7 +497,7 @@ async function buildMessageContent(
|
|
|
474
497
|
*/
|
|
475
498
|
export async function getGroupHistory(
|
|
476
499
|
groupId: number,
|
|
477
|
-
ctx:
|
|
500
|
+
ctx: MiokuContext,
|
|
478
501
|
count: number = 100,
|
|
479
502
|
selfId: number,
|
|
480
503
|
db?: {
|
|
@@ -547,8 +570,9 @@ export async function getGroupHistory(
|
|
|
547
570
|
}
|
|
548
571
|
|
|
549
572
|
try {
|
|
550
|
-
const bot = ctx.pickBot(selfId)
|
|
573
|
+
const bot = ctx.pickBot(selfId);
|
|
551
574
|
const historyMediaOptions: HistoryMediaProcessingOptions = {
|
|
575
|
+
bot,
|
|
552
576
|
ai: mediaOptions?.ai,
|
|
553
577
|
workingModel: mediaOptions?.workingModel,
|
|
554
578
|
multimodalWorkingModel: mediaOptions?.multimodalWorkingModel,
|
|
@@ -561,32 +585,23 @@ export async function getGroupHistory(
|
|
|
561
585
|
saveMediaSummarySource: db.saveMediaSummarySource?.bind(db),
|
|
562
586
|
}
|
|
563
587
|
: undefined,
|
|
564
|
-
bot,
|
|
565
588
|
groupId,
|
|
566
589
|
};
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
parse_mult_msg: true,
|
|
575
|
-
quick_reply: false,
|
|
576
|
-
});
|
|
577
|
-
const messages = result?.messages || result?.data?.messages || [];
|
|
578
|
-
if (!Array.isArray(messages)) {
|
|
579
|
-
logger.warn("[getGroupHistory] API 返回格式异常:", result);
|
|
580
|
-
return botMessages;
|
|
581
|
-
}
|
|
590
|
+
const messages = bot
|
|
591
|
+
? await bot.getHistory(
|
|
592
|
+
{ type: "group", group_id: groupId },
|
|
593
|
+
undefined,
|
|
594
|
+
Math.min(count, 200),
|
|
595
|
+
)
|
|
596
|
+
: [];
|
|
582
597
|
|
|
583
598
|
const botUin = selfId;
|
|
584
599
|
const memberNameCache = new Map<string, string>();
|
|
585
600
|
const hashLookupCache = new Map<string, string | null>();
|
|
586
601
|
|
|
587
602
|
for (const m of messages) {
|
|
588
|
-
const uid = m
|
|
589
|
-
const senderName = m
|
|
603
|
+
const uid = m.user_id;
|
|
604
|
+
const senderName = m.nickname;
|
|
590
605
|
if (uid != null && senderName && !memberNameCache.has(String(uid))) {
|
|
591
606
|
memberNameCache.set(String(uid), String(senderName));
|
|
592
607
|
}
|
|
@@ -657,3 +672,20 @@ export function normalizeIdList(input: unknown): number[] {
|
|
|
657
672
|
),
|
|
658
673
|
);
|
|
659
674
|
}
|
|
675
|
+
|
|
676
|
+
async function fetchQuotedMessage(
|
|
677
|
+
e: any,
|
|
678
|
+
ctx: MiokuContext,
|
|
679
|
+
): Promise<{
|
|
680
|
+
message: any[];
|
|
681
|
+
sender?: { user_id?: string; nickname?: string };
|
|
682
|
+
} | null> {
|
|
683
|
+
const bot = e?.bot ?? ctx.pickBot(e?.self_id ?? "");
|
|
684
|
+
if (!bot || !e?.quote_id) return null;
|
|
685
|
+
try {
|
|
686
|
+
const result = await bot.getMessage(e.quote_id);
|
|
687
|
+
return result ?? null;
|
|
688
|
+
} catch {
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
}
|