Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 110 additions & 5 deletions module_logs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
-- For conditions of distribution and use, see copyright notice in LICENSE

local Bot = Bot
local Discordia = Discordia
local Client = Client
local Clock = Discordia.Clock
local Date = Discordia.Date
local Discordia = Discordia


Module.Name = "logs"

local messagesToDelete = {}

function Module:GetConfigTable()
return {
{
Expand All @@ -24,10 +28,16 @@ function Module:GetConfigTable()
Optional = true
},
{
Name = "NicknameChangedLogChannel",
Description = "Where nickname changes should be logged",
Type = Bot.ConfigType.Channel,
Optional = true
Name = "EnableLogRotation",
Description = "Enable log rotation",
Type = Bot.ConfigType.Boolean,
Default = false
},
{
Name = "LogRetentionPeriod",
Description = "The retention period of logs",
Type = Bot.ConfigType.Duration,
Default = 2 * 365 * 24 * 60 * 60 -- 2 years
},
{
Name = "IgnoredDeletedMessageChannels",
Expand All @@ -36,6 +46,12 @@ function Module:GetConfigTable()
Array = true,
Default = {}
},
{
Name = "NicknameChangedLogChannel",
Description = "Where nickname changes should be logged",
Type = Bot.ConfigType.Channel,
Optional = true
},
{
Global = true,
Name = "PersistentMessageCacheSize",
Expand All @@ -46,6 +62,50 @@ function Module:GetConfigTable()
}
end


function Module:OnLoaded()
self.RotationClock = Clock()
self.RotationClock:on("day", function ()
self:ForEachGuild(function (guildId, config, data, persistentData)
local guild = Client:getGuild(guildId)
if (guild) then
local config = self:GetConfig(guild)
if not config.EnableLogRotation then
return
end

Module:RotateLogs(guild,
config.LogRetentionPeriod,
{
config.ChannelManagementLogChannel,
config.DeletedMessageChannel,
config.NicknameChangedLogChannel,
}
)
end
end)
end)

self.DeletionClock = Clock()
self.DeletionClock:on("sec", function ()
if next(messagesToDelete) then
table.remove(messagesToDelete):delete()
end
end)

return true
end

function Module:OnUnload()
self.RotationClock:stop()
self.DeletionClock:stop()
end

function Module:OnReady()
self.RotationClock:start()
self.DeletionClock:start()
end

function Module:OnEnable(guild)
local data = self:GetData(guild)

Expand Down Expand Up @@ -252,6 +312,7 @@ function Module:OnMessageDelete(message)

local desc = string.format("🗑️ **Deleted message - sent by %s in %s**\n",
message.author.mentionString, message.channel.mentionString)

local embed = Bot:BuildQuoteEmbed(message, { initialContentSize = #desc })
embed.description = desc .. (embed.description or "")
embed.footer = {
Expand Down Expand Up @@ -315,3 +376,47 @@ function Module:OnMessageCreate(message)
table.remove(cachedMessages, 1)
end
end

local function isMessageTooOld(message, logRetentionPeriod)
local messageTimestamp = Date.fromSnowflake(message.id):toSeconds()
return os.difftime(os.time(), messageTimestamp) > logRetentionPeriod
end

local function scheduleOldMessagesToDeletion(firstMessage, channel, logRetentionPeriod)
local buf = {}

repeat
-- Sort by id to keep the temporal order
local messages = channel:getMessagesAfter(firstMessage.id, 100):toArray("id")
table.insert(messages, 1, firstMessage)

for _, msg in ipairs(messages) do
if msg.author.id == Client.user.id
and (msg.embed and msg.embed.description and msg.embed.description:match("Deleted message"))
and isMessageTooOld(msg, logRetentionPeriod) then
table.insert(buf, msg)
end
end

firstMessage = messages[#messages]
until next(buf) or messages == nil

messagesToDelete = buf
end

function Module:RotateLogs(guild, logRetentionPeriod, logChannels)
local done = {}
for _, logChannelId in pairs(logChannels) do
if not done[logChannelId] then
local logChannel = guild:getChannel(logChannelId)
local firstMessage = logChannel:getFirstMessage()

if firstMessage then
scheduleOldMessagesToDeletion(firstMessage, logChannel, logRetentionPeriod)
end

done[logChannelId] = true
end
end
end