all ready

This commit is contained in:
Sense T 2024-01-13 12:43:07 +08:00
parent dde441bf9f
commit 3eba88b3dd
6 changed files with 121 additions and 80 deletions

14
.vscode/settings.json vendored
View File

@ -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
View File

@ -2379,9 +2379,9 @@ dependencies = [
[[package]] [[package]]
name = "sea-query" name = "sea-query"
version = "0.30.2" version = "0.30.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb3e6bba153bb198646c8762c48414942a38db27d142e44735a133cabddcc820" checksum = "4166a1e072292d46dc91f31617c2a1cdaf55a8be4b5c9f4bf2ba248e3ac4999b"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"chrono", "chrono",

View File

@ -1,16 +1,16 @@
use std::{collections::HashMap, error::Error}; use std::collections::HashMap;
use rand::{rngs::OsRng, Rng}; use rand::{rngs::OsRng, Rng};
use sea_orm::DbErr; use sea_orm::DbErr;
use strfmt::Format; use strfmt::Format;
use teloxide::{ use teloxide::{
payloads::{SendMessage, SendMessageSetters}, payloads::SendMessageSetters,
prelude::Bot, prelude::Bot,
requests::{JsonRequest, Requester}, requests::Requester,
types::{Message, ParseMode}, types::{Message, ParseMode},
utils::{command::BotCommands, markdown::escape}, utils::command::BotCommands,
RequestError,
}; };
use wd_log::log_error_ln;
use crate::{ use crate::{
config::Args, config::Args,
@ -65,43 +65,42 @@ impl CommandHandler {
bot: &Bot, bot: &Bot,
message: &Message, message: &Message,
text: String, text: String,
) -> JsonRequest<SendMessage> { ) -> Result<Message, RequestError> {
bot.send_message(message.chat.id, text) bot.send_message(message.chat.id, text)
.reply_to_message_id(message.id) .reply_to_message_id(message.id)
.parse_mode(ParseMode::MarkdownV2) .parse_mode(ParseMode::MarkdownV2)
.await
} }
pub async fn help_handler( pub async fn help_handler(
&self, &self,
bot: &Bot, bot: &Bot,
message: &Message, message: &Message,
) -> Result<JsonRequest<SendMessage>, Box<dyn Error + Send + Sync>> { ) -> Result<Message, RequestError> {
Ok(self self.send_text_reply(bot, message, Commands::descriptions().to_string())
.send_text_reply(bot, message, Commands::descriptions().to_string()) .await
.await)
} }
pub async fn about_handler( pub async fn about_handler(
&self, &self,
bot: &Bot, bot: &Bot,
message: &Message, message: &Message,
) -> Result<JsonRequest<SendMessage>, Box<dyn Error + Send + Sync>> { ) -> Result<Message, RequestError> {
Ok(self self.send_text_reply(bot, message, BOT_ABOUT.to_string())
.send_text_reply(bot, message, BOT_ABOUT.to_string()) .await
.await)
} }
pub async fn hangit_handler( pub async fn hangit_handler(
&self, &self,
bot: &Bot, bot: &Bot,
message: &Message, message: &Message,
) -> Result<JsonRequest<SendMessage>, Box<dyn Error + Send + Sync>> { ) -> Result<Message, RequestError> {
let reply = match message.reply_to_message() { let reply = match message.reply_to_message() {
Some(reply) => reply, Some(reply) => reply,
None => { None => {
return Ok(self return self
.send_text_reply(bot, message, BOT_TEXT_NO_TARGET.to_string()) .send_text_reply(bot, message, BOT_TEXT_NO_TARGET.to_string())
.await) .await
} }
}; };
@ -113,7 +112,7 @@ impl CommandHandler {
}; };
let mut vars = HashMap::new(); let mut vars = HashMap::new();
let index = if is_self { let index = if is_self {
OsRng.gen::<usize>() % BOT_TEXT_HANGED_SELF.len() OsRng.gen::<usize>() % BOT_TEXT_HANGED_SELF.len()
} else { } else {
@ -132,56 +131,46 @@ impl CommandHandler {
.controller .controller
.hangit(&user.full_name(), message.chat.id) .hangit(&user.full_name(), message.chat.id)
.await; .await;
Ok(self self.send_text_reply(bot, reply, text.format(&vars).unwrap())
.send_text_reply(bot, reply, escape(&text.format(&vars).unwrap())) .await
.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( pub async fn top_handler(&self, bot: &Bot, message: &Message) -> Result<Message, RequestError> {
&self, let chat = &message.chat;
bot: &Bot, let scope = match chat.is_group() || chat.is_supergroup() {
message: &Message,
) -> Result<JsonRequest<SendMessage>, Box<dyn Error + Send + Sync>> {
let chat_id = message.chat.id;
let scope = match chat_id.is_group() {
true => BOT_TEXT_TOP_GROUP, true => BOT_TEXT_TOP_GROUP,
false => BOT_TEXT_TOP_GLOBAL, false => BOT_TEXT_TOP_GLOBAL,
}; };
let mut index = 1; let mut index = 1;
let mut text = format!("{}-{}\n\n", BOT_TEXT_TOP_TITLE, scope); let mut text = format!("{}\\-{}\n\n", BOT_TEXT_TOP_TITLE, scope);
let results = match self.controller.top(chat_id).await { let results = match self.controller.top(chat).await {
Ok(r) => match r { Some(result) => result,
Some(result) => result, None => {
None => { return self
return Ok(self .send_text_reply(bot, message, BOT_TEXT_TOP_NONE.to_string())
.send_text_reply(bot, message, BOT_TEXT_TOP_NONE.to_string()) .await
.await)
}
},
Err(error) => {
log_error_ln!("{}", error);
return Err(Box::new(error));
} }
}; };
for result in results { 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("name".to_string(), result.name);
vars.insert("count".to_string(), result.counts.to_string()); vars.insert("count".to_string(), result.counts.to_string());
let record = BOT_TEXT_TOP_TEMPLATE.format(&vars).unwrap(); let record = BOT_TEXT_TOP_TEMPLATE.format(&vars).unwrap();
text = format!("{}{} {}\n", text, index, record); text = format!("{}{}\\. {}\n", text, index, record);
index += 1; index += 1;
} }
Ok(self.send_text_reply(bot, message, text).await) self.send_text_reply(bot, message, text).await
} }
} }

View File

@ -1,11 +1,19 @@
use migration::{Migrator, MigratorTrait}; use migration::{Migrator, MigratorTrait};
use models::prelude::*; use models::prelude::*;
use sea_orm::{ use sea_orm::{
ActiveModelTrait, ColumnTrait, Database, DatabaseConnection, DbErr, EntityTrait, ActiveModelTrait, ColumnTrait, ConnectionTrait, Database, DatabaseConnection,
PaginatorTrait, QueryFilter, QueryOrder, Set, TransactionTrait, DbErr, EntityTrait, QueryFilter, QueryOrder, QuerySelect, QueryTrait, Set, TransactionTrait, FromQueryResult, prelude::BigDecimal,
}; };
use teloxide::types::ChatId; use teloxide::types::{Chat, ChatId};
use wd_log::{log_info_ln, log_warn_ln}; 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)] #[derive(Debug, Clone)]
pub struct Controller { pub struct Controller {
@ -63,28 +71,49 @@ impl Controller {
} }
/// stats /// 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; const LIMIT: u64 = 10;
let transcation = self.db.begin().await?; let transcation = match self.db.begin().await {
Ok(t) => t,
let query = match group_id.is_group() { Err(error) => {
true => { log_error_ln!("{}", error);
Stats::find() return None;
.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?
} }
}; };
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
}
}
} }
} }

View File

@ -43,14 +43,25 @@ async fn main() {
Commands::repl(bot, move |bot: Bot, message: Message, cmd: Commands| { Commands::repl(bot, move |bot: Bot, message: Message, cmd: Commands| {
let command_handler = command_handler.clone(); let command_handler = command_handler.clone();
async move { async move {
let _ = match cmd { let r = match cmd {
Commands::Help => command_handler.clone().help_handler(&bot, &message).await, Commands::Help => command_handler.help_handler(&bot, &message).await,
Commands::About => command_handler.clone().about_handler(&bot, &message).await, Commands::About => command_handler.about_handler(&bot, &message).await,
Commands::Top => command_handler.clone().top_handler(&bot, &message).await, Commands::Top => command_handler.top_handler(&bot, &message).await,
Commands::HangIt => command_handler.clone().hangit_handler(&bot, &message).await, Commands::HangIt => command_handler.hangit_handler(&bot, &message).await,
}; };
Ok(())
match r {
Ok(_r) => {
log_debug_ln!("will send: {:?}", _r.text());
Ok(())
},
Err(err) => {
log_error_ln!("{:?}", err);
Err(err)
}
}
} }
}) })
.await; .await;

View File

@ -1,5 +1,5 @@
pub const BOT_ABOUT: &'static str = 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_NO_TARGET: &'static str = "请在回复某一条信息时使用该指令";
pub const BOT_TEXT_IS_CHANNEL: &'static str = "这不是个人,吊不起来"; pub const BOT_TEXT_IS_CHANNEL: &'static str = "这不是个人,吊不起来";