Some checks failed
CI / CI (push) Has been cancelled
Also 'cargo clippy --fix', as current clippy warns on more things.
142 lines
5.7 KiB
Rust
142 lines
5.7 KiB
Rust
// bebot
|
|
// Copyright (C) 2023 Brian Tarricone <brian@tarricone.org>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use constant_time_eq::constant_time_eq;
|
|
use matrix_sdk::{
|
|
ruma::{events::room::message::RoomMessageEventContent, OwnedRoomOrAliasId},
|
|
Client,
|
|
};
|
|
use tokio::sync::mpsc;
|
|
use warp::{filters::BoxedFilter, http::StatusCode, reply::Reply, Filter};
|
|
|
|
use crate::{
|
|
config::GitlabWebhookConfig,
|
|
gitlab_event::{parse_ref, GitlabEvent, GitlabEventExt},
|
|
matrix,
|
|
};
|
|
|
|
pub fn build_gitlab_messages(event: &GitlabEvent) -> Vec<String> {
|
|
let project = event.project();
|
|
let refname = event.r#ref().map(parse_ref);
|
|
event
|
|
.titles()
|
|
.iter()
|
|
.map(|title| {
|
|
format!(
|
|
"\\[{}\\] {}*{}* {}",
|
|
project.path_with_namespace,
|
|
refname.as_ref().map(|rn| format!("`{rn}` ")).unwrap_or_default(),
|
|
event.user(),
|
|
title,
|
|
)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub async fn handle_gitlab_event(
|
|
event: GitlabEvent,
|
|
room_id: &OwnedRoomOrAliasId,
|
|
matrix_client: &Client,
|
|
) -> anyhow::Result<()> {
|
|
let room = matrix::ensure_room_joined(matrix_client, room_id).await?;
|
|
for msg in build_gitlab_messages(&event) {
|
|
debug!("Sending message to {room_id}: {msg}");
|
|
let msg_content = RoomMessageEventContent::text_markdown(&msg);
|
|
room.send(msg_content).await?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn build_route(config: GitlabWebhookConfig, matrix_client: Client) -> anyhow::Result<BoxedFilter<(impl Reply,)>> {
|
|
let (event_tx, mut event_rx) = mpsc::channel::<(GitlabEvent, OwnedRoomOrAliasId)>(100);
|
|
tokio::spawn(async move {
|
|
while let Some((event, room)) = event_rx.recv().await {
|
|
if let Err(err) = handle_gitlab_event(event, &room, &matrix_client).await {
|
|
warn!("Failed to handle payload: {err}");
|
|
}
|
|
}
|
|
});
|
|
|
|
let gitlab_root_path = if let Some(url_prefix) = config.url_prefix.as_ref() {
|
|
url_prefix.split('/').fold(warp::any().boxed(), |last, segment| {
|
|
if segment.is_empty() {
|
|
last
|
|
} else {
|
|
last.and(warp::path(segment.to_string())).boxed()
|
|
}
|
|
})
|
|
} else {
|
|
warp::any().boxed()
|
|
};
|
|
|
|
let config = Arc::new(config);
|
|
let gitlab = gitlab_root_path
|
|
.and(warp::path!("hooks" / "gitlab"))
|
|
.and(warp::post())
|
|
.and(warp::header::<String>("x-gitlab-token"))
|
|
.and(warp::body::json())
|
|
.then(move |token: String, event: GitlabEvent| {
|
|
let event_tx = event_tx.clone();
|
|
let config = Arc::clone(&config);
|
|
|
|
async move {
|
|
match event {
|
|
GitlabEvent::Other => {
|
|
warp::reply::with_status("Unsupported Gitlab event type", StatusCode::BAD_REQUEST)
|
|
}
|
|
_ => {
|
|
let project = event.project();
|
|
let config_key = project.web_url.replace("http://", "").replace("https://", "");
|
|
if let Some(repo_config) = config.repo_configs.get(&config_key) {
|
|
if !constant_time_eq(token.as_bytes(), repo_config.token.as_bytes()) {
|
|
warn!("Invalid token for repo '{config_key}'");
|
|
warp::reply::with_status("Invalid token", StatusCode::FORBIDDEN)
|
|
} else {
|
|
debug!("payload: {event:?}");
|
|
if let Some(room) = &repo_config.room.as_ref().or(config.default_room.as_ref()) {
|
|
let publish_events = repo_config
|
|
.publish_events
|
|
.as_ref()
|
|
.or(config.default_publish_events.as_ref());
|
|
if publish_events.map(|ecs| event.should_publish(ecs)).unwrap_or(true) {
|
|
if let Err(err) = event_tx.send((event, (*room).clone())).await {
|
|
warn!("Failed to enqueue payload: {err}");
|
|
}
|
|
}
|
|
warp::reply::with_status("OK", StatusCode::OK)
|
|
} else {
|
|
info!("Channel not configured for repo '{config_key}'");
|
|
warp::reply::with_status(
|
|
"Matrix room not configured for repo",
|
|
StatusCode::NOT_FOUND,
|
|
)
|
|
}
|
|
}
|
|
} else {
|
|
info!("Repo '{config_key}' unconfigured");
|
|
warp::reply::with_status("Repo not configured", StatusCode::NOT_FOUND)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.boxed();
|
|
|
|
Ok(gitlab)
|
|
}
|