@elizaos/plugin-discord 1.0.3 → 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 +486 -177
- package/dist/index.js.map +1 -1
- package/package.json +6 -8
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,
|
|
@@ -2586,8 +2670,6 @@ var MessageManager = class {
|
|
|
2586
2670
|
};
|
|
2587
2671
|
|
|
2588
2672
|
// src/voice.ts
|
|
2589
|
-
import { EventEmitter } from "node:events";
|
|
2590
|
-
import { pipeline } from "node:stream";
|
|
2591
2673
|
import {
|
|
2592
2674
|
NoSubscriberBehavior,
|
|
2593
2675
|
StreamType,
|
|
@@ -2602,15 +2684,48 @@ import {
|
|
|
2602
2684
|
ChannelType as ChannelType8,
|
|
2603
2685
|
ModelType as ModelType8,
|
|
2604
2686
|
createUniqueUuid as createUniqueUuid5,
|
|
2605
|
-
getWavHeader,
|
|
2606
2687
|
logger as logger5
|
|
2607
2688
|
} from "@elizaos/core";
|
|
2608
2689
|
import {
|
|
2609
2690
|
ChannelType as DiscordChannelType3
|
|
2610
2691
|
} from "discord.js";
|
|
2692
|
+
import { EventEmitter } from "node:events";
|
|
2693
|
+
import { pipeline } from "node:stream";
|
|
2611
2694
|
import prism from "prism-media";
|
|
2612
2695
|
var DECODE_FRAME_SIZE = 1024;
|
|
2613
2696
|
var DECODE_SAMPLE_RATE = 16e3;
|
|
2697
|
+
function createOpusDecoder(options) {
|
|
2698
|
+
try {
|
|
2699
|
+
return new prism.opus.Decoder(options);
|
|
2700
|
+
} catch (error) {
|
|
2701
|
+
logger5.warn(`Failed to create opus decoder with prism-media: ${error}`);
|
|
2702
|
+
try {
|
|
2703
|
+
const { generateDependencyReport } = __require("@discordjs/voice");
|
|
2704
|
+
const report = generateDependencyReport();
|
|
2705
|
+
logger5.debug("Voice dependency report:", report);
|
|
2706
|
+
} catch (reportError) {
|
|
2707
|
+
logger5.warn("Could not generate dependency report:", reportError);
|
|
2708
|
+
}
|
|
2709
|
+
throw error;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
function getWavHeader(audioLength, sampleRate, channelCount = 1, bitsPerSample = 16) {
|
|
2713
|
+
const wavHeader = Buffer.alloc(44);
|
|
2714
|
+
wavHeader.write("RIFF", 0);
|
|
2715
|
+
wavHeader.writeUInt32LE(36 + audioLength, 4);
|
|
2716
|
+
wavHeader.write("WAVE", 8);
|
|
2717
|
+
wavHeader.write("fmt ", 12);
|
|
2718
|
+
wavHeader.writeUInt32LE(16, 16);
|
|
2719
|
+
wavHeader.writeUInt16LE(1, 20);
|
|
2720
|
+
wavHeader.writeUInt16LE(channelCount, 22);
|
|
2721
|
+
wavHeader.writeUInt32LE(sampleRate, 24);
|
|
2722
|
+
wavHeader.writeUInt32LE(sampleRate * bitsPerSample * channelCount / 8, 28);
|
|
2723
|
+
wavHeader.writeUInt16LE(bitsPerSample * channelCount / 8, 32);
|
|
2724
|
+
wavHeader.writeUInt16LE(bitsPerSample, 34);
|
|
2725
|
+
wavHeader.write("data", 36);
|
|
2726
|
+
wavHeader.writeUInt32LE(audioLength, 40);
|
|
2727
|
+
return wavHeader;
|
|
2728
|
+
}
|
|
2614
2729
|
var AudioMonitor = class {
|
|
2615
2730
|
readable;
|
|
2616
2731
|
buffers = [];
|
|
@@ -2632,7 +2747,10 @@ var AudioMonitor = class {
|
|
|
2632
2747
|
this.lastFlagged = this.buffers.length;
|
|
2633
2748
|
}
|
|
2634
2749
|
this.buffers.push(chunk);
|
|
2635
|
-
const currentSize = this.buffers.reduce(
|
|
2750
|
+
const currentSize = this.buffers.reduce(
|
|
2751
|
+
(acc, cur) => acc + cur.length,
|
|
2752
|
+
0
|
|
2753
|
+
);
|
|
2636
2754
|
while (currentSize > this.maxSize) {
|
|
2637
2755
|
this.buffers.shift();
|
|
2638
2756
|
this.lastFlagged--;
|
|
@@ -2799,7 +2917,10 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2799
2917
|
this.stopMonitoringMember(member.id);
|
|
2800
2918
|
}
|
|
2801
2919
|
if (newChannelId && this.connections.has(newChannelId)) {
|
|
2802
|
-
await this.monitorMember(
|
|
2920
|
+
await this.monitorMember(
|
|
2921
|
+
member,
|
|
2922
|
+
newState.channel
|
|
2923
|
+
);
|
|
2803
2924
|
}
|
|
2804
2925
|
}
|
|
2805
2926
|
/**
|
|
@@ -2830,9 +2951,13 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2830
2951
|
entersState(connection, VoiceConnectionStatus.Ready, 2e4),
|
|
2831
2952
|
entersState(connection, VoiceConnectionStatus.Signalling, 2e4)
|
|
2832
2953
|
]);
|
|
2833
|
-
logger5.log(
|
|
2954
|
+
logger5.log(
|
|
2955
|
+
`Voice connection established in state: ${connection.state.status}`
|
|
2956
|
+
);
|
|
2834
2957
|
connection.on("stateChange", async (oldState, newState) => {
|
|
2835
|
-
logger5.log(
|
|
2958
|
+
logger5.log(
|
|
2959
|
+
`Voice connection state changed from ${oldState.status} to ${newState.status}`
|
|
2960
|
+
);
|
|
2836
2961
|
if (newState.status === VoiceConnectionStatus.Disconnected) {
|
|
2837
2962
|
logger5.log("Handling disconnection...");
|
|
2838
2963
|
try {
|
|
@@ -2929,20 +3054,34 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2929
3054
|
emitClose: true
|
|
2930
3055
|
});
|
|
2931
3056
|
if (!receiveStream || receiveStream.readableLength === 0) {
|
|
2932
|
-
logger5.warn(
|
|
3057
|
+
logger5.warn(
|
|
3058
|
+
`[monitorMember] No receiveStream or empty stream for user ${entityId}`
|
|
3059
|
+
);
|
|
3060
|
+
return;
|
|
3061
|
+
}
|
|
3062
|
+
let opusDecoder;
|
|
3063
|
+
try {
|
|
3064
|
+
opusDecoder = createOpusDecoder({
|
|
3065
|
+
channels: 1,
|
|
3066
|
+
rate: DECODE_SAMPLE_RATE,
|
|
3067
|
+
frameSize: DECODE_FRAME_SIZE
|
|
3068
|
+
});
|
|
3069
|
+
} catch (error) {
|
|
3070
|
+
logger5.error(
|
|
3071
|
+
`[monitorMember] Failed to create opus decoder for user ${entityId}: ${error}`
|
|
3072
|
+
);
|
|
2933
3073
|
return;
|
|
2934
3074
|
}
|
|
2935
|
-
const opusDecoder = new prism.opus.Decoder({
|
|
2936
|
-
channels: 1,
|
|
2937
|
-
rate: DECODE_SAMPLE_RATE,
|
|
2938
|
-
frameSize: DECODE_FRAME_SIZE
|
|
2939
|
-
});
|
|
2940
3075
|
const volumeBuffer = [];
|
|
2941
3076
|
const VOLUME_WINDOW_SIZE = 30;
|
|
2942
3077
|
const SPEAKING_THRESHOLD = 0.05;
|
|
2943
3078
|
opusDecoder.on("data", (pcmData) => {
|
|
2944
3079
|
if (this.activeAudioPlayer) {
|
|
2945
|
-
const samples = new Int16Array(
|
|
3080
|
+
const samples = new Int16Array(
|
|
3081
|
+
pcmData.buffer,
|
|
3082
|
+
pcmData.byteOffset,
|
|
3083
|
+
pcmData.length / 2
|
|
3084
|
+
);
|
|
2946
3085
|
const maxAmplitude = Math.max(...samples.map(Math.abs)) / 32768;
|
|
2947
3086
|
volumeBuffer.push(maxAmplitude);
|
|
2948
3087
|
if (volumeBuffer.length > VOLUME_WINDOW_SIZE) {
|
|
@@ -2956,15 +3095,21 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2956
3095
|
}
|
|
2957
3096
|
}
|
|
2958
3097
|
});
|
|
2959
|
-
pipeline(
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
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
|
+
}
|
|
2966
3111
|
}
|
|
2967
|
-
|
|
3112
|
+
);
|
|
2968
3113
|
this.streams.set(entityId, opusDecoder);
|
|
2969
3114
|
this.connections.set(entityId, connection);
|
|
2970
3115
|
opusDecoder.on("error", (err) => {
|
|
@@ -2987,7 +3132,14 @@ var VoiceManager = class extends EventEmitter {
|
|
|
2987
3132
|
opusDecoder.on("error", errorHandler);
|
|
2988
3133
|
opusDecoder.on("close", closeHandler);
|
|
2989
3134
|
receiveStream?.on("close", streamCloseHandler);
|
|
2990
|
-
this.client?.emit(
|
|
3135
|
+
this.client?.emit(
|
|
3136
|
+
"userStream",
|
|
3137
|
+
entityId,
|
|
3138
|
+
name,
|
|
3139
|
+
userName,
|
|
3140
|
+
channel,
|
|
3141
|
+
opusDecoder
|
|
3142
|
+
);
|
|
2991
3143
|
}
|
|
2992
3144
|
/**
|
|
2993
3145
|
* Leaves the specified voice channel and stops monitoring all members in that channel.
|
|
@@ -3049,7 +3201,13 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3049
3201
|
this.transcriptionTimeout = setTimeout(async () => {
|
|
3050
3202
|
this.processingVoice = true;
|
|
3051
3203
|
try {
|
|
3052
|
-
await this.processTranscription(
|
|
3204
|
+
await this.processTranscription(
|
|
3205
|
+
entityId,
|
|
3206
|
+
channel.id,
|
|
3207
|
+
channel,
|
|
3208
|
+
name,
|
|
3209
|
+
userName
|
|
3210
|
+
);
|
|
3053
3211
|
this.userStates.forEach((state, _) => {
|
|
3054
3212
|
state.buffers.length = 0;
|
|
3055
3213
|
state.totalLength = 0;
|
|
@@ -3130,7 +3288,10 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3130
3288
|
state.totalLength = 0;
|
|
3131
3289
|
const wavBuffer = await this.convertOpusToWav(inputBuffer);
|
|
3132
3290
|
logger5.debug("Starting transcription...");
|
|
3133
|
-
const transcriptionText = await this.runtime.useModel(
|
|
3291
|
+
const transcriptionText = await this.runtime.useModel(
|
|
3292
|
+
ModelType8.TRANSCRIPTION,
|
|
3293
|
+
wavBuffer
|
|
3294
|
+
);
|
|
3134
3295
|
if (transcriptionText && isValidTranscription2(transcriptionText)) {
|
|
3135
3296
|
state.transcriptionText += transcriptionText;
|
|
3136
3297
|
}
|
|
@@ -3138,7 +3299,14 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3138
3299
|
this.cleanupAudioPlayer(this.activeAudioPlayer);
|
|
3139
3300
|
const finalText = state.transcriptionText;
|
|
3140
3301
|
state.transcriptionText = "";
|
|
3141
|
-
await this.handleMessage(
|
|
3302
|
+
await this.handleMessage(
|
|
3303
|
+
finalText,
|
|
3304
|
+
entityId,
|
|
3305
|
+
channelId,
|
|
3306
|
+
channel,
|
|
3307
|
+
name,
|
|
3308
|
+
userName
|
|
3309
|
+
);
|
|
3142
3310
|
}
|
|
3143
3311
|
} catch (error) {
|
|
3144
3312
|
console.error(`Error transcribing audio for user ${entityId}:`, error);
|
|
@@ -3176,7 +3344,10 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3176
3344
|
worldName: channel.guild.name
|
|
3177
3345
|
});
|
|
3178
3346
|
const memory = {
|
|
3179
|
-
id: createUniqueUuid5(
|
|
3347
|
+
id: createUniqueUuid5(
|
|
3348
|
+
this.runtime,
|
|
3349
|
+
`${channelId}-voice-message-${Date.now()}`
|
|
3350
|
+
),
|
|
3180
3351
|
agentId: this.runtime.agentId,
|
|
3181
3352
|
entityId: uniqueEntityId,
|
|
3182
3353
|
roomId,
|
|
@@ -3194,7 +3365,10 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3194
3365
|
const callback = async (content, _files = []) => {
|
|
3195
3366
|
try {
|
|
3196
3367
|
const responseMemory = {
|
|
3197
|
-
id: createUniqueUuid5(
|
|
3368
|
+
id: createUniqueUuid5(
|
|
3369
|
+
this.runtime,
|
|
3370
|
+
`${memory.id}-voice-response-${Date.now()}`
|
|
3371
|
+
),
|
|
3198
3372
|
entityId: this.runtime.agentId,
|
|
3199
3373
|
agentId: this.runtime.agentId,
|
|
3200
3374
|
content: {
|
|
@@ -3223,11 +3397,14 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3223
3397
|
return [];
|
|
3224
3398
|
}
|
|
3225
3399
|
};
|
|
3226
|
-
this.runtime.emitEvent(
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3400
|
+
this.runtime.emitEvent(
|
|
3401
|
+
["DISCORD_VOICE_MESSAGE_RECEIVED", "VOICE_MESSAGE_RECEIVED"],
|
|
3402
|
+
{
|
|
3403
|
+
runtime: this.runtime,
|
|
3404
|
+
message: memory,
|
|
3405
|
+
callback
|
|
3406
|
+
}
|
|
3407
|
+
);
|
|
3231
3408
|
} catch (error) {
|
|
3232
3409
|
console.error("Error processing voice message:", error);
|
|
3233
3410
|
}
|
|
@@ -3256,7 +3433,9 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3256
3433
|
async scanGuild(guild) {
|
|
3257
3434
|
let chosenChannel = null;
|
|
3258
3435
|
try {
|
|
3259
|
-
const channelId = this.runtime.getSetting(
|
|
3436
|
+
const channelId = this.runtime.getSetting(
|
|
3437
|
+
"DISCORD_VOICE_CHANNEL_ID"
|
|
3438
|
+
);
|
|
3260
3439
|
if (channelId) {
|
|
3261
3440
|
const channel = await guild.channels.fetch(channelId);
|
|
3262
3441
|
if (channel?.isVoiceBased()) {
|
|
@@ -3313,12 +3492,15 @@ var VoiceManager = class extends EventEmitter {
|
|
|
3313
3492
|
audioPlayer.on("error", (err) => {
|
|
3314
3493
|
logger5.debug(`Audio player error: ${err}`);
|
|
3315
3494
|
});
|
|
3316
|
-
audioPlayer.on(
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
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
|
+
}
|
|
3320
3502
|
}
|
|
3321
|
-
|
|
3503
|
+
);
|
|
3322
3504
|
}
|
|
3323
3505
|
/**
|
|
3324
3506
|
* Cleans up the provided audio player by stopping it, removing all listeners,
|
|
@@ -3420,7 +3602,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3420
3602
|
}
|
|
3421
3603
|
const token = runtime.getSetting("DISCORD_API_TOKEN");
|
|
3422
3604
|
if (!token || token.trim() === "") {
|
|
3423
|
-
logger6.warn(
|
|
3605
|
+
logger6.warn(
|
|
3606
|
+
"Discord API Token not provided - Discord functionality will be unavailable"
|
|
3607
|
+
);
|
|
3424
3608
|
this.client = null;
|
|
3425
3609
|
return;
|
|
3426
3610
|
}
|
|
@@ -3438,7 +3622,12 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3438
3622
|
GatewayIntentBits.GuildMessageTyping,
|
|
3439
3623
|
GatewayIntentBits.GuildMessageReactions
|
|
3440
3624
|
],
|
|
3441
|
-
partials: [
|
|
3625
|
+
partials: [
|
|
3626
|
+
Partials.Channel,
|
|
3627
|
+
Partials.Message,
|
|
3628
|
+
Partials.User,
|
|
3629
|
+
Partials.Reaction
|
|
3630
|
+
]
|
|
3442
3631
|
});
|
|
3443
3632
|
this.runtime = runtime;
|
|
3444
3633
|
this.voiceManager = new VoiceManager(this, runtime);
|
|
@@ -3469,7 +3658,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3469
3658
|
*/
|
|
3470
3659
|
registerSendHandler() {
|
|
3471
3660
|
if (this.runtime) {
|
|
3472
|
-
this.runtime.registerSendHandler(
|
|
3661
|
+
this.runtime.registerSendHandler(
|
|
3662
|
+
"discord",
|
|
3663
|
+
this.handleSendMessage.bind(this)
|
|
3664
|
+
);
|
|
3473
3665
|
}
|
|
3474
3666
|
}
|
|
3475
3667
|
/**
|
|
@@ -3517,10 +3709,14 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3517
3709
|
await targetChannel.send(chunk);
|
|
3518
3710
|
}
|
|
3519
3711
|
} else {
|
|
3520
|
-
logger6.warn(
|
|
3712
|
+
logger6.warn(
|
|
3713
|
+
"[Discord SendHandler] No text content provided to send."
|
|
3714
|
+
);
|
|
3521
3715
|
}
|
|
3522
3716
|
} else {
|
|
3523
|
-
throw new Error(
|
|
3717
|
+
throw new Error(
|
|
3718
|
+
`Target channel ${targetChannel.id} does not have a send method.`
|
|
3719
|
+
);
|
|
3524
3720
|
}
|
|
3525
3721
|
} else {
|
|
3526
3722
|
throw new Error(
|
|
@@ -3577,12 +3773,32 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3577
3773
|
if (!this.client) {
|
|
3578
3774
|
return;
|
|
3579
3775
|
}
|
|
3580
|
-
this.client.on("messageCreate", (message) => {
|
|
3581
|
-
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
|
+
);
|
|
3582
3781
|
return;
|
|
3583
3782
|
}
|
|
3584
3783
|
if (this.allowedChannelIds && !this.allowedChannelIds.includes(message.channel.id)) {
|
|
3585
|
-
|
|
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
|
+
}
|
|
3586
3802
|
}
|
|
3587
3803
|
try {
|
|
3588
3804
|
this.messageManager?.handleMessage(message);
|
|
@@ -3640,11 +3856,20 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3640
3856
|
logger6.error(`Error handling interaction: ${error}`);
|
|
3641
3857
|
}
|
|
3642
3858
|
});
|
|
3643
|
-
this.client.on(
|
|
3644
|
-
|
|
3645
|
-
|
|
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
|
+
}
|
|
3646
3871
|
}
|
|
3647
|
-
|
|
3872
|
+
);
|
|
3648
3873
|
}
|
|
3649
3874
|
/**
|
|
3650
3875
|
* Handles the event when a new member joins a guild.
|
|
@@ -3743,7 +3968,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3743
3968
|
}
|
|
3744
3969
|
const userSelections = this.userSelections.get(userId);
|
|
3745
3970
|
if (!userSelections) {
|
|
3746
|
-
logger6.error(
|
|
3971
|
+
logger6.error(
|
|
3972
|
+
`User selections map unexpectedly missing for user ${userId}`
|
|
3973
|
+
);
|
|
3747
3974
|
return;
|
|
3748
3975
|
}
|
|
3749
3976
|
try {
|
|
@@ -3763,9 +3990,13 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3763
3990
|
}
|
|
3764
3991
|
if (interaction.isButton()) {
|
|
3765
3992
|
logger6.info("Button interaction detected");
|
|
3766
|
-
logger6.info(
|
|
3993
|
+
logger6.info(
|
|
3994
|
+
`Button pressed by user ${userId}: ${interaction.customId}`
|
|
3995
|
+
);
|
|
3767
3996
|
const formSelections = userSelections[messageId] || {};
|
|
3768
|
-
logger6.info(
|
|
3997
|
+
logger6.info(
|
|
3998
|
+
`Form data being submitted: ${JSON.stringify(formSelections)}`
|
|
3999
|
+
);
|
|
3769
4000
|
this.runtime.emitEvent(["DISCORD_INTERACTION"], {
|
|
3770
4001
|
interaction: {
|
|
3771
4002
|
customId: interaction.customId,
|
|
@@ -3829,7 +4060,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3829
4060
|
(member) => channel.permissionsFor(member)?.has(PermissionsBitField2.Flags.ViewChannel)
|
|
3830
4061
|
).map((member) => createUniqueUuid6(this.runtime, member.id));
|
|
3831
4062
|
} catch (error) {
|
|
3832
|
-
logger6.warn(
|
|
4063
|
+
logger6.warn(
|
|
4064
|
+
`Failed to get participants for channel ${channel.name}:`,
|
|
4065
|
+
error
|
|
4066
|
+
);
|
|
3833
4067
|
}
|
|
3834
4068
|
}
|
|
3835
4069
|
rooms.push({
|
|
@@ -3866,9 +4100,11 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3866
4100
|
id: createUniqueUuid6(this.runtime, member.id),
|
|
3867
4101
|
names: Array.from(
|
|
3868
4102
|
new Set(
|
|
3869
|
-
[
|
|
3870
|
-
|
|
3871
|
-
|
|
4103
|
+
[
|
|
4104
|
+
member.user.username,
|
|
4105
|
+
member.displayName,
|
|
4106
|
+
member.user.globalName
|
|
4107
|
+
].filter(Boolean)
|
|
3872
4108
|
)
|
|
3873
4109
|
),
|
|
3874
4110
|
agentId: this.runtime.agentId,
|
|
@@ -3903,9 +4139,11 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3903
4139
|
id: entityId,
|
|
3904
4140
|
names: Array.from(
|
|
3905
4141
|
new Set(
|
|
3906
|
-
[
|
|
3907
|
-
|
|
3908
|
-
|
|
4142
|
+
[
|
|
4143
|
+
member.user.username,
|
|
4144
|
+
member.displayName,
|
|
4145
|
+
member.user.globalName
|
|
4146
|
+
].filter(Boolean)
|
|
3909
4147
|
)
|
|
3910
4148
|
),
|
|
3911
4149
|
agentId: this.runtime.agentId,
|
|
@@ -3946,9 +4184,11 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
3946
4184
|
id: createUniqueUuid6(this.runtime, member.id),
|
|
3947
4185
|
names: Array.from(
|
|
3948
4186
|
new Set(
|
|
3949
|
-
[
|
|
3950
|
-
|
|
3951
|
-
|
|
4187
|
+
[
|
|
4188
|
+
member.user.username,
|
|
4189
|
+
member.displayName,
|
|
4190
|
+
member.user.globalName
|
|
4191
|
+
].filter(Boolean)
|
|
3952
4192
|
)
|
|
3953
4193
|
),
|
|
3954
4194
|
agentId: this.runtime.agentId,
|
|
@@ -4056,9 +4296,13 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4056
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.
|
|
4057
4297
|
*/
|
|
4058
4298
|
async getTextChannelMembers(channelId, useCache = true) {
|
|
4059
|
-
logger6.info(
|
|
4299
|
+
logger6.info(
|
|
4300
|
+
`Fetching members for text channel ${channelId}, useCache=${useCache}`
|
|
4301
|
+
);
|
|
4060
4302
|
try {
|
|
4061
|
-
const channel = await this.client?.channels.fetch(
|
|
4303
|
+
const channel = await this.client?.channels.fetch(
|
|
4304
|
+
channelId
|
|
4305
|
+
);
|
|
4062
4306
|
if (!channel) {
|
|
4063
4307
|
logger6.error(`Channel not found: ${channelId}`);
|
|
4064
4308
|
return [];
|
|
@@ -4082,7 +4326,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4082
4326
|
} else {
|
|
4083
4327
|
try {
|
|
4084
4328
|
if (useCache && guild.members.cache.size > 0) {
|
|
4085
|
-
logger6.info(
|
|
4329
|
+
logger6.info(
|
|
4330
|
+
`Using cached members (${guild.members.cache.size} members)`
|
|
4331
|
+
);
|
|
4086
4332
|
members = guild.members.cache;
|
|
4087
4333
|
} else {
|
|
4088
4334
|
logger6.info(`Fetching members for guild ${guild.name}`);
|
|
@@ -4107,7 +4353,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4107
4353
|
username: member.user.username,
|
|
4108
4354
|
displayName: member.displayName || member.user.username
|
|
4109
4355
|
}));
|
|
4110
|
-
logger6.info(
|
|
4356
|
+
logger6.info(
|
|
4357
|
+
`Found ${channelMembers.length} members with access to channel ${channel.name}`
|
|
4358
|
+
);
|
|
4111
4359
|
return channelMembers;
|
|
4112
4360
|
} catch (error) {
|
|
4113
4361
|
logger6.error(`Error fetching channel members: ${error}`);
|
|
@@ -4138,7 +4386,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4138
4386
|
}
|
|
4139
4387
|
}
|
|
4140
4388
|
const timestamp = Date.now();
|
|
4141
|
-
const roomId = createUniqueUuid6(
|
|
4389
|
+
const roomId = createUniqueUuid6(
|
|
4390
|
+
this.runtime,
|
|
4391
|
+
reaction.message.channel.id
|
|
4392
|
+
);
|
|
4142
4393
|
const entityId = createUniqueUuid6(this.runtime, user.id);
|
|
4143
4394
|
const reactionUUID = createUniqueUuid6(
|
|
4144
4395
|
this.runtime,
|
|
@@ -4160,7 +4411,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4160
4411
|
entityId,
|
|
4161
4412
|
roomId,
|
|
4162
4413
|
userName,
|
|
4163
|
-
worldId: createUniqueUuid6(
|
|
4414
|
+
worldId: createUniqueUuid6(
|
|
4415
|
+
this.runtime,
|
|
4416
|
+
reaction.message.guild?.id ?? roomId
|
|
4417
|
+
),
|
|
4164
4418
|
worldName: reaction.message.guild?.name,
|
|
4165
4419
|
name,
|
|
4166
4420
|
source: "discord",
|
|
@@ -4179,7 +4433,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4179
4433
|
text: reactionMessage,
|
|
4180
4434
|
source: "discord",
|
|
4181
4435
|
inReplyTo,
|
|
4182
|
-
channelType: await this.getChannelType(
|
|
4436
|
+
channelType: await this.getChannelType(
|
|
4437
|
+
reaction.message.channel
|
|
4438
|
+
)
|
|
4183
4439
|
},
|
|
4184
4440
|
roomId,
|
|
4185
4441
|
createdAt: timestamp
|
|
@@ -4189,14 +4445,19 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4189
4445
|
logger6.error("No channel found for reaction message");
|
|
4190
4446
|
return [];
|
|
4191
4447
|
}
|
|
4192
|
-
await reaction.message.channel.send(
|
|
4448
|
+
await reaction.message.channel.send(
|
|
4449
|
+
content.text ?? ""
|
|
4450
|
+
);
|
|
4193
4451
|
return [];
|
|
4194
4452
|
};
|
|
4195
|
-
this.runtime.emitEvent(
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4453
|
+
this.runtime.emitEvent(
|
|
4454
|
+
["DISCORD_REACTION_RECEIVED", "REACTION_RECEIVED"],
|
|
4455
|
+
{
|
|
4456
|
+
runtime: this.runtime,
|
|
4457
|
+
message: memory,
|
|
4458
|
+
callback
|
|
4459
|
+
}
|
|
4460
|
+
);
|
|
4200
4461
|
} catch (error) {
|
|
4201
4462
|
logger6.error("Error handling reaction:", error);
|
|
4202
4463
|
}
|
|
@@ -4216,14 +4477,20 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4216
4477
|
try {
|
|
4217
4478
|
await reaction.fetch();
|
|
4218
4479
|
} catch (error) {
|
|
4219
|
-
logger6.error(
|
|
4480
|
+
logger6.error(
|
|
4481
|
+
"Something went wrong when fetching the message:",
|
|
4482
|
+
error
|
|
4483
|
+
);
|
|
4220
4484
|
return;
|
|
4221
4485
|
}
|
|
4222
4486
|
}
|
|
4223
4487
|
const messageContent = reaction.message.content || "";
|
|
4224
4488
|
const truncatedContent = messageContent.length > 50 ? `${messageContent.substring(0, 50)}...` : messageContent;
|
|
4225
4489
|
const reactionMessage = `*Removed <${emoji}> from: \\"${truncatedContent}\\"*`;
|
|
4226
|
-
const roomId = createUniqueUuid6(
|
|
4490
|
+
const roomId = createUniqueUuid6(
|
|
4491
|
+
this.runtime,
|
|
4492
|
+
reaction.message.channel.id
|
|
4493
|
+
);
|
|
4227
4494
|
const entityId = createUniqueUuid6(this.runtime, user.id);
|
|
4228
4495
|
const timestamp = Date.now();
|
|
4229
4496
|
const reactionUUID = createUniqueUuid6(
|
|
@@ -4236,7 +4503,10 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4236
4503
|
entityId,
|
|
4237
4504
|
roomId,
|
|
4238
4505
|
userName,
|
|
4239
|
-
worldId: createUniqueUuid6(
|
|
4506
|
+
worldId: createUniqueUuid6(
|
|
4507
|
+
this.runtime,
|
|
4508
|
+
reaction.message.guild?.id ?? roomId
|
|
4509
|
+
),
|
|
4240
4510
|
worldName: reaction.message.guild?.name,
|
|
4241
4511
|
name,
|
|
4242
4512
|
source: "discord",
|
|
@@ -4254,7 +4524,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4254
4524
|
text: reactionMessage,
|
|
4255
4525
|
source: "discord",
|
|
4256
4526
|
inReplyTo: createUniqueUuid6(this.runtime, reaction.message.id),
|
|
4257
|
-
channelType: await this.getChannelType(
|
|
4527
|
+
channelType: await this.getChannelType(
|
|
4528
|
+
reaction.message.channel
|
|
4529
|
+
)
|
|
4258
4530
|
},
|
|
4259
4531
|
roomId,
|
|
4260
4532
|
createdAt: Date.now()
|
|
@@ -4264,7 +4536,9 @@ var DiscordService = class _DiscordService extends Service {
|
|
|
4264
4536
|
logger6.error("No channel found for reaction message");
|
|
4265
4537
|
return [];
|
|
4266
4538
|
}
|
|
4267
|
-
await reaction.message.channel.send(
|
|
4539
|
+
await reaction.message.channel.send(
|
|
4540
|
+
content.text ?? ""
|
|
4541
|
+
);
|
|
4268
4542
|
return [];
|
|
4269
4543
|
};
|
|
4270
4544
|
this.runtime.emitEvent(["DISCORD_REACTION_RECEIVED" /* REACTION_RECEIVED */], {
|
|
@@ -4323,8 +4597,15 @@ import {
|
|
|
4323
4597
|
createAudioResource as createAudioResource2,
|
|
4324
4598
|
entersState as entersState2
|
|
4325
4599
|
} from "@discordjs/voice";
|
|
4326
|
-
import {
|
|
4327
|
-
|
|
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";
|
|
4328
4609
|
var TEST_IMAGE_URL = "https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true";
|
|
4329
4610
|
var DiscordTestSuite = class {
|
|
4330
4611
|
name = "discord";
|
|
@@ -4374,7 +4655,9 @@ var DiscordTestSuite = class {
|
|
|
4374
4655
|
*/
|
|
4375
4656
|
async testCreatingDiscordClient(runtime) {
|
|
4376
4657
|
try {
|
|
4377
|
-
this.discordClient = runtime.getService(
|
|
4658
|
+
this.discordClient = runtime.getService(
|
|
4659
|
+
ServiceType2.DISCORD
|
|
4660
|
+
);
|
|
4378
4661
|
if (!this.discordClient) {
|
|
4379
4662
|
throw new Error("Failed to get DiscordService from runtime.");
|
|
4380
4663
|
}
|
|
@@ -4383,7 +4666,9 @@ var DiscordTestSuite = class {
|
|
|
4383
4666
|
} else {
|
|
4384
4667
|
logger7.info("Waiting for DiscordService to be ready...");
|
|
4385
4668
|
if (!this.discordClient.client) {
|
|
4386
|
-
throw new Error(
|
|
4669
|
+
throw new Error(
|
|
4670
|
+
"Discord client instance is missing within the service."
|
|
4671
|
+
);
|
|
4387
4672
|
}
|
|
4388
4673
|
await new Promise((resolve, reject) => {
|
|
4389
4674
|
this.discordClient.client?.once(Events2.ClientReady, resolve);
|
|
@@ -4425,7 +4710,9 @@ var DiscordTestSuite = class {
|
|
|
4425
4710
|
if (!this.discordClient.voiceManager) {
|
|
4426
4711
|
throw new Error("VoiceManager is not available on the Discord client.");
|
|
4427
4712
|
}
|
|
4428
|
-
await this.discordClient.voiceManager.handleJoinChannelCommand(
|
|
4713
|
+
await this.discordClient.voiceManager.handleJoinChannelCommand(
|
|
4714
|
+
fakeJoinInteraction
|
|
4715
|
+
);
|
|
4429
4716
|
logger7.success("Join voice slash command test completed successfully.");
|
|
4430
4717
|
} catch (error) {
|
|
4431
4718
|
throw new Error(`Error in join voice slash commands test: ${error}`);
|
|
@@ -4456,7 +4743,9 @@ var DiscordTestSuite = class {
|
|
|
4456
4743
|
if (!this.discordClient.voiceManager) {
|
|
4457
4744
|
throw new Error("VoiceManager is not available on the Discord client.");
|
|
4458
4745
|
}
|
|
4459
|
-
await this.discordClient.voiceManager.handleLeaveChannelCommand(
|
|
4746
|
+
await this.discordClient.voiceManager.handleLeaveChannelCommand(
|
|
4747
|
+
fakeLeaveInteraction
|
|
4748
|
+
);
|
|
4460
4749
|
logger7.success("Leave voice slash command test completed successfully.");
|
|
4461
4750
|
} catch (error) {
|
|
4462
4751
|
throw new Error(`Error in leave voice slash commands test: ${error}`);
|
|
@@ -4526,7 +4815,11 @@ var DiscordTestSuite = class {
|
|
|
4526
4815
|
throw new Error("Cannot send message to a non-text channel.");
|
|
4527
4816
|
}
|
|
4528
4817
|
const attachment = new AttachmentBuilder(TEST_IMAGE_URL);
|
|
4529
|
-
await this.sendMessageToChannel(
|
|
4818
|
+
await this.sendMessageToChannel(
|
|
4819
|
+
channel,
|
|
4820
|
+
"Testing Message",
|
|
4821
|
+
[attachment]
|
|
4822
|
+
);
|
|
4530
4823
|
} catch (error) {
|
|
4531
4824
|
throw new Error(`Error in sending text message: ${error}`);
|
|
4532
4825
|
}
|
|
@@ -4558,7 +4851,9 @@ var DiscordTestSuite = class {
|
|
|
4558
4851
|
attachments: []
|
|
4559
4852
|
};
|
|
4560
4853
|
if (!this.discordClient.messageManager) {
|
|
4561
|
-
throw new Error(
|
|
4854
|
+
throw new Error(
|
|
4855
|
+
"MessageManager is not available on the Discord client."
|
|
4856
|
+
);
|
|
4562
4857
|
}
|
|
4563
4858
|
await this.discordClient.messageManager.handleMessage(fakeMessage);
|
|
4564
4859
|
} catch (error) {
|
|
@@ -4594,9 +4889,16 @@ var DiscordTestSuite = class {
|
|
|
4594
4889
|
async sendMessageToChannel(channel, messageContent, files) {
|
|
4595
4890
|
try {
|
|
4596
4891
|
if (!channel || !channel.isTextBased()) {
|
|
4597
|
-
throw new Error(
|
|
4892
|
+
throw new Error(
|
|
4893
|
+
"Channel is not a text-based channel or does not exist."
|
|
4894
|
+
);
|
|
4598
4895
|
}
|
|
4599
|
-
await sendMessageInChunks(
|
|
4896
|
+
await sendMessageInChunks(
|
|
4897
|
+
channel,
|
|
4898
|
+
messageContent,
|
|
4899
|
+
"",
|
|
4900
|
+
files
|
|
4901
|
+
);
|
|
4600
4902
|
} catch (error) {
|
|
4601
4903
|
throw new Error(`Error sending message: ${error}`);
|
|
4602
4904
|
}
|
|
@@ -4692,7 +4994,14 @@ var discordPlugin = {
|
|
|
4692
4994
|
name: "discord",
|
|
4693
4995
|
description: "Discord service plugin for integration with Discord servers and channels",
|
|
4694
4996
|
services: [DiscordService],
|
|
4695
|
-
actions: [
|
|
4997
|
+
actions: [
|
|
4998
|
+
chatWithAttachments_default,
|
|
4999
|
+
downloadMedia,
|
|
5000
|
+
joinVoice,
|
|
5001
|
+
leaveVoice,
|
|
5002
|
+
summarize,
|
|
5003
|
+
transcribeMedia
|
|
5004
|
+
],
|
|
4696
5005
|
providers: [channelStateProvider, voiceStateProvider],
|
|
4697
5006
|
tests: [new DiscordTestSuite()],
|
|
4698
5007
|
init: async (config, runtime) => {
|