1
Fork 0
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:
Steffo 2022-07-31 20:32:58 +02:00
parent e17c2b446f
commit 6810e1acc7
Signed by: steffo
GPG key ID: 6965406171929D01
6 changed files with 147 additions and 52 deletions

View file

@ -1,10 +1,7 @@
//! 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 crate::data::schema::Card;
use log::*;
#[derive(Debug)]
@ -15,7 +12,7 @@ enum LoadingError {
/// 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)
.map_err(LoadingError::IO)?;
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.
fn load_setbundle_infallible(path: PathBuf) -> Vec<Card> {
fn load_setbundle_infallible(path: std::path::PathBuf) -> Vec<Card> {
match load_setbundle(path) {
Ok(v) => v,
Err(e) => {
warn!("{:?}", e);
log::warn!("{:?}", e);
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.
pub fn load_setbundles_infallible(pattern: &str) -> Vec<Card> {
glob(pattern)
glob::glob(pattern)
.expect("a valid glob")
.filter_map(GlobResult::ok)
.filter_map(glob::GlobResult::ok)
.map(load_setbundle_infallible)
.concat()
}

View file

@ -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).
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[serde(rename_all="camelCase")]
pub struct Card {
/// Localized names of the cards associated with this one.
/// 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.
associated_card_refs: Vec<String>,
pub associated_card_refs: Vec<String>,
/// Art assets of this card.
assets: Vec<Asset>,
pub assets: Vec<Asset>,
/// Localized names of the regions this card belongs to.
regions: Vec<String>,
pub regions: Vec<String>,
/// IDs of the regions this card belongs to.
region_refs: Vec<String>,
pub region_refs: Vec<String>,
/// Base attack of the card.
attack: i8,
pub attack: i8,
/// Base cost of the card.
cost: i8,
pub cost: i8,
/// Base health of the card.
health: i8,
pub health: i8,
/// Localized description of the card, in XML.
description: String,
pub description: String,
/// Localized description of the card, in plain text.
description_raw: String,
pub description_raw: String,
/// 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.
levelup_description_raw: String,
pub levelup_description_raw: String,
/// 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.
artist_name: String,
pub artist_name: String,
/// Localized name of the card.
name: String,
pub name: String,
/// Unique seven-character identifier of the card.
card_code: String,
pub card_code: String,
/// 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.
keyword_refs: Vec<String>,
pub keyword_refs: Vec<String>,
/// Localized spell speed.
spell_speed: String,
pub spell_speed: String,
/// [SpellSpeed] of the card.
spell_speed_ref: SpellSpeed,
pub spell_speed_ref: SpellSpeed,
/// Localized rarity of the card.
rarity: String,
pub rarity: String,
/// [CardRarity] of the card.
rarity_ref: CardRarity,
pub rarity_ref: CardRarity,
/// The subtypes the card has, such as `PORO`.
subtypes: Vec<String>,
pub subtypes: Vec<String>,
/// 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 `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.
set: String,
pub set: String,
#[serde(rename(serialize = "type", deserialize = "type"))]
card_type: CardType,
pub card_type: CardType,
}
/// 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")]
pub struct Asset {
/// 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.
full_absolute_path: String,
pub full_absolute_path: String,
}
/// Possible card types.
#[non_exhaustive]
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum CardType {
/// A spell.
Spell,
@ -99,12 +99,14 @@ pub enum CardType {
Ability,
/// A landmark.
Landmark,
/// A trap or boon.
Trap,
}
/// Possible card supertypes.
#[non_exhaustive]
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum CardSupertype {
#[serde(alias = "")]
None,
@ -115,7 +117,7 @@ pub enum CardSupertype {
/// Possible card rarities.
#[non_exhaustive]
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum CardRarity {
#[serde(alias = "NONE")]
None,
@ -132,7 +134,7 @@ pub enum CardRarity {
/// Possible spell speeds.
#[non_exhaustive]
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum SpellSpeed {
/// Non-spell cards have this speed.
#[serde(alias = "")]
@ -148,7 +150,7 @@ pub enum SpellSpeed {
/// Release sets [Card]s may belong to.
#[non_exhaustive]
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum CardSet {
#[serde(rename = "Set1")]
Foundations,

View file

@ -1,27 +1,52 @@
#[macro_use] extern crate tantivy;
#[macro_use] extern crate log;
use teloxide::prelude::*;
use teloxide::types::*;
use log::*;
use tantivy::{IndexReader, ReloadPolicy};
mod data;
mod search;
#[tokio::main]
async fn main() {
pretty_env_logger::init();
let data = data::load::load_files();
debug!("{:?}", data);
debug!("Loading Set Bundles...");
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...");
let bot = Bot::from_env().auto_send();
let handler = Update::filter_inline_query().branch(dptree::endpoint(|query: InlineQuery, bot: AutoSend<Bot>| async move {
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(())
}));
let handler = Update::filter_inline_query().branch(dptree::endpoint(inline_query_closure));
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
View 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
View file

@ -0,0 +1,2 @@
pub mod schema;
pub mod index;

28
src/search/schema.rs Normal file
View 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,
)
}