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
Show all changes
68 commits
Select commit Hold shift + click to select a range
89dcb98
A weird implementation of cobalt with using `ccobalt`
Weever1337 Aug 28, 2025
610431a
Added new button `Summarize` in transcription.rs and a bit implemente…
Weever1337 Aug 28, 2025
1e92cbf
Updated README a bit
Weever1337 Aug 28, 2025
6d2ca58
feat(transcription): send error message about file size limit
nixxoq Aug 28, 2025
305f092
A very early version of translate command
Weever1337 Aug 28, 2025
c0e4d3c
chore(env): add REDIS_URL value
nixxoq Aug 29, 2025
58b69ef
feat(db): add redis database
nixxoq Aug 29, 2025
ee6ed39
feat(errors): use thiserror with other errors handling
nixxoq Aug 29, 2025
75fd4bc
chore(loader): fix compile error by using MyError
nixxoq Aug 29, 2025
9b1ff73
feat(transcription): save original and summarized texts to the redis …
nixxoq Aug 29, 2025
fc32e53
feat(markuper): use new handler to delete message from bot, add handl…
nixxoq Aug 29, 2025
08853ee
chore(cobalter & currency): fix compilation errors
nixxoq Aug 29, 2025
2bc7c76
chore(cargo): add redis, redis-macros and url dependencies
nixxoq Aug 29, 2025
63998a3
Added a very-very bad settings system
Weever1337 Aug 29, 2025
6ba57f7
Removed warnings; Enabled cobalt system
Weever1337 Aug 29, 2025
68ff604
Implemented Whisper System and some fixes in namespaces
Weever1337 Aug 30, 2025
3754691
Updated license
Weever1337 Aug 30, 2025
1a7c4a4
Finished translate system
Weever1337 Aug 30, 2025
309df92
Separate delete button from transcription; Added universal id `72` fo…
Weever1337 Aug 31, 2025
48d49b1
Added error handler for catching it and fix
Weever1337 Aug 31, 2025
4469c10
Reformat imports in transcription.rs
Weever1337 Aug 31, 2025
54ec68d
Fixed problem with pagination in translate (Teloxide(Api(MessageNotMo…
Weever1337 Aug 31, 2025
d2bbc12
Fixed delete button issue
Weever1337 Aug 31, 2025
1fe5467
Added support for threads in logs
Weever1337 Aug 31, 2025
6c416fd
Added cobalt tools (backport from nixxoq's patches and some upgrades …
Weever1337 Aug 31, 2025
8fbb4d0
fix(loader): filter also video_note
nixxoq Sep 2, 2025
17205fd
feat(config): use log::error! macros instead of except
nixxoq Sep 2, 2025
75939db
my brain is not braining
Weever1337 Sep 4, 2025
aa43004
feat(whisper): store recent recipients to redis cache
nixxoq Sep 6, 2025
2951cdc
chore(translate): clippy messing
nixxoq Sep 6, 2025
ef0eb07
chore(currency & loader): import messin'
nixxoq Sep 6, 2025
4f55d4c
feat(cobalter): read changes in description
nixxoq Sep 6, 2025
b1e63f4
new structure of project
Weever1337 Sep 6, 2025
62f0f43
fixed translation escape
Weever1337 Sep 6, 2025
dc8f291
New structure of project [2/3]
nixxoq Sep 8, 2025
d770104
[2.5/3] updated structure of fulturate
Weever1337 Sep 10, 2025
d77aeb6
[3/3] updated structure of fulturate
Weever1337 Sep 10, 2025
fe3b8bc
fixed speech recognition command with "unknown" answer
Weever1337 Sep 13, 2025
4108bfb
added pagination in transcriptions for better experience
Weever1337 Sep 13, 2025
da267c3
implement keyboard-paginator builder
nixxoq Sep 13, 2025
e33bbdd
Merge remote-tracking branch 'origin/dev' into dev
Weever1337 Sep 13, 2025
97f53cc
added delete-confirm system
Weever1337 Sep 13, 2025
f2b47a9
reimplemented transcription paginator on new pagination system
Weever1337 Sep 13, 2025
c861523
ability to convert serde_json::Error to MyError enum
nixxoq Sep 12, 2025
1598ea2
chore(callbacks/mod): refactor general callbacks handler
nixxoq Sep 17, 2025
9683d65
feat(core/db): add new Settings schemas
nixxoq Sep 17, 2025
eb17f84
chore(core/db/schemas): format import
nixxoq Sep 17, 2025
f26d5c0
feat(errors): handle TranslationError on MyError enumerator
nixxoq Sep 17, 2025
2c48918
So, I spent four days on reworking the current settings in a new way.…
nixxoq Sep 17, 2025
32112d0
removed get_url_by_hash and set_url_by_hash_mapping because useless
Weever1337 Sep 17, 2025
8dea991
fixed google translation issue with limit via SYMBOL LIMITTT🤙🤙🤙🤙🤙
Weever1337 Sep 17, 2025
e9144b7
fix(callbacks/mod): delete confirmation don't work properly after maj…
nixxoq Sep 17, 2025
c3bfdc6
chore(cargo): add futures crate
nixxoq Sep 17, 2025
7cc7f71
feat(translator): split message into chunks and do the translation a …
nixxoq Sep 17, 2025
fa3c9e1
feat(cobalt): show video after sending inline query result to chat (a…
nixxoq Sep 18, 2025
697d872
fix(commands/settings): register settings for new user with default m…
nixxoq Sep 19, 2025
cf589b5
fixed settings issue
Weever1337 Sep 20, 2025
56d293b
implemented whisper module
Weever1337 Sep 20, 2025
92c8de1
global inline handlers like user register and disabled modules
Weever1337 Sep 20, 2025
47112b5
early settings design stuff
Weever1337 Sep 20, 2025
e86027d
no to description, yes to name!
Weever1337 Sep 20, 2025
fb273f0
enhanced settings stuff with designed system
Weever1337 Sep 21, 2025
30a8a57
added button "delete your data" in settings
Weever1337 Sep 21, 2025
7388664
now other users cant interact with setting buttons
Weever1337 Sep 21, 2025
c457481
updated start command and welcome message
Weever1337 Sep 21, 2025
6e64573
cargo clippy my brother
Weever1337 Sep 21, 2025
a751130
updated descriptions for modules
Weever1337 Sep 21, 2025
4827333
Merge remote-tracking branch 'origin/main' into dev
Weever1337 Sep 21, 2025
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
11 changes: 8 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
BOT_TOKEN=
GEMINI_API_KEY=
OWNERS=701733705, 6452350296
MONGODB_URL=
RUST_LOG=info
COBALT_API_KEY=
OWNERS=701733705,6452350296
LOG_CHAT_ID=
ERROR_CHAT_THREAD_ID=0
WARN_CHAT_THREAD_ID=0
MONGODB_URL=mongodb://localhost:27017
RUST_LOG=info
REDIS_URL=redis://127.0.0.1:6379
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,12 @@ async-trait = "0.1.88"
log = "0.4.27"
pretty_env_logger = "0.5.0"
uuid = { version = "1.17.0", features = ["v4"] }
ccobalt = { git = "https://github.com/fulturate/ccobalt.git" }
translators = { version = "0.1.5", features = ["google", "tokio-async"] }
redis = { version = "0.32.5", features = ["tokio-comp"] }
redis-macros = { version = "0.5.6" }
url = "2.5.4"
base64 = "0.22.1"
mime = "0.3.17"
md5 = "0.7.0"
futures = "0.3.31"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2012-2025 Weever (Weever1337)
Copyright (c) 2012-2025 Fulturate

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
21 changes: 5 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
<div align="center">
<img src="https://weever.my/_next/image?url=https%3A%2F%2Fcamo.githubusercontent.com%2Fa3662ea2d6a9a0b4ba2bfc2501db31c0815afd2c4d25ec3d29ad3b7aaac464ce%2F68747470733a2f2f63646e2e646973636f72646170702e636f6d2f6174746163686d656e74732f3732343938333636393131343134323735312f313336303330383531303737363336353230382f6368616e6e656c73345f70726f66696c652e6a70673f65783d36376661613538362669733d363766393534303626686d3d6366616562303939656438333961313932333735663863316134636636313134363037303165623231343137383965313438336534366434373830643661323626&w=640&q=75" width="150">
<h1>Fulturate-rs - Telegram Utility Bot 🤖</h1>
(Currently, the bot is in Russian. The next version**s** will include a 100% language switcher.)
<h1>Fulturate-rs - Best utility bot for Telegram on Rust 🤖</h1>
</div>

## ✨ Features:
- **Speech Recognition** 🎤: Convert spoken words into text for a more interactive experience.
- **Currency Conversion** 💰: Convert between different currencies easily.

## 🔹 Todo:
- **Download Videos/Music from URLs** 🎥🎶: Seamlessly download multimedia content.
- **Download Images from URLs** 🖼️: Fetch images directly from URLs.
- **Download Files from URLs** 📂: Download various types of files from provided URLs.
- **Download Audios from URLs** 🎧: Grab audio files from URLs for easy access.
- **Settings** ⚙️: Manage and adjust bot settings to your preferences.
- **Model Switcher (Speech Recognition)** 🧠: Switch between different speech recognition models for better accuracy.
- **Language Switcher** 🌍: Switch between multiple languages with ease.
- **Message Translation** 🌐: Translate messages into your preferred language.
- **Summarization** ✨: Summarize audio files.

## 🚀 Getting Started:
To get started with Fulturate-rs, follow these steps:
*(If you don't have Rust installed, visit: [Rust Install](https://www.rust-lang.org/tools/install))*

1. Clone the repository:
`git clone https://github.com/Weever1337/fulturate-rs.git`
`git clone https://github.com/Fulturate/bot.git`
2. Run the bot:
`cargo run`
OR run with Docker:
Expand All @@ -33,10 +23,9 @@ To get started with Fulturate-rs, follow these steps:
- **News Channel**: [Fulturate News](https://t.me/FulturateNews)
- **Bot**: [Fulturate Bot](https://t.me/FulturateBot)
- **Owner's Bio-Channel in Telegram**: [Weever](https://t.me/Weever)
- **Owner's Shitpost Channel (RUS) in Telegram**: [WeeverDev](https://t.me/WeeverDev)

## 💖 Credits:
- **Nixxoq**: [GitHub](https://github.com/nixxoq), [Telegram](https://t.me/nixxoq)
- **Nixxoq (did a LOT of work)**: [GitHub](https://github.com/nixxoq), [Telegram](https://t.me/nixxoq)

## 📄 License:
- **MIT License**: [MIT License](https://github.com/Weever1337/fulturate-rs/blob/main/LICENSE)
- **MIT License**: [MIT License](https://github.com/Fulturate/bot/blob/main/LICENSE)
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"ai_model": "gemini-2.5-flash",
"ai_prompt": "You are a highly specialized audio-to-text transcription service. Your SOLE purpose is to accurately transcribe the spoken words from the audio track of the provided file.\n\n**Crucial Instruction: You MUST completely ignore the visual stream of the file. Your task is NOT to describe the video.**\n\n- **DO:** Listen to the audio and transcribe it word-for-word (verbatim).\n- **DO:** Maintain the original language of the speech.\n\n- **DO NOT:** Describe scenes, people, objects, actions, logos, or the environment.\n- **DO NOT:** Analyze the camera work or shot composition.\n- **DO NOT:** Provide summaries, explanations, or any commentary.\n- **DO NOT:** Add headers, timestamps, or any formatting.\n\nReturn ONLY the raw, plain transcribed text. If no speech is present, return \"[no speech]\"."
"ai_prompt": "You are a highly specialized audio-to-text transcription service. Your SOLE purpose is to accurately transcribe the spoken words from the audio track of the provided file.\n\n**Crucial Instruction: You MUST completely ignore the visual stream of the file. Your task is NOT to describe the video.**\n\n- **DO:** Listen to the audio and transcribe it word-for-word (verbatim).\n- **DO:** Maintain the original language of the speech.\n\n- **DO NOT:** Describe scenes, people, objects, actions, logos, or the environment.\n- **DO NOT:** Analyze the camera work or shot composition.\n- **DO NOT:** Provide summaries, explanations, or any commentary.\n- **DO NOT:** Add headers, timestamps, or any formatting.\n\nReturn ONLY the raw, plain transcribed text. If no speech is present, return \"[no speech]\".",
"summarize_prompt": "You are an assistant that transcribes and then summarizes spoken content. First, accurately and fully transcribe the voice message, keeping the original language. Then, briefly summarize the transcribed text in the same language. Output only the final summary. Do not include the full transcription, and do not add any extra words like 'Summary' or 'Transcription'. Do not explain or comment. The output must be plain and concise."
}
120 changes: 120 additions & 0 deletions src/bot/callbacks/cobalt_pagination.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use crate::{
bot::keyboards::cobalt::make_photo_pagination_keyboard,
core::{config::Config, services::cobalt::DownloadResult},
errors::MyError,
};
use std::sync::Arc;
use teloxide::{
ApiError, RequestError,
prelude::*,
types::{CallbackQuery, InputFile, InputMedia, InputMediaPhoto},
};

struct PagingData<'a> {
original_user_id: u64,
index: usize,
total: usize,
url_hash: &'a str,
}

impl<'a> PagingData<'a> {
fn from_parts(parts: &'a [&'a str]) -> Option<Self> {
if parts.len() < 5 {
return None;
}

Some(Self {
original_user_id: parts.get(1)?.parse().ok()?,
index: parts.get(2)?.parse().ok()?,
total: parts.get(3)?.parse().ok()?,
url_hash: parts.get(4)?,
})
}
}

pub async fn handle_cobalt_pagination(
bot: Bot,
q: CallbackQuery,
config: Arc<Config>,
) -> Result<(), MyError> {
let Some(data) = q.data else { return Ok(()) };
let parts: Vec<&str> = data.split(':').collect();

if parts.get(1) == Some(&"noop") {
bot.answer_callback_query(q.id).await?;
return Ok(());
}

let Some(paging_data) = PagingData::from_parts(&parts) else {
log::error!("Invalid callback data format: {}", data);
return Ok(());
};

if q.from.id.0 != paging_data.original_user_id {
bot.answer_callback_query(q.id)
.text("Вы не можете использовать эти кнопки.")
.show_alert(true)
.await?;
return Ok(());
}

let cache_key = format!("cobalt_cache:{}", paging_data.url_hash);
let redis = config.get_redis_client();
let Ok(Some(DownloadResult::Photos { urls, original_url })) = redis.get(&cache_key).await
else {
bot.answer_callback_query(q.id)
.text("Извините, срок хранения этих фото истёк.")
.show_alert(true)
.await?;
return Ok(());
};

let Some(photo_url_str) = urls.get(paging_data.index) else {
log::error!(
"Pagination index {} is out of bounds for len {}",
paging_data.index,
urls.len()
);
return Ok(());
};
let Ok(url) = photo_url_str.parse() else {
log::error!("Failed to parse photo URL: {}", photo_url_str);
return Ok(());
};

let media = InputMedia::Photo(InputMediaPhoto::new(InputFile::url(url)));
let keyboard = make_photo_pagination_keyboard(
paging_data.url_hash,
paging_data.index,
paging_data.total,
paging_data.original_user_id,
&original_url,
);

let edit_result = if let Some(msg) = q.message {
bot.edit_message_media(msg.chat().id, msg.id(), media)
.reply_markup(keyboard)
.await
.map(|_| ())
} else if let Some(inline_id) = q.inline_message_id {
bot.edit_message_media_inline(inline_id, media)
.reply_markup(keyboard)
.await
.map(|_| ())
} else {
return Ok(());
};

if let Err(e) = edit_result
&& !matches!(e, RequestError::Api(ApiError::MessageNotModified))
{
log::error!("Failed to edit message for pagination: {}", e);
bot.answer_callback_query(q.id.clone())
.text("Не удалось обновить фото.")
.await?;
}

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

Ok(())
}
Loading