From 369e97048a56bda56e47d419f71320a4ce535e1b Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 09:31:29 +0000 Subject: [PATCH 01/12] Add support for `CardSet::TheDarkinSaga.to_code` --- src/data/setbundle/set.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/setbundle/set.rs b/src/data/setbundle/set.rs index 637cfdb..0d8c102 100644 --- a/src/data/setbundle/set.rs +++ b/src/data/setbundle/set.rs @@ -84,6 +84,7 @@ impl CardSet { Self::EmpiresOfTheAscended => Some("04".to_string()), Self::BeyondTheBandlewood => Some("05".to_string()), Self::Worldwalker => Some("06".to_string()), + Self::TheDarkinSaga => Some("06".to_string()), _ => None, } From 11bd4204ddd3e56b76adb4c61c98e5556506c50f Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 09:33:26 +0000 Subject: [PATCH 02/12] Remove support for `CardSet::Worldwalker.from_code` 06 is now ambiguous --- src/data/setbundle/set.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/setbundle/set.rs b/src/data/setbundle/set.rs index 0d8c102..b8ada99 100644 --- a/src/data/setbundle/set.rs +++ b/src/data/setbundle/set.rs @@ -65,7 +65,6 @@ impl CardSet { "03" => Self::CallOfTheMountain, "04" => Self::EmpiresOfTheAscended, "05" => Self::BeyondTheBandlewood, - "06" => Self::Worldwalker, _ => Self::Unsupported, } From e5aa83af96efb0b28adc6407f81f8cc42782062f Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 09:35:33 +0000 Subject: [PATCH 03/12] Document the `CardSet::from_code` change --- src/data/setbundle/set.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/data/setbundle/set.rs b/src/data/setbundle/set.rs index b8ada99..b75f3af 100644 --- a/src/data/setbundle/set.rs +++ b/src/data/setbundle/set.rs @@ -55,8 +55,10 @@ impl CardSet { 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. pub fn from_code(value: &str) -> Self { match value { From 75279c8ba7714980e97de4b403e586bb38914261 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 09:38:55 +0000 Subject: [PATCH 04/12] Remove support for `CardSet::Worldwalker::from::` 06 is now ambiguous --- src/data/setbundle/set.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/setbundle/set.rs b/src/data/setbundle/set.rs index b75f3af..6a3ff27 100644 --- a/src/data/setbundle/set.rs +++ b/src/data/setbundle/set.rs @@ -103,7 +103,6 @@ impl From for CardSet { 3 => CardSet::CallOfTheMountain, 4 => CardSet::EmpiresOfTheAscended, 5 => CardSet::BeyondTheBandlewood, - 6 => CardSet::Worldwalker, _ => CardSet::Unsupported, } } From 8978ccd82ed976655e53122a1ea6e64b78846499 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 09:41:14 +0000 Subject: [PATCH 05/12] Allow `u32::try_from::` to work on `CardSet::TheDarkinSaga` --- src/data/setbundle/set.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/setbundle/set.rs b/src/data/setbundle/set.rs index 6a3ff27..da9d7d3 100644 --- a/src/data/setbundle/set.rs +++ b/src/data/setbundle/set.rs @@ -122,6 +122,7 @@ impl TryFrom for u32 { CardSet::EmpiresOfTheAscended => Ok(4), CardSet::BeyondTheBandlewood => Ok(5), CardSet::Worldwalker => Ok(6), + CardSet::TheDarkinSaga => Ok(6), _ => Err(()), } } From 6c63d36ca3cf4e7080fe4e4c2368c2091357c960 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 09:44:14 +0000 Subject: [PATCH 06/12] Document the `CardSet::from::` changes --- src/data/setbundle/set.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/data/setbundle/set.rs b/src/data/setbundle/set.rs index da9d7d3..5962e88 100644 --- a/src/data/setbundle/set.rs +++ b/src/data/setbundle/set.rs @@ -94,6 +94,8 @@ impl CardSet { /// 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. impl From for CardSet { fn from(value: u32) -> Self { From 961ab2cea870aac1887f895a890de4199e82fc5f Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 15:15:32 +0000 Subject: [PATCH 07/12] Allow deck names to be specified in the Telegram bot --- src/telegram/display.rs | 9 ++++++--- src/telegram/handler.rs | 35 ++++++++++++++++++++++++----------- src/telegram/inline.rs | 11 +++++++---- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/telegram/display.rs b/src/telegram/display.rs index d53170a..a301fe4 100644 --- a/src/telegram/display.rs +++ b/src/telegram/display.rs @@ -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 -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 let cards = deck .contents @@ -182,5 +182,8 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: String) -> String { }) .join("\n"); - format!("{}\n\n{}", &code, &cards) + match name { + Some(name) => format!("{}\n{}\n\n{}", &name, &code, &cards), + None => format!("{}\n\n{}", &code, &cards), + } } diff --git a/src/telegram/handler.rs b/src/telegram/handler.rs index c2fff99..7cc1f34 100644 --- a/src/telegram/handler.rs +++ b/src/telegram/handler.rs @@ -10,6 +10,8 @@ use teloxide::payloads::{AnswerInlineQuery, SendMessage}; use teloxide::prelude::*; use teloxide::requests::{JsonRequest, ResponseResult}; use teloxide::types::{ParseMode, Recipient}; +use lazy_static::lazy_static; +use regex::Regex; /// Handle inline queries by searching cards on the [CardSearchEngine]. 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()) { - debug!("Parsed deck successfully!"); - break AnswerInlineQuery { - inline_query_id: query.id.clone(), - results: vec![deck_to_inlinequeryresult(&engine.cards, &deck)], - cache_time: None, - is_personal: Some(false), - next_offset: None, - switch_pm_text: None, - switch_pm_parameter: None, - }; + lazy_static! { + static ref DECK_RE: Regex = Regex::new(r#"^(?P[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]+)(?:\s+(?P.+?))?\s*$"#).unwrap(); + } + + if let Some(deck_captures) = DECK_RE.captures(&query.query) { + if let Some(deck_code) = deck_captures.name("code") { + if let Ok(deck) = Deck::from_code(&deck_code.as_str()) { + + debug!("Parsed deck successfully!"); + 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..."); diff --git a/src/telegram/inline.rs b/src/telegram/inline.rs index 3b55fef..8eae007 100644 --- a/src/telegram/inline.rs +++ b/src/telegram/inline.rs @@ -43,17 +43,20 @@ pub fn card_to_inlinequeryresult( }) } -/// Convert a [Deck] into a [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 .to_code(DeckCodeFormat::F1) .expect("serialized deck to deserialize properly"); InlineQueryResult::Article(InlineQueryResultArticle { 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 { - message_text: display_deck(index, deck, code), + message_text: display_deck(index, deck, &code, &name), parse_mode: Some(ParseMode::Html), entities: None, disable_web_page_preview: Some(true), From cb00027ff89055a504b9cf41529926d24b8a1907 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 15:24:38 +0000 Subject: [PATCH 08/12] Document how to send a named deck list --- src/bin/patched_porobot_telegram.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/bin/patched_porobot_telegram.rs b/src/bin/patched_porobot_telegram.rs index 7abdd6a..7157406 100644 --- a/src/bin/patched_porobot_telegram.rs +++ b/src/bin/patched_porobot_telegram.rs @@ -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)! //! -//! ### Deck queries +//! ### Deck parsing //! //! 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! //! +//! #### 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 From 1dfa9d97b79e75fdf92571131f7fbd70247b2c80 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 21:11:35 +0200 Subject: [PATCH 09/12] Fix bug in `deck!` where it was trying to access the caller crate --- src/data/deckcode/deck.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/deckcode/deck.rs b/src/data/deckcode/deck.rs index 34f56aa..95ca339 100644 --- a/src/data/deckcode/deck.rs +++ b/src/data/deckcode/deck.rs @@ -484,9 +484,9 @@ pub type DeckEncodingResult = Result; #[macro_export] macro_rules! deck { [$($cd:literal: $qty:literal),* $(,)?] => { - crate::data::deckcode::deck::Deck { + $crate::data::deckcode::deck::Deck { 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),)* ]) } } From 1f6e05d419dde77453e257fc015482549de7e0bf Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 22:11:41 +0200 Subject: [PATCH 10/12] Do not try to convert "set" deck code portions to `CardSet` variants --- src/data/deckcode/deck.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/data/deckcode/deck.rs b/src/data/deckcode/deck.rs index 34f56aa..e092d28 100644 --- a/src/data/deckcode/deck.rs +++ b/src/data/deckcode/deck.rs @@ -198,9 +198,7 @@ impl Deck { let card_count = reader.read_u32_varint().map_err(DeckDecodingError::Read)?; let set = reader.read_u32_varint().map_err(DeckDecodingError::Read)?; - let set = CardSet::from(set) - .to_code() - .ok_or(DeckDecodingError::UnknownSet)?; + let set = format!("{:02}", &set); let region = reader.read_u32_varint().map_err(DeckDecodingError::Read)?; let region = CardRegion::from(region) @@ -229,8 +227,7 @@ impl Deck { .write_u32_varint(len) .map_err(DeckEncodingError::Write)?; - let set: u32 = CardSet::from_code(set) - .try_into() + let set: u32 = set.parse() .map_err(|_| DeckEncodingError::UnknownSet)?; writer .write_u32_varint(set) From d87e19d81f4df46b6725acf45c449b5a37b9a4b7 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 18 Oct 2022 23:54:51 +0200 Subject: [PATCH 11/12] Add underline to deck titles --- src/telegram/display.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/telegram/display.rs b/src/telegram/display.rs index a301fe4..03645be 100644 --- a/src/telegram/display.rs +++ b/src/telegram/display.rs @@ -183,7 +183,7 @@ pub fn display_deck(index: &CardIndex, deck: &Deck, code: &str, name: &Option<&s .join("\n"); match name { - Some(name) => format!("{}\n{}\n\n{}", &name, &code, &cards), + Some(name) => format!("{}\n{}\n\n{}", &name, &code, &cards), None => format!("{}\n\n{}", &code, &cards), } } From 0874a5dd1734cc5e1d7e3353330907be72697fae Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Thu, 20 Oct 2022 14:27:13 +0000 Subject: [PATCH 12/12] Run tests on every pull request --- .github/workflows/tests.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..be20421 --- /dev/null +++ b/.github/workflows/tests.yml @@ -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