Slash commands are cool
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

150 satır
6.2KB

  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::{InteractionResponse, InteractionApplicationCommandCallbackData, ApplicationCommand};
  11. use crate::command_definitions::hello_world;
  12. use std::collections::{HashMap, HashSet};
  13. static GLOBAL_COMMAND_KEY: &str = "GLOBAL command KEY";
  14. lazy_static! {
  15. pub static ref VERIFICATION_KEY: RwLock<Option<ed25519_dalek::PublicKey>> = RwLock::new(None);
  16. pub static ref APPLICATION_ID: RwLock<Option<String>> = RwLock::new(None);
  17. pub static ref BOT_TOKEN: RwLock<Option<String>> = RwLock::new(None);
  18. }
  19. #[post("/", data = "<interaction>")]
  20. fn root_post(interaction: AuthenticatedInteraction) -> Json<InteractionResponse> {
  21. if interaction.interaction.is_ping() {
  22. return Json(InteractionResponse::Pong {})
  23. }
  24. let resp = InteractionResponse::ChannelMessage {
  25. data: Some(InteractionApplicationCommandCallbackData {
  26. tts: false,
  27. content: "Hello".to_string(),
  28. allowed_mentions: None,
  29. }),
  30. };
  31. Json(resp)
  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 App Id {}", &app_id);
  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. let mut seen_cmds = HashMap::<String, HashSet<String>>::new();
  58. // TODO
  59. register_command(&hello_world, &req_client, &mut seen_cmds);
  60. // start web server
  61. rocket::ignite().mount("/", routes![root_post, hello_get]).launch();
  62. }
  63. fn register_command(f: &dyn Fn() -> (discord::ApplicationCommand, Option<String>),
  64. client: &reqwest::blocking::Client,
  65. registered: &mut HashMap<String, HashSet<String>>) {
  66. let (payload, guild_opt) = f();
  67. if let Some(guild_id) = guild_opt {
  68. if !registered.contains_key(&guild_id) {
  69. let mut seen_guild_cmds = HashSet::new();
  70. let cmds = get_guild_commands(client, &guild_id);
  71. for c in cmds {
  72. println!("Found app command {} ID:{} App:{}", &c.name, &c.id.unwrap(), guild_id);
  73. seen_guild_cmds.insert(c.name);
  74. }
  75. registered.insert(guild_id.clone(), seen_guild_cmds);
  76. }
  77. if !registered.get(&guild_id).unwrap().contains(&payload.name) {
  78. // create new command
  79. let url = format!("https://discord.com/api/v8/applications/{}/guilds/{}/commands", APPLICATION_ID.read().unwrap().as_ref().unwrap().clone(), &guild_id);
  80. let res = client.post(&url)
  81. .header("Authorization", format!("Bot {}", BOT_TOKEN.read().unwrap().as_ref().unwrap().clone()))
  82. .json(&payload)
  83. .send();
  84. if let Ok(d) = res {
  85. println!("`{}` status {}", &payload.name, &d.status().as_str());
  86. println!("{}", &d.text().unwrap());
  87. registered.get_mut(&guild_id).unwrap().insert(payload.name);
  88. }
  89. }
  90. } else {
  91. if !registered.contains_key(GLOBAL_COMMAND_KEY) {
  92. let mut seen_cmds = HashSet::new();
  93. let cmds = get_commands(client);
  94. for c in cmds {
  95. println!("Found global command {} ID:{}", &c.name, &c.id.unwrap());
  96. seen_cmds.insert(c.name);
  97. }
  98. registered.insert(GLOBAL_COMMAND_KEY.to_string(), seen_cmds);
  99. }
  100. if !registered.get(GLOBAL_COMMAND_KEY).unwrap().contains(&payload.name) {
  101. // create new command
  102. let url = format!("https://discord.com/api/v8/applications/{}/commands", APPLICATION_ID.read().unwrap().as_ref().unwrap().clone());
  103. let res = client.post(&url)
  104. .header("Authorization", format!("Bot {}", BOT_TOKEN.read().unwrap().as_ref().unwrap().clone()))
  105. .json(&payload)
  106. .send();
  107. if let Ok(d) = res {
  108. println!("`{}` status {}", &payload.name, &d.status().as_str());
  109. println!("{}", &d.text().unwrap());
  110. registered.get_mut(GLOBAL_COMMAND_KEY.clone()).unwrap().insert(payload.name);
  111. }
  112. }
  113. }
  114. }
  115. fn get_commands(client: &reqwest::blocking::Client) -> Vec<ApplicationCommand> {
  116. let url = format!("https://discord.com/api/v8/applications/{}/commands", APPLICATION_ID.read().unwrap().as_ref().unwrap().clone());
  117. let res = client.get(&url)
  118. .header("Authorization", format!("Bot {}", BOT_TOKEN.read().unwrap().as_ref().unwrap().clone()))
  119. .send();
  120. if let Ok(d) = res {
  121. println!("Commands GET status {}", &d.status().as_str());
  122. return d.json().unwrap();
  123. }
  124. return Vec::new();
  125. }
  126. fn get_guild_commands(client: &reqwest::blocking::Client, guild_id: &str) -> Vec<ApplicationCommand> {
  127. let url = format!("https://discord.com/api/v8/applications/{}/guilds/{}/commands", APPLICATION_ID.read().unwrap().as_ref().unwrap().clone(), guild_id);
  128. let res = client.get(&url)
  129. .header("Authorization", format!("Bot {}", BOT_TOKEN.read().unwrap().as_ref().unwrap().clone()))
  130. .send();
  131. if let Ok(d) = res {
  132. println!("Commands GET status {}", &d.status().as_str());
  133. return d.json().unwrap();
  134. }
  135. return Vec::new();
  136. }