@elizaos/plugin-discord 1.0.4 → 1.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/dist/index.js +442 -170
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -79,7 +79,7 @@ var chatWithAttachments = {
|
|
|
79
79
|
description: "Answer a user request informed by specific attachments based on their IDs. If a user asks to chat with a PDF, or wants more specific information about a link or video or anything else they've attached, this is the action to use.",
|
|
80
80
|
validate: async (_runtime, message, _state) => {
|
|
81
81
|
const room = await _runtime.getRoom(message.roomId);
|
|
82
|
-
if (room?.type !== ChannelType.GROUP) {
|
|
82
|
+
if (room?.type !== ChannelType.GROUP || room?.source !== "discord") {
|
|
83
83
|
return false;
|
|
84
84
|
}
|
|
85
85
|
const keywords = [
|
|
@@ -148,20 +148,28 @@ var chatWithAttachments = {
|
|
|
148
148
|
count: conversationLength,
|
|
149
149
|
unique: false
|
|
150
150
|
});
|
|
151
|
-
const attachments = recentMessages.filter(
|
|
151
|
+
const attachments = recentMessages.filter(
|
|
152
|
+
(msg) => msg.content.attachments && msg.content.attachments.length > 0
|
|
153
|
+
).flatMap((msg) => msg.content.attachments).filter(
|
|
152
154
|
(attachment) => attachment && (attachmentIds.map((attch) => attch.toLowerCase().slice(0, 5)).includes(attachment.id.toLowerCase().slice(0, 5)) || // or check the other way
|
|
153
155
|
attachmentIds.some((id) => {
|
|
154
156
|
const attachmentId = id.toLowerCase().slice(0, 5);
|
|
155
157
|
return attachment && attachment.id.toLowerCase().includes(attachmentId);
|
|
156
158
|
}))
|
|
157
159
|
);
|
|
158
|
-
const attachmentsWithText = attachments.filter(
|
|
160
|
+
const attachmentsWithText = attachments.filter(
|
|
161
|
+
(attachment) => !!attachment
|
|
162
|
+
).map((attachment) => `# ${attachment.title}
|
|
159
163
|
${attachment.text}`).join("\n\n");
|
|
160
164
|
let currentSummary = "";
|
|
161
165
|
const chunkSize = 8192;
|
|
162
166
|
state.values.attachmentsWithText = attachmentsWithText;
|
|
163
167
|
state.values.objective = objective;
|
|
164
|
-
const template = await trimTokens(
|
|
168
|
+
const template = await trimTokens(
|
|
169
|
+
summarizationTemplate,
|
|
170
|
+
chunkSize,
|
|
171
|
+
runtime
|
|
172
|
+
);
|
|
165
173
|
const prompt = composePromptFromState({
|
|
166
174
|
state,
|
|
167
175
|
// make sure it fits, we can pad the tokens a bit
|
|
@@ -220,7 +228,9 @@ ${currentSummary.trim()}
|
|
|
220
228
|
throw error;
|
|
221
229
|
}
|
|
222
230
|
} else {
|
|
223
|
-
console.warn(
|
|
231
|
+
console.warn(
|
|
232
|
+
"Empty response from chat with attachments action, skipping"
|
|
233
|
+
);
|
|
224
234
|
}
|
|
225
235
|
return callbackData;
|
|
226
236
|
},
|
|
@@ -390,7 +400,9 @@ var downloadMedia = {
|
|
|
390
400
|
retries++;
|
|
391
401
|
console.error(`Error sending message (attempt ${retries}):`, error);
|
|
392
402
|
if (retries === maxRetries) {
|
|
393
|
-
console.error(
|
|
403
|
+
console.error(
|
|
404
|
+
"Max retries reached. Failed to send message with attachment."
|
|
405
|
+
);
|
|
394
406
|
break;
|
|
395
407
|
}
|
|
396
408
|
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
@@ -496,8 +508,12 @@ var getDateRange = async (runtime, _message, state) => {
|
|
|
496
508
|
const parsedResponse = parseJSONObjectFromText3(response);
|
|
497
509
|
if (parsedResponse) {
|
|
498
510
|
if (parsedResponse.objective && parsedResponse.start && parsedResponse.end) {
|
|
499
|
-
const startIntegerString = parsedResponse.start.match(
|
|
500
|
-
|
|
511
|
+
const startIntegerString = parsedResponse.start.match(
|
|
512
|
+
/\d+/
|
|
513
|
+
)?.[0];
|
|
514
|
+
const endIntegerString = parsedResponse.end.match(
|
|
515
|
+
/\d+/
|
|
516
|
+
)?.[0];
|
|
501
517
|
const multipliers = {
|
|
502
518
|
second: 1 * 1e3,
|
|
503
519
|
minute: 60 * 1e3,
|
|
@@ -507,7 +523,9 @@ var getDateRange = async (runtime, _message, state) => {
|
|
|
507
523
|
const startMultiplier = parsedResponse.start.match(
|
|
508
524
|
/second|minute|hour|day/
|
|
509
525
|
)?.[0];
|
|
510
|
-
const endMultiplier = parsedResponse.end.match(
|
|
526
|
+
const endMultiplier = parsedResponse.end.match(
|
|
527
|
+
/second|minute|hour|day/
|
|
528
|
+
)?.[0];
|
|
511
529
|
const startInteger = startIntegerString ? Number.parseInt(startIntegerString) : 0;
|
|
512
530
|
const endInteger = endIntegerString ? Number.parseInt(endIntegerString) : 0;
|
|
513
531
|
const startTime = startInteger * multipliers[startMultiplier];
|
|
@@ -641,7 +659,11 @@ ${attachments}`;
|
|
|
641
659
|
const chunk = chunks[i];
|
|
642
660
|
state.values.currentSummary = currentSummary;
|
|
643
661
|
state.values.currentChunk = chunk;
|
|
644
|
-
const template = await trimTokens2(
|
|
662
|
+
const template = await trimTokens2(
|
|
663
|
+
summarizationTemplate2,
|
|
664
|
+
chunkSize + 500,
|
|
665
|
+
runtime
|
|
666
|
+
);
|
|
645
667
|
const prompt = composePromptFromState3({
|
|
646
668
|
state,
|
|
647
669
|
// make sure it fits, we can pad the tokens a bit
|
|
@@ -695,7 +717,9 @@ ${currentSummary.trim()}
|
|
|
695
717
|
[summaryFilename]
|
|
696
718
|
);
|
|
697
719
|
} else {
|
|
698
|
-
console.warn(
|
|
720
|
+
console.warn(
|
|
721
|
+
"Empty response from summarize conversation action, skipping"
|
|
722
|
+
);
|
|
699
723
|
}
|
|
700
724
|
return callbackData;
|
|
701
725
|
},
|
|
@@ -875,7 +899,11 @@ var transcribeMedia = {
|
|
|
875
899
|
count: conversationLength,
|
|
876
900
|
unique: false
|
|
877
901
|
});
|
|
878
|
-
const attachment = recentMessages.filter(
|
|
902
|
+
const attachment = recentMessages.filter(
|
|
903
|
+
(msg) => msg.content.attachments && msg.content.attachments.length > 0
|
|
904
|
+
).flatMap((msg) => msg.content.attachments).find(
|
|
905
|
+
(attachment2) => attachment2?.id.toLowerCase() === attachmentId.toLowerCase()
|
|
906
|
+
);
|
|
879
907
|
if (!attachment) {
|
|
880
908
|
console.error(`Couldn't find attachment with ID ${attachmentId}`);
|
|
881
909
|
await runtime.createMemory(
|
|
@@ -1011,7 +1039,9 @@ var joinVoice = {
|
|
|
1011
1039
|
if (!serverId) {
|
|
1012
1040
|
throw new Error("No server ID found");
|
|
1013
1041
|
}
|
|
1014
|
-
const discordClient = runtime.getService(
|
|
1042
|
+
const discordClient = runtime.getService(
|
|
1043
|
+
ServiceType2.DISCORD
|
|
1044
|
+
);
|
|
1015
1045
|
const client = discordClient.client;
|
|
1016
1046
|
const voiceManager = discordClient.voiceManager;
|
|
1017
1047
|
if (!client) {
|
|
@@ -1314,7 +1344,9 @@ var leaveVoice = {
|
|
|
1314
1344
|
if (!serverId) {
|
|
1315
1345
|
throw new Error("No server ID found 9");
|
|
1316
1346
|
}
|
|
1317
|
-
const discordClient = runtime.getService(
|
|
1347
|
+
const discordClient = runtime.getService(
|
|
1348
|
+
ServiceType2.DISCORD
|
|
1349
|
+
);
|
|
1318
1350
|
const voiceManager = discordClient.voiceManager;
|
|
1319
1351
|
const client = discordClient.client;
|
|
1320
1352
|
if (!client) {
|
|
@@ -1574,7 +1606,9 @@ var channelStateProvider = {
|
|
|
1574
1606
|
};
|
|
1575
1607
|
}
|
|
1576
1608
|
channelId = room.channelId;
|
|
1577
|
-
const discordService = runtime.getService(
|
|
1609
|
+
const discordService = runtime.getService(
|
|
1610
|
+
ServiceType2.DISCORD
|
|
1611
|
+
);
|
|
1578
1612
|
if (!discordService) {
|
|
1579
1613
|
console.warn("No discord client found");
|
|
1580
1614
|
return {
|
|
@@ -1772,7 +1806,10 @@ import {
|
|
|
1772
1806
|
import fs3 from "node:fs";
|
|
1773
1807
|
import { trimTokens as trimTokens3 } from "@elizaos/core";
|
|
1774
1808
|
import { parseJSONObjectFromText as parseJSONObjectFromText5 } from "@elizaos/core";
|
|
1775
|
-
import {
|
|
1809
|
+
import {
|
|
1810
|
+
ModelType as ModelType6,
|
|
1811
|
+
ServiceType as ServiceType3
|
|
1812
|
+
} from "@elizaos/core";
|
|
1776
1813
|
import { Collection } from "discord.js";
|
|
1777
1814
|
import ffmpeg from "fluent-ffmpeg";
|
|
1778
1815
|
async function generateSummary(runtime, text) {
|
|
@@ -1854,7 +1891,9 @@ var AttachmentManager = class {
|
|
|
1854
1891
|
media = await this.processAudioVideoAttachment(attachment);
|
|
1855
1892
|
} else if (attachment.contentType?.startsWith("image/")) {
|
|
1856
1893
|
media = await this.processImageAttachment(attachment);
|
|
1857
|
-
} else if (attachment.contentType?.startsWith("video/") || this.runtime.getService(ServiceType3.VIDEO)?.isVideoUrl(
|
|
1894
|
+
} else if (attachment.contentType?.startsWith("video/") || this.runtime.getService(ServiceType3.VIDEO)?.isVideoUrl(
|
|
1895
|
+
attachment.url
|
|
1896
|
+
)) {
|
|
1858
1897
|
media = await this.processVideoAttachment(attachment);
|
|
1859
1898
|
} else {
|
|
1860
1899
|
media = await this.processGenericAttachment(attachment);
|
|
@@ -1881,8 +1920,14 @@ var AttachmentManager = class {
|
|
|
1881
1920
|
} else {
|
|
1882
1921
|
throw new Error("Unsupported audio/video format");
|
|
1883
1922
|
}
|
|
1884
|
-
const transcription = await this.runtime.useModel(
|
|
1885
|
-
|
|
1923
|
+
const transcription = await this.runtime.useModel(
|
|
1924
|
+
ModelType6.TRANSCRIPTION,
|
|
1925
|
+
audioBuffer
|
|
1926
|
+
);
|
|
1927
|
+
const { title, description } = await generateSummary(
|
|
1928
|
+
this.runtime,
|
|
1929
|
+
transcription
|
|
1930
|
+
);
|
|
1886
1931
|
return {
|
|
1887
1932
|
id: attachment.id,
|
|
1888
1933
|
url: attachment.url,
|
|
@@ -1893,7 +1938,9 @@ var AttachmentManager = class {
|
|
|
1893
1938
|
};
|
|
1894
1939
|
} catch (error) {
|
|
1895
1940
|
if (error instanceof Error) {
|
|
1896
|
-
console.error(
|
|
1941
|
+
console.error(
|
|
1942
|
+
`Error processing audio/video attachment: ${error.message}`
|
|
1943
|
+
);
|
|
1897
1944
|
}
|
|
1898
1945
|
return {
|
|
1899
1946
|
id: attachment.id,
|
|
@@ -1997,9 +2044,13 @@ var AttachmentManager = class {
|
|
|
1997
2044
|
};
|
|
1998
2045
|
} catch (error) {
|
|
1999
2046
|
if (error instanceof Error) {
|
|
2000
|
-
console.error(
|
|
2047
|
+
console.error(
|
|
2048
|
+
`Error processing plaintext attachment: ${error.message}`
|
|
2049
|
+
);
|
|
2001
2050
|
} else {
|
|
2002
|
-
console.error(
|
|
2051
|
+
console.error(
|
|
2052
|
+
`An unknown error occurred during plaintext attachment processing`
|
|
2053
|
+
);
|
|
2003
2054
|
}
|
|
2004
2055
|
return {
|
|
2005
2056
|
id: attachment.id,
|
|
@@ -2075,7 +2126,10 @@ var AttachmentManager = class {
|
|
|
2075
2126
|
};
|
|
2076
2127
|
}
|
|
2077
2128
|
if (typeof videoService.isVideoUrl === "function" && videoService.isVideoUrl(attachment.url)) {
|
|
2078
|
-
const videoInfo = await videoService.processVideo(
|
|
2129
|
+
const videoInfo = await videoService.processVideo(
|
|
2130
|
+
attachment.url,
|
|
2131
|
+
this.runtime
|
|
2132
|
+
);
|
|
2079
2133
|
return {
|
|
2080
2134
|
id: attachment.id,
|
|
2081
2135
|
url: attachment.url,
|
|
@@ -2147,7 +2201,9 @@ async function sendMessageInChunks(channel, content, _inReplyTo, files, componen
|
|
|
2147
2201
|
};
|
|
2148
2202
|
logger3.info(`Components received: ${safeStringify(components)}`);
|
|
2149
2203
|
if (!Array.isArray(components)) {
|
|
2150
|
-
logger3.warn(
|
|
2204
|
+
logger3.warn(
|
|
2205
|
+
"Components is not an array, skipping component processing"
|
|
2206
|
+
);
|
|
2151
2207
|
} else if (components.length > 0 && components[0] && typeof components[0].toJSON === "function") {
|
|
2152
2208
|
options.components = components;
|
|
2153
2209
|
} else {
|
|
@@ -2177,7 +2233,9 @@ async function sendMessageInChunks(channel, content, _inReplyTo, files, componen
|
|
|
2177
2233
|
return new ButtonBuilder().setCustomId(comp.custom_id).setLabel(comp.label || "").setStyle(comp.style || 1);
|
|
2178
2234
|
}
|
|
2179
2235
|
if (comp.type === 3) {
|
|
2180
|
-
const selectMenu = new StringSelectMenuBuilder().setCustomId(comp.custom_id).setPlaceholder(
|
|
2236
|
+
const selectMenu = new StringSelectMenuBuilder().setCustomId(comp.custom_id).setPlaceholder(
|
|
2237
|
+
comp.placeholder || "Select an option"
|
|
2238
|
+
);
|
|
2181
2239
|
if (typeof comp.min_values === "number")
|
|
2182
2240
|
selectMenu.setMinValues(comp.min_values);
|
|
2183
2241
|
if (typeof comp.max_values === "number")
|
|
@@ -2284,7 +2342,9 @@ function canSendMessage(channel) {
|
|
|
2284
2342
|
reason: "Could not retrieve permissions"
|
|
2285
2343
|
};
|
|
2286
2344
|
}
|
|
2287
|
-
const missingPermissions = requiredPermissions.filter(
|
|
2345
|
+
const missingPermissions = requiredPermissions.filter(
|
|
2346
|
+
(perm) => !permissions.has(perm)
|
|
2347
|
+
);
|
|
2288
2348
|
return {
|
|
2289
2349
|
canSend: missingPermissions.length === 0,
|
|
2290
2350
|
missingPermissions,
|
|
@@ -2364,7 +2424,10 @@ var MessageManager = class {
|
|
|
2364
2424
|
try {
|
|
2365
2425
|
const canSendResult = canSendMessage(message.channel);
|
|
2366
2426
|
if (!canSendResult.canSend) {
|
|
2367
|
-
return logger4.warn(
|
|
2427
|
+
return logger4.warn(
|
|
2428
|
+
`Cannot send message to channel ${message.channel}`,
|
|
2429
|
+
canSendResult
|
|
2430
|
+
);
|
|
2368
2431
|
}
|
|
2369
2432
|
const { processedContent, attachments } = await this.processMessage(message);
|
|
2370
2433
|
const audioAttachments = message.attachments.filter(
|
|
@@ -2389,10 +2452,8 @@ var MessageManager = class {
|
|
|
2389
2452
|
logger4.warn("Error sending typing indicator:", err);
|
|
2390
2453
|
}
|
|
2391
2454
|
};
|
|
2392
|
-
startTyping();
|
|
2393
|
-
const typingInterval = setInterval(startTyping, 8e3);
|
|
2394
2455
|
const typingData = {
|
|
2395
|
-
interval:
|
|
2456
|
+
interval: null,
|
|
2396
2457
|
cleared: false
|
|
2397
2458
|
};
|
|
2398
2459
|
const newMessage = {
|
|
@@ -2406,62 +2467,75 @@ var MessageManager = class {
|
|
|
2406
2467
|
text: processedContent || " ",
|
|
2407
2468
|
attachments,
|
|
2408
2469
|
source: "discord",
|
|
2470
|
+
channelType: type,
|
|
2409
2471
|
url: message.url,
|
|
2410
2472
|
inReplyTo: message.reference?.messageId ? createUniqueUuid4(this.runtime, message.reference?.messageId) : void 0
|
|
2411
2473
|
},
|
|
2474
|
+
// metadata of memory
|
|
2412
2475
|
metadata: {
|
|
2413
2476
|
entityName: name,
|
|
2477
|
+
// include very technical/exact reference to this user for security reasons
|
|
2478
|
+
// don't remove or change this, spartan needs this
|
|
2479
|
+
fromId: message.author.id,
|
|
2480
|
+
// why message? all Memories contain content (which is basically a message)
|
|
2481
|
+
// what are the other types?
|
|
2414
2482
|
type: "message"
|
|
2415
2483
|
},
|
|
2416
2484
|
createdAt: message.createdTimestamp
|
|
2417
2485
|
};
|
|
2418
2486
|
const callback = async (content, files) => {
|
|
2419
2487
|
try {
|
|
2488
|
+
startTyping();
|
|
2489
|
+
const typingInterval = setInterval(startTyping, 8e3);
|
|
2490
|
+
typingData.interval = typingInterval;
|
|
2491
|
+
typingData.cleared = false;
|
|
2420
2492
|
if (message.id && !content.inReplyTo) {
|
|
2421
2493
|
content.inReplyTo = createUniqueUuid4(this.runtime, message.id);
|
|
2422
2494
|
}
|
|
2423
|
-
|
|
2424
|
-
|
|
2495
|
+
let messages = [];
|
|
2496
|
+
if (content?.source === "DM") {
|
|
2497
|
+
const u = await this.client.users.fetch(message.author.id);
|
|
2498
|
+
if (!u) {
|
|
2499
|
+
logger4.warn("Discord - User not found", message.author.id);
|
|
2500
|
+
return [];
|
|
2501
|
+
}
|
|
2502
|
+
u.send(content.text);
|
|
2503
|
+
messages = [content];
|
|
2504
|
+
} else {
|
|
2505
|
+
messages = await sendMessageInChunks(
|
|
2425
2506
|
channel,
|
|
2426
2507
|
content.text ?? "",
|
|
2427
2508
|
message.id,
|
|
2428
2509
|
files
|
|
2429
2510
|
);
|
|
2430
|
-
const memories = [];
|
|
2431
|
-
for (const m of messages) {
|
|
2432
|
-
const actions = content.actions;
|
|
2433
|
-
const memory = {
|
|
2434
|
-
id: createUniqueUuid4(this.runtime, m.id),
|
|
2435
|
-
entityId: this.runtime.agentId,
|
|
2436
|
-
agentId: this.runtime.agentId,
|
|
2437
|
-
content: {
|
|
2438
|
-
...content,
|
|
2439
|
-
actions,
|
|
2440
|
-
inReplyTo: messageId,
|
|
2441
|
-
url: m.url,
|
|
2442
|
-
channelType: type
|
|
2443
|
-
},
|
|
2444
|
-
roomId,
|
|
2445
|
-
createdAt: m.createdTimestamp
|
|
2446
|
-
};
|
|
2447
|
-
memories.push(memory);
|
|
2448
|
-
}
|
|
2449
|
-
for (const m of memories) {
|
|
2450
|
-
await this.runtime.createMemory(m, "messages");
|
|
2451
|
-
}
|
|
2452
|
-
if (typingData.interval && !typingData.cleared) {
|
|
2453
|
-
clearInterval(typingData.interval);
|
|
2454
|
-
typingData.cleared = true;
|
|
2455
|
-
}
|
|
2456
|
-
return memories;
|
|
2457
|
-
} catch (error) {
|
|
2458
|
-
console.error("Error sending message:", error);
|
|
2459
|
-
if (typingData.interval && !typingData.cleared) {
|
|
2460
|
-
clearInterval(typingData.interval);
|
|
2461
|
-
typingData.cleared = true;
|
|
2462
|
-
}
|
|
2463
|
-
return [];
|
|
2464
2511
|
}
|
|
2512
|
+
const memories = [];
|
|
2513
|
+
for (const m of messages) {
|
|
2514
|
+
const actions = content.actions;
|
|
2515
|
+
const memory = {
|
|
2516
|
+
id: createUniqueUuid4(this.runtime, m.id),
|
|
2517
|
+
entityId: this.runtime.agentId,
|
|
2518
|
+
agentId: this.runtime.agentId,
|
|
2519
|
+
content: {
|
|
2520
|
+
...content,
|
|
2521
|
+
actions,
|
|
2522
|
+
inReplyTo: messageId,
|
|
2523
|
+
url: m.url,
|
|
2524
|
+
channelType: type
|
|
2525
|
+
},
|
|
2526
|
+
roomId,
|
|
2527
|
+
createdAt: m.createdTimestamp
|
|
2528
|
+
};
|
|
2529
|
+
memories.push(memory);
|
|
2530
|
+
}
|
|
2531
|
+
for (const m of memories) {
|
|
2532
|
+
await this.runtime.createMemory(m, "messages");
|
|
2533
|
+
}
|
|
2534
|
+
if (typingData.interval && !typingData.cleared) {
|
|
2535
|
+
clearInterval(typingData.interval);
|
|
2536
|
+
typingData.cleared = true;
|
|
2537
|
+
}
|
|
2538
|
+
return memories;
|
|
2465
2539
|
} catch (error) {
|
|
2466
2540
|
console.error("Error handling message:", error);
|
|
2467
2541
|
if (typingData.interval && !typingData.cleared) {
|
|
@@ -2471,11 +2545,14 @@ var MessageManager = class {
|
|
|
2471
2545
|
return [];
|
|
2472
2546
|
}
|
|
2473
2547
|
};
|
|
2474
|
-
this.runtime.emitEvent(
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2548
|
+
this.runtime.emitEvent(
|
|
2549
|
+
["DISCORD_MESSAGE_RECEIVED" /* MESSAGE_RECEIVED */, EventType.MESSAGE_RECEIVED],
|
|
2550
|
+
{
|
|
2551
|
+
runtime: this.runtime,
|
|
2552
|
+
message: newMessage,
|
|
2553
|
+
callback
|
|
2554
|
+
}
|
|
2555
|
+
);
|
|
2479
2556
|
setTimeout(() => {
|
|
2480
2557
|
if (typingData.interval && !typingData.cleared) {
|
|
2481
2558
|
clearInterval(typingData.interval);
|
|
@@ -2497,13 +2574,16 @@ var MessageManager = class {
|
|
|
2497
2574
|
let processedContent = message.content;
|
|
2498
2575
|
let attachments = [];
|
|
2499
2576
|
const mentionRegex = /<@!?(\d+)>/g;
|
|
2500
|
-
processedContent = processedContent.replace(
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2577
|
+
processedContent = processedContent.replace(
|
|
2578
|
+
mentionRegex,
|
|
2579
|
+
(match2, entityId) => {
|
|
2580
|
+
const user = message.mentions.users.get(entityId);
|
|
2581
|
+
if (user) {
|
|
2582
|
+
return `${user.username} (@${entityId})`;
|
|
2583
|
+
}
|
|
2584
|
+
return match2;
|
|
2504
2585
|
}
|
|
2505
|
-
|
|
2506
|
-
});
|
|
2586
|
+
);
|
|
2507
2587
|
const codeBlockRegex = /```([\s\S]*?)```/g;
|
|
2508
2588
|
let match;
|
|
2509
2589
|
while (match = codeBlockRegex.exec(processedContent)) {
|
|
@@ -2520,10 +2600,15 @@ var MessageManager = class {
|
|
|
2520
2600
|
description,
|
|
2521
2601
|
text: codeBlock
|
|
2522
2602
|
});
|
|
2523
|
-
processedContent = processedContent.replace(
|
|
2603
|
+
processedContent = processedContent.replace(
|
|
2604
|
+
match[0],
|
|
2605
|
+
`Code Block (${attachmentId})`
|
|
2606
|
+
);
|
|
2524
2607
|
}
|
|
2525
2608
|
if (message.attachments.size > 0) {
|
|
2526
|
-
attachments = await this.attachmentManager.processAttachments(
|
|
2609
|
+
attachments = await this.attachmentManager.processAttachments(
|
|
2610
|
+
message.attachments
|
|
2611
|
+
);
|
|
2527
2612
|
}
|
|
2528
2613
|
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
|
2529
2614
|
const urls = processedContent.match(urlRegex) || [];
|
|
@@ -2540,15 +2625,14 @@ var MessageManager = class {
|
|
|
2540
2625
|
text: videoInfo.text
|
|
2541
2626
|
});
|
|
2542
2627
|
} else {
|
|
2543
|
-
const browserService = this.runtime.getService(
|
|
2628
|
+
const browserService = this.runtime.getService(
|
|
2629
|
+
ServiceType4.BROWSER
|
|
2630
|
+
);
|
|
2544
2631
|
if (!browserService) {
|
|
2545
2632
|
logger4.warn("Browser service not found");
|
|
2546
2633
|
continue;
|
|
2547
2634
|
}
|
|
2548
|
-
const { title, description: summary } = await browserService.getPageContent(
|
|
2549
|
-
url,
|
|
2550
|
-
this.runtime
|
|
2551
|
-
);
|
|
2635
|
+
const { title, description: summary } = await browserService.getPageContent(url, this.runtime);
|
|
2552
2636
|
attachments.push({
|
|
2553
2637
|
id: `webpage-${Date.now()}`,
|
|
2554
2638
|
url,
|
|
@@ -2663,7 +2747,10 @@ var AudioMonitor = class {
|
|
|
2663
2747
|
this.lastFlagged = this.buffers.length;
|
|
2664
2748
|
}
|
|
2665
2749
|
this.buffers.push(chunk);
|
|
2666
|
-
const currentSize = this.buffers.reduce(
|
|
2750
|
+
const currentSize = this.buffers.reduce(
|
|
2751
|
+
(acc, cur) => acc + cur.length,
|
|
2752
|
+
0
|
|
2753
|
+
);
|
|
2667
2754
|
while (currentSize > this.maxSize) {
|
|
2668
2755
|
this.buffers.shift();
|
|
2669
2756
|
this.lastFlagged--;
|
|
@@ -2830,7 +2917,10 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2830
2917
|
this.stopMonitoringMember(member.id);
|
|
2831
2918
|
}
|
|
2832
2919
|
if (newChannelId && this.connections.has(newChannelId)) {
|
|
2833
|
-
await this.monitorMember(
|
|
2920
|
+
await this.monitorMember(
|
|
2921
|
+
member,
|
|
2922
|
+
newState.channel
|
|
2923
|
+
);
|
|
2834
2924
|
}
|
|
2835
2925
|
}
|
|
2836
2926
|
/**
|
|
@@ -2861,9 +2951,13 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2861
2951
|
entersState(connection, VoiceConnectionStatus.Ready, 2e4),
|
|
2862
2952
|
entersState(connection, VoiceConnectionStatus.Signalling, 2e4)
|
|
2863
2953
|
]);
|
|
2864
|
-
logger5.log(
|
|
2954
|
+
logger5.log(
|
|
2955
|
+
`Voice connection established in state: ${connection.state.status}`
|
|
2956
|
+
);
|
|
2865
2957
|
connection.on("stateChange", async (oldState, newState) => {
|
|
2866
|
-
logger5.log(
|
|
2958
|
+
logger5.log(
|
|
2959
|
+
`Voice connection state changed from ${oldState.status} to ${newState.status}`
|
|
2960
|
+
);
|
|
2867
2961
|
if (newState.status === VoiceConnectionStatus.Disconnected) {
|
|
2868
2962
|
logger5.log("Handling disconnection...");
|
|
2869
2963
|
try {
|
|
@@ -2960,7 +3054,9 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2960
3054
|
emitClose: true
|
|
2961
3055
|
});
|
|
2962
3056
|
if (!receiveStream || receiveStream.readableLength === 0) {
|
|
2963
|
-
logger5.warn(
|
|
3057
|
+
logger5.warn(
|
|
3058
|
+
`[monitorMember] No receiveStream or empty stream for user ${entityId}`
|
|
3059
|
+
);
|
|
2964
3060
|
return;
|
|
2965
3061
|
}
|
|
2966
3062
|
let opusDecoder;
|
|
@@ -2971,7 +3067,9 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2971
3067
|
frameSize: DECODE_FRAME_SIZE
|
|
2972
3068
|
});
|
|
2973
3069
|
} catch (error) {
|
|
2974
|
-
logger5.error(
|
|
3070
|
+
logger5.error(
|
|
3071
|
+
`[monitorMember] Failed to create opus decoder for user ${entityId}: ${error}`
|
|
3072
|
+
);
|
|
2975
3073
|
return;
|
|
2976
3074
|
}
|
|
2977
3075
|
const volumeBuffer = [];
|
|
@@ -2979,7 +3077,11 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2979
3077
|
const SPEAKING_THRESHOLD = 0.05;
|
|
2980
3078
|
opusDecoder.on("data", (pcmData) => {
|
|
2981
3079
|
if (this.activeAudioPlayer) {
|
|
2982
|
-
const samples = new Int16Array(
|
|
3080
|
+
const samples = new Int16Array(
|
|
3081
|
+
pcmData.buffer,
|
|
3082
|
+
pcmData.byteOffset,
|
|
3083
|
+
pcmData.length / 2
|
|
3084
|
+
);
|
|
2983
3085
|
const maxAmplitude = Math.max(...samples.map(Math.abs)) / 32768;
|
|
2984
3086
|
volumeBuffer.push(maxAmplitude);
|
|
2985
3087
|
if (volumeBuffer.length > VOLUME_WINDOW_SIZE) {
|
|
@@ -2993,15 +3095,21 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2993
3095
|
}
|
|
2994
3096
|
}
|
|
2995
3097
|
});
|
|
2996
|
-
pipeline(
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3098
|
+
pipeline(
|
|
3099
|
+
receiveStream,
|
|
3100
|
+
opusDecoder,
|
|
3101
|
+
(err) => {
|
|
3102
|
+
if (err) {
|
|
3103
|
+
logger5.debug(
|
|
3104
|
+
`[monitorMember] Opus decoding pipeline error for user ${entityId}: ${err}`
|
|
3105
|
+
);
|
|
3106
|
+
} else {
|
|
3107
|
+
logger5.debug(
|
|
3108
|
+
`[monitorMember] Opus decoding pipeline finished successfully for user ${entityId}`
|
|
3109
|
+
);
|
|
3110
|
+
}
|
|
3003
3111
|
}
|
|
3004
|
-
|
|
3112
|
+
);
|
|
3005
3113
|
this.streams.set(entityId, opusDecoder);
|
|
3006
3114
|
this.connections.set(entityId, connection);
|
|
3007
3115
|
opusDecoder.on("error", (err) => {
|
|
@@ -3024,7 +3132,14 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3024
3132
|
opusDecoder.on("error", errorHandler);
|
|
3025
3133
|
opusDecoder.on("close", closeHandler);
|
|
3026
3134
|
receiveStream?.on("close", streamCloseHandler);
|
|
3027
|
-
this.client?.emit(
|
|
3135
|
+
this.client?.emit(
|
|
3136
|
+
"userStream",
|
|
3137
|
+
entityId,
|
|
3138
|
+
name,
|
|
3139
|
+
userName,
|
|
3140
|
+
channel,
|
|
3141
|
+
opusDecoder
|
|
3142
|
+
);
|
|
3028
3143
|
}
|
|
3029
3144
|
/**
|
|
3030
3145
|
* Leaves the specified voice channel and stops monitoring all members in that channel.
|
|
@@ -3086,7 +3201,13 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3086
3201
|
this.transcriptionTimeout = setTimeout(async () => {
|
|
3087
3202
|
this.processingVoice = true;
|
|
3088
3203
|
try {
|
|
3089
|
-
await this.processTranscription(
|
|
3204
|
+
await this.processTranscription(
|
|
3205
|
+
entityId,
|
|
3206
|
+
channel.id,
|
|
3207
|
+
channel,
|
|
3208
|
+
name,
|
|
3209
|
+
userName
|
|
3210
|
+
);
|
|
3090
3211
|
this.userStates.forEach((state, _) => {
|
|
3091
3212
|
state.buffers.length = 0;
|
|
3092
3213
|
state.totalLength = 0;
|
|
@@ -3167,7 +3288,10 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3167
3288
|
state.totalLength = 0;
|
|
3168
3289
|
const wavBuffer = await this.convertOpusToWav(inputBuffer);
|
|
3169
3290
|
logger5.debug("Starting transcription...");
|
|
3170
|
-
const transcriptionText = await this.runtime.useModel(
|
|
3291
|
+
const transcriptionText = await this.runtime.useModel(
|
|
3292
|
+
ModelType8.TRANSCRIPTION,
|
|
3293
|
+
wavBuffer
|
|
3294
|
+
);
|
|
3171
3295
|
if (transcriptionText && isValidTranscription2(transcriptionText)) {
|
|
3172
3296
|
state.transcriptionText += transcriptionText;
|
|
3173
3297
|
}
|
|
@@ -3175,7 +3299,14 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3175
3299
|
this.cleanupAudioPlayer(this.activeAudioPlayer);
|
|
3176
3300
|
const finalText = state.transcriptionText;
|
|
3177
3301
|
state.transcriptionText = "";
|
|
3178
|
-
await this.handleMessage(
|
|
3302
|
+
await this.handleMessage(
|
|
3303
|
+
finalText,
|
|
3304
|
+
entityId,
|
|
3305
|
+
channelId,
|
|
3306
|
+
channel,
|
|
3307
|
+
name,
|
|
3308
|
+
userName
|
|
3309
|
+
);
|
|
3179
3310
|
}
|
|
3180
3311
|
} catch (error) {
|
|
3181
3312
|
console.error(`Error transcribing audio for user ${entityId}:`, error);
|
|
@@ -3213,7 +3344,10 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3213
3344
|
worldName: channel.guild.name
|
|
3214
3345
|
});
|
|
3215
3346
|
const memory = {
|
|
3216
|
-
id: createUniqueUuid5(
|
|
3347
|
+
id: createUniqueUuid5(
|
|
3348
|
+
this.runtime,
|
|
3349
|
+
`${channelId}-voice-message-${Date.now()}`
|
|
3350
|
+
),
|
|
3217
3351
|
agentId: this.runtime.agentId,
|
|
3218
3352
|
entityId: uniqueEntityId,
|
|
3219
3353
|
roomId,
|
|
@@ -3231,7 +3365,10 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3231
3365
|
const callback = async (content, _files = []) => {
|
|
3232
3366
|
try {
|
|
3233
3367
|
const responseMemory = {
|
|
3234
|
-
id: createUniqueUuid5(
|
|
3368
|
+
id: createUniqueUuid5(
|
|
3369
|
+
this.runtime,
|
|
3370
|
+
`${memory.id}-voice-response-${Date.now()}`
|
|
3371
|
+
),
|
|
3235
3372
|
entityId: this.runtime.agentId,
|
|
3236
3373
|
agentId: this.runtime.agentId,
|
|
3237
3374
|
content: {
|
|
@@ -3260,11 +3397,14 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3260
3397
|
return [];
|
|
3261
3398
|
}
|
|
3262
3399
|
};
|
|
3263
|
-
this.runtime.emitEvent(
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3400
|
+
this.runtime.emitEvent(
|
|
3401
|
+
["DISCORD_VOICE_MESSAGE_RECEIVED", "VOICE_MESSAGE_RECEIVED"],
|
|
3402
|
+
{
|
|
3403
|
+
runtime: this.runtime,
|
|
3404
|
+
message: memory,
|
|
3405
|
+
callback
|
|
3406
|
+
}
|
|
3407
|
+
);
|
|
3268
3408
|
} catch (error) {
|
|
3269
3409
|
console.error("Error processing voice message:", error);
|
|
3270
3410
|
}
|
|
@@ -3293,7 +3433,9 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3293
3433
|
async scanGuild(guild) {
|
|
3294
3434
|
let chosenChannel = null;
|
|
3295
3435
|
try {
|
|
3296
|
-
const channelId = this.runtime.getSetting(
|
|
3436
|
+
const channelId = this.runtime.getSetting(
|
|
3437
|
+
"DISCORD_VOICE_CHANNEL_ID"
|
|
3438
|
+
);
|
|
3297
3439
|
if (channelId) {
|
|
3298
3440
|
const channel = await guild.channels.fetch(channelId);
|
|
3299
3441
|
if (channel?.isVoiceBased()) {
|
|
@@ -3350,12 +3492,15 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3350
3492
|
audioPlayer.on("error", (err) => {
|
|
3351
3493
|
logger5.debug(`Audio player error: ${err}`);
|
|
3352
3494
|
});
|
|
3353
|
-
audioPlayer.on(
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3495
|
+
audioPlayer.on(
|
|
3496
|
+
"stateChange",
|
|
3497
|
+
(_oldState, newState) => {
|
|
3498
|
+
if (newState.status === "idle") {
|
|
3499
|
+
const idleTime = Date.now();
|
|
3500
|
+
logger5.debug(`Audio playback took: ${idleTime - audioStartTime}ms`);
|
|
3501
|
+
}
|
|
3357
3502
|
}
|
|
3358
|
-
|
|
3503
|
+
);
|
|
3359
3504
|
}
|
|
3360
3505
|
/**
|
|
3361
3506
|
* Cleans up the provided audio player by stopping it, removing all listeners,
|
|
@@ -3457,7 +3602,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3457
3602
|
}
|
|
3458
3603
|
const token = runtime.getSetting("DISCORD_API_TOKEN");
|
|
3459
3604
|
if (!token || token.trim() === "") {
|
|
3460
|
-
logger6.warn(
|
|
3605
|
+
logger6.warn(
|
|
3606
|
+
"Discord API Token not provided - Discord functionality will be unavailable"
|
|
3607
|
+
);
|
|
3461
3608
|
this.client = null;
|
|
3462
3609
|
return;
|
|
3463
3610
|
}
|
|
@@ -3475,7 +3622,12 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3475
3622
|
GatewayIntentBits.GuildMessageTyping,
|
|
3476
3623
|
GatewayIntentBits.GuildMessageReactions
|
|
3477
3624
|
],
|
|
3478
|
-
partials: [
|
|
3625
|
+
partials: [
|
|
3626
|
+
Partials.Channel,
|
|
3627
|
+
Partials.Message,
|
|
3628
|
+
Partials.User,
|
|
3629
|
+
Partials.Reaction
|
|
3630
|
+
]
|
|
3479
3631
|
});
|
|
3480
3632
|
this.runtime = runtime;
|
|
3481
3633
|
this.voiceManager = new VoiceManager(this, runtime);
|
|
@@ -3506,7 +3658,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3506
3658
|
*/
|
|
3507
3659
|
registerSendHandler() {
|
|
3508
3660
|
if (this.runtime) {
|
|
3509
|
-
this.runtime.registerSendHandler(
|
|
3661
|
+
this.runtime.registerSendHandler(
|
|
3662
|
+
"discord",
|
|
3663
|
+
this.handleSendMessage.bind(this)
|
|
3664
|
+
);
|
|
3510
3665
|
}
|
|
3511
3666
|
}
|
|
3512
3667
|
/**
|
|
@@ -3554,10 +3709,14 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3554
3709
|
await targetChannel.send(chunk);
|
|
3555
3710
|
}
|
|
3556
3711
|
} else {
|
|
3557
|
-
logger6.warn(
|
|
3712
|
+
logger6.warn(
|
|
3713
|
+
"[Discord SendHandler] No text content provided to send."
|
|
3714
|
+
);
|
|
3558
3715
|
}
|
|
3559
3716
|
} else {
|
|
3560
|
-
throw new Error(
|
|
3717
|
+
throw new Error(
|
|
3718
|
+
`Target channel ${targetChannel.id} does not have a send method.`
|
|
3719
|
+
);
|
|
3561
3720
|
}
|
|
3562
3721
|
} else {
|
|
3563
3722
|
throw new Error(
|
|
@@ -3614,12 +3773,32 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3614
3773
|
if (!this.client) {
|
|
3615
3774
|
return;
|
|
3616
3775
|
}
|
|
3617
|
-
this.client.on("messageCreate", (message) => {
|
|
3618
|
-
if (message.author.id === this.client?.user?.id || message.author.bot) {
|
|
3776
|
+
this.client.on("messageCreate", async (message) => {
|
|
3777
|
+
if (message.author.id === this.client?.user?.id || message.author.bot && this.runtime.character.settings?.discord?.shouldIgnoreBotMessages) {
|
|
3778
|
+
logger6.info(
|
|
3779
|
+
`Got message where author is ${message.author.bot && this.runtime.character.settings?.discord?.shouldIgnoreBotMessages ? "a bot. To reply anyway, set `shouldIgnoreBotMessages=true`." : "the current user. Ignore!"}`
|
|
3780
|
+
);
|
|
3619
3781
|
return;
|
|
3620
3782
|
}
|
|
3621
3783
|
if (this.allowedChannelIds && !this.allowedChannelIds.includes(message.channel.id)) {
|
|
3622
|
-
|
|
3784
|
+
const channel = await this.client?.channels.fetch(message.channel.id);
|
|
3785
|
+
if (!channel) {
|
|
3786
|
+
logger6.error(`Channel id ${message.channel.id} not found. Ignore!`);
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3789
|
+
if (channel.isThread()) {
|
|
3790
|
+
if (!channel.parentId || !this.allowedChannelIds.includes(channel.parentId)) {
|
|
3791
|
+
logger6.info(
|
|
3792
|
+
`Thread not in an allowed channel. Add the channel ${channel.parentId} to CHANNEL_IDS to enable replies.`
|
|
3793
|
+
);
|
|
3794
|
+
return;
|
|
3795
|
+
}
|
|
3796
|
+
} else {
|
|
3797
|
+
logger6.info(
|
|
3798
|
+
`Channel not allowed. Add the channel ${message.channel.id} to CHANNEL_IDS to enable replies.`
|
|
3799
|
+
);
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3623
3802
|
}
|
|
3624
3803
|
try {
|
|
3625
3804
|
this.messageManager?.handleMessage(message);
|
|
@@ -3677,11 +3856,20 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3677
3856
|
logger6.error(`Error handling interaction: ${error}`);
|
|
3678
3857
|
}
|
|
3679
3858
|
});
|
|
3680
|
-
this.client.on(
|
|
3681
|
-
|
|
3682
|
-
|
|
3859
|
+
this.client.on(
|
|
3860
|
+
"userStream",
|
|
3861
|
+
(entityId, name, userName, channel, opusDecoder) => {
|
|
3862
|
+
if (entityId !== this.client?.user?.id) {
|
|
3863
|
+
this.voiceManager?.handleUserStream(
|
|
3864
|
+
entityId,
|
|
3865
|
+
name,
|
|
3866
|
+
userName,
|
|
3867
|
+
channel,
|
|
3868
|
+
opusDecoder
|
|
3869
|
+
);
|
|
3870
|
+
}
|
|
3683
3871
|
}
|
|
3684
|
-
|
|
3872
|
+
);
|
|
3685
3873
|
}
|
|
3686
3874
|
/**
|
|
3687
3875
|
* Handles the event when a new member joins a guild.
|
|
@@ -3780,7 +3968,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3780
3968
|
}
|
|
3781
3969
|
const userSelections = this.userSelections.get(userId);
|
|
3782
3970
|
if (!userSelections) {
|
|
3783
|
-
logger6.error(
|
|
3971
|
+
logger6.error(
|
|
3972
|
+
`User selections map unexpectedly missing for user ${userId}`
|
|
3973
|
+
);
|
|
3784
3974
|
return;
|
|
3785
3975
|
}
|
|
3786
3976
|
try {
|
|
@@ -3800,9 +3990,13 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3800
3990
|
}
|
|
3801
3991
|
if (interaction.isButton()) {
|
|
3802
3992
|
logger6.info("Button interaction detected");
|
|
3803
|
-
logger6.info(
|
|
3993
|
+
logger6.info(
|
|
3994
|
+
`Button pressed by user ${userId}: ${interaction.customId}`
|
|
3995
|
+
);
|
|
3804
3996
|
const formSelections = userSelections[messageId] || {};
|
|
3805
|
-
logger6.info(
|
|
3997
|
+
logger6.info(
|
|
3998
|
+
`Form data being submitted: ${JSON.stringify(formSelections)}`
|
|
3999
|
+
);
|
|
3806
4000
|
this.runtime.emitEvent(["DISCORD_INTERACTION"], {
|
|
3807
4001
|
interaction: {
|
|
3808
4002
|
customId: interaction.customId,
|
|
@@ -3866,7 +4060,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3866
4060
|
(member) => channel.permissionsFor(member)?.has(PermissionsBitField2.Flags.ViewChannel)
|
|
3867
4061
|
).map((member) => createUniqueUuid6(this.runtime, member.id));
|
|
3868
4062
|
} catch (error) {
|
|
3869
|
-
logger6.warn(
|
|
4063
|
+
logger6.warn(
|
|
4064
|
+
`Failed to get participants for channel ${channel.name}:`,
|
|
4065
|
+
error
|
|
4066
|
+
);
|
|
3870
4067
|
}
|
|
3871
4068
|
}
|
|
3872
4069
|
rooms.push({
|
|
@@ -3903,9 +4100,11 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3903
4100
|
id: createUniqueUuid6(this.runtime, member.id),
|
|
3904
4101
|
names: Array.from(
|
|
3905
4102
|
new Set(
|
|
3906
|
-
[
|
|
3907
|
-
|
|
3908
|
-
|
|
4103
|
+
[
|
|
4104
|
+
member.user.username,
|
|
4105
|
+
member.displayName,
|
|
4106
|
+
member.user.globalName
|
|
4107
|
+
].filter(Boolean)
|
|
3909
4108
|
)
|
|
3910
4109
|
),
|
|
3911
4110
|
agentId: this.runtime.agentId,
|
|
@@ -3940,9 +4139,11 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3940
4139
|
id: entityId,
|
|
3941
4140
|
names: Array.from(
|
|
3942
4141
|
new Set(
|
|
3943
|
-
[
|
|
3944
|
-
|
|
3945
|
-
|
|
4142
|
+
[
|
|
4143
|
+
member.user.username,
|
|
4144
|
+
member.displayName,
|
|
4145
|
+
member.user.globalName
|
|
4146
|
+
].filter(Boolean)
|
|
3946
4147
|
)
|
|
3947
4148
|
),
|
|
3948
4149
|
agentId: this.runtime.agentId,
|
|
@@ -3983,9 +4184,11 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3983
4184
|
id: createUniqueUuid6(this.runtime, member.id),
|
|
3984
4185
|
names: Array.from(
|
|
3985
4186
|
new Set(
|
|
3986
|
-
[
|
|
3987
|
-
|
|
3988
|
-
|
|
4187
|
+
[
|
|
4188
|
+
member.user.username,
|
|
4189
|
+
member.displayName,
|
|
4190
|
+
member.user.globalName
|
|
4191
|
+
].filter(Boolean)
|
|
3989
4192
|
)
|
|
3990
4193
|
),
|
|
3991
4194
|
agentId: this.runtime.agentId,
|
|
@@ -4093,9 +4296,13 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4093
4296
|
* @returns {Promise<Array<{id: string, username: string, displayName: string}>>} A promise that resolves with an array of channel member objects, each containing id, username, and displayName.
|
|
4094
4297
|
*/
|
|
4095
4298
|
async getTextChannelMembers(channelId, useCache = true) {
|
|
4096
|
-
logger6.info(
|
|
4299
|
+
logger6.info(
|
|
4300
|
+
`Fetching members for text channel ${channelId}, useCache=${useCache}`
|
|
4301
|
+
);
|
|
4097
4302
|
try {
|
|
4098
|
-
const channel = await this.client?.channels.fetch(
|
|
4303
|
+
const channel = await this.client?.channels.fetch(
|
|
4304
|
+
channelId
|
|
4305
|
+
);
|
|
4099
4306
|
if (!channel) {
|
|
4100
4307
|
logger6.error(`Channel not found: ${channelId}`);
|
|
4101
4308
|
return [];
|
|
@@ -4119,7 +4326,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4119
4326
|
} else {
|
|
4120
4327
|
try {
|
|
4121
4328
|
if (useCache && guild.members.cache.size > 0) {
|
|
4122
|
-
logger6.info(
|
|
4329
|
+
logger6.info(
|
|
4330
|
+
`Using cached members (${guild.members.cache.size} members)`
|
|
4331
|
+
);
|
|
4123
4332
|
members = guild.members.cache;
|
|
4124
4333
|
} else {
|
|
4125
4334
|
logger6.info(`Fetching members for guild ${guild.name}`);
|
|
@@ -4144,7 +4353,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4144
4353
|
username: member.user.username,
|
|
4145
4354
|
displayName: member.displayName || member.user.username
|
|
4146
4355
|
}));
|
|
4147
|
-
logger6.info(
|
|
4356
|
+
logger6.info(
|
|
4357
|
+
`Found ${channelMembers.length} members with access to channel ${channel.name}`
|
|
4358
|
+
);
|
|
4148
4359
|
return channelMembers;
|
|
4149
4360
|
} catch (error) {
|
|
4150
4361
|
logger6.error(`Error fetching channel members: ${error}`);
|
|
@@ -4175,7 +4386,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4175
4386
|
}
|
|
4176
4387
|
}
|
|
4177
4388
|
const timestamp = Date.now();
|
|
4178
|
-
const roomId = createUniqueUuid6(
|
|
4389
|
+
const roomId = createUniqueUuid6(
|
|
4390
|
+
this.runtime,
|
|
4391
|
+
reaction.message.channel.id
|
|
4392
|
+
);
|
|
4179
4393
|
const entityId = createUniqueUuid6(this.runtime, user.id);
|
|
4180
4394
|
const reactionUUID = createUniqueUuid6(
|
|
4181
4395
|
this.runtime,
|
|
@@ -4197,7 +4411,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4197
4411
|
entityId,
|
|
4198
4412
|
roomId,
|
|
4199
4413
|
userName,
|
|
4200
|
-
worldId: createUniqueUuid6(
|
|
4414
|
+
worldId: createUniqueUuid6(
|
|
4415
|
+
this.runtime,
|
|
4416
|
+
reaction.message.guild?.id ?? roomId
|
|
4417
|
+
),
|
|
4201
4418
|
worldName: reaction.message.guild?.name,
|
|
4202
4419
|
name,
|
|
4203
4420
|
source: "discord",
|
|
@@ -4216,7 +4433,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4216
4433
|
text: reactionMessage,
|
|
4217
4434
|
source: "discord",
|
|
4218
4435
|
inReplyTo,
|
|
4219
|
-
channelType: await this.getChannelType(
|
|
4436
|
+
channelType: await this.getChannelType(
|
|
4437
|
+
reaction.message.channel
|
|
4438
|
+
)
|
|
4220
4439
|
},
|
|
4221
4440
|
roomId,
|
|
4222
4441
|
createdAt: timestamp
|
|
@@ -4226,14 +4445,19 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4226
4445
|
logger6.error("No channel found for reaction message");
|
|
4227
4446
|
return [];
|
|
4228
4447
|
}
|
|
4229
|
-
await reaction.message.channel.send(
|
|
4448
|
+
await reaction.message.channel.send(
|
|
4449
|
+
content.text ?? ""
|
|
4450
|
+
);
|
|
4230
4451
|
return [];
|
|
4231
4452
|
};
|
|
4232
|
-
this.runtime.emitEvent(
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4453
|
+
this.runtime.emitEvent(
|
|
4454
|
+
["DISCORD_REACTION_RECEIVED", "REACTION_RECEIVED"],
|
|
4455
|
+
{
|
|
4456
|
+
runtime: this.runtime,
|
|
4457
|
+
message: memory,
|
|
4458
|
+
callback
|
|
4459
|
+
}
|
|
4460
|
+
);
|
|
4237
4461
|
} catch (error) {
|
|
4238
4462
|
logger6.error("Error handling reaction:", error);
|
|
4239
4463
|
}
|
|
@@ -4253,14 +4477,20 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4253
4477
|
try {
|
|
4254
4478
|
await reaction.fetch();
|
|
4255
4479
|
} catch (error) {
|
|
4256
|
-
logger6.error(
|
|
4480
|
+
logger6.error(
|
|
4481
|
+
"Something went wrong when fetching the message:",
|
|
4482
|
+
error
|
|
4483
|
+
);
|
|
4257
4484
|
return;
|
|
4258
4485
|
}
|
|
4259
4486
|
}
|
|
4260
4487
|
const messageContent = reaction.message.content || "";
|
|
4261
4488
|
const truncatedContent = messageContent.length > 50 ? `${messageContent.substring(0, 50)}...` : messageContent;
|
|
4262
4489
|
const reactionMessage = `*Removed <${emoji}> from: \\"${truncatedContent}\\"*`;
|
|
4263
|
-
const roomId = createUniqueUuid6(
|
|
4490
|
+
const roomId = createUniqueUuid6(
|
|
4491
|
+
this.runtime,
|
|
4492
|
+
reaction.message.channel.id
|
|
4493
|
+
);
|
|
4264
4494
|
const entityId = createUniqueUuid6(this.runtime, user.id);
|
|
4265
4495
|
const timestamp = Date.now();
|
|
4266
4496
|
const reactionUUID = createUniqueUuid6(
|
|
@@ -4273,7 +4503,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4273
4503
|
entityId,
|
|
4274
4504
|
roomId,
|
|
4275
4505
|
userName,
|
|
4276
|
-
worldId: createUniqueUuid6(
|
|
4506
|
+
worldId: createUniqueUuid6(
|
|
4507
|
+
this.runtime,
|
|
4508
|
+
reaction.message.guild?.id ?? roomId
|
|
4509
|
+
),
|
|
4277
4510
|
worldName: reaction.message.guild?.name,
|
|
4278
4511
|
name,
|
|
4279
4512
|
source: "discord",
|
|
@@ -4291,7 +4524,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4291
4524
|
text: reactionMessage,
|
|
4292
4525
|
source: "discord",
|
|
4293
4526
|
inReplyTo: createUniqueUuid6(this.runtime, reaction.message.id),
|
|
4294
|
-
channelType: await this.getChannelType(
|
|
4527
|
+
channelType: await this.getChannelType(
|
|
4528
|
+
reaction.message.channel
|
|
4529
|
+
)
|
|
4295
4530
|
},
|
|
4296
4531
|
roomId,
|
|
4297
4532
|
createdAt: Date.now()
|
|
@@ -4301,7 +4536,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4301
4536
|
logger6.error("No channel found for reaction message");
|
|
4302
4537
|
return [];
|
|
4303
4538
|
}
|
|
4304
|
-
await reaction.message.channel.send(
|
|
4539
|
+
await reaction.message.channel.send(
|
|
4540
|
+
content.text ?? ""
|
|
4541
|
+
);
|
|
4305
4542
|
return [];
|
|
4306
4543
|
};
|
|
4307
4544
|
this.runtime.emitEvent(["DISCORD_REACTION_RECEIVED" /* REACTION_RECEIVED */], {
|
|
@@ -4360,8 +4597,15 @@ import {
|
|
|
4360
4597
|
createAudioResource as createAudioResource2,
|
|
4361
4598
|
entersState as entersState2
|
|
4362
4599
|
} from "@discordjs/voice";
|
|
4363
|
-
import {
|
|
4364
|
-
|
|
4600
|
+
import {
|
|
4601
|
+
ModelType as ModelType9,
|
|
4602
|
+
logger as logger7
|
|
4603
|
+
} from "@elizaos/core";
|
|
4604
|
+
import {
|
|
4605
|
+
ChannelType as ChannelType10,
|
|
4606
|
+
Events as Events2,
|
|
4607
|
+
AttachmentBuilder
|
|
4608
|
+
} from "discord.js";
|
|
4365
4609
|
var TEST_IMAGE_URL = "https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true";
|
|
4366
4610
|
var DiscordTestSuite = class {
|
|
4367
4611
|
name = "discord";
|
|
@@ -4411,7 +4655,9 @@ var DiscordTestSuite = class {
|
|
|
4411
4655
|
*/
|
|
4412
4656
|
async testCreatingDiscordClient(runtime) {
|
|
4413
4657
|
try {
|
|
4414
|
-
this.discordClient = runtime.getService(
|
|
4658
|
+
this.discordClient = runtime.getService(
|
|
4659
|
+
ServiceType2.DISCORD
|
|
4660
|
+
);
|
|
4415
4661
|
if (!this.discordClient) {
|
|
4416
4662
|
throw new Error("Failed to get DiscordService from runtime.");
|
|
4417
4663
|
}
|
|
@@ -4420,7 +4666,9 @@ var DiscordTestSuite = class {
|
|
|
4420
4666
|
} else {
|
|
4421
4667
|
logger7.info("Waiting for DiscordService to be ready...");
|
|
4422
4668
|
if (!this.discordClient.client) {
|
|
4423
|
-
throw new Error(
|
|
4669
|
+
throw new Error(
|
|
4670
|
+
"Discord client instance is missing within the service."
|
|
4671
|
+
);
|
|
4424
4672
|
}
|
|
4425
4673
|
await new Promise((resolve, reject) => {
|
|
4426
4674
|
this.discordClient.client?.once(Events2.ClientReady, resolve);
|
|
@@ -4462,7 +4710,9 @@ var DiscordTestSuite = class {
|
|
|
4462
4710
|
if (!this.discordClient.voiceManager) {
|
|
4463
4711
|
throw new Error("VoiceManager is not available on the Discord client.");
|
|
4464
4712
|
}
|
|
4465
|
-
await this.discordClient.voiceManager.handleJoinChannelCommand(
|
|
4713
|
+
await this.discordClient.voiceManager.handleJoinChannelCommand(
|
|
4714
|
+
fakeJoinInteraction
|
|
4715
|
+
);
|
|
4466
4716
|
logger7.success("Join voice slash command test completed successfully.");
|
|
4467
4717
|
} catch (error) {
|
|
4468
4718
|
throw new Error(`Error in join voice slash commands test: ${error}`);
|
|
@@ -4493,7 +4743,9 @@ var DiscordTestSuite = class {
|
|
|
4493
4743
|
if (!this.discordClient.voiceManager) {
|
|
4494
4744
|
throw new Error("VoiceManager is not available on the Discord client.");
|
|
4495
4745
|
}
|
|
4496
|
-
await this.discordClient.voiceManager.handleLeaveChannelCommand(
|
|
4746
|
+
await this.discordClient.voiceManager.handleLeaveChannelCommand(
|
|
4747
|
+
fakeLeaveInteraction
|
|
4748
|
+
);
|
|
4497
4749
|
logger7.success("Leave voice slash command test completed successfully.");
|
|
4498
4750
|
} catch (error) {
|
|
4499
4751
|
throw new Error(`Error in leave voice slash commands test: ${error}`);
|
|
@@ -4563,7 +4815,11 @@ var DiscordTestSuite = class {
|
|
|
4563
4815
|
throw new Error("Cannot send message to a non-text channel.");
|
|
4564
4816
|
}
|
|
4565
4817
|
const attachment = new AttachmentBuilder(TEST_IMAGE_URL);
|
|
4566
|
-
await this.sendMessageToChannel(
|
|
4818
|
+
await this.sendMessageToChannel(
|
|
4819
|
+
channel,
|
|
4820
|
+
"Testing Message",
|
|
4821
|
+
[attachment]
|
|
4822
|
+
);
|
|
4567
4823
|
} catch (error) {
|
|
4568
4824
|
throw new Error(`Error in sending text message: ${error}`);
|
|
4569
4825
|
}
|
|
@@ -4595,7 +4851,9 @@ var DiscordTestSuite = class {
|
|
|
4595
4851
|
attachments: []
|
|
4596
4852
|
};
|
|
4597
4853
|
if (!this.discordClient.messageManager) {
|
|
4598
|
-
throw new Error(
|
|
4854
|
+
throw new Error(
|
|
4855
|
+
"MessageManager is not available on the Discord client."
|
|
4856
|
+
);
|
|
4599
4857
|
}
|
|
4600
4858
|
await this.discordClient.messageManager.handleMessage(fakeMessage);
|
|
4601
4859
|
} catch (error) {
|
|
@@ -4631,9 +4889,16 @@ var DiscordTestSuite = class {
|
|
|
4631
4889
|
async sendMessageToChannel(channel, messageContent, files) {
|
|
4632
4890
|
try {
|
|
4633
4891
|
if (!channel || !channel.isTextBased()) {
|
|
4634
|
-
throw new Error(
|
|
4892
|
+
throw new Error(
|
|
4893
|
+
"Channel is not a text-based channel or does not exist."
|
|
4894
|
+
);
|
|
4635
4895
|
}
|
|
4636
|
-
await sendMessageInChunks(
|
|
4896
|
+
await sendMessageInChunks(
|
|
4897
|
+
channel,
|
|
4898
|
+
messageContent,
|
|
4899
|
+
"",
|
|
4900
|
+
files
|
|
4901
|
+
);
|
|
4637
4902
|
} catch (error) {
|
|
4638
4903
|
throw new Error(`Error sending message: ${error}`);
|
|
4639
4904
|
}
|
|
@@ -4729,7 +4994,14 @@ var discordPlugin = {
|
|
|
4729
4994
|
name: "discord",
|
|
4730
4995
|
description: "Discord service plugin for integration with Discord servers and channels",
|
|
4731
4996
|
services: [DiscordService],
|
|
4732
|
-
actions: [
|
|
4997
|
+
actions: [
|
|
4998
|
+
chatWithAttachments_default,
|
|
4999
|
+
downloadMedia,
|
|
5000
|
+
joinVoice,
|
|
5001
|
+
leaveVoice,
|
|
5002
|
+
summarize,
|
|
5003
|
+
transcribeMedia
|
|
5004
|
+
],
|
|
4733
5005
|
providers: [channelStateProvider, voiceStateProvider],
|
|
4734
5006
|
tests: [new DiscordTestSuite()],
|
|
4735
5007
|
init: async (config, runtime) => {
|