Connector
in
One network the bot bridges Discord with.
Everything platform-specific lives behind this: a connector owns its client, its socket, its authentication and its idea of what a room is, and hands the core Incoming messages and Room descriptions. Nothing in the core mentions Twitch or Telegram, and adding a third network is a new package rather than an edit to this one.
A connector is registered with Bot::addConnector(), which calls
boot() immediately and start() once Discord is ready — the
order matters, because listeners registered in boot() must already be in
place when the first message arrives.
What a connector must not do
Never build its own HTTP client for Discord. The bot has exactly one
Discord\Http, and that object is where the per-route rate-limit buckets and
the concurrency cap live. A second client keeps its own empty bucket table
and races the first into 429s against the same token — and 10,000 rejected
requests in ten minutes is a Cloudflare ban on the whole host. Use the parts
and repositories DiscordPHP hands over; they already route through it.
Its own network is its own business: a connector is expected to pace that side itself, which is what RateLimiter is for.
Tags
Table of Contents
Methods
- boot() : void
- Builds the client and registers everything that must exist before the first message arrives. Called once, when the connector is added.
- joined() : array<int, string>
- Every room the connector is currently in.
- label() : string
- What to call the network in front of a human: `Twitch`, `Telegram`.
- name() : string
- A short, lowercase, stable name — `twitch`, `telegram`.
- normalise() : string|null
- Turns however somebody referred to a room into the form this connector addresses it by, or `null` when it is not a room reference at all.
- onIncoming() : void
- Registers the handler every inbound message is passed to.
- queued() : int
- How many outbound messages are waiting on this connector's own pacing.
- relay() : PromiseInterface<string|int, string|null>
- Renders a Discord message for this network and says it.
- resolve() : PromiseInterface<string|int, Room|null>
- Looks a room up, resolving to `null` when it does not exist.
- send() : PromiseInterface
- Says something in a room, exactly as given.
- start() : PromiseInterface<string|int, mixed>
- Connects. Called once Discord is ready and the bridges are known.
- stop() : void
- Disconnects cleanly, for shutdown.
- surface() : Surface
- What this network's chat can take; see {@see Surface}.
-
sync()
: array{join: list
, part: list } - Joins and leaves whatever the routing table now says, and reports what it did.
Methods
boot()
Builds the client and registers everything that must exist before the first message arrives. Called once, when the connector is added.
public
boot(Bot $bot) : void
Parameters
- $bot : Bot
joined()
Every room the connector is currently in.
public
joined() : array<int, string>
The startup check compares this against what was restored from disk: a join that silently failed leaves a bridge that works in one direction only, which is invisible from the configuration alone.
Return values
array<int, string>label()
What to call the network in front of a human: `Twitch`, `Telegram`.
public
label() : string
Return values
stringname()
A short, lowercase, stable name — `twitch`, `telegram`.
public
name() : string
It is not only a label. It keys this connector's bridges in Store, and it is the top-level slash command the connector publishes, so changing it strands both.
Return values
stringnormalise()
Turns however somebody referred to a room into the form this connector addresses it by, or `null` when it is not a room reference at all.
public
normalise(string $input) : string|null
twitch.tv/Foo and #Foo are both foo; t.me/name is @name. The
result is what gets stored, so it must be stable — normalising one way on
Monday and another on Tuesday orphans every bridge made before the change.
Parameters
- $input : string
Return values
string|nullonIncoming()
Registers the handler every inbound message is passed to.
public
onIncoming(callable(Incoming): void $handler) : void
The connector is responsible for setting Incoming::$own on anything the bot itself said. A bridge that repeats its own output is an infinite loop that gets the account banned from both networks within minutes, and only the connector can recognise its own voice.
Parameters
- $handler : callable(Incoming): void
queued()
How many outbound messages are waiting on this connector's own pacing.
public
queued() : int
Return values
intrelay()
Renders a Discord message for this network and says it.
public
relay(string $target, Outgoing $message) : PromiseInterface<string|int, string|null>
The rendering is the connector's because the answer is: IRC cannot carry a newline, Telegram's HTML mode needs exactly three characters escaped, and the limits are 500 and 4096. MessageText holds the parts that are the same everywhere — resolving mentions, budgeting attachment links, truncating on a character boundary — so a connector writes only what is genuinely its own.
Resolves to null when there was nothing worth relaying, which is not a
failure: an embed-only message says nothing a chat can repeat.
Parameters
- $target : string
- $message : Outgoing
Return values
PromiseInterface<string|int, string|null> —The id of what was said, when the network has them.
resolve()
Looks a room up, resolving to `null` when it does not exist.
public
resolve(string $target) : PromiseInterface<string|int, Room|null>
Used by link before wiring anything up — a typo otherwise produces a
bridge that silently never works — and by the startup check.
A lookup that failed is not proof a room is gone: reject, or resolve to
a Room, rather than resolving to null on a network error.
Parameters
- $target : string
Return values
PromiseInterface<string|int, Room|null>send()
Says something in a room, exactly as given.
public
send(string $target, string $text[, array<string, mixed> $options = [] ]) : PromiseInterface
For text this bot composed itself — a command's reply, a notice. Relayed chat goes through relay() instead, which is the one that still has to be rendered.
Parameters
- $target : string
- $text : string
- $options : array<string, mixed> = []
-
Connector-specific extras — a reply target, a file to attach. Unknown keys are ignored.
Return values
PromiseInterfacestart()
Connects. Called once Discord is ready and the bridges are known.
public
start() : PromiseInterface<string|int, mixed>
Resolves once the connector can actually send and receive, and rejects when it cannot. That is not a formality: the core joins rooms only after this resolves, reports a network that failed rather than one that merely looks quiet, and refuses to remove stale slash commands unless every connector got here — pruning against a connector that never declared its commands would delete them.
Return values
PromiseInterface<string|int, mixed>stop()
Disconnects cleanly, for shutdown.
public
stop() : void
surface()
What this network's chat can take; see {@see Surface}.
public
surface() : Surface
Return values
Surfacesync()
Joins and leaves whatever the routing table now says, and reports what it did.
public
sync(Links $links) : array{join: list, part: list}
A diff rather than "rejoin everything", so one server's change does not blink every other server's bridge offline.
Parameters
- $links : Links