feat: ocr & mute on rejoin (#20)

This commit is contained in:
oSumAtrIX 2022-08-27 02:23:35 +02:00 committed by GitHub
parent 8840a65c94
commit afb7e79b67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 743 additions and 125 deletions

View File

@ -19,6 +19,9 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install dependencies
run: sudo apt install libleptonica-dev libtesseract-dev libssl-dev
- name: Setup Rust toolchain - name: Setup Rust toolchain
id: rust-toolchain id: rust-toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@ -28,12 +31,6 @@ jobs:
override: true override: true
components: clippy components: clippy
- name: Setup Node.js
if: github.event_name != 'pull_request'
uses: actions/setup-node@v2
with:
node-version: "lts/*"
- name: Cache - name: Cache
uses: actions/cache@v3 uses: actions/cache@v3
id: cache id: cache
@ -47,8 +44,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --release --target=x86_64-unknown-linux-musl args: --release
use-cross: true
- name: cargo clippy - name: cargo clippy
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
@ -60,7 +56,7 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: revanced-discord-bot name: revanced-discord-bot
path: target/x86_64-unknown-linux-musl/release/revanced-discord-bot path: target/release/revanced-discord-bot
- name: Setup semantic-release - name: Setup semantic-release
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'

476
Cargo.lock generated
View File

@ -64,6 +64,17 @@ dependencies = [
"webpki-roots", "webpki-roots",
] ]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -76,6 +87,29 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bindgen"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"clap",
"env_logger",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"which",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -134,6 +168,15 @@ version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cexpr"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -155,6 +198,48 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "clang-sys"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim 0.8.0",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.2" version = "0.2.2"
@ -213,7 +298,7 @@ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim 0.10.0",
"syn", "syn",
] ]
@ -227,7 +312,7 @@ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim 0.10.0",
"syn", "syn",
] ]
@ -347,6 +432,28 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "env_logger"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.24" version = "1.0.24"
@ -363,6 +470,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.0.1" version = "1.0.1"
@ -482,6 +604,12 @@ dependencies = [
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
] ]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.13" version = "0.3.13"
@ -582,6 +710,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.20" version = "0.14.20"
@ -619,6 +753,19 @@ dependencies = [
"tokio-rustls", "tokio-rustls",
] ]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]] [[package]]
name = "ident_case" name = "ident_case"
version = "1.0.1" version = "1.0.1"
@ -646,6 +793,15 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "ipconfig" name = "ipconfig"
version = "0.3.0" version = "0.3.0"
@ -685,12 +841,49 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "leptonica-plumbing"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13adbee6950b65822bbc7a737aff78ee1d837b732a4bcee50f3aba5d89c519c3"
dependencies = [
"leptonica-sys",
"thiserror",
]
[[package]]
name = "leptonica-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418fed6d9441395144dead36d763865d6a1dd44f42f2d328515f23a63c21ce35"
dependencies = [
"bindgen",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.127" version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
[[package]]
name = "libloading"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
"cfg-if",
"winapi",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@ -822,7 +1015,7 @@ dependencies = [
"sha2", "sha2",
"socket2", "socket2",
"stringprep", "stringprep",
"strsim", "strsim 0.10.0",
"take_mut", "take_mut",
"thiserror", "thiserror",
"tokio", "tokio",
@ -835,6 +1028,34 @@ dependencies = [
"webpki-roots", "webpki-roots",
] ]
[[package]]
name = "native-tls"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
dependencies = [
"lazy_static",
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"memchr",
"version_check",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.45" version = "0.1.45"
@ -879,6 +1100,51 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "openssl"
version = "0.10.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f"
dependencies = [
"autocfg",
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "ordered-float" name = "ordered-float"
version = "2.10.0" version = "2.10.0"
@ -930,6 +1196,12 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.1.0" version = "2.1.0"
@ -948,6 +1220,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]] [[package]]
name = "poise" name = "poise"
version = "0.3.0" version = "0.3.0"
@ -1076,6 +1354,15 @@ version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.11" version = "0.11.11"
@ -1092,12 +1379,14 @@ dependencies = [
"http-body", "http-body",
"hyper", "hyper",
"hyper-rustls", "hyper-rustls",
"hyper-tls",
"ipnet", "ipnet",
"js-sys", "js-sys",
"lazy_static", "lazy_static",
"log", "log",
"mime", "mime",
"mime_guess", "mime_guess",
"native-tls",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls", "rustls",
@ -1106,6 +1395,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"tokio", "tokio",
"tokio-native-tls",
"tokio-rustls", "tokio-rustls",
"tokio-util", "tokio-util",
"tower-service", "tower-service",
@ -1139,9 +1429,12 @@ dependencies = [
"mongodb", "mongodb",
"poise", "poise",
"regex", "regex",
"reqwest",
"serde", "serde",
"serde_json", "serde_json",
"serde_regex", "serde_regex",
"serde_with_macros 2.0.0",
"tesseract",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -1162,6 +1455,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.2.3" version = "0.2.3"
@ -1223,6 +1522,16 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "schannel"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
"lazy_static",
"windows-sys",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@ -1239,6 +1548,29 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "security-framework"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
dependencies = [
"bitflags",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "semver" name = "semver"
version = "0.9.0" version = "0.9.0"
@ -1334,7 +1666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
dependencies = [ dependencies = [
"serde", "serde",
"serde_with_macros", "serde_with_macros 1.5.2",
] ]
[[package]] [[package]]
@ -1349,6 +1681,18 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_with_macros"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de337f322382fcdfbb21a014f7c224ee041a23785651db67b9827403178f698f"
dependencies = [
"darling 0.14.1",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "serenity" name = "serenity"
version = "0.11.5" version = "0.11.5"
@ -1412,6 +1756,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shlex"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.7" version = "0.4.7"
@ -1453,6 +1803,12 @@ dependencies = [
"unicode-normalization", "unicode-normalization",
] ]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"
@ -1482,6 +1838,72 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "tesseract"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcaefd467b7d5ea46c96427fe8f69996d0e692a7e70eaccbef931126d64d11de"
dependencies = [
"tesseract-plumbing",
"tesseract-sys",
"thiserror",
]
[[package]]
name = "tesseract-plumbing"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef98c10d582467f68646ab60fb257cbcb122fa70b3153ec9dac858170ccc58f7"
dependencies = [
"leptonica-plumbing",
"tesseract-sys",
"thiserror",
]
[[package]]
name = "tesseract-sys"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f885433c23fbe6e22176bbc500025abf0331bd27d1c882f163bb696759bc27"
dependencies = [
"bindgen",
"leptonica-sys",
"pkg-config",
"vcpkg",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.32" version = "1.0.32"
@ -1587,6 +2009,16 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tokio-native-tls"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
dependencies = [
"native-tls",
"tokio",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.23.4" version = "0.23.4"
@ -1802,6 +2234,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.7.1" version = "0.7.1"
@ -1852,6 +2290,18 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
@ -1975,6 +2425,15 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "which"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "widestring" name = "widestring"
version = "0.5.1" version = "0.5.1"
@ -1997,6 +2456,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

View File

@ -18,16 +18,19 @@ panic = "abort"
[dependencies] [dependencies]
bson = "2.4" bson = "2.4"
serde_with_macros = "2.0"
mongodb = "2.3" mongodb = "2.3"
poise = "0.3" poise = "0.3"
decancer = "1.4" decancer = "1.4"
tokio = { version = "1.20.1", features = ["rt-multi-thread"] } tokio = { version = "1.20", features = ["rt-multi-thread"] }
dotenv = "0.15" dotenv = "0.15"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
regex = "1.0" regex = "1.0"
serde_regex = "1.1" serde_regex = "1.1"
reqwest = { version = "0.11" }
chrono = "0.4" chrono = "0.4"
dirs = "4.0" dirs = "4.0"
tesseract = "0.12"
tracing = { version = "0.1", features = ["max_level_debug", "release_max_level_info"] } tracing = { version = "0.1", features = ["max_level_debug", "release_max_level_info"] }
tracing-subscriber = "0.3" tracing-subscriber = "0.3"

View File

@ -23,11 +23,17 @@
{ {
"includes": { "includes": {
"channels": [0], "channels": [0],
"match": [""] "match": {
"text": [],
"ocr": [""]
}
}, },
"excludes": { "excludes": {
"roles": [0], "roles": [0],
"match": [""] "match": {
"text": [""],
"ocr": []
}
}, },
"condition": { "condition": {
"user": { "user": {

View File

@ -22,11 +22,14 @@
{ {
"includes": { "includes": {
"channels": [952946952348270626, 953965039105232906], "channels": [952946952348270626, 953965039105232906],
"match": ["(?i)(((vanced|download|install|get|manager).*){2,})"] "match": {
"ocr": [],
"text": ["(?i)(((vanced|download|install|get|manager).*){2,})"]
}
}, },
"excludes": { "excludes": {
"roles": [934329947164667954], "roles": [934329947164667954],
"match": ["(?i)music", "(?i)eta", "(?i)error"] "match": { "ocr": [], "text": ["(?i)music", "(?i)eta", "(?i)error"] }
}, },
"condition": { "condition": {
"user": { "user": {
@ -66,11 +69,14 @@
{ {
"includes": { "includes": {
"channels": [952946952348270626, 953965039105232906], "channels": [952946952348270626, 953965039105232906],
"match": ["(?i)(((help|how|install|fix|vanced|manager).*){3,})"] "match": {
"ocr": [],
"text": ["(?i)(((help|how|install|fix|vanced|manager).*){3,})"]
}
}, },
"excludes": { "excludes": {
"roles": [934329947164667954], "roles": [934329947164667954],
"match": ["(?i)download", "(?i)get"] "match": { "ocr": [], "text": ["(?i)download", "(?i)get"] }
}, },
"condition": { "condition": {
"user": { "user": {
@ -110,13 +116,16 @@
{ {
"includes": { "includes": {
"channels": [952946952348270626, 953965039105232906], "channels": [952946952348270626, 953965039105232906],
"match": [ "match": {
"(?i)(((vanced|when|where|release|out|progress|update|manager|eta).*){2,})" "ocr": [],
] "text": [
"(?i)(((vanced|when|where|release|out|progress|update|manager|eta).*){2,})"
]
}
}, },
"excludes": { "excludes": {
"roles": [934329947164667954], "roles": [934329947164667954],
"match": ["(?i)error", "(?i)problem"] "match": { "ocr": [], "text": ["(?i)error", "(?i)problem"] }
}, },
"condition": { "condition": {
"user": { "user": {

View File

@ -145,11 +145,23 @@
"minItems": 1 "minItems": 1
}, },
"match": { "match": {
"type": "object",
"properties": {
"text": {
"$ref": "#/$defs/regex",
"description": "A list of regex strings."
},
"ocr": {
"$ref": "#/$defs/regex",
"description": "A list of regex strings to ocr."
}
}
},
"regex": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
}, },
"description": "A list of regex strings.",
"uniqueItems": true, "uniqueItems": true,
"minItems": 1 "minItems": 1
}, },

View File

@ -1,4 +1,4 @@
pub mod configuration; pub mod configuration;
pub mod misc;
pub mod moderation; pub mod moderation;
pub mod utils; pub mod utils;
pub mod misc;

View File

@ -30,7 +30,7 @@ pub async fn unmute(
&ctx, &ctx,
ModerationKind::Unmute( ModerationKind::Unmute(
queue_unmute_member( queue_unmute_member(
ctx.discord(), &ctx.discord().http,
&data.database, &data.database,
&member, &member,
configuration.general.mute.role, configuration.general.mute.role,
@ -98,21 +98,22 @@ pub async fn mute(
if let Err(add_role_result) = member.add_role(&ctx.discord().http, mute_role_id).await { if let Err(add_role_result) = member.add_role(&ctx.discord().http, mute_role_id).await {
Some(Error::from(add_role_result)) Some(Error::from(add_role_result))
} else { } else {
// accumulate all roles to take from the member
let removed_roles = member let removed_roles = member
.roles .roles
.iter() .iter()
.filter(|r| take.contains(&r.0)) .filter(|r| take.contains(&r.0))
.map(|r| r.to_string()) .map(|r| r.to_string())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// take them from the member, get remaining roles
let removed = member let remaining_roles = member
.remove_roles( .remove_roles(
&ctx.discord().http, &ctx.discord().http,
&take.iter().map(|&r| RoleId::from(r)).collect::<Vec<_>>(), &take.iter().map(|&r| RoleId::from(r)).collect::<Vec<_>>(),
) )
.await; .await;
if let Err(remove_role_result) = removed { if let Err(remove_role_result) = remaining_roles {
Some(Error::from(remove_role_result)) Some(Error::from(remove_role_result))
} else { } else {
// Roles which were removed from the user // Roles which were removed from the user
@ -161,7 +162,7 @@ pub async fn mute(
data.pending_unmutes.insert( data.pending_unmutes.insert(
member.user.id.0, member.user.id.0,
queue_unmute_member( queue_unmute_member(
ctx.discord(), &ctx.discord().http,
&data.database, &data.database,
&member, &member,
mute_role_id, mute_role_id,
@ -303,11 +304,7 @@ pub async fn ban(
.unwrap(); .unwrap();
let ban_result = member let ban_result = member
.ban_with_reason( .ban_with_reason(&ctx.discord().http, cmp::min(dmd.unwrap_or(0), 7), reason)
&ctx.discord().http,
cmp::min(dmd.unwrap_or(0), 7),
reason,
)
.await; .await;
let embed_color = ctx.data().read().await.configuration.general.embed_color; let embed_color = ctx.data().read().await.configuration.general.embed_color;

View File

@ -2,19 +2,16 @@ use std::fmt::Display;
use bson::Document; use bson::Document;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with_macros::skip_serializing_none;
// Models // Models
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Default)] #[derive(Serialize, Deserialize, Debug, Default)]
pub struct Muted { pub struct Muted {
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>, pub user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub guild_id: Option<String>, pub guild_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub taken_roles: Option<Vec<String>>, pub taken_roles: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires: Option<u64>, pub expires: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>, pub reason: Option<String>,
} }

View File

@ -1,6 +1,9 @@
use super::*; use super::*;
use crate::utils::decancer::cure; use crate::utils::decancer::cure;
use crate::utils::moderation::mute_on_join;
pub async fn guild_member_addition(ctx: &serenity::Context, new_member: &mut serenity::Member) {
mute_on_join(ctx, new_member).await;
pub async fn guild_member_addition(ctx: &serenity::Context, new_member: &serenity::Member) {
cure(ctx, &None, new_member).await; cure(ctx, &None, new_member).await;
} }

View File

@ -1,84 +1,140 @@
use chrono::{DateTime, Duration, NaiveDateTime, Utc}; use chrono::{DateTime, Duration, NaiveDateTime, Utc};
use poise::serenity_prelude::Attachment;
use regex::Regex; use regex::Regex;
use tracing::debug; use tracing::debug;
use super::*; use super::*;
use crate::utils::bot::get_data_lock; use crate::utils::bot::get_data_lock;
use crate::utils::ocr;
fn contains_match(regex: &[Regex], text: &str) -> bool { fn contains_match(regex: &[Regex], text: &str) -> bool {
regex.iter().any(|r| r.is_match(text)) regex.iter().any(|r| r.is_match(text))
} }
async fn attachments_contains(attachments: &[Attachment], regex: &[Regex]) -> bool {
for attachment in attachments {
debug!("Checking attachment {}", &attachment.url);
if !&attachment.content_type.as_ref().unwrap().contains("image") {
continue;
}
if contains_match(
regex,
&ocr::get_text_from_image_url(&attachment.url).await.unwrap(),
) {
return true;
}
}
false
}
pub async fn message_create(ctx: &serenity::Context, new_message: &serenity::Message) { pub async fn message_create(ctx: &serenity::Context, new_message: &serenity::Message) {
debug!("Received message: {}", new_message.content); debug!("Received message: {}", new_message.content);
if new_message.guild_id.is_none() || new_message.author.bot { if new_message.guild_id.is_none() || new_message.author.bot {
return; return;
} }
if let Some(message_response) = get_data_lock(ctx) let data_lock = get_data_lock(ctx).await;
.await let responses = &data_lock.read().await.configuration.message_responses;
.read()
.await
.configuration
.message_responses
.iter()
.find(|&response| {
// check if the message was sent in a channel that is included in the responder
response.includes.channels.iter().any(|&channel_id| channel_id == new_message.channel_id.0)
// check if the message was sent by a user that is not excluded from the responder
&& !response.excludes.roles.iter().any(|&role_id| role_id == new_message.author.id.0)
// check if the message does not match any of the excludes
&& !contains_match(&response.excludes.match_field, &new_message.content)
// check if the message matches any of the includes
&& contains_match(&response.includes.match_field, &new_message.content)
})
{
let min_age = message_response.condition.user.server_age;
if min_age != 0 { for response in responses {
let joined_at = ctx // check if the message was sent in a channel that is included in the responder
.http if !response
.get_member(new_message.guild_id.unwrap().0, new_message.author.id.0) .includes
.await .channels
.unwrap() .iter()
.joined_at .any(|&channel_id| channel_id == new_message.channel_id.0)
.unwrap() {
.unix_timestamp(); continue;
}
let must_joined_at = let excludes = &response.excludes;
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(joined_at, 0), Utc); // check if the message was sent by a user that is not excluded from the responder
let but_joined_at = Utc::now() - Duration::days(min_age); if excludes
.roles
.iter()
.any(|&role_id| role_id == new_message.author.id.0)
{
continue;
}
if must_joined_at <= but_joined_at { let message = &new_message.content;
return; let contains_attachments = !new_message.attachments.is_empty();
}
new_message.channel_id // check if the message does not match any of the excludes
.send_message(&ctx.http, |m| { if contains_match(&excludes.match_field.text, message) {
m.reference_message(new_message); continue;
match &message_response.response.embed { }
Some(embed) => m.embed(|e| {
e.title(&embed.title) if contains_attachments
.description(&embed.description) && !excludes.match_field.ocr.is_empty()
.color(embed.color) && attachments_contains(&new_message.attachments, &excludes.match_field.ocr).await
.fields(embed.fields.iter().map(|field| { {
(field.name.clone(), field.value.clone(), field.inline) continue;
})) }
.footer(|f| {
f.text(&embed.footer.text); // check if the message does match any of the includes
f.icon_url(&embed.footer.icon_url) if !(contains_match(&response.includes.match_field.text, message)
}) || (contains_attachments
.thumbnail(&embed.thumbnail.url) && !response.includes.match_field.ocr.is_empty()
.image(&embed.image.url) && attachments_contains(
.author(|a| { &new_message.attachments,
a.name(&embed.author.name).icon_url(&embed.author.icon_url) &response.includes.match_field.ocr,
}) )
}), .await))
None => m.content(message_response.response.message.as_ref().unwrap()), {
} continue;
}) }
.await
.expect("Could not reply to message author."); let min_age = response.condition.user.server_age;
}
} if min_age != 0 {
let joined_at = ctx
.http
.get_member(new_message.guild_id.unwrap().0, new_message.author.id.0)
.await
.unwrap()
.joined_at
.unwrap()
.unix_timestamp();
let must_joined_at =
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(joined_at, 0), Utc);
let but_joined_at = Utc::now() - Duration::days(min_age);
if must_joined_at <= but_joined_at {
return;
}
new_message
.channel_id
.send_message(&ctx.http, |m| {
m.reference_message(new_message);
match &response.response.embed {
Some(embed) => m.embed(|e| {
e.title(&embed.title)
.description(&embed.description)
.color(embed.color)
.fields(embed.fields.iter().map(|field| {
(field.name.clone(), field.value.clone(), field.inline)
}))
.footer(|f| {
f.text(&embed.footer.text);
f.icon_url(&embed.footer.icon_url)
})
.thumbnail(&embed.thumbnail.url)
.image(&embed.image.url)
.author(|a| {
a.name(&embed.author.name).icon_url(&embed.author.icon_url)
})
}),
None => m.content(response.response.message.as_ref().unwrap()),
}
})
.await
.expect("Could not reply to message author.");
}
}
} }

View File

@ -87,8 +87,8 @@ impl serenity::EventHandler for Handler<Arc<RwLock<Data>>> {
thread_create::thread_create(&ctx, &thread).await; thread_create::thread_create(&ctx, &thread).await;
} }
async fn guild_member_addition(&self, ctx: serenity::Context, new_member: serenity::Member) { async fn guild_member_addition(&self, ctx: serenity::Context, mut new_member: serenity::Member) {
guild_member_addition::guild_member_addition(&ctx, &new_member).await; guild_member_addition::guild_member_addition(&ctx, &mut new_member).await;
} }
async fn guild_member_update( async fn guild_member_update(

View File

@ -39,7 +39,7 @@ pub async fn load_muted_members(ctx: &serenity::Context, _: &serenity::Ready) {
data.pending_unmutes.insert( data.pending_unmutes.insert(
member.user.id.0, member.user.id.0,
queue_unmute_member( queue_unmute_member(
ctx, &ctx.http,
&data.database, &data.database,
&member, &member,
mute_role_id, mute_role_id,

View File

@ -28,7 +28,7 @@ impl serenity::TypeMapKey for Data {
pub struct Data { pub struct Data {
configuration: Configuration, configuration: Configuration,
database: Database, database: Arc<Database>,
pending_unmutes: HashMap<u64, JoinHandle<Option<Error>>>, pending_unmutes: HashMap<u64, JoinHandle<Option<Error>>>,
} }
@ -67,12 +67,14 @@ async fn main() {
let data = Arc::new(RwLock::new(Data { let data = Arc::new(RwLock::new(Data {
configuration, configuration,
database: Database::new( database: Arc::new(
&env::var("MONGODB_URI").expect("MONGODB_URI environment variable not set"), Database::new(
"revanced_discord_bot", &env::var("MONGODB_URI").expect("MONGODB_URI environment variable not set"),
) "revanced_discord_bot",
.await )
.unwrap(), .await
.unwrap(),
),
pending_unmutes: HashMap::new(), pending_unmutes: HashMap::new(),
})); }));

View File

@ -5,6 +5,7 @@ use std::path::Path;
use dirs::config_dir; use dirs::config_dir;
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with_macros::skip_serializing_none;
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct Configuration { pub struct Configuration {
@ -144,15 +145,24 @@ pub struct Author {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Includes { pub struct Includes {
pub channels: Vec<u64>, pub channels: Vec<u64>,
#[serde(rename = "match", with = "serde_regex")] #[serde(rename = "match")]
pub match_field: Vec<Regex>, pub match_field: Match,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Excludes { pub struct Excludes {
pub roles: Vec<u64>, pub roles: Vec<u64>,
#[serde(rename = "match", with = "serde_regex")] #[serde(rename = "match")]
pub match_field: Vec<Regex>, pub match_field: Match,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize)]
pub struct Match {
#[serde(with = "serde_regex")]
pub text: Vec<Regex>,
#[serde(with = "serde_regex")]
pub ocr: Vec<Regex>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]

View File

@ -4,3 +4,4 @@ pub mod bot;
pub mod decancer; pub mod decancer;
pub mod embed; pub mod embed;
pub mod moderation; pub mod moderation;
pub mod ocr;

View File

@ -1,5 +1,11 @@
use tokio::task::JoinHandle; use std::sync::Arc;
use mongodb::options::FindOptions;
use poise::serenity_prelude::Http;
use tokio::task::JoinHandle;
use tracing::{debug, error, trace};
use super::bot::get_data_lock;
use super::*; use super::*;
use crate::db::database::Database; use crate::db::database::Database;
use crate::db::model::Muted; use crate::db::model::Muted;
@ -10,14 +16,54 @@ pub enum ModerationKind {
Unmute(Option<Error>), // Error Unmute(Option<Error>), // Error
} }
pub async fn mute_on_join(ctx: &serenity::Context, new_member: &mut serenity::Member) {
let data = get_data_lock(ctx).await;
let data = data.read().await;
if let Ok(mut cursor) = data
.database
.find::<Muted>(
"muted",
Muted {
user_id: Some(new_member.user.id.to_string()),
..Default::default()
}
.into(),
Some(FindOptions::builder().limit(1).build()),
)
.await
{
if cursor.advance().await.is_ok() {
trace!("Muted member {} rejoined the server", new_member.user.tag());
if new_member
.add_role(&ctx.http, RoleId(data.configuration.general.mute.role))
.await
.is_ok()
{
debug!(
"Muted member {} was successfully muted",
new_member.user.tag()
);
} else {
error!(
"Failed to mute member {} after rejoining the server",
new_member.user.tag()
);
}
}
} else {
error!("Failed to query database for muted users");
}
}
pub fn queue_unmute_member( pub fn queue_unmute_member(
ctx: &serenity::Context, http: &Arc<Http>,
database: &Database, database: &Arc<Database>,
member: &Member, member: &Member,
mute_role_id: u64, mute_role_id: u64,
mute_duration: u64, mute_duration: u64,
) -> JoinHandle<Option<Error>> { ) -> JoinHandle<Option<Error>> {
let ctx = ctx.clone(); let http = http.clone();
let database = database.clone(); let database = database.clone();
let mut member = member.clone(); let mut member = member.clone();
@ -46,9 +92,9 @@ pub fn queue_unmute_member(
.map(|r| RoleId::from(r.parse::<u64>().unwrap())) .map(|r| RoleId::from(r.parse::<u64>().unwrap()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if let Err(add_role_result) = member.add_roles(&ctx.http, &taken_roles).await { if let Err(add_role_result) = member.add_roles(&http, &taken_roles).await {
Some(Error::from(add_role_result)) Some(Error::from(add_role_result))
} else if let Err(remove_result) = member.remove_role(ctx.http, mute_role_id).await { } else if let Err(remove_result) = member.remove_role(http, mute_role_id).await {
Some(Error::from(remove_result)) Some(Error::from(remove_result))
} else { } else {
None None

12
src/utils/ocr.rs Normal file
View File

@ -0,0 +1,12 @@
use reqwest::Error;
use tesseract::Tesseract;
pub async fn get_text_from_image_url(url: &str) -> Result<String, Error> {
let image = &reqwest::get(url).await?.bytes().await.unwrap().to_vec();
Ok(Tesseract::new(None, None)
.unwrap()
.set_image_from_mem(image)
.unwrap()
.get_text()
.unwrap())
}