Add working bot
This commit is contained in:
parent
fb6cfa8e08
commit
83c753add8
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
.direnv
|
||||||
|
.envrc.local
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "rolebot"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serenity = { version = "0.11.5", default-features = false, features = ["builder", "cache", "chrono", "client", "gateway", "model", "http", "utils", "rustls_backend"] }
|
||||||
|
tokio = { version = "1.28.0", features = ["rt-multi-thread"] }
|
||||||
|
tracing = "0.1.37"
|
||||||
|
tracing-subscriber = "0.3.17"
|
|
@ -0,0 +1,210 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"devshell": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1682700442,
|
||||||
|
"narHash": "sha256-qjaAAcCYgp1pBBG7mY9z95ODUBZMtUpf0Qp3Gt/Wha0=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "devshell",
|
||||||
|
"rev": "fb6673fe9fe4409e3f43ca86968261e970918a83",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "devshell",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1642700792,
|
||||||
|
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681202837,
|
||||||
|
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"naersk": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1679567394,
|
||||||
|
"narHash": "sha256-ZvLuzPeARDLiQUt6zSZFGOs+HZmE+3g4QURc8mkBsfM=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "naersk",
|
||||||
|
"rev": "88cd22380154a2c36799fe8098888f0f59861a15",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "naersk",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1677383253,
|
||||||
|
"narHash": "sha256-UfpzWfSxkfXHnb4boXZNaKsAcUrZT9Hw+tao1oZxd08=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "9952d6bc395f5841262b006fbace8dd7e143b634",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1678101631,
|
||||||
|
"narHash": "sha256-vuuvWBNGhNSPPbFCjp2XZmBqJOvsFF1T0hyleRnHZlc=",
|
||||||
|
"path": "/nix/store/9hi0ykjc2h1hzldcqdfmjgw2hp0lld2v-source",
|
||||||
|
"rev": "934e613c31cf7af0624dcf088b9e2d9b802d0717",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1678101631,
|
||||||
|
"narHash": "sha256-vuuvWBNGhNSPPbFCjp2XZmBqJOvsFF1T0hyleRnHZlc=",
|
||||||
|
"path": "/nix/store/9hi0ykjc2h1hzldcqdfmjgw2hp0lld2v-source",
|
||||||
|
"rev": "934e613c31cf7af0624dcf088b9e2d9b802d0717",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_4": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681358109,
|
||||||
|
"narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devshell": "devshell",
|
||||||
|
"naersk": "naersk",
|
||||||
|
"nixpkgs": "nixpkgs_3",
|
||||||
|
"rust-overlay": "rust-overlay",
|
||||||
|
"utils": "utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs_4"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1683426137,
|
||||||
|
"narHash": "sha256-iqLjaJDMppvK1ae1eL55b2r7EX+rbUuEnssFOe9eOm8=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "546a8209ce0965475495dd422e4ab3c6de7a268c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681202837,
|
||||||
|
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
description = "A discord bot that allows users to add and remove themself to roles";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
utils.url = "github:numtide/flake-utils";
|
||||||
|
devshell.url = "github:numtide/devshell";
|
||||||
|
naersk.url = "github:nix-community/naersk";
|
||||||
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
utils,
|
||||||
|
naersk,
|
||||||
|
devshell,
|
||||||
|
rust-overlay,
|
||||||
|
}:
|
||||||
|
utils.lib.eachDefaultSystem (system: let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [(import rust-overlay)];
|
||||||
|
};
|
||||||
|
rust = (pkgs.rust-bin.stable.latest.default.override {
|
||||||
|
extensions = [ "rust-src" ];
|
||||||
|
});
|
||||||
|
# Override naersk to use our chosen rust version from rust-overlay
|
||||||
|
naersk-lib = naersk.lib.${system}.override {
|
||||||
|
cargo = rust;
|
||||||
|
rustc = rust;
|
||||||
|
};
|
||||||
|
in rec {
|
||||||
|
packages.default = naersk-lib.buildPackage {
|
||||||
|
pname = "rolebot";
|
||||||
|
root = ./.;
|
||||||
|
};
|
||||||
|
|
||||||
|
apps.default = utils.lib.mkApp {drv = packages.default;};
|
||||||
|
|
||||||
|
# Provide a dev env with rust and rls
|
||||||
|
devShells.default = let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [devshell.overlays.default];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
pkgs.devshell.mkShell {
|
||||||
|
packages = with pkgs; [rust rust-analyzer];
|
||||||
|
};
|
||||||
|
formatter = pkgs.alejandra;
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
use std::env;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use serenity::async_trait;
|
||||||
|
use serenity::builder::CreateApplicationCommands;
|
||||||
|
use serenity::model::application::command::Command;
|
||||||
|
use serenity::model::application::interaction::{Interaction, InteractionResponseType};
|
||||||
|
use serenity::model::gateway::Ready;
|
||||||
|
use serenity::model::id::GuildId;
|
||||||
|
use serenity::model::prelude::command::CommandOptionType;
|
||||||
|
use serenity::model::prelude::interaction::application_command::{
|
||||||
|
ApplicationCommandInteraction, CommandDataOptionValue,
|
||||||
|
};
|
||||||
|
use serenity::model::prelude::Role;
|
||||||
|
use serenity::prelude::*;
|
||||||
|
use tracing::{event, Level};
|
||||||
|
|
||||||
|
type StdErr<T> = Result<T, Box<dyn Error + Send + Sync>>;
|
||||||
|
|
||||||
|
struct Handler;
|
||||||
|
|
||||||
|
async fn user_to_role(ctx: &Context, cmd: &ApplicationCommandInteraction) -> StdErr<String> {
|
||||||
|
if let Some(role_option) = cmd.data.options.iter().find(|opt| opt.name == "role") {
|
||||||
|
if let Some(CommandDataOptionValue::Role(role)) = &role_option.resolved {
|
||||||
|
let guild_id = cmd.guild_id.ok_or("No guild id in command")?.into();
|
||||||
|
let user_id = cmd.user.id.into();
|
||||||
|
if cmd.data.name == "giverole" {
|
||||||
|
ctx.http
|
||||||
|
.add_member_role(
|
||||||
|
guild_id,
|
||||||
|
user_id,
|
||||||
|
role.id.into(),
|
||||||
|
Some("User added themselves to role via bot."),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
ctx.http
|
||||||
|
.remove_member_role(
|
||||||
|
guild_id,
|
||||||
|
user_id,
|
||||||
|
role.id.into(),
|
||||||
|
Some("User removed themselves from role via bot."),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok("Role updated".into())
|
||||||
|
} else {
|
||||||
|
Err("Invalid role value".into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("No role option given".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(r: StdErr<String>) -> String {
|
||||||
|
r.unwrap_or_else(|error| {
|
||||||
|
event!(Level::ERROR, ?error, "Error handling command");
|
||||||
|
"Sorry, there was an error while handling that command.".into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_commands(cmds: &mut CreateApplicationCommands) -> &mut CreateApplicationCommands {
|
||||||
|
cmds.create_application_command(|cmd| cmd.name("ping").description("Go pong!"))
|
||||||
|
.create_application_command(|cmd| cmd.name("roles").description("List available roles"))
|
||||||
|
.create_application_command(|cmd| {
|
||||||
|
cmd.name("giverole")
|
||||||
|
.description("Add yourself to a role")
|
||||||
|
.create_option(|c| {
|
||||||
|
c.name("role")
|
||||||
|
.kind(CommandOptionType::Role)
|
||||||
|
.description("The role you want to be added to")
|
||||||
|
.required(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.create_application_command(|cmd| {
|
||||||
|
cmd.name("takerole")
|
||||||
|
.description("Remove yourself from a role")
|
||||||
|
.create_option(|c| {
|
||||||
|
c.name("role")
|
||||||
|
.kind(CommandOptionType::Role)
|
||||||
|
.description("The role you want to be removed from")
|
||||||
|
.required(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EventHandler for Handler {
|
||||||
|
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||||
|
if let Interaction::ApplicationCommand(command) = interaction {
|
||||||
|
println!("Received command interaction: {:#?}", command);
|
||||||
|
|
||||||
|
let content = match command.data.name.as_str() {
|
||||||
|
"ping" => "pong!".to_string(),
|
||||||
|
"giverole" => handle(user_to_role(&ctx, &command).await).await,
|
||||||
|
"takerole" => handle(user_to_role(&ctx, &command).await).await,
|
||||||
|
_ => "not implemented :(".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(why) = command
|
||||||
|
.create_interaction_response(&ctx.http, |response| {
|
||||||
|
response
|
||||||
|
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|message| {
|
||||||
|
message.content(content).ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
println!("Cannot respond to slash command: {}", why);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||||
|
println!("{} is connected!", ready.user.name);
|
||||||
|
|
||||||
|
// If a guild id is given, run in dev mode and register commands to that guild.
|
||||||
|
if let Ok(gid) = env::var("GUILD_ID") {
|
||||||
|
event!(Level::INFO, ?gid, "Running in debug mode for guild");
|
||||||
|
let guild_id = GuildId(gid.parse().expect("GUILD_ID must be int"));
|
||||||
|
let commands = GuildId::set_application_commands(&guild_id, &ctx.http, |commands| {
|
||||||
|
register_commands(commands)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
let guild_command = Command::set_global_application_commands(&ctx.http, |commands| {
|
||||||
|
register_commands(commands)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> StdErr<()> {
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
// Configure the client with your Discord bot token in the environment.
|
||||||
|
let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
|
||||||
|
|
||||||
|
// Build our client.
|
||||||
|
let mut client = Client::builder(token, GatewayIntents::empty())
|
||||||
|
.event_handler(Handler)
|
||||||
|
.await
|
||||||
|
.expect("Error creating client");
|
||||||
|
|
||||||
|
Ok(client.start().await?)
|
||||||
|
}
|
Loading…
Reference in New Issue