194c44c
use config::ai::AiConfig;
use export::AppState;
use models::users::user::{self, ProfileVisibility};
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
use time::OffsetDateTime;
/// Resolve or create the AI bot account in KenOAuth, then project it locally.
pub async fn resolve_bot_user(state: &AppState, cfg: &AiConfig) -> Result<i64, sea_orm::DbErr> {
let username = cfg.bot_username.trim();
if username.is_empty() {
return Err(sea_orm::DbErr::Custom(
"ai.bot_username must not be empty".to_string(),
));
}
ensure_bot(
state,
username,
cfg.bot_display_name.trim(),
true,
)
.await
}
pub async fn ensure_bot(
state: &AppState,
username: &str,
displayname: &str,
system: bool,
) -> Result<i64, sea_orm::DbErr> {
let client = state
.kenauth
.as_ref()
.ok_or_else(|| sea_orm::DbErr::Custom("kenoauth is not configured".to_string()))?;
let public = client
.ensure_bot_user(username, displayname, system)
.await
.map_err(|error| sea_orm::DbErr::Custom(error.to_string()))?;
upsert_local_bot(state, &public, system).await?;
Ok(public.user_id)
}
pub async fn upsert_local_bot(
state: &AppState,
public: &kenauth::proto::common::v1::UserPublic,
system: bool,
) -> Result<user::Model, sea_orm::DbErr> {
let db = state.database.write();
let now = OffsetDateTime::now_utc();
let lower = public.username.to_lowercase();
let display_name = if public.displayname.trim().is_empty() {
public.username.clone()
} else {
public.displayname.clone()
};
let existing = user::Entity::find()
.filter(user::Column::LowerName.eq(&lower))
.one(db)
.await?;
let row = match existing {
Some(row) => {
let mut active: user::ActiveModel = row.into();
active.uid = Set(public.user_id);
active.username = Set(public.username.clone());
active.lower_name = Set(lower);
active.display_name = Set(display_name);
active.avatar_url = Set(public.avatar_url.clone());
active.is_active = Set(true);
active.prohibit_login = Set(true);
active.bot = Set(true);
active.system = Set(system);
active.updated_at = Set(now);
active.update(db).await?
}
None => {
user::ActiveModel {
uid: Set(public.user_id),
username: Set(public.username.clone()),
lower_name: Set(lower),
display_name: Set(display_name),
avatar_url: Set(public.avatar_url.clone()),
is_active: Set(true),
is_restricted: Set(false),
prohibit_login: Set(true),
visibility: Set(ProfileVisibility::Public),
bot: Set(true),
system: Set(system),
last_login_at: Set(None),
last_login_ip_hash: Set(None),
version: Set(1),
passwd_hash: Set(None),
password_last_changed_at: Set(None),
passwd_version: Set(None),
totp_encrypted_secret: Set(None),
totp_enabled: Set(false),
totp_last_used_at: Set(None),
totp_last_counter: Set(None),
timezone: Set(None),
locale: Set(None),
language: Set(None),
created_at: Set(now),
updated_at: Set(now),
}
.insert(db)
.await?
}
};
Ok(row)
}