Skip to content

About

Discord Bot source code for ArchiveTune server

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ArchiveTune Bot

English · 日本語 · Tiếng Việt

ArchiveTune Bot is a focused Discord bot written in Go for managing tags and posts in Discord Forum Channels. The bot has no database and does not process content with AI. Discord remains the source of truth, while config.yaml stores channel and bot settings.

Discord represents a Forum post as a thread. Available Forum tags are stored on the parent channel, while the tags applied to an individual post are stored on the thread.1 2 This bot therefore uses the Gateway for realtime events and the Discord REST API for deterministic post and tag operations.

Current scope

Area Behavior
Forum synchronization Synchronizes guidelines, configured tags, and the Require Tags when posting flag.
Automatic suggestion tagging Adds Maybe to every new untagged post in the configured suggestion channel.
Tag management Adds, removes, or replaces tags on managed posts.
Post management Renames, archives, unarchives, locks, and unlocks posts where the command requires it.
Moderation workflows Provides status commands for issues and suggestions, including author mentions and rejection reasons.
Deployment Supports a Go binary, Docker, or systemd. Secrets are read from a local configuration file and are not committed.
Presence Shows the custom status Why do I exist? with Do Not Disturb (DND) presence.

The bot intentionally does not provide AI moderation, reaction management, history storage, or content-based automatic classification. Its boundary is deterministic Forum tag and post management.

Managed channels

Channel ID Special behavior
issues 1498327801923637439 .solved, .false, and .false-report are restricted to this Forum Channel. Successful .solved and .false operations mention the post author.
suggestion 1498328044635422790 New untagged posts automatically receive Maybe. /fix-suggestion, .dupe, .done, .in-progress, .exist, .reject, .tba, and .tbd operate here.

Slash commands

Command Usage Behavior
/help Available in any channel. Shows the complete command list in an Embed.
/forum-sync channel:<Forum Channel> Run after changing config.yaml. Synchronizes guidelines, tags, and the required-tag flag.
/fix-suggestion Moderator command. Scans active and archived accessible suggestion posts and adds Maybe to posts with no applied tag.
/tag-add tag:<name> Run in a post, or provide post_id. Adds a configured tag without replacing existing tags.
/tag-remove tag:<name> Run in a post, or provide post_id. Removes a configured tag.
/post-rename name:<new name> Run in a post, or provide post_id. Renames a managed post.
/post-state state:<open|close|lock|unlock> Run in a post, or provide post_id. Changes the archive or lock state of a managed post.
/close Run in your own suggestion post. Closes and deletes the suggestion post (OP only).
/ytd url:<YouTube URL> type:<video|audio|thumbnail> [quality:<format id>] Available to all members. Omit quality to open an interactive selector first. Downloads the selected media with yt-dlp and returns a temporary temp.sh link.

Prefix commands

Prefix commands are typed as normal messages. They require the Message Content Intent and moderator access, except .help and .ytd, which are available to all members. All command responses, including success, error, usage, and permission messages, are sent as branded ArchiveTune Bot Embeds.

Command Behavior
.help Shows the complete command list in an Embed.
.close Close and delete your suggestion post (OP only).

Issue commands

Command Behavior
.solved In issues, replaces the current workflow tag with Solved, locks the post, and renames it to [SOLVED] <old name>. The bot mentions the author.
.false In issues, replaces the current workflow tag with False report, locks the post, and renames it to [FALSE REPORT] <old name>. The bot mentions the author.
.false-report Full-name alias for the False report workflow in issues.

Suggestion commands

Command Usage Behavior
.dupe <post link or message link> Requires a Discord post/message link. Replaces all existing tags with Duplicate, closes and locks the target suggestion post, renames it to [DUPLICATE] <old name>, and mentions the target author.
.done Run directly inside the current suggestion post. Replaces all existing tags with Done, closes and locks the post, and renames it to [DONE] <old name>.
.in-progress Run directly inside the current suggestion post. Replaces all existing tags with In Progress..., closes and locks the post, and renames it to [IN PROGRESS] <old name>.
.exist Run directly inside the current suggestion post. Replaces all existing tags with Already exist, closes and locks the post, renames it to [ALREADY EXIST] <old name>, and mentions the author.
.reject <reason> Run directly inside the current suggestion post. Replaces all existing tags with Reject, closes and locks the post, renames it to [REJECTED] <old name>, mentions the author, and sends the rejection reason in a text code block. The reason is required, limited to 1,000 characters, and its mentions are disabled.
.tba Run directly inside a suggestion post. Removes all existing tags and keeps only TBA. It does not close, lock, or rename the post.
.tbd Run directly inside a suggestion post. Removes all existing tags and keeps only TBD. It does not close, lock, or rename the post.
.accept Run directly inside a suggestion post. Replaces all existing tags with Accept, closes and locks the post, and renames it to [ACCEPTED] <old name>.
.accepted Run directly inside a suggestion post. Alias for .accept.
.maybe Run directly inside a managed post. Applies the configured tag, locks the post, and adds the corresponding status prefix.
.ytd <YouTube URL> type:<video|audio|thumbnail> [quality:<format id>] Available to all members. Omit quality to inspect formats first. Downloads media with yt-dlp and returns a temporary temp.sh link.

.done, .in-progress, .exist, .reject, .tba, and .tbd do not require a link. .dupe accepts either of the following forms:

YouTube downloader

The prefix command .ytd and slash command /ytd accept YouTube and YouTube Music URLs. Supported types are video, audio, and thumbnail:

.ytd https://youtu.be/dQw4w9WgXcQ?si=example type:video
.ytd https://youtu.be/dQw4w9WgXcQ?si=example type:audio quality:251
.ytd https://youtu.be/dQw4w9WgXcQ?si=example type:thumbnail

For video or audio, omit quality first to receive an interactive quality selector. Choose a format from the dropdown, then press Download; the bot does not start downloading until you confirm. You may still provide a format ID or quality:best directly when using the command. By default, playlist and album URLs are rejected before yt-dlp inspection or download, including YouTube URLs with a list parameter and YouTube Music collection pages. This behavior is controlled by ytd.block_playlist_album_download, which defaults to true. Set it to false only when you intentionally want yt-dlp to handle collection URLs; the bot still expects exactly one output file. Before a single media download starts, the bot checks free space in the media work directory and refuses the job when the available space is below the estimated requirement plus a safety margin. Video downloads select the highest available video and audio combination by default; audio downloads preserve the best available source and embed metadata and thumbnail when supported. Thumbnail mode downloads the source thumbnail without media conversion. Files are named using the sanitized media title and type, such as My_Song_audio.opus, My_Video_video.mp4, or My_Video_thumbnail.webp. For YouTube Music audio, the filename uses the track metadata format Song_Title - Artist.opus without the _audio suffix. The original extension selected by yt-dlp is preserved. Files are uploaded to temp.sh, which currently states a 4GB maximum and three-day expiry; treat returned links as temporary. The bot retries transient network errors and HTTP 408, 425, 429, and 5xx responses up to three attempts with exponential backoff; permanent client errors are returned immediately.

The Pterodactyl startup script automatically installs local yt-dlp and static ffmpeg under .tools/media when they are missing. Set AUTO_INSTALL_MEDIA_TOOLS=0 to disable this behavior and provide YTDLP_BIN and FFMPEG_BIN yourself.

https://discord.com/channels/<guild_id>/<post_id>
https://discord.com/channels/<guild_id>/<post_id>/<message_id>

The bot validates the guild ID and refuses to process a .dupe target that is not in the configured suggestion Forum Channel.

Prefix autocorrection

Set prefix_autocorrect: true to let the bot correct a close typo when there is exactly one unambiguous command within prefix_max_distance. For example, .sloved is not a registered alias, but it can be recognized as a typo for .solved. The bot reports the correction before applying the command. If multiple commands are equally close, it does nothing to avoid an unsafe guess.

Configuration

Copy the sample configuration and fill in the bot token and guild ID:

cp config.example.yaml config.yaml

The sample already contains the configured issues and suggestion channel IDs and the tag names shown in the server setup. Never commit config.yaml or share the bot token.

bot_token: "replace-with-discord-bot-token"
guild_id: "replace-with-server-id"
moderator_role_ids: []
replace_existing_tags: false
prefix_autocorrect: true
prefix_max_distance: 2
ytd:
  block_playlist_album_download: true

replace_existing_tags: false preserves tags that already exist in Discord and updates or appends declared tags. Set it to true only when the configured list should become the complete list of available tags for a channel. On startup, the bot automatically adds known missing configuration defaults, such as the ytd block and block_playlist_album_download: true, without overwriting any existing value. The update is atomic and preserves existing YAML comments where possible; if the config file is not writable, startup stops with a configuration error.

Tag emoji configuration

The emoji field accepts normal Unicode emoji and Discord custom emoji markup. Static custom emoji use <:name:id>, while animated custom emoji use <a:name:id>:

- name: "ArchiveTune Version"
  emoji: "<:02V:1520005999040266240>"

- name: "Animated Status"
  emoji: "<a:loading:1520005999040266240>"

The parser also accepts the explicit form emoji, emoji_id, and emoji_animated. When custom emoji markup is used, the bot extracts the ID and sends only Discord's emoji_id Forum Tag field; emoji_name is left empty because Discord rejects both fields in the same tag payload. Unicode emoji are sent through emoji_name. Do not use emoji_animated: true for a static emoji.

Create the Discord application

Create an Application and Bot User in the Discord Developer Portal. Invite the bot with the bot and applications.commands scopes.

The bot should be able to see both managed Forum Channels and should have at least the following permissions:

Permission Purpose
View Channel Read Forum Channels and posts.
Send Messages in Threads Send command results and moderation notices.
Manage Threads Rename, archive, unarchive, lock, unlock, and manage posts.
Manage Channels Update guidelines, available tags, and the Require Tags when posting flag.

Enable Message Content Intent under the Bot settings. Without it, slash commands continue to work, but prefix commands cannot read message content. The bot must also be invited with the applications.commands scope so guild slash commands can be registered.

Run locally

Go 1.22 or newer is required.

git clone https://github.com/sang765/discord-forum-bot.git
cd discord-forum-bot
cp config.example.yaml config.yaml
# edit config.yaml
make test
make vet
make build
./bin/discord-forum-bot

Use another configuration path with:

CONFIG_FILE=/path/to/config.yaml ./bin/discord-forum-bot

When the bot receives Ready, it overwrites the guild slash-command set and synchronizes all configured Forum Channels. After changing tags or guidelines, restart the bot or run /forum-sync for the relevant channel.

Docker

docker build -t discord-forum-bot:latest .
docker run --rm \
  -e CONFIG_FILE=/app/config.yaml \
  -v "$PWD/config.yaml:/app/config.yaml:ro" \
  discord-forum-bot:latest

Pterodactyl startup script

The repository includes run.sh and the unified install-dependencies.sh. Set the Pterodactyl startup command to ./${EXECUTABLE}, set EXECUTABLE to run.sh, and set GO_PACKAGE to ./cmd/bot. The startup script changes to the project directory, uses the container Go compiler when available, and otherwise calls the unified installer for Go, yt-dlp, and ffmpeg as needed. It then builds a fresh Linux binary and starts it with CONFIG_FILE=./config.yaml. If installation is unavailable, it falls back to an existing executable ./discord-forum-bot. The installer can also be run manually as ./install-dependencies.sh all, ./install-dependencies.sh go, or ./install-dependencies.sh media.

Startup Command: ./\${EXECUTABLE}
GO PACKAGE: ./cmd/bot
EXECUTABLE: run.sh

This allows source edits made through the Pterodactyl File Manager to be picked up on the next restart without manually running go build. The first start needs outbound HTTPS access to go.dev and enough disk space to store the user-local toolchain under .tools/go; later starts reuse it. If the container has no network access, upload a prebuilt executable named discord-forum-bot instead.

For a hands-off source update, upload a .zip archive to the project root and restart the server. run.sh selects the newest ZIP, validates and extracts it, removes the old source, preserves run.sh, config.yaml, and .tools, and deletes root ZIP files after a successful replacement. A malformed or unsafe archive is rejected without deleting the current source.

The .tools/go directory contains the local Go toolchain. Go module downloads and compiler caches are stored under .tools/go-work, while .tools/.discord-forum-bot-build-fingerprint records the source state. When source files have not changed, restarts reuse the existing binary and do not rebuild or download dependencies again.

systemd

The repository includes deploy/discord-forum-bot.service. The following is an example installation on a Linux host:

sudo useradd --system --home /opt/discord-forum-bot --shell /usr/sbin/nologin discordbot
sudo mkdir -p /opt/discord-forum-bot
sudo chown -R discordbot:discordbot /opt/discord-forum-bot
sudo cp deploy/discord-forum-bot.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now discord-forum-bot
sudo journalctl -u discord-forum-bot -f

Deployment options

Option Trade-off Cost Setup complexity
Existing personal machine or Go-compatible server No new infrastructure cost, but the machine must stay online and the operator manages restarts, updates, and security. None if hardware already exists. Low to medium.
Cloud server or Docker/Go-compatible platform Independent 24/7 operation, but requires management of hosting cost, secrets, and operating-system updates. Depends on the provider. Medium.

The binary, Dockerfile, and systemd unit are portable across both options.

Testing

The project includes unit tests for configuration validation, tag merging, command parsing, link parsing, status restrictions, archived-thread pagination, and rejection reasons.

GOTOOLCHAIN=local go test ./...
GOTOOLCHAIN=local go vet ./...
GOTOOLCHAIN=local go build ./cmd/bot

Live testing requires a Discord token and a test server. Do not put secrets in source code or commit them to Git.

References

About

Discord Bot source code for ArchiveTune server

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages