Packages

A Gleam library for interacting with the Discord API

Current section

Files

Jump to
shimmer src shimmer.gleam
Raw

src/shimmer.gleam

import shimmer/internal/error
import shimmer/ws/event_loop
import gleam/otp/actor
import gleam/otp/supervisor
import gleam/erlang/process
import shimmer/client.{Client, Shard}
import shimmer/intents.{Intent}
import gleam/result
import shimmer/handlers
import shimmer/types/presence.{Presence}
import shimmer/builders/presence_builder.{PresenceBuilder}
import shimmer/shards.{ShardsManager}
import shimmer/http/endpoints
import shimmer/internal/types/responses/bot_gateway.{BotGatewayResponse}
pub type ClientOptions {
ClientOptions(intents: List(Intent))
}
/// Create a new client with the defualt setup, reccomended for most users
pub fn new(token: String) -> Client(event_loop.Message) {
let subject = process.new_subject()
Client(
token: token,
intents: intents.default(),
to_self: subject,
shard: Shard(id: 0, total: 1, to_all: subject),
)
}
/// Create a new Shimmer Client with more control over the options
pub fn new_with_opts(
token: String,
opts: ClientOptions,
) -> Client(event_loop.Message) {
let default = new(token)
Client(..default, intents: opts.intents)
}
/// Create a sharding manager to support larger bots, uses Client under the hood
pub fn new_sharded(token: String) -> ShardsManager(event_loop.Message) {
ShardsManager(
token: token,
intents: intents.default(),
to_clients: process.new_subject(),
clients: [],
)
}
/// Create a new sharding manager with more control over the options, uses Client under the hood
pub fn new_sharded_with_opts(
token: String,
opts: ClientOptions,
) -> ShardsManager(event_loop.Message) {
let default = new_sharded(token)
ShardsManager(..default, intents: opts.intents)
}
/// Opens a websocket connection to the Discord Gateway. Passes this off to an actor to listen to messages.
pub fn connect(
client: Client(event_loop.Message),
handlers_builder: handlers.HandlersBuilder(event_loop.Message),
) -> Result(Client(event_loop.Message), error.ShimmerError) {
try gateway_settings = endpoints.bot_gateway(client.token)
let actor_spec =
actor.Spec(
init: event_loop.actor_setup(
client,
gateway_settings.url,
handlers.handlers_from_builder(handlers_builder),
),
// 30 seconds
init_timeout: 30 * 1000,
loop: event_loop.actor_loop,
)
try _ =
actor.start_spec(actor_spec)
|> result.map_error(error.ActorError)
Ok(client)
}
/// Internal function that adds a child to the supervisor tree for each shard
fn add_child(
shards: ShardsManager(event_loop.Message),
gateway_settings: BotGatewayResponse,
client_handlers: handlers.Handlers(event_loop.Message),
children: supervisor.Children(a),
current_n: Int,
max_n: Int,
) -> supervisor.Children(a) {
let children =
children
|> supervisor.add(supervisor.worker(fn(_name) {
let actor_spec =
actor.Spec(
init: event_loop.actor_setup(
Client(
token: shards.token,
intents: shards.intents,
to_self: process.new_subject(),
shard: Shard(
id: current_n,
total: max_n,
to_all: shards.to_clients,
),
),
gateway_settings.url,
client_handlers,
),
// 30 seconds
init_timeout: 30 * 1000,
loop: event_loop.actor_loop,
)
actor.start_spec(actor_spec)
}))
let add_more = current_n + 1 > max_n
case add_more {
True ->
add_child(
shards,
gateway_settings,
client_handlers,
children,
current_n + 1,
max_n,
)
False -> children
}
}
/// Opens a sharded websocket connection to the Discord Gateway. Passes this off to an actor to listen to messages.
pub fn connect_sharded(
shards: ShardsManager(event_loop.Message),
handlers_builder: handlers.HandlersBuilder(event_loop.Message),
) -> Result(ShardsManager(event_loop.Message), error.ShimmerError) {
let client_handlers = handlers.handlers_from_builder(handlers_builder)
try gateway_settings = endpoints.bot_gateway(shards.token)
let supervisor_spec =
supervisor.Spec(
argument: 1,
frequency_period: 1,
max_frequency: 5,
init: fn(children) {
add_child(
shards,
gateway_settings,
client_handlers,
children,
0,
gateway_settings.shards,
)
},
)
try _ =
supervisor.start_spec(supervisor_spec)
|> result.map_error(error.ActorError)
Ok(shards)
}
/// Update the presence of the client, returns the client to be used for future operations
/// > **Note** you must call `connect` before calling this function and do-not know if it has succeeded
pub fn update_client_presence(
client: Client(event_loop.Message),
presence: Presence,
) -> Client(event_loop.Message) {
client.to_self
|> process.send(event_loop.UpdatePresence(presence))
client
}
/// Update the presence of the client, returns the client to be used for future operations
/// > **Note** you must call `connect` before calling this function and do-not know if it has succeeded
/// Builds the presence from the builder and calls `update_client_presence`
pub fn update_client_presence_from_builder(
client: Client(event_loop.Message),
builder: PresenceBuilder,
) -> Result(Client(event_loop.Message), error.ShimmerError) {
try built_presence = presence_builder.build(builder)
Ok(update_client_presence(client, built_presence))
}
/// Update the presence of every client managed by the shards manager, returns the client to be used for future operations
/// > **Note** you must call `connect` before calling this function and do-not know if it has succeeded
pub fn update_all_shards_presence(
shards: ShardsManager(event_loop.Message),
presence: Presence,
) -> ShardsManager(event_loop.Message) {
shards.to_clients
|> process.send(event_loop.UpdatePresence(presence))
shards
}
/// Update the presence of every client managed by the shards manager, returns the client to be used for future operations
/// > **Note** you must call `connect` before calling this function and do-not know if it has succeeded
/// Builds the presence from the builder and calls `update_client_presence`
pub fn update_all_shards_presence_from_builder(
shards: ShardsManager(event_loop.Message),
builder: PresenceBuilder,
) -> Result(ShardsManager(event_loop.Message), error.ShimmerError) {
try built_presence = presence_builder.build(builder)
Ok(update_all_shards_presence(shards, built_presence))
}