A Discord bot framework built with Gleam, heavily leveraging OTP
  • Gleam 98.8%
  • Erlang 1.2%
Find a file
graphiteisaac a8999cef48
Some checks failed
test / test (push) Has been cancelled
improve README
2026-05-12 11:44:20 +10:00
.github/workflows Hello Joe! 2026-05-04 12:01:32 +10:00
dev rename some interal rest stuff 2026-05-12 10:25:12 +10:00
src fix collectible decoder, silly me! 2026-05-12 11:27:28 +10:00
test Hello Joe! 2026-05-04 12:01:32 +10:00
.gitignore Hello Joe! 2026-05-04 12:01:32 +10:00
gleam.toml add guild caching via ETS 2026-05-07 14:04:01 +10:00
manifest.toml add guild caching via ETS 2026-05-07 14:04:01 +10:00
README.md improve README 2026-05-12 11:44:20 +10:00
run.log add guild caching via ETS 2026-05-07 14:04:01 +10:00

disco

A Gleam Discord bot framework, written to leverage OTP for high scalability.

At the current point in time, the library is very simple, only containing basic gateway events, a few REST functions,

Package Version Hex Docs

gleam add disco@1
import disco
import disco/client
import disco/rest
import disco/api
import gleam/otp/static_supervisor
import gleam/otp/actor
import gleam/bool
import gleam/int

pub fn main() -> Nil {
    let client = disco.new("insert your bot token here")
    |> disco.with_intents([
      client.GuildsIntent,
      client.GuildMessagesIntent,
      client.MessageContentIntent,
    ])

    let assert Ok(bot) = disco.supervised(client: client, handle_message: handle_message, initial_state: 0)
}

fn handle_message(
    state: #(gateway.GatewayState, Int),
    msg: gateway.GatewayEvent,
) {
    case msg {
        gateway.MessageCreate(api.Message(content:, channel_id:, author:, ..)) -> {
            // Guard against responding to our own messages (or any bot message)
            use <- bool.guard(author.bot, actor.continue(state))

            case content {
                "!add" -> {
                    // Voiding the error here... in production, you definitely should check the error.
                    let _ = rest.send_message(
                        state.0.client,
                        channel_id,
                        api.MessageContent(
                          text: "You have added 1 to the count",
                          embeds: [],
                        ),
                    )

                    actor.continue(#(state.0, state.1 + 1))
                }

                "!count" -> {
                    // Still just voiding... do as I say, not as I do! 
                    let _ = rest.send_message(
                        state.0.client,
                        channel_id,
                        api.MessageContent(
                          text: "The current count is " <> int.to_string(state.1),
                          embeds: [],
                        ),
                    )

                    actor.continue(#(state.0, state.1))
                }

                _ -> {
                    echo "We got a new non-command message from " <> author.name <> " that says " <> content
                    actor.continue(state)
                }
            }
        }
        _ -> actor.continue(state)
    }
}

Further documentation can be found at https://hexdocs.pm/disco.

Development

gleam run   # Run the project
gleam dev   # Run the example bot in the dev directory. Must provide BOT_TOKEN env variable
gleam test  # Run the tests