main.rs 3.0 KB

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