DiscordPHP-BridgeBot Documentation

Bot extends MessageCommandClient
in package

The bot: one Discord gateway connection, one HTTP client, and however many connectors the application installed.

Extending MessageCommandClient rather than owning one is what lets the Discord side keep everything that class already provides — prefix handling, aliases, cooldowns, the command registry — while each connector brings the equivalent for its own network. None of them know about each other: they are all fed from one ActionRegistry, and a command written once appears in every chat.

One client, deliberately

There is exactly one Discord\Http in the process, built by Discord itself, and every request to Discord goes through it — including the relay's webhook executions, which are Webhook::execute() and therefore already routed there. That object holds the per-route rate-limit buckets and the concurrency cap, so a second client would keep its own empty bucket table and race the first into 429s against the same token. Connectors are forbidden to build one; see Connector.

What the buckets cannot do is decline to send. Merging two bots into one halved the budget — two applications with two tokens became one — so relayed traffic is additionally paced per destination channel by OutboundPacer, leaving room for the commands somebody is waiting on.

Tags
author

Valithor Obsidion valithor@discordphp.org

Table of Contents

Constants

BRIDGE_CHECK_DELAY  : mixed = 10.0
How long after startup to check the restored bridges.
GITHUB  : mixed = 'https://github.com/discord-php/DiscordPHP-Bridge'
Where this lives. Printed into chat, so it has to resolve.
INTENTS  : mixed = \Discord\WebSockets\Intents::GUILDS | \Discord\...
MESSAGE_CONTENT is privileged; without it every relayed message is empty.

Properties

$actions  : ActionRegistry
$components  : ComponentRouter
$config  : Config
$connectors  : array<string, Connector>
$cooldowns  : Cooldowns
$declared  : array<string|int, mixed>
Every top-level slash command this build defines; see {@see Support\CommandSync}.
$delivery  : WebhookDelivery|null
$lastCheck  : string|null
The last bridge check's headline.
$modules  : array<int, Module>
$modulesBooted  : bool
$pacer  : OutboundPacer
$relay  : ChatRelay|null
$started  : bool
$startupWarnings  : array<int, string>
$store  : Store
$up  : array<string, bool>

Methods

__construct()  : mixed
addConnector()  : self
Installs a connector.
addModule()  : self
addStartupWarnings()  : void
Records trouble found before the bot had a log or an owner to report it to — reading a damaged store, most obviously.
components()  : ComponentRouter
connector()  : Connector|null
One connector by name, or `null` when it is not installed.
connectors()  : array<string, Connector>
cooldowns()  : Cooldowns
One clock for every surface, so a cooldown cannot be dodged by switching chats.
declareCommand()  : void
Records a top-level command name this build defines, so the prune pass spares it.
declaredCommands()  : array<int, string>
delivery()  : WebhookDelivery
getActions()  : ActionRegistry
getConfig()  : Config
getLastCheck()  : string|null
The last bridge check's headline, or `null` before it has run.
getStore()  : Store
isUp()  : bool
Whether a connector has started and not failed.
modules()  : array<int, Module>
notifyOwner()  : void
Tells a human, when one is configured, rather than only the log.
pacer()  : OutboundPacer
rememberCheck()  : void
What the last bridge check found.
shutdown()  : void
Flushes state and disconnects every connector.
sync()  : void
Brings one connector's memberships in line with the routing table. Safe to call before it has connected; it becomes a no-op there.
bootModules()  : void
guildsWithBridges()  : array<int, string>
Every guild with a bridge on any connector.
pruneCommands()  : void
Removes global commands this build no longer defines.
reportRestoredBridges()  : void
Reports what was restored from disk, and whether it still works.
start()  : void
Wires up Discord once it is ready, then connects every connector.
startConnector()  : PromiseInterface<string|int, bool>
Starts one connector, then joins its rooms.
verifyBridges()  : void
Probes every restored bridge, then says what is wrong with which.

Constants

BRIDGE_CHECK_DELAY

How long after startup to check the restored bridges.

public mixed BRIDGE_CHECK_DELAY = 10.0

Long enough for the guild caches to fill and the joins to land.

GITHUB

Where this lives. Printed into chat, so it has to resolve.

public mixed GITHUB = 'https://github.com/discord-php/DiscordPHP-Bridge'

INTENTS

MESSAGE_CONTENT is privileged; without it every relayed message is empty.

public mixed INTENTS = \Discord\WebSockets\Intents::GUILDS | \Discord\WebSockets\Intents::GUILD_MESSAGES | \Discord\WebSockets\Intents::MESSAGE_CONTENT

Properties

$declared

Every top-level slash command this build defines; see {@see Support\CommandSync}.

private array<string|int, mixed> $declared = []

$lastCheck

The last bridge check's headline.

private string|null $lastCheck = null

$modules

private array<int, Module> $modules = []

$modulesBooted

private bool $modulesBooted = false

$started

private bool $started = false

$startupWarnings

private array<int, string> $startupWarnings = []

Trouble found before there was anywhere to report it.

$up

private array<string, bool> $up = []

connector name => whether it started

Methods

__construct()

public __construct(Config $config, Store $store[, array<string|int, mixed> $options = [] ]) : mixed
Parameters
$config : Config
$store : Store
$options : array<string|int, mixed> = []

addConnector()

Installs a connector.

public addConnector(Connector $connector) : self

Booted straight away rather than at ready, because whatever it registers has to be in place before the first message arrives; it is connected later, in start().

Parameters
$connector : Connector
Return values
self

addModule()

public addModule(Module $module) : self
Parameters
$module : Module
Return values
self

addStartupWarnings()

Records trouble found before the bot had a log or an owner to report it to — reading a damaged store, most obviously.

public addStartupWarnings(string $what, array<int, string> $warnings) : void
Parameters
$what : string
$warnings : array<int, string>

connector()

One connector by name, or `null` when it is not installed.

public connector(string $name) : Connector|null
Parameters
$name : string
Return values
Connector|null

cooldowns()

One clock for every surface, so a cooldown cannot be dodged by switching chats.

public cooldowns() : Cooldowns
Return values
Cooldowns

declareCommand()

Records a top-level command name this build defines, so the prune pass spares it.

public declareCommand(string $name) : void
Parameters
$name : string

declaredCommands()

public declaredCommands() : array<int, string>
Return values
array<int, string>

getLastCheck()

The last bridge check's headline, or `null` before it has run.

public getLastCheck() : string|null

Worth showing next to the bridges themselves: one whose Discord channel or remote room went away while the bot was down reads exactly like a working one from a listing alone.

Return values
string|null

isUp()

Whether a connector has started and not failed.

public isUp(string $name) : bool
Parameters
$name : string
Return values
bool

modules()

public modules() : array<int, Module>
Return values
array<int, Module>

notifyOwner()

Tells a human, when one is configured, rather than only the log.

public notifyOwner(string $markdown) : void

A bot that needs attention and only whispers it into a logfile stays broken until somebody happens to look.

Parameters
$markdown : string

rememberCheck()

What the last bridge check found.

public rememberCheck(string $summary) : void
Parameters
$summary : string

shutdown()

Flushes state and disconnects every connector.

public shutdown() : void

For a signal handler: once the loop stops a queued write would never run, and the change somebody just made would be the one lost.

sync()

Brings one connector's memberships in line with the routing table. Safe to call before it has connected; it becomes a no-op there.

public sync(string $connector) : void
Parameters
$connector : string

bootModules()

private bootModules() : void

guildsWithBridges()

Every guild with a bridge on any connector.

private guildsWithBridges() : array<int, string>
Return values
array<int, string>

pruneCommands()

Removes global commands this build no longer defines.

private pruneCommands(bool $everyConnectorStarted) : void

Renaming a command is two operations and only one of them is obvious: publishing /twitch leaves the /relay it replaced sitting in every server's command list, pointing at a handler that is gone. That reads as a broken bot rather than a renamed one.

Only ever run when every connector started. One that failed to boot never declared its commands, and pruning against an incomplete list would delete the working commands of whichever package happened to be unlucky — a far worse outcome than leaving a stale name for one restart.

Parameters
$everyConnectorStarted : bool

reportRestoredBridges()

Reports what was restored from disk, and whether it still works.

private reportRestoredBridges() : void

Configuration outlives the process, and both ends of a bridge can stop working while the bot is down — a Discord channel deleted, the bot removed from the server, a room renamed. None of that produces an error at startup; it produces a bridge that quietly relays nothing, which looks exactly like a quiet day.

start()

Wires up Discord once it is ready, then connects every connector.

private start() : void

Discord's half goes first and does not wait. The catalogue is complete the moment the connectors are installed — each adds its actions then — so there is nothing to wait for, and waiting would let a network that is slow to come up, or never does, hold Discord's commands hostage. An operator has to be able to ask what went wrong from somewhere.

Each connector then starts on its own. One that fails does not take the others with it; its rooms are simply never joined, and the bridge check says so.

startConnector()

Starts one connector, then joins its rooms.

private startConnector(string $name, Connector $connector) : PromiseInterface<string|int, bool>

Joining waits for the start rather than racing it: a connector cannot join anything before it has a connection to join with, and one that tried would report every room as missing.

Parameters
$name : string
$connector : Connector
Return values
PromiseInterface<string|int, bool> —

Whether it came up. Never rejects.

verifyBridges()

Probes every restored bridge, then says what is wrong with which.

private verifyBridges() : void

A connector that is down is reported once, as itself, rather than as a dozen separate bridges that each "cannot hear their room" — the dozen would be true and would hide the one thing worth fixing. Bridges for a connector that is not installed at all are reported the same way: they are kept, but nothing is relaying them.

On this page

Search results