@inline-openclaw/inline 0.0.3 → 0.0.5
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/README.md +47 -2
- package/dist/inline/actions.d.ts +3 -0
- package/dist/inline/actions.d.ts.map +1 -0
- package/dist/inline/actions.js +480 -0
- package/dist/inline/actions.js.map +1 -0
- package/dist/inline/channel.d.ts.map +1 -1
- package/dist/inline/channel.js +110 -28
- package/dist/inline/channel.js.map +1 -1
- package/dist/inline/config-schema.d.ts +189 -0
- package/dist/inline/config-schema.d.ts.map +1 -1
- package/dist/inline/config-schema.js +16 -1
- package/dist/inline/config-schema.js.map +1 -1
- package/dist/inline/media.d.ts +9 -0
- package/dist/inline/media.d.ts.map +1 -0
- package/dist/inline/media.js +117 -0
- package/dist/inline/media.js.map +1 -0
- package/dist/inline/monitor.d.ts.map +1 -1
- package/dist/inline/monitor.js +360 -42
- package/dist/inline/monitor.js.map +1 -1
- package/dist/inline/policy.d.ts +19 -0
- package/dist/inline/policy.d.ts.map +1 -0
- package/dist/inline/policy.js +108 -0
- package/dist/inline/policy.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { detectMime, extensionForMime, loadWebMedia, resolveChannelMediaMaxBytes, } from "openclaw/plugin-sdk";
|
|
3
|
+
const DEFAULT_MEDIA_MAX_MB = 20;
|
|
4
|
+
const SUPPORTED_INLINE_PHOTO_MIME = new Set(["image/jpeg", "image/png", "image/gif"]);
|
|
5
|
+
const SUPPORTED_INLINE_VIDEO_MIME = new Set(["video/mp4"]);
|
|
6
|
+
const DEFAULT_VIDEO_WIDTH = 1280;
|
|
7
|
+
const DEFAULT_VIDEO_HEIGHT = 720;
|
|
8
|
+
const DEFAULT_VIDEO_DURATION = 1;
|
|
9
|
+
function normalizeMime(raw) {
|
|
10
|
+
const trimmed = raw?.trim().toLowerCase();
|
|
11
|
+
return trimmed || undefined;
|
|
12
|
+
}
|
|
13
|
+
function normalizeExt(rawFileName) {
|
|
14
|
+
const ext = path.extname(rawFileName ?? "").trim().toLowerCase();
|
|
15
|
+
if (!ext)
|
|
16
|
+
return undefined;
|
|
17
|
+
return ext.replace(/^\./, "");
|
|
18
|
+
}
|
|
19
|
+
function isSupportedPhoto(params) {
|
|
20
|
+
if (params.mime && SUPPORTED_INLINE_PHOTO_MIME.has(params.mime))
|
|
21
|
+
return true;
|
|
22
|
+
return params.ext === "jpg" || params.ext === "jpeg" || params.ext === "png" || params.ext === "gif";
|
|
23
|
+
}
|
|
24
|
+
function isSupportedVideo(params) {
|
|
25
|
+
if (params.mime && SUPPORTED_INLINE_VIDEO_MIME.has(params.mime))
|
|
26
|
+
return true;
|
|
27
|
+
return params.ext === "mp4";
|
|
28
|
+
}
|
|
29
|
+
function chooseUploadType(params) {
|
|
30
|
+
// Prefer explicit MIME/extension compatibility when available.
|
|
31
|
+
if (isSupportedPhoto(params))
|
|
32
|
+
return "photo";
|
|
33
|
+
if (isSupportedVideo(params))
|
|
34
|
+
return "video";
|
|
35
|
+
// Fall back to loader-detected media kind when MIME/extension is unavailable.
|
|
36
|
+
if (params.kind === "image")
|
|
37
|
+
return "photo";
|
|
38
|
+
if (params.kind === "video")
|
|
39
|
+
return "video";
|
|
40
|
+
// Fallback to document when Inline media validators would reject the file.
|
|
41
|
+
return "document";
|
|
42
|
+
}
|
|
43
|
+
function ensureUploadFileName(params) {
|
|
44
|
+
const trimmed = params.fileName?.trim();
|
|
45
|
+
if (trimmed) {
|
|
46
|
+
const ext = normalizeExt(trimmed);
|
|
47
|
+
if (ext)
|
|
48
|
+
return trimmed;
|
|
49
|
+
}
|
|
50
|
+
const inferredExt = params.ext ?? extensionForMime(params.mime) ?? undefined;
|
|
51
|
+
const fallbackExt = inferredExt ??
|
|
52
|
+
(params.uploadType === "photo" ? "jpg" : params.uploadType === "video" ? "mp4" : "bin");
|
|
53
|
+
return `attachment.${fallbackExt}`;
|
|
54
|
+
}
|
|
55
|
+
function resolveMediaMaxBytes(params) {
|
|
56
|
+
return (resolveChannelMediaMaxBytes({
|
|
57
|
+
cfg: params.cfg,
|
|
58
|
+
...(params.accountId != null ? { accountId: params.accountId } : {}),
|
|
59
|
+
resolveChannelLimitMb: ({ cfg, accountId }) => cfg.channels?.inline?.accounts?.[accountId]?.mediaMaxMb ??
|
|
60
|
+
cfg.channels?.inline?.mediaMaxMb,
|
|
61
|
+
}) ??
|
|
62
|
+
DEFAULT_MEDIA_MAX_MB * 1024 * 1024);
|
|
63
|
+
}
|
|
64
|
+
function mediaFromUploadResult(params) {
|
|
65
|
+
if (params.uploadType === "photo" && params.result.photoId != null) {
|
|
66
|
+
return { kind: "photo", photoId: params.result.photoId };
|
|
67
|
+
}
|
|
68
|
+
if (params.uploadType === "video" && params.result.videoId != null) {
|
|
69
|
+
return { kind: "video", videoId: params.result.videoId };
|
|
70
|
+
}
|
|
71
|
+
if (params.result.documentId != null) {
|
|
72
|
+
return { kind: "document", documentId: params.result.documentId };
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`inline media upload: missing ${params.uploadType} id in upload response`);
|
|
75
|
+
}
|
|
76
|
+
export async function uploadInlineMediaFromUrl(params) {
|
|
77
|
+
const maxBytes = resolveMediaMaxBytes({
|
|
78
|
+
cfg: params.cfg,
|
|
79
|
+
accountId: params.accountId ?? null,
|
|
80
|
+
});
|
|
81
|
+
const loaded = await loadWebMedia(params.mediaUrl, maxBytes);
|
|
82
|
+
const detectedMime = normalizeMime(loaded.contentType ??
|
|
83
|
+
(await detectMime({
|
|
84
|
+
buffer: loaded.buffer,
|
|
85
|
+
...(loaded.fileName ? { filePath: loaded.fileName } : {}),
|
|
86
|
+
})));
|
|
87
|
+
const normalizedExt = normalizeExt(loaded.fileName);
|
|
88
|
+
const uploadType = chooseUploadType({
|
|
89
|
+
kind: loaded.kind,
|
|
90
|
+
...(detectedMime ? { mime: detectedMime } : {}),
|
|
91
|
+
...(normalizedExt ? { ext: normalizedExt } : {}),
|
|
92
|
+
});
|
|
93
|
+
const fileName = ensureUploadFileName({
|
|
94
|
+
...(loaded.fileName ? { fileName: loaded.fileName } : {}),
|
|
95
|
+
uploadType,
|
|
96
|
+
...(detectedMime ? { mime: detectedMime } : {}),
|
|
97
|
+
...(normalizedExt ? { ext: normalizedExt } : {}),
|
|
98
|
+
});
|
|
99
|
+
const upload = await params.client.uploadFile({
|
|
100
|
+
type: uploadType,
|
|
101
|
+
file: loaded.buffer,
|
|
102
|
+
fileName,
|
|
103
|
+
...(detectedMime ? { contentType: detectedMime } : {}),
|
|
104
|
+
...(uploadType === "video"
|
|
105
|
+
? {
|
|
106
|
+
width: DEFAULT_VIDEO_WIDTH,
|
|
107
|
+
height: DEFAULT_VIDEO_HEIGHT,
|
|
108
|
+
duration: DEFAULT_VIDEO_DURATION,
|
|
109
|
+
}
|
|
110
|
+
: {}),
|
|
111
|
+
});
|
|
112
|
+
return mediaFromUploadResult({
|
|
113
|
+
uploadType,
|
|
114
|
+
result: upload,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/inline/media.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../src/inline/monitor.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../src/inline/monitor.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAO9E,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B,CAAA;AAED,KAAK,UAAU,GAAG,CAAC,KAAK,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAA;AAgQ1G,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,GAAG,EAAE,cAAc,CAAA;IACnB,OAAO,EAAE,qBAAqB,CAAA;IAC9B,OAAO,EAAE,UAAU,CAAA;IACnB,WAAW,EAAE,WAAW,CAAA;IACxB,GAAG,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAA;IAC/H,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqiB/B"}
|
package/dist/inline/monitor.js
CHANGED
|
@@ -3,8 +3,15 @@ import path from "node:path";
|
|
|
3
3
|
import { createReplyPrefixOptions, createTypingCallbacks, logInboundDrop, resolveControlCommandGate, resolveMentionGatingWithBypass, } from "openclaw/plugin-sdk";
|
|
4
4
|
import { InlineSdkClient, JsonFileStateStore, Method } from "@inline-chat/realtime-sdk";
|
|
5
5
|
import { resolveInlineToken } from "./accounts.js";
|
|
6
|
+
import { resolveInlineGroupRequireMention } from "./policy.js";
|
|
6
7
|
import { getInlineRuntime } from "../runtime.js";
|
|
8
|
+
import { uploadInlineMediaFromUrl } from "./media.js";
|
|
7
9
|
const CHANNEL_ID = "inline";
|
|
10
|
+
const DEFAULT_GROUP_HISTORY_LIMIT = 12;
|
|
11
|
+
const DEFAULT_DM_HISTORY_LIMIT = 6;
|
|
12
|
+
const HISTORY_LINE_MAX_CHARS = 280;
|
|
13
|
+
const BOT_MESSAGE_CACHE_LIMIT = 500;
|
|
14
|
+
const REACTION_TARGET_LOOKUP_LIMIT = 8;
|
|
8
15
|
function normalizeAllowEntry(raw) {
|
|
9
16
|
return raw.trim().replace(/^inline:/i, "").replace(/^user:/i, "");
|
|
10
17
|
}
|
|
@@ -54,6 +61,146 @@ function rewriteNumericMentionsToUsernames(text, senderProfilesById) {
|
|
|
54
61
|
return `${prefix}@${username}`;
|
|
55
62
|
});
|
|
56
63
|
}
|
|
64
|
+
function rememberBotMessageId(cache, chatId, messageId) {
|
|
65
|
+
const key = String(chatId);
|
|
66
|
+
const list = cache.get(key) ?? [];
|
|
67
|
+
const nextId = String(messageId);
|
|
68
|
+
if (!list.includes(nextId))
|
|
69
|
+
list.push(nextId);
|
|
70
|
+
if (list.length > BOT_MESSAGE_CACHE_LIMIT) {
|
|
71
|
+
list.splice(0, list.length - BOT_MESSAGE_CACHE_LIMIT);
|
|
72
|
+
}
|
|
73
|
+
cache.set(key, list);
|
|
74
|
+
}
|
|
75
|
+
function hasBotMessageId(cache, chatId, messageId) {
|
|
76
|
+
const key = String(chatId);
|
|
77
|
+
return (cache.get(key) ?? []).includes(String(messageId));
|
|
78
|
+
}
|
|
79
|
+
async function isReactionTargetBotMessage(params) {
|
|
80
|
+
if (hasBotMessageId(params.botMessageIdsByChat, params.chatId, params.messageId)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
const result = await params.client.invokeRaw(Method.GET_CHAT_HISTORY, {
|
|
84
|
+
oneofKind: "getChatHistory",
|
|
85
|
+
getChatHistory: {
|
|
86
|
+
peerId: {
|
|
87
|
+
type: {
|
|
88
|
+
oneofKind: "chat",
|
|
89
|
+
chat: { chatId: params.chatId },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
offsetId: params.messageId + 1n,
|
|
93
|
+
limit: REACTION_TARGET_LOOKUP_LIMIT,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
if (result.oneofKind !== "getChatHistory") {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const messages = result.getChatHistory.messages ?? [];
|
|
100
|
+
for (const item of messages) {
|
|
101
|
+
if (item.fromId === params.meId) {
|
|
102
|
+
rememberBotMessageId(params.botMessageIdsByChat, params.chatId, item.id);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const target = messages.find((item) => item.id === params.messageId);
|
|
106
|
+
if (!target)
|
|
107
|
+
return false;
|
|
108
|
+
return target.fromId === params.meId;
|
|
109
|
+
}
|
|
110
|
+
function normalizeHistoryText(raw) {
|
|
111
|
+
const compact = (raw ?? "").replace(/\s+/g, " ").trim();
|
|
112
|
+
if (!compact)
|
|
113
|
+
return "";
|
|
114
|
+
if (compact.length <= HISTORY_LINE_MAX_CHARS)
|
|
115
|
+
return compact;
|
|
116
|
+
return `${compact.slice(0, HISTORY_LINE_MAX_CHARS - 1)}…`;
|
|
117
|
+
}
|
|
118
|
+
function resolveHistorySenderLabel(params) {
|
|
119
|
+
if (params.senderId === params.meId)
|
|
120
|
+
return "assistant";
|
|
121
|
+
const senderId = String(params.senderId);
|
|
122
|
+
const profile = params.senderProfilesById.get(senderId);
|
|
123
|
+
if (profile?.username)
|
|
124
|
+
return `@${profile.username}`;
|
|
125
|
+
if (profile?.name)
|
|
126
|
+
return profile.name;
|
|
127
|
+
return `user:${senderId}`;
|
|
128
|
+
}
|
|
129
|
+
function resolveHistoryLimit(params) {
|
|
130
|
+
if (params.isGroup) {
|
|
131
|
+
return params.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT;
|
|
132
|
+
}
|
|
133
|
+
return params.dmHistoryLimit ?? params.historyLimit ?? DEFAULT_DM_HISTORY_LIMIT;
|
|
134
|
+
}
|
|
135
|
+
async function buildHistoryContext(params) {
|
|
136
|
+
if (params.historyLimit <= 0) {
|
|
137
|
+
return {
|
|
138
|
+
historyText: null,
|
|
139
|
+
repliedToBot: params.replyToMsgId != null &&
|
|
140
|
+
hasBotMessageId(params.botMessageIdsByChat, params.chatId, params.replyToMsgId),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const result = await params.client.invokeRaw(Method.GET_CHAT_HISTORY, {
|
|
144
|
+
oneofKind: "getChatHistory",
|
|
145
|
+
getChatHistory: {
|
|
146
|
+
peerId: {
|
|
147
|
+
type: {
|
|
148
|
+
oneofKind: "chat",
|
|
149
|
+
chat: { chatId: params.chatId },
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
offsetId: params.currentMessageId,
|
|
153
|
+
limit: params.historyLimit,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
if (result.oneofKind !== "getChatHistory") {
|
|
157
|
+
return {
|
|
158
|
+
historyText: null,
|
|
159
|
+
repliedToBot: params.replyToMsgId != null &&
|
|
160
|
+
hasBotMessageId(params.botMessageIdsByChat, params.chatId, params.replyToMsgId),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const messages = [...(result.getChatHistory.messages ?? [])]
|
|
164
|
+
.filter((item) => item.id !== params.currentMessageId)
|
|
165
|
+
.sort((a, b) => {
|
|
166
|
+
const byDate = Number(a.date - b.date);
|
|
167
|
+
if (byDate !== 0)
|
|
168
|
+
return byDate;
|
|
169
|
+
if (a.id === b.id)
|
|
170
|
+
return 0;
|
|
171
|
+
return a.id < b.id ? -1 : 1;
|
|
172
|
+
});
|
|
173
|
+
const lines = [];
|
|
174
|
+
let repliedToBot = params.replyToMsgId != null &&
|
|
175
|
+
hasBotMessageId(params.botMessageIdsByChat, params.chatId, params.replyToMsgId);
|
|
176
|
+
for (const item of messages) {
|
|
177
|
+
if (item.fromId === params.meId) {
|
|
178
|
+
rememberBotMessageId(params.botMessageIdsByChat, params.chatId, item.id);
|
|
179
|
+
}
|
|
180
|
+
if (params.replyToMsgId != null &&
|
|
181
|
+
item.id === params.replyToMsgId &&
|
|
182
|
+
item.fromId === params.meId) {
|
|
183
|
+
repliedToBot = true;
|
|
184
|
+
}
|
|
185
|
+
const text = normalizeHistoryText(item.message);
|
|
186
|
+
if (!text)
|
|
187
|
+
continue;
|
|
188
|
+
const label = resolveHistorySenderLabel({
|
|
189
|
+
senderId: item.fromId,
|
|
190
|
+
meId: params.meId,
|
|
191
|
+
senderProfilesById: params.senderProfilesById,
|
|
192
|
+
});
|
|
193
|
+
const replySuffix = item.replyToMsgId != null ? ` ->${String(item.replyToMsgId)}` : "";
|
|
194
|
+
lines.push(`#${String(item.id)}${replySuffix} ${label}: ${text}`);
|
|
195
|
+
}
|
|
196
|
+
if (!lines.length) {
|
|
197
|
+
return { historyText: null, repliedToBot };
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
historyText: `Recent thread messages (oldest -> newest):\n${lines.join("\n")}`,
|
|
201
|
+
repliedToBot,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
57
204
|
export async function monitorInlineProvider(params) {
|
|
58
205
|
const { cfg, account, runtime, abortSignal, log, statusSink } = params;
|
|
59
206
|
const core = getInlineRuntime();
|
|
@@ -81,6 +228,7 @@ export async function monitorInlineProvider(params) {
|
|
|
81
228
|
log?.info(`[${account.accountId}] inline connected (me=${String(me.userId)})`);
|
|
82
229
|
const chatCache = new Map();
|
|
83
230
|
const senderProfilesById = new Map();
|
|
231
|
+
const botMessageIdsByChat = new Map();
|
|
84
232
|
const hydratedParticipantChats = new Set();
|
|
85
233
|
const participantFetches = new Map();
|
|
86
234
|
const hydrateChatParticipants = async (chatId) => {
|
|
@@ -127,15 +275,83 @@ export async function monitorInlineProvider(params) {
|
|
|
127
275
|
for await (const event of client.events()) {
|
|
128
276
|
if (abortSignal.aborted)
|
|
129
277
|
break;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
278
|
+
let msg;
|
|
279
|
+
let rawBody = "";
|
|
280
|
+
let reactionEvent = null;
|
|
281
|
+
if (event.kind === "message.new") {
|
|
282
|
+
msg = event.message;
|
|
283
|
+
rawBody = messageText(msg);
|
|
284
|
+
if (!rawBody)
|
|
285
|
+
continue;
|
|
286
|
+
// Ignore echoes / our own outbound messages.
|
|
287
|
+
if (msg.out || msg.fromId === me.userId)
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
else if (event.kind === "reaction.add") {
|
|
291
|
+
if (event.reaction.userId === me.userId)
|
|
292
|
+
continue;
|
|
293
|
+
const onBotMessage = await isReactionTargetBotMessage({
|
|
294
|
+
client,
|
|
295
|
+
chatId: event.chatId,
|
|
296
|
+
messageId: event.reaction.messageId,
|
|
297
|
+
meId: me.userId,
|
|
298
|
+
botMessageIdsByChat,
|
|
299
|
+
}).catch((err) => {
|
|
300
|
+
statusSink?.({ lastError: `getChatHistory (reaction target) failed: ${String(err)}` });
|
|
301
|
+
return false;
|
|
302
|
+
});
|
|
303
|
+
if (!onBotMessage)
|
|
304
|
+
continue;
|
|
305
|
+
reactionEvent = {
|
|
306
|
+
action: "added",
|
|
307
|
+
emoji: event.reaction.emoji,
|
|
308
|
+
targetMessageId: event.reaction.messageId,
|
|
309
|
+
};
|
|
310
|
+
msg = {
|
|
311
|
+
id: event.reaction.messageId,
|
|
312
|
+
chatId: event.chatId,
|
|
313
|
+
date: event.date,
|
|
314
|
+
fromId: event.reaction.userId,
|
|
315
|
+
message: "",
|
|
316
|
+
out: false,
|
|
317
|
+
mentioned: false,
|
|
318
|
+
replyToMsgId: event.reaction.messageId,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
else if (event.kind === "reaction.delete") {
|
|
322
|
+
if (event.userId === me.userId)
|
|
323
|
+
continue;
|
|
324
|
+
const onBotMessage = await isReactionTargetBotMessage({
|
|
325
|
+
client,
|
|
326
|
+
chatId: event.chatId,
|
|
327
|
+
messageId: event.messageId,
|
|
328
|
+
meId: me.userId,
|
|
329
|
+
botMessageIdsByChat,
|
|
330
|
+
}).catch((err) => {
|
|
331
|
+
statusSink?.({ lastError: `getChatHistory (reaction target) failed: ${String(err)}` });
|
|
332
|
+
return false;
|
|
333
|
+
});
|
|
334
|
+
if (!onBotMessage)
|
|
335
|
+
continue;
|
|
336
|
+
reactionEvent = {
|
|
337
|
+
action: "removed",
|
|
338
|
+
emoji: event.emoji,
|
|
339
|
+
targetMessageId: event.messageId,
|
|
340
|
+
};
|
|
341
|
+
msg = {
|
|
342
|
+
id: event.messageId,
|
|
343
|
+
chatId: event.chatId,
|
|
344
|
+
date: event.date,
|
|
345
|
+
fromId: event.userId,
|
|
346
|
+
message: "",
|
|
347
|
+
out: false,
|
|
348
|
+
mentioned: false,
|
|
349
|
+
replyToMsgId: event.messageId,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
138
353
|
continue;
|
|
354
|
+
}
|
|
139
355
|
const chatId = event.chatId;
|
|
140
356
|
statusSink?.({ lastInboundAt: Date.now() });
|
|
141
357
|
let chatInfo;
|
|
@@ -153,6 +369,19 @@ export async function monitorInlineProvider(params) {
|
|
|
153
369
|
const senderProfile = senderProfilesById.get(senderId);
|
|
154
370
|
const senderUsername = senderProfile?.username;
|
|
155
371
|
const senderName = senderProfile?.name ?? (!isGroup ? chatInfo.title ?? undefined : undefined);
|
|
372
|
+
if (reactionEvent) {
|
|
373
|
+
const actor = senderUsername != null && senderUsername.length > 0
|
|
374
|
+
? `@${senderUsername}`
|
|
375
|
+
: senderName ?? `user:${senderId}`;
|
|
376
|
+
const emoji = reactionEvent.emoji.trim() || "a reaction";
|
|
377
|
+
const messageId = String(reactionEvent.targetMessageId);
|
|
378
|
+
if (reactionEvent.action === "added") {
|
|
379
|
+
rawBody = `${actor} reacted with ${emoji} to your message #${messageId}`;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
rawBody = `${actor} removed ${emoji} from your message #${messageId}`;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
156
385
|
const dmPolicy = account.config.dmPolicy ?? "pairing";
|
|
157
386
|
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
|
158
387
|
const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
|
|
@@ -256,12 +485,43 @@ export async function monitorInlineProvider(params) {
|
|
|
256
485
|
: mentionRegexes.length
|
|
257
486
|
? core.channel.mentions.matchesMentionPatterns(rawBody, mentionRegexes)
|
|
258
487
|
: false;
|
|
259
|
-
const
|
|
488
|
+
const historyLimit = resolveHistoryLimit({
|
|
489
|
+
isGroup,
|
|
490
|
+
historyLimit: account.config.historyLimit,
|
|
491
|
+
dmHistoryLimit: account.config.dmHistoryLimit,
|
|
492
|
+
});
|
|
493
|
+
const historyContext = await buildHistoryContext({
|
|
494
|
+
client,
|
|
495
|
+
chatId,
|
|
496
|
+
currentMessageId: msg.id,
|
|
497
|
+
replyToMsgId: msg.replyToMsgId,
|
|
498
|
+
senderProfilesById,
|
|
499
|
+
meId: me.userId,
|
|
500
|
+
historyLimit,
|
|
501
|
+
botMessageIdsByChat,
|
|
502
|
+
}).catch((err) => {
|
|
503
|
+
statusSink?.({ lastError: `getChatHistory failed: ${String(err)}` });
|
|
504
|
+
return { historyText: null, repliedToBot: false };
|
|
505
|
+
});
|
|
506
|
+
const implicitMention = (reactionEvent != null && isGroup) ||
|
|
507
|
+
(isGroup &&
|
|
508
|
+
(account.config.replyToBotWithoutMention ?? false) &&
|
|
509
|
+
msg.replyToMsgId != null &&
|
|
510
|
+
historyContext.repliedToBot);
|
|
511
|
+
const requireMention = isGroup
|
|
512
|
+
? resolveInlineGroupRequireMention({
|
|
513
|
+
cfg,
|
|
514
|
+
groupId: String(chatId),
|
|
515
|
+
accountId: account.accountId,
|
|
516
|
+
requireMentionDefault: account.config.requireMention ?? false,
|
|
517
|
+
})
|
|
518
|
+
: false;
|
|
260
519
|
const mentionGate = resolveMentionGatingWithBypass({
|
|
261
520
|
isGroup,
|
|
262
521
|
requireMention,
|
|
263
522
|
canDetectMention: typeof msg.mentioned === "boolean" || mentionRegexes.length > 0,
|
|
264
523
|
wasMentioned,
|
|
524
|
+
implicitMention,
|
|
265
525
|
allowTextCommands,
|
|
266
526
|
hasControlCommand,
|
|
267
527
|
commandAuthorized,
|
|
@@ -281,7 +541,9 @@ export async function monitorInlineProvider(params) {
|
|
|
281
541
|
timestamp,
|
|
282
542
|
...(previousTimestamp != null ? { previousTimestamp } : {}),
|
|
283
543
|
envelope: envelopeOptions,
|
|
284
|
-
body:
|
|
544
|
+
body: historyContext.historyText
|
|
545
|
+
? `${historyContext.historyText}\n\nCurrent message:\n${rawBody}`
|
|
546
|
+
: rawBody,
|
|
285
547
|
});
|
|
286
548
|
const ctxPayload = core.channel.reply.finalizeInboundContext({
|
|
287
549
|
Body: body,
|
|
@@ -323,19 +585,35 @@ export async function monitorInlineProvider(params) {
|
|
|
323
585
|
: {}),
|
|
324
586
|
onRecordError: (err) => runtime.error?.(`inline: failed updating session meta: ${String(err)}`),
|
|
325
587
|
});
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
588
|
+
const prefixConfig = (typeof createReplyPrefixOptions === "function"
|
|
589
|
+
? createReplyPrefixOptions({
|
|
590
|
+
cfg,
|
|
591
|
+
agentId: route.agentId,
|
|
592
|
+
channel: CHANNEL_ID,
|
|
593
|
+
accountId: account.accountId,
|
|
594
|
+
})
|
|
595
|
+
: {});
|
|
596
|
+
const onModelSelected = typeof prefixConfig.onModelSelected === "function"
|
|
597
|
+
? prefixConfig.onModelSelected
|
|
598
|
+
: undefined;
|
|
599
|
+
const { onModelSelected: _ignoredOnModelSelected, ...prefixOptions } = prefixConfig;
|
|
600
|
+
const typingCallbacks = typeof createTypingCallbacks === "function"
|
|
601
|
+
? createTypingCallbacks({
|
|
602
|
+
start: () => client.sendTyping({ chatId, typing: true }),
|
|
603
|
+
stop: () => client.sendTyping({ chatId, typing: false }),
|
|
604
|
+
onStartError: (err) => runtime.error?.(`inline typing start failed: ${String(err)}`),
|
|
605
|
+
onStopError: (err) => runtime.error?.(`inline typing stop failed: ${String(err)}`),
|
|
606
|
+
})
|
|
607
|
+
: {};
|
|
338
608
|
const parseMarkdown = account.config.parseMarkdown ?? true;
|
|
609
|
+
const disableBlockStreaming = typeof account.config.blockStreaming === "boolean"
|
|
610
|
+
? !account.config.blockStreaming
|
|
611
|
+
: undefined;
|
|
612
|
+
const replyOptions = {
|
|
613
|
+
...(onModelSelected ? { onModelSelected } : {}),
|
|
614
|
+
blockReplyTimeoutMs: 25_000,
|
|
615
|
+
...(typeof disableBlockStreaming === "boolean" ? { disableBlockStreaming } : {}),
|
|
616
|
+
};
|
|
339
617
|
await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
340
618
|
ctx: ctxPayload,
|
|
341
619
|
cfg,
|
|
@@ -343,21 +621,13 @@ export async function monitorInlineProvider(params) {
|
|
|
343
621
|
...prefixOptions,
|
|
344
622
|
...typingCallbacks,
|
|
345
623
|
deliver: async (payload) => {
|
|
346
|
-
const
|
|
624
|
+
const rawText = (payload.text ?? "").trim();
|
|
347
625
|
const mediaList = payload.mediaUrls?.length
|
|
348
626
|
? payload.mediaUrls
|
|
349
627
|
: payload.mediaUrl
|
|
350
628
|
? [payload.mediaUrl]
|
|
351
629
|
: [];
|
|
352
|
-
const
|
|
353
|
-
const combined = text
|
|
354
|
-
? mediaBlock
|
|
355
|
-
? `${text}\n\n${mediaBlock}`
|
|
356
|
-
: text
|
|
357
|
-
: mediaBlock;
|
|
358
|
-
if (!combined.trim())
|
|
359
|
-
return;
|
|
360
|
-
const outboundText = rewriteNumericMentionsToUsernames(combined, senderProfilesById);
|
|
630
|
+
const outboundText = rewriteNumericMentionsToUsernames(rawText, senderProfilesById);
|
|
361
631
|
let replyToMsgId;
|
|
362
632
|
if (payload.replyToId != null) {
|
|
363
633
|
try {
|
|
@@ -367,20 +637,68 @@ export async function monitorInlineProvider(params) {
|
|
|
367
637
|
// ignore
|
|
368
638
|
}
|
|
369
639
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
640
|
+
// Keep reply chains threaded when inbound is a reply in group chats.
|
|
641
|
+
if (replyToMsgId == null && isGroup && msg.replyToMsgId != null) {
|
|
642
|
+
replyToMsgId = msg.id;
|
|
643
|
+
}
|
|
644
|
+
const rememberSent = (messageId) => {
|
|
645
|
+
if (messageId != null) {
|
|
646
|
+
rememberBotMessageId(botMessageIdsByChat, chatId, messageId);
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
const sendTextFallback = async (text, includeReplyTo) => {
|
|
650
|
+
if (!text.trim())
|
|
651
|
+
return;
|
|
652
|
+
const sent = await client.sendMessage({
|
|
653
|
+
chatId,
|
|
654
|
+
text,
|
|
655
|
+
...(includeReplyTo && replyToMsgId != null ? { replyToMsgId } : {}),
|
|
656
|
+
parseMarkdown,
|
|
657
|
+
});
|
|
658
|
+
rememberSent(sent.messageId);
|
|
659
|
+
};
|
|
660
|
+
if (mediaList.length === 0) {
|
|
661
|
+
if (!outboundText.trim())
|
|
662
|
+
return;
|
|
663
|
+
await sendTextFallback(outboundText, true);
|
|
664
|
+
statusSink?.({ lastOutboundAt: Date.now() });
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
for (let index = 0; index < mediaList.length; index++) {
|
|
668
|
+
const mediaUrl = mediaList[index];
|
|
669
|
+
if (!mediaUrl?.trim())
|
|
670
|
+
continue;
|
|
671
|
+
const isFirst = index === 0;
|
|
672
|
+
const caption = isFirst ? outboundText : "";
|
|
673
|
+
try {
|
|
674
|
+
const media = await uploadInlineMediaFromUrl({
|
|
675
|
+
client,
|
|
676
|
+
cfg,
|
|
677
|
+
accountId: account.accountId,
|
|
678
|
+
mediaUrl,
|
|
679
|
+
});
|
|
680
|
+
const sent = await client.sendMessage({
|
|
681
|
+
chatId,
|
|
682
|
+
...(caption ? { text: caption } : {}),
|
|
683
|
+
media,
|
|
684
|
+
...(isFirst && replyToMsgId != null ? { replyToMsgId } : {}),
|
|
685
|
+
...(caption ? { parseMarkdown } : {}),
|
|
686
|
+
});
|
|
687
|
+
rememberSent(sent.messageId);
|
|
688
|
+
}
|
|
689
|
+
catch (error) {
|
|
690
|
+
runtime.error?.(`inline media upload failed; falling back to url text (${String(error)})`);
|
|
691
|
+
const fallbackText = caption
|
|
692
|
+
? `${caption}\n\nAttachment: ${mediaUrl}`
|
|
693
|
+
: `Attachment: ${mediaUrl}`;
|
|
694
|
+
await sendTextFallback(fallbackText, isFirst);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
376
697
|
statusSink?.({ lastOutboundAt: Date.now() });
|
|
377
698
|
},
|
|
378
699
|
onError: (err, info) => runtime.error?.(`inline ${info.kind} reply failed: ${String(err)}`),
|
|
379
700
|
},
|
|
380
|
-
replyOptions
|
|
381
|
-
onModelSelected,
|
|
382
|
-
blockReplyTimeoutMs: 25_000,
|
|
383
|
-
},
|
|
701
|
+
replyOptions,
|
|
384
702
|
});
|
|
385
703
|
}
|
|
386
704
|
}
|