bot: receive events from webhook
This commit is contained in:
parent
e6926d5ba2
commit
d09070dae7
|
@ -2,6 +2,7 @@ use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufReader, BufWriter},
|
io::{BufReader, BufWriter},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
sync::mpsc::Receiver,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
@ -12,7 +13,7 @@ use matrix_sdk::{
|
||||||
};
|
};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::config::ProloloConfig;
|
use crate::{config::ProloloConfig, webhooks::Event};
|
||||||
|
|
||||||
mod handlers;
|
mod handlers;
|
||||||
use handlers::autojoin::autojoin_authorized_rooms;
|
use handlers::autojoin::autojoin_authorized_rooms;
|
||||||
|
@ -61,11 +62,25 @@ impl Prololo {
|
||||||
///
|
///
|
||||||
/// [`Prololo::init`] **must** be called before this function, otherwise the [`Client`] isn't
|
/// [`Prololo::init`] **must** be called before this function, otherwise the [`Client`] isn't
|
||||||
/// logged in.
|
/// logged in.
|
||||||
pub async fn run(&self) {
|
pub async fn run(&self, events: Receiver<Event>) {
|
||||||
debug!("running...");
|
debug!("running...");
|
||||||
|
|
||||||
|
let client = self.client.clone();
|
||||||
|
let config = self.config.clone();
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
Self::handle_events(events, client, config);
|
||||||
|
});
|
||||||
|
|
||||||
self.client.sync(SyncSettings::default()).await
|
self.client.sync(SyncSettings::default()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_events(events: Receiver<Event>, client: Client, config: ProloloConfig) {
|
||||||
|
loop {
|
||||||
|
let event = events.recv().unwrap();
|
||||||
|
debug!("received event: {:?}", event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This loads the session information from an existing file, and tries to login with it. If no such
|
/// This loads the session information from an existing file, and tries to login with it. If no such
|
||||||
/// file is found, then login using username and password, and save the new session information on
|
/// file is found, then login using username and password, and save the new session information on
|
||||||
/// disk.
|
/// disk.
|
||||||
|
|
|
@ -4,7 +4,7 @@ use matrix_sdk::ruma::RoomId;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct ProloloConfig {
|
pub struct ProloloConfig {
|
||||||
/// The URL for the homeserver we should connect to
|
/// The URL for the homeserver we should connect to
|
||||||
pub matrix_homeserver: Url,
|
pub matrix_homeserver: Url,
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -1,6 +1,7 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::mpsc::sync_channel;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
|
@ -13,7 +14,7 @@ mod config;
|
||||||
use config::ProloloConfig;
|
use config::ProloloConfig;
|
||||||
|
|
||||||
mod webhooks;
|
mod webhooks;
|
||||||
use webhooks::github_webhook;
|
use webhooks::{github_webhook, EventSender};
|
||||||
|
|
||||||
#[derive(Clap)]
|
#[derive(Clap)]
|
||||||
#[clap(version = "0.1")]
|
#[clap(version = "0.1")]
|
||||||
|
@ -33,10 +34,14 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let config: ProloloConfig = serde_yaml::from_reader(BufReader::new(config_file))
|
let config: ProloloConfig = serde_yaml::from_reader(BufReader::new(config_file))
|
||||||
.context("couldn't parse config file")?;
|
.context("couldn't parse config file")?;
|
||||||
|
|
||||||
|
let (sender, receiver) = sync_channel(42);
|
||||||
|
|
||||||
let prololo = Prololo::new(config).context("failed to create prololo bot")?;
|
let prololo = Prololo::new(config).context("failed to create prololo bot")?;
|
||||||
prololo.init().await.context("failed to init prololo bot")?;
|
prololo.init().await.context("failed to init prololo bot")?;
|
||||||
tokio::spawn(async move { prololo.run().await });
|
tokio::spawn(async move { prololo.run(receiver).await });
|
||||||
|
|
||||||
let rocket = rocket::build().mount("/", routes![github_webhook]);
|
let rocket = rocket::build()
|
||||||
|
.mount("/", routes![github_webhook])
|
||||||
|
.manage(EventSender(sender));
|
||||||
rocket.launch().await.map_err(|err| anyhow::anyhow!(err))
|
rocket.launch().await.map_err(|err| anyhow::anyhow!(err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,47 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::{anyhow, bail};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::Status,
|
http::Status,
|
||||||
request::{FromRequest, Outcome},
|
request::{FromRequest, Outcome},
|
||||||
Request,
|
Request, State,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
mod signing;
|
mod signing;
|
||||||
use signing::SignedGitHubPayload;
|
use signing::SignedGitHubPayload;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::webhooks::{Event, EventSender};
|
||||||
|
|
||||||
const X_GITHUB_EVENT: &str = "X-GitHub-Event";
|
const X_GITHUB_EVENT: &str = "X-GitHub-Event";
|
||||||
|
|
||||||
struct GitHubSecret(String);
|
struct GitHubSecret(String);
|
||||||
|
|
||||||
#[rocket::post("/api/webhooks/github", data = "<payload>")]
|
#[rocket::post("/api/webhooks/github", data = "<payload>")]
|
||||||
pub fn github_webhook(event: GitHubEventType, payload: SignedGitHubPayload) -> &'static str {
|
pub fn github_webhook(
|
||||||
|
event: GitHubEventType,
|
||||||
|
payload: SignedGitHubPayload,
|
||||||
|
sender: &State<EventSender>,
|
||||||
|
) -> Status {
|
||||||
info!(
|
info!(
|
||||||
"received event {:?} with signed payload:\n{}",
|
"received event {:?} with signed payload:\n{}",
|
||||||
event, payload.0
|
event, payload.0
|
||||||
);
|
);
|
||||||
|
|
||||||
"OK"
|
let event = match event.parse_payload(&payload) {
|
||||||
|
Ok(event) => event,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"couldn't parse payload for event {:?}: {}\n{}",
|
||||||
|
event, e, payload.0
|
||||||
|
);
|
||||||
|
return Status::BadRequest;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sender.0.send(Event::GitHub(event)).unwrap();
|
||||||
|
|
||||||
|
Status::Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -34,6 +54,16 @@ pub enum GitHubEventType {
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GitHubEventType {
|
||||||
|
fn parse_payload(&self, payload: &SignedGitHubPayload) -> anyhow::Result<GitHubEvent> {
|
||||||
|
Ok(match self {
|
||||||
|
Self::Create => GitHubEvent::Create(serde_json::from_str(&payload.0)?),
|
||||||
|
Self::Unknown => bail!("unknown event type"),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for GitHubEventType {
|
impl<'r> FromRequest<'r> for GitHubEventType {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
@ -70,16 +100,37 @@ impl<'r> FromRequest<'r> for GitHubEventType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GitHubEvent {
|
#[derive(Debug)]
|
||||||
Create { ref_type: RefType },
|
pub enum GitHubEvent {
|
||||||
|
Create(CreateEvent),
|
||||||
Issues,
|
Issues,
|
||||||
IssueComment,
|
IssueComment,
|
||||||
Push,
|
Push,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
enum RefType {
|
pub enum RefType {
|
||||||
Branch,
|
Branch,
|
||||||
Tag,
|
Tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct GitHubUser {
|
||||||
|
login: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Repository {
|
||||||
|
name: String,
|
||||||
|
full_name: String,
|
||||||
|
html_url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct CreateEvent {
|
||||||
|
r#ref: String,
|
||||||
|
ref_type: RefType,
|
||||||
|
repository: Repository,
|
||||||
|
sender: GitHubUser,
|
||||||
|
}
|
||||||
|
|
|
@ -1,2 +1,11 @@
|
||||||
|
use std::sync::{mpsc::SyncSender};
|
||||||
|
|
||||||
mod github;
|
mod github;
|
||||||
pub use github::github_webhook;
|
pub use github::{github_webhook, GitHubEvent};
|
||||||
|
|
||||||
|
pub struct EventSender(pub SyncSender<Event>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Event {
|
||||||
|
GitHub(GitHubEvent),
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue