-- Copyright (C) 2023 Mjllnir#3515
-- This file is part of the "Not a Bot" application
-- For conditions of distribution and use, see copyright notice in LICENSE
-- API limitations on bulk deletion:
-- It is not possible to delete less than 2 messages or more than 100 messages at a time
-- It is not possible to delete messages older than 14 days
-- getMessagesBefore and getMessagesAfter have also a limitation to 100 messages at a time
local Date = Discordia.Date
local Enums = Discordia.enums
local MSG_TIME_LIMIT = 1209600 -- seconds
local NB_MSG_MAX_LIMIT = 100
Module.Name = "prune"
local function bulkDeleteChunks(channel, messagesChunks)
local nbDeletedMessages = 0
for _, chunk in ipairs(messagesChunks) do
if #chunk > 1 then
channel:bulkDelete(chunk)
nbDeletedMessages = nbDeletedMessages + #chunk
else
chunk[1]:delete()
nbDeletedMessages = nbDeletedMessages + 1
end
end
return nbDeletedMessages
end
local function hasValidDate(message)
local messageTimestamp = Date.fromSnowflake(message.id):toSeconds()
return os.difftime(os.time(), messageTimestamp) < MSG_TIME_LIMIT
end
local function hasManagePermission(member)
return member:hasPermission(Enums.permission.manageMessages)
end
function Module:bulkDeleteByNumber(commandMessage, nbMessages)
local channel = commandMessage.channel
local currentMessageId = commandMessage.id
local messagesToDelete = {}
local quotient = math.floor(nbMessages / NB_MSG_MAX_LIMIT)
local remainder = nbMessages - quotient * NB_MSG_MAX_LIMIT
for _ = 1, quotient do
local messages = channel:getMessagesBefore(currentMessageId, NB_MSG_MAX_LIMIT):toArray("id", hasValidDate)
if next(messages) ~= nil then
table.insert(messagesToDelete, messages)
currentMessageId = messages[1].id
end
end
if remainder > 0 then
table.insert(messagesToDelete, channel:getMessagesBefore(currentMessageId, remainder):toArray("id", hasValidDate))
end
return bulkDeleteChunks(channel, messagesToDelete)
end
function Module:bulkDeleteById(commandMessage, targetMessage)
local channel = commandMessage.channel
local currentMessageId = targetMessage.id
local messagesToDelete = {}
local messages
repeat
messages = channel:getMessagesAfter(currentMessageId, NB_MSG_MAX_LIMIT):toArray("id", hasValidDate)
if next(messages) ~= nil then
table.insert(messagesToDelete, messages)
currentMessageId = messages[#messages].id
end
until next(messages) == nil
-- This command is silent so we don't have to remove the command message here
table.remove(messagesToDelete[#messagesToDelete], #messagesToDelete[#messagesToDelete])
-- Delete also the selected message
if hasValidDate(targetMessage) then
table.insert(messagesToDelete[#messagesToDelete], targetMessage)
end
return bulkDeleteChunks(channel, messagesToDelete)
end
function Module:OnLoaded()
self:RegisterCommand({
Name = "prune",
Args = {
{ Name = "", Type = Bot.ConfigType.Integer }
},
PrivilegeCheck = hasManagePermission,
Help = function (guild) return Bot:Format(guild, "PRUNE_HELP") end,
Silent = true,
Func = function (commandMessage, nbMessages)
local guild = commandMessage.guild
local nbDeletedMessages = self:bulkDeleteByNumber(commandMessage, nbMessages)
local response = "";
if nbDeletedMessages ~= nbMessages then
response = string.format("%s\n", Bot:Format(guild, "PRUNE_CANNOT_DELETE"))
end
response = response .. Bot:Format(guild, "PRUNE_RESULT", nbDeletedMessages)
return commandMessage:reply(response)
end
})
self:RegisterCommand({
Name = "prunefrom",
Args = {
{ Name = "", Type = Bot.ConfigType.Message }
},
PrivilegeCheck = hasManagePermission,
Help = function (guild) return Bot:Format(guild, "PRUNEFROM_HELP") end,
Silent = true,
Func = function (commandMessage, targetMessage)
local guild = commandMessage.guild
local nbDeletedMessages = self:bulkDeleteById(commandMessage, targetMessage)
local response = "";
if not hasValidDate(targetMessage) then
response = string.format("%s\n", Bot:Format(guild, "PRUNE_CANNOT_DELETE"))
end
response = response .. Bot:Format(guild, "PRUNE_RESULT", nbDeletedMessages)
return commandMessage:reply(response)
end
})
return true
end