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