@@ -22,6 +22,7 @@ import { MessageManager } from "./messages.ts";
22
22
import channelStateProvider from "./providers/channelState.ts" ;
23
23
import voiceStateProvider from "./providers/voiceState.ts" ;
24
24
import { VoiceManager } from "./voice.ts" ;
25
+ import { PermissionsBitField } from "discord.js" ;
25
26
import path from "path" ;
26
27
import { fileURLToPath } from "url" ;
27
28
@@ -143,85 +144,144 @@ export class DiscordClient extends EventEmitter {
143
144
console . error ( "Error registering slash commands:" , error ) ;
144
145
}
145
146
147
+ // Required permissions for the bot
148
+ const requiredPermissions = [
149
+ // Text Permissions
150
+ PermissionsBitField . Flags . ViewChannel ,
151
+ PermissionsBitField . Flags . SendMessages ,
152
+ PermissionsBitField . Flags . SendMessagesInThreads ,
153
+ PermissionsBitField . Flags . CreatePrivateThreads ,
154
+ PermissionsBitField . Flags . CreatePublicThreads ,
155
+ PermissionsBitField . Flags . EmbedLinks ,
156
+ PermissionsBitField . Flags . AttachFiles ,
157
+ PermissionsBitField . Flags . AddReactions ,
158
+ PermissionsBitField . Flags . UseExternalEmojis ,
159
+ PermissionsBitField . Flags . UseExternalStickers ,
160
+ PermissionsBitField . Flags . MentionEveryone ,
161
+ PermissionsBitField . Flags . ManageMessages ,
162
+ PermissionsBitField . Flags . ReadMessageHistory ,
163
+ // Voice Permissions
164
+ PermissionsBitField . Flags . Connect ,
165
+ PermissionsBitField . Flags . Speak ,
166
+ PermissionsBitField . Flags . UseVAD ,
167
+ PermissionsBitField . Flags . PrioritySpeaker ,
168
+ ] . reduce ( ( a , b ) => a | b , 0n ) ;
169
+
146
170
elizaLogger . success ( "Use this URL to add the bot to your server:" ) ;
147
171
elizaLogger . success (
148
- `https://discord.com/api/oauth2/authorize?client_id=${ readyClient . user ?. id } &permissions=0 &scope=bot%20applications.commands`
172
+ `https://discord.com/api/oauth2/authorize?client_id=${ readyClient . user ?. id } &permissions=${ requiredPermissions } &scope=bot%20applications.commands`
149
173
) ;
150
174
await this . onReady ( ) ;
151
175
}
152
176
153
177
async handleReactionAdd ( reaction : MessageReaction , user : User ) {
154
- elizaLogger . log ( "Reaction added" ) ;
155
- // if (user.bot) return;
156
-
157
- let emoji = reaction . emoji . name ;
158
- if ( ! emoji && reaction . emoji . id ) {
159
- emoji = `<:${ reaction . emoji . name } :${ reaction . emoji . id } >` ;
160
- }
178
+ try {
179
+ elizaLogger . log ( "Reaction added" ) ;
161
180
162
- // Fetch the full message if it's a partial
163
- if ( reaction . partial ) {
164
- try {
165
- await reaction . fetch ( ) ;
166
- } catch ( error ) {
167
- console . error (
168
- "Something went wrong when fetching the message:" ,
169
- error
170
- ) ;
181
+ // Early returns
182
+ if ( ! reaction || ! user ) {
183
+ elizaLogger . warn ( "Invalid reaction or user" ) ;
171
184
return ;
172
185
}
173
- }
174
186
175
- const messageContent = reaction . message . content ;
176
- const truncatedContent =
177
- messageContent . length > 100
178
- ? messageContent . substring ( 0 , 100 ) + "..."
179
- : messageContent ;
187
+ // Get emoji info
188
+ let emoji = reaction . emoji . name ;
189
+ if ( ! emoji && reaction . emoji . id ) {
190
+ emoji = `<: ${ reaction . emoji . name } : ${ reaction . emoji . id } >` ;
191
+ }
180
192
181
- const reactionMessage = `*<${ emoji } >: "${ truncatedContent } "*` ;
193
+ // Fetch full message if partial
194
+ if ( reaction . partial ) {
195
+ try {
196
+ await reaction . fetch ( ) ;
197
+ } catch ( error ) {
198
+ elizaLogger . error (
199
+ "Failed to fetch partial reaction:" ,
200
+ error
201
+ ) ;
202
+ return ;
203
+ }
204
+ }
182
205
183
- const roomId = stringToUuid (
184
- reaction . message . channel . id + "-" + this . runtime . agentId
185
- ) ;
186
- const userIdUUID = stringToUuid ( user . id + "-" + this . runtime . agentId ) ;
206
+ // Generate IDs with timestamp to ensure uniqueness
207
+ const timestamp = Date . now ( ) ;
208
+ const roomId = stringToUuid (
209
+ `${ reaction . message . channel . id } -${ this . runtime . agentId } `
210
+ ) ;
211
+ const userIdUUID = stringToUuid (
212
+ `${ user . id } -${ this . runtime . agentId } `
213
+ ) ;
214
+ const reactionUUID = stringToUuid (
215
+ `${ reaction . message . id } -${ user . id } -${ emoji } -${ timestamp } -${ this . runtime . agentId } `
216
+ ) ;
217
+
218
+ // Validate IDs
219
+ if ( ! userIdUUID || ! roomId ) {
220
+ elizaLogger . error ( "Invalid user ID or room ID" , {
221
+ userIdUUID,
222
+ roomId,
223
+ } ) ;
224
+ return ;
225
+ }
187
226
188
- // Generate a unique UUID for the reaction
189
- const reactionUUID = stringToUuid (
190
- `${ reaction . message . id } -${ user . id } -${ emoji } -${ this . runtime . agentId } `
191
- ) ;
227
+ // Process message content
228
+ const messageContent = reaction . message . content || "" ;
229
+ const truncatedContent =
230
+ messageContent . length > 100
231
+ ? `${ messageContent . substring ( 0 , 100 ) } ...`
232
+ : messageContent ;
233
+ const reactionMessage = `*<${ emoji } >: "${ truncatedContent } "*` ;
234
+
235
+ // Get user info
236
+ const userName = reaction . message . author ?. username || "unknown" ;
237
+ const name = reaction . message . author ?. displayName || userName ;
238
+
239
+ // Ensure connection
240
+ await this . runtime . ensureConnection (
241
+ userIdUUID ,
242
+ roomId ,
243
+ userName ,
244
+ name ,
245
+ "discord"
246
+ ) ;
247
+
248
+ // Create memory with retry logic
249
+ const memory = {
250
+ id : reactionUUID ,
251
+ userId : userIdUUID ,
252
+ agentId : this . runtime . agentId ,
253
+ content : {
254
+ text : reactionMessage ,
255
+ source : "discord" ,
256
+ inReplyTo : stringToUuid (
257
+ `${ reaction . message . id } -${ this . runtime . agentId } `
258
+ ) ,
259
+ } ,
260
+ roomId,
261
+ createdAt : timestamp ,
262
+ embedding : embeddingZeroVector ,
263
+ } ;
192
264
193
- // ensure the user id and room id are valid
194
- if ( ! userIdUUID || ! roomId ) {
195
- console . error ( "Invalid user id or room id" ) ;
196
- return ;
265
+ try {
266
+ await this . runtime . messageManager . createMemory ( memory ) ;
267
+ elizaLogger . debug ( "Reaction memory created" , {
268
+ reactionId : reactionUUID ,
269
+ emoji,
270
+ userId : user . id ,
271
+ } ) ;
272
+ } catch ( error ) {
273
+ if ( error . code === "23505" ) {
274
+ // Duplicate key error
275
+ elizaLogger . warn ( "Duplicate reaction memory, skipping" , {
276
+ reactionId : reactionUUID ,
277
+ } ) ;
278
+ return ;
279
+ }
280
+ throw error ; // Re-throw other errors
281
+ }
282
+ } catch ( error ) {
283
+ elizaLogger . error ( "Error handling reaction:" , error ) ;
197
284
}
198
- const userName = reaction . message . author . username ;
199
- const name = reaction . message . author . displayName ;
200
-
201
- await this . runtime . ensureConnection (
202
- userIdUUID ,
203
- roomId ,
204
- userName ,
205
- name ,
206
- "discord"
207
- ) ;
208
-
209
- // Save the reaction as a message
210
- await this . runtime . messageManager . createMemory ( {
211
- id : reactionUUID , // This is the ID of the reaction message
212
- userId : userIdUUID ,
213
- agentId : this . runtime . agentId ,
214
- content : {
215
- text : reactionMessage ,
216
- source : "discord" ,
217
- inReplyTo : stringToUuid (
218
- reaction . message . id + "-" + this . runtime . agentId
219
- ) , // This is the ID of the original message
220
- } ,
221
- roomId,
222
- createdAt : Date . now ( ) ,
223
- embedding : embeddingZeroVector ,
224
- } ) ;
225
285
}
226
286
227
287
async handleReactionRemove ( reaction : MessageReaction , user : User ) {
0 commit comments