mirror of
https://github.com/Steffo99/patched-porobot.git
synced 2024-12-22 17:44:22 +00:00
Complete most of the telegram bot
This commit is contained in:
parent
b2a2535b93
commit
25df599586
9 changed files with 189 additions and 78 deletions
|
@ -7,6 +7,7 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/card-data" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/benches" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1036,8 +1036,10 @@ version = "0.3.0"
|
|||
dependencies = [
|
||||
"glob",
|
||||
"itertools 0.10.3",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
@ -12,7 +12,9 @@ categories = ["games", "parser-implementations"]
|
|||
[dependencies]
|
||||
# base
|
||||
log = { version = "0.4.17" }
|
||||
itertools = { version = "0.10.3" } # Not using this yet
|
||||
itertools = { version = "0.10.3" }
|
||||
regex = { version = "1.6.0" }
|
||||
lazy_static = { version = "1.4.0" }
|
||||
# exec
|
||||
pretty_env_logger = { version = "0.4.0", optional = true }
|
||||
glob = { version = "0.3.0", optional = true }
|
||||
|
|
127
src/bin/patched_porobot_telegram.rs
Normal file
127
src/bin/patched_porobot_telegram.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
#[cfg(not(feature = "telegram"))]
|
||||
fn main() {
|
||||
println!("The `telegram` feature was not included on compilation, therefore this binary is not available.")
|
||||
}
|
||||
|
||||
#[cfg(feature = "telegram")]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use std::path::PathBuf;
|
||||
use log::*;
|
||||
use patched_porobot::data::setbundle::card::{Card, CardIndex};
|
||||
use patched_porobot::data::corebundle::CoreBundle;
|
||||
use patched_porobot::data::setbundle::SetBundle;
|
||||
use patched_porobot::data::corebundle::globals::LocalizedGlobalsIndexes;
|
||||
use patched_porobot::search::cardsearch::CardSearchEngine;
|
||||
use patched_porobot::telegram::inline::card_to_inlinequeryresult;
|
||||
use teloxide::payloads::AnswerInlineQuery;
|
||||
use teloxide::requests::JsonRequest;
|
||||
use teloxide::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
pretty_env_logger::init();
|
||||
debug!("Logger initialized successfully!");
|
||||
|
||||
debug!("Loading bundles...");
|
||||
let core = CoreBundle::load(&*PathBuf::from("./card-data/core-en_us")).expect("to be able to load `core-en_us` bundle");
|
||||
let set1 = SetBundle::load(&*PathBuf::from("./card-data/set1-en_us")).expect("to be able to load `set1-en_us` bundle");
|
||||
let set2 = SetBundle::load(&*PathBuf::from("./card-data/set2-en_us")).expect("to be able to load `set2-en_us` bundle");
|
||||
let set3 = SetBundle::load(&*PathBuf::from("./card-data/set3-en_us")).expect("to be able to load `set3-en_us` bundle");
|
||||
let set4 = SetBundle::load(&*PathBuf::from("./card-data/set4-en_us")).expect("to be able to load `set4-en_us` bundle");
|
||||
let set5 = SetBundle::load(&*PathBuf::from("./card-data/set5-en_us")).expect("to be able to load `set5-en_us` bundle");
|
||||
let set6 = SetBundle::load(&*PathBuf::from("./card-data/set6-en_us")).expect("to be able to load `set6-en_us` bundle");
|
||||
debug!("Loaded all bundles!");
|
||||
|
||||
debug!("Indexing globals...");
|
||||
let globals = LocalizedGlobalsIndexes::from(core.globals);
|
||||
debug!("Indexed globals!");
|
||||
|
||||
debug!("Indexing cards...");
|
||||
let cards: Vec<Card> = [
|
||||
set1.cards,
|
||||
set2.cards,
|
||||
set3.cards,
|
||||
set4.cards,
|
||||
set5.cards,
|
||||
set6.cards
|
||||
].concat();
|
||||
|
||||
let mut index = CardIndex::new();
|
||||
for card in cards {
|
||||
index.insert(card.code.clone(), card);
|
||||
}
|
||||
let cards = index;
|
||||
debug!("Indexed cards!");
|
||||
|
||||
debug!("Creating search engine...");
|
||||
let engine = CardSearchEngine::new(globals, cards);
|
||||
debug!("Created search engine!");
|
||||
|
||||
debug!("Creating Telegram bot with parameters from the environment...");
|
||||
let bot = Bot::from_env();
|
||||
let me = bot.get_me().send().await.expect("Telegram bot parameters to be valid");
|
||||
debug!("Created Telegram bot!");
|
||||
|
||||
debug!("Creating inline query handler...");
|
||||
let handler = Update::filter_inline_query().chain(dptree::endpoint(move |query: InlineQuery, bot: Bot| {
|
||||
info!("Handling inline query: `{}`", &query.query);
|
||||
|
||||
debug!("Querying the search engine...");
|
||||
let performed_query = engine.query(&query.query, 50);
|
||||
|
||||
let payload = match performed_query {
|
||||
Ok(results) => {
|
||||
if results.len() > 0 {
|
||||
AnswerInlineQuery {
|
||||
inline_query_id: query.id.clone(),
|
||||
results: results
|
||||
.iter()
|
||||
.map(|card| card_to_inlinequeryresult(&engine.globals, card))
|
||||
.collect_vec(),
|
||||
cache_time: Some(86400),
|
||||
is_personal: Some(false),
|
||||
next_offset: None,
|
||||
switch_pm_text: None,
|
||||
switch_pm_parameter: None,
|
||||
}
|
||||
}
|
||||
else {
|
||||
AnswerInlineQuery {
|
||||
inline_query_id: query.id.clone(),
|
||||
results: vec![],
|
||||
cache_time: None,
|
||||
is_personal: Some(false),
|
||||
next_offset: None,
|
||||
switch_pm_text: Some("No results found".to_string()),
|
||||
switch_pm_parameter: Some("err-no-results".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
AnswerInlineQuery {
|
||||
inline_query_id: query.id.clone(),
|
||||
results: vec![],
|
||||
cache_time: None,
|
||||
is_personal: Some(false),
|
||||
next_offset: None,
|
||||
switch_pm_text: Some("Invalid query syntax".to_string()),
|
||||
switch_pm_parameter: Some("err-invalid-query".to_string()),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async move {
|
||||
let telegram_reply = JsonRequest::new(bot.clone(), payload).send().await;
|
||||
|
||||
if let Err(e) = telegram_reply {
|
||||
error!("{:?}", &e);
|
||||
}
|
||||
|
||||
respond(())
|
||||
}
|
||||
}));
|
||||
debug!("Create inline query handler!");
|
||||
|
||||
info!("@{} is ready!", &me.username.as_ref().expect("bot to have an username"));
|
||||
Dispatcher::builder(bot, handler).enable_ctrlc_handler().build().dispatch().await;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#[cfg(not(feature = "telegram"))]
|
||||
fn main() {
|
||||
println!("The `telegram` feature was not included on compilation, therefore this binary is not available.")
|
||||
}
|
||||
|
||||
#[cfg(feature = "telegram")]
|
||||
fn main() {
|
||||
println!("Hello telegram world!")
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
//! Module defining [CardArt].
|
||||
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
|
||||
/// The illustration of a [Card](super::card::Card), also referred to as an *art asset*.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CardArt {
|
||||
|
@ -40,9 +43,13 @@ impl CardArt {
|
|||
/// ```
|
||||
///
|
||||
pub fn card_jpg(&self) -> String {
|
||||
self.card_png
|
||||
.replace("https://dd.b.pvp.net/latest/set1", "https://poro.steffo.eu/set1-en_us")
|
||||
.replace(".png", ".jpg")
|
||||
lazy_static! {
|
||||
static ref GET_JPG: Regex = Regex::new(
|
||||
r#"https?://dd[.]b[.]pvp[.]net/[^/]+/(?P<bundle>[^/]+)/(?P<locale>[^/]+)/img/cards/(?P<code>.+)[.]png$"#
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
GET_JPG.replace_all(&self.card_png, "https://poro.steffo.eu/$bundle-$locale/$locale/img/cards/$code.jpg").to_string()
|
||||
}
|
||||
|
||||
/// URL to the `.jpg` image of the `en_us` locale of the full card art, via `poro.steffo.eu`.
|
||||
|
@ -56,9 +63,13 @@ impl CardArt {
|
|||
/// ```
|
||||
///
|
||||
pub fn full_jpg(&self) -> String {
|
||||
self.full_png
|
||||
.replace("https://dd.b.pvp.net/latest/set1", "https://poro.steffo.eu/set1-en_us")
|
||||
.replace(".png", ".jpg")
|
||||
lazy_static! {
|
||||
static ref GET_JPG: Regex = Regex::new(
|
||||
r#"https?://dd[.]b[.]pvp[.]net/[^/]+/(?P<bundle>[^/]+)/(?P<locale>[^/]+)/img/cards/(?P<code>.+)[.]png$"#
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
GET_JPG.replace_all(&self.full_png, "https://poro.steffo.eu/$bundle-$locale/$locale/img/cards/$code.jpg").to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,12 +47,17 @@ impl CardSearchEngine {
|
|||
/// Create the [tantivy::schema::TextOptions] for card codes.
|
||||
///
|
||||
/// Card codes should:
|
||||
/// - never be tokenized;
|
||||
/// - TODO: be tokenized without alterations;
|
||||
/// - ignore positioning;
|
||||
/// - be retrievable (what [tantivy] calls "stored").
|
||||
fn options_code() -> TextOptions {
|
||||
use tantivy::schema::*;
|
||||
|
||||
TextOptions::default()
|
||||
.set_indexing_options(TextFieldIndexing::default()
|
||||
.set_tokenizer("card")
|
||||
.set_index_option(IndexRecordOption::Basic)
|
||||
)
|
||||
.set_stored()
|
||||
.set_fast()
|
||||
}
|
||||
|
@ -253,7 +258,14 @@ impl CardSearchEngine {
|
|||
fn parser(index: &Index, fields: CardSchemaFields) -> QueryParser {
|
||||
QueryParser::for_index(
|
||||
&index,
|
||||
Vec::from(fields)
|
||||
vec![
|
||||
fields.code,
|
||||
fields.name,
|
||||
fields.description,
|
||||
fields.flavor,
|
||||
fields.subtypes,
|
||||
fields.supertype,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -346,28 +358,3 @@ struct CardSchemaFields {
|
|||
/// [Card::supertype].
|
||||
pub supertype: Field,
|
||||
}
|
||||
|
||||
impl From<CardSchemaFields> for Vec<Field> {
|
||||
fn from(fields: CardSchemaFields) -> Self {
|
||||
vec![
|
||||
fields.code,
|
||||
fields.name,
|
||||
fields.r#type,
|
||||
fields.set,
|
||||
fields.rarity,
|
||||
fields.collectible,
|
||||
fields.regions,
|
||||
fields.attack,
|
||||
fields.cost,
|
||||
fields.health,
|
||||
fields.spellspeed,
|
||||
fields.keywords,
|
||||
fields.description,
|
||||
fields.levelup,
|
||||
fields.flavor,
|
||||
fields.artist,
|
||||
fields.subtypes,
|
||||
fields.supertype,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,24 +22,23 @@ use crate::data::setbundle::supertype::CardSupertype;
|
|||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String {
|
||||
let title = format!(
|
||||
r#"<a href="{}"><b><i>{}</b></i></a>"#,
|
||||
&card.main_art().expect("Card to have at least one illustration").card_png,
|
||||
"<b><u>{}</u></b>\n",
|
||||
escape(&card.name),
|
||||
);
|
||||
|
||||
let stats = match &card.r#type {
|
||||
CardType::Spell => format!(
|
||||
"{} mana",
|
||||
"{} mana\n",
|
||||
escape(&card.cost.to_string()),
|
||||
),
|
||||
CardType::Unit => format!(
|
||||
"{} mana {}|{}",
|
||||
"{} mana {}|{}\n",
|
||||
escape(&card.cost.to_string()),
|
||||
escape(&card.attack.to_string()),
|
||||
escape(&card.health.to_string()),
|
||||
),
|
||||
CardType::Landmark => format!(
|
||||
"{} mana",
|
||||
"{} mana\n",
|
||||
&card.cost
|
||||
),
|
||||
_ => "".to_string(),
|
||||
|
@ -49,32 +48,25 @@ pub fn display_card(globals: &LocalizedGlobalsIndexes, card: &Card) -> String {
|
|||
let regions = display_regions(&card.regions, &globals.regions);
|
||||
let r#type = display_types(&card.r#type, &card.supertype, &card.subtypes);
|
||||
|
||||
let breadcrumbs = format!("{} › {} › {}", &set, ®ions, &r#type);
|
||||
let breadcrumbs = format!("{} › {} › {}\n", &set, ®ions, &r#type);
|
||||
|
||||
let keywords = display_keywords(&card.keywords, &globals.keywords);
|
||||
|
||||
let description = escape(&card.localized_description_text);
|
||||
let description = format!("{}\n", escape(&card.localized_description_text));
|
||||
|
||||
let flavor = format!(
|
||||
"<i>{}</i>",
|
||||
"<i>{}</i>\n",
|
||||
escape(&card.localized_flavor_text)
|
||||
);
|
||||
|
||||
let artist = format!(
|
||||
r#"<a href="{}">Illustration by {}</a>"#,
|
||||
r#"<a href="{}">Illustration</a> by {}"#,
|
||||
&card.main_art().expect("Card to have at least one illustration").full_png,
|
||||
escape(&card.artist_name)
|
||||
);
|
||||
|
||||
format!(
|
||||
"{title} {stats}\n{breadcrumbs}\n\n{keywords}\n{description}\n\n-----\n{flavor}\n\n{artist}",
|
||||
title=title,
|
||||
stats=stats,
|
||||
breadcrumbs=breadcrumbs,
|
||||
keywords=keywords,
|
||||
description=description,
|
||||
flavor=flavor,
|
||||
artist=artist,
|
||||
"{title}{breadcrumbs}\n{keywords}{stats}{description}\n-----\n{flavor}{artist}",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -145,12 +137,16 @@ fn display_types(r#type: &CardType, supertype: &CardSupertype, subtypes: &[CardS
|
|||
///
|
||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||
fn display_keywords(keywords: &[CardKeyword], hm: &LocalizedCardKeywordIndex) -> String {
|
||||
keywords
|
||||
.iter()
|
||||
.map(|keyword| keyword
|
||||
.localized(hm)
|
||||
.map(|o| format!("[<b>{}</b>]", escape(&o.name)))
|
||||
.unwrap_or_else(|| "Unknown".to_string())
|
||||
)
|
||||
.join(" ")
|
||||
format!(
|
||||
"{}\n",
|
||||
keywords
|
||||
.iter()
|
||||
.map(|keyword| keyword
|
||||
.localized(hm)
|
||||
.map(|o| format!("[<b>{}</b>]", escape(&o.name)))
|
||||
.unwrap_or_else(|| "Unknown".to_string())
|
||||
)
|
||||
.join(" ")
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//!
|
||||
//! [inline mode]: https://core.telegram.org/bots/api#inline-mode
|
||||
|
||||
use teloxide::types::{InlineQueryResult, InlineQueryResultPhoto, InputMessageContent, InputMessageContentText, ParseMode};
|
||||
use teloxide::types::{InlineQueryResult, InlineQueryResultPhoto, ParseMode};
|
||||
use crate::data::corebundle::globals::LocalizedGlobalsIndexes;
|
||||
use crate::data::setbundle::card::Card;
|
||||
use crate::telegram::display::display_card;
|
||||
|
@ -13,6 +13,8 @@ pub fn card_to_inlinequeryresult(globals: &LocalizedGlobalsIndexes, card: &Card)
|
|||
InlineQueryResult::Photo(InlineQueryResultPhoto {
|
||||
id: card.code.to_owned(),
|
||||
title: Some(card.name.to_owned()),
|
||||
caption: Some(display_card(&globals, &card)),
|
||||
parse_mode: Some(ParseMode::Html),
|
||||
photo_url: card
|
||||
.main_art()
|
||||
.expect("Card to have at least one illustration")
|
||||
|
@ -26,17 +28,9 @@ pub fn card_to_inlinequeryresult(globals: &LocalizedGlobalsIndexes, card: &Card)
|
|||
photo_width: Some(680),
|
||||
photo_height: Some(1024),
|
||||
|
||||
input_message_content: Some(InputMessageContent::Text(InputMessageContentText {
|
||||
message_text: display_card(&globals, &card),
|
||||
parse_mode: Some(ParseMode::Html),
|
||||
entities: None,
|
||||
disable_web_page_preview: Some(true)
|
||||
})),
|
||||
|
||||
description: None,
|
||||
caption: None,
|
||||
parse_mode: None,
|
||||
caption_entities: None,
|
||||
reply_markup: None,
|
||||
input_message_content: None,
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue