Slash commands are cool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 line
5.0KB

  1. #![feature(proc_macro_hygiene, decl_macro)]
  2. mod auth_tools;
  3. mod discord;
  4. mod command_definitions;
  5. #[macro_use] extern crate rocket;
  6. use lazy_static::lazy_static;
  7. use rocket_contrib::json::Json;
  8. use std::sync::RwLock;
  9. use crate::auth_tools::{AuthenticatedInteraction};
  10. use crate::discord::{Interaction, InteractionResponse, /*ApplicationCommand, */InteractionResponseRaw};
  11. use crate::command_definitions::{hello_world, def_hello_world};
  12. static GLOBAL_COMMAND_KEY: &str = "GLOBAL command KEY";
  13. lazy_static! {
  14. pub static ref VERIFICATION_KEY: RwLock<Option<ed25519_dalek::PublicKey>> = RwLock::new(None);
  15. pub static ref APPLICATION_ID: RwLock<Option<String>> = RwLock::new(None);
  16. pub static ref BOT_TOKEN: RwLock<Option<String>> = RwLock::new(None);
  17. }
  18. #[post("/", data = "<interaction>")]
  19. fn root_post(interaction: AuthenticatedInteraction) -> Json<InteractionResponseRaw> {
  20. Json(
  21. match &interaction.interaction {
  22. Interaction::Ping {} => InteractionResponse::Pong {},
  23. Interaction::Command {data, ..} => {
  24. match &data.name as &str {
  25. "hello-world" => hello_world(&interaction.interaction),
  26. _ => InteractionResponse::AcknowledgeWithSource {},
  27. }
  28. },
  29. _ => InteractionResponse::AcknowledgeWithSource {},
  30. }.raw()
  31. )
  32. }
  33. #[get("/")]
  34. fn hello_get() -> &'static str {
  35. "Hello, why are you here?"
  36. }
  37. fn main() {
  38. // Init verification key
  39. let public_key = std::env::var("DISCORD_PUBLIC_KEY")
  40. .expect("Environment variable DISCORD_PUBLIC_KEY not found");
  41. println!("Discord Pub Key {}", &public_key);
  42. *VERIFICATION_KEY.write().unwrap() = Some(ed25519_dalek::PublicKey::from_bytes(
  43. &hex::decode(public_key)
  44. .expect("Invalid hex string")).expect("Invalid public key"));
  45. // get application ID for sending API requests
  46. let app_id = std::env::var("DISCORD_APP_ID")
  47. .expect("Environment variable DISCORD_APP_ID not found");
  48. println!("Discord App Id {}", &app_id);
  49. *APPLICATION_ID.write().unwrap() = Some(app_id.to_string());
  50. // get bot token for sending API requests
  51. let token = std::env::var("DISCORD_TOKEN")
  52. .expect("Environment variable DISCORD_TOKEN not found");
  53. println!("Discord Token {}", &token);
  54. *BOT_TOKEN.write().unwrap() = Some(token.to_string());
  55. // send API requests to bootstrap commands
  56. let req_client = reqwest::blocking::Client::new();
  57. // TODO add more commands
  58. register_command(&def_hello_world, &req_client);
  59. // start web server
  60. rocket::ignite().mount("/", routes![root_post, hello_get]).launch();
  61. }
  62. fn register_command(f: &dyn Fn() -> (discord::ApplicationCommand, Option<String>),
  63. client: &reqwest::blocking::Client) {
  64. let (payload, guild_opt) = f();
  65. if let Some(guild_id) = guild_opt {
  66. // create/upsert command
  67. let url = format!("https://discord.com/api/v8/applications/{}/guilds/{}/commands", APPLICATION_ID.read().unwrap().as_ref().unwrap().clone(), &guild_id);
  68. let res = client.post(&url)
  69. .header("Authorization", format!("Bot {}", BOT_TOKEN.read().unwrap().as_ref().unwrap().clone()))
  70. .json(&payload)
  71. .send();
  72. if let Ok(d) = res {
  73. println!("`{}` status {}", &payload.name, &d.status().as_str());
  74. println!("{}", &d.text().unwrap());
  75. }
  76. } else {
  77. // create/upsert command
  78. let url = format!("https://discord.com/api/v8/applications/{}/commands", APPLICATION_ID.read().unwrap().as_ref().unwrap().clone());
  79. let res = client.post(&url)
  80. .header("Authorization", format!("Bot {}", BOT_TOKEN.read().unwrap().as_ref().unwrap().clone()))
  81. .json(&payload)
  82. .send();
  83. if let Ok(d) = res {
  84. println!("`{}` status {}", &payload.name, &d.status().as_str());
  85. println!("{}", &d.text().unwrap());
  86. }
  87. }
  88. }
  89. /*
  90. fn get_commands(client: &reqwest::blocking::Client) -> Vec<ApplicationCommand> {
  91. let url = format!("https://discord.com/api/v8/applications/{}/commands", APPLICATION_ID.read().unwrap().as_ref().unwrap().clone());
  92. let res = client.get(&url)
  93. .header("Authorization", format!("Bot {}", BOT_TOKEN.read().unwrap().as_ref().unwrap().clone()))
  94. .send();
  95. if let Ok(d) = res {
  96. println!("Commands GET status {}", &d.status().as_str());
  97. return d.json().unwrap();
  98. }
  99. return Vec::new();
  100. }
  101. fn get_guild_commands(client: &reqwest::blocking::Client, guild_id: &str) -> Vec<ApplicationCommand> {
  102. let url = format!("https://discord.com/api/v8/applications/{}/guilds/{}/commands", APPLICATION_ID.read().unwrap().as_ref().unwrap().clone(), guild_id);
  103. let res = client.get(&url)
  104. .header("Authorization", format!("Bot {}", BOT_TOKEN.read().unwrap().as_ref().unwrap().clone()))
  105. .send();
  106. if let Ok(d) = res {
  107. println!("Commands GET status {}", &d.status().as_str());
  108. return d.json().unwrap();
  109. }
  110. return Vec::new();
  111. }
  112. */