22-- This file is part of the "Not a Bot" application
33-- For conditions of distribution and use, see copyright notice in LICENSE
44
5- local client = Client
6- local discordia = Discordia
7- local bot = Bot
8- local enums = discordia .enums
5+ local Bot = Bot
6+ local Discordia = Discordia
7+ local Clock = Discordia .Clock
8+ local Date = Discordia .Date
9+
910
1011Module .Name = " logs"
1112
13+ local messagesToDelete = {}
14+
1215function Module :GetConfigTable ()
1316 return {
1417 {
1518 Name = " ChannelManagementLogChannel" ,
1619 Description = " Where channel created/updated/deleted should be logged" ,
17- Type = bot .ConfigType .Channel ,
20+ Type = Bot .ConfigType .Channel ,
1821 Optional = true
1922 },
2023 {
2124 Name = " DeletedMessageChannel" ,
2225 Description = " Where deleted messages should be logged" ,
23- Type = bot .ConfigType .Channel ,
26+ Type = Bot .ConfigType .Channel ,
2427 Optional = true
2528 },
29+ {
30+ Name = " EnableLogRotation" ,
31+ Description = " Enable log rotation" ,
32+ Type = Bot .ConfigType .Boolean ,
33+ Default = false
34+ },
35+ {
36+ Name = " LogRetentionPeriod" ,
37+ Description = " The retention period of logs" ,
38+ Type = Bot .ConfigType .Duration ,
39+ Default = 2 * 365 * 24 * 60 * 60 -- 2 years
40+ },
2641 {
2742 Name = " IgnoredDeletedMessageChannels" ,
2843 Description = " Messages deleted in those channels will not be logged" ,
29- Type = bot .ConfigType .Channel ,
44+ Type = Bot .ConfigType .Channel ,
3045 Array = true ,
3146 Default = {}
3247 },
3348 {
3449 Name = " NicknameChangedLogChannel" ,
3550 Description = " Where nickname changes should be logged" ,
36- Type = bot .ConfigType .Channel ,
51+ Type = Bot .ConfigType .Channel ,
3752 Optional = true
3853 },
3954 {
4055 Global = true ,
4156 Name = " PersistentMessageCacheSize" ,
4257 Description = " How many of the last messages of every text channel should stay in bot memory?" ,
43- Type = bot .ConfigType .Integer ,
58+ Type = Bot .ConfigType .Integer ,
4459 Default = 50
4560 },
4661 }
4762end
4863
64+
65+ function Module :OnLoaded ()
66+ self .RotationClock = Clock ()
67+ self .RotationClock :on (" day" , function ()
68+ self :ForEachGuild (function (guildId , config , data , persistentData )
69+ local guild = Client :getGuild (guildId )
70+ if (guild ) then
71+ local config = self :GetConfig (guild )
72+ if not config .EnableLogRotation then
73+ return
74+ end
75+
76+ Module :RotateLogs (guild ,
77+ config .LogRetentionPeriod ,
78+ {
79+ config .ChannelManagementLogChannel ,
80+ config .DeletedMessageChannel ,
81+ config .NicknameChangedLogChannel ,
82+ }
83+ )
84+ end
85+ end )
86+ end )
87+
88+ self .DeletionClock = Clock ()
89+ self .DeletionClock :on (" sec" , function ()
90+ if next (messagesToDelete ) then
91+ table.remove (messagesToDelete ):delete ()
92+ end
93+ end )
94+
95+ return true
96+ end
97+
98+ function Module :OnUnload ()
99+ self .RotationClock :stop ()
100+ self .DeletionClock :stop ()
101+ end
102+
103+ function Module :OnReady ()
104+ self .RotationClock :start ()
105+ self .DeletionClock :start ()
106+ end
107+
49108function Module :OnEnable (guild )
50109 local data = self :GetData (guild )
51110
@@ -96,7 +155,7 @@ function Module:OnChannelDelete(channel)
96155 embed = {
97156 title = " Channel deleted" ,
98157 description = channel .name ,
99- timestamp = discordia . Date ():toISO (' T' , ' Z' )
158+ timestamp = Date ():toISO (' T' , ' Z' )
100159 }
101160 })
102161end
@@ -123,7 +182,7 @@ function Module:OnChannelCreate(channel)
123182 embed = {
124183 title = " Channel created" ,
125184 description = " <#" .. channel .id .. " >" ,
126- timestamp = discordia . Date ():toISO (' T' , ' Z' )
185+ timestamp = Date ():toISO (' T' , ' Z' )
127186 }
128187 })
129188end
@@ -154,7 +213,7 @@ function Module:OnMemberUpdate(member)
154213 embed = {
155214 title = " Nickname changed" ,
156215 description = string.format (" %s - `%s` → `%s`" , member .mentionString , data .nicknames [member .id ], member .name ),
157- timestamp = discordia . Date ():toISO (' T' , ' Z' )
216+ timestamp = Date ():toISO (' T' , ' Z' )
158217 }
159218 })
160219 end
@@ -164,7 +223,7 @@ function Module:OnMemberUpdate(member)
164223 embed = {
165224 title = " Username changed" ,
166225 description = string.format (" %s - `%s` → `%s`" , member .mentionString , data .usernames [member .id ], member .user .username ),
167- timestamp = discordia . Date ():toISO (' T' , ' Z' )
226+ timestamp = Date ():toISO (' T' , ' Z' )
168227 }
169228 })
170229 end
@@ -199,7 +258,7 @@ function Module:OnMessageDelete(message)
199258 embed .footer = {
200259 text = string.format (" Author ID: %s | Message ID: %s" , message .author .id , message .id )
201260 }
202- embed .timestamp = discordia . Date ():toISO (' T' , ' Z' )
261+ embed .timestamp = Date ():toISO (' T' , ' Z' )
203262
204263 logChannel :send ({
205264 embed = embed
@@ -231,7 +290,7 @@ function Module:OnMessageDeleteUncached(channel, messageId)
231290 footer = {
232291 text = string.format (" Message ID: %s" , messageId )
233292 },
234- timestamp = discordia . Date ():toISO (' T' , ' Z' )
293+ timestamp = Date ():toISO (' T' , ' Z' )
235294 }
236295 })
237296end
@@ -257,3 +316,47 @@ function Module:OnMessageCreate(message)
257316 table.remove (cachedMessages , 1 )
258317 end
259318end
319+
320+ local function isMessageTooOld (message , logRetentionPeriod )
321+ local messageTimestamp = Date .fromSnowflake (message .id ):toSeconds ()
322+ return os.difftime (os.time (), messageTimestamp ) > logRetentionPeriod
323+ end
324+
325+ local function scheduleOldMessagesToDeletion (firstMessage , channel , logRetentionPeriod )
326+ local buf = {}
327+
328+ repeat
329+ -- Sort by id to keep the temporal order
330+ local messages = channel :getMessagesAfter (firstMessage .id , 100 ):toArray (" id" )
331+ table.insert (messages , 1 , firstMessage )
332+
333+ for _ , msg in ipairs (messages ) do
334+ if msg .author .id == Client .user .id
335+ and (msg .embed and msg .embed .description and msg .embed .description :match (" Deleted message" ))
336+ and isMessageTooOld (msg , logRetentionPeriod ) then
337+ table.insert (buf , msg )
338+ end
339+ end
340+
341+ firstMessage = messages [# messages ]
342+ until next (buf ) or messages == nil
343+
344+ messagesToDelete = buf
345+ end
346+
347+ function Module :RotateLogs (guild , logRetentionPeriod , logChannels )
348+ local done = {}
349+ for _ , logChannelId in pairs (logChannels ) do
350+ if not done [logChannelId ] then
351+ local logChannel = guild :getChannel (logChannelId )
352+ local firstMessage = logChannel :getFirstMessage ()
353+
354+ if firstMessage then
355+ scheduleOldMessagesToDeletion (firstMessage , logChannel , logRetentionPeriod )
356+ end
357+
358+ done [logChannelId ] = true
359+ end
360+ end
361+ end
362+
0 commit comments