diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d7acbc5..e42e426 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,9 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Install dependencies + run: sudo apt install libleptonica-dev libtesseract-dev libssl-dev + - name: Setup Rust toolchain id: rust-toolchain uses: actions-rs/toolchain@v1 @@ -28,12 +31,6 @@ jobs: override: true components: clippy - - name: Setup Node.js - if: github.event_name != 'pull_request' - uses: actions/setup-node@v2 - with: - node-version: "lts/*" - - name: Cache uses: actions/cache@v3 id: cache @@ -47,8 +44,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --release --target=x86_64-unknown-linux-musl - use-cross: true + args: --release - name: cargo clippy uses: actions-rs/cargo@v1 @@ -60,7 +56,7 @@ jobs: uses: actions/upload-artifact@v3 with: 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 if: github.event_name != 'pull_request' diff --git a/Cargo.lock b/Cargo.lock index c47c6d2..7f1fe2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,17 @@ dependencies = [ "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]] name = "autocfg" version = "1.1.0" @@ -76,6 +87,29 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "bitflags" version = "1.3.2" @@ -134,6 +168,15 @@ version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -155,6 +198,48 @@ dependencies = [ "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]] name = "cpufeatures" version = "0.2.2" @@ -213,7 +298,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn", ] @@ -227,7 +312,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn", ] @@ -347,6 +432,28 @@ dependencies = [ "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]] name = "flate2" version = "1.0.24" @@ -363,6 +470,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "form_urlencoded" version = "1.0.1" @@ -482,6 +604,12 @@ dependencies = [ "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]] name = "h2" version = "0.3.13" @@ -582,6 +710,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.20" @@ -619,6 +753,19 @@ dependencies = [ "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]] name = "ident_case" version = "1.0.1" @@ -646,6 +793,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipconfig" version = "0.3.0" @@ -685,12 +841,49 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "libc" version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "linked-hash-map" version = "0.5.6" @@ -822,7 +1015,7 @@ dependencies = [ "sha2", "socket2", "stringprep", - "strsim", + "strsim 0.10.0", "take_mut", "thiserror", "tokio", @@ -835,6 +1028,34 @@ dependencies = [ "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]] name = "num-integer" version = "0.1.45" @@ -879,6 +1100,51 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "ordered-float" version = "2.10.0" @@ -930,6 +1196,12 @@ dependencies = [ "digest", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -948,6 +1220,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + [[package]] name = "poise" version = "0.3.0" @@ -1076,6 +1354,15 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "reqwest" version = "0.11.11" @@ -1092,12 +1379,14 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "lazy_static", "log", "mime", "mime_guess", + "native-tls", "percent-encoding", "pin-project-lite", "rustls", @@ -1106,6 +1395,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-util", "tower-service", @@ -1139,9 +1429,12 @@ dependencies = [ "mongodb", "poise", "regex", + "reqwest", "serde", "serde_json", "serde_regex", + "serde_with_macros 2.0.0", + "tesseract", "tokio", "tracing", "tracing-subscriber", @@ -1162,6 +1455,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.2.3" @@ -1223,6 +1522,16 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "scopeguard" version = "1.1.0" @@ -1239,6 +1548,29 @@ dependencies = [ "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]] name = "semver" version = "0.9.0" @@ -1334,7 +1666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ "serde", - "serde_with_macros", + "serde_with_macros 1.5.2", ] [[package]] @@ -1349,6 +1681,18 @@ dependencies = [ "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]] name = "serenity" version = "0.11.5" @@ -1412,6 +1756,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + [[package]] name = "slab" version = "0.4.7" @@ -1453,6 +1803,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strsim" version = "0.10.0" @@ -1482,6 +1838,72 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "thiserror" version = "1.0.32" @@ -1587,6 +2009,16 @@ dependencies = [ "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]] name = "tokio-rustls" version = "0.23.4" @@ -1802,6 +2234,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "untrusted" version = "0.7.1" @@ -1852,6 +2290,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "version_check" version = "0.9.4" @@ -1975,6 +2425,15 @@ dependencies = [ "webpki", ] +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + [[package]] name = "widestring" version = "0.5.1" @@ -1997,6 +2456,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 90dfdee..8809c2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,16 +18,19 @@ panic = "abort" [dependencies] bson = "2.4" +serde_with_macros = "2.0" mongodb = "2.3" poise = "0.3" decancer = "1.4" -tokio = { version = "1.20.1", features = ["rt-multi-thread"] } +tokio = { version = "1.20", features = ["rt-multi-thread"] } dotenv = "0.15" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" regex = "1.0" serde_regex = "1.1" +reqwest = { version = "0.11" } chrono = "0.4" dirs = "4.0" +tesseract = "0.12" tracing = { version = "0.1", features = ["max_level_debug", "release_max_level_info"] } tracing-subscriber = "0.3" \ No newline at end of file diff --git a/configuration.example.json b/configuration.example.json index cc2a239..b3b96f1 100644 --- a/configuration.example.json +++ b/configuration.example.json @@ -23,11 +23,17 @@ { "includes": { "channels": [0], - "match": [""] + "match": { + "text": [], + "ocr": [""] + } }, "excludes": { "roles": [0], - "match": [""] + "match": { + "text": [""], + "ocr": [] + } }, "condition": { "user": { diff --git a/configuration.revanced.json b/configuration.revanced.json index c603502..cf57705 100644 --- a/configuration.revanced.json +++ b/configuration.revanced.json @@ -22,11 +22,14 @@ { "includes": { "channels": [952946952348270626, 953965039105232906], - "match": ["(?i)(((vanced|download|install|get|manager).*){2,})"] + "match": { + "ocr": [], + "text": ["(?i)(((vanced|download|install|get|manager).*){2,})"] + } }, "excludes": { "roles": [934329947164667954], - "match": ["(?i)music", "(?i)eta", "(?i)error"] + "match": { "ocr": [], "text": ["(?i)music", "(?i)eta", "(?i)error"] } }, "condition": { "user": { @@ -66,11 +69,14 @@ { "includes": { "channels": [952946952348270626, 953965039105232906], - "match": ["(?i)(((help|how|install|fix|vanced|manager).*){3,})"] + "match": { + "ocr": [], + "text": ["(?i)(((help|how|install|fix|vanced|manager).*){3,})"] + } }, "excludes": { "roles": [934329947164667954], - "match": ["(?i)download", "(?i)get"] + "match": { "ocr": [], "text": ["(?i)download", "(?i)get"] } }, "condition": { "user": { @@ -110,13 +116,16 @@ { "includes": { "channels": [952946952348270626, 953965039105232906], - "match": [ - "(?i)(((vanced|when|where|release|out|progress|update|manager|eta).*){2,})" - ] + "match": { + "ocr": [], + "text": [ + "(?i)(((vanced|when|where|release|out|progress|update|manager|eta).*){2,})" + ] + } }, "excludes": { "roles": [934329947164667954], - "match": ["(?i)error", "(?i)problem"] + "match": { "ocr": [], "text": ["(?i)error", "(?i)problem"] } }, "condition": { "user": { diff --git a/configuration.schema.json b/configuration.schema.json index 4b5023e..5cb6021 100644 --- a/configuration.schema.json +++ b/configuration.schema.json @@ -145,11 +145,23 @@ "minItems": 1 }, "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", "items": { "type": "string" }, - "description": "A list of regex strings.", "uniqueItems": true, "minItems": 1 }, diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 105fd8b..19ddd4d 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,4 +1,4 @@ pub mod configuration; +pub mod misc; pub mod moderation; pub mod utils; -pub mod misc; \ No newline at end of file diff --git a/src/commands/moderation.rs b/src/commands/moderation.rs index 2acd345..7905b01 100644 --- a/src/commands/moderation.rs +++ b/src/commands/moderation.rs @@ -30,7 +30,7 @@ pub async fn unmute( &ctx, ModerationKind::Unmute( queue_unmute_member( - ctx.discord(), + &ctx.discord().http, &data.database, &member, 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 { Some(Error::from(add_role_result)) } else { + // accumulate all roles to take from the member let removed_roles = member .roles .iter() .filter(|r| take.contains(&r.0)) .map(|r| r.to_string()) .collect::>(); - - let removed = member + // take them from the member, get remaining roles + let remaining_roles = member .remove_roles( &ctx.discord().http, &take.iter().map(|&r| RoleId::from(r)).collect::>(), ) .await; - if let Err(remove_role_result) = removed { + if let Err(remove_role_result) = remaining_roles { Some(Error::from(remove_role_result)) } else { // Roles which were removed from the user @@ -161,7 +162,7 @@ pub async fn mute( data.pending_unmutes.insert( member.user.id.0, queue_unmute_member( - ctx.discord(), + &ctx.discord().http, &data.database, &member, mute_role_id, @@ -303,11 +304,7 @@ pub async fn ban( .unwrap(); let ban_result = member - .ban_with_reason( - &ctx.discord().http, - cmp::min(dmd.unwrap_or(0), 7), - reason, - ) + .ban_with_reason(&ctx.discord().http, cmp::min(dmd.unwrap_or(0), 7), reason) .await; let embed_color = ctx.data().read().await.configuration.general.embed_color; diff --git a/src/db/model.rs b/src/db/model.rs index 5e7a0cb..c4c12b3 100644 --- a/src/db/model.rs +++ b/src/db/model.rs @@ -2,19 +2,16 @@ use std::fmt::Display; use bson::Document; use serde::{Deserialize, Serialize}; +use serde_with_macros::skip_serializing_none; // Models +#[skip_serializing_none] #[derive(Serialize, Deserialize, Debug, Default)] pub struct Muted { - #[serde(skip_serializing_if = "Option::is_none")] pub user_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub guild_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub taken_roles: Option>, - #[serde(skip_serializing_if = "Option::is_none")] pub expires: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option, } diff --git a/src/events/guild_member_addition.rs b/src/events/guild_member_addition.rs index 62ae99f..5f3ba0e 100644 --- a/src/events/guild_member_addition.rs +++ b/src/events/guild_member_addition.rs @@ -1,6 +1,9 @@ use super::*; 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; -} \ No newline at end of file +} diff --git a/src/events/message_create.rs b/src/events/message_create.rs index fadcf29..25526ec 100644 --- a/src/events/message_create.rs +++ b/src/events/message_create.rs @@ -1,84 +1,140 @@ use chrono::{DateTime, Duration, NaiveDateTime, Utc}; +use poise::serenity_prelude::Attachment; use regex::Regex; use tracing::debug; use super::*; use crate::utils::bot::get_data_lock; +use crate::utils::ocr; fn contains_match(regex: &[Regex], text: &str) -> bool { 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) { debug!("Received message: {}", new_message.content); + if new_message.guild_id.is_none() || new_message.author.bot { return; } - if let Some(message_response) = get_data_lock(ctx) - .await - .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 { - 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::::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 &message_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(message_response.response.message.as_ref().unwrap()), - } - }) - .await - .expect("Could not reply to message author."); - } - } + let data_lock = get_data_lock(ctx).await; + let responses = &data_lock.read().await.configuration.message_responses; + + for response in responses { + // check if the message was sent in a channel that is included in the responder + if !response + .includes + .channels + .iter() + .any(|&channel_id| channel_id == new_message.channel_id.0) + { + continue; + } + + let excludes = &response.excludes; + // check if the message was sent by a user that is not excluded from the responder + if excludes + .roles + .iter() + .any(|&role_id| role_id == new_message.author.id.0) + { + continue; + } + + let message = &new_message.content; + let contains_attachments = !new_message.attachments.is_empty(); + + // check if the message does not match any of the excludes + if contains_match(&excludes.match_field.text, message) { + continue; + } + + if contains_attachments + && !excludes.match_field.ocr.is_empty() + && attachments_contains(&new_message.attachments, &excludes.match_field.ocr).await + { + continue; + } + + // check if the message does match any of the includes + if !(contains_match(&response.includes.match_field.text, message) + || (contains_attachments + && !response.includes.match_field.ocr.is_empty() + && attachments_contains( + &new_message.attachments, + &response.includes.match_field.ocr, + ) + .await)) + { + continue; + } + + 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::::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."); + } + } } diff --git a/src/events/mod.rs b/src/events/mod.rs index c9c7e65..461661e 100644 --- a/src/events/mod.rs +++ b/src/events/mod.rs @@ -87,8 +87,8 @@ impl serenity::EventHandler for Handler>> { thread_create::thread_create(&ctx, &thread).await; } - async fn guild_member_addition(&self, ctx: serenity::Context, new_member: serenity::Member) { - guild_member_addition::guild_member_addition(&ctx, &new_member).await; + async fn guild_member_addition(&self, ctx: serenity::Context, mut new_member: serenity::Member) { + guild_member_addition::guild_member_addition(&ctx, &mut new_member).await; } async fn guild_member_update( diff --git a/src/events/ready.rs b/src/events/ready.rs index a339ef9..47d8019 100644 --- a/src/events/ready.rs +++ b/src/events/ready.rs @@ -39,7 +39,7 @@ pub async fn load_muted_members(ctx: &serenity::Context, _: &serenity::Ready) { data.pending_unmutes.insert( member.user.id.0, queue_unmute_member( - ctx, + &ctx.http, &data.database, &member, mute_role_id, diff --git a/src/main.rs b/src/main.rs index 45b6eaf..da9611f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,7 @@ impl serenity::TypeMapKey for Data { pub struct Data { configuration: Configuration, - database: Database, + database: Arc, pending_unmutes: HashMap>>, } @@ -67,12 +67,14 @@ async fn main() { let data = Arc::new(RwLock::new(Data { configuration, - database: Database::new( - &env::var("MONGODB_URI").expect("MONGODB_URI environment variable not set"), - "revanced_discord_bot", - ) - .await - .unwrap(), + database: Arc::new( + Database::new( + &env::var("MONGODB_URI").expect("MONGODB_URI environment variable not set"), + "revanced_discord_bot", + ) + .await + .unwrap(), + ), pending_unmutes: HashMap::new(), })); diff --git a/src/model/application.rs b/src/model/application.rs index c85dac5..61ccc6e 100644 --- a/src/model/application.rs +++ b/src/model/application.rs @@ -5,6 +5,7 @@ use std::path::Path; use dirs::config_dir; use regex::Regex; use serde::{Deserialize, Serialize}; +use serde_with_macros::skip_serializing_none; #[derive(Default, Serialize, Deserialize)] pub struct Configuration { @@ -144,15 +145,24 @@ pub struct Author { #[derive(Serialize, Deserialize)] pub struct Includes { pub channels: Vec, - #[serde(rename = "match", with = "serde_regex")] - pub match_field: Vec, + #[serde(rename = "match")] + pub match_field: Match, } #[derive(Serialize, Deserialize)] pub struct Excludes { pub roles: Vec, - #[serde(rename = "match", with = "serde_regex")] - pub match_field: Vec, + #[serde(rename = "match")] + pub match_field: Match, +} + +#[skip_serializing_none] +#[derive(Serialize, Deserialize)] +pub struct Match { + #[serde(with = "serde_regex")] + pub text: Vec, + #[serde(with = "serde_regex")] + pub ocr: Vec, } #[derive(Serialize, Deserialize)] diff --git a/src/utils/mod.rs b/src/utils/mod.rs index c07315a..8bd34b6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,3 +4,4 @@ pub mod bot; pub mod decancer; pub mod embed; pub mod moderation; +pub mod ocr; \ No newline at end of file diff --git a/src/utils/moderation.rs b/src/utils/moderation.rs index cda69b6..ba1e5c5 100644 --- a/src/utils/moderation.rs +++ b/src/utils/moderation.rs @@ -1,5 +1,11 @@ +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 crate::db::database::Database; use crate::db::model::Muted; @@ -10,14 +16,54 @@ pub enum ModerationKind { Unmute(Option), // 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 { + 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( - ctx: &serenity::Context, - database: &Database, + http: &Arc, + database: &Arc, member: &Member, mute_role_id: u64, mute_duration: u64, ) -> JoinHandle> { - let ctx = ctx.clone(); + let http = http.clone(); let database = database.clone(); let mut member = member.clone(); @@ -46,9 +92,9 @@ pub fn queue_unmute_member( .map(|r| RoleId::from(r.parse::().unwrap())) .collect::>(); - 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)) - } 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)) } else { None diff --git a/src/utils/ocr.rs b/src/utils/ocr.rs new file mode 100644 index 0000000..521b6ab --- /dev/null +++ b/src/utils/ocr.rs @@ -0,0 +1,12 @@ +use reqwest::Error; +use tesseract::Tesseract; + +pub async fn get_text_from_image_url(url: &str) -> Result { + 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()) +}