glyde
glyde is a sans-IO Discord library that brings its own IO. glyde/gateway
is the protocol state machine; this module is the supervision tree already
wired to it, so a bot is the handlers and nothing else.
import gleam/bool
import glyde
import glyde/intents
import glyde/message
pub fn main() -> Nil {
glyde.new(token:, intents: intents.new([intents.GuildMessages]))
|> glyde.on_message(fn(api, msg) {
use <- bool.guard(msg.content != "!ping", Nil)
let _ = message.reply(api, msg, message.text("pong!"))
Nil
})
|> glyde.run
}
Each event is handed to a fresh process under a supervisor, so a slow or crashing handler never holds up the next one. An endpoint call blocks that one process on the rate limiter and the network; the shard keeps reading.
glyde/client is the layer below, for driving the machine yourself.
Types
A bot before it starts. Opaque and a value: every builder gives back a new one.
pub opaque type Bot
pub type CallFailure =
api.CallFailure
pub type ChannelId =
id.Id(id.Channel)
pub type Event =
event.Event
pub type Failure =
error.ApiError
pub type Interaction =
interaction.Interaction
pub type Message =
message.Message
pub type Ready =
ready.Ready
pub type Status =
status.Status
The platform half: a socket, an HTTP client and a clock.
pub type Transport =
transport.Transport
Values
pub const api_version: Int
The Discord API version glyde speaks, for both the gateway and REST.
pub fn new(
token secret: String,
intents intents: intents.Intents,
) -> Bot
One shard, no compression, a real socket, and a line on stderr when something goes wrong.
Build a bot once, at startup, and keep the value. This mints the three
process names its tree registers under, and process.new_name creates an
atom that is never collected — so a new per start would fill the atom
table and take the VM down with it. Holding the names on the bot is what
makes supervised safe to restart forever.
pub fn on_event(
bot: Bot,
handler: fn(api.Api, event.Event) -> a,
) -> Bot
Every dispatch, decoded. One glyde does not model arrives as event.Raw
with Discord’s payload untouched.
pub fn on_interaction(
bot: Bot,
handler: fn(api.Api, interaction.Interaction) -> a,
) -> Bot
Every INTERACTION_CREATE: slash commands, buttons, autocomplete.
pub fn on_message(
bot: Bot,
handler: fn(api.Api, message.Message) -> a,
) -> Bot
Every MESSAGE_CREATE. Add as many as you like; each runs in the same handler process for that event, in the order added.
pub fn on_ready(
bot: Bot,
handler: fn(api.Api, ready.Ready) -> a,
) -> Bot
READY, which carries the bot’s own user and the guilds it is in. Sent again after every fresh identify, so this is not once per process.
pub fn on_status(
bot: Bot,
handler: fn(status.Status) -> a,
) -> Bot
Watch the runtime: dials, reconnects, halts, failed sends, diagnostics.
Runs on the shard process (or the limiter for Paced), not a handler
process, so keep it cheap.
pub fn run(bot: Bot) -> Nil
Start the tree and block until the shard halts. What main calls: a bad
token or disallowed intents come back as Nil rather than a hung process.
pub fn start(bot: Bot) -> Result(process.Pid, actor.StartError)
Build the supervision tree and start it. Returns the top supervisor’s pid,
so the caller can monitor it. To put it under a supervisor of your own,
reach for supervised.
One bot is one tree: starting the same bot twice over fails the second time, because its names are already registered to the first.
pub fn supervised(
bot: Bot,
) -> supervision.ChildSpecification(static_supervisor.Supervisor)
A child specification, for putting glyde under a supervision tree of your own. Restarting is safe: the names live on the bot, so the tree comes back on the same ones.
supervisor.new(supervisor.OneForOne)
|> supervisor.add(glyde.supervised(bot))
|> supervisor.start
pub fn with_transport(
bot: Bot,
transport: transport.Transport,
) -> Bot
Run over a socket, a clock and an HTTP client of your own. The rate limiter
sits around request, so yours is paced without knowing it.