thegamecracks / theticketbot

A ticket bot built on Discord's native threads.
MIT License
2 stars 0 forks source link

Automatically lock tickets when archived by staff #5

Closed thegamecracks closed 4 months ago

thegamecracks commented 4 months ago

I suspect that most of the time when staff wants to close a ticket, they also want to lock it so the ticket owner can't re-open the thread. This requires two interactions, one to lock the thread and another to close the thread, which can be somewhat inconvenient for staff and allows for accidentally leaving a ticket unlocked.

A workaround to this is removing the ticket owner so the bot automatically locks and archives it, but this requires having the Manage Threads permission which may not be available for all staff, particularly if there are multiple inboxes in one channel and that staff member shouldn't be allowed to see all of them.

To make this easier on staff, the bot should lock the thread for them if it is granted the Manage Threads permission.

thegamecracks commented 4 months ago

Here is a possible implementation:

# cogs/inbox.py
@commands.Cog.listener("on_raw_thread_update")
async def lock_archived_tickets(self, payload: discord.RawThreadUpdateEvent):
    guild_id = payload.guild_id
    thread_id = payload.thread_id

    guild = self.bot.get_guild(guild_id)
    if guild is None:
        return log.warning("Ignoring unknown guild %d", guild_id)

    if int(payload.data["owner_id"]) != guild.me.id:
        return

    thread_metadata = payload.data["thread_metadata"]
    if not thread_metadata["archived"] or thread_metadata.get("locked"):
        return

    channel_id = int(payload.data["parent_id"])
    channel = guild.get_channel(channel_id)
    if channel is None:
        return log.warning(
            "Ignoring unknown parent %d for thread %d",
            channel_id,
            thread_id,
        )

    permissions = channel.permissions_for(guild.me)
    if not permissions.manage_threads:
        return

    async with self.bot.acquire() as conn:
        row = await conn.fetchone("SELECT 1 FROM ticket WHERE id = ?", thread_id)
        if row is None:
            return

    await self.bot.http.edit_channel(thread_id, archived=False)
    await self.bot.http.edit_channel(thread_id, archived=True, locked=True)

Since threads are removed from cache before the thread update event fires, this uses the raw data and http methods instead.

An issue with this is that Discord seems to have strict ratelimits on editing threads. As such, re-opening the thread so the bot can lock it results in the thread appearing to close, re-open for a few seconds, and then close again. Adding a 10 second delay might make it look better, but it might be possible for the ticket owner or staff to re-opening during that time period.

thegamecracks commented 4 months ago

An issue with this is that Discord seems to have strict ratelimits on editing threads. As such, re-opening the thread so the bot can lock it results in the thread appearing to close, re-open for a few seconds, and then close again.

I forgot you could simply send a message to un-archive the thread before locking it, making it an easy workaround. 😊