Skip to content
This repository was archived by the owner on Sep 29, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fulturate"
version = "0.13.1"
version = "0.13.2"
edition = "2024"

[dependencies]
Expand All @@ -10,7 +10,7 @@ dotenv = "0.15"
sysinfo = "0.37.0"
reqwest = "0.12.22"
bytes = "1.10.0"
gem-rs = "0.1.80"
gem-rs = { git = "https://github.com/Fulturate/Gem-rs.git"}
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
regex = "1.11.1"
Expand Down
46 changes: 15 additions & 31 deletions src/bot/callbacks/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,16 @@ use crate::{
services::speech_recognition::back_handler,
},
errors::MyError,
util::is_admin_or_author,
};
use log::error;
use mongodb::bson::doc;
use oximod::Model;
use teloxide::{
prelude::*,
types::{ChatId, InlineKeyboardButton, InlineKeyboardMarkup, User},
types::{InlineKeyboardButton, InlineKeyboardMarkup, User},
};

async fn has_delete_permission(
bot: &Bot,
chat_id: ChatId,
is_group: bool,
clicker: &User,
target_user_id: u64,
) -> bool {
if target_user_id == 72 || clicker.id.0 == target_user_id {
return true;
}

if is_group && let Ok(member) = bot.get_chat_member(chat_id, clicker.id).await {
return member.is_privileged();
}

false
}

async fn has_data_delete_permission(
bot: &Bot,
chat: &teloxide::types::Chat,
Expand All @@ -45,9 +28,10 @@ async fn has_data_delete_permission(
return true;
}
if (chat.is_group() || chat.is_supergroup())
&& let Ok(member) = bot.get_chat_member(chat.id, clicker.id).await {
return member.is_owner();
}
&& let Ok(member) = bot.get_chat_member(chat.id, clicker.id).await
{
return member.is_owner();
}
false
}

Expand Down Expand Up @@ -117,14 +101,14 @@ pub async fn handle_delete_request(bot: Bot, query: CallbackQuery) -> Result<(),
return Ok(());
};

let can_delete = has_delete_permission(
let can_delete = is_admin_or_author(
&bot,
message.chat.id,
message.chat.is_group() || message.chat.is_supergroup(),
&query.from,
target_user_id,
)
.await;
.await;

if !can_delete {
bot.answer_callback_query(query.id)
Expand All @@ -140,8 +124,8 @@ pub async fn handle_delete_request(bot: Bot, query: CallbackQuery) -> Result<(),
message.id,
"Вы уверены, что хотите удалить?",
)
.reply_markup(confirm_delete_keyboard(target_user_id))
.await?;
.reply_markup(confirm_delete_keyboard(target_user_id))
.await?;

Ok(())
}
Expand All @@ -168,14 +152,14 @@ pub async fn handle_delete_confirmation(
};
let action = parts[2];

let can_delete = has_delete_permission(
let can_delete = is_admin_or_author(
&bot,
message.chat.id,
message.chat.is_group() || message.chat.is_supergroup(),
&query.from,
target_user_id,
)
.await;
.await;

if !can_delete {
bot.answer_callback_query(query.id)
Expand Down Expand Up @@ -270,11 +254,11 @@ pub async fn handle_delete_data_confirmation(
message.id(),
"✅ Удаление данных отменено.",
)
.reply_markup(InlineKeyboardMarkup::new(vec![vec![]]))
.await?;
.reply_markup(InlineKeyboardMarkup::new(vec![vec![]]))
.await?;
}
_ => {}
}

Ok(())
}
}
44 changes: 37 additions & 7 deletions src/bot/callbacks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use teloxide::{
payloads::{AnswerCallbackQuerySetters, EditMessageTextSetters},
prelude::{CallbackQuery, Requester},
};
use crate::core::services::speech_recognition::retry_speech_handler;

pub mod cobalt_pagination;
pub mod delete;
Expand Down Expand Up @@ -55,7 +56,15 @@ enum CallbackAction<'a> {
DeleteDataConfirmation,
DeleteMessage,
DeleteConfirmation,
Summarize,
Summarize {
user_id: u64,
},
RetrySpeech {
message_id: i32,
user_id: u64,
action_type: &'a str, // "transcribe" or "summarize"
attempt: u32,
},
SpeechPage,
BackToFull,
Whisper,
Expand Down Expand Up @@ -135,8 +144,26 @@ fn parse_callback_data(data: &'_ str) -> Option<CallbackAction<'_>> {
if data.starts_with("delete_confirm:") {
return Some(CallbackAction::DeleteConfirmation);
}
if data.starts_with("summarize") {
return Some(CallbackAction::Summarize);
// if data.starts_with("summarize") {
if let Some(author_id) = data.strip_prefix("summarize:")
&& let Ok(author_id) = author_id.parse()
{
return Some(CallbackAction::Summarize { user_id: author_id });
}
if let Some(rest) = data.strip_prefix("retry_speech:") {
let parts: Vec<_> = rest.splitn(4, ':').collect();
if parts.len() == 4
&& let Ok(message_id) = parts[0].parse()
&& let Ok(user_id) = parts[1].parse()
&& let Ok(attempt) = parts[3].parse()
{
return Some(CallbackAction::RetrySpeech {
message_id,
user_id,
action_type: parts[2], // ahh tupoy The trait bound `&str: FromStr` is not satisfied
attempt
});
}
}
if data.starts_with("speech:page:") {
return Some(CallbackAction::SpeechPage);
Expand All @@ -160,7 +187,7 @@ fn parse_callback_data(data: &'_ str) -> Option<CallbackAction<'_>> {
pub async fn callback_query_handlers(bot: Bot, q: CallbackQuery) -> Result<(), MyError> {
let config = Arc::new(Config::new().await);

let Some(data) = &q.data else {
let Some(data) = &q.data.clone() else {
return Ok(());
};

Expand Down Expand Up @@ -250,7 +277,7 @@ pub async fn callback_query_handlers(bot: Bot, q: CallbackQuery) -> Result<(), M
} else {
"group"
})
.to_string(),
.to_string(),
};
module
.handle_callback(bot, &q, &owner, rest, commander_id)
Expand All @@ -275,7 +302,10 @@ pub async fn callback_query_handlers(bot: Bot, q: CallbackQuery) -> Result<(), M
Some(CallbackAction::DeleteConfirmation) => {
handle_delete_confirmation(bot, q, &config).await?
}
Some(CallbackAction::Summarize) => summarization_handler(bot, q, &config).await?,
Some(CallbackAction::Summarize {user_id}) => summarization_handler(bot, q, &config, user_id).await?,
Some(CallbackAction::RetrySpeech { message_id, user_id, action_type, attempt }) => {
retry_speech_handler(bot, q.clone(), &config, message_id, user_id, action_type, attempt).await?
}
Some(CallbackAction::SpeechPage) => pagination_handler(bot, q, &config).await?,
Some(CallbackAction::BackToFull) => back_handler(bot, q, &config).await?,
Some(CallbackAction::Whisper) => handle_whisper_callback(bot, q, &config).await?,
Expand All @@ -290,4 +320,4 @@ pub async fn callback_query_handlers(bot: Bot, q: CallbackQuery) -> Result<(), M
}

Ok(())
}
}
56 changes: 44 additions & 12 deletions src/bot/callbacks/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ use crate::{
services::translation::{SUPPORTED_LANGUAGES, normalize_language_code},
},
errors::MyError,
util::paginator::{FrameBuild, Paginator},
util::{
is_author,
paginator::{FrameBuild, Paginator},
},
};
use futures::future::join_all;
use log::info;
use teloxide::{
ApiError, RequestError,
prelude::*,
types::{
CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, MaybeInaccessibleMessage,
Message, ParseMode,
Message, ParseMode, User,
},
utils::html::escape,
};
Expand All @@ -29,11 +33,25 @@ pub async fn handle_translate_callback(
config: &Config,
) -> Result<(), MyError> {
if let (Some(data), Some(MaybeInaccessibleMessage::Regular(message))) = (&q.data, &q.message) {
if let Some(author_id) = data
.rsplit_once(":")
.and_then(|(_, last)| last.parse::<u64>().ok())
&& !is_author(&q.from, author_id)
{
bot.answer_callback_query(q.id)
.text("❌ у вас нет прав использовать эту кнопку!")
.show_alert(true)
.await?;

return Ok(());
}

bot.answer_callback_query(q.id.clone()).await?;

if let Some(rest) = data.strip_prefix("tr:page:") {
let parts: Vec<_> = rest.split(':').collect();
if parts.len() == 2 {
if parts.len() >= 2 {
info!("parts: {:#?}", parts);
let translation_id = parts[0];
if let Ok(page) = parts[1].parse::<usize>() {
handle_translation_pagination(&bot, message, translation_id, page, config)
Expand All @@ -44,7 +62,7 @@ pub async fn handle_translate_callback(
handle_language_menu_pagination(bot, message, data).await?;
} else if data.starts_with("tr_lang:") {
handle_language_selection(bot, message, data, q.from.clone(), config).await?;
} else if data == "tr_show_langs" {
} else if data.starts_with("tr_show_langs") {
handle_show_languages(&bot, message, &q.from, config).await?;
}
} else {
Expand Down Expand Up @@ -110,8 +128,14 @@ async fn handle_language_menu_pagination(
message: &Message,
data: &str,
) -> Result<(), MyError> {
if let Ok(page) = data.trim_start_matches("tr_page:").parse::<usize>() {
let keyboard = create_language_keyboard(page);
let parts = data
.strip_prefix("tr_page:")
.and_then(|rest| rest.rsplit_once(':'));

if let Some((page_str, user_id_str)) = parts
&& let (Ok(page), Ok(user_id)) = (page_str.parse(), user_id_str.parse())
{
let keyboard = create_language_keyboard(page, user_id);
if let Err(e) = bot
.edit_message_reply_markup(message.chat.id, message.id)
.reply_markup(keyboard)
Expand All @@ -128,10 +152,16 @@ async fn handle_language_selection(
bot: Bot,
message: &Message,
data: &str,
user: teloxide::types::User,
user: User,
config: &Config,
) -> Result<(), MyError> {
let target_lang = data.trim_start_matches("tr_lang:");
let Some(target_lang) = data
.trim_start_matches("tr_lang:")
.rsplit_once(":").map(|(lang, _)| lang)
else {
return Ok(());
};

let redis_client = config.get_redis_client();

let redis_key_job = format!("translate_job:{}", user.id);
Expand Down Expand Up @@ -185,8 +215,10 @@ async fn handle_language_selection(

if display_pages.len() <= 1 {
let response = format!("<blockquote>{}</blockquote>", escape(&full_translated_text));
let switch_lang_button =
InlineKeyboardButton::callback(lang_display_name.to_string(), "tr_show_langs");
let switch_lang_button = InlineKeyboardButton::callback(
lang_display_name.to_string(),
format!("tr_show_langs:{}", user.id.0),
);
let mut keyboard = delete_message_button(user.id.0);
if let Some(first_row) = keyboard.inline_keyboard.get_mut(0) {
first_row.insert(0, switch_lang_button);
Expand Down Expand Up @@ -234,7 +266,7 @@ async fn handle_language_selection(
async fn handle_show_languages(
bot: &Bot,
message: &Message,
user: &teloxide::types::User,
user: &User,
config: &Config,
) -> Result<(), MyError> {
if let Some(original_message) = message.reply_to_message() {
Expand Down Expand Up @@ -262,7 +294,7 @@ async fn handle_show_languages(
return Ok(());
}

let keyboard = create_language_keyboard(0);
let keyboard = create_language_keyboard(0, user.id.0);
bot.edit_message_text(
message.chat.id,
message.id,
Expand Down
2 changes: 1 addition & 1 deletion src/bot/commands/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ pub async fn translate_handler(
.set(&format!("translate_job:{}", user.id), &job, 600)
.await?;

let keyboard = create_language_keyboard(0);
let keyboard = create_language_keyboard(0, user.id.0);
bot.send_message(msg.chat.id, "Выберите язык для перевода:")
.reply_markup(keyboard)
.reply_parameters(ReplyParameters::new(replied_to_message.id))
Expand Down
Loading