Documentation handler and callback query dispatcher

This commit is contained in:
Nefrace 2022-10-18 15:17:11 +03:00
parent 759dc374ea
commit a13d6dc0ca
6 changed files with 158 additions and 27 deletions

4
Cargo.lock generated
View File

@ -357,6 +357,10 @@ dependencies = [
"lazy_static",
"log",
"pretty_env_logger",
"regex",
"reqwest",
"serde",
"serde_json",
"teloxide",
"tokio",
"url",

View File

@ -13,3 +13,7 @@ tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] }
lazy_static = "1.4.0"
url = "2.3.1"
dotenv = "0.15.0"
regex = "1"
reqwest = "0.11.12"
serde = "1.0.145"
serde_json = "1.0.86"

View File

@ -39,29 +39,33 @@ impl Godette {
pub fn create_handler(
&self,
) -> Handler<'static, DependencyMap, Result<(), RequestError>, DpHandlerDescription> {
Update::filter_message()
// User commands
dptree::entry()
.branch(Update::filter_callback_query().endpoint(Godette::callback_dispatcher))
.branch(
dptree::entry()
.filter_command::<commands::Command>()
.endpoint(Godette::commands_dispatcher),
)
// Admin commands
.branch(
dptree::entry()
.filter_command::<commands::AdminCommand>()
.endpoint(Godette::admin_dispatcher),
)
// Replies
// .branch(Message::filter_reply_to_message().endpoint(Godette::reply_dispatcher))
// Messages
.branch(
dptree::filter(|msg: Message| {
msg.from()
.map(|user| user.id == UserId(60441930))
.unwrap_or_default()
})
.endpoint(Godette::message_dispatcher),
Update::filter_message()
// User commands
.branch(
dptree::entry()
.filter_command::<commands::Command>()
.endpoint(Godette::commands_dispatcher),
)
// Admin commands
.branch(
dptree::entry()
.filter_command::<commands::AdminCommand>()
.endpoint(Godette::admin_dispatcher),
)
// Replies
// .branch(Message::filter_reply_to_message().endpoint(Godette::reply_dispatcher))
// Messages
.branch(
dptree::filter(|msg: Message| {
msg.from()
.map(|user| user.id == UserId(60441930))
.unwrap_or_default()
})
.endpoint(Godette::message_dispatcher),
),
)
}
}

View File

@ -1,7 +1,9 @@
use teloxide::prelude::*;
use super::{handlers, utils, Godette, KarmaTrigger, Trigger};
use super::{handlers, utils, Godette};
use crate::commands::{AdminCommand, Command};
use lazy_static::lazy_static;
use regex::Regex;
impl Godette {
pub async fn commands_dispatcher(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> {
@ -40,12 +42,27 @@ impl Godette {
}
None => (),
};
let text = utils::get_text_or_empty(&msg).to_lowercase();
match text.find("оффтоп") {
let text = utils::get_text_or_empty(&msg);
match text.to_lowercase().find("оффтоп") {
Some(_) => handlers::offtop(&bot, &msg).await?,
None => (),
}
lazy_static! {
static ref DOC_RE: Regex =
Regex::new(r"(?i)док(ументац[а-я]+|[а-я])? ((п)?о )?(?P<topic>@?[\w\d]{1,32})")
.unwrap();
}
if let Some(caps) = DOC_RE.captures(&text) {
match caps.name("topic") {
Some(topic) => {
handlers::documentation(&bot, &msg, String::from(topic.as_str())).await?
}
None => (),
}
}
Ok(())
}
@ -55,4 +72,17 @@ impl Godette {
Ok(())
}
pub async fn callback_dispatcher(bot: Bot, q: CallbackQuery) -> ResponseResult<()> {
if let Some(data) = q.data {
bot.answer_callback_query(q.id).await?;
if data == "no_thanks" {
if let Some(Message { id, chat, .. }) = q.message {
bot.delete_message(chat.id, id).await?;
}
}
}
Ok(())
}
}

View File

@ -5,6 +5,7 @@ use teloxide::{
utils::command::BotCommands,
utils::markdown::{self, bold, escape, italic},
};
use url::Url;
use crate::commands::{AdminCommand, Command};
@ -114,3 +115,38 @@ pub async fn offtop(bot: &Bot, msg: &Message) -> ResponseResult<()> {
.await?;
Ok(())
}
pub async fn documentation(bot: &Bot, msg: &Message, topic: String) -> ResponseResult<()> {
let mut text = format!(
"Извините, по запросу \"{}\" ничего не найдено\\.",
escape(&topic)
);
let btn_text = format!("Поиск \"{}\"", topic);
let btn_url = Url::parse(&format!(
"https://docs.godotengine.org/ru/stable/search.html?q={}",
topic
))
.unwrap();
let results = utils::request_docs(&topic).await;
if results.len() > 0 {
let links = results
.iter()
.take(10)
.map(|res| format!("\\- [{}]({})", escape(&res.title), res.path))
.collect::<Vec<String>>()
.join("\n");
text = format!(
"Вот что удалось мне найти в документации по запроу {}:\n\n{}",
bold(&escape(&topic)),
links
);
}
bot.send_message(msg.chat.id, text)
.parse_mode(MarkdownV2)
.reply_markup(utils::make_docs_keyboard(btn_text, btn_url))
.reply_to_message_id(msg.id)
.await?;
Ok(())
}

View File

@ -1,3 +1,4 @@
use serde::{Deserialize, Serialize};
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, Message};
use url::Url;
@ -7,8 +8,60 @@ pub fn get_text_or_empty(msg: &Message) -> String {
.to_string()
}
fn no_thanks_button() -> InlineKeyboardButton {
InlineKeyboardButton::callback("Спасибо, не надо", "no_thanks")
}
pub fn make_offtop_keyboard() -> InlineKeyboardMarkup {
let link = Url::parse("https://t.me/Godot_Engine_Offtop").unwrap();
let button = InlineKeyboardButton::url("Godot Engine оффтоп чат".to_owned(), link);
return InlineKeyboardMarkup::new(vec![[button]]);
InlineKeyboardMarkup::new(vec![[button], [no_thanks_button()]])
}
pub fn make_docs_keyboard(text: String, url: Url) -> InlineKeyboardMarkup {
let button = InlineKeyboardButton::url(text, url);
InlineKeyboardMarkup::new(vec![[button], [no_thanks_button()]])
}
#[derive(Serialize, Deserialize, Debug)]
struct DocResponse {
count: u32,
next: Option<String>,
previous: Option<String>,
results: Vec<DocResult>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DocResult {
pub title: String,
pub path: String,
}
pub async fn request_docs(query: &str) -> Vec<DocResult> {
let empty_results: Vec<DocResult> = Vec::new();
let link = format!(
"https://docs.godotengine.org/_/api/v2/search/?q={}&project=godot-ru&version=stable&language=ru",
query
);
let domain = "https://docs.godotengine.org";
let response = reqwest::get(link).await;
match response {
Ok(response) => {
let json = response.json::<DocResponse>().await;
match json {
Ok(json) => {
return json
.results
.iter()
.map(|res| DocResult {
path: format!("{}{}", domain, res.path),
title: res.title.clone(),
})
.collect::<Vec<DocResult>>()
}
Err(_) => empty_results,
}
}
Err(_) => empty_results,
}
}