Compare commits
1 Commits
main
...
4f8f16ee77
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f8f16ee77 |
1985
Cargo.lock
generated
1985
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,8 @@ color-eyre = "0.6.3"
|
|||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
poise = "0.6.1"
|
poise = "0.6.1"
|
||||||
rand = "0.10.0"
|
rand = "0.8.5"
|
||||||
rusqlite = { version = "0.38.0", features = ["rusqlite-macros"] }
|
rusqlite = { version = "0.32.1", features = ["rusqlite-macros"] }
|
||||||
serenity = "0.12.2"
|
serenity = "0.12.2"
|
||||||
tokio = { version = "1.41.0", features = ["rt", "net"] }
|
tokio = { version = "1.41.0", features = ["rt", "net"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
|
|||||||
78
flake.lock
generated
78
flake.lock
generated
@@ -1,12 +1,15 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"crane": {
|
"crane": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1771438068,
|
"lastModified": 1714864355,
|
||||||
"narHash": "sha256-nGBbXvEZVe/egCPVPFcu89RFtd8Rf6J+4RFoVCFec0A=",
|
"narHash": "sha256-uXNW6bapWFfkYIkK1EagydSrFMqycOYEDSq75GmUpjk=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "b5090e53e9d68c523a4bb9ad42b4737ee6747597",
|
"rev": "442a7a6152f49b907e73206dc8e1f46a61e8e873",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -23,11 +26,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1771493599,
|
"lastModified": 1715269385,
|
||||||
"narHash": "sha256-kwfV7N65lx07pSTnLtK5PqxkrqauhYHswePAgHd2J1M=",
|
"narHash": "sha256-97UnOLbYWqBP2RY6GtMf49SoVrYiYd3EEm6phWBjKv8=",
|
||||||
"owner": "bluepython508",
|
"owner": "bluepython508",
|
||||||
"repo": "crane-flake-parts",
|
"repo": "crane-flake-parts",
|
||||||
"rev": "260ca5837e562c25b0a1ba0504fcfe953dc5b680",
|
"rev": "abd7ab48488e81617740eeb4eeefc3be2e4a6b7f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -41,11 +44,11 @@
|
|||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769996383,
|
"lastModified": 1730504689,
|
||||||
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
|
"narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
|
"rev": "506278e768c2a08bec68eb62932193e341f55c90",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -56,11 +59,39 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1771369470,
|
"lastModified": 1714656196,
|
||||||
"narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=",
|
"narHash": "sha256-kjQkA98lMcsom6Gbhw8SYzmwrSo+2nruiTcTZp5jK7o=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0182a361324364ae3f436a63005877674cf45efb",
|
"rev": "94035b482d181af0a0f8f77823a790b256b7c3cc",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1730504152,
|
||||||
|
"narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=",
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1730200266,
|
||||||
|
"narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -70,26 +101,11 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-lib": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1769909678,
|
|
||||||
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nixpkgs.lib",
|
|
||||||
"rev": "72716169fe93074c333e8d0173151350670b824c",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nixpkgs.lib",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"crane-flake-parts": "crane-flake-parts",
|
"crane-flake-parts": "crane-flake-parts",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -100,11 +116,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1771470520,
|
"lastModified": 1730514457,
|
||||||
"narHash": "sha256-PvytHcaYN5cPUll7FB70mXv1rRsIBRmu47fFfq3haxA=",
|
"narHash": "sha256-cjFX208s9pyaOfMvF9xI6WyafyXINqdhMF7b1bMQpLI=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "a1d4cc1f264c45d3745af0d2ca5e59d460e58777",
|
"rev": "1ff38ca26eb31858e4dfe7fe738b6b3ce5d74922",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
config,
|
config,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
pkg = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
pkg = self.packages.${pkgs.system}.default;
|
||||||
cfg = config.bluepython508.botc-mover;
|
cfg = config.bluepython508.botc-mover;
|
||||||
in {
|
in {
|
||||||
options.bluepython508.botc-mover = with lib; {
|
options.bluepython508.botc-mover = with lib; {
|
||||||
|
|||||||
159
src/main.rs
159
src/main.rs
@@ -1,6 +1,6 @@
|
|||||||
use std::{io, mem, path::Path, time::Duration};
|
use std::{future::Future, io, path::Path};
|
||||||
|
|
||||||
use ::serenity::all::{ChannelId, EditMessage, GuildId, Mentionable, UserId};
|
use ::serenity::all::{ChannelId, EditMember, GuildId, UserId};
|
||||||
use eyre::{Context as _, Error, OptionExt, Result};
|
use eyre::{Context as _, Error, OptionExt, Result};
|
||||||
use futures::{FutureExt, StreamExt, TryStreamExt};
|
use futures::{FutureExt, StreamExt, TryStreamExt};
|
||||||
use poise::serenity_prelude as serenity;
|
use poise::serenity_prelude as serenity;
|
||||||
@@ -107,21 +107,19 @@ async fn channel_children(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
async fn move_users<'a>(
|
fn move_users<'a>(
|
||||||
ctx: Context<'a>,
|
ctx: Context<'a>,
|
||||||
guild: GuildId,
|
guild: GuildId,
|
||||||
users: Vec<(ChannelId, UserId)>,
|
users: Vec<(ChannelId, UserId)>,
|
||||||
) -> Result<()> {
|
) -> impl Future<Output = Result<()>> + Send + 'a {
|
||||||
futures::stream::iter(users.into_iter())
|
futures::stream::iter(users.into_iter().map(move |(channel, user)| async move {
|
||||||
.map(Ok::<_, eyre::Error>)
|
|
||||||
.try_for_each_concurrent(10, move |(channel, user)| async move {
|
|
||||||
tracing::info!(?channel, ?user, "Moving user");
|
tracing::info!(?channel, ?user, "Moving user");
|
||||||
guild.move_member(ctx, user, channel).await?;
|
guild.move_member(ctx, user, channel).await?;
|
||||||
tracing::info!(?channel, ?user, "Moved user");
|
tracing::info!(?channel, ?user, "Moved user");
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
}))
|
||||||
.await?;
|
.buffer_unordered(10)
|
||||||
Ok(())
|
.try_collect::<()>()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
@@ -143,13 +141,11 @@ async fn spread(
|
|||||||
.ok_or_eyre("This bot only works in servers")?;
|
.ok_or_eyre("This bot only works in servers")?;
|
||||||
let mut to = channel_children(&ctx, to).await?;
|
let mut to = channel_children(&ctx, to).await?;
|
||||||
let mut users = from.members(ctx)?;
|
let mut users = from.members(ctx)?;
|
||||||
{
|
users.shuffle(&mut rand::thread_rng());
|
||||||
let mut rng = rand::rng();
|
|
||||||
users.shuffle(&mut rng);
|
|
||||||
|
|
||||||
to.retain(|x| x.members(ctx).is_ok_and(|x| x.is_empty()));
|
to.retain(|x| x.members(ctx).is_ok_and(|x| x.is_empty()));
|
||||||
to.shuffle(&mut rng);
|
to.shuffle(&mut rand::thread_rng());
|
||||||
}
|
|
||||||
move_users(
|
move_users(
|
||||||
ctx,
|
ctx,
|
||||||
guild,
|
guild,
|
||||||
@@ -271,88 +267,62 @@ async fn st(ctx: Context<'_>) -> Result<()> {
|
|||||||
.guild_id()
|
.guild_id()
|
||||||
.ok_or_eyre("This bot only works in servers")?;
|
.ok_or_eyre("This bot only works in servers")?;
|
||||||
let GuildData { st, .. } = ctx.data().get(guild).await?;
|
let GuildData { st, .. } = ctx.data().get(guild).await?;
|
||||||
let sender = ctx.author().id;
|
|
||||||
|
|
||||||
futures::future::try_join_all(guild.members(&ctx, None, None).await?.into_iter().map(
|
futures::future::try_join_all(guild.members(&ctx, None, None).await?.into_iter().map(
|
||||||
|member| async move {
|
|mut member| async move {
|
||||||
match (sender == member.user.id, member.roles.contains(&st)) {
|
match (&member.user == ctx.author(), member.roles.contains(&st)) {
|
||||||
(true, true) | (false, false) => Ok(()),
|
(true, true) | (false, false) => Ok(()),
|
||||||
(true, false) => member.add_role(&ctx, st).await,
|
(true, false) => {
|
||||||
(false, true) => member.remove_role(&ctx, st).await,
|
member
|
||||||
|
.edit(
|
||||||
|
&ctx,
|
||||||
|
EditMember::new().nickname(format!("(ST) {}", member.display_name())),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
member.add_role(&ctx, st).await
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
member
|
||||||
|
.edit(
|
||||||
|
&ctx,
|
||||||
|
EditMember::new().nickname(
|
||||||
|
member
|
||||||
|
.display_name()
|
||||||
|
.strip_prefix("(ST) ")
|
||||||
|
.unwrap_or(member.display_name()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
member.remove_role(&ctx, st).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
ctx.reply("You are now the ST").await?;
|
ctx.reply("You have been granted ST").await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[poise::command(slash_command, ephemeral)]
|
#[poise::command(slash_command, ephemeral)]
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
async fn spectate(ctx: Context<'_>, player: Option<serenity::UserId>) -> Result<()> {
|
async fn currently_playing(ctx: Context<'_>, players: Vec<serenity::UserId>) -> Result<()> {
|
||||||
let guild = ctx
|
|
||||||
.guild_id()
|
|
||||||
.ok_or_eyre("This bot only works in servers")?;
|
|
||||||
let GuildData { st, .. } = ctx.data().get(guild).await?;
|
|
||||||
|
|
||||||
let member = guild
|
|
||||||
.member(&ctx, player.unwrap_or(ctx.author().id))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let msg = if member.roles.contains(&st) {
|
|
||||||
member.remove_role(&ctx, st).await?;
|
|
||||||
"no longer a spectator"
|
|
||||||
} else {
|
|
||||||
member.add_role(&ctx, st).await?;
|
|
||||||
"now a spectator"
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.reply(if player.is_some() {
|
|
||||||
format!("{} is {}", member.mention(), msg)
|
|
||||||
} else {
|
|
||||||
format!("You are {}", msg)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[poise::command(slash_command, ephemeral)]
|
|
||||||
#[tracing::instrument]
|
|
||||||
async fn currently_playing(ctx: Context<'_>, player: Option<serenity::UserId>) -> Result<()> {
|
|
||||||
let guild = ctx
|
let guild = ctx
|
||||||
.guild_id()
|
.guild_id()
|
||||||
.ok_or_eyre("This bot only works in servers")?;
|
.ok_or_eyre("This bot only works in servers")?;
|
||||||
let GuildData {
|
let GuildData {
|
||||||
currently_playing, ..
|
currently_playing, ..
|
||||||
} = ctx.data().get(guild).await?;
|
} = ctx.data().get(guild).await?;
|
||||||
|
for player in &players {
|
||||||
let member = guild
|
guild
|
||||||
.member(&ctx, player.unwrap_or(ctx.author().id))
|
.member(&ctx, player)
|
||||||
|
.await?
|
||||||
|
.add_role(&ctx, currently_playing)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
let msg = if member.roles.contains(¤tly_playing) {
|
ctx.reply(format!("Given {} members currently playing", players.len()))
|
||||||
member.remove_role(&ctx, currently_playing).await?;
|
|
||||||
"no longer"
|
|
||||||
} else {
|
|
||||||
member.add_role(&ctx, currently_playing).await?;
|
|
||||||
"now"
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.reply(if player.is_some() {
|
|
||||||
format!(
|
|
||||||
"{} is {} {}",
|
|
||||||
member.mention(),
|
|
||||||
msg,
|
|
||||||
currently_playing.mention()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!("You are {} {}", msg, currently_playing.mention())
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,38 +363,16 @@ async fn register_commands(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(ctx))]
|
#[tracing::instrument(skip(ctx, framework))]
|
||||||
async fn handle_message(
|
|
||||||
ctx: impl AsRef<serenity::Http>,
|
|
||||||
message: &serenity::Message,
|
|
||||||
) -> Result<()> {
|
|
||||||
tracing::info!("Got message: {}", message.content);
|
|
||||||
if message.content.contains("https://clocktower.live") {
|
|
||||||
let mut message = message.clone();
|
|
||||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
||||||
message
|
|
||||||
.edit(&ctx.as_ref(), EditMessage::new().suppress_embeds(true))
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_event(
|
async fn on_event(
|
||||||
ctx: &serenity::Context,
|
ctx: &serenity::Context,
|
||||||
event: &serenity::FullEvent,
|
event: &serenity::FullEvent,
|
||||||
framework: poise::FrameworkContext<'_, Data, Error>,
|
framework: poise::FrameworkContext<'_, Data, Error>,
|
||||||
_: &Data,
|
_: &Data,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match event {
|
if let serenity::FullEvent::GuildCreate { guild, is_new: _ } = event {
|
||||||
serenity::FullEvent::GuildCreate { guild, is_new: _ } => {
|
register_commands(ctx, framework.options, guild.id).await?;
|
||||||
register_commands(ctx, framework.options, guild.id).await?
|
};
|
||||||
}
|
|
||||||
serenity::FullEvent::Message { new_message: msg }
|
|
||||||
| serenity::FullEvent::MessageUpdate { new: Some(msg), .. } => {
|
|
||||||
handle_message(ctx, msg).await?
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,10 +396,8 @@ async fn main() -> Result<()> {
|
|||||||
)
|
)
|
||||||
.wrap_err("A Discord token is required")?;
|
.wrap_err("A Discord token is required")?;
|
||||||
let dbfile = std::env::var_os("DB_FILE").ok_or_eyre("A database file is required")?;
|
let dbfile = std::env::var_os("DB_FILE").ok_or_eyre("A database file is required")?;
|
||||||
let intents = serenity::GatewayIntents::non_privileged()
|
let intents =
|
||||||
| serenity::GatewayIntents::GUILD_MEMBERS
|
serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::GUILD_MEMBERS;
|
||||||
| serenity::GatewayIntents::GUILD_MESSAGES
|
|
||||||
| serenity::GatewayIntents::MESSAGE_CONTENT;
|
|
||||||
|
|
||||||
let framework = poise::Framework::builder()
|
let framework = poise::Framework::builder()
|
||||||
.options(poise::FrameworkOptions {
|
.options(poise::FrameworkOptions {
|
||||||
@@ -459,7 +405,6 @@ async fn main() -> Result<()> {
|
|||||||
dusk(),
|
dusk(),
|
||||||
dawn(),
|
dawn(),
|
||||||
st(),
|
st(),
|
||||||
spectate(),
|
|
||||||
currently_playing(),
|
currently_playing(),
|
||||||
join(),
|
join(),
|
||||||
configure(),
|
configure(),
|
||||||
|
|||||||
Reference in New Issue
Block a user