all ready
This commit is contained in:
		
							
								
								
									
										14
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,15 @@ | ||||
| { | ||||
|     "rust-analyzer.showUnlinkedFileNotification": false | ||||
|     "rust-analyzer.showUnlinkedFileNotification": false, | ||||
|     "sqltools.connections": [ | ||||
|         { | ||||
|             "previewLimit": 50, | ||||
|             "server": "postgresql", | ||||
|             "port": 5432, | ||||
|             "askForPassword": true, | ||||
|             "driver": "PostgreSQL", | ||||
|             "name": "hangitbot", | ||||
|             "database": "hangitbot", | ||||
|             "username": "hangitbot" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										4
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -2379,9 +2379,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "sea-query" | ||||
| version = "0.30.2" | ||||
| version = "0.30.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fb3e6bba153bb198646c8762c48414942a38db27d142e44735a133cabddcc820" | ||||
| checksum = "4166a1e072292d46dc91f31617c2a1cdaf55a8be4b5c9f4bf2ba248e3ac4999b" | ||||
| dependencies = [ | ||||
|  "bigdecimal", | ||||
|  "chrono", | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| use std::{collections::HashMap, error::Error}; | ||||
| use std::collections::HashMap; | ||||
|  | ||||
| use rand::{rngs::OsRng, Rng}; | ||||
| use sea_orm::DbErr; | ||||
| use strfmt::Format; | ||||
| use teloxide::{ | ||||
|     payloads::{SendMessage, SendMessageSetters}, | ||||
|     payloads::SendMessageSetters, | ||||
|     prelude::Bot, | ||||
|     requests::{JsonRequest, Requester}, | ||||
|     requests::Requester, | ||||
|     types::{Message, ParseMode}, | ||||
|     utils::{command::BotCommands, markdown::escape}, | ||||
|     utils::command::BotCommands, | ||||
|     RequestError, | ||||
| }; | ||||
| use wd_log::log_error_ln; | ||||
|  | ||||
| use crate::{ | ||||
|     config::Args, | ||||
| @@ -65,43 +65,42 @@ impl CommandHandler { | ||||
|         bot: &Bot, | ||||
|         message: &Message, | ||||
|         text: String, | ||||
|     ) -> JsonRequest<SendMessage> { | ||||
|     ) -> Result<Message, RequestError> { | ||||
|         bot.send_message(message.chat.id, text) | ||||
|             .reply_to_message_id(message.id) | ||||
|             .parse_mode(ParseMode::MarkdownV2) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     pub async fn help_handler( | ||||
|         &self, | ||||
|         bot: &Bot, | ||||
|         message: &Message, | ||||
|     ) -> Result<JsonRequest<SendMessage>, Box<dyn Error + Send + Sync>> { | ||||
|         Ok(self | ||||
|             .send_text_reply(bot, message, Commands::descriptions().to_string()) | ||||
|             .await) | ||||
|     ) -> Result<Message, RequestError> { | ||||
|         self.send_text_reply(bot, message, Commands::descriptions().to_string()) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     pub async fn about_handler( | ||||
|         &self, | ||||
|         bot: &Bot, | ||||
|         message: &Message, | ||||
|     ) -> Result<JsonRequest<SendMessage>, Box<dyn Error + Send + Sync>> { | ||||
|         Ok(self | ||||
|             .send_text_reply(bot, message, BOT_ABOUT.to_string()) | ||||
|             .await) | ||||
|     ) -> Result<Message, RequestError> { | ||||
|         self.send_text_reply(bot, message, BOT_ABOUT.to_string()) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     pub async fn hangit_handler( | ||||
|         &self, | ||||
|         bot: &Bot, | ||||
|         message: &Message, | ||||
|     ) -> Result<JsonRequest<SendMessage>, Box<dyn Error + Send + Sync>> { | ||||
|     ) -> Result<Message, RequestError> { | ||||
|         let reply = match message.reply_to_message() { | ||||
|             Some(reply) => reply, | ||||
|             None => { | ||||
|                 return Ok(self | ||||
|                 return self | ||||
|                     .send_text_reply(bot, message, BOT_TEXT_NO_TARGET.to_string()) | ||||
|                     .await) | ||||
|                     .await | ||||
|             } | ||||
|         }; | ||||
|  | ||||
| @@ -132,56 +131,46 @@ impl CommandHandler { | ||||
|                     .controller | ||||
|                     .hangit(&user.full_name(), message.chat.id) | ||||
|                     .await; | ||||
|                 Ok(self | ||||
|                     .send_text_reply(bot, reply, escape(&text.format(&vars).unwrap())) | ||||
|                     .await) | ||||
|                 self.send_text_reply(bot, reply, text.format(&vars).unwrap()) | ||||
|                     .await | ||||
|             } | ||||
|             None => { | ||||
|                 self.send_text_reply(bot, message, BOT_TEXT_IS_CHANNEL.to_string()) | ||||
|                     .await | ||||
|             } | ||||
|             None => Ok(self | ||||
|                 .send_text_reply(bot, message, BOT_TEXT_IS_CHANNEL.to_string()) | ||||
|                 .await), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub async fn top_handler( | ||||
|         &self, | ||||
|         bot: &Bot, | ||||
|         message: &Message, | ||||
|     ) -> Result<JsonRequest<SendMessage>, Box<dyn Error + Send + Sync>> { | ||||
|         let chat_id = message.chat.id; | ||||
|         let scope = match chat_id.is_group() { | ||||
|     pub async fn top_handler(&self, bot: &Bot, message: &Message) -> Result<Message, RequestError> { | ||||
|         let chat = &message.chat; | ||||
|         let scope = match chat.is_group() || chat.is_supergroup() { | ||||
|             true => BOT_TEXT_TOP_GROUP, | ||||
|             false => BOT_TEXT_TOP_GLOBAL, | ||||
|         }; | ||||
|  | ||||
|         let mut index = 1; | ||||
|         let mut text = format!("{}-{}\n\n", BOT_TEXT_TOP_TITLE, scope); | ||||
|         let results = match self.controller.top(chat_id).await { | ||||
|             Ok(r) => match r { | ||||
|         let mut text = format!("{}\\-{}\n\n", BOT_TEXT_TOP_TITLE, scope); | ||||
|         let results = match self.controller.top(chat).await { | ||||
|             Some(result) => result, | ||||
|             None => { | ||||
|                     return Ok(self | ||||
|                 return self | ||||
|                     .send_text_reply(bot, message, BOT_TEXT_TOP_NONE.to_string()) | ||||
|                         .await) | ||||
|                 } | ||||
|             }, | ||||
|             Err(error) => { | ||||
|                 log_error_ln!("{}", error); | ||||
|                 return Err(Box::new(error)); | ||||
|                     .await | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         for result in results { | ||||
|             let mut vars = HashMap::new(); | ||||
|             let mut vars: HashMap<String, String> = HashMap::new(); | ||||
|  | ||||
|             vars.insert("name".to_string(), result.name); | ||||
|             vars.insert("count".to_string(), result.counts.to_string()); | ||||
|  | ||||
|             let record = BOT_TEXT_TOP_TEMPLATE.format(&vars).unwrap(); | ||||
|  | ||||
|             text = format!("{}{} {}\n", text, index, record); | ||||
|             text = format!("{}{}\\. {}\n", text, index, record); | ||||
|             index += 1; | ||||
|         } | ||||
|  | ||||
|         Ok(self.send_text_reply(bot, message, text).await) | ||||
|         self.send_text_reply(bot, message, text).await | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,19 @@ | ||||
| use migration::{Migrator, MigratorTrait}; | ||||
| use models::prelude::*; | ||||
| use sea_orm::{ | ||||
|     ActiveModelTrait, ColumnTrait, Database, DatabaseConnection, DbErr, EntityTrait, | ||||
|     PaginatorTrait, QueryFilter, QueryOrder, Set, TransactionTrait, | ||||
|     ActiveModelTrait, ColumnTrait, ConnectionTrait, Database, DatabaseConnection, | ||||
|     DbErr, EntityTrait, QueryFilter, QueryOrder, QuerySelect, QueryTrait, Set, TransactionTrait, FromQueryResult, prelude::BigDecimal, | ||||
| }; | ||||
| use teloxide::types::ChatId; | ||||
| use wd_log::{log_info_ln, log_warn_ln}; | ||||
| use teloxide::types::{Chat, ChatId}; | ||||
| use wd_log::{log_debug_ln, log_error_ln, log_info_ln, log_warn_ln}; | ||||
|  | ||||
|  | ||||
| #[derive(Debug, FromQueryResult)] | ||||
| pub struct TopData { | ||||
|     pub name: String, | ||||
|     pub counts: BigDecimal, | ||||
| } | ||||
|  | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Controller { | ||||
| @@ -63,28 +71,49 @@ impl Controller { | ||||
|     } | ||||
|  | ||||
|     /// stats | ||||
|     pub async fn top(&self, group_id: ChatId) -> Result<Option<Vec<StatsModel>>, DbErr> { | ||||
|     pub async fn top(&self, chat: &Chat) -> Option<Vec<TopData>> { | ||||
|         const LIMIT: u64 = 10; | ||||
|         let transcation = self.db.begin().await?; | ||||
|  | ||||
|         let query = match group_id.is_group() { | ||||
|             true => { | ||||
|                 Stats::find() | ||||
|                     .filter(StatsColumn::GroupId.eq(group_id.0)) | ||||
|                     .order_by_desc(StatsColumn::Counts) | ||||
|                     .paginate(&transcation, LIMIT) | ||||
|                     .fetch() | ||||
|                     .await? | ||||
|             } | ||||
|             false => { | ||||
|                 Stats::find() | ||||
|                     .order_by_desc(StatsColumn::Counts) | ||||
|                     .paginate(&transcation, LIMIT) | ||||
|                     .fetch() | ||||
|                     .await? | ||||
|         let transcation = match self.db.begin().await { | ||||
|             Ok(t) => t, | ||||
|             Err(error) => { | ||||
|                 log_error_ln!("{}", error); | ||||
|                 return None; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         Ok(Some(query)) | ||||
|         let query = match chat.is_group() || chat.is_supergroup() { | ||||
|             true => { | ||||
|                 Stats::find() | ||||
|                     .filter(StatsColumn::GroupId.eq(chat.id.0)) | ||||
|                     .order_by_desc(StatsColumn::Counts) | ||||
|                     .limit(LIMIT).into_model() | ||||
|                     .all(&transcation) | ||||
|                     .await | ||||
|             } | ||||
|             false => { | ||||
|                 let query = Stats::find() | ||||
|                     .select_only() | ||||
|                     .column(StatsColumn::Name) | ||||
|                     .column_as(StatsColumn::Counts.sum(), "counts") | ||||
|                     .group_by(StatsColumn::Name) | ||||
|                     .order_by_desc(StatsColumn::Counts.sum()) | ||||
|                     .limit(LIMIT); | ||||
|  | ||||
|                 log_debug_ln!( | ||||
|                     "SQL: {:?}", | ||||
|                     query.build(transcation.get_database_backend()).to_string() | ||||
|                 ); | ||||
|  | ||||
|                 query.into_model().all(&transcation).await | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         match query { | ||||
|             Ok(query) => Some(query), | ||||
|             Err(error) => { | ||||
|                 log_error_ln!("{}", error); | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										21
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -43,14 +43,25 @@ async fn main() { | ||||
|  | ||||
|     Commands::repl(bot, move |bot: Bot, message: Message, cmd: Commands| { | ||||
|         let command_handler = command_handler.clone(); | ||||
|  | ||||
|         async move { | ||||
|             let _ = match cmd { | ||||
|                 Commands::Help => command_handler.clone().help_handler(&bot, &message).await, | ||||
|                 Commands::About => command_handler.clone().about_handler(&bot, &message).await, | ||||
|                 Commands::Top => command_handler.clone().top_handler(&bot, &message).await, | ||||
|                 Commands::HangIt => command_handler.clone().hangit_handler(&bot, &message).await, | ||||
|             let r = match cmd { | ||||
|                 Commands::Help => command_handler.help_handler(&bot, &message).await, | ||||
|                 Commands::About => command_handler.about_handler(&bot, &message).await, | ||||
|                 Commands::Top => command_handler.top_handler(&bot, &message).await, | ||||
|                 Commands::HangIt => command_handler.hangit_handler(&bot, &message).await, | ||||
|             }; | ||||
|              | ||||
|             match r { | ||||
|                 Ok(_r) => { | ||||
|                     log_debug_ln!("will send: {:?}", _r.text()); | ||||
|                     Ok(()) | ||||
|                 }, | ||||
|                 Err(err) => { | ||||
|                     log_error_ln!("{:?}", err); | ||||
|                     Err(err) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }) | ||||
|     .await; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| pub const BOT_ABOUT: &'static str = | ||||
|     "*Hang it bot*\n\nHang your boss up!\n[Github](https://github.com/senseab/saysthbot-reborn) @ssthbot"; | ||||
|     "*Hang it bot*\n\nHang your boss up\\!\n[Github](https://github.com/senseab/hangitbot) @hangitbot"; | ||||
|  | ||||
| pub const BOT_TEXT_NO_TARGET: &'static str = "请在回复某一条信息时使用该指令"; | ||||
| pub const BOT_TEXT_IS_CHANNEL: &'static str = "这不是个人,吊不起来"; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user