Compare commits

...

8 Commits

Author SHA1 Message Date
Akulij
03e42c9108 fix bug: editing text kept previous media in message 2025-04-28 00:06:22 +03:00
Akulij
a905325a52 update user in db from telegram user info 2025-04-27 23:55:45 +03:00
Akulij
4bf7c214b9 create update_user method on User struct 2025-04-27 23:55:24 +03:00
Akulij
345ea66f8e fix cargo warnings 2025-04-27 23:04:58 +03:00
Akulij
82045a39a9 create BotController 2025-04-27 23:01:40 +03:00
Akulij
b318e3ec8e use DB::init function instead of manual connection and migration 2025-04-27 21:04:34 +03:00
Akulij
09f7e76d96 create DB::init function
semantically, its better to have function, that fully prepares db
(connect and migrate)
2025-04-27 21:02:05 +03:00
Akulij
29acb02a97 fix message of becoming an admin 2025-04-26 19:56:33 +03:00
3 changed files with 72 additions and 15 deletions

View File

@ -83,7 +83,7 @@ pub async fn secret_command_handler(
.await?; .await?;
} else if pass == admin_password { } else if pass == admin_password {
db.set_admin(user.id, true).await; db.set_admin(user.id, true).await;
bot.send_message(msg.from.unwrap().id, "You are admin now!") bot.send_message(msg.from.unwrap().id, "You are an admin now!")
.await?; .await?;
} }
Ok(()) Ok(())

View File

@ -4,11 +4,7 @@ use enum_stringify::EnumStringify;
use futures::stream::{StreamExt, TryStreamExt}; use futures::stream::{StreamExt, TryStreamExt};
use mongodb::options::IndexOptions; use mongodb::options::IndexOptions;
use mongodb::{ use mongodb::{bson::doc, options::ClientOptions, Client};
bson::doc,
options::{ClientOptions, ResolverConfig},
Client,
};
use mongodb::{Database, IndexModel}; use mongodb::{Database, IndexModel};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -39,6 +35,36 @@ pub struct User {
pub language_code: Option<String>, pub language_code: Option<String>,
} }
macro_rules! query_call {
($func_name:ident, $self:ident, $db:ident, $return_type:ty, $body:block) => {
pub async fn $func_name<D: CallDB>(&$self, $db: &mut D)
-> Result<$return_type, Box<dyn std::error::Error>> $body
};
}
impl User {
query_call!(update_user, self, db, (), {
let db_collection = db.get_database().await.collection::<Self>("users");
db_collection
.update_one(
doc! { "_id": self._id },
doc! {
"$set": {
"first_name": &self.first_name,
"last_name": &self.last_name,
"username": &self.username,
"language_code": &self.language_code,
"is_admin": &self.is_admin,
}
},
)
.await?;
Ok(())
});
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Message { pub struct Message {
pub _id: bson::oid::ObjectId, pub _id: bson::oid::ObjectId,
@ -94,6 +120,13 @@ impl DB {
.await?; .await?;
Ok(()) Ok(())
} }
pub async fn init<S: Into<String>>(db_url: S) -> Result<Self, mongodb::error::Error> {
let mut db = Self::new(db_url).await;
db.migrate().await?;
Ok(db)
}
} }
#[async_trait] #[async_trait]

View File

@ -28,7 +28,7 @@ use teloxide::{
type BotDialogue = Dialogue<State, MongodbStorage<Json>>; type BotDialogue = Dialogue<State, MongodbStorage<Json>>;
#[derive(Envconfig)] #[derive(Envconfig)]
struct Config { pub struct Config {
#[envconfig(from = "BOT_TOKEN")] #[envconfig(from = "BOT_TOKEN")]
pub bot_token: String, pub bot_token: String,
#[envconfig(from = "DATABASE_URL")] #[envconfig(from = "DATABASE_URL")]
@ -68,16 +68,27 @@ pub enum State {
}, },
} }
pub struct BotController {
pub bot: Bot,
pub db: DB,
}
impl BotController {
pub async fn new(config: &Config) -> Result<Self, Box<dyn std::error::Error>> {
let bot = Bot::new(&config.bot_token);
let db = DB::init(&config.db_url).await?;
Ok(Self { bot, db })
}
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv()?; dotenvy::dotenv()?;
let config = Config::init_from_env()?; let config = Config::init_from_env()?;
let bot = Bot::new(&config.bot_token); let mut bc = BotController::new(&config).await?;
let mut db = DB::new(&config.db_url).await; let state_mgr = MongodbStorage::open(config.db_url.clone().as_ref(), "gongbot", Json).await?;
db.migrate().await.unwrap();
let db_url2 = config.db_url.clone();
let state_mgr = MongodbStorage::open(&db_url2, "gongbot", Json).await?;
// TODO: delete this in production // TODO: delete this in production
let events: Vec<DateTime<Utc>> = vec!["2025-04-09T18:00:00+04:00", "2025-04-11T16:00:00+04:00"] let events: Vec<DateTime<Utc>> = vec!["2025-04-09T18:00:00+04:00", "2025-04-11T16:00:00+04:00"]
@ -86,7 +97,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.collect(); .collect();
for event in events { for event in events {
match db.clone().create_event(event).await { match bc.db.create_event(event).await {
Ok(e) => println!("Created event {}", e._id), Ok(e) => println!("Created event {}", e._id),
Err(err) => println!("Failed to create event, error: {}", err), Err(err) => println!("Failed to create event, error: {}", err),
} }
@ -127,8 +138,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
) )
.branch(Update::filter_message().endpoint(echo)); .branch(Update::filter_message().endpoint(echo));
Dispatcher::builder(bot, handler) Dispatcher::builder(bc.bot, handler)
.dependencies(dptree::deps![db, state_mgr]) .dependencies(dptree::deps![bc.db, state_mgr])
.enable_ctrlc_handler() .enable_ctrlc_handler()
.build() .build()
.dispatch() .dispatch()
@ -231,6 +242,7 @@ async fn edit_msg_handler(
match msg.media_kind { match msg.media_kind {
MediaKind::Text(text) => { MediaKind::Text(text) => {
db.drop_media(&literal).await.unwrap();
if is_caption_set { if is_caption_set {
return Ok(()); return Ok(());
}; };
@ -395,6 +407,8 @@ async fn user_command_handler(
let user = db let user = db
.get_or_init_user(tguser.id.0 as i64, &tguser.first_name) .get_or_init_user(tguser.id.0 as i64, &tguser.first_name)
.await; .await;
let user = update_user_tg(user, msg.from.as_ref().unwrap());
user.update_user(&mut db).await.unwrap();
println!("MSG: {}", msg.html_text().unwrap()); println!("MSG: {}", msg.html_text().unwrap());
match cmd { match cmd {
UserCommands::Start => { UserCommands::Start => {
@ -575,3 +589,13 @@ async fn echo(bot: Bot, msg: Message) -> Result<(), teloxide::RequestError> {
.await?; .await?;
Ok(()) Ok(())
} }
fn update_user_tg(user: db::User, tguser: &teloxide::types::User) -> db::User {
db::User {
first_name: tguser.first_name.clone(),
last_name: tguser.last_name.clone(),
username: tguser.username.clone(),
language_code: tguser.language_code.clone(),
..user
}
}