mirror of
https://github.com/Steffo99/patched-porobot.git
synced 2024-12-23 01:54:22 +00:00
Continue building the skeleton of the bot
This commit is contained in:
parent
e17c2b446f
commit
6810e1acc7
6 changed files with 147 additions and 52 deletions
|
@ -1,10 +1,7 @@
|
||||||
//! This module contains functions to load **Set Bundles** from [Data Dragon](https://developer.riotgames.com/docs/lol).
|
//! This module contains functions to load **Set Bundles** from [Data Dragon](https://developer.riotgames.com/docs/lol).
|
||||||
|
|
||||||
use std::path::*;
|
|
||||||
use glob::{glob, GlobResult};
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use crate::data::schema::Card;
|
use crate::data::schema::Card;
|
||||||
use log::*;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -15,7 +12,7 @@ enum LoadingError {
|
||||||
|
|
||||||
|
|
||||||
/// Load a single Set Bundle and create a [Vec] with the cards contained in it.
|
/// Load a single Set Bundle and create a [Vec] with the cards contained in it.
|
||||||
fn load_setbundle(path: PathBuf) -> Result<Vec<Card>, LoadingError> {
|
fn load_setbundle(path: std::path::PathBuf) -> Result<Vec<Card>, LoadingError> {
|
||||||
let file = std::fs::File::open(path)
|
let file = std::fs::File::open(path)
|
||||||
.map_err(LoadingError::IO)?;
|
.map_err(LoadingError::IO)?;
|
||||||
let data = serde_json::de::from_reader::<std::fs::File, Vec<Card>>(file)
|
let data = serde_json::de::from_reader::<std::fs::File, Vec<Card>>(file)
|
||||||
|
@ -25,11 +22,11 @@ fn load_setbundle(path: PathBuf) -> Result<Vec<Card>, LoadingError> {
|
||||||
|
|
||||||
|
|
||||||
/// Load a single Set Bundle (similarly to [load_setbundle]), but instead of returning a [Result], return an empty [Vec] in case of failure and log a [warn]ing.
|
/// Load a single Set Bundle (similarly to [load_setbundle]), but instead of returning a [Result], return an empty [Vec] in case of failure and log a [warn]ing.
|
||||||
fn load_setbundle_infallible(path: PathBuf) -> Vec<Card> {
|
fn load_setbundle_infallible(path: std::path::PathBuf) -> Vec<Card> {
|
||||||
match load_setbundle(path) {
|
match load_setbundle(path) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("{:?}", e);
|
log::warn!("{:?}", e);
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,9 +35,9 @@ fn load_setbundle_infallible(path: PathBuf) -> Vec<Card> {
|
||||||
|
|
||||||
/// Load all Set Bundles matched by the passed glob, using [load_setbundle_infallible] and then concatenating the resulting [Vec]s.
|
/// Load all Set Bundles matched by the passed glob, using [load_setbundle_infallible] and then concatenating the resulting [Vec]s.
|
||||||
pub fn load_setbundles_infallible(pattern: &str) -> Vec<Card> {
|
pub fn load_setbundles_infallible(pattern: &str) -> Vec<Card> {
|
||||||
glob(pattern)
|
glob::glob(pattern)
|
||||||
.expect("a valid glob")
|
.expect("a valid glob")
|
||||||
.filter_map(GlobResult::ok)
|
.filter_map(glob::GlobResult::ok)
|
||||||
.map(load_setbundle_infallible)
|
.map(load_setbundle_infallible)
|
||||||
.concat()
|
.concat()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +1,94 @@
|
||||||
/// A single Legends of Runeterra card as represented in the data files from [Data Dragon](https://developer.riotgames.com/docs/lor).
|
/// A single Legends of Runeterra card as represented in the data files from [Data Dragon](https://developer.riotgames.com/docs/lor).
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
#[serde(rename_all="camelCase")]
|
#[serde(rename_all="camelCase")]
|
||||||
pub struct Card {
|
pub struct Card {
|
||||||
/// Localized names of the cards associated with this one.
|
/// Localized names of the cards associated with this one.
|
||||||
/// For some reason, might not match what is contained in `associated_card_refs`.
|
/// For some reason, might not match what is contained in `associated_card_refs`.
|
||||||
associated_cards: Vec<String>,
|
pub associated_cards: Vec<String>,
|
||||||
/// `card_code`s of the cards associated with this one.
|
/// `card_code`s of the cards associated with this one.
|
||||||
associated_card_refs: Vec<String>,
|
pub associated_card_refs: Vec<String>,
|
||||||
|
|
||||||
/// Art assets of this card.
|
/// Art assets of this card.
|
||||||
assets: Vec<Asset>,
|
pub assets: Vec<Asset>,
|
||||||
|
|
||||||
/// Localized names of the regions this card belongs to.
|
/// Localized names of the regions this card belongs to.
|
||||||
regions: Vec<String>,
|
pub regions: Vec<String>,
|
||||||
/// IDs of the regions this card belongs to.
|
/// IDs of the regions this card belongs to.
|
||||||
region_refs: Vec<String>,
|
pub region_refs: Vec<String>,
|
||||||
|
|
||||||
/// Base attack of the card.
|
/// Base attack of the card.
|
||||||
attack: i8,
|
pub attack: i8,
|
||||||
/// Base cost of the card.
|
/// Base cost of the card.
|
||||||
cost: i8,
|
pub cost: i8,
|
||||||
/// Base health of the card.
|
/// Base health of the card.
|
||||||
health: i8,
|
pub health: i8,
|
||||||
|
|
||||||
/// Localized description of the card, in XML.
|
/// Localized description of the card, in XML.
|
||||||
description: String,
|
pub description: String,
|
||||||
/// Localized description of the card, in plain text.
|
/// Localized description of the card, in plain text.
|
||||||
description_raw: String,
|
pub description_raw: String,
|
||||||
|
|
||||||
/// Localized level up text of the card, in XML.
|
/// Localized level up text of the card, in XML.
|
||||||
levelup_description: String,
|
pub levelup_description: String,
|
||||||
/// Localized level up text of the card, in plain text.
|
/// Localized level up text of the card, in plain text.
|
||||||
levelup_description_raw: String,
|
pub levelup_description_raw: String,
|
||||||
|
|
||||||
/// Flavor text of the card, displayed when its image is inspected.
|
/// Flavor text of the card, displayed when its image is inspected.
|
||||||
flavor_text: String,
|
pub flavor_text: String,
|
||||||
/// Name of the artist who drew the card.
|
/// Name of the artist who drew the card.
|
||||||
artist_name: String,
|
pub artist_name: String,
|
||||||
|
|
||||||
/// Localized name of the card.
|
/// Localized name of the card.
|
||||||
name: String,
|
pub name: String,
|
||||||
/// Unique seven-character identifier of the card.
|
/// Unique seven-character identifier of the card.
|
||||||
card_code: String,
|
pub card_code: String,
|
||||||
|
|
||||||
/// List of keywords of this card, with their localized names.
|
/// List of keywords of this card, with their localized names.
|
||||||
keywords: Vec<String>,
|
pub keywords: Vec<String>,
|
||||||
/// List of keywords of this card, with their internal names.
|
/// List of keywords of this card, with their internal names.
|
||||||
keyword_refs: Vec<String>,
|
pub keyword_refs: Vec<String>,
|
||||||
|
|
||||||
/// Localized spell speed.
|
/// Localized spell speed.
|
||||||
spell_speed: String,
|
pub spell_speed: String,
|
||||||
/// [SpellSpeed] of the card.
|
/// [SpellSpeed] of the card.
|
||||||
spell_speed_ref: SpellSpeed,
|
pub spell_speed_ref: SpellSpeed,
|
||||||
|
|
||||||
/// Localized rarity of the card.
|
/// Localized rarity of the card.
|
||||||
rarity: String,
|
pub rarity: String,
|
||||||
/// [CardRarity] of the card.
|
/// [CardRarity] of the card.
|
||||||
rarity_ref: CardRarity,
|
pub rarity_ref: CardRarity,
|
||||||
|
|
||||||
/// The subtypes the card has, such as `PORO`.
|
/// The subtypes the card has, such as `PORO`.
|
||||||
subtypes: Vec<String>,
|
pub subtypes: Vec<String>,
|
||||||
/// The [CardSupertype] the card belongs to, such as `Champion`.
|
/// The [CardSupertype] the card belongs to, such as `Champion`.
|
||||||
supertype: CardSupertype,
|
pub supertype: CardSupertype,
|
||||||
|
|
||||||
/// If `true`, the card can be found in chests, crafted, or used in decks.
|
/// If `true`, the card can be found in chests, crafted, or used in decks.
|
||||||
/// If `false`, the card is not available for direct use, as it is probably created by another card.
|
/// If `false`, the card is not available for direct use, as it is probably created by another card.
|
||||||
collectible: bool,
|
pub collectible: bool,
|
||||||
|
|
||||||
/// The [CardSet] the card belongs to.
|
/// The [CardSet] the card belongs to.
|
||||||
set: String,
|
pub set: String,
|
||||||
|
|
||||||
#[serde(rename(serialize = "type", deserialize = "type"))]
|
#[serde(rename(serialize = "type", deserialize = "type"))]
|
||||||
card_type: CardType,
|
pub card_type: CardType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// An art asset associated with a given card.
|
/// An art asset associated with a given card.
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
#[serde(rename_all="camelCase")]
|
#[serde(rename_all="camelCase")]
|
||||||
pub struct Asset {
|
pub struct Asset {
|
||||||
/// URL to the card art as it is displayed in-game.
|
/// URL to the card art as it is displayed in-game.
|
||||||
game_absolute_path: String,
|
pub game_absolute_path: String,
|
||||||
/// URL to the full-size card art as it is displayed when the card is inspected.
|
/// URL to the full-size card art as it is displayed when the card is inspected.
|
||||||
full_absolute_path: String,
|
pub full_absolute_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Possible card types.
|
/// Possible card types.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub enum CardType {
|
pub enum CardType {
|
||||||
/// A spell.
|
/// A spell.
|
||||||
Spell,
|
Spell,
|
||||||
|
@ -99,12 +99,14 @@ pub enum CardType {
|
||||||
Ability,
|
Ability,
|
||||||
/// A landmark.
|
/// A landmark.
|
||||||
Landmark,
|
Landmark,
|
||||||
|
/// A trap or boon.
|
||||||
|
Trap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Possible card supertypes.
|
/// Possible card supertypes.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub enum CardSupertype {
|
pub enum CardSupertype {
|
||||||
#[serde(alias = "")]
|
#[serde(alias = "")]
|
||||||
None,
|
None,
|
||||||
|
@ -115,7 +117,7 @@ pub enum CardSupertype {
|
||||||
|
|
||||||
/// Possible card rarities.
|
/// Possible card rarities.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub enum CardRarity {
|
pub enum CardRarity {
|
||||||
#[serde(alias = "NONE")]
|
#[serde(alias = "NONE")]
|
||||||
None,
|
None,
|
||||||
|
@ -132,7 +134,7 @@ pub enum CardRarity {
|
||||||
|
|
||||||
/// Possible spell speeds.
|
/// Possible spell speeds.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub enum SpellSpeed {
|
pub enum SpellSpeed {
|
||||||
/// Non-spell cards have this speed.
|
/// Non-spell cards have this speed.
|
||||||
#[serde(alias = "")]
|
#[serde(alias = "")]
|
||||||
|
@ -148,7 +150,7 @@ pub enum SpellSpeed {
|
||||||
|
|
||||||
/// Release sets [Card]s may belong to.
|
/// Release sets [Card]s may belong to.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub enum CardSet {
|
pub enum CardSet {
|
||||||
#[serde(rename = "Set1")]
|
#[serde(rename = "Set1")]
|
||||||
Foundations,
|
Foundations,
|
||||||
|
|
41
src/main.rs
41
src/main.rs
|
@ -1,27 +1,52 @@
|
||||||
|
#[macro_use] extern crate tantivy;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude::*;
|
||||||
use teloxide::types::*;
|
use teloxide::types::*;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
use tantivy::{IndexReader, ReloadPolicy};
|
||||||
|
|
||||||
mod data;
|
mod data;
|
||||||
|
mod search;
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
let data = data::load::load_files();
|
debug!("Loading Set Bundles...");
|
||||||
debug!("{:?}", data);
|
let cards = data::load::load_setbundles_infallible("./data/*/en_us/data/*-en_us.json");
|
||||||
|
|
||||||
|
debug!("Creating Tantivy index...");
|
||||||
|
let card_index = search::index::build_card_index();
|
||||||
|
|
||||||
|
debug!("Writing cards to the index...");
|
||||||
|
search::index::write_cards_to_index(&card_index, &cards);
|
||||||
|
|
||||||
|
debug!("Creating Tantivy reader...");
|
||||||
|
let card_reader = search::index::build_reader(&card_index);
|
||||||
|
|
||||||
|
let inline_query_closure = |query: InlineQuery, bot: AutoSend<Bot>| async move {
|
||||||
|
bot.answer_inline_query(&query.id, handle_query(&query, &card_reader)).await;
|
||||||
|
respond(())
|
||||||
|
};
|
||||||
|
|
||||||
info!("patched-porobot is starting...");
|
info!("patched-porobot is starting...");
|
||||||
|
|
||||||
let bot = Bot::from_env().auto_send();
|
let bot = Bot::from_env().auto_send();
|
||||||
|
|
||||||
let handler = Update::filter_inline_query().branch(dptree::endpoint(|query: InlineQuery, bot: AutoSend<Bot>| async move {
|
let handler = Update::filter_inline_query().branch(dptree::endpoint(inline_query_closure));
|
||||||
let result = InlineQueryResult::Article(InlineQueryResultArticle::new("test", "Test", InputMessageContent::Text(InputMessageContentText::new("Qui è dove metterei la mia carta, se solo ne avessi una!"))));
|
|
||||||
|
|
||||||
let response = bot.answer_inline_query(&query.id, vec![result]).await;
|
|
||||||
respond(())
|
|
||||||
}));
|
|
||||||
|
|
||||||
Dispatcher::builder(bot, handler).enable_ctrlc_handler().build().dispatch().await;
|
Dispatcher::builder(bot, handler).enable_ctrlc_handler().build().dispatch().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Handle a [InlineQuery] incoming from Telegram.
|
||||||
|
fn handle_query(query: &InlineQuery, reader: &IndexReader) -> Vec<InlineQueryResult> {
|
||||||
|
debug!("Creating Tantivy searcher...");
|
||||||
|
let card_searcher = reader.searcher();
|
||||||
|
|
||||||
|
let result = InlineQueryResult::Article(InlineQueryResultArticle::new("test", "Test", InputMessageContent::Text(InputMessageContentText::new("Qui è dove metterei la mia carta, se solo ne avessi una!"))));
|
||||||
|
|
||||||
|
vec![result]
|
||||||
|
}
|
41
src/search/index.rs
Normal file
41
src/search/index.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use tantivy::*;
|
||||||
|
use crate::data::schema::Card;
|
||||||
|
use crate::search::schema;
|
||||||
|
|
||||||
|
|
||||||
|
/// Build a [tantivy] [Index] storing [Card]s as documents.
|
||||||
|
pub fn build_card_index() -> Index {
|
||||||
|
Index::create_in_ram(schema::build_card_schema())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Build a [tantivy] [IndexWriter] from the given [Index] with some preset parameters.
|
||||||
|
///
|
||||||
|
/// Currently allocates 4 MB.
|
||||||
|
pub fn build_writer(index: &Index) -> IndexWriter {
|
||||||
|
index.writer(4_000_000)
|
||||||
|
.expect("to be able to allocate a tantivy writer")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Write a [Vec] of [Card]s to a [tantivy] [Index], using a writer built with [build_writer].
|
||||||
|
pub fn write_cards_to_index(index: &Index, cards: &Vec<Card>) -> () {
|
||||||
|
let schema = index.schema();
|
||||||
|
let mut writer = build_writer(index);
|
||||||
|
|
||||||
|
for card in cards {
|
||||||
|
let document = schema::card_to_document(&schema, card.to_owned());
|
||||||
|
writer.add_document(document)
|
||||||
|
.expect("to be able to add a document to the index");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.commit()
|
||||||
|
.expect("to be able to commit the schema changes");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Build a [tantivy] [IndexReader] from the given [Index] with some preset parameters.
|
||||||
|
pub fn build_reader(index: &Index) -> IndexReader {
|
||||||
|
index.reader_builder().reload_policy(ReloadPolicy::OnCommit).try_into()
|
||||||
|
.expect("to be able to allocate a tantivy reader")
|
||||||
|
}
|
2
src/search/mod.rs
Normal file
2
src/search/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod schema;
|
||||||
|
pub mod index;
|
28
src/search/schema.rs
Normal file
28
src/search/schema.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use tantivy::schema::*;
|
||||||
|
use crate::data::schema::Card;
|
||||||
|
|
||||||
|
|
||||||
|
/// Build a [tantivy] [Schema] storing [Card]s as documents.
|
||||||
|
///
|
||||||
|
/// TODO: Allow search on all fields.
|
||||||
|
pub fn build_card_schema() -> Schema {
|
||||||
|
let mut schema_builder = Schema::builder();
|
||||||
|
schema_builder.add_text_field("name", TEXT);
|
||||||
|
schema_builder.add_text_field("description", TEXT);
|
||||||
|
schema_builder.add_text_field("code", STRING | STORED);
|
||||||
|
schema_builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Convert a [Card] to a Tantivy [Document], using the specified [Schema] (which should come from [build_card_schema]).
|
||||||
|
pub fn card_to_document(schema: &Schema, card: Card) -> Document {
|
||||||
|
let name = schema.get_field("name").unwrap();
|
||||||
|
let description = schema.get_field("description").unwrap();
|
||||||
|
let code = schema.get_field("code").unwrap();
|
||||||
|
|
||||||
|
doc!(
|
||||||
|
name => card.name,
|
||||||
|
description => card.description_raw,
|
||||||
|
code => card.card_code,
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in a new issue