diff options
| author | salmonik <raisersoft@proton.me> | 2026-07-08 02:13:35 +0200 |
|---|---|---|
| committer | salmonik <raisersoft@proton.me> | 2026-07-08 02:13:35 +0200 |
| commit | cd8ed8bc5793985df2c1692072a932ceee517b95 (patch) | |
| tree | 42d18a4140259b5c68b4dd05b7fcb398f78b8d7f /src/commands/qr.rs | |
| parent | Merge pull request 'Voice related fixes, alongside simplifying assistant' (#5... (diff) | |
| download | senator-sperma-cd8ed8bc5793985df2c1692072a932ceee517b95.tar.gz senator-sperma-cd8ed8bc5793985df2c1692072a932ceee517b95.zip | |
OPEN THE RUST GATES
Signed-off-by: salmonik <raisersoft@proton.me>
Diffstat (limited to 'src/commands/qr.rs')
| -rw-r--r-- | src/commands/qr.rs | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/src/commands/qr.rs b/src/commands/qr.rs new file mode 100644 index 0000000..26232ee --- /dev/null +++ b/src/commands/qr.rs | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | use poise::serenity_prelude as serenity; | ||
| 2 | |||
| 3 | use crate::Data; | ||
| 4 | use crate::error::{Error, Result}; | ||
| 5 | use crate::lang; | ||
| 6 | |||
| 7 | type Context<'a> = poise::Context<'a, Data, Error>; | ||
| 8 | |||
| 9 | /// QR group | ||
| 10 | #[poise::command(slash_command, subcommands("dekodowanie", "generowanie",))] | ||
| 11 | pub async fn qr(_ctx: Context<'_>) -> Result<()> { | ||
| 12 | Ok(()) | ||
| 13 | } | ||
| 14 | |||
| 15 | /// Generate a QR code from text | ||
| 16 | #[poise::command(slash_command)] | ||
| 17 | pub async fn generowanie( | ||
| 18 | ctx: Context<'_>, | ||
| 19 | #[description = "Zawartość kodu QR"] text: String, | ||
| 20 | ) -> Result<()> { | ||
| 21 | ctx.defer().await?; | ||
| 22 | |||
| 23 | let code = qrcode::QrCode::new(text.as_bytes())?; | ||
| 24 | let image = code.render::<image::Luma<u8>>().build(); | ||
| 25 | |||
| 26 | let scaled = image::imageops::resize( | ||
| 27 | &image, | ||
| 28 | image.width() * 10, | ||
| 29 | image.height() * 10, | ||
| 30 | image::imageops::Nearest, | ||
| 31 | ); | ||
| 32 | |||
| 33 | let mut buf = std::io::Cursor::new(Vec::new()); | ||
| 34 | scaled.write_to(&mut buf, image::ImageFormat::Png)?; | ||
| 35 | |||
| 36 | ctx.send( | ||
| 37 | poise::CreateReply::default() | ||
| 38 | .content(lang::L_QR_GENERATED) | ||
| 39 | .attachment(serenity::CreateAttachment::bytes( | ||
| 40 | buf.into_inner(), | ||
| 41 | "qrcode.png", | ||
| 42 | )), | ||
| 43 | ) | ||
| 44 | .await?; | ||
| 45 | Ok(()) | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Decode a QR code from an uploaded image | ||
| 49 | #[poise::command(slash_command)] | ||
| 50 | pub async fn dekodowanie( | ||
| 51 | ctx: Context<'_>, | ||
| 52 | #[description = "Obraz zawierający kod QR"] image: serenity::Attachment, | ||
| 53 | ) -> Result<()> { | ||
| 54 | ctx.defer().await?; | ||
| 55 | |||
| 56 | let img_data = ctx | ||
| 57 | .data() | ||
| 58 | .http_client | ||
| 59 | .get(&image.url) | ||
| 60 | .send() | ||
| 61 | .await? | ||
| 62 | .bytes() | ||
| 63 | .await?; | ||
| 64 | let img = image::load_from_memory(&img_data)?; | ||
| 65 | let gray = img.to_luma8(); | ||
| 66 | |||
| 67 | let decoded = decode_qr_simple(&gray); | ||
| 68 | match decoded { | ||
| 69 | Some(text) => { | ||
| 70 | ctx.say(lang::fmt_qr_decoded(&text)).await?; | ||
| 71 | } | ||
| 72 | None => { | ||
| 73 | ctx.say(lang::L_QR_DECODE_FAILED).await?; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | Ok(()) | ||
| 77 | } | ||
| 78 | |||
| 79 | fn decode_qr_simple(img: &image::ImageBuffer<image::Luma<u8>, Vec<u8>>) -> Option<String> { | ||
| 80 | rxing::helpers::detect_in_luma(img.as_raw().to_vec(), img.width(), img.height(), None) | ||
| 81 | .ok() | ||
| 82 | .map(|r| r.getText().to_owned()) | ||
| 83 | } | ||
| 84 | |||
| 85 | pub fn commands() -> Vec<poise::Command<Data, Error>> { | ||
| 86 | vec![qr()] | ||
| 87 | } | ||
