main.rs 3.0 KB

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