///|
/// Wraps the bot's identify queue with Discord's daily session start limit.
/// After the queue grants a slot, the live `session_start_limit` is read from
/// `GET /gateway/bot`; a shard whose IDENTIFY would exceed it is held while
/// `Bot::run` ends with `SessionStartLimitExceeded`. Exceeding that limit
/// resets the bot token, so an exhausted limit fails closed. A limit that
/// cannot be read is reported through the app's warning hook and the shard
/// identifies anyway, because a REST outage must not stop a running bot.
priv struct IdentifyGuard {
bot : Bot
client : @dhttp.Client
inner : &@queue.IdentifyQueue
refusals : @aqueue.Queue[ManagedShardEvent]
}
///|
impl @queue.IdentifyQueue for IdentifyGuard with fn wait_for_identify(
self,
shard_id,
) {
self.inner.wait_for_identify(shard_id)
let info = self.bot.gateway_bot_info(self.client) catch {
error => {
self.bot.app_.warn(
"identifying shard \{shard_id} without reading the session start limit: \{Repr(error)}",
)
return
}
}
let limit = info.session_start_limit
if limit.remaining <= 0 {
self.refusals.put(
IdentifyRefused(
BotError::SessionStartLimitExceeded(
required=1,
remaining=limit.remaining,
reset_after_ms=limit.reset_after,
),
),
)
// Hold the shard here; `Bot::run` raises the error and cancels the task
// group, which tears this shard down without sending IDENTIFY.
let parked : @async.Semaphore = Semaphore(1, initial_value=0)
parked.acquire()
}
}