Compare commits
20 Commits
c6121d69b6
...
b884410b58
Author | SHA1 | Date |
---|---|---|
Harsh Shandilya | b884410b58 | |
Harsh Shandilya | 60c425700b | |
Harsh Shandilya | 7325945e49 | |
Harsh Shandilya | 41c103bdc9 | |
Harsh Shandilya | 7836e09e15 | |
Harsh Shandilya | d2144a9aa1 | |
Harsh Shandilya | 893c511221 | |
Harsh Shandilya | 0afdad7a3d | |
Harsh Shandilya | 74c7259ae1 | |
Harsh Shandilya | cbfd9c394e | |
Harsh Shandilya | 1ebdccff4a | |
Harsh Shandilya | dc6b8db163 | |
Harsh Shandilya | 24f9a5243f | |
Harsh Shandilya | 61928ddea7 | |
Harsh Shandilya | a68a8204fe | |
Harsh Shandilya | e81fff6f24 | |
Harsh Shandilya | db626e403c | |
Harsh Shandilya | 65c6979881 | |
Harsh Shandilya | bdc9b21bb6 | |
Harsh Shandilya | 9cf2a70291 |
|
@ -0,0 +1,8 @@
|
|||
name: Build and publish Docker image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
|
|
@ -8,6 +8,9 @@ on:
|
|||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy app
|
||||
|
@ -33,7 +36,7 @@ jobs:
|
|||
nix build .#container
|
||||
docker load < result
|
||||
|
||||
- name: Publish image and deploy
|
||||
- name: Deploy to fly.io
|
||||
shell: bash
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
|
@ -41,7 +44,23 @@ jobs:
|
|||
flyctl auth docker
|
||||
flyctl deploy
|
||||
|
||||
- name: Trigger package bump in dotfiles repo
|
||||
run: gh api repos/msfjarvis/dotfiles/dispatches --field event_type="package-bump" --field client_payload[package_name]="linkleaner"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.DISPATCH_TOKEN }}
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and publish image to GitHub Container Registry
|
||||
shell: bash
|
||||
run: |
|
||||
# Get image details
|
||||
IMAGE_NAME="$(nix eval --raw .#packages.x86_64-linux.ghContainer.imageName)"
|
||||
IMAGE_TAG="$(nix eval --raw .#packages.x86_64-linux.ghContainer.imageTag)"
|
||||
|
||||
# Build and load the image
|
||||
nix build .#ghContainer
|
||||
docker load < ./result
|
||||
|
||||
# Push image to ghcr.io
|
||||
docker push "${IMAGE_NAME}":"${IMAGE_TAG}"
|
||||
|
|
|
@ -109,9 +109,9 @@ dependencies = [
|
|||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.28",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"itoa",
|
||||
"matchit",
|
||||
"memchr",
|
||||
|
@ -135,8 +135,8 @@ dependencies = [
|
|||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"http",
|
||||
"http-body",
|
||||
"mime",
|
||||
"rustversion",
|
||||
"tower-layer",
|
||||
|
@ -164,12 +164,6 @@ version = "0.21.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -534,7 +528,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"http",
|
||||
"indexmap 2.2.6",
|
||||
"slab",
|
||||
"tokio",
|
||||
|
@ -560,7 +554,7 @@ version = "7.5.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64",
|
||||
"byteorder",
|
||||
"flate2",
|
||||
"nom",
|
||||
|
@ -590,17 +584,6 @@ dependencies = [
|
|||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
|
@ -608,30 +591,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 0.2.12",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
|
@ -664,8 +624,8 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
|
@ -677,25 +637,6 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"httparse",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.24.2"
|
||||
|
@ -703,28 +644,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.28",
|
||||
"rustls 0.21.12",
|
||||
"http",
|
||||
"hyper",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-rustls 0.24.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"hyper 1.3.1",
|
||||
"hyper-util",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tower-service",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -733,32 +657,12 @@ version = "0.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
|
||||
dependencies = [
|
||||
"hyper 0.14.28",
|
||||
"hyper",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-io-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"hyper 1.3.1",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
|
@ -867,14 +771,14 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
|||
|
||||
[[package]]
|
||||
name = "linkleaner"
|
||||
version = "1.9.2"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"console-subscriber",
|
||||
"dotenvy",
|
||||
"futures",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"reqwest 0.12.4",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
@ -1221,16 +1125,16 @@ version = "0.11.27"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.28",
|
||||
"hyper-rustls 0.24.2",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
|
@ -1239,15 +1143,15 @@ dependencies = [
|
|||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls 0.21.12",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-rustls 0.24.1",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
"url",
|
||||
|
@ -1255,49 +1159,8 @@ dependencies = [
|
|||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"webpki-roots 0.25.4",
|
||||
"winreg 0.50.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
|
||||
dependencies = [
|
||||
"base64 0.22.0",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body-util",
|
||||
"hyper 1.3.1",
|
||||
"hyper-rustls 0.26.0",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pemfile 2.1.2",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots 0.26.1",
|
||||
"winreg 0.52.0",
|
||||
"webpki-roots",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1338,49 +1201,19 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
|||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-webpki 0.101.7",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.3",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
|
||||
dependencies = [
|
||||
"base64 0.22.0",
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.7"
|
||||
|
@ -1391,17 +1224,6 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.15"
|
||||
|
@ -1546,12 +1368,6 @@ version = "0.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
@ -1655,7 +1471,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"pin-project",
|
||||
"rc-box",
|
||||
"reqwest 0.11.27",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
|
@ -1770,18 +1586,7 @@ version = "0.24.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||
dependencies = [
|
||||
"rustls 0.21.12",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
||||
dependencies = [
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -1819,12 +1624,12 @@ dependencies = [
|
|||
"async-stream",
|
||||
"async-trait",
|
||||
"axum",
|
||||
"base64 0.21.7",
|
||||
"base64",
|
||||
"bytes",
|
||||
"h2",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.28",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-timeout",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
|
@ -1875,7 +1680,6 @@ version = "0.1.40"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
|
@ -2127,15 +1931,6 @@ version = "0.25.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -2307,16 +2102,6 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.32"
|
||||
|
@ -2336,9 +2121,3 @@ dependencies = [
|
|||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "linkleaner"
|
||||
version = "1.9.2"
|
||||
version = "2.0.0"
|
||||
authors = ["Harsh Shandilya <me@msfjarvis.dev>"]
|
||||
edition = "2021"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
|
13
flake.nix
13
flake.nix
|
@ -62,11 +62,11 @@
|
|||
});
|
||||
linkleaner-clippy = craneLib.cargoClippy (commonArgs
|
||||
// {
|
||||
cargoArtifacts = linkleaner-fmt;
|
||||
inherit cargoArtifacts;
|
||||
});
|
||||
linkleaner = craneLib.buildPackage (commonArgs
|
||||
// {
|
||||
cargoArtifacts = linkleaner-clippy;
|
||||
inherit cargoArtifacts;
|
||||
doCheck = false;
|
||||
});
|
||||
linkleaner-nextest = craneLib.cargoNextest (commonArgs
|
||||
|
@ -77,8 +77,7 @@
|
|||
});
|
||||
linkleaner-audit = craneLib.cargoAudit (commonArgs
|
||||
// {
|
||||
inherit advisory-db;
|
||||
cargoArtifacts = linkleaner;
|
||||
inherit advisory-db cargoArtifacts;
|
||||
});
|
||||
in {
|
||||
checks = {
|
||||
|
@ -97,6 +96,12 @@
|
|||
};
|
||||
config.Cmd = ["${linkleaner}/bin/linkleaner"];
|
||||
};
|
||||
packages.ghContainer = pkgs.dockerTools.buildLayeredImage {
|
||||
name = "ghcr.io/msfjarvis/linkleaner";
|
||||
tag = "latest";
|
||||
created = "now";
|
||||
config.Cmd = ["${linkleaner}/bin/linkleaner"];
|
||||
};
|
||||
|
||||
apps.default = flake-utils.lib.mkApp {drv = linkleaner;};
|
||||
|
||||
|
|
205
src/commands.rs
205
src/commands.rs
|
@ -1,15 +1,21 @@
|
|||
use crate::{message::BotExt, utils::parse_bool};
|
||||
use crate::{
|
||||
fixer::FixerState,
|
||||
message::BotExt,
|
||||
utils::{parse_bool, AsyncError},
|
||||
FIXER_STATE,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{env, error::Error, marker::Send};
|
||||
use std::env;
|
||||
use teloxide::{
|
||||
payloads::SendMessageSetters,
|
||||
prelude::Requester,
|
||||
types::{Message, UserId},
|
||||
types::{ChatAction, Message, UserId},
|
||||
utils::command::BotCommands,
|
||||
Bot,
|
||||
};
|
||||
|
||||
pub(crate) type FilterState = String;
|
||||
|
||||
static BOT_OWNER: Lazy<UserId> = Lazy::new(|| {
|
||||
let value = env::var("BOT_OWNER_ID").expect("BOT_OWNER_ID must be defined");
|
||||
let id = value
|
||||
|
@ -43,58 +49,127 @@ pub(crate) enum Command {
|
|||
YouTube { filter_state: FilterState },
|
||||
}
|
||||
|
||||
async fn check_authorized(bot: &Bot, message: &Message) -> Result<bool, AsyncError> {
|
||||
if message.chat.is_private() {
|
||||
return Ok(true);
|
||||
}
|
||||
let admins = bot.get_chat_administrators(message.chat.id).await?;
|
||||
let admins = admins.iter().map(|c| c.user.clone()).collect::<Vec<_>>();
|
||||
let from = message.from().ok_or("No user found")?;
|
||||
Ok(from.id == *BOT_OWNER || admins.contains(from))
|
||||
}
|
||||
|
||||
fn update_fixer_state<F>(message: &Message, update_state: F)
|
||||
where
|
||||
F: FnOnce(&mut FixerState) + Copy,
|
||||
{
|
||||
if let Ok(ref mut map) = FIXER_STATE.try_lock() {
|
||||
map.entry(message.chat.id)
|
||||
.and_modify(update_state)
|
||||
.or_insert_with(|| {
|
||||
let mut state = FixerState::default();
|
||||
update_state(&mut state);
|
||||
state
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fixer_state<F>(message: &Message, get_state: F) -> &str
|
||||
where
|
||||
F: FnOnce(&FixerState) -> bool + Copy,
|
||||
{
|
||||
if let Ok(ref mut map) = FIXER_STATE.try_lock() {
|
||||
let state = map.entry(message.chat.id).or_insert(FixerState::default());
|
||||
if get_state(state) {
|
||||
return "enabled";
|
||||
}
|
||||
}
|
||||
"disabled"
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)] // Problem for another day
|
||||
pub(crate) async fn handler(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
command: Command,
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
) -> Result<(), AsyncError> {
|
||||
match command {
|
||||
Command::Help | Command::Start => {
|
||||
bot.send_chat_message(message, Command::descriptions().to_string())
|
||||
bot.send_chat_message(&message, Command::descriptions().to_string())
|
||||
.await?;
|
||||
}
|
||||
Command::Ping => {
|
||||
bot.send_chat_message(message, "Pong".to_string()).await?;
|
||||
bot.send_chat_message(&message, "Pong".to_string()).await?;
|
||||
}
|
||||
#[cfg(feature = "ddinstagram")]
|
||||
Command::Instagram { filter_state } => {
|
||||
if let Some(from) = message.from()
|
||||
&& from.id != *BOT_OWNER
|
||||
{
|
||||
bot.send_chat_message(
|
||||
message,
|
||||
"You are not authorized for this action".to_string(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
if check_authorized(&bot, &message).await? {
|
||||
match parse_bool(&filter_state) {
|
||||
Ok(filter_state) => {
|
||||
crate::instagram::set_filter_state(bot, message, filter_state).await?;
|
||||
update_fixer_state(&message, |x| x.instagram(filter_state));
|
||||
let state = if filter_state { "enabled" } else { "disabled" };
|
||||
bot.send_chat_message(
|
||||
&message,
|
||||
format!("Instagram link replacement is now {state}"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Err(error_message) => {
|
||||
bot.send_chat_message(message, error_message).await?;
|
||||
if filter_state.is_empty() {
|
||||
bot.send_chat_message(
|
||||
&message,
|
||||
format!(
|
||||
"Instagram link replacement is {}",
|
||||
get_fixer_state(&message, |x| x.instagram)
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
bot.send_chat_message(&message, error_message).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(message.chat.id, "You are not authorized for this action")
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Command::Medium { filter_state } => {
|
||||
if let Some(from) = message.from()
|
||||
&& from.id != *BOT_OWNER
|
||||
{
|
||||
bot.send_chat_message(
|
||||
message,
|
||||
"You are not authorized for this action".to_string(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
if check_authorized(&bot, &message).await? {
|
||||
match parse_bool(&filter_state) {
|
||||
Ok(filter_state) => {
|
||||
crate::medium::set_filter_state(bot, message, filter_state).await?;
|
||||
update_fixer_state(&message, |x| x.medium(filter_state));
|
||||
let state = if filter_state { "enabled" } else { "disabled" };
|
||||
bot.send_chat_message(
|
||||
&message,
|
||||
format!("Medium link replacement is now {state}"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Err(error_message) => {
|
||||
bot.send_chat_message(message, error_message).await?;
|
||||
if filter_state.is_empty() {
|
||||
bot.send_chat_message(
|
||||
&message,
|
||||
format!(
|
||||
"Medium link replacement is {}",
|
||||
get_fixer_state(&message, |x| x.medium)
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
bot.send_chat_message(&message, error_message).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(message.chat.id, "You are not authorized for this action")
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Command::Ttv { names } => {
|
||||
|
@ -104,43 +179,73 @@ pub(crate) async fn handler(
|
|||
.await?;
|
||||
}
|
||||
Command::Twitter { filter_state } => {
|
||||
if let Some(from) = message.from()
|
||||
&& from.id != *BOT_OWNER
|
||||
{
|
||||
bot.send_chat_message(
|
||||
message,
|
||||
"You are not authorized for this action".to_string(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
if check_authorized(&bot, &message).await? {
|
||||
match parse_bool(&filter_state) {
|
||||
Ok(filter_state) => {
|
||||
crate::twitter::set_filter_state(bot, message, filter_state).await?;
|
||||
update_fixer_state(&message, |x| x.twitter(filter_state));
|
||||
let state = if filter_state { "enabled" } else { "disabled" };
|
||||
bot.send_chat_message(
|
||||
&message,
|
||||
format!("Twitter link replacement is now {state}"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Err(error_message) => {
|
||||
bot.send_chat_message(message, error_message).await?;
|
||||
if filter_state.is_empty() {
|
||||
bot.send_chat_message(
|
||||
&message,
|
||||
format!(
|
||||
"Twitter link replacement is {}",
|
||||
get_fixer_state(&message, |x| x.twitter)
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
bot.send_chat_message(&message, error_message).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(message.chat.id, "You are not authorized for this action")
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Command::YouTube { filter_state } => {
|
||||
if let Some(from) = message.from()
|
||||
&& from.id != *BOT_OWNER
|
||||
{
|
||||
bot.send_chat_message(
|
||||
message,
|
||||
"You are not authorized for this action".to_string(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
if check_authorized(&bot, &message).await? {
|
||||
match parse_bool(&filter_state) {
|
||||
Ok(filter_state) => {
|
||||
crate::youtube::set_filter_state(bot, message, filter_state).await?;
|
||||
update_fixer_state(&message, |x| x.youtube(filter_state));
|
||||
let state = if filter_state { "enabled" } else { "disabled" };
|
||||
bot.send_chat_message(
|
||||
&message,
|
||||
format!("YouTube link replacement is now {state}"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Err(error_message) => {
|
||||
bot.send_chat_message(message, error_message).await?;
|
||||
if filter_state.is_empty() {
|
||||
bot.send_chat_message(
|
||||
&message,
|
||||
format!(
|
||||
"YouTube link replacement is {}",
|
||||
get_fixer_state(&message, |x| x.youtube)
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
bot.send_chat_message(&message, error_message).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(message.chat.id, "You are not authorized for this action")
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
mod model;
|
||||
|
||||
use crate::{message::BotExt, utils::get_urls_from_message};
|
||||
use crate::{
|
||||
message::BotExt,
|
||||
utils::{get_urls_from_message, AsyncError},
|
||||
};
|
||||
use model::AMPResponse;
|
||||
use reqwest::Url;
|
||||
use std::{error::Error, str::FromStr};
|
||||
use std::str::FromStr;
|
||||
use teloxide::{prelude::Requester, types::Message, utils::html::link, Bot};
|
||||
use tracing::debug;
|
||||
|
||||
|
@ -13,10 +16,7 @@ fn deserialize_amp_response(text: &str) -> Result<AMPResponse, serde_json::Error
|
|||
serde_json::from_str(text)
|
||||
}
|
||||
|
||||
pub async fn handler(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
pub async fn handler(bot: Bot, message: Message) -> Result<(), AsyncError> {
|
||||
if let Some(text) = message.text()
|
||||
&& let Some(user) = message.from()
|
||||
{
|
||||
|
@ -38,7 +38,7 @@ pub async fn handler(
|
|||
}
|
||||
let text = format!("{}: {}", link(user.url().as_str(), &user.full_name()), text);
|
||||
let _del = bot.delete_message(message.chat.id, message.id).await;
|
||||
bot.try_reply(message, text).await?;
|
||||
bot.try_reply(&message, text).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#[allow(clippy::struct_excessive_bools)] // Does not apply
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct FixerState {
|
||||
pub(crate) instagram: bool,
|
||||
pub(crate) medium: bool,
|
||||
pub(crate) twitter: bool,
|
||||
pub(crate) youtube: bool,
|
||||
}
|
||||
|
||||
impl Default for FixerState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
instagram: true,
|
||||
medium: true,
|
||||
twitter: true,
|
||||
youtube: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FixerState {
|
||||
pub(crate) fn instagram(&mut self, value: bool) {
|
||||
self.instagram = value;
|
||||
}
|
||||
|
||||
pub(crate) fn medium(&mut self, value: bool) {
|
||||
self.medium = value;
|
||||
}
|
||||
|
||||
pub(crate) fn twitter(&mut self, value: bool) {
|
||||
self.twitter = value;
|
||||
}
|
||||
|
||||
pub(crate) fn youtube(&mut self, value: bool) {
|
||||
self.youtube = value;
|
||||
}
|
||||
}
|
|
@ -1,17 +1,10 @@
|
|||
use crate::{message::BotExt, utils::scrub_urls};
|
||||
use crate::{
|
||||
message::BotExt,
|
||||
utils::{scrub_urls, AsyncError},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
use teloxide::{
|
||||
payloads::SendMessageSetters,
|
||||
prelude::Requester,
|
||||
types::{ChatAction, Message},
|
||||
utils::html::link,
|
||||
Bot,
|
||||
};
|
||||
use teloxide::{prelude::Requester, types::Message, utils::html::link, Bot};
|
||||
|
||||
const HOST_MATCH_GROUP: &str = "host";
|
||||
|
||||
|
@ -19,49 +12,7 @@ pub static MATCH_REGEX: Lazy<Regex> = Lazy::new(|| {
|
|||
Regex::new("https://(?:www.)?(?P<host>instagram.com)/(p|reel|tv)/[A-Za-z0-9]+.*/").unwrap()
|
||||
});
|
||||
|
||||
pub static FILTER_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
pub async fn set_filter_state(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
filter_state: Option<bool>,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
match filter_state {
|
||||
None => {
|
||||
let state = if FILTER_ENABLED.load(Ordering::Relaxed) {
|
||||
"enabled"
|
||||
} else {
|
||||
"disabled"
|
||||
};
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
format!("Instagram link replacement is {state}"),
|
||||
)
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
Some(state) => {
|
||||
FILTER_ENABLED.store(state, Ordering::Relaxed);
|
||||
let state = if state { "enabled" } else { "disabled" };
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
format!("Instagram link replacement has been {state}"),
|
||||
)
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handler(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
pub async fn handler(bot: Bot, message: Message) -> Result<(), AsyncError> {
|
||||
if let Some(text) = scrub_urls(&message)
|
||||
&& let Some(user) = message.from()
|
||||
&& let Some(caps) = MATCH_REGEX.captures(&text)
|
||||
|
@ -69,7 +20,7 @@ pub async fn handler(
|
|||
let text = text.replace(&caps[HOST_MATCH_GROUP], "ddinstagram.com");
|
||||
let text = format!("{}: {}", link(user.url().as_str(), &user.full_name()), text);
|
||||
let _del = bot.delete_message(message.chat.id, message.id).await;
|
||||
bot.try_reply(message, text).await?;
|
||||
bot.try_reply(&message, text).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
98
src/main.rs
98
src/main.rs
|
@ -1,6 +1,7 @@
|
|||
#![feature(let_chains)]
|
||||
mod commands;
|
||||
mod deamp;
|
||||
mod fixer;
|
||||
#[cfg(feature = "ddinstagram")]
|
||||
mod instagram;
|
||||
mod logging;
|
||||
|
@ -13,16 +14,23 @@ mod youtube;
|
|||
use crate::commands::Command;
|
||||
use crate::logging::TeloxideLogger;
|
||||
use dotenvy::dotenv;
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
use fixer::FixerState;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use teloxide::{
|
||||
dispatching::{HandlerExt, UpdateFilterExt},
|
||||
dispatching::{dialogue::GetChatId, HandlerExt, UpdateFilterExt},
|
||||
dptree,
|
||||
prelude::Dispatcher,
|
||||
types::{Message, Update},
|
||||
types::{ChatId, Message, Update},
|
||||
update_listeners::Polling,
|
||||
Bot,
|
||||
};
|
||||
|
||||
pub(crate) static FIXER_STATE: Lazy<Mutex<HashMap<ChatId, FixerState>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
const REPLACE_SKIP_TOKEN: &str = "#skip";
|
||||
|
||||
async fn run() {
|
||||
|
@ -42,51 +50,77 @@ async fn run() {
|
|||
)
|
||||
.branch(
|
||||
dptree::filter(|msg: Message| {
|
||||
twitter::FILTER_ENABLED.load(Ordering::Relaxed)
|
||||
&& msg
|
||||
.text()
|
||||
.map(|text| {
|
||||
twitter::MATCH_REGEX.is_match(text)
|
||||
&& !text.contains(REPLACE_SKIP_TOKEN)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
if let Ok(ref mut map) = FIXER_STATE.try_lock()
|
||||
&& let Some(chat_id) = msg.chat_id()
|
||||
{
|
||||
let state = map.entry(chat_id).or_insert(FixerState::default());
|
||||
return state.twitter
|
||||
&& msg
|
||||
.text()
|
||||
.map(|text| {
|
||||
twitter::MATCH_REGEX.is_match(text)
|
||||
&& !text.contains(REPLACE_SKIP_TOKEN)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
}
|
||||
false
|
||||
})
|
||||
.endpoint(twitter::handler),
|
||||
);
|
||||
#[cfg(feature = "ddinstagram")]
|
||||
let handler = handler.branch(
|
||||
dptree::filter(|msg: Message| {
|
||||
instagram::FILTER_ENABLED.load(Ordering::Relaxed)
|
||||
&& msg
|
||||
.text()
|
||||
.map(|text| {
|
||||
instagram::MATCH_REGEX.is_match(text) && !text.contains(REPLACE_SKIP_TOKEN)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
if let Ok(ref mut map) = FIXER_STATE.try_lock()
|
||||
&& let Some(chat_id) = msg.chat_id()
|
||||
{
|
||||
let state = map.entry(chat_id).or_insert(FixerState::default());
|
||||
return state.instagram
|
||||
&& msg
|
||||
.text()
|
||||
.map(|text| {
|
||||
instagram::MATCH_REGEX.is_match(text)
|
||||
&& !text.contains(REPLACE_SKIP_TOKEN)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
}
|
||||
false
|
||||
})
|
||||
.endpoint(instagram::handler),
|
||||
);
|
||||
let handler = handler.branch(
|
||||
dptree::filter(|msg: Message| {
|
||||
youtube::FILTER_ENABLED.load(Ordering::Relaxed)
|
||||
&& msg
|
||||
.text()
|
||||
.map(|text| {
|
||||
youtube::MATCH_REGEX.is_match(text) && !text.contains(REPLACE_SKIP_TOKEN)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
if let Ok(ref mut map) = FIXER_STATE.try_lock()
|
||||
&& let Some(chat_id) = msg.chat_id()
|
||||
{
|
||||
let state = map.entry(chat_id).or_insert(FixerState::default());
|
||||
return state.youtube
|
||||
&& msg
|
||||
.text()
|
||||
.map(|text| {
|
||||
youtube::MATCH_REGEX.is_match(text)
|
||||
&& !text.contains(REPLACE_SKIP_TOKEN)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
}
|
||||
false
|
||||
})
|
||||
.endpoint(youtube::handler),
|
||||
);
|
||||
let handler = handler.branch(
|
||||
dptree::filter(|msg: Message| {
|
||||
medium::FILTER_ENABLED.load(Ordering::Relaxed)
|
||||
&& msg
|
||||
.text()
|
||||
.map(|text| {
|
||||
medium::MATCH_REGEX.is_match(text) && !text.contains(REPLACE_SKIP_TOKEN)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
if let Ok(ref mut map) = FIXER_STATE.try_lock()
|
||||
&& let Some(chat_id) = msg.chat_id()
|
||||
{
|
||||
let state = map.entry(chat_id).or_insert(FixerState::default());
|
||||
return state.medium
|
||||
&& msg
|
||||
.text()
|
||||
.map(|text| {
|
||||
medium::MATCH_REGEX.is_match(text) && !text.contains(REPLACE_SKIP_TOKEN)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
}
|
||||
false
|
||||
})
|
||||
.endpoint(medium::handler),
|
||||
);
|
||||
|
|
|
@ -1,66 +1,17 @@
|
|||
use crate::{message::BotExt, utils::scrub_urls};
|
||||
use crate::{
|
||||
message::BotExt,
|
||||
utils::{scrub_urls, AsyncError},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
use teloxide::{
|
||||
payloads::SendMessageSetters,
|
||||
prelude::Requester,
|
||||
types::{ChatAction, Message},
|
||||
utils::html::link,
|
||||
Bot,
|
||||
};
|
||||
use teloxide::{prelude::Requester, types::Message, utils::html::link, Bot};
|
||||
|
||||
const HOST_MATCH_GROUP: &str = "host";
|
||||
|
||||
pub static MATCH_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new("https://(?P<host>(?:.*)?medium.com)/.*").unwrap());
|
||||
|
||||
pub static FILTER_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
pub async fn set_filter_state(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
filter_state: Option<bool>,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
match filter_state {
|
||||
None => {
|
||||
let state = if FILTER_ENABLED.load(Ordering::Relaxed) {
|
||||
"enabled"
|
||||
} else {
|
||||
"disabled"
|
||||
};
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
format!("Medium link replacement is {state}"),
|
||||
)
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
Some(state) => {
|
||||
FILTER_ENABLED.store(state, Ordering::Relaxed);
|
||||
let state = if state { "enabled" } else { "disabled" };
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
format!("Medium link replacement has been {state}"),
|
||||
)
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handler(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
pub async fn handler(bot: Bot, message: Message) -> Result<(), AsyncError> {
|
||||
if let Some(text) = scrub_urls(&message)
|
||||
&& let Some(user) = message.from()
|
||||
&& let Some(caps) = MATCH_REGEX.captures(&text)
|
||||
|
@ -68,7 +19,7 @@ pub async fn handler(
|
|||
let text = text.replace(&caps[HOST_MATCH_GROUP], "medium.rip");
|
||||
let text = format!("{}: {}", link(user.url().as_str(), &user.full_name()), text);
|
||||
let _del = bot.delete_message(message.chat.id, message.id).await;
|
||||
bot.try_reply(message, text).await?;
|
||||
bot.try_reply(&message, text).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -6,16 +6,16 @@ use teloxide::{
|
|||
};
|
||||
|
||||
pub(crate) trait BotExt {
|
||||
async fn try_reply(&self, message: Message, text: String) -> Result<Message, RequestError>;
|
||||
async fn try_reply(&self, message: &Message, text: String) -> Result<Message, RequestError>;
|
||||
async fn send_chat_message(
|
||||
&self,
|
||||
message: Message,
|
||||
message: &Message,
|
||||
text: String,
|
||||
) -> Result<Message, RequestError>;
|
||||
}
|
||||
|
||||
impl BotExt for Bot {
|
||||
async fn try_reply(&self, message: Message, text: String) -> Result<Message, RequestError> {
|
||||
async fn try_reply(&self, message: &Message, text: String) -> Result<Message, RequestError> {
|
||||
if let Some(reply) = message.reply_to_message() {
|
||||
self.send_message(message.chat.id, text)
|
||||
.reply_to_message_id(reply.id)
|
||||
|
@ -30,7 +30,7 @@ impl BotExt for Bot {
|
|||
|
||||
async fn send_chat_message(
|
||||
&self,
|
||||
message: Message,
|
||||
message: &Message,
|
||||
text: String,
|
||||
) -> Result<Message, RequestError> {
|
||||
self.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
use crate::{message::BotExt, utils::scrub_urls};
|
||||
use crate::{
|
||||
message::BotExt,
|
||||
utils::{scrub_urls, AsyncError},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
use teloxide::{
|
||||
payloads::SendMessageSetters,
|
||||
prelude::Requester,
|
||||
types::{ChatAction, Message},
|
||||
utils::html::link,
|
||||
Bot,
|
||||
};
|
||||
use teloxide::{prelude::Requester, types::Message, utils::html::link, Bot};
|
||||
|
||||
const HOST_MATCH_GROUP: &str = "host";
|
||||
const ROOT_MATCH_GROUP: &str = "root";
|
||||
|
@ -21,49 +14,7 @@ pub static MATCH_REGEX: Lazy<Regex> = Lazy::new(|| {
|
|||
.unwrap()
|
||||
});
|
||||
|
||||
pub static FILTER_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
pub async fn set_filter_state(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
filter_state: Option<bool>,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
match filter_state {
|
||||
None => {
|
||||
let state = if FILTER_ENABLED.load(Ordering::Relaxed) {
|
||||
"enabled"
|
||||
} else {
|
||||
"disabled"
|
||||
};
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
format!("Twitter link replacement is {state}"),
|
||||
)
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
Some(state) => {
|
||||
FILTER_ENABLED.store(state, Ordering::Relaxed);
|
||||
let state = if state { "enabled" } else { "disabled" };
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
format!("Twitter link replacement has been {state}"),
|
||||
)
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handler(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
pub async fn handler(bot: Bot, message: Message) -> Result<(), AsyncError> {
|
||||
if let Some(text) = scrub_urls(&message)
|
||||
&& let Some(user) = message.from()
|
||||
&& let Some(caps) = MATCH_REGEX.captures(&text)
|
||||
|
@ -78,7 +29,7 @@ pub async fn handler(
|
|||
};
|
||||
let text = format!("{}: {}", link(user.url().as_str(), &user.full_name()), text);
|
||||
let _del = bot.delete_message(message.chat.id, message.id).await;
|
||||
bot.try_reply(message, text).await?;
|
||||
bot.try_reply(&message, text).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
10
src/utils.rs
10
src/utils.rs
|
@ -1,8 +1,11 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use reqwest::Url;
|
||||
use std::error::Error;
|
||||
use teloxide::types::{Message, MessageEntityKind};
|
||||
use tracing::{error, info};
|
||||
|
||||
pub(crate) type AsyncError = Box<dyn Error + Send + Sync + 'static>;
|
||||
|
||||
pub(crate) fn get_urls_from_message(msg: &Message) -> Vec<String> {
|
||||
if let Some(entities) = msg.entities()
|
||||
&& !entities.is_empty()
|
||||
|
@ -51,7 +54,7 @@ pub(crate) fn scrub_urls(msg: &Message) -> Option<String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_bool(input: &str) -> Result<Option<bool>, String> {
|
||||
pub(crate) fn parse_bool(input: &str) -> Result<bool, String> {
|
||||
const TRUE_VALUES: [&str; 4] = ["true", "on", "yes", "enable"];
|
||||
const FALSE_VALUES: [&str; 4] = ["false", "off", "no", "disable"];
|
||||
static EXPECTED_VALUES: Lazy<String> = Lazy::new(|| {
|
||||
|
@ -72,9 +75,8 @@ pub(crate) fn parse_bool(input: &str) -> Result<Option<bool>, String> {
|
|||
}
|
||||
|
||||
match input[0].to_lowercase().as_str() {
|
||||
arg if TRUE_VALUES.contains(&arg) => Ok(Some(true)),
|
||||
arg if FALSE_VALUES.contains(&arg) => Ok(Some(false)),
|
||||
"" => Ok(None),
|
||||
arg if TRUE_VALUES.contains(&arg) => Ok(true),
|
||||
arg if FALSE_VALUES.contains(&arg) => Ok(false),
|
||||
arg => {
|
||||
let message = format!(
|
||||
"Unexpected argument '{arg}'. Expected one of: {}.",
|
||||
|
|
|
@ -1,65 +1,16 @@
|
|||
use crate::{message::BotExt, utils::scrub_urls};
|
||||
use crate::{
|
||||
message::BotExt,
|
||||
utils::{scrub_urls, AsyncError},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
use teloxide::{
|
||||
payloads::SendMessageSetters,
|
||||
prelude::Requester,
|
||||
types::{ChatAction, Message},
|
||||
utils::html::link,
|
||||
Bot,
|
||||
};
|
||||
use teloxide::{prelude::Requester, types::Message, utils::html::link, Bot};
|
||||
|
||||
pub static MATCH_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new("https://(?:www.)?youtube.com/(?P<shorts>shorts/)[A-Za-z0-9-_]{11}.*").unwrap()
|
||||
});
|
||||
|
||||
pub static FILTER_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
pub async fn set_filter_state(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
filter_state: Option<bool>,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
match filter_state {
|
||||
None => {
|
||||
let state = if FILTER_ENABLED.load(Ordering::Relaxed) {
|
||||
"enabled"
|
||||
} else {
|
||||
"disabled"
|
||||
};
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
format!("YouTube link replacement is {state}"),
|
||||
)
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
Some(state) => {
|
||||
FILTER_ENABLED.store(state, Ordering::Relaxed);
|
||||
let state = if state { "enabled" } else { "disabled" };
|
||||
bot.send_chat_action(message.chat.id, ChatAction::Typing)
|
||||
.await?;
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
format!("YouTube link replacement has been {state}"),
|
||||
)
|
||||
.reply_to_message_id(message.id)
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handler(
|
||||
bot: Bot,
|
||||
message: Message,
|
||||
) -> Result<(), Box<dyn Error + Sync + Send + 'static>> {
|
||||
pub async fn handler(bot: Bot, message: Message) -> Result<(), AsyncError> {
|
||||
if let Some(text) = scrub_urls(&message)
|
||||
&& let Some(user) = message.from()
|
||||
&& let Some(caps) = MATCH_REGEX.captures(&text)
|
||||
|
@ -67,7 +18,7 @@ pub async fn handler(
|
|||
let text = text.replace(&caps["shorts"], "watch?v=");
|
||||
let text = format!("{}: {}", link(user.url().as_str(), &user.full_name()), text);
|
||||
let _del = bot.delete_message(message.chat.id, message.id).await;
|
||||
bot.try_reply(message, text).await?;
|
||||
bot.try_reply(&message, text).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue