@@ -3,16 +3,18 @@ package bot
33import (
44 "fmt"
55 "log"
6+ "sync"
67 "time"
78
89 "github.com/bwmarrin/discordgo"
910)
1011
1112// AutoCleanWorker manages automatic channel cleaning
1213type AutoCleanWorker struct {
13- bot * Bot
14- stopChan chan bool
15- ticker * time.Ticker
14+ bot * Bot
15+ stopChan chan bool
16+ ticker * time.Ticker
17+ cleaningLocks sync.Map // Prevents concurrent cleans of the same channel
1618}
1719
1820// NewAutoCleanWorker creates a new auto-clean worker
@@ -49,9 +51,15 @@ func (w *AutoCleanWorker) Stop() {
4951
5052// checkScheduledCleans checks for channels that need cleaning
5153func (w * AutoCleanWorker ) checkScheduledCleans () {
54+ // Don't run if bot is not connected
55+ if w .bot .Session == nil || w .bot .Session .State == nil || w .bot .Session .State .User == nil {
56+ log .Printf ("[AutoClean] Skipping check - bot not connected" )
57+ return
58+ }
59+
5260 rows , err := w .bot .DB .Query (`
5361 SELECT guild_id, channel_id, interval_hours, warning_minutes, next_run, custom_message, custom_image
54- FROM autoclean
62+ FROM autoclean
5563 WHERE enabled = 1 AND datetime(next_run) <= datetime('now')
5664 ` )
5765 if err != nil {
@@ -69,19 +77,38 @@ func (w *AutoCleanWorker) checkScheduledCleans() {
6977 continue
7078 }
7179
80+ // Check if already cleaning this channel (prevent duplicate cleans)
81+ lockKey := guildID + ":" + channelID
82+ if _ , alreadyCleaning := w .cleaningLocks .LoadOrStore (lockKey , true ); alreadyCleaning {
83+ log .Printf ("[AutoClean] Channel %s already being cleaned, skipping" , channelID )
84+ continue
85+ }
86+
7287 // Clean the channel
7388 go w .cleanChannel (guildID , channelID , intervalHours , customMessage , customImage )
7489 }
7590}
7691
7792// cleanChannel performs the actual channel cleaning
7893func (w * AutoCleanWorker ) cleanChannel (guildID , channelID string , intervalHours int , customMessage , customImage string ) {
79- log .Printf ("Cleaning channel %s in guild %s" , channelID , guildID )
94+ lockKey := guildID + ":" + channelID
95+
96+ // Always release the lock when done
97+ defer w .cleaningLocks .Delete (lockKey )
98+
99+ log .Printf ("[AutoClean] Cleaning channel %s in guild %s" , channelID , guildID )
100+
101+ // Double-check connection before proceeding
102+ if w .bot .Session == nil || w .bot .Session .State == nil {
103+ log .Printf ("[AutoClean] Aborting clean - bot disconnected" )
104+ w .markCleanFailed (guildID , channelID )
105+ return
106+ }
80107
81108 // Get the original channel
82109 oldChannel , err := w .bot .Session .Channel (channelID )
83110 if err != nil {
84- log .Printf ("Failed to get channel %s: %v" , channelID , err )
111+ log .Printf ("[AutoClean] Failed to get channel %s: %v" , channelID , err )
85112 w .markCleanFailed (guildID , channelID )
86113 return
87114 }
@@ -101,25 +128,44 @@ func (w *AutoCleanWorker) cleanChannel(guildID, channelID string, intervalHours
101128 })
102129
103130 if err != nil {
104- log .Printf ("Failed to clone channel %s: %v" , channelID , err )
131+ log .Printf ("[AutoClean] Failed to clone channel %s: %v" , channelID , err )
105132 w .markCleanFailed (guildID , channelID )
106133 return
107134 }
108135
136+ // CRITICAL: Update database IMMEDIATELY after creating new channel
137+ // This prevents the loop where old channel keeps getting selected
138+ nextRun := time .Now ().Add (time .Duration (intervalHours ) * time .Hour )
139+ _ , err = w .bot .DB .Exec (`
140+ UPDATE autoclean
141+ SET channel_id = ?, next_run = ?, last_clean = datetime('now'), warned = 0
142+ WHERE guild_id = ? AND channel_id = ?` ,
143+ newChannel .ID , nextRun .Format (time .RFC3339 ), guildID , channelID )
144+
145+ if err != nil {
146+ log .Printf ("[AutoClean] CRITICAL: Failed to update database with new channel ID: %v" , err )
147+ // Try to delete the new channel since we couldn't update DB
148+ w .bot .Session .ChannelDelete (newChannel .ID )
149+ w .markCleanFailed (guildID , channelID )
150+ return
151+ }
152+
153+ log .Printf ("[AutoClean] Database updated: old=%s -> new=%s" , channelID , newChannel .ID )
154+
109155 // Move new channel to same position (Discord might not respect position in create)
110156 newPosition := oldChannel .Position
111157 _ , err = w .bot .Session .ChannelEditComplex (newChannel .ID , & discordgo.ChannelEdit {
112158 Position : & newPosition ,
113159 })
114160 if err != nil {
115- log .Printf ("Warning: Failed to reposition channel: %v" , err )
161+ log .Printf ("[AutoClean] Warning: Failed to reposition channel: %v" , err )
116162 }
117163
118- // Delete the old channel
164+ // Delete the old channel (non-critical - if this fails, we still have a working new channel)
119165 _ , err = w .bot .Session .ChannelDelete (channelID )
120166 if err != nil {
121- log .Printf ("Failed to delete old channel %s: %v" , channelID , err )
122- // Don't return - we still want to update the database with new channel ID
167+ log .Printf ("[AutoClean] Warning: Failed to delete old channel %s: %v (new channel %s is active) " , channelID , err , newChannel . ID )
168+ // Don't fail - the new channel is already set up and DB is updated
123169 }
124170
125171 // Send completion message
@@ -145,22 +191,10 @@ func (w *AutoCleanWorker) cleanChannel(guildID, channelID string, intervalHours
145191
146192 _ , err = w .bot .Session .ChannelMessageSendEmbed (newChannel .ID , embed )
147193 if err != nil {
148- log .Printf ("Failed to send clean message: %v" , err )
149- }
150-
151- // Update database with new channel ID and next run time
152- nextRun := time .Now ().Add (time .Duration (intervalHours ) * time .Hour )
153- _ , err = w .bot .DB .Exec (`
154- UPDATE autoclean
155- SET channel_id = ?, next_run = ?, last_clean = datetime('now')
156- WHERE guild_id = ? AND channel_id = ?` ,
157- newChannel .ID , nextRun .Format (time .RFC3339 ), guildID , channelID )
158-
159- if err != nil {
160- log .Printf ("Failed to update autoclean database: %v" , err )
194+ log .Printf ("[AutoClean] Warning: Failed to send clean message: %v" , err )
161195 }
162196
163- log .Printf ("Successfully cleaned channel. Old: %s, New: %s" , channelID , newChannel .ID )
197+ log .Printf ("[AutoClean] Successfully cleaned channel. Old: %s, New: %s" , channelID , newChannel .ID )
164198}
165199
166200// markCleanFailed marks a clean as failed and reschedules
0 commit comments