Skip to content

Bulk-moving messages #155

@00-kat

Description

@00-kat

This is quite similar to the fabled #18; as Ghostty is no longer in a private beta, there isn't much need to create the threads themselves anymore, but mostly just move them.

Update: with the new resurrection of threads, an option to create a new thread instead of using an existing channel/thread should also be implemented.

I've not figured out the exact details yet; I'll edit this as I do that. I also haven't determined the most convenient UI for this.

Some of these aren't necessarily related to bulk-moving, but for normally moving a single message too. While I should probably make a different issue for these, I'm lazy and am going to dump them here.

Checklist:

As these items are completed (or have PRs open), their corresponding details section will also be collapsed.

Nitro emoji handling

  1. Mimic “fake-nitro”; i.e. [emoji-name](link-to-emoji-in-discord-cdn).
    • These break down when you have text other than only a fake-nitro emoji in a message, but ¯\_(ツ)_/¯.
  2. Temporarily add them to the application, falling back to fake-nitro.
  3. Temporarily add them to the server, falling back to fake-nitro.

Update: approach three will not be tried, and approach two will be revisited after the next discord.py version arrives.

Stickers are not stored

Even the built-in Discord stickers aren't stored. Nitro stickers should be given the fake-nitro treatment described above; adding it to the server shall not be considered as there are very limited sticker slots available update: Webhooks cannot add stickers anyway.

VictimsUsers can't delete or edit moved messages

Update: deletion has been added in #219.

For editing, there can be a right click menu action that presents the user with a brief explanation and two buttons in an ephemeral message:

  • “Edit via modal” — displays a text input box that is pre-filled with the message contents, with an already-specified character limit.
  • “Edit in thread” — creates a private thread and pings the user in, along with a message telling the user to copy text and send a new one containing the text to replace it with. Uploaded attachments are added on too, if possible (maybe even add a tip about this to the info message). After ten minutes, a ping + warning is sent to the user, alerting them to press a button to avoid the thread being deleted, and after fifteen minutes or once the user sends an edited version the thread is deleted.
  • “Remove attachments” — edits the ephemeral message to contain a selection box of every attachment, allowing the user to pick one or more to remove them.

Editing via a thread allows for Markdown preview and easy mentioning of special items such as channels and users. It also allows the user to use emojis from the current server without Nitro.

Message.create_thread() throws a few exceptions; apart from Forbidden which can be handled just as it sounds like it should be handled (a notice telling the invoker to notify an admin), the only relevant code values of HTTPException are:

  • 160004 — A thread has already been created for this message.
  • 160006 — Maximum number of active threads reached.

(Taken from Discord's status code documentation.)

Their documentation does not seem to mention what the maximum number is, but somebody over at r/discordapp says the limit is 1000 across the whole server. (Note: this is only active threads; archived threads don't count to the limit.)

See also: Replaced Zig codeblocks can't be edited or deleted.

Timestamps are discarded

Neither the moved message's timestamp nor their edit time are stored.

Replies are not stored

  1. Fall-back to Markdown quote blocks, perhaps with a heading or two; this was the method used before Discord added replies.
  2. Use an embed (similar to most bridges and PluralKit).

Storing the link that it is replying to would likely be difficult as you need the new one if it's moved, and the old one if it's not. Even worse if it's moved after the fact.

Revision of the above sentence: after some discussion with @trag1c, storing the link will likely not be particularly problematic but only when bulk-moving. That means, replies may have a dead link if one does not bulk-move them all in the same go.

Forwards are not stored

Discord does not support nesting block-quotes, so I'm not sure what to do here. Perhaps faking block quotes with box-drawing characters might work...
Any link people in the server that the message was forwarded from would not be preserved; not much can be done on this front.
Some code that was removed in #154 will need to be reintroduced if this is to be done.

Update: it seems like it is not possible to fetch the content of forwards from servers that the bot is not in.

Note to self: one test case to try is:

  1. Send a message.
  2. Forward it.
  3. Edit the original message.
  4. Move the forward.

Poll movement is lossy

Votes aren't moved

If Webhooks can vote, this can be done. Otherwise, this cannot be done.

Update: Webhooks can't vote.

Relevant documentation links: discord.Poll.answers, discord.PollAnswer.voters.

The timer is reset

Not sure if anything can be done about this one.
If the previous one can't be done, this should not be done to give people time to vote again.

Update: the previous one cannot be done.

Moving closed polls errors

Full traceback
2025-02-16 16:15:14 ERROR    discord.ui.view Ignoring exception in view <SelectChannel timeout=180.0 children=1> for item <ChannelSelect placeholder='Select a channel' min_values=1 max_values=1 disabled=False>
Traceback (most recent call last):
  File "/path/to/bot/env/lib/python3.12/site-packages/discord/ui/view.py", line 430, in _scheduled_task
    await item.callback(interaction)
  File "/path/to/bot/app/components/move_message.py", line 49, in select_channel
    await move_message_via_webhook(
  File "/path/to/bot/app/utils/webhooks.py", line 52, in move_message_via_webhook
    msg = await webhook.send(
          ^^^^^^^^^^^^^^^^^^^
  File "/path/to/bot/env/lib/python3.12/site-packages/discord/webhook/async_.py", line 1843, in send
    data = await adapter.execute_webhook(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/bot/env/lib/python3.12/site-packages/discord/webhook/async_.py", line 223, in request
    raise HTTPException(response, data)
discord.errors.HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
In poll.duration: int32 value should be greater than or equal to 1.

This can probably be mitigated by checking if the poll has ended and fixing up the metadata (probably discord.Poll.expires_at) if so then calling discord.Poll.end on the new poll.

Update: this cannot be fixed as polls created by Webhooks cannot be ended manually.
Closed polls will be ignored in #165 to fix the traceback, however.

Ghostty Bot deletes its Zig code blocks and entity mentions

Moving a message like #1234 results in Ghostty Bot deleting its entity mention before people have a chance to move Ghostty Bot's message. It also does not bring back the entity mention in the new place.

If the entity mention or code block is to be recreated rather than moved automatically, reactions to the old entity mention or code block ought to be preserved too.

Interaction views are not stored

Moving a Zig code block or an entity mention removes the views it has. This probably isn't fixable in a general capacity, but it would be nice if Ghostty Bot's own views were special-cased.

Replaced Zig codeblocks can't be edited or deleted

This is split off from VictimsUsers can't delete or edit moved messages.

Currently, the replace button on Zig codeblocks sets include_move_marks=False, which means that its subtext does not contain the author and none of the delete or edit code will work with it. The easiest way to work around this is to link the Zig codeblock message to the author for an hour (or more, to decide) and construct a MovedMessage from a raw author (and make SplitSubtext work with not having a subtext) before continuing.

An in-memory map is reasonable for replaced Zig codeblocks but not for moved messages because the author is the only one who can replace a Zig codeblock so they are almost certainly able to edit the message fast enough, while any moderator can move a message and the author may not see it until a considerable amount of time passes. Of course, this solution isn't perfect either and it would be nice if Zig codeblocks could be edited whenever, but this is the current best idea that I have.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions