mirror of
https://github.com/Steffo99/patched-porobot.git
synced 2024-12-23 10:04:21 +00:00
Merge branch 'main' into reqeast
This commit is contained in:
commit
325376de55
10 changed files with 92 additions and 35 deletions
30
.github/workflows/tests.yml
vendored
Normal file
30
.github/workflows/tests.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
name: "Continuous integration"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cargotest:
|
||||||
|
steps:
|
||||||
|
- name: "Checkout repository"
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: "Install Rust toolchain"
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- name: "Run cargo clippy"
|
||||||
|
uses: actions-rs/clippy-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --all-features
|
||||||
|
|
||||||
|
- name: "Run cargo test"
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1039,7 +1039,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "patched_porobot"
|
name = "patched_porobot"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"glob",
|
"glob",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "patched_porobot"
|
name = "patched_porobot"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
authors = ["Stefano Pigozzi <me@steffo.eu>"]
|
authors = ["Stefano Pigozzi <me@steffo.eu>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Legends of Runeterra card database utilities and bots"
|
description = "Legends of Runeterra card database utilities and bots"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM rust:1.62 AS labels
|
FROM rust:1.64 AS labels
|
||||||
|
|
||||||
LABEL org.opencontainers.image.title="Patched Porobot"
|
LABEL org.opencontainers.image.title="Patched Porobot"
|
||||||
LABEL org.opencontainers.image.description="Legends of Runeterra card database utilities and bots"
|
LABEL org.opencontainers.image.description="Legends of Runeterra card database utilities and bots"
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
//!
|
//!
|
||||||
//! Since [@patchedporobot] uses [`tantivy`] internally, you might find more information on even more advanced queries in the [documentation of their `QueryParser`](tantivy::query::QueryParser)!
|
//! Since [@patchedporobot] uses [`tantivy`] internally, you might find more information on even more advanced queries in the [documentation of their `QueryParser`](tantivy::query::QueryParser)!
|
||||||
//!
|
//!
|
||||||
//! ### Deck queries
|
//! ### Deck parsing
|
||||||
//!
|
//!
|
||||||
//! You can have [@patchedporobot] display a deck and its cards by pasting the deck code after the bot's username:
|
//! You can have [@patchedporobot] display a deck and its cards by pasting the deck code after the bot's username:
|
||||||
//!
|
//!
|
||||||
|
@ -82,6 +82,15 @@
|
||||||
//!
|
//!
|
||||||
//! Then, select the "Deck with N cards" option to send the deck's card list in the chat!
|
//! Then, select the "Deck with N cards" option to send the deck's card list in the chat!
|
||||||
//!
|
//!
|
||||||
|
//! #### Named decks
|
||||||
|
//!
|
||||||
|
//! Optionally, you may add a name to your deck, which will be displayed above the deck code:
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! @patchedporobot CIBQCAICAQAQGBQIBEBAMBAJBMGBUHJNGE4AEAIBAIYQEAQGEU2QCAIBAIUQ Gimbo's Depths
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! If entered correctly, the bot will display a slightly different option containing the deck's name (_Deck "NAME" with N cards_), which you can check before the message is sent to the chat.
|
||||||
//!
|
//!
|
||||||
//! [@patchedporobot]: https://t.me/patchedporobot
|
//! [@patchedporobot]: https://t.me/patchedporobot
|
||||||
|
|
||||||
|
|
|
@ -198,9 +198,7 @@ impl Deck {
|
||||||
let card_count = reader.read_u32_varint().map_err(DeckDecodingError::Read)?;
|
let card_count = reader.read_u32_varint().map_err(DeckDecodingError::Read)?;
|
||||||
|
|
||||||
let set = reader.read_u32_varint().map_err(DeckDecodingError::Read)?;
|
let set = reader.read_u32_varint().map_err(DeckDecodingError::Read)?;
|
||||||
let set = CardSet::from(set)
|
let set = format!("{:02}", &set);
|
||||||
.to_code()
|
|
||||||
.ok_or(DeckDecodingError::UnknownSet)?;
|
|
||||||
|
|
||||||
let region = reader.read_u32_varint().map_err(DeckDecodingError::Read)?;
|
let region = reader.read_u32_varint().map_err(DeckDecodingError::Read)?;
|
||||||
let region = CardRegion::from(region)
|
let region = CardRegion::from(region)
|
||||||
|
@ -229,8 +227,7 @@ impl Deck {
|
||||||
.write_u32_varint(len)
|
.write_u32_varint(len)
|
||||||
.map_err(DeckEncodingError::Write)?;
|
.map_err(DeckEncodingError::Write)?;
|
||||||
|
|
||||||
let set: u32 = CardSet::from_code(set)
|
let set: u32 = set.parse()
|
||||||
.try_into()
|
|
||||||
.map_err(|_| DeckEncodingError::UnknownSet)?;
|
.map_err(|_| DeckEncodingError::UnknownSet)?;
|
||||||
writer
|
writer
|
||||||
.write_u32_varint(set)
|
.write_u32_varint(set)
|
||||||
|
@ -484,9 +481,9 @@ pub type DeckEncodingResult<T> = Result<T, DeckEncodingError>;
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! deck {
|
macro_rules! deck {
|
||||||
[$($cd:literal: $qty:literal),* $(,)?] => {
|
[$($cd:literal: $qty:literal),* $(,)?] => {
|
||||||
crate::data::deckcode::deck::Deck {
|
$crate::data::deckcode::deck::Deck {
|
||||||
contents: std::collections::HashMap::from([
|
contents: std::collections::HashMap::from([
|
||||||
$((crate::data::setbundle::code::CardCode { full: $cd.to_string() }, $qty),)*
|
$(($crate::data::setbundle::code::CardCode { full: $cd.to_string() }, $qty),)*
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,9 @@ impl CardSet {
|
||||||
hm.get(self)
|
hm.get(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`CardSet`] from its short code, **assuming it is not an [`CardSet::Events`] card**.
|
/// Get the [`CardSet`] from its short code.
|
||||||
|
///
|
||||||
|
/// [`CardSet::Worldwalker`] and [`CardSet::TheDarkinSaga`] share the same code `06`, so a variant cannot be determined.
|
||||||
///
|
///
|
||||||
/// [`CardSet::Events`] cards have the short code of the set they were released in, so it is impossible to determine if a card belongs to that set from its short code.
|
/// [`CardSet::Events`] cards have the short code of the set they were released in, so it is impossible to determine if a card belongs to that set from its short code.
|
||||||
pub fn from_code(value: &str) -> Self {
|
pub fn from_code(value: &str) -> Self {
|
||||||
|
@ -65,7 +67,6 @@ impl CardSet {
|
||||||
"03" => Self::CallOfTheMountain,
|
"03" => Self::CallOfTheMountain,
|
||||||
"04" => Self::EmpiresOfTheAscended,
|
"04" => Self::EmpiresOfTheAscended,
|
||||||
"05" => Self::BeyondTheBandlewood,
|
"05" => Self::BeyondTheBandlewood,
|
||||||
"06" => Self::Worldwalker,
|
|
||||||
|
|
||||||
_ => Self::Unsupported,
|
_ => Self::Unsupported,
|
||||||
}
|
}
|
||||||
|
@ -84,6 +85,7 @@ impl CardSet {
|
||||||
Self::EmpiresOfTheAscended => Some("04".to_string()),
|
Self::EmpiresOfTheAscended => Some("04".to_string()),
|
||||||
Self::BeyondTheBandlewood => Some("05".to_string()),
|
Self::BeyondTheBandlewood => Some("05".to_string()),
|
||||||
Self::Worldwalker => Some("06".to_string()),
|
Self::Worldwalker => Some("06".to_string()),
|
||||||
|
Self::TheDarkinSaga => Some("06".to_string()),
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -92,6 +94,8 @@ impl CardSet {
|
||||||
|
|
||||||
/// Get the [`CardSet`] from its internal id.
|
/// Get the [`CardSet`] from its internal id.
|
||||||
///
|
///
|
||||||
|
/// [`CardSet::Worldwalker`] and [`CardSet::TheDarkinSaga`] share the same id, so a variant cannot be determined.
|
||||||
|
///
|
||||||
/// [`CardSet::Events`] cards have the id of the set they were released in, so it is impossible to determine if a card belongs to that set from its id.
|
/// [`CardSet::Events`] cards have the id of the set they were released in, so it is impossible to determine if a card belongs to that set from its id.
|
||||||
impl From<u32> for CardSet {
|
impl From<u32> for CardSet {
|
||||||
fn from(value: u32) -> Self {
|
fn from(value: u32) -> Self {
|
||||||
|
@ -101,7 +105,6 @@ impl From<u32> for CardSet {
|
||||||
3 => CardSet::CallOfTheMountain,
|
3 => CardSet::CallOfTheMountain,
|
||||||
4 => CardSet::EmpiresOfTheAscended,
|
4 => CardSet::EmpiresOfTheAscended,
|
||||||
5 => CardSet::BeyondTheBandlewood,
|
5 => CardSet::BeyondTheBandlewood,
|
||||||
6 => CardSet::Worldwalker,
|
|
||||||
_ => CardSet::Unsupported,
|
_ => CardSet::Unsupported,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,6 +124,7 @@ impl TryFrom<CardSet> for u32 {
|
||||||
CardSet::EmpiresOfTheAscended => Ok(4),
|
CardSet::EmpiresOfTheAscended => Ok(4),
|
||||||
CardSet::BeyondTheBandlewood => Ok(5),
|
CardSet::BeyondTheBandlewood => Ok(5),
|
||||||
CardSet::Worldwalker => Ok(6),
|
CardSet::Worldwalker => Ok(6),
|
||||||
|
CardSet::TheDarkinSaga => Ok(6),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,10 +153,10 @@ fn display_levelup(levelup: &String) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a [Deck] in [Telegram Bot HTML].
|
/// Render a [Deck] in [Telegram Bot HTML], with an optional `name`.
|
||||||
///
|
///
|
||||||
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
/// [Telegram Bot HTML]: https://core.telegram.org/bots/api#html-style
|
||||||
pub fn display_deck(index: &CardIndex, deck: &Deck, code: String) -> String {
|
pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&str>) -> String {
|
||||||
// TODO: optimize this
|
// TODO: optimize this
|
||||||
let cards = deck
|
let cards = deck
|
||||||
.contents
|
.contents
|
||||||
|
@ -182,5 +182,8 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: String) -> String {
|
||||||
})
|
})
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
format!("<code>{}</code>\n\n{}", &code, &cards)
|
match name {
|
||||||
|
Some(name) => format!("<b><u>{}</u></b>\n<code>{}</code>\n\n{}", &name, &code, &cards),
|
||||||
|
None => format!("<code>{}</code>\n\n{}", &code, &cards),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ use teloxide::payloads::{AnswerInlineQuery, SendMessage};
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude::*;
|
||||||
use teloxide::requests::{JsonRequest, ResponseResult};
|
use teloxide::requests::{JsonRequest, ResponseResult};
|
||||||
use teloxide::types::{ParseMode, Recipient};
|
use teloxide::types::{ParseMode, Recipient};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
/// Handle inline queries by searching cards on the [CardSearchEngine].
|
/// Handle inline queries by searching cards on the [CardSearchEngine].
|
||||||
pub fn inline_query_handler(
|
pub fn inline_query_handler(
|
||||||
|
@ -33,17 +35,28 @@ pub fn inline_query_handler(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(deck) = Deck::from_code(&query.query.to_ascii_uppercase()) {
|
lazy_static! {
|
||||||
debug!("Parsed deck successfully!");
|
static ref DECK_RE: Regex = Regex::new(r#"^(?P<code>[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]+)(?:\s+(?P<name>.+?))?\s*$"#).unwrap();
|
||||||
break AnswerInlineQuery {
|
}
|
||||||
inline_query_id: query.id.clone(),
|
|
||||||
results: vec![deck_to_inlinequeryresult(&engine.cards, &deck)],
|
if let Some(deck_captures) = DECK_RE.captures(&query.query) {
|
||||||
cache_time: None,
|
if let Some(deck_code) = deck_captures.name("code") {
|
||||||
is_personal: Some(false),
|
if let Ok(deck) = Deck::from_code(&deck_code.as_str()) {
|
||||||
next_offset: None,
|
|
||||||
switch_pm_text: None,
|
debug!("Parsed deck successfully!");
|
||||||
switch_pm_parameter: None,
|
let name = deck_captures.name("name").map(|m| m.as_str());
|
||||||
};
|
|
||||||
|
break AnswerInlineQuery {
|
||||||
|
inline_query_id: query.id.clone(),
|
||||||
|
results: vec![deck_to_inlinequeryresult(&engine.cards, &deck, &name)],
|
||||||
|
cache_time: None,
|
||||||
|
is_personal: Some(false),
|
||||||
|
next_offset: None,
|
||||||
|
switch_pm_text: None,
|
||||||
|
switch_pm_parameter: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Querying the card search engine...");
|
debug!("Querying the card search engine...");
|
||||||
|
|
|
@ -7,15 +7,12 @@ use crate::data::deckcode::deck::Deck;
|
||||||
use crate::data::deckcode::format::DeckCodeFormat;
|
use crate::data::deckcode::format::DeckCodeFormat;
|
||||||
use crate::data::setbundle::card::{Card, CardIndex};
|
use crate::data::setbundle::card::{Card, CardIndex};
|
||||||
use crate::telegram::display::{display_card, display_deck};
|
use crate::telegram::display::{display_card, display_deck};
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::ptr::hash;
|
|
||||||
use teloxide::types::{
|
use teloxide::types::{
|
||||||
InlineQueryResult, InlineQueryResultArticle, InlineQueryResultPhoto, InputMessageContent,
|
InlineQueryResult, InlineQueryResultArticle, InlineQueryResultPhoto, InputMessageContent,
|
||||||
InputMessageContentText, ParseMode,
|
InputMessageContentText, ParseMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Converts a [Card] into a [InlineQueryResult].
|
/// Convert a [Card] into a [InlineQueryResult].
|
||||||
pub fn card_to_inlinequeryresult(
|
pub fn card_to_inlinequeryresult(
|
||||||
globals: &LocalizedGlobalsIndexes,
|
globals: &LocalizedGlobalsIndexes,
|
||||||
card: &Card,
|
card: &Card,
|
||||||
|
@ -46,16 +43,20 @@ pub fn card_to_inlinequeryresult(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deck_to_inlinequeryresult(index: &CardIndex, deck: &Deck) -> InlineQueryResult {
|
/// Convert a [Deck] with an optional name into a [InlineQueryResult].
|
||||||
|
pub fn deck_to_inlinequeryresult(index: &CardIndex, deck: &Deck, name: &Option<&str>) -> InlineQueryResult {
|
||||||
let code = deck
|
let code = deck
|
||||||
.to_code(DeckCodeFormat::F1)
|
.to_code(DeckCodeFormat::F1)
|
||||||
.expect("serialized deck to deserialize properly");
|
.expect("serialized deck to deserialize properly");
|
||||||
|
|
||||||
InlineQueryResult::Article(InlineQueryResultArticle {
|
InlineQueryResult::Article(InlineQueryResultArticle {
|
||||||
id: format!("{:x}", md5::compute(&code)),
|
id: format!("{:x}", md5::compute(&code)),
|
||||||
title: format!("Deck with {} cards", deck.contents.len()),
|
title: match &name {
|
||||||
|
Some(name) => format!(r#"Deck "{}" with {} cards"#, name, deck.contents.len()),
|
||||||
|
None => format!("Deck with {} cards", deck.contents.len())
|
||||||
|
},
|
||||||
input_message_content: InputMessageContent::Text(InputMessageContentText {
|
input_message_content: InputMessageContent::Text(InputMessageContentText {
|
||||||
message_text: display_deck(index, deck, code),
|
message_text: display_deck(index, deck, &code, &name),
|
||||||
parse_mode: Some(ParseMode::Html),
|
parse_mode: Some(ParseMode::Html),
|
||||||
entities: None,
|
entities: None,
|
||||||
disable_web_page_preview: Some(true),
|
disable_web_page_preview: Some(true),
|
||||||
|
|
Loading…
Reference in a new issue