Skip to content

Commit ae1b459

Browse files
authored
Add basic scryfall plugin (#78)
* Add basic scryfall plugin * Update rust to 1.88 * nix flake update (and more Rust 1.88.0)
1 parent e2f59e8 commit ae1b459

8 files changed

Lines changed: 634 additions & 67 deletions

File tree

Cargo.lock

Lines changed: 513 additions & 53 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ lazy_static = "1.4"
2424
rand = "0.8"
2525
regex = "1.10"
2626
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "gzip", "json", "stream"] }
27+
scryfall = "0.21"
2728
serde = { version = "1.0", features = ["derive"] }
2829
sqlx = { version = "0.8", features = ["runtime-tokio", "macros", "migrate", "postgres"] }
2930
time = { version = "0.3", features = ["formatting"] }

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM rust:1.81-bullseye as builder
1+
FROM rust:1.88-bullseye AS builder
22
WORKDIR /usr/src/app
33

44
# Workaround to allow arm64 builds to work properly

flake.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
{
1818
devShells.default = pkgs.mkShell {
1919
nativeBuildInputs = [
20-
(pkgs.rust-bin.stable."1.81.0".default.override {
20+
(pkgs.rust-bin.stable."1.88.0".default.override {
2121
extensions = ["rust-src"];
2222
})
2323
pkgs.rust-analyzer

src/plugin.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub async fn load(bot: Arc<Client>) -> Result<Vec<PluginMetadata>> {
5353
"net_tools",
5454
"noaa",
5555
"riddle",
56+
"scryfall",
5657
"introspection",
5758
"help",
5859
];
@@ -140,6 +141,10 @@ pub async fn load(bot: Arc<Client>) -> Result<Vec<PluginMetadata>> {
140141
ret.push(start_plugin::<plugins::RiddlePlugin>(&bot)?);
141142
}
142143

144+
if config.plugin_enabled("scryfall") {
145+
ret.push(start_plugin::<plugins::ScryfallPlugin>(&bot)?);
146+
}
147+
143148
if config.plugin_enabled("introspection") {
144149
ret.push(start_plugin::<plugins::IntrospectionPlugin>(&bot)?);
145150
}

src/plugins/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ pub use self::noaa::NoaaPlugin;
2525
mod riddle;
2626
pub use self::riddle::RiddlePlugin;
2727

28+
mod scryfall;
29+
pub use self::scryfall::ScryfallPlugin;
30+
2831
mod introspection;
2932
pub use self::introspection::IntrospectionPlugin;
3033

src/plugins/scryfall.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use regex::Regex;
2+
use scryfall::Card;
3+
4+
use crate::prelude::*;
5+
6+
pub struct ScryfallPlugin {
7+
re: Regex,
8+
}
9+
10+
impl ScryfallPlugin {
11+
pub fn new() -> Self {
12+
ScryfallPlugin {
13+
re: Regex::new(r#"\[\[(.+?)\]\]"#).unwrap(),
14+
}
15+
}
16+
}
17+
18+
impl ScryfallPlugin {
19+
async fn handle_scryfall(&self, ctx: &Arc<Context>, arg: &str) -> Result<()> {
20+
let card = Card::named_fuzzy(arg).await?;
21+
22+
let card_uri = card.scryfall_uri;
23+
let image_uri = card.image_uris.and_then(|uris| uris.png);
24+
25+
match image_uri {
26+
Some(image_uri) => {
27+
ctx.mention_reply(&format!("{} ({})", card_uri, image_uri.as_str()))
28+
.await?
29+
}
30+
None => ctx.mention_reply(&format!("{}", card_uri)).await?,
31+
}
32+
33+
Ok(())
34+
}
35+
36+
async fn handle_privmsg(&self, ctx: &Arc<Context>, msg: &str) -> Result<()> {
37+
let captures: Vec<_> = self.re.captures_iter(msg).collect();
38+
39+
if captures.is_empty() {
40+
return Ok(());
41+
}
42+
43+
let mut change_errors = Vec::new();
44+
45+
// Loop through all captures, adding them to the output.
46+
for capture in captures {
47+
match self.handle_scryfall(ctx, &capture[1]).await {
48+
Ok(_) => {}
49+
Err(e) => {
50+
change_errors.push(format!("failed to look up \"{}\": {}", &capture[1], e));
51+
}
52+
};
53+
}
54+
55+
if !change_errors.is_empty() {
56+
ctx.mention_reply(&change_errors.join(", ")).await?;
57+
}
58+
59+
Ok(())
60+
}
61+
}
62+
63+
#[async_trait]
64+
impl Plugin for ScryfallPlugin {
65+
fn new_from_env() -> Result<Self> {
66+
Ok(ScryfallPlugin::new())
67+
}
68+
69+
fn command_metadata(&self) -> Vec<CommandMetadata> {
70+
vec![CommandMetadata {
71+
name: "scryfall".to_string(),
72+
short_help: "usage: scryfall [card name]. gives a link to a card on Scryfall."
73+
.to_string(),
74+
full_help: "gives a link to a given card on Scryfall if it exists".to_string(),
75+
}]
76+
}
77+
78+
async fn run(self, bot: Arc<Client>) -> Result<()> {
79+
let mut stream = bot.subscribe();
80+
81+
while let Ok(ctx) = stream.recv().await {
82+
let res = match ctx.as_event() {
83+
Ok(Event::Command("scryfall", possible_arg)) => {
84+
match possible_arg.or_else(|| ctx.sender()) {
85+
Some(nick) => self.handle_scryfall(&ctx, nick).await,
86+
None => Err(format_err!("no card name found")),
87+
}
88+
}
89+
Ok(Event::Message(_, msg)) => self.handle_privmsg(&ctx, msg).await,
90+
_ => Ok(()),
91+
};
92+
93+
crate::check_err(&ctx, res).await;
94+
}
95+
96+
Err(format_err!("karma plugin lagged"))
97+
}
98+
}

0 commit comments

Comments
 (0)