NAME

Discord::Client - Discord bots using IO::Async and Future

SYNOPSIS

use v5.26;
use warnings;
use Discord::Client;

# Create the bot.
my $bot = Discord::Client->new(token => $ENV{DISCORD_BOT_TOKEN});

# Reply to /ping.
$bot->command(
    ping => (
        description => 'Replies with Pong',
        handler => sub {
            my ($interaction) = @_;
            return $interaction->reply('Pong!');
        },
    ),
);

# Run until shutdown.
$bot->run;

DESCRIPTION

Build Discord bots with IO::Async and Future. REST, Gateway and HTTP Interactions can also be used independently. The API is experimental. This library is unofficial and is not affiliated with or endorsed by Discord.

Declare commands locally and deploy them explicitly with "sync" in Discord::Client::Commands. Startup never deploys commands. Return Futures from handlers so the SDK can report failures and cancel work on shutdown. Blocking code stops the shared loop.

METHODS

new

my $bot = Discord::Client->new(
    token => $token, loop => $loop, intents => ['GUILDS'],
);

Returns a client without network I/O. token is the bot token. loop is an optional IO::Async::Loop; otherwise one is created. intents accepts names or a bitmask and defaults to ['GUILDS'].

application_id can set the command application's ID. shards accepts auto or a positive count; see Discord::Client::Gateway::Shards. cache accepts 1, an options hash or a Discord::Client::Cache object; it is disabled by default.

rest_options, gateway_options and interaction_options pass constructor options to those components. Tests can inject rest and gateway objects. Shutdown closes an injected Gateway and leaves an injected REST client caller-owned. max_callbacks defaults to 1024 pending event callback Futures; excess work reports queue_full.

on_error accepts a synchronous callback receiving ($error, $interaction_or_undef, $bot). Without an error callback, the SDK warns with a sanitized message.

run

Runs startup, Gateway lifetime and shutdown; blocks until stopped. Handles INT/TERM where supported and removes its signal handlers on exit. Startup or lifetime errors are raised after shutdown is attempted; otherwise a shutdown error is raised. Returns no value.

start

Returns a Future resolving to the client when the Gateway is ready. Cancelling it closes the client. The caller drives the loop.

completion

Returns the Gateway lifetime Future. It resolves on orderly shutdown and fails on a fatal error.

close

Returns a Future waiting for shutdown. Repeated calls return the same Future. Cancels handlers and owned network work. Closing from a handler stops later callbacks and cancels that handler's pending Future, unless it is the Future returned by close itself. Returning close lets graceful shutdown finish.

command

$bot->command(name => description => 'Description', handler => $callback);

Declares a command and returns the client. The callback receives an Discord::Client::Interaction. Options follow "define" in Discord::Client::Commands. Invalid declarations throw before changing the registry.

component

$bot->component(custom_id => $callback);

Registers a button, select menu or modal handler receiving an Interaction. Returns the client.

on

$bot->on(MESSAGE_CREATE => sub {
    my ($data, $packet, $bot) = @_;
    return handle_message($data);
});

Registers an event callback and returns the client. Event names accept either case. Data is a hashref with unknown fields and string IDs preserved. Thrown exceptions or failed handler Futures reach the error callbacks. Gateway callback queue limits may drop these notifications. SDK cache and READY updates and command dispatch precede this queue; command dispatch is separately bounded by interaction_options. on(error => CODE) receives the same arguments as on_error.

on_message

# Ignore bot messages and return the reply Future.
$bot->on_message(sub {
    my ($message, $bot) = @_;
    return if $message->is_from_bot;
    return unless $message->content eq '!ping';
    return $message->reply('Pong!');
});

Registers a callback receiving ($message, $bot) and returns the client. The message is a Discord::Client::Model::Message snapshot using the client's REST transport. Wrapping performs no request and needs no cache. Set message intents explicitly and enable Message Content in the Developer Portal when reading message text. Bot messages are not filtered automatically. Raw MESSAGE_CREATE callbacks still receive hashrefs.

model

my $channel = $bot->model(channel => {id => $channel_id});

Synchronously wraps data with the client's REST transport. Types are message, channel, user and guild; see "wrap" in Discord::Client::Model. Invalid data throws. Wrapping performs no request.

rest

Returns the Discord::Client::REST client.

gateway

Returns the Discord::Client::Gateway or Discord::Client::Gateway::Shards.

commands

Returns the Discord::Client::Commands registry.

interactions

Returns the Discord::Client::Interactions dispatcher.

loop

Returns the IO::Async::Loop.

user

Returns the bot user hashref after READY, or undef before it.

cache

Returns the configured Cache object, or undef when disabled.

stats

Returns a hashref of REST, Gateway, pending callback and optional cache counters. It does not include payloads or credentials.

EXAMPLES

From a source checkout, set DISCORD_BOT_TOKEN and TEST_GUILD_ID. Invite the bot with bot and applications.commands scopes and leave the Interactions Endpoint URL empty for Gateway delivery.

# Deploy /ping once, then run the bot.
perl examples/sync-commands.pl examples/ping.pl
perl examples/ping.pl

Deployment replaces this application's commands in the test server, removing commands absent from the example. Run one example receiver per bot token.

The example scripts cover arguments, buttons, forms, channels, messages and asynchronous work. Replace ping.pl in both commands to run a slash-command example. messages.pl needs no deployment; it selects GUILDS, GUILD_MESSAGES, DIRECT_MESSAGES and MESSAGE_CONTENT. Enable Message Content in the portal. workflow-await.pl additionally needs cpanm Future::AsyncAwait. For dependencies under local/, prefix commands with PERL5LIB=local/lib/perl5.

LICENSE

Apache License 2.0.