Compare commits

...

40 commits

Author SHA1 Message Date
Yash Karandikar d547d12925 Refactor if-let chain and support NOTICEs 2023-06-12 13:15:14 -05:00
Yash Karandikar f5705ed296 Fix pinging unknown users 2023-04-25 14:12:37 -05:00
Yash Karandikar 722eb46756 I Love Cargo Clippy 2023-04-22 19:12:46 -05:00
Yash Karandikar 8828a0c44f Fix raw prefix parsing 2023-04-17 22:33:12 -05:00
Yash Karandikar e6cc8c613c The Markdown Update part 2 2023-04-17 21:40:02 -05:00
Yash Karandikar 0e18740c43 Support markdown links 2023-04-16 19:54:35 -05:00
Yash Karandikar 95f292c8cd Relay IRC kicks 2023-03-13 14:13:55 -05:00
Yash Karandikar 91d72ca3b6 Fix clippy warnings 2023-03-04 21:11:27 -06:00
Yash Karandikar 69deba3493 Allow pinging discord users based on nick or username 2023-03-04 21:03:09 -06:00
Yash Karandikar ee832eb515 Oops 2022-08-28 16:05:54 -05:00
Yash Karandikar f503d0103e Port it to the raw message sender lol 2022-08-28 16:03:08 -05:00
Yash Karandikar 476bcf392a Don't panic on empty messages 2022-08-28 14:40:12 -05:00
Yash Karandikar 7ec420b23a Rename avatar_ttl to cache_ttl 2022-08-05 11:50:10 -05:00
Yash Karandikar b4551d68d4 Handle custom emojis on irc->discord 2022-08-05 00:29:37 -05:00
Yash Karandikar 449543678d Fix emoji handling 2022-08-04 23:53:29 -05:00
Yash Karandikar c3898644be Add sample config 2022-08-02 19:27:03 -05:00
Yash Karandikar a03899f3f8 Make avatar TTL configurable 2022-08-02 19:18:20 -05:00
Yash Karandikar 3979987b3a Expire avatar cache after a certain amount of time 2022-08-02 19:11:18 -05:00
Yash Karandikar fb4987dc1f Revert "Don't cache avatar_url"
This reverts commit 26c8fd41c0.
2022-08-02 19:05:33 -05:00
Yash Karandikar 12d5076e28 Make reply content limit configurable 2022-08-02 18:57:43 -05:00
Yash Karandikar 331ef2db6f Remove \u{f} from the end of a message 2022-07-29 18:45:36 -05:00
Yash Karandikar 226043f67b Handle member removal 2022-07-28 18:36:32 -05:00
Yash Karandikar 8965be4a8b Use ellipse crate for truncation 2022-07-28 18:32:17 -05:00
Yash Karandikar 5fb4e69b6d Strip away discord's "link embed escape" syntax 2022-07-25 19:05:03 -05:00
Yash Karandikar 245cb167bb Add LICENSE 2022-07-24 13:06:58 -05:00
Yash Karandikar 4ea5c30209 Update crates to latest version 2022-07-24 11:38:19 -05:00
Yash Karandikar 26c8fd41c0 Don't cache avatar_url 2022-07-24 11:12:07 -05:00
Yash Karandikar 946e9f8b84 Enable GUILD_MEMBERS intent 2022-07-23 21:37:10 -05:00
Yash Karandikar c9bb6843d1 Don't allow here and everyone pings 2022-07-16 16:20:50 -04:00
Yash Karandikar 36c6488fa0 Mirror IRC topics to discord 2022-07-16 13:46:55 -04:00
Yash Karandikar 121f3da671 Separate attachments in reply with a space 2022-07-16 07:45:18 -04:00
Yash Karandikar 4e10398d62 Reformat 2022-07-12 14:43:06 +05:30
Yash Karandikar 0d79e8aed2 Don't crash dircord if it gets ratelimited 2022-07-12 14:43:06 +05:30
delta 0854a70100 cargo fmt 2022-07-11 09:07:43 +02:00
delta e0f896e23e added support for DIRCORD_POLARIAN_MODE compile time env var 2022-07-11 08:59:47 +02:00
delta ba49971dd0 EVERYONE IS POLARIAN NOW 2022-07-11 08:30:59 +02:00
Yash Karandikar 67500f8e98 Attempt to figure out why it crashes 2022-07-07 11:52:39 +05:30
Yash Karandikar f34c424425 Fix clippy warnings 2022-07-06 00:21:05 +05:30
Yash Karandikar 8e40803215 Process replied message 2022-07-05 23:51:02 +05:30
Yash Karandikar c025ee2291 Merge pull request 'fix stuff for large messages' (#22) from missing/dircord:master into master
Reviewed-on: karx/dircord#22
2022-06-18 00:30:54 -05:00
8 changed files with 660 additions and 340 deletions

458
Cargo.lock generated
View file

@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.52"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
[[package]]
name = "async-trait"
@ -36,15 +36,15 @@ dependencies = [
[[package]]
name = "async-tungstenite"
version = "0.11.0"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7cc5408453d37e2b1c6f01d8078af1da58b6cfa6a80fa2ede3bd2b9a6ada9c4"
checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb"
dependencies = [
"futures-io",
"futures-util",
"log",
"native-tls",
"pin-project",
"pin-project-lite",
"tokio",
"tokio-native-tls",
"tungstenite",
@ -52,15 +52,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "base64"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
@ -91,9 +85,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.9.0"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
]
@ -110,12 +104,6 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
[[package]]
name = "bytes"
version = "1.1.0"
@ -146,8 +134,7 @@ dependencies = [
"libc",
"num-integer",
"num-traits",
"serde",
"time",
"time 0.1.44",
"winapi",
]
@ -186,12 +173,36 @@ dependencies = [
]
[[package]]
name = "digest"
version = "0.9.0"
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "dashmap"
version = "5.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f"
dependencies = [
"cfg-if",
"hashbrown 0.12.3",
"lock_api",
"parking_lot_core 0.9.3",
"serde",
]
[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
@ -199,6 +210,7 @@ name = "dircord"
version = "0.1.0"
dependencies = [
"anyhow",
"ellipse",
"fancy-regex",
"irc",
"lazy_static",
@ -206,10 +218,20 @@ dependencies = [
"serde",
"serenity",
"tokio",
"tokio-stream",
"toml",
"vergen",
]
[[package]]
name = "ellipse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1835a82a08e5c9393639e7cf99786a65af71f7fa9df7c91a519f2d52e6fa052d"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "encoding"
version = "0.2.33"
@ -285,18 +307,18 @@ dependencies = [
[[package]]
name = "enum-iterator"
version = "0.7.0"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6"
checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401"
dependencies = [
"enum-iterator-derive",
]
[[package]]
name = "enum-iterator-derive"
version = "0.7.0"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159"
checksum = "b13f1e69590421890f90448c3cd5f554746a31adc6dc0dac406ec6901db8dc25"
dependencies = [
"proc-macro2",
"quote",
@ -440,17 +462,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.3"
@ -493,7 +504,7 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f072413d126e57991455e0a922b31e4c8ba7c2ffbebf6b78b4f8521397d65cd"
dependencies = [
"bytes 1.1.0",
"bytes",
"fnv",
"futures-core",
"futures-sink",
@ -512,6 +523,12 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -527,7 +544,7 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
dependencies = [
"bytes 1.1.0",
"bytes",
"fnv",
"itoa 0.4.8",
]
@ -538,7 +555,7 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
dependencies = [
"bytes 1.1.0",
"bytes",
"http",
"pin-project-lite",
]
@ -561,7 +578,7 @@ version = "0.14.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55"
dependencies = [
"bytes 1.1.0",
"bytes",
"futures-channel",
"futures-core",
"futures-util",
@ -585,7 +602,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes 1.1.0",
"bytes",
"hyper",
"native-tls",
"tokio",
@ -610,16 +627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "input_buffer"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754"
dependencies = [
"bytes 0.5.6",
"hashbrown 0.11.2",
]
[[package]]
@ -649,7 +657,7 @@ dependencies = [
"irc-proto",
"log",
"native-tls",
"parking_lot",
"parking_lot 0.11.2",
"pin-project",
"serde",
"serde_derive",
@ -667,7 +675,7 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55fa0a52d825e59ba8aea5b7503890245aea000f77e68d9b1903f3491fa33643"
dependencies = [
"bytes 1.1.0",
"bytes",
"encoding",
"thiserror",
"tokio",
@ -712,9 +720,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.112"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "libgit2-sys"
@ -742,10 +750,11 @@ dependencies = [
[[package]]
name = "lock_api"
version = "0.4.5"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
"autocfg",
"scopeguard",
]
@ -798,24 +807,14 @@ dependencies = [
[[package]]
name = "mio"
version = "0.7.14"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
]
[[package]]
@ -836,15 +835,6 @@ dependencies = [
"tempfile",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@ -874,18 +864,21 @@ dependencies = [
"libc",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.38"
@ -919,6 +912,15 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "ordered-float"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
dependencies = [
"num-traits",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
@ -927,7 +929,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
"parking_lot_core 0.8.5",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core 0.9.3",
]
[[package]]
@ -944,6 +956,19 @@ dependencies = [
"winapi",
]
[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -1020,11 +1045,11 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.34"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
@ -1041,26 +1066,13 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
]
[[package]]
name = "rand"
version = "0.8.4"
@ -1068,19 +1080,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
"rand_hc 0.3.1",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
@ -1090,16 +1092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.3",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.16",
"rand_core",
]
[[package]]
@ -1108,16 +1101,7 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom 0.2.3",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
"getrandom",
]
[[package]]
@ -1126,7 +1110,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core 0.6.3",
"rand_core",
]
[[package]]
@ -1170,8 +1154,8 @@ version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c4e0a76dc12a116108933f6301b95e83634e0c47b0afbed6abbaa0601e99258"
dependencies = [
"base64 0.13.0",
"bytes 1.1.0",
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
@ -1254,18 +1238,28 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.132"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.132"
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
dependencies = [
"proc-macro2",
"quote",
@ -1274,9 +1268,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.73"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [
"itoa 1.0.1",
"ryu",
@ -1297,22 +1291,28 @@ dependencies = [
[[package]]
name = "serenity"
version = "0.10.9"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6275d443266aedf2be507a245ddc23db0c07b1b99774e16f3c879e96a78b067a"
checksum = "6d1a6cef5e72d4e5787c96413ec0a45f9317c59f0099e2ff2038b73cb352fefd"
dependencies = [
"async-trait",
"async-tungstenite",
"base64 0.13.0",
"base64",
"bitflags",
"bytes 1.1.0",
"chrono",
"bytes",
"cfg-if",
"dashmap",
"flate2",
"futures",
"mime",
"mime_guess",
"parking_lot 0.12.1",
"percent-encoding",
"reqwest",
"serde",
"serde-value",
"serde_json",
"time 0.3.11",
"tokio",
"tracing",
"typemap_rev",
@ -1321,15 +1321,13 @@ dependencies = [
[[package]]
name = "sha-1"
version = "0.9.8"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
"block-buffer",
"cfg-if",
"cpufeatures",
"digest",
"opaque-debug",
]
[[package]]
@ -1355,9 +1353,9 @@ checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
[[package]]
name = "socket2"
version = "0.4.2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
@ -1365,13 +1363,13 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.84"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
@ -1382,7 +1380,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if",
"libc",
"rand 0.8.4",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
@ -1419,6 +1417,18 @@ dependencies = [
"winapi",
]
[[package]]
name = "time"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
dependencies = [
"itoa 1.0.1",
"libc",
"num_threads",
"serde",
]
[[package]]
name = "tinyvec"
version = "1.5.1"
@ -1436,19 +1446,21 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.15.0"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e"
dependencies = [
"bytes 1.1.0",
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"parking_lot",
"parking_lot 0.12.1",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"winapi",
]
@ -1476,9 +1488,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9"
dependencies = [
"futures-core",
"pin-project-lite",
@ -1491,7 +1503,7 @@ version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
dependencies = [
"bytes 1.1.0",
"bytes",
"futures-core",
"futures-sink",
"log",
@ -1501,9 +1513,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.5.8"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
@ -1555,20 +1567,20 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "tungstenite"
version = "0.11.1"
version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23"
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
dependencies = [
"base64 0.12.3",
"base64",
"byteorder",
"bytes 0.5.6",
"bytes",
"http",
"httparse",
"input_buffer",
"log",
"native-tls",
"rand 0.7.3",
"rand",
"sha-1",
"thiserror",
"url",
"utf-8",
]
@ -1600,6 +1612,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-ident"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
[[package]]
name = "unicode-normalization"
version = "0.1.19"
@ -1609,18 +1627,18 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "url"
version = "2.2.2"
@ -1631,6 +1649,7 @@ dependencies = [
"idna",
"matches",
"percent-encoding",
"serde",
]
[[package]]
@ -1647,18 +1666,18 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vergen"
version = "5.1.17"
version = "7.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cf88d94e969e7956d924ba70741316796177fa0c79a2c9f4ab04998d96e966e"
checksum = "f10de320f0fe3f21913dabbfcbced6867bbe47a6b1a5db830e37df3a50279bd0"
dependencies = [
"anyhow",
"cfg-if",
"chrono",
"enum-iterator",
"getset",
"git2",
"rustversion",
"thiserror",
"time 0.3.11",
]
[[package]]
@ -1677,18 +1696,18 @@ dependencies = [
"try-lock",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.78"
@ -1787,6 +1806,49 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "winreg"
version = "0.7.0"

View file

@ -6,24 +6,26 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.52"
irc = "0.15"
toml = "0.5"
serde = "1.0"
lazy_static = "1.4"
anyhow = "1.0.58"
irc = "0.15.0"
toml = "0.5.9"
serde = "1.0.140"
lazy_static = "1.4.0"
pulldown-cmark = "0.9.1"
fancy-regex = "0.10.0"
tokio-stream = "0.1.9"
ellipse = "0.2.0"
[dependencies.tokio]
version = "1.15.0"
version = "1.20.0"
features = ["full"]
[dependencies.serenity]
version = "0.10"
version = "0.11.4"
default-features = false
features = ["builder", "cache", "client", "gateway", "model", "utils", "native_tls_backend"]
[build-dependencies.vergen]
version = "5"
version = "7.3.1"
default-features = false
features = ["git"]

14
LICENSE Normal file
View file

@ -0,0 +1,14 @@
BSD Zero Clause License
Copyright (c) 2022 Yash Karandikar
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View file

@ -12,3 +12,5 @@ TODO:
- [x] handle multiple pings
- [x] multiple channels
- [x] IRC and Discord formatting
<sub>For extra fun set the DIRCORD_POLARIAN_MODE environmental variable to any value ;)</sub>

17
sample_config.toml Normal file
View file

@ -0,0 +1,17 @@
token = "..." # REQUIRED: discord bot token
nickname = "dircord" # REQUIRED: IRC nickname
server = "karx.xyz""
port = 6697
tls = true # OPTIONAL: DEFAULT: false
mode = "+B" # OPTIONAL: DEFAULT: none
raw_prefix = "++" # OPTIONAL: DEFAULT: ++
ref_content_limit = 512 # OPTIONAL: where to truncate replied messages. Defaults to ~512 minus the prefix
cache_ttl = 1800 # OPTIONAL: how long to store caches, in seconds. Defaults to 1800 (30 minutes)
[channels]
# irc channel name -> discord channel id
'#channel_name' = 1234
[webhooks] # OPTIONAL
# irc channel name -> discord webhook URL
'#channel_name' = '...'

View file

@ -1,6 +1,8 @@
use crate::{
regex, ChannelMappingKey, MembersKey, OptionReplacer, OptionStringKey, SenderKey, UserIdKey,
regex, ChannelMappingKey, MembersKey, OptionReplacer, OptionStringKey, RefContentLimitKey,
SenderKey, UserIdKey,
};
use ellipse::Ellipse;
use fancy_regex::{Captures, Replacer};
use pulldown_cmark::Parser;
use serenity::{
@ -10,12 +12,15 @@ use serenity::{
model::{
channel::{Channel, Message, MessageReference, MessageType},
guild::Member,
prelude::{ChannelId, GuildId, Ready, Role, RoleId},
id::GuildId,
prelude::{ChannelId, Ready, Role, RoleId},
user::User,
},
prelude::*,
};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;
struct StrChunks<'a> {
v: &'a str,
@ -59,13 +64,17 @@ impl<'a> StrChunks<'a> {
}
async fn create_prefix(msg: &Message, is_reply: bool, http: impl CacheHttp) -> (String, usize) {
let nick = match msg.member(http).await {
let mut nick = match msg.member(http).await {
Ok(Member {
nick: Some(nick), ..
}) => Cow::Owned(nick),
_ => Cow::Borrowed(&msg.author.name),
};
if option_env!("DIRCORD_POLARIAN_MODE").is_some() {
nick = Cow::Owned("polarbear".to_string());
}
let mut chars = nick.char_indices();
let first_char = chars.next().unwrap().1;
let second_char_offset = chars.next().unwrap().0;
@ -107,6 +116,7 @@ impl EventHandler for Handler {
.as_deref()
.unwrap_or("++");
let mapping = ctx_data.get::<ChannelMappingKey>().unwrap().clone();
let ref_content_limit = ctx_data.get::<RefContentLimitKey>().unwrap();
if user_id == msg.author.id || msg.author.bot {
return;
@ -134,7 +144,7 @@ impl EventHandler for Handler {
let members_lock = members.lock().await;
let computed = discord_to_irc_processing(&msg.content, &**members_lock, &ctx, &roles).await;
let computed = discord_to_irc_processing(&msg.content, &members_lock, &ctx, &roles).await;
if let Some(MessageReference {
guild_id,
@ -150,41 +160,37 @@ impl EventHandler for Handler {
let mut content = reply.content;
content = content.replace("\r\n", " "); // just in case
content = content.replace('\n', " ");
content = format!(
"{} {}",
content,
reply
.attachments
.iter()
.map(|a| &*a.url)
.collect::<String>()
let atts: Vec<&str> = reply.attachments.iter().map(|a| &*a.url).collect();
content = format!("{} {}", content, atts.join(" "));
content = discord_to_irc_processing(&content, &members_lock, &ctx, &roles).await;
let to_send = (&*content).truncate_ellipse(
ref_content_limit
.map(|l| l as usize)
.unwrap_or(reply_content_limit),
);
let to_send = if content.len() > reply_content_limit {
format!("{}...", &content[..reply_content_limit - 3])
} else {
content
};
sender
.send_privmsg(channel, &format!("{}{}", reply_prefix, to_send))
.send_privmsg(channel, format!("{reply_prefix}{to_send}"))
.unwrap();
}
}
if let Some((stripped, false)) = computed
.strip_prefix(&raw_prefix)
.strip_prefix(raw_prefix)
.map(str::trim)
.and_then(|v| v.strip_suffix('\x0F'))
.map(|v| (v, v.is_empty()))
{
let to_send = stripped.trim_matches('\u{f}');
sender.send_privmsg(channel, &prefix).unwrap();
sender.send_privmsg(channel, stripped).unwrap();
sender.send_privmsg(channel, to_send).unwrap();
} else {
for line in computed.lines() {
for chunk in StrChunks::new(line, content_limit) {
let to_send = chunk.trim_matches('\u{f}');
sender
.send_privmsg(channel, &format!("{}{}", prefix, chunk))
.send_privmsg(channel, &format!("{prefix}{to_send}"))
.unwrap();
}
}
@ -192,7 +198,7 @@ impl EventHandler for Handler {
for attachment in attachments {
sender
.send_privmsg(channel, &format!("{}{}", prefix, attachment))
.send_privmsg(channel, &format!("{prefix}{attachment}"))
.unwrap();
}
}
@ -204,7 +210,7 @@ impl EventHandler for Handler {
data.insert::<UserIdKey>(id);
}
async fn guild_member_addition(&self, ctx: Context, _: GuildId, new_member: Member) {
async fn guild_member_addition(&self, ctx: Context, new_member: Member) {
let ctx_data = ctx.data.read().await;
let mut members = ctx_data.get::<MembersKey>().unwrap().lock().await;
members.push(new_member);
@ -221,6 +227,20 @@ impl EventHandler for Handler {
members.remove(x);
members.push(new);
}
async fn guild_member_removal(
&self,
ctx: Context,
_guild_id: GuildId,
user: User,
_member: Option<Member>,
) {
let ctx_data = ctx.data.read().await;
let mut members = ctx_data.get::<MembersKey>().unwrap().lock().await;
let pos = members.iter().position(|m| m.user.id == user.id).unwrap();
members.remove(pos);
}
}
async fn discord_to_irc_processing(
@ -242,7 +262,7 @@ async fn discord_to_irc_processing(
});
if let Some(display_name) = display_name {
dst.push_str(&format!("@{}", display_name));
write!(dst, "@{display_name}").unwrap();
} else {
dst.push_str(caps.get(0).unwrap().as_str());
}
@ -252,13 +272,17 @@ async fn discord_to_irc_processing(
regex! {
static PING_RE_1 = r"<@([0-9]+)>";
static PING_RE_2 = r"<@!([0-9]+)>";
static PING_RE_3 = r"\{@([0-9]+)\}";
static EMOJI_RE = r"<:(\w+):[0-9]+>";
static CHANNEL_RE = r"<#([0-9]+)>";
static ROLE_RE = r"<@&([0-9]+)>";
static URL_ESCAPE_RE = r"<(https?://[^\s/$.?#].\S*)>";
}
let mut computed = message.to_owned();
computed = URL_ESCAPE_RE.replace_all(&computed, "$1").into_owned();
computed = PING_RE_1
.replace_all(&computed, MemberReplacer { members })
.into_owned();
@ -267,7 +291,7 @@ async fn discord_to_irc_processing(
.replace_all(&computed, MemberReplacer { members })
.into_owned();
computed = EMOJI_RE.replace_all(&computed, "$1").into_owned();
computed = EMOJI_RE.replace_all(&computed, ":$1:").into_owned();
// FIXME: the await makes it impossible to use `replace_all`, idk how to fix this
for caps in CHANNEL_RE.captures_iter(&computed.clone()) {
@ -294,47 +318,74 @@ async fn discord_to_irc_processing(
)
.into_owned();
// switch brackets of unknown pings
computed = PING_RE_1.replace_all(&computed, "{@$1}").into_owned();
computed = {
#[allow(clippy::enum_glob_use)]
use pulldown_cmark::{Event::*, Tag::*};
let mut new = String::with_capacity(computed.len());
for line in computed.lines() {
let parser = Parser::new(line);
let parser = Parser::new(&computed);
let mut computed_line = String::with_capacity(line.len());
let mut list_level = 0;
let mut numbered = false;
let mut next_num = 0;
for event in parser {
match event {
Text(t) | Html(t) => computed_line.push_str(&t),
Code(t) => computed_line.push_str(&format!("`{}`", t)),
End(_) => computed_line.push('\x0F'),
Start(Emphasis) => computed_line.push('\x1D'),
Start(Strong) => computed_line.push('\x02'),
Start(Link(_, dest, _)) => {
computed_line.push_str(&dest);
continue;
}
Start(List(num)) => {
if let Some(num) = num {
computed_line.push_str(&format!("{}. ", num));
} else {
computed_line.push_str("- ");
}
}
Start(BlockQuote) => computed_line.push_str("> "),
_ => {}
for event in parser {
match event {
Text(t) | Html(t) => new.push_str(&t),
Code(t) => write!(new, "`{t}`").unwrap(),
Start(Emphasis) => new.push('\x1D'),
Start(Strong) => new.push('\x02'),
Start(Link(_, _, _)) => {
new.push('[');
}
End(Link(_, url, title)) => {
write!(new, "]: {url}").unwrap();
if !title.is_empty() {
write!(new, " ({title})").unwrap();
}
}
Start(List(num)) => {
list_level += 1;
if let Some(num) = num {
numbered = true;
next_num = num;
} else {
numbered = false;
}
}
End(List(_)) => list_level -= 1,
Start(Item) => {
let prefix = if numbered {
format!("{next_num}.")
} else {
if list_level > 1 { '◦' } else { '•' }.into()
};
write!(new, "\n{}{} ", " ".repeat(list_level - 1), prefix).unwrap();
}
End(Item) => {
if numbered {
next_num += 1;
}
}
Start(BlockQuote) => new.push_str("> "),
Start(Heading(ty, _, _)) => {
write!(new, "{} \x02", "#".repeat(ty as usize)).unwrap();
}
SoftBreak | HardBreak | End(Paragraph) => new.push('\n'),
End(_) => new.push('\x0F'),
_ => {}
}
computed_line.push('\n');
new.push_str(&computed_line);
}
new
};
// switch them back
computed = PING_RE_3.replace_all(&computed, "<@$1>").into_owned();
computed
}

View file

@ -1,17 +1,23 @@
use irc::{client::Client as IrcClient, proto::Command};
use std::{collections::HashMap, sync::Arc};
use std::{collections::HashMap, sync::Arc, time::Instant};
use tokio::sync::Mutex;
use tokio::sync::{mpsc::unbounded_channel, Mutex};
use tokio_stream::wrappers::UnboundedReceiverStream;
use serenity::{
cache::Cache,
futures::StreamExt,
http::Http,
model::{
prelude::{ChannelId, GuildChannel, Member, UserId},
guild::Emoji,
id::ChannelId,
prelude::{GuildChannel, Member, UserId},
webhook::Webhook,
},
prelude::*,
utils::{content_safe, ContentSafeOptions},
};
use crate::{regex, OptionReplacer};
@ -27,25 +33,46 @@ macro_rules! unwrap_or_continue {
};
}
#[allow(clippy::too_many_lines)] // missing, fight me
pub async fn irc_loop(
mut client: IrcClient,
http: Arc<Http>,
cache: Arc<Cache>,
mapping: Arc<HashMap<String, u64>>,
webhooks: HashMap<String, Webhook>,
members: Arc<Mutex<Vec<Member>>>,
cache_ttl: Option<u64>,
) -> anyhow::Result<()> {
let (send, recv) = unbounded_channel();
tokio::spawn(msg_task(UnboundedReceiverStream::new(recv)));
let mut avatar_cache: HashMap<String, Option<String>> = HashMap::new();
let mut id_cache: HashMap<String, Option<u64>> = HashMap::new();
let mut emoji_cache: Vec<Emoji> = Vec::new();
let mut channel_users: HashMap<String, Vec<String>> = HashMap::new();
let mut ttl = Instant::now();
client.identify()?;
let mut stream = client.stream()?;
for k in mapping.keys() {
client.send(Command::NAMES(Some(k.clone()), None))?;
client.send_topic(k, "")?;
}
let mut channels_cache = None;
let mut guild = None;
while let Some(orig_message) = stream.next().await.transpose()? {
if ttl.elapsed().as_secs() > cache_ttl.unwrap_or(1800) {
avatar_cache.clear();
channels_cache = None;
guild = None;
emoji_cache.clear();
ttl = Instant::now();
}
if let Command::Response(response, args) = orig_message.command {
use irc::client::prelude::Response;
@ -57,76 +84,107 @@ pub async fn irc_loop(
.collect::<Vec<String>>();
channel_users.insert(channel, users);
} else if response == Response::RPL_TOPIC {
let channel = &args[1];
let topic = &args[2];
let channel = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
channel.edit(&http, |c| c.topic(topic)).await?;
}
continue;
};
let nickname = unwrap_or_continue!(orig_message.source_nickname());
let mut nickname = unwrap_or_continue!(orig_message.source_nickname());
if let Command::PRIVMSG(ref channel, ref message) = orig_message.command {
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
if option_env!("DIRCORD_POLARIAN_MODE").is_some() {
nickname = "polarbear";
}
match orig_message.command {
Command::PRIVMSG(ref channel, ref message)
| Command::NOTICE(ref channel, ref message) => {
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let channels = channel_id
.to_channel(&http)
.await?
.guild()
.unwrap()
.guild_id
.channels(&http)
.await?;
if channels_cache.is_none() || guild.is_none() || emoji_cache.is_empty() {
let (cc, g, es) = {
let guild = channel_id
.to_channel(&http)
.await?
.guild()
.unwrap()
.guild_id;
let members_lock = members.lock().await;
let chans = guild.channels(&http).await?;
let emojis = guild.emojis(&http).await?;
let computed =
irc_to_discord_processing(message, &*members_lock, &mut id_cache, &channels);
(chans, guild, emojis)
};
channels_cache = Some(cc);
guild = Some(g);
emoji_cache = es;
}
let channels = channels_cache.as_ref().unwrap();
if let Some(webhook) = webhooks.get(channel) {
let avatar = &*avatar_cache.entry(nickname.to_owned()).or_insert_with(|| {
members_lock.iter().find_map(|member| {
(*member.display_name() == nickname)
.then(|| member.user.avatar_url())
.flatten()
})
});
let members_lock = members.lock().await;
webhook
.execute(&http, false, |w| {
if let Some(ref url) = avatar {
w.avatar_url(url);
}
let mut computed = irc_to_discord_processing(
message,
&members_lock,
&mut id_cache,
channels,
&emoji_cache,
);
w.username(nickname).content(computed)
})
.await?;
} else {
channel_id
.say(&http, format!("<{}> {}", nickname, computed))
.await?;
computed = {
let opts = ContentSafeOptions::new()
.clean_role(false)
.clean_user(false)
.clean_channel(false)
.show_discriminator(false)
.clean_here(true) // setting these to true explicitly isn't needed,
.clean_everyone(true); // but i did it anyway for readability
content_safe(&cache, computed, &opts, &[])
};
if let Some(webhook) = webhooks.get(channel) {
let avatar = &*avatar_cache.entry(nickname.to_owned()).or_insert_with(|| {
members_lock.iter().find_map(|member| {
(*member.display_name() == nickname)
.then(|| member.user.avatar_url())
.flatten()
})
});
send.send(QueuedMessage::Webhook {
webhook: webhook.clone(),
http: http.clone(),
avatar_url: avatar.clone(),
content: computed,
nickname: nickname.to_string(),
})?;
} else {
send.send(QueuedMessage::Raw {
channel_id,
http: http.clone(),
message: format!("<{nickname}>, {computed}"),
})?;
}
}
} else if let Command::JOIN(ref channel, _, _) = orig_message.command {
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let users = unwrap_or_continue!(channel_users.get_mut(channel));
Command::JOIN(ref channel, _, _) => {
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let users = unwrap_or_continue!(channel_users.get_mut(channel));
users.push(nickname.to_string());
users.push(nickname.to_string());
channel_id
.say(&http, format!("*{}* has joined the channel", nickname))
.await?;
} else if let Command::PART(ref channel, ref reason) = orig_message.command {
let users = unwrap_or_continue!(channel_users.get_mut(channel));
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let pos = unwrap_or_continue!(users.iter().position(|u| u == nickname));
users.swap_remove(pos);
let reason = reason.as_deref().unwrap_or("Connection closed");
channel_id
.say(&http, format!("*{}* has quit ({})", nickname, reason))
.await?;
} else if let Command::QUIT(ref reason) = orig_message.command {
for (channel, users) in &mut channel_users {
send.send(QueuedMessage::Raw {
channel_id,
http: http.clone(),
message: format!("*{nickname}* has joined the channel"),
})?;
}
Command::PART(ref channel, ref reason) => {
let users = unwrap_or_continue!(channel_users.get_mut(channel));
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let pos = unwrap_or_continue!(users.iter().position(|u| u == nickname));
@ -134,24 +192,58 @@ pub async fn irc_loop(
let reason = reason.as_deref().unwrap_or("Connection closed");
channel_id
.say(&http, format!("*{}* has quit ({})", nickname, reason))
.await?;
send.send(QueuedMessage::Raw {
channel_id,
http: http.clone(),
message: format!("*{nickname}* has quit ({reason})"),
})?;
}
} else if let Command::NICK(ref new_nick) = orig_message.command {
for (channel, users) in &mut channel_users {
Command::QUIT(ref reason) => {
for (channel, users) in &mut channel_users {
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let pos = unwrap_or_continue!(users.iter().position(|u| u == nickname));
users.swap_remove(pos);
let reason = reason.as_deref().unwrap_or("Connection closed");
send.send(QueuedMessage::Raw {
channel_id,
http: http.clone(),
message: format!("*{nickname}* has quit ({reason})"),
})?;
}
}
Command::NICK(ref new_nick) => {
for (channel, users) in &mut channel_users {
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let pos = unwrap_or_continue!(users.iter().position(|u| u == nickname));
users[pos] = new_nick.to_string();
send.send(QueuedMessage::Raw {
channel_id,
http: http.clone(),
message: format!("*{nickname}* is now known as *{new_nick}*"),
})?;
}
}
Command::TOPIC(ref channel, ref topic) => {
let topic = unwrap_or_continue!(topic.as_ref());
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let pos = unwrap_or_continue!(users.iter().position(|u| u == nickname));
users[pos] = new_nick.to_string();
channel_id
.say(
&http,
format!("*{}* is now known as *{}*", nickname, new_nick),
)
.await?;
channel_id.edit(&http, |c| c.topic(topic)).await?;
}
Command::KICK(ref channel, ref user, ref reason) => {
let channel_id = ChannelId::from(*unwrap_or_continue!(mapping.get(channel)));
let reason = reason.as_deref().unwrap_or("None");
send.send(QueuedMessage::Raw {
channel_id,
http: http.clone(),
message: format!("*{nickname}* has kicked *{user}* ({reason})"),
})?;
}
_ => {}
}
}
Ok(())
@ -162,6 +254,7 @@ fn irc_to_discord_processing(
members: &[Member],
id_cache: &mut HashMap<String, Option<u64>>,
channels: &HashMap<ChannelId, GuildChannel>,
emojis: &[Emoji],
) -> String {
struct MemberReplacer<'a> {
id_cache: &'a mut HashMap<String, Option<u64>>,
@ -177,7 +270,9 @@ fn irc_to_discord_processing(
.entry(slice.to_owned())
.or_insert_with(|| {
self.members.iter().find_map(|member| {
(slice == member.display_name().as_str()).then(|| member.user.id.0)
(slice == member.display_name().as_str()
|| slice == member.user.name.as_str())
.then_some(member.user.id.0)
})
})
.map(UserId);
@ -196,10 +291,11 @@ fn irc_to_discord_processing(
static CONTROL_CHAR_RE = r"\x1f|\x02|\x12|\x0f|\x16|\x03(?:\d{1,2}(?:,\d{1,2})?)?";
static WHITESPACE_RE = r"^\s";
static CHANNEL_RE = r"#([A-Za-z-*]+)";
static EMOJI_RE = r":(\w+):";
}
if WHITESPACE_RE.is_match(message).unwrap() && !PING_RE_2.is_match(message).unwrap() {
return format!("`{}`", message);
return format!("`{message}`");
}
let mut computed = message.to_owned();
@ -223,12 +319,23 @@ fn irc_to_discord_processing(
)
.into_owned();
computed = EMOJI_RE
.replace_all(
&computed,
OptionReplacer(|caps: &Captures| {
emojis
.iter()
.find_map(|e| (e.name == caps[1]).then(|| format!("<:{}:{}>", e.name, e.id.0)))
}),
)
.into_owned();
#[allow(clippy::map_unwrap_or)]
{
computed = computed
.strip_prefix("\x01ACTION ")
.and_then(|s| s.strip_suffix('\x01'))
.map(|s| format!("*{}*", s))
.map(|s| format!("*{s}*"))
.unwrap_or_else(|| computed); // if any step in the way fails, fall back to using computed
}
@ -263,3 +370,58 @@ fn irc_to_discord_processing(
computed
}
#[allow(clippy::large_enum_variant)] // lmao
#[derive(Debug)]
enum QueuedMessage {
Webhook {
webhook: Webhook,
http: Arc<Http>,
avatar_url: Option<String>,
content: String,
nickname: String,
},
Raw {
channel_id: ChannelId,
http: Arc<Http>,
message: String,
},
}
async fn msg_task(mut recv: UnboundedReceiverStream<QueuedMessage>) -> anyhow::Result<()> {
while let Some(msg) = recv.next().await {
match msg {
QueuedMessage::Webhook {
webhook,
http,
avatar_url,
content,
nickname,
} => {
if content.is_empty() {
continue;
}
webhook
.execute(&http, true, |w| {
if let Some(ref url) = avatar_url {
w.avatar_url(url);
}
w.username(nickname).content(content)
})
.await?;
}
QueuedMessage::Raw {
channel_id,
http,
message,
} => {
if message.is_empty() {
continue;
}
channel_id.say(&http, message).await?;
}
}
}
Ok(())
}

View file

@ -8,6 +8,7 @@ use std::{borrow::Cow, collections::HashMap, env, fs::File, io::Read, sync::Arc}
use serenity::{
http::Http,
model::{
gateway::GatewayIntents,
guild::Member,
id::{ChannelId, UserId},
webhook::Webhook,
@ -36,6 +37,8 @@ struct DircordConfig {
raw_prefix: Option<String>,
channels: HashMap<String, u64>,
webhooks: Option<HashMap<String, String>>,
ref_content_limit: Option<u16>,
cache_ttl: Option<u64>,
}
macro_rules! type_map_key {
@ -59,6 +62,7 @@ type_map_key!(
StringKey => String,
OptionStringKey => Option<String>,
ChannelMappingKey => HashMap<String, u64>,
RefContentLimitKey => Option<u16>,
);
#[cfg(unix)]
@ -90,7 +94,11 @@ async fn main() -> anyhow::Result<()> {
let conf: DircordConfig = toml::from_str(&data)?;
let mut discord_client = DiscordClient::builder(&conf.token)
let intents = GatewayIntents::non_privileged()
| GatewayIntents::GUILD_MEMBERS
| GatewayIntents::MESSAGE_CONTENT;
let mut discord_client = DiscordClient::builder(&conf.token, intents)
.event_handler(Handler)
.await?;
@ -107,6 +115,7 @@ async fn main() -> anyhow::Result<()> {
let irc_client = IrcClient::from_config(config).await?;
let http = discord_client.cache_and_http.http.clone();
let cache = discord_client.cache_and_http.cache.clone();
let members = Arc::new(Mutex::new({
let channel_id = ChannelId::from(*conf.channels.iter().next().unwrap().1);
@ -129,6 +138,7 @@ async fn main() -> anyhow::Result<()> {
data.insert::<MembersKey>(members.clone());
data.insert::<OptionStringKey>(conf.raw_prefix);
data.insert::<ChannelMappingKey>((*channels).clone());
data.insert::<RefContentLimitKey>(conf.ref_content_limit);
}
let mut webhooks_transformed: HashMap<String, Webhook> = HashMap::new();
@ -144,12 +154,12 @@ async fn main() -> anyhow::Result<()> {
}
select! {
r = irc_loop(irc_client, http.clone(), channels.clone(), webhooks_transformed, members) => r?,
r = discord_client.start() => r?,
r = irc_loop(irc_client, http.clone(), cache.clone(), channels.clone(), webhooks_transformed, members, conf.cache_ttl) => r.unwrap(),
r = discord_client.start() => r.unwrap(),
_ = terminate_signal() => {
for (_, &v) in channels.iter() {
let channel_id = ChannelId::from(v);
channel_id.say(&http, format!("dircord shutting down! (dircord {}-{})", env!("VERGEN_GIT_BRANCH"), &env!("VERGEN_GIT_SHA")[..7])).await?;
channel_id.say(&http, format!("dircord shutting down! (dircord {}-{})", env!("VERGEN_GIT_BRANCH"), &env!("VERGEN_GIT_SHA")[..7])).await.unwrap();
}
},
}