Discord

One Discord bot for every project, where its token comes from, how a project binds a server, the operator and mates audiences, channels, roles and member cards, and the discord page.

01one bot#

The Discord bridge is one bot that serves every project that binds a server of its own. The binding is in the project's local half. A record in a chat posts only to the channel its project's binding maps to that chat. A chat with no channel, or a project whose box is off, posts nothing anywhere.

A press, a slash command or a channel message comes back through one check. The server it came from must be bound, the card it names must be that server's project's, and the person must be the operator or a member with the right. A press from Discord is the same press as in the window, and the thread says "(from discord)". The operator's direct messages talk to discord.chat.

The switch is one session for every bound server. discord.enabled absent means on. The bridge asks for the privileged intents, and if Discord refuses them it falls back to cards and commands.

02the token and desk.json#

The desk.json block discord is the one bot every bound project shares, and its operator. Its keys are in desk-json: operator, token_env, token_file, chat and enabled. The environment variable is in env-vars.

The block holds no token. DiscordToken reads it like this:

  1. With no discord block, or with discord.operator empty, the feature is off, and the reason says which.
  2. The variable named by token_env is read, and SMITH_DISCORD_BOT_TOKEN when token_env is empty (DefaultDiscordTokenEnv). A non-empty value wins.
  3. Otherwise token_file is read, and its trailing line breaks are trimmed. An unset, unreadable or empty file leaves the feature off.

The reason text never contains the token. When a connect fails, the token in the message is replaced with [token].

discord_state tells the conductor in plain words whether the bridge is on with chat, on with cards only (Discord refused message content), off, no token, or not set up.

03a project binds a server#

A project's discord block is in its local half, so it is machine-local. See projects. The conductor can't write it directly: it drafts the change with propose_config_edit. The fields:

A conductor's posting tool is refused when the project is bound but both free chat and gate cards are off.

04audiences#

audience says who reads the server's channel chats: operator (the default, and what empty means) or mates, people who aren't technical. Anything else is refused.

The audience sets the turn tag of a message that reaches the conductor:

tag when
[via discord for mates] a channel chat in a server whose audience is mates, and not the DMs' chat
[via discord] the message came from Discord, or the chat is the DMs' chat, or a channel's chat in a bound server
none a turn in the window

The reply style for mates is in the conductor's brief: plain words in one or two lines, no ids, hashes, tool or command names, file paths or json, and a choice on a card is pointed at by its buttons. See conductor. The reply style for the operator audience is not checked here.

05members#

A member is a person of the server who may act. research covers /research, go on a research card and approving a confirm gate. read covers /status and /tickets. The fields are id, name, research, read, added_by and added_at. A gate other than confirm is the operator's.

06channels and the conductor's tools#

The conductor builds in its own project's server. There is no server or project to pass: both come from the calling chat, and every id is looked up in that server first, so another project's ids are refused. Each done call writes one [discord] line in the chat.

tool what it does
discord_channels lists the server's channels by category, threads under their channel, each one's chat, and the project's chats to map to. Changes nothing
discord_read reads a channel's or thread's messages, oldest first. limit is 20 by default and at most 100, before pages back
discord_post posts text, cut at line breaks, and an optional embed. Returns the last message's id
discord_channel_new makes a text channel or a category, with an optional category, topic and chat to map to
discord_thread_new starts a public thread in a channel, on a message or none
discord_topic sets a channel's topic
discord_pin pins a message
discord_react reacts to a message
discord_state whether the bridge is on

(;.) These are chores: the conductor does them directly. Building is a chore, and destroying and rights are the operator's click on a card.

07the cards#

Six propose_discord* cards. The conductor drafts one and the operator's click does it (;):

card what the click does
propose_discord switches the bridge on or off. chat is needed to switch on when none is set
propose_discord_chat sets which chat the operator's DMs talk to, or unbinds it with an empty chat. It never switches the bridge on or off
propose_discord_delete deletes a channel or thread (channel), or one message in it (message)
propose_discord_roles act is create, edit, assign, unassign or overwrite, with the fields each needs. Permission names are those on the discord page
propose_discord_member kicks someone, or bans them with ban true. reason goes to Discord's audit log
propose_discord_server renames the server

For every card, see cards. Autopilot presses none of them.

08the discord page#

The page is #/p/<project>/discord. It shows the binding, the bridge's cached look at the server and the project's chats for the map. It asks Discord nothing when it loads. A project with its box on has a discord item in the menu, with the bridge's state as a dot.

A bound server has six checks: bot token, bot in server, message content, server members, commands and posting. Each is ok, fail, warn or not run, with what it found and how to fix a failure. The two intents are privileged and set in the Discord developer portal. Posting is only tested when the operator presses test.

The page lists the permissions the bot asks for on invite, from one list. Among them, manage roles is for roles and overwrites, kick members and ban members for propose_discord_member, and manage server for renaming. Granting a permission is not what lets the conductor act without a click: that line is drawn by whether a route is a chore or a press.

The slash commands are /research, /status and /tickets (names them under the use application commands permission).