Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/command.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
mod ping;
mod restart;

use crate::{ADMIN_GUILD_ID, CTX};
use twilight_model::{
application::{
command::Command,
interaction::{InteractionData, InteractionType},
},
application::interaction::{InteractionData, InteractionType},
gateway::payload::incoming::InteractionCreate,
};

pub fn admin_commands(shards: u32) -> [Command; 1] {
[restart::command(shards)]
}

pub fn global_commands() -> [Command; 1] {
[ping::command()]
pub async fn register() -> anyhow::Result<()> {
CTX.interaction()
.set_global_commands(&[ping::command()])
.await?;
CTX.interaction()
.set_guild_commands(ADMIN_GUILD_ID, &[restart::command(CTX.shards.len() as u32)])
.await?;
Ok(())
}

#[derive(Clone, Copy, Debug)]
Expand Down
18 changes: 4 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,6 @@ async fn main() -> anyhow::Result<()> {
let info = async { anyhow::Ok(http.gateway().authed().await?.model().await?) }
.await
.context("getting info")?;
async {
http.interaction(app.id)
.set_global_commands(&command::global_commands())
.await?;
http.interaction(app.id)
.set_guild_commands(ADMIN_GUILD_ID, &command::admin_commands(info.shards))
.await?;
anyhow::Ok(())
}
.await
.context("putting commands")?;
let shards = DashMap::new();
context::init(app.id, http, shards);

// The queue defaults are static and may be incorrect for large or newly
// restarted bots.
Expand All @@ -59,9 +46,12 @@ async fn main() -> anyhow::Result<()> {
info.session_start_limit.total,
);
let config = ConfigBuilder::new(token, INTENTS).queue(queue).build();

let shards = resume::restore(config, info.shards).await;

context::init(app.id, http, DashMap::with_capacity(shards.len()));

command::register().await.context("registering commands")?;

let tasks = shards
.into_iter()
.map(|shard| tokio::spawn(dispatch::run(event_handler, shard, |_shard| ())))
Expand Down
33 changes: 16 additions & 17 deletions src/resume.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use std::iter;
use tokio::fs;
use twilight_gateway::{Config, ConfigBuilder, Session, Shard, ShardId};

Expand Down Expand Up @@ -54,37 +55,35 @@ pub async fn save(info: &[Info]) -> anyhow::Result<()> {
}

/// Restores shard resumption information from the file system.
pub async fn restore(config: Config, shards: u32) -> Vec<Shard> {
pub async fn restore(config: Config, recommended_shards: u32) -> Vec<Shard> {
let info = async {
let contents = fs::read(INFO_FILE).await?;
anyhow::Ok(serde_json::from_slice::<Vec<Info>>(&contents)?)
}
.await;

let shard_ids = (0..shards).map(|shard| ShardId::new(shard, shards));

// A session may only be successfully resumed if it retains its shard ID, but
// Discord may have recommend a different shard count (producing different shard
// IDs).
let shards: Vec<_> = if let Ok(info) = info
&& info.len() == shards as usize
// The recommended shard count targets 1000 guilds per shard (out of a maximum
// of 2500), so it might be different from the previous shard count.
let shards = if let Ok(info) = info
&& recommended_shards / 2 <= info.len() as u32
{
tracing::info!("resuming previous gateway sessions");
shard_ids
let configs = iter::repeat_n(config, info.len())
.zip(info)
.map(|(shard_id, info)| {
let builder = ConfigBuilder::from(config.clone()).resume_info(info);
Shard::with_config(shard_id, builder.build())
})
.collect()
.map(|(config, info)| ConfigBuilder::from(config).resume_info(info).build());
shards(configs).collect()
} else {
shard_ids
.map(|shard_id| Shard::with_config(shard_id, config.clone()))
.collect()
shards(iter::repeat_n(config, recommended_shards as usize)).collect()
};

// Resumed or not, the saved resume info is now stale.
_ = fs::remove_file(INFO_FILE).await;

shards
}

fn shards(iter: impl ExactSizeIterator<Item = Config>) -> impl ExactSizeIterator<Item = Shard> {
let total = iter.len() as u32;
iter.zip((0..total).map(move |id| ShardId::new(id, total)))
.map(|(config, shard_id)| Shard::with_config(shard_id, config))
}