Documentation handler and callback query dispatcher
This commit is contained in:
parent
759dc374ea
commit
a13d6dc0ca
|
@ -357,6 +357,10 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
"regex",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"teloxide",
|
"teloxide",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
|
|
|
@ -12,4 +12,8 @@ pretty_env_logger = "0.4"
|
||||||
tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
url = "2.3.1"
|
url = "2.3.1"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
regex = "1"
|
||||||
|
reqwest = "0.11.12"
|
||||||
|
serde = "1.0.145"
|
||||||
|
serde_json = "1.0.86"
|
|
@ -39,29 +39,33 @@ impl Godette {
|
||||||
pub fn create_handler(
|
pub fn create_handler(
|
||||||
&self,
|
&self,
|
||||||
) -> Handler<'static, DependencyMap, Result<(), RequestError>, DpHandlerDescription> {
|
) -> Handler<'static, DependencyMap, Result<(), RequestError>, DpHandlerDescription> {
|
||||||
Update::filter_message()
|
dptree::entry()
|
||||||
// User commands
|
.branch(Update::filter_callback_query().endpoint(Godette::callback_dispatcher))
|
||||||
.branch(
|
.branch(
|
||||||
dptree::entry()
|
Update::filter_message()
|
||||||
.filter_command::<commands::Command>()
|
// User commands
|
||||||
.endpoint(Godette::commands_dispatcher),
|
.branch(
|
||||||
)
|
dptree::entry()
|
||||||
// Admin commands
|
.filter_command::<commands::Command>()
|
||||||
.branch(
|
.endpoint(Godette::commands_dispatcher),
|
||||||
dptree::entry()
|
)
|
||||||
.filter_command::<commands::AdminCommand>()
|
// Admin commands
|
||||||
.endpoint(Godette::admin_dispatcher),
|
.branch(
|
||||||
)
|
dptree::entry()
|
||||||
// Replies
|
.filter_command::<commands::AdminCommand>()
|
||||||
// .branch(Message::filter_reply_to_message().endpoint(Godette::reply_dispatcher))
|
.endpoint(Godette::admin_dispatcher),
|
||||||
// Messages
|
)
|
||||||
.branch(
|
// Replies
|
||||||
dptree::filter(|msg: Message| {
|
// .branch(Message::filter_reply_to_message().endpoint(Godette::reply_dispatcher))
|
||||||
msg.from()
|
// Messages
|
||||||
.map(|user| user.id == UserId(60441930))
|
.branch(
|
||||||
.unwrap_or_default()
|
dptree::filter(|msg: Message| {
|
||||||
})
|
msg.from()
|
||||||
.endpoint(Godette::message_dispatcher),
|
.map(|user| user.id == UserId(60441930))
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.endpoint(Godette::message_dispatcher),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude::*;
|
||||||
|
|
||||||
use super::{handlers, utils, Godette, KarmaTrigger, Trigger};
|
use super::{handlers, utils, Godette};
|
||||||
use crate::commands::{AdminCommand, Command};
|
use crate::commands::{AdminCommand, Command};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
impl Godette {
|
impl Godette {
|
||||||
pub async fn commands_dispatcher(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> {
|
pub async fn commands_dispatcher(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> {
|
||||||
|
@ -40,12 +42,27 @@ impl Godette {
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
};
|
};
|
||||||
let text = utils::get_text_or_empty(&msg).to_lowercase();
|
let text = utils::get_text_or_empty(&msg);
|
||||||
match text.find("оффтоп") {
|
match text.to_lowercase().find("оффтоп") {
|
||||||
Some(_) => handlers::offtop(&bot, &msg).await?,
|
Some(_) => handlers::offtop(&bot, &msg).await?,
|
||||||
None => (),
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,4 +72,17 @@ impl Godette {
|
||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use teloxide::{
|
||||||
utils::command::BotCommands,
|
utils::command::BotCommands,
|
||||||
utils::markdown::{self, bold, escape, italic},
|
utils::markdown::{self, bold, escape, italic},
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use crate::commands::{AdminCommand, Command};
|
use crate::commands::{AdminCommand, Command};
|
||||||
|
|
||||||
|
@ -114,3 +115,38 @@ pub async fn offtop(bot: &Bot, msg: &Message) -> ResponseResult<()> {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, Message};
|
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, Message};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -7,8 +8,60 @@ pub fn get_text_or_empty(msg: &Message) -> String {
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn no_thanks_button() -> InlineKeyboardButton {
|
||||||
|
InlineKeyboardButton::callback("Спасибо, не надо", "no_thanks")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_offtop_keyboard() -> InlineKeyboardMarkup {
|
pub fn make_offtop_keyboard() -> InlineKeyboardMarkup {
|
||||||
let link = Url::parse("https://t.me/Godot_Engine_Offtop").unwrap();
|
let link = Url::parse("https://t.me/Godot_Engine_Offtop").unwrap();
|
||||||
let button = InlineKeyboardButton::url("Godot Engine оффтоп чат".to_owned(), link);
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue