Skip to content

Commit

Permalink
Fix 'already_imported' check
Browse files Browse the repository at this point in the history
  • Loading branch information
Leonid Kozarin committed Oct 11, 2023
1 parent 432bf4a commit c4c5d4d
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 37 deletions.
7 changes: 7 additions & 0 deletions locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ commands:
result: "The Dick of the Day is <b>%{name}</b>!\n\nHis dick has became longer for <b>%{growth} cm</b> and is <b>%{length} cm</b> long now"
already_chosen: "The Dick of the Day has been already chosen for today! It's <b>%{name}</b>"
import:
result:
template: "The following users were imported:\n\n%{imported}\n\nThe following users are not in the game yet:\n\n%{not_found}"
line:
imported: "➖ <b>%{name}</b> (<i>%{length}</i> cm)"
already_present: "➖ <b>%{name}</b> (<i>%{length}</i> cm)"
not_found: "➖ <b>%{name}</b>"
errors:
not_admin: "This command is supposed to be used by admins only!"
not_reply: "You must reply to a message sent by %{origin_bot}"

title:
Expand Down
81 changes: 52 additions & 29 deletions src/handlers/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,20 @@ pub enum ImportCommands {

type Username = String;

pub struct OriginalUser {
struct OriginalUser {
name: String,
length: u32
}

struct UserInfo {
uid: UserId,
name: String,
length: u32
}

struct ImportResult {
found: Vec<OriginalUser>,
imported: Vec<UserInfo>,
already_present: Vec<UserInfo>,
not_found: Vec<Username>,
}

Expand All @@ -44,7 +51,6 @@ struct InvalidLines;
enum BeforeImportCheckErrors {
NotAdmin,
NotReply,
AlreadyImported,
Other(anyhow::Error)
}

Expand All @@ -62,27 +68,33 @@ struct Repositories<'a> {
pub async fn import_cmd_handler(bot: Bot, msg: Message,
users: repo::Users, imports: repo::Imports) -> HandlerResult {
let lang_code = ensure_lang_code(msg.from());
let repos = Repositories {
users: &users,
imports: &imports,
};

let answer = match check_params_and_parse_message(&bot, &msg, &repos).await {
let answer = match check_and_parse_message(&bot, &msg).await {
Ok(txt) => {
let repos = Repositories {
users: &users,
imports: &imports,
};
let result = import_impl(repos, msg.chat.id, txt).await?;
let imported = result.found.into_iter()
.map(|u| t!("commands.import.result.imported",
let imported = result.imported.into_iter()
.map(|u| t!("commands.import.result.line.imported",
name = u.name, length = u.length,
locale = lang_code.as_str()))
.collect::<Vec<String>>()
.join("\n");
let already_present = result.already_present.into_iter()
.map(|u| t!("commands.import.result.line.already_present",
name = u.name, length = u.length,
locale = lang_code.as_str()))
.collect::<Vec<String>>()
.join("\n");
let not_found = result.not_found.into_iter()
.map(|name| t!("commands.import.result.not_found",
.map(|name| t!("commands.import.result.line.not_found",
name = name,
locale = lang_code.as_str()))
.collect::<Vec<String>>()
.join("\n");
t!("commands.import.result.template", imported = imported, not_found = not_found,
t!("commands.import.result.template", imported = imported,
already_present = already_present, not_found = not_found,
locale = lang_code.as_str())
},
Err(BeforeImportCheckErrors::Other(e)) => Err(e)?,
Expand All @@ -92,7 +104,7 @@ pub async fn import_cmd_handler(bot: Bot, msg: Message,
reply_html(bot, msg, answer).await
}

async fn check_params_and_parse_message<'a>(bot: &Bot, msg: &Message, repos: &Repositories<'a>) -> Result<String, BeforeImportCheckErrors> {
async fn check_and_parse_message<'a>(bot: &Bot, msg: &Message) -> Result<String, BeforeImportCheckErrors> {
let admin_ids = bot.get_chat_administrators(msg.chat.id)
.await
.map_err(|e| BeforeImportCheckErrors::Other(anyhow!(e)))?
Expand All @@ -114,11 +126,6 @@ async fn check_params_and_parse_message<'a>(bot: &Bot, msg: &Message, repos: &Re
Some(text) => text.to_owned()
};

let already_imported = repos.imports.were_dicks_already_imported(msg.chat.id).await?;
if already_imported {
return Err(BeforeImportCheckErrors::AlreadyImported)
}

Ok(text)
}

Expand Down Expand Up @@ -150,30 +157,46 @@ async fn import_impl<'a>(repos: Repositories<'a>, chat_id: ChatId, text: String)
}

let top: Vec<OriginalUser> = top.into_iter()
.filter_map(map_users)
.filter_map(map_user)
.collect();
let (existing, not_existing): (Vec<OriginalUser>, Vec<OriginalUser>) = top.into_iter()
.partition(|u| member_names.contains(&u.name));
let existing: Vec<UserInfo> = existing.into_iter()
.map(|u| UserInfo {
uid: members[&u.name],
name: u.name,
length: u.length
})
.collect();
let not_found = not_existing.into_iter()
.map(|u| u.name)
.collect();

let imported_uids: HashSet<UserId> = repos.imports.get_imported_users(chat_id)
.await?.into_iter()
.filter_map(|u| u.uid.try_into().ok())
.map(|uid| UserId(uid))
.collect();

let (to_import, already_present): (Vec<UserInfo>, Vec<UserInfo>) = existing.into_iter()
.partition(|u| imported_uids.contains(&u.uid));

let users: Vec<repo::ExternalUser> = existing.iter()
.filter_map(|u| repo::ExternalUser::new(members[&u.name], u.length).ok())
let users: Vec<repo::ExternalUser> = to_import.iter()
.filter_map(|u| repo::ExternalUser::new(u.uid, u.length).ok())
.collect();
if users.len() != existing.len() {
if users.len() != to_import.len() {
return Err(anyhow!("couldn't convert integers for external users"))
}

repos.imports.import(chat_id, &users).await?;

let not_found = not_existing.into_iter()
.map(|u| u.name)
.collect();
Ok(ImportResult {
found: existing,
imported: to_import,
already_present,
not_found
})
}

fn map_users(capture: Option<Captures>) -> Option<OriginalUser> {
fn map_user(capture: Option<Captures>) -> Option<OriginalUser> {
let pos = capture?;
if let (Some(name), Some(length)) = (pos.name("name"), pos.name("length")) {
let name = name.as_str().to_owned();
Expand Down
18 changes: 10 additions & 8 deletions src/repo/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use sqlx::{Postgres, Transaction};
use teloxide::types::{ChatId, UserId};
use crate::repository;

#[derive(sqlx::FromRow)]
pub struct ExternalUser {
uid: i64,
length: i32
pub uid: i64,
pub length: i32
}

impl ExternalUser {
Expand All @@ -18,12 +19,13 @@ impl ExternalUser {
}

repository!(Imports,
pub async fn were_dicks_already_imported(&self, chat_id: ChatId) -> anyhow::Result<bool> {
sqlx::query_scalar("SELECT count(*) > 0 AS exists FROM Imports WHERE chat_id = $1")
.bind(chat_id.0)
.fetch_one(&self.pool)
.await
.map_err(|e| e.into())
pub async fn get_imported_users(&self, chat_id: ChatId) -> anyhow::Result<Vec<ExternalUser>> {
let chat_id: i64 = chat_id.0.try_into()?;
let res = sqlx::query_as::<_, ExternalUser>("SELECT uid, original_length AS length FROM Imports WHERE chat_id = $1")
.bind(chat_id)
.fetch_all(&self.pool)
.await?;
Ok(res)
}
,
pub async fn import(&self, chat_id: ChatId, users: &Vec<ExternalUser>) -> anyhow::Result<()> {
Expand Down

0 comments on commit c4c5d4d

Please sign in to comment.