DiscordPHP-BridgeBot Documentation

OutboundPacer
in package

FinalYes

Paces what the bot says *into* Discord, per channel, across every connector.

Discord\Http already queues per rate-limit bucket and caps concurrency, and everything here goes through it — nothing in this project may hold a second HTTP client. What that machinery cannot do is decline to make a request in the first place, and the arithmetic changed when the platforms were merged:

  • Two separate bots were two applications with two tokens, and therefore two 50-requests-per-second budgets. One bot has one.
  • One message on a busy network fans out to every Discord channel following that room, across unrelated servers.
  • Two connectors relaying into the same channel each stay under the limit on their own and breach it together, which is exactly the failure a per-sender limit cannot see.

So relayed traffic is throttled at the source, by destination channel, leaving the budget for the things a person is waiting on. Interaction responses deliberately do not come through here: Discord documents them as exempt from the global limit, so a command keeps answering while the relay is saturated.

Bursts are not the enemy — a token bucket lets ordinary chat through immediately and only slows a genuine flood. A channel out of tokens is skipped over rather than stalling the whole queue behind it.

Tags
link
https://docs.discord.com/developers/topics/rate-limits
author

Valithor Obsidion valithor@discordphp.org

Table of Contents

Constants

CAPACITY  : mixed = 5
Discord's per-channel message sublimit: five in five seconds.
PER  : mixed = 5.0

Properties

$capacity  : int
$clock  : mixed
$draining  : bool
$limiters  : array<string, RateLimiter>
$loop  : LoopInterface
$per  : float
$queue  : array<int, Deferred}>

Methods

__construct()  : mixed
enqueue()  : PromiseInterface
Runs `$send` as soon as the channel's budget allows, and resolves with whatever it returns.
queued()  : int
How many sends are waiting, for logging and health checks.
queuedFor()  : int
How many are waiting on one channel.
drain()  : void
Sends whatever the budget allows, then re-arms a timer for the rest.

Constants

CAPACITY

Discord's per-channel message sublimit: five in five seconds.

public mixed CAPACITY = 5

Not published as a header — it is a documented property of the message routes — so it is encoded here rather than discovered from a 429.

Properties

Methods

__construct()

public __construct(LoopInterface $loop[, int $capacity = self::CAPACITY ][, float $per = self::PER ][, callable(): float|null $clock = null ]) : mixed
Parameters
$loop : LoopInterface
$capacity : int = self::CAPACITY
$per : float = self::PER
$clock : callable(): float|null = null

Defaults to microtime(true); injected for tests.

enqueue()

Runs `$send` as soon as the channel's budget allows, and resolves with whatever it returns.

public enqueue(string $channelId, callable(): PromiseInterface $send) : PromiseInterface

The caller gets a promise rather than a callback so a failure — a webhook that has been deleted, a channel the bot was removed from — still surfaces at the point that asked for the send.

Parameters
$channelId : string
$send : callable(): PromiseInterface
Return values
PromiseInterface

queued()

How many sends are waiting, for logging and health checks.

public queued() : int
Return values
int

queuedFor()

How many are waiting on one channel.

public queuedFor(string $channelId) : int
Parameters
$channelId : string
Return values
int

drain()

Sends whatever the budget allows, then re-arms a timer for the rest.

private drain() : void

Head-of-line blocking is avoided by skipping over a channel that is out of tokens instead of stalling the whole queue behind it: one busy channel should not delay a quiet one in another server.

On this page

Search results