Warning
The bot is tailor-made for the Ghostty community and will most definitely be unsuitable for other servers. If you're looking to use similar features, you should consider looking for a more general-purpose bot, or forking this project and modifying it to suit your needs. That said, the core intent of this guide is to help contributors set up their development environment for building and testing new features. Contributions are the goal, not standalone usage.
- Go to the Discord Developer Portal.
- Click on the "New Application" button.
- Pick a name for your bot.
On your newly created bot's dashboard:
- Go to "Bot" on the sidebar.
- Click on the "Reset Token" button.
- Save the newly generated token for later.
- Under "Privileged Gateway Intents", enable:
- Server Members Intent
- Message Content Intent
- Go to "OAuth2" on the sidebar.
- Under "OAuth2 URL Generator", select the
botscope. - Under "Bot Permissions" that appears, choose the following permissions:
- Attach Files
- Manage Messages
- Manage Roles
- Manage Threads
- Manage Webhooks
- Send Messages
- Use External Apps
(your URL should contain a1125917892061184bitfield forpermissions)
- Use the generated URL at the bottom of the page to invite the bot to your server.
A GitHub token is necessary for the bot's Entity Mentions feature.
You can get one in two ways:
- On GitHub, go to Settings > Developer settings > Personal access tokens > Tokens (classic) > Generate new token, or use this link: Generate new token. As the bot only accesses public repositories, it doesn't require any scopes.
- If you have the
ghCLI installed and authenticated, rungh auth token.
Tip
This can be skipped if you're not going to interact with the bot's webhook feature.
The bot has a webhook feed feature which lets it stream customized GitHub repo activity to Discord channels. In order to set up a webhook stream from your repo:
- Go to https://smee.io/ (any webhook relay service should work, but smee.io is field-tested) and choose "Start a new channel".
- Copy the URL of your channel.
- Go to your GitHub repository, then Settings > Webhooks > Add webhook.
- In "Payload URL", paste in your channel's URL.
- Set "Content type" to
application/json. - Set a "Secret" (optional, but recommended). It can be any string.
- For "Which events would you like to trigger this webhook?", it's easiest to choose "Send me everything." because the bot's webhook client still only triggers for events specified by hooks in the code.
More resources:
- What are webhooks? — GitHub Docs
- Why is a secret recommended? — Monalisten Docs
The following text channels will be necessary:
#media#showcase#webhook#discussion-feed#botlog-everything
Additionally, a forum channel named #help is needed. It must have the
following tags:
- Moved to GitHub
- Solved
- Stale
- Duplicate
The following roles will be necessary (both requiring the Manage Messages permission):
modhelper
Create a config.toml file in the root of the project based on
config-example.toml. Below are explanations for all fields variable:
accept_invite_url: a URL to visit to accept the Ghostty invite- Channel/role IDs from step 4:
guild_id: the id of the server you prepared (optional; useful when your bot is in multiple servers).channels.hcb_feedchannels.helpchannels.help_tags: a table oftag_name→tag_idpairs. The tag names aremoved,solved,staleandduplicate.channels.mediachannels.showcasechannels.logpairs. The feed type names aremainanddiscussions.roles.modroles.helper
data_dir: a directory path for persistent statetokens.discord: the Discord bot token from step 1.tokens.github: the GitHub token from step 2.sentry_dsn: the Sentry DSN (optional).- Webhook environment variables from step 3 (if
you skipped that section, you can use the dummy values from
config-example.toml):webhook.url: the URL to receive events from.webhook.secret: a token for validating events (optional).webhook.channels: a table offeed_type→channel_idpairs.
.env files are also supported, although they have a lower priority than the
TOML file.
For temporary changes, you can set environment variables in the command-line or
use the CLI interface available under uv run -m app --help.
Since the TOML config has a nested structure, the environment variable names
indicate nesting with a double underscore, e.g. overriding roles.mod would be
done with the BOT__ROLES__MOD variable, and overriding channels.help_tags
would be done with the BOT__CHANNELS__HELP_TAGS variable.
The complete priority is, in decreasing order:
- CLI arguments
- Environment variables
__init__arguments.tomlconfig.envfiles
This bot runs on Python 3.14+ and is managed with uv. To get started:
-
Install uv.
-
Run the bot:
uv run -m app
-
After you've made your changes, run the required checks with just:
just check
These checks are enforced by CI, so make sure to fix any issues shown. You can format all code as required by running
just format, and fixable issues (including formatting issues) can have their fixes applied withjust fix.If you do not want to use just, you will have to run the checks manually, which is significantly more complicated.
-
There are a large number of checks to run. CI ensures that everything is formatted uniformly, so first run the formatters:
uv run taplo fmt pyproject.toml packages/*/pyproject.toml config-example.toml uv run ruff format uv run mdformat --number --wrap 80 *.md
These must be run from the project root.
-
Ghostty Bot is split into many packages. Ruff performs many checks on all packages at the same time. Run it from the project root:
uv run ruff check
-
The other checks do not work on all packages at the same time. Run these for every directory under
packages:cd packages/<package name> uv run basedpyright src tests uv run pytest tests uv run taplo fmt --check --diff pyproject.toml
For example:
cd packages/toolbox uv run basedpyright src tests uv run pytest tests uv run taplo fmt --check --diff pyproject.tomlDo not skip the checks for subpackages you did not modify, as subpackages may depend on each other.
-
The same checks must be run for the application itself, as changes to the subpackages may have broken the bot. Run these from the project root.
uv run basedpyright app tests uv run pytest tests
Note that the
basedpyrightcommand usesapp, notsrc.
-
Ghostty Bot's code is split into multiple packages:
- The main package, containing the bot's features and integration code.
toolbox, containing common code, utilities, and code to move messages.
All packages but the main one are in their own directory under packages/. The
main package, under app/, is structured as follows:
flowchart LR;
config{{config.py}} --> bot{{bot.py}}
config --> components(components/)
config --> main{{\_\_main__.py}}
log{{log.py}} --> main
bot --> main
bot --> components
components/is a place for all dedicated features (cogs), such as message filters or entity mentions. Most new features should become modules belonging to this package. Events (e.g.on_ready,on_message,on_error) should be defined within the component.bot.pycontains custom attributes and behaviors for the overall Discord bot and then loads extensions found incomponents.config.pyhandles reading and parsing the environment variables and the localconfig.tomlfile, and creates the GitHub client.log.pysets up logging and optionally Sentry.__main__.pyinitializes logging and starts the bot.