Productivity bot for Discord
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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

279 lines
13KB

  1. use std::env;
  2. use std::str::FromStr;
  3. extern crate serenity;
  4. use serenity::{
  5. model::channel::Message,
  6. //model::channel::Attachment,
  7. prelude::*,
  8. utils::MessageBuilder,
  9. utils::Colour,
  10. };
  11. extern crate regex;
  12. use regex::{Regex, RegexBuilder};
  13. extern crate swagger;
  14. use swagger::apis;
  15. //use swagger::apis::RepositoryApi;
  16. extern crate hyper;
  17. use hyper::{Client, Uri};
  18. extern crate hyper_tls;
  19. use hyper_tls::HttpsConnector;
  20. extern crate tokio_core;
  21. use tokio_core::reactor::Core;
  22. extern crate futures;
  23. use futures::prelude::Future;
  24. use crate::traits::Command;
  25. pub struct CmdGitea {
  26. format: Regex,
  27. help_format: Regex,
  28. }
  29. impl Command for CmdGitea {
  30. fn execute(&mut self, ctx: &Context, msg: &Message) {
  31. if let Some(parsed) = self.format.captures(&msg.content) {
  32. if let Some(op) = parsed.get(1) {
  33. let op_str = op.as_str().to_lowercase();
  34. if op_str != "release" && op_str != "repo" && op_str != "repository" {
  35. let response = MessageBuilder::new()
  36. .push("Invalid operation specified. Supported operations are `release,repo`")
  37. .build();
  38. if let Err(why) = msg.channel_id.say(&ctx.http, response) {
  39. println!("Failed to send gitea invalid option message {:?}", why);
  40. }
  41. }
  42. if let Some(full_url) = parsed.get(2) {
  43. let token = env::var("GITEA_TOKEN").expect("Expected a Gitea API token in GITEA_TOKEN environment variable");
  44. let mut core = Core::new().unwrap();
  45. //let https = HttpsConnector::new();
  46. let handler = core.handle();
  47. let client = Client::configure()
  48. .connector(HttpsConnector::new(4, &handler).unwrap())
  49. .build(&handler);
  50. let mut config = apis::configuration::Configuration::new(client);
  51. config.api_key = Some(apis::configuration::ApiKey {
  52. prefix: Some("token ".to_string()),
  53. key: token,
  54. });
  55. match Uri::from_str(full_url.as_str()) {
  56. Err(why) => {
  57. let response = MessageBuilder::new()
  58. .push(format!("Could not parse repository URL {:?}", why))
  59. .build();
  60. if let Err(why) = msg.channel_id.say(&ctx.http, response) {
  61. println!("Failed to send gitea error message {:?}", why);
  62. return;
  63. }
  64. }
  65. Ok(url) => {
  66. config.base_path = url.scheme().unwrap().to_owned() + "://" + url.host().unwrap() + "/api/v1";
  67. let client = apis::client::APIClient::new(config);
  68. if let Some(owner) = parsed.get(3) {
  69. if let Some(repo_name) = parsed.get(4) {
  70. if op_str == "release" {
  71. self.get_release(ctx, msg, full_url.as_str(), owner.as_str(), repo_name.as_str(), &client, &mut core);
  72. return;
  73. } else if op_str == "repo" || op_str == "repository" {
  74. self.get_repo(ctx, msg, full_url.as_str(), owner.as_str(), repo_name.as_str(), &client, &mut core);
  75. return;
  76. }
  77. }
  78. }
  79. }
  80. }
  81. }
  82. }
  83. }
  84. let response = MessageBuilder::new()
  85. .push(format!("Unknown error occurred"))
  86. .build();
  87. if let Err(why) = msg.channel_id.say(&ctx.http, response) {
  88. println!("Failed to send gitea unknown error message {:?}", why);
  89. return;
  90. }
  91. }
  92. fn valid(&self, _ctx: &Context, msg: &Message) -> bool {
  93. return self.format.is_match(&msg.content) && !msg.author.bot;
  94. }
  95. fn help(&self, ctx: &Context, msg:&Message) {
  96. let mut response = MessageBuilder::new();
  97. response.push("**Access the gitea API**\n!gitea release (repository URL)\n!gitea repo (repository URL)");
  98. if let Err(why) = msg.channel_id.say(&ctx.http, &response.build()) {
  99. println!("Failed to send gitea help message {:?}", why);
  100. }
  101. }
  102. fn valid_help(&self, _ctx: &Context, msg: &Message) -> bool {
  103. return self.help_format.is_match(&msg.content);
  104. }
  105. }
  106. impl CmdGitea {
  107. pub fn new() -> CmdGitea {
  108. return CmdGitea {
  109. format:
  110. RegexBuilder::new(r#"^!gitea\s+(release|repo|repository)\s+<?(https?://[^/\s]+/([^/\s]+)/([^/\s>]+))>?"#)
  111. .multi_line(true)
  112. .case_insensitive(true)
  113. .build()
  114. .unwrap(),
  115. help_format:
  116. RegexBuilder::new(r#"^!help\s*(?:gitea)"#)
  117. .case_insensitive(true)
  118. .build()
  119. .unwrap(),
  120. };
  121. }
  122. fn get_release<C>(&self, ctx: &Context, msg: &Message, full_url: &str, owner: &str, repo_name: &str, client: &apis::client::APIClient<C>, core: &mut Core) where C : hyper::client::Connect {
  123. let future = client.repository_api().repo_list_releases(owner, repo_name, 1, 5);
  124. //let result = async {future.await;};
  125. if let Err(async_err) = core.run(future.then(|result| {
  126. match result {
  127. Ok(items) => {
  128. // items is Vec of release
  129. if items.len() > 0 {
  130. //println!("Release info {:?}", items[0]);
  131. // releases found, use first one (most recent)
  132. if let Err(why) = msg.channel_id.send_message(&ctx.http, |m| {
  133. return m.embed(|e| {
  134. let mut files = "".to_owned();
  135. for f in items[0].assets().unwrap() {
  136. files += &format!("[{}]({})\n", f.name().unwrap(), f.browser_download_url().unwrap())
  137. }
  138. if files.len() > 4 {
  139. e.field("Download", files, true);
  140. }
  141. return e.colour(Colour::from_rgb(0, 200, 0))
  142. .title(format!("{} {}", repo_name, items[0].tag_name().unwrap()))
  143. .description(items[0].body().unwrap())
  144. .author(|a| {
  145. return a.name(items[0].name().unwrap())
  146. .url(full_url.to_owned() + "/releases");
  147. })
  148. .footer(|f| {
  149. return f.text(items[0].author().unwrap().login().unwrap())
  150. .icon_url(items[0].author().unwrap().avatar_url().unwrap());
  151. })
  152. });
  153. }) {
  154. println!("Failed to send gitea release message {:?}", why);
  155. return futures::future::ok::<u32, u32>(42);
  156. }
  157. } else {
  158. let response = MessageBuilder::new()
  159. .push(format!("No releases found for <{}>", full_url))
  160. .build();
  161. if let Err(why) = msg.channel_id.say(&ctx.http, response) {
  162. println!("Failed to send gitea error message {:?}", why);
  163. return futures::future::ok::<u32, u32>(42);
  164. }
  165. }
  166. return futures::future::ok::<u32, u32>(42);
  167. }
  168. Err(reason) => {
  169. println!("Gitea API request failed {:?}", reason);
  170. let response = MessageBuilder::new()
  171. .push(format!("Gitea API request failed {:?}", reason))
  172. .build();
  173. if let Err(why) = msg.channel_id.say(&ctx.http, response) {
  174. println!("Failed to send gitea error message {:?}", why);
  175. return futures::future::ok::<u32, u32>(42);
  176. }
  177. return futures::future::ok::<u32, u32>(42);
  178. }
  179. }
  180. })) {
  181. let response = MessageBuilder::new()
  182. .push(format!("Gitea API request failed to complete {:?}", async_err))
  183. .build();
  184. if let Err(why) = msg.channel_id.say(&ctx.http, response) {
  185. println!("Failed to send gitea error message {:?}", why);
  186. return;
  187. }
  188. return;
  189. }
  190. //return;
  191. }
  192. fn get_repo<C>(&self, ctx: &Context, msg: &Message, full_url: &str, owner: &str, repo_name: &str, client: &apis::client::APIClient<C>, core: &mut Core) where C : hyper::client::Connect {
  193. let future = client.repository_api().repo_get(owner, repo_name);
  194. match core.run(future) {
  195. Ok(repo) => {
  196. if let Err(why) = msg.channel_id.send_message(&ctx.http, |m| {
  197. //println!("{}", repo.clone_url().unwrap());
  198. if *repo.empty().unwrap() {
  199. return m.embed(|e| {
  200. return e.colour(Colour::from_rgb(200, 42, 42)) // ~dark red
  201. .title("")
  202. .description("-- Empty Repository --")
  203. .author(|a| {
  204. return a.icon_url(repo.avatar_url().unwrap())
  205. .name(repo.name().unwrap())
  206. .url(full_url);
  207. })
  208. .footer(|f| {
  209. let owner = repo.owner().unwrap();
  210. return f.text(owner.login().unwrap())
  211. .icon_url(owner.avatar_url().unwrap());
  212. });
  213. });
  214. }
  215. return m.embed(|e| {
  216. let mut desc = format!("{}", repo.description().unwrap());
  217. if let Some(website) = repo.website() {
  218. if website == "" {
  219. desc = format!("{desc}\n[Releases]({html}/releases)", desc=desc, html=repo.html_url().unwrap());
  220. } else {
  221. desc = format!("{desc}\n[Website]({url})\n[Releases]({html}/releases)", desc=desc, url=website, html=repo.html_url().unwrap());
  222. }
  223. }
  224. //desc = format!("{}\nCreated at {} (updated at {})", desc, repo.created_at().unwrap(), repo.updated_at().unwrap());
  225. return e.colour(Colour::from_rgb(128, 42, 42)) // ~brown
  226. .title("")
  227. .description(desc)
  228. .author(|a| {
  229. return a.icon_url(repo.avatar_url().unwrap())
  230. .name(format!("{}", repo.name().unwrap()))
  231. .url(full_url);
  232. })
  233. .footer(|f| {
  234. let owner = repo.owner().unwrap();
  235. return f.text(owner.login().unwrap())
  236. .icon_url(owner.avatar_url().unwrap());
  237. })
  238. .field("Watchers", format!("{} ({}**\\***)", repo.watchers_count().unwrap(), repo.stars_count().unwrap()), true)
  239. .field("Issues", format!("{}", repo.open_issues_count().unwrap()), true)
  240. .field("Forks", format!("{} ({} PRs)", repo.forks_count().unwrap(), repo.open_pr_counter().unwrap()), true)
  241. .field("Size", format!("{} KB", repo.size().unwrap()), true)
  242. //.field("Releases", format!("{}", repo.release_counter().unwrap()), true) // always zero (why?)
  243. .field("Created At", format!("{}", repo.created_at().unwrap()), true);
  244. });
  245. }) {
  246. println!("Failed to send gitea release message {:?}", why);
  247. }
  248. }
  249. Err(async_err) => {
  250. let response = MessageBuilder::new()
  251. .push(format!("Gitea API request failed to complete {:?}", async_err))
  252. .build();
  253. if let Err(why) = msg.channel_id.say(&ctx.http, response) {
  254. println!("Failed to send gitea error message {:?}", why);
  255. }
  256. }
  257. }
  258. }
  259. }