rustlog/src/app/mod.rs

142 lines
4.6 KiB
Rust

pub mod cache;
use self::cache::UsersCache;
use crate::{config::Config, error::Error, Result};
use anyhow::Context;
use dashmap::DashSet;
use std::{collections::HashMap, sync::Arc};
use tracing::{debug, info};
use twitch_api2::{helix::users::GetUsersRequest, twitch_oauth2::AppAccessToken, HelixClient};
#[derive(Clone)]
pub struct App {
pub helix_client: HelixClient<'static, reqwest::Client>,
pub token: Arc<AppAccessToken>,
pub users: UsersCache,
// pub optout_codes: Arc<DashSet<String>>,
pub db: Arc<clickhouse::Client>,
pub config: Arc<Config>,
}
impl App {
pub async fn get_users(
&self,
ids: Vec<String>,
names: Vec<String>,
) -> Result<HashMap<String, String>> {
let mut users = HashMap::new();
let mut ids_to_request = Vec::new();
let mut names_to_request = Vec::new();
for id in ids {
match self.users.get_login(&id) {
Some(Some(login)) => {
users.insert(id, login);
}
Some(None) => (),
None => ids_to_request.push(id.into()),
}
}
for name in names {
match self.users.get_id(&name) {
Some(Some(id)) => {
users.insert(id, name);
}
Some(None) => (),
None => names_to_request.push(name.into()),
}
}
let mut new_users = Vec::with_capacity(ids_to_request.len() + names_to_request.len());
// There are no chunks if the vec is empty, so there is no empty request made
for chunk in ids_to_request.chunks(100) {
debug!("Requesting user info for ids {chunk:?}");
let request = GetUsersRequest::builder().id(chunk.to_vec()).build();
let response = self.helix_client.req_get(request, &*self.token).await?;
new_users.extend(response.data);
}
for chunk in names_to_request.chunks(100) {
debug!("Requesting user info for names {chunk:?}");
let request = GetUsersRequest::builder().login(chunk.to_vec()).build();
let response = self.helix_client.req_get(request, &*self.token).await?;
new_users.extend(response.data);
}
for user in new_users {
let id = user.id.into_string();
let login = user.login.into_string();
self.users.insert(id.clone(), login.clone());
users.insert(id, login);
}
// Banned users which were not returned by the api
for id in ids_to_request {
if !users.contains_key(id.as_str()) {
self.users.insert_optional(Some(id.into_string()), None);
}
}
for name in names_to_request {
if !users.values().any(|login| login == name.as_str()) {
self.users.insert_optional(None, Some(name.into_string()));
}
}
Ok(users)
}
pub async fn get_user_id_by_name(&self, name: &str) -> Result<String> {
match self.users.get_id(name) {
Some(Some(id)) => Ok(id),
Some(None) => Err(Error::NotFound),
None => {
let request = GetUsersRequest::builder().login(vec![name.into()]).build();
let response = self.helix_client.req_get(request, &*self.token).await?;
match response.data.into_iter().next() {
Some(user) => {
let user_id = user.id.into_string();
self.users.insert(user_id.clone(), user.login.into_string());
Ok(user_id)
}
None => {
self.users.insert_optional(None, Some(name.to_owned()));
Err(Error::NotFound)
}
}
}
}
}
// pub async fn optout_user(&self, user_id: &str) -> anyhow::Result<()> {
// delete_user_logs(&self.db, user_id)
// .await
// .context("Could not delete logs")?;
// self.config.opt_out.insert(user_id.to_owned(), true);
// self.config.save()?;
// info!("User {user_id} opted out");
// Ok(())
// }
pub fn check_opted_out(&self, channel_id: &str, user_id: Option<&str>) -> Result<()> {
if self.config.opt_out.contains_key(channel_id) {
return Err(Error::ChannelOptedOut);
}
if let Some(user_id) = user_id {
if self.config.opt_out.contains_key(user_id) {
return Err(Error::UserOptedOut);
}
}
Ok(())
}
}