main.rs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. #![feature(proc_macro_hygiene, decl_macro)]
  2. #![allow(clippy::new_without_default)]
  3. #[macro_use]
  4. extern crate rocket;
  5. use failure::{Error, ResultExt};
  6. use reqwest::Client;
  7. use rocket::request;
  8. use rocket::State;
  9. use rocket::{http::Status, Outcome, Request};
  10. use std::env;
  11. use std::sync::Arc;
  12. mod handlers;
  13. mod registry;
  14. mod config;
  15. mod github;
  16. mod interactions;
  17. mod payload;
  18. mod team;
  19. use payload::SignedPayload;
  20. use registry::HandleRegistry;
  21. enum EventName {
  22. IssueComment,
  23. Other,
  24. }
  25. impl<'a, 'r> request::FromRequest<'a, 'r> for EventName {
  26. type Error = String;
  27. fn from_request(req: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
  28. let ev = if let Some(ev) = req.headers().get_one("X-GitHub-Event") {
  29. ev
  30. } else {
  31. return Outcome::Failure((Status::BadRequest, "Needs a X-GitHub-Event".into()));
  32. };
  33. let ev = match ev {
  34. "issue_comment" => EventName::IssueComment,
  35. _ => EventName::Other,
  36. };
  37. Outcome::Success(ev)
  38. }
  39. }
  40. #[derive(Debug)]
  41. struct WebhookError(Error);
  42. impl<'r> rocket::response::Responder<'r> for WebhookError {
  43. fn respond_to(self, _: &Request) -> rocket::response::Result<'r> {
  44. let body = format!("{:?}", self.0);
  45. rocket::Response::build()
  46. .header(rocket::http::ContentType::Plain)
  47. .status(rocket::http::Status::InternalServerError)
  48. .sized_body(std::io::Cursor::new(body))
  49. .ok()
  50. }
  51. }
  52. impl From<Error> for WebhookError {
  53. fn from(e: Error) -> WebhookError {
  54. WebhookError(e)
  55. }
  56. }
  57. #[post("/github-hook", data = "<payload>")]
  58. fn webhook(
  59. event: EventName,
  60. payload: SignedPayload,
  61. reg: State<HandleRegistry>,
  62. ) -> Result<(), WebhookError> {
  63. match event {
  64. EventName::IssueComment => {
  65. let payload = payload
  66. .deserialize::<github::IssueCommentEvent>()
  67. .context("IssueCommentEvent failed to deserialize")
  68. .map_err(Error::from)?;
  69. let event = github::Event::IssueComment(payload);
  70. reg.handle(&event).map_err(Error::from)?;
  71. }
  72. // Other events need not be handled
  73. EventName::Other => {}
  74. }
  75. Ok(())
  76. }
  77. #[catch(404)]
  78. fn not_found(_: &Request) -> &'static str {
  79. "Not Found"
  80. }
  81. fn main() {
  82. dotenv::dotenv().ok();
  83. let client = Client::new();
  84. let gh = github::GithubClient::new(
  85. client.clone(),
  86. env::var("GITHUB_API_TOKEN").expect("Missing GITHUB_API_TOKEN"),
  87. );
  88. let username = Arc::new(github::User::current(&gh).unwrap().login);
  89. let mut registry = HandleRegistry::new();
  90. handlers::register_all(&mut registry, gh.clone(), username);
  91. let mut config = rocket::Config::active().unwrap();
  92. config.set_port(
  93. env::var("TRIAGEBOT_PORT")
  94. .map(|port| port.parse().unwrap())
  95. .unwrap_or(8000),
  96. );
  97. rocket::custom(config)
  98. .manage(gh)
  99. .manage(registry)
  100. .mount("/", routes![webhook])
  101. .register(catchers![not_found])
  102. .launch();
  103. }