chore: extract out Discord event handling
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
mod one_to_many;
|
mod one_to_many;
|
||||||
mod one_to_many_with_data;
|
mod one_to_many_with_data;
|
||||||
mod one_to_one;
|
mod one_to_one;
|
||||||
|
mod track_vcs;
|
||||||
mod vc_user;
|
mod vc_user;
|
||||||
|
|
||||||
pub use one_to_many::OneToManyUniqueBTreeMap;
|
pub use one_to_many::OneToManyUniqueBTreeMap;
|
||||||
pub use one_to_many_with_data::OneToManyUniqueBTreeMapWithData;
|
pub use one_to_many_with_data::OneToManyUniqueBTreeMapWithData;
|
||||||
pub use one_to_one::OneToOneBTreeMap;
|
pub use one_to_one::OneToOneBTreeMap;
|
||||||
|
|
||||||
|
pub use track_vcs::{VCs, initialize_vcs, update_vcs};
|
||||||
pub use vc_user::{UserInVCData, VoiceStatus};
|
pub use vc_user::{UserInVCData, VoiceStatus};
|
||||||
|
|||||||
120
src/main.rs
120
src/main.rs
@@ -1,29 +1,14 @@
|
|||||||
use std::{
|
|
||||||
collections::BTreeMap,
|
|
||||||
fmt::{Debug, Display},
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use fomo_reducer::{VCs, initialize_vcs, update_vcs};
|
||||||
use opendal::{IntoOperatorUri, Operator, OperatorUri};
|
use opendal::{IntoOperatorUri, Operator, OperatorUri};
|
||||||
use secrecy::{ExposeSecret, SecretString};
|
use secrecy::{ExposeSecret, SecretString};
|
||||||
|
use snafu::Snafu;
|
||||||
|
use std::{fmt::Debug, str::FromStr};
|
||||||
use tracing_subscriber::fmt::format::FmtSpan;
|
use tracing_subscriber::fmt::format::FmtSpan;
|
||||||
use twilight_gateway::{
|
use twilight_gateway::{
|
||||||
Event, EventTypeFlags, Intents, Shard, ShardId, StreamExt, error::ReceiveMessageErrorType,
|
Event, EventTypeFlags, Intents, Shard, ShardId, StreamExt, error::ReceiveMessageErrorType,
|
||||||
};
|
};
|
||||||
use twilight_model::{
|
use twilight_model::id::{Id, marker::UserMarker};
|
||||||
channel::ChannelType,
|
|
||||||
id::{
|
|
||||||
Id,
|
|
||||||
marker::{ChannelMarker, GuildMarker, UserMarker},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use typed_builder::TypedBuilder;
|
|
||||||
use vc_notifier::{OneToManyUniqueBTreeMapWithData, UserInVCData, VoiceStatus};
|
|
||||||
|
|
||||||
type VCsInServer = OneToManyUniqueBTreeMapWithData<Id<ChannelMarker>, Id<UserMarker>, UserInVCData>;
|
|
||||||
|
|
||||||
type VCs = BTreeMap<Id<GuildMarker>, VCsInServer>;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct OpendalOperator {
|
struct OpendalOperator {
|
||||||
@@ -67,10 +52,17 @@ struct Args {
|
|||||||
|
|
||||||
#[arg(long, env)]
|
#[arg(long, env)]
|
||||||
storage: OpendalOperator,
|
storage: OpendalOperator,
|
||||||
|
|
||||||
|
#[arg(long, env)]
|
||||||
|
bot_owner: Id<UserMarker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Snafu)]
|
||||||
|
enum MainError {}
|
||||||
|
|
||||||
|
#[snafu::report]
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() -> Result<(), MainError> {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.pretty()
|
.pretty()
|
||||||
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
||||||
@@ -83,6 +75,7 @@ async fn main() {
|
|||||||
let Args {
|
let Args {
|
||||||
discord_token,
|
discord_token,
|
||||||
storage,
|
storage,
|
||||||
|
bot_owner,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
rustls::crypto::aws_lc_rs::default_provider()
|
rustls::crypto::aws_lc_rs::default_provider()
|
||||||
@@ -112,98 +105,15 @@ async fn main() {
|
|||||||
|
|
||||||
next_event = shard.next_event(vc_event_types);
|
next_event = shard.next_event(vc_event_types);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip(discord_client), ret)]
|
Ok(())
|
||||||
async fn initialize_vcs(discord_client: &twilight_http::Client) -> VCs {
|
|
||||||
let mut vcs = VCs::default();
|
|
||||||
|
|
||||||
if let Ok(guilds_res) = discord_client.current_user_guilds().limit(200).await
|
|
||||||
&& let Ok(guilds) = guilds_res.model().await
|
|
||||||
{
|
|
||||||
for guild in guilds {
|
|
||||||
if let Ok(guild_members_res) = discord_client.guild_members(guild.id).limit(999).await
|
|
||||||
&& let Ok(guild_members) = guild_members_res.model().await
|
|
||||||
{
|
|
||||||
for member in guild_members {
|
|
||||||
if let Ok(voice_state_res) = discord_client
|
|
||||||
.user_voice_state(guild.id, member.user.id)
|
|
||||||
.await
|
|
||||||
&& let Ok(voice_state) = voice_state_res.model().await
|
|
||||||
{
|
|
||||||
tracing::info!(?member.user.id, ?voice_state);
|
|
||||||
|
|
||||||
let voice_status = VoiceStatus::builder()
|
|
||||||
.self_deafened(voice_state.self_deaf)
|
|
||||||
.self_muted(voice_state.self_mute)
|
|
||||||
.server_deafened(voice_state.deaf)
|
|
||||||
.server_muted(voice_state.mute)
|
|
||||||
.camming(voice_state.self_video)
|
|
||||||
.streaming(voice_state.self_stream)
|
|
||||||
.build();
|
|
||||||
let user_in_vc_data = voice_status.into();
|
|
||||||
|
|
||||||
if let Some(channel_id) = voice_state.channel_id {
|
|
||||||
vcs.entry(guild.id).or_default().insert(
|
|
||||||
channel_id,
|
|
||||||
member.user.id,
|
|
||||||
user_in_vc_data,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vcs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(vcs))]
|
#[tracing::instrument(skip(vcs))]
|
||||||
async fn handle_event(event: Event, vcs: &mut VCs) {
|
async fn handle_event(event: Event, vcs: &mut VCs) {
|
||||||
match event {
|
match event {
|
||||||
Event::VoiceStateUpdate(voice_state_update) => {
|
Event::VoiceStateUpdate(voice_state_update) => {
|
||||||
let user_id = voice_state_update.user_id;
|
update_vcs(&voice_state_update, vcs);
|
||||||
match voice_state_update.guild_id {
|
|
||||||
Some(guild_id) => match voice_state_update.channel_id {
|
|
||||||
Some(channel_id) => {
|
|
||||||
let voice_status = VoiceStatus::builder()
|
|
||||||
.self_deafened(voice_state_update.self_deaf)
|
|
||||||
.self_muted(voice_state_update.self_mute)
|
|
||||||
.server_deafened(voice_state_update.deaf)
|
|
||||||
.server_muted(voice_state_update.mute)
|
|
||||||
.camming(voice_state_update.self_video)
|
|
||||||
.streaming(voice_state_update.self_stream)
|
|
||||||
.build();
|
|
||||||
let user_in_vc_data = voice_status.into();
|
|
||||||
|
|
||||||
vcs.entry(guild_id).or_default().insert(
|
|
||||||
channel_id,
|
|
||||||
user_id,
|
|
||||||
user_in_vc_data,
|
|
||||||
);
|
|
||||||
|
|
||||||
tracing::info!(
|
|
||||||
?guild_id,
|
|
||||||
?channel_id,
|
|
||||||
?user_id,
|
|
||||||
"connected or otherwise changed state while connected"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
None => {
|
|
||||||
if let Some(channel_vcers) = vcs.get_mut(&guild_id) {
|
|
||||||
channel_vcers.remove_right(&user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing::info!(?guild_id, ?user_id, "disconnected");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
None => {
|
|
||||||
tracing::error!("why doesn't this have a guild id attached?!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
tracing::warn!(?other, "wasn't expected");
|
tracing::warn!(?other, "wasn't expected");
|
||||||
|
|||||||
102
src/track_vcs.rs
Normal file
102
src/track_vcs.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
type VCsInServer = OneToManyUniqueBTreeMapWithData<Id<ChannelMarker>, Id<UserMarker>, UserInVCData>;
|
||||||
|
|
||||||
|
pub type VCs = BTreeMap<Id<GuildMarker>, VCsInServer>;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use twilight_model::{
|
||||||
|
gateway::payload::incoming::VoiceStateUpdate,
|
||||||
|
id::{
|
||||||
|
Id,
|
||||||
|
marker::{ChannelMarker, GuildMarker, UserMarker},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{OneToManyUniqueBTreeMapWithData, UserInVCData, VoiceStatus};
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(discord_client), ret)]
|
||||||
|
pub async fn initialize_vcs(discord_client: &twilight_http::Client) -> VCs {
|
||||||
|
let mut vcs = VCs::default();
|
||||||
|
|
||||||
|
if let Ok(guilds_res) = discord_client.current_user_guilds().limit(200).await
|
||||||
|
&& let Ok(guilds) = guilds_res.model().await
|
||||||
|
{
|
||||||
|
for guild in guilds {
|
||||||
|
if let Ok(guild_members_res) = discord_client.guild_members(guild.id).limit(999).await
|
||||||
|
&& let Ok(guild_members) = guild_members_res.model().await
|
||||||
|
{
|
||||||
|
for member in guild_members {
|
||||||
|
if let Ok(voice_state_res) = discord_client
|
||||||
|
.user_voice_state(guild.id, member.user.id)
|
||||||
|
.await
|
||||||
|
&& let Ok(voice_state) = voice_state_res.model().await
|
||||||
|
{
|
||||||
|
tracing::info!(?member.user.id, ?voice_state);
|
||||||
|
|
||||||
|
let voice_status = VoiceStatus::builder()
|
||||||
|
.self_deafened(voice_state.self_deaf)
|
||||||
|
.self_muted(voice_state.self_mute)
|
||||||
|
.server_deafened(voice_state.deaf)
|
||||||
|
.server_muted(voice_state.mute)
|
||||||
|
.camming(voice_state.self_video)
|
||||||
|
.streaming(voice_state.self_stream)
|
||||||
|
.build();
|
||||||
|
let user_in_vc_data = voice_status.into();
|
||||||
|
|
||||||
|
if let Some(channel_id) = voice_state.channel_id {
|
||||||
|
vcs.entry(guild.id).or_default().insert(
|
||||||
|
channel_id,
|
||||||
|
member.user.id,
|
||||||
|
user_in_vc_data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vcs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_vcs(voice_state_update: &VoiceStateUpdate, vcs: &mut VCs) {
|
||||||
|
let user_id = voice_state_update.user_id;
|
||||||
|
match voice_state_update.guild_id {
|
||||||
|
Some(guild_id) => match voice_state_update.channel_id {
|
||||||
|
Some(channel_id) => {
|
||||||
|
let voice_status = VoiceStatus::builder()
|
||||||
|
.self_deafened(voice_state_update.self_deaf)
|
||||||
|
.self_muted(voice_state_update.self_mute)
|
||||||
|
.server_deafened(voice_state_update.deaf)
|
||||||
|
.server_muted(voice_state_update.mute)
|
||||||
|
.camming(voice_state_update.self_video)
|
||||||
|
.streaming(voice_state_update.self_stream)
|
||||||
|
.build();
|
||||||
|
let user_in_vc_data = voice_status.into();
|
||||||
|
|
||||||
|
vcs.entry(guild_id)
|
||||||
|
.or_default()
|
||||||
|
.insert(channel_id, user_id, user_in_vc_data);
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
?guild_id,
|
||||||
|
?channel_id,
|
||||||
|
?user_id,
|
||||||
|
"connected or otherwise changed state while connected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
None => {
|
||||||
|
if let Some(channel_vcers) = vcs.get_mut(&guild_id) {
|
||||||
|
channel_vcers.remove_right(&user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!(?guild_id, ?user_id, "disconnected");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
None => {
|
||||||
|
tracing::error!("why doesn't this have a guild id attached?!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user