diff --git a/server/migrations/2024-03-15-050949_create_users/down.sql b/server/migrations/2024-03-15-050949_create_users/down.sql new file mode 100644 index 0000000..dc3714b --- /dev/null +++ b/server/migrations/2024-03-15-050949_create_users/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE users; diff --git a/server/migrations/2024-03-15-050949_create_users/up.sql b/server/migrations/2024-03-15-050949_create_users/up.sql new file mode 100644 index 0000000..cf73ff9 --- /dev/null +++ b/server/migrations/2024-03-15-050949_create_users/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + github_login TEXT, + github_id BIGINT, + github_name TEXT, + github_avatar_url TEXT, + github_email TEXT, + telegram_chat_id BIGINT +); diff --git a/server/src/bot.rs b/server/src/bot.rs index 573b843..deaff24 100644 --- a/server/src/bot.rs +++ b/server/src/bot.rs @@ -2,11 +2,15 @@ use crate::{ api::{pipeline_new, pipeline_new_pr, pipeline_status, worker_status, JobSource}, formatter::to_html_new_pipeline_summary, github::{get_github_token, login_github}, + models::{NewUser, User}, DbPool, ALL_ARCH, ARGS, }; +use anyhow::Context; use buildit_utils::github::{get_archs, OpenPRError, OpenPRRequest}; use chrono::Local; +use diesel::{Connection, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; use serde::Deserialize; +use tracing::warn; use std::borrow::Cow; use teloxide::{ @@ -142,6 +146,65 @@ async fn pipeline_new_and_report( Ok(()) } +async fn sync_github_info_inner( + pool: DbPool, + telegram_chat: ChatId, + access_token: String, +) -> anyhow::Result<()> { + let crab = octocrab::Octocrab::builder() + .user_access_token(access_token) + .build()?; + let author = crab.current().user().await?; + let mut conn = pool + .get() + .context("Failed to get db connection from pool")?; + + conn.transaction::<(), diesel::result::Error, _>(|conn| { + use crate::schema::users::dsl::*; + match users + .filter(telegram_chat_id.eq(&telegram_chat.0)) + .first::(conn) + .optional()? + { + Some(user) => { + diesel::update(users.find(user.id)) + .set(( + github_login.eq(author.login), + github_id.eq(author.id.0 as i64), + github_avatar_url.eq(author.avatar_url.to_string()), + github_email.eq(author.email), + )) + .execute(conn)?; + } + None => { + let new_user = NewUser { + github_login: Some(author.login), + github_id: Some(author.id.0 as i64), + github_name: None, // TODO + github_avatar_url: Some(author.avatar_url.to_string()), + github_email: author.email, + telegram_chat_id: Some(telegram_chat.0), + }; + diesel::insert_into(crate::schema::users::table) + .values(&new_user) + .execute(conn)?; + } + } + + Ok(()) + })?; + Ok(()) +} + +async fn sync_github_info(pool: DbPool, telegram_chat_id: ChatId, access_token: String) { + if let Err(err) = sync_github_info_inner(pool, telegram_chat_id, access_token).await { + warn!( + "Failed to sync github info for telegram chat {}: {}", + telegram_chat_id, err + ); + } +} + pub async fn answer(bot: Bot, msg: Message, cmd: Command, pool: DbPool) -> ResponseResult<()> { bot.send_chat_action(msg.chat.id, ChatAction::Typing) .await?; @@ -287,6 +350,9 @@ pub async fn answer(bot: Bot, msg: Message, cmd: Command, pool: DbPool) -> Respo } }; + // sync github info, but do not wait for result + tokio::spawn(sync_github_info(pool, msg.chat.id, token.clone())); + if (3..=5).contains(&parts.len()) { let tags = if parts.len() >= 4 { if parts[3].is_empty() { diff --git a/server/src/main.rs b/server/src/main.rs index d28d866..730f49f 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -8,7 +8,8 @@ use diesel::r2d2::Pool; use server::bot::{answer, Command}; use server::recycler::recycler_worker; use server::routes::{ - dashboard_status, job_info, job_list, job_restart, ping, pipeline_info, pipeline_list, pipeline_new_pr, worker_info, worker_job_update, worker_list, worker_poll, AppState + dashboard_status, job_info, job_list, job_restart, ping, pipeline_info, pipeline_list, + pipeline_new_pr, worker_info, worker_job_update, worker_list, worker_poll, AppState, }; use server::routes::{pipeline_new, worker_heartbeat}; use server::routes::{pipeline_status, worker_status}; diff --git a/server/src/models.rs b/server/src/models.rs index 1157cd7..1cfdcdb 100644 --- a/server/src/models.rs +++ b/server/src/models.rs @@ -91,3 +91,28 @@ pub struct NewWorker { pub logical_cores: i32, pub last_heartbeat_time: chrono::DateTime, } + +#[derive(Queryable, Selectable)] +#[diesel(table_name = crate::schema::users)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct User { + pub id: i32, + pub github_login: Option, + pub github_id: Option, + pub github_name: Option, + pub github_avatar_url: Option, + pub github_email: Option, + pub telegram_chat_id: Option, +} + +#[derive(Insertable, AsChangeset)] +#[diesel(table_name = crate::schema::users)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct NewUser { + pub github_login: Option, + pub github_id: Option, + pub github_name: Option, + pub github_avatar_url: Option, + pub github_email: Option, + pub telegram_chat_id: Option, +} diff --git a/server/src/schema.rs b/server/src/schema.rs index cd3f37a..5da998b 100644 --- a/server/src/schema.rs +++ b/server/src/schema.rs @@ -37,6 +37,18 @@ diesel::table! { } } +diesel::table! { + users (id) { + id -> Int4, + github_login -> Nullable, + github_id -> Nullable, + github_name -> Nullable, + github_avatar_url -> Nullable, + github_email -> Nullable, + telegram_chat_id -> Nullable, + } +} + diesel::table! { workers (id) { id -> Int4, @@ -51,4 +63,4 @@ diesel::table! { diesel::joinable!(jobs -> pipelines (pipeline_id)); -diesel::allow_tables_to_appear_in_same_query!(jobs, pipelines, workers,); +diesel::allow_tables_to_appear_in_same_query!(jobs, pipelines, users, workers,);