@@ -154,6 +154,101 @@ function checkRateLimit(userId, action, limit) {
154154 return { allowed : true , retryAfter : 0 } ;
155155}
156156
157+ /**
158+ * Required permissions for bot operations
159+ */
160+ const REQUIRED_PERMISSIONS = {
161+ SEND_MESSAGES : "SendMessages" ,
162+ EMBED_LINKS : "EmbedLinks" ,
163+ READ_MESSAGE_HISTORY : "ReadMessageHistory" ,
164+ VIEW_CHANNEL : "ViewChannel"
165+ } ;
166+
167+ /**
168+ * Check if bot has required permissions in a channel
169+ * @param {import('discord.js').GuildChannel } channel - The channel to check
170+ * @param {string[] } requiredPermissions - Array of permission names to check
171+ * @returns {Object } - { hasPermissions: boolean, missing: string[] }
172+ */
173+ function checkChannelPermissions ( channel , requiredPermissions = [ ] ) {
174+ if ( ! channel ) {
175+ return { hasPermissions : false , missing : [ "Channel not found" ] } ;
176+ }
177+
178+ // Check if channel is a text-based channel
179+ if ( ! channel . isTextBased ?. ( ) ) {
180+ return { hasPermissions : false , missing : [ "Channel is not text-based" ] } ;
181+ }
182+
183+ const missing = [ ] ;
184+ const botMember = channel . guild ?. members ?. me ;
185+
186+ if ( ! botMember ) {
187+ return { hasPermissions : false , missing : [ "Bot member not found in guild" ] } ;
188+ }
189+
190+ const permissions = channel . permissionsFor ( botMember ) ;
191+ if ( ! permissions ) {
192+ return { hasPermissions : false , missing : [ "Could not resolve permissions" ] } ;
193+ }
194+
195+ // Check each required permission
196+ for ( const perm of requiredPermissions ) {
197+ if ( ! permissions . has ( perm ) ) {
198+ missing . push ( perm ) ;
199+ }
200+ }
201+
202+ return {
203+ hasPermissions : missing . length === 0 ,
204+ missing
205+ } ;
206+ }
207+
208+ /**
209+ * Validate bot can send messages to a channel
210+ * @param {import('discord.js').GuildChannel } channel - The channel to validate
211+ * @returns {Object } - { valid: boolean, error?: string }
212+ */
213+ function validateChannelForSend ( channel ) {
214+ const result = checkChannelPermissions ( channel , [
215+ REQUIRED_PERMISSIONS . VIEW_CHANNEL ,
216+ REQUIRED_PERMISSIONS . SEND_MESSAGES ,
217+ REQUIRED_PERMISSIONS . EMBED_LINKS
218+ ] ) ;
219+
220+ if ( ! result . hasPermissions ) {
221+ return {
222+ valid : false ,
223+ error : `Missing permissions: ${ result . missing . join ( ", " ) } `
224+ } ;
225+ }
226+
227+ return { valid : true } ;
228+ }
229+
230+ /**
231+ * Validate bot can edit messages in a channel
232+ * @param {import('discord.js').GuildChannel } channel - The channel to validate
233+ * @returns {Object } - { valid: boolean, error?: string }
234+ */
235+ function validateChannelForEdit ( channel ) {
236+ const result = checkChannelPermissions ( channel , [
237+ REQUIRED_PERMISSIONS . VIEW_CHANNEL ,
238+ REQUIRED_PERMISSIONS . READ_MESSAGE_HISTORY ,
239+ REQUIRED_PERMISSIONS . EMBED_LINKS
240+ ] ) ;
241+
242+ if ( ! result . hasPermissions ) {
243+ return {
244+ valid : false ,
245+ error : `Missing permissions: ${ result . missing . join ( ", " ) } `
246+ } ;
247+ }
248+
249+ return { valid : true } ;
250+ }
251+
157252/**
158253 * Validate IPv4 address format
159254 * @param {string } ip - The IP address to validate
@@ -736,6 +831,13 @@ bot.on("ready", async () => {
736831 logChannel = guild . channels . cache . get ( config . logging . channelID ) ;
737832 if ( ! logChannel ) {
738833 console . warn ( `Log channel ${ config . logging . channelID } not found in guild ${ config . logging . guildID } ` ) ;
834+ } else {
835+ // Validate bot has required permissions in log channel
836+ const permCheck = validateChannelForSend ( logChannel ) ;
837+ if ( ! permCheck . valid ) {
838+ console . warn ( `Log channel ${ config . logging . channelID } permission issue: ${ permCheck . error } ` ) ;
839+ logChannel = null ; // Disable logging if permissions are missing
840+ }
739841 }
740842 } else {
741843 console . warn ( `Guild ${ config . logging . guildID } not found` ) ;
@@ -826,6 +928,13 @@ async function intervalFunction() {
826928 try {
827929 await withRetry ( async ( ) => {
828930 const channel = await bot . channels . fetch ( e . channelID ) ;
931+
932+ // Validate permissions before editing
933+ const permCheck = validateChannelForEdit ( channel ) ;
934+ if ( ! permCheck . valid ) {
935+ throw new Error ( `Permission check failed for channel ${ e . channelID } : ${ permCheck . error } ` ) ;
936+ }
937+
829938 const message = await channel . messages . fetch ( e . messageID ) ;
830939 await message . edit ( { content : "" , embeds : [ embed ] } ) ;
831940 } ) ;
@@ -1477,6 +1586,11 @@ const notifyUsers = async (map, serverObj) => {
14771586 if ( ! channel ) {
14781587 throw new Error ( `Fallback channel ${ config . fallback . channelID } not found` ) ;
14791588 }
1589+ // Validate permissions before sending
1590+ const permCheck = validateChannelForSend ( channel ) ;
1591+ if ( ! permCheck . valid ) {
1592+ throw new Error ( `Fallback channel permission error: ${ permCheck . error } ` ) ;
1593+ }
14801594 channel . send ( {
14811595 embeds : [ backupEmbed ] ,
14821596 content : fallbackContent
@@ -1563,13 +1677,23 @@ const notifyUsers = async (map, serverObj) => {
15631677
15641678 try {
15651679 await withRetry ( async ( ) => {
1566- bot . guilds . cache
1567- . get ( config . fallback . guildID )
1568- . channels . cache . get ( config . fallback . channelID )
1569- . send ( {
1570- embeds : [ backupEmbed ] ,
1571- content : fallbackContent
1572- } ) ;
1680+ const guild = bot . guilds . cache . get ( config . fallback . guildID ) ;
1681+ if ( ! guild ) {
1682+ throw new Error ( `Fallback guild ${ config . fallback . guildID } not found` ) ;
1683+ }
1684+ const channel = guild . channels . cache . get ( config . fallback . channelID ) ;
1685+ if ( ! channel ) {
1686+ throw new Error ( `Fallback channel ${ config . fallback . channelID } not found` ) ;
1687+ }
1688+ // Validate permissions before sending
1689+ const permCheck = validateChannelForSend ( channel ) ;
1690+ if ( ! permCheck . valid ) {
1691+ throw new Error ( `Fallback channel permission error: ${ permCheck . error } ` ) ;
1692+ }
1693+ channel . send ( {
1694+ embeds : [ backupEmbed ] ,
1695+ content : fallbackContent
1696+ } ) ;
15731697 } ) ;
15741698 } catch ( fallbackError ) {
15751699 console . error ( `Failed to send fallback notification for ${ map } :` , fallbackError ) ;
0 commit comments