Skip to content

Commit f8b42df

Browse files
committed
handle message permissions better
1 parent 9f6ac99 commit f8b42df

File tree

1 file changed

+119
-67
lines changed

1 file changed

+119
-67
lines changed

core/src/clients/discord/messages.ts

+119-67
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import {
22
ChannelType,
33
Client,
44
Message as DiscordMessage,
5+
PermissionsBitField,
56
TextChannel,
7+
ThreadChannel,
68
} from "discord.js";
79
import { composeContext } from "../../core/context.ts";
810
import {
@@ -27,6 +29,7 @@ import { AttachmentManager } from "./attachments.ts";
2729
import { messageHandlerTemplate, shouldRespondTemplate } from "./templates.ts";
2830
import { InterestChannels } from "./types.ts";
2931
import { VoiceManager } from "./voice.ts";
32+
import { prettyConsole } from "../../index.ts";
3033

3134
const MAX_MESSAGE_LENGTH = 1900;
3235

@@ -102,6 +105,52 @@ function splitMessage(content: string): string[] {
102105
return messages;
103106
}
104107

108+
109+
function canSendMessage(channel) {
110+
// Get the bot member in the guild
111+
const botMember = channel.guild?.members.cache.get(channel.client.user.id);
112+
113+
if (!botMember) {
114+
return {
115+
canSend: false,
116+
reason: 'Not a guild channel or bot member not found'
117+
};
118+
}
119+
120+
// Required permissions for sending messages
121+
const requiredPermissions = [
122+
PermissionsBitField.Flags.ViewChannel,
123+
PermissionsBitField.Flags.SendMessages,
124+
PermissionsBitField.Flags.ReadMessageHistory
125+
];
126+
127+
// Add thread-specific permission if it's a thread
128+
if (channel instanceof ThreadChannel) {
129+
requiredPermissions.push(PermissionsBitField.Flags.SendMessagesInThreads);
130+
}
131+
132+
// Check permissions
133+
const permissions = channel.permissionsFor(botMember);
134+
135+
if (!permissions) {
136+
return {
137+
canSend: false,
138+
reason: 'Could not retrieve permissions'
139+
};
140+
}
141+
142+
// Check each required permission
143+
const missingPermissions = requiredPermissions.filter(perm => !permissions.has(perm));
144+
145+
return {
146+
canSend: missingPermissions.length === 0,
147+
missingPermissions: missingPermissions,
148+
reason: missingPermissions.length > 0
149+
? `Missing permissions: ${missingPermissions.map(p => String(p)).join(', ')}`
150+
: null
151+
};
152+
}
153+
105154
export class MessageManager {
106155
private client: Client;
107156
private runtime: IAgentRuntime;
@@ -122,7 +171,7 @@ export class MessageManager {
122171
if (
123172
message.interaction ||
124173
message.author.id ===
125-
this.client.user?.id /* || message.author?.bot*/
174+
this.client.user?.id /* || message.author?.bot*/
126175
)
127176
return;
128177
const userId = message.author.id as UUID;
@@ -178,14 +227,6 @@ export class MessageManager {
178227
roomId,
179228
};
180229

181-
let state = (await this.runtime.composeState(userMessage, {
182-
discordClient: this.client,
183-
discordMessage: message,
184-
agentName:
185-
this.runtime.character.name ||
186-
this.client.user?.displayName,
187-
})) as State;
188-
189230
const memory: Memory = {
190231
id: stringToUuid(message.id + "-" + this.runtime.agentId),
191232
...userMessage,
@@ -201,7 +242,18 @@ export class MessageManager {
201242
await this.runtime.messageManager.createMemory(memory);
202243
}
203244

204-
state = await this.runtime.updateRecentMessageState(state);
245+
let state = (await this.runtime.composeState(userMessage, {
246+
discordClient: this.client,
247+
discordMessage: message,
248+
agentName:
249+
this.runtime.character.name ||
250+
this.client.user?.displayName,
251+
})) as State;
252+
253+
if (!canSendMessage(message.channel).canSend) {
254+
return prettyConsole.warn(`Cannot send message to channel ${message.channel}`, canSendMessage(message.channel));
255+
}
256+
205257

206258
if (!shouldIgnore) {
207259
shouldIgnore = await this._shouldIgnore(message);
@@ -267,70 +319,70 @@ export class MessageManager {
267319
files: any[]
268320
) => {
269321
try {
270-
if (message.id && !content.inReplyTo) {
271-
content.inReplyTo = stringToUuid(message.id + "-" + this.runtime.agentId);
272-
}
273-
if (message.channel.type === ChannelType.GuildVoice) {
274-
// For voice channels, use text-to-speech
275-
const audioStream =
276-
await this.runtime.speechService.generate(
277-
this.runtime,
278-
content.text
322+
if (message.id && !content.inReplyTo) {
323+
content.inReplyTo = stringToUuid(message.id + "-" + this.runtime.agentId);
324+
}
325+
if (message.channel.type === ChannelType.GuildVoice) {
326+
// For voice channels, use text-to-speech
327+
const audioStream =
328+
await this.runtime.speechService.generate(
329+
this.runtime,
330+
content.text
331+
);
332+
await this.voiceManager.playAudioStream(
333+
userId,
334+
audioStream
279335
);
280-
await this.voiceManager.playAudioStream(
281-
userId,
282-
audioStream
283-
);
284-
const memory: Memory = {
285-
id: stringToUuid(message.id + "-" + this.runtime.agentId),
286-
userId: this.runtime.agentId,
287-
agentId: this.runtime.agentId,
288-
content,
289-
roomId,
290-
embedding: embeddingZeroVector,
291-
};
292-
return [memory];
293-
} else {
294-
// For text channels, send the message
295-
const messages = await sendMessageInChunks(
296-
message.channel as TextChannel,
297-
content.text,
298-
message.id,
299-
files
300-
);
301-
302-
const memories: Memory[] = [];
303-
for (const m of messages) {
304-
let action = content.action;
305-
// If there's only one message or it's the last message, keep the original action
306-
// For multiple messages, set all but the last to 'CONTINUE'
307-
if (
308-
messages.length > 1 &&
309-
m !== messages[messages.length - 1]
310-
) {
311-
action = "CONTINUE";
312-
}
313-
314336
const memory: Memory = {
315-
id: stringToUuid(m.id + "-" + this.runtime.agentId),
337+
id: stringToUuid(message.id + "-" + this.runtime.agentId),
316338
userId: this.runtime.agentId,
317339
agentId: this.runtime.agentId,
318-
content: {
319-
...content,
320-
action,
321-
inReplyTo: messageId,
322-
url: m.url,
323-
},
340+
content,
324341
roomId,
325342
embedding: embeddingZeroVector,
326-
createdAt: m.createdTimestamp,
327343
};
328-
memories.push(memory);
329-
}
330-
for (const m of memories) {
331-
await this.runtime.messageManager.createMemory(m);
332-
}
333-
return memories;
344+
return [memory];
345+
} else {
346+
// For text channels, send the message
347+
const messages = await sendMessageInChunks(
348+
message.channel as TextChannel,
349+
content.text,
350+
message.id,
351+
files
352+
);
353+
354+
const memories: Memory[] = [];
355+
for (const m of messages) {
356+
let action = content.action;
357+
// If there's only one message or it's the last message, keep the original action
358+
// For multiple messages, set all but the last to 'CONTINUE'
359+
if (
360+
messages.length > 1 &&
361+
m !== messages[messages.length - 1]
362+
) {
363+
action = "CONTINUE";
364+
}
365+
366+
const memory: Memory = {
367+
id: stringToUuid(m.id + "-" + this.runtime.agentId),
368+
userId: this.runtime.agentId,
369+
agentId: this.runtime.agentId,
370+
content: {
371+
...content,
372+
action,
373+
inReplyTo: messageId,
374+
url: m.url,
375+
},
376+
roomId,
377+
embedding: embeddingZeroVector,
378+
createdAt: m.createdTimestamp,
379+
};
380+
memories.push(memory);
381+
}
382+
for (const m of memories) {
383+
await this.runtime.messageManager.createMemory(m);
384+
}
385+
return memories;
334386
}
335387
} catch (error) {
336388
console.error("Error sending message:", error);

0 commit comments

Comments
 (0)