Skip to content

Commit f427ff5

Browse files
committed
RetentionResponder now requires a channel.
1 parent 9a8b57a commit f427ff5

1 file changed

Lines changed: 108 additions & 63 deletions

File tree

src/Automation/Responders/RetentionResponder.cs

Lines changed: 108 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -47,92 +47,137 @@ public async Task ProcessMessage(IMessage originalMessage, CancellationToken tok
4747
}
4848

4949
var argText = content.Length > trigger.Length ? content[trigger.Length..].Trim() : string.Empty;
50+
var tokens = argText.Split(' ', StringSplitOptions.RemoveEmptyEntries);
51+
if (tokens.Length == 0)
52+
{
53+
await originalMessage.Channel.SendMessageAsync("Usage: purgeold <#channel>|<channelId>|#name <count>", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
54+
return;
55+
}
56+
57+
// Parse target channel from first token
58+
var channelArg = tokens[0];
59+
var guild = textChannel.Guild;
60+
ITextChannel targetChannel = null;
61+
if (channelArg.StartsWith("<#") && channelArg.EndsWith(">") && channelArg.Length > 3)
62+
{
63+
var inner = channelArg.Substring(2, channelArg.Length - 3);
64+
if (ulong.TryParse(inner, out var mentionedId))
65+
{
66+
targetChannel = await guild.GetTextChannelAsync(mentionedId);
67+
}
68+
}
69+
else if (ulong.TryParse(channelArg, out var channelIdFromNumber))
70+
{
71+
targetChannel = await guild.GetTextChannelAsync(channelIdFromNumber);
72+
}
73+
else
74+
{
75+
var name = channelArg.StartsWith("#") && channelArg.Length > 1 ? channelArg[1..] : channelArg;
76+
var allTextChannels = await guild.GetTextChannelsAsync();
77+
targetChannel = allTextChannels.FirstOrDefault(c => string.Equals(c.Name, name, StringComparison.InvariantCultureIgnoreCase));
78+
}
79+
80+
if (targetChannel == null)
81+
{
82+
await originalMessage.Channel.SendMessageAsync("Channel not found. Specify a valid channel (mention, ID, or #name).", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
83+
return;
84+
}
85+
86+
if (targetChannel.IsPublicChannel())
87+
{
88+
await originalMessage.Channel.SendMessageAsync("Refusing to purge a public channel.", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
89+
return;
90+
}
91+
92+
// Parse count from second token
5093
int requestedCount = 0;
51-
_ = int.TryParse(argText, out requestedCount);
94+
if (tokens.Length > 1)
95+
{
96+
_ = int.TryParse(tokens[1], out requestedCount);
97+
}
98+
99+
if (requestedCount <= 0)
100+
{
101+
await originalMessage.Channel.SendMessageAsync("Please specify a positive count (e.g., purgeold #my-channel 100).", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
102+
return;
103+
}
52104

53105
var deleteDelayMs = 5_000;
54106

55107
var cutoff = DateTimeOffset.UtcNow - TimeSpan.FromDays(30);
56108

57109
int deleted = 0;
58-
var channelBreakdown = new List<string>();
59-
await originalMessage.Channel.SendMessageAsync($"Purging up to {requestedCount} messages older than 30 days across private channels...", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
110+
await originalMessage.Channel.SendMessageAsync($"Purging up to {requestedCount} messages older than 30 days in #{targetChannel.Name}...", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
60111

61-
var guild = textChannel.Guild;
62-
var channels = await guild.GetTextChannelsAsync();
63-
foreach (var channel in channels.Where(c => !c.IsPublicChannel()))
112+
var channel = targetChannel;
113+
var pinned = await channel.GetPinnedMessagesAsync(options: token.ToRequestOptions());
114+
var pinnedIds = new HashSet<ulong>(pinned.Select(x => x.Id));
115+
116+
IMessage fromMessage = null;
117+
DateTimeOffset? lastPurgedTimestamp = null;
118+
int deletedInThisChannel = 0;
119+
while (deleted < requestedCount)
64120
{
65-
if (deleted >= requestedCount)
121+
IList<IMessage> messages = [];
122+
if (fromMessage == null)
66123
{
67-
break;
124+
Console.WriteLine($"Retrieving latest messages for #{channel.Name}...");
125+
messages = [.. await channel.GetMessagesAsync().FlattenAsync()];
126+
}
127+
else
128+
{
129+
Console.WriteLine($"Retrieving messages before {fromMessage.Id} for #{channel.Name}...");
130+
messages = [.. await channel.GetMessagesAsync(fromMessage, Direction.Before, 100, options: token.ToRequestOptions()).FlattenAsync()];
68131
}
69132

70-
var pinned = await channel.GetPinnedMessagesAsync(options: token.ToRequestOptions());
71-
var pinnedIds = new HashSet<ulong>(pinned.Select(x => x.Id));
133+
if (messages.Count == 0)
134+
{
135+
break;
136+
}
72137

73-
IMessage fromMessage = null;
74-
DateTimeOffset? lastPurgedTimestamp = null;
75-
int deletedInThisChannel = 0;
76-
while (deleted < requestedCount)
138+
foreach (var message in messages)
77139
{
78-
IList<IMessage> messages = [];
79-
if (fromMessage == null)
140+
fromMessage = message;
141+
142+
if (deleted >= requestedCount)
80143
{
81-
Console.WriteLine($"Retrieving latest messages for #{channel.Name}...");
82-
messages = [.. await channel.GetMessagesAsync().FlattenAsync()];
144+
break;
83145
}
84-
else
146+
147+
if (pinnedIds.Contains(message.Id))
85148
{
86-
Console.WriteLine($"Retrieving messages before {fromMessage.Id} for #{channel.Name}...");
87-
messages = [.. await channel.GetMessagesAsync(fromMessage, Direction.Before, 100, options: token.ToRequestOptions()).FlattenAsync()];
88-
}
149+
continue;
150+
}
89151

90-
foreach (var message in messages)
152+
if (message.Timestamp >= cutoff)
91153
{
92-
fromMessage = message;
93-
94-
if (deleted >= requestedCount)
95-
{
96-
break;
97-
}
98-
99-
if (pinnedIds.Contains(message.Id))
100-
{
101-
continue;
102-
}
103-
104-
if (message.Timestamp >= cutoff)
105-
{
106-
continue;
107-
}
108-
109-
try
110-
{
111-
var requestOptions = token.ToRequestOptions();
112-
requestOptions.RetryMode = RetryMode.Retry502 | RetryMode.RetryTimeouts;
113-
await message.DeleteAsync(options: requestOptions);
114-
deleted++;
115-
deletedInThisChannel++;
116-
lastPurgedTimestamp = message.Timestamp;
117-
}
118-
catch (Exception ex)
119-
{
120-
_logger.LogWarning(ex, "Failed to delete message {MessageId} in channel {ChannelId}", message.Id, channel.Id);
121-
}
122-
123-
await Task.Delay(deleteDelayMs, token);
124-
}
125-
}
154+
continue;
155+
}
126156

127-
if (deletedInThisChannel > 0)
128-
{
129-
channelBreakdown.Add($"#{channel.Name} ({deletedInThisChannel})");
130-
await originalMessage.Channel.SendMessageAsync($"Last message purged timestamp: {lastPurgedTimestamp.Value:O}", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
157+
try
158+
{
159+
var requestOptions = token.ToRequestOptions();
160+
requestOptions.RetryMode = RetryMode.Retry502 | RetryMode.RetryTimeouts;
161+
await message.DeleteAsync(options: requestOptions);
162+
deleted++;
163+
deletedInThisChannel++;
164+
lastPurgedTimestamp = message.Timestamp;
165+
}
166+
catch (Exception ex)
167+
{
168+
_logger.LogWarning(ex, "Failed to delete message {MessageId} in channel {ChannelId}", message.Id, channel.Id);
169+
}
170+
171+
await Task.Delay(deleteDelayMs, token);
131172
}
132173
}
133174

134-
var breakdownText = channelBreakdown.Count > 0 ? $" Deleted from: {string.Join(", ", channelBreakdown)}" : string.Empty;
135-
await originalMessage.Channel.SendMessageAsync($"Purged {deleted} message(s) across private channels.{breakdownText}", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
175+
if (deletedInThisChannel > 0)
176+
{
177+
await originalMessage.Channel.SendMessageAsync($"Last message purged timestamp: {lastPurgedTimestamp.Value:O}", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
178+
}
179+
180+
await originalMessage.Channel.SendMessageAsync($"Purged {deleted} message(s) in #{channel.Name}.", messageReference: new MessageReference(originalMessage.Id), options: token.ToRequestOptions());
136181
}
137182
}
138183
}

0 commit comments

Comments
 (0)