diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Cargo.lock | 1174 | ||||
| -rw-r--r-- | Cargo.toml | 31 | ||||
| -rw-r--r-- | macros/Cargo.lock | 7 | ||||
| -rw-r--r-- | macros/Cargo.toml | 12 | ||||
| -rw-r--r-- | macros/src/lib.rs | 158 | ||||
| -rw-r--r-- | macros/src/macro_types/mod.rs | 249 | ||||
| -rw-r--r-- | rustfmt.toml | 1 | ||||
| -rw-r--r-- | src/config.rs | 46 | ||||
| -rw-r--r-- | src/hooks/mod.rs | 39 | ||||
| -rw-r--r-- | src/hooks/sed.rs | 175 | ||||
| -rw-r--r-- | src/hooks/shifty_eyes.rs | 46 | ||||
| -rw-r--r-- | src/lib.rs | 70 | ||||
| -rw-r--r-- | src/main.rs | 38 |
14 files changed, 2048 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f182a35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +config.json diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f299a92 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1174 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "catinator" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "futures", + "irc", + "irc-proto", + "macros", + "regex", + "sasl", + "sedregex", + "serde", + "tokio", + "toml", + "tracing", + "tracing-futures", + "tracing-subscriber", +] + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cpufeatures" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec1028182c380cc45a2e2c5ec841134f2dfd0f8f5f0a5bcd68004f81b5efdf4" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-mac" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + +[[package]] +name = "futures" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" + +[[package]] +name = "futures-executor" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" + +[[package]] +name = "futures-macro" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" + +[[package]] +name = "futures-task" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" + +[[package]] +name = "futures-util" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "irc" +version = "0.15.0" +dependencies = [ + "chrono", + "encoding", + "futures-util", + "irc-proto", + "log", + "parking_lot", + "pin-project", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-stream", + "tokio-util", + "webpki-roots", +] + +[[package]] +name = "irc-proto" +version = "0.15.0" +dependencies = [ + "bytes", + "encoding", + "thiserror", + "tokio", + "tokio-util", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" + +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "mio" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +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", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "pbkdf2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b8c0d71734018084da0c0354193a5edfb81b20d2d57a92c5b154aefc554a4a" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "pin-project" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "sasl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572d7dce028927bbc1cde21a93c6a1f4d7a1a7cc0e643d418a2864800a362dc9" +dependencies = [ + "base64", + "getrandom", + "hmac", + "pbkdf2", + "sha-1", + "sha2", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sedregex" +version = "0.2.4" +source = "git+https://gitlab.com/audron/sedregex#2ac3be5f56f53122cd89a160bc2ff9f7387a9467" +dependencies = [ + "regex", +] + +[[package]] +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sharded-slab" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "subtle" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" + +[[package]] +name = "syn" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tokio" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e177a5d8c3bf36de9ebe6d58537d8879e964332f93fb3339e43f618c81361af0" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa5553bf0883ba7c9cbe493b085c29926bd41b66afc31ff72cf17ff4fb60dcd5" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" + +[[package]] +name = "web-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..acce7b1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "catinator" +version = "0.1.0" +authors = ["Max Audron <audron@cocaine.farm>"] +edition = "2018" + +[dependencies] +macros = { path = "./macros" } + +irc = { path = "/home/audron/repo/github.com/maxaudron/irc", version = "0.15", features = ["json", "tls-rust"], default_features = false } +irc-proto = { path = "/home/audron/repo/github.com/maxaudron/irc/irc-proto" } +sasl = "0.5" +base64 = "0.13" + +serde = { version = "1.0", features = ["derive"] } +toml = "0.5" + +anyhow = "1" + +futures = "0.3" +tokio = { version = "1.5.0", features = ["full", "rt-multi-thread"] } + +tracing = "0.1" +tracing-subscriber = "0.2" +tracing-futures = "0.2" + +regex = "1" +sedregex = { git = "https://gitlab.com/audron/sedregex" } + +[workspace] +members = ["macros"] diff --git a/macros/Cargo.lock b/macros/Cargo.lock new file mode 100644 index 0000000..6d255d1 --- /dev/null +++ b/macros/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "macros" +version = "0.1.0" diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..30019d7 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "macros" +version = "0.1.0" +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "1", features = ["parsing"] } +proc-macro2 = "1" +quote = "1" diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..222f14d --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,158 @@ +extern crate proc_macro; +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::quote; + +use syn::parse_macro_input; + +mod macro_types; + +use macro_types::*; + +fn generate_help(items: &Items) -> proc_macro2::TokenStream { + let command_help = items.inner.iter().filter_map(|x| { + if let Item::Command(command) = x { + let help = command.help(); + Some(quote! { + bot.send_notice(target, #help).unwrap(); + }) + } else { + None + } + }); + + let matcher_help = items.inner.iter().filter_map(|x| { + if let Item::Matcher(matcher) = x { + let help = matcher.help(); + Some(quote! { + bot.send_notice(target, #help).unwrap(); + }) + } else { + None + } + }); + + let hook_help = items.inner.iter().filter_map(|x| { + if let Item::Hook(hook) = x { + let help = hook.help(); + Some(quote! { + bot.send_notice(target, #help).unwrap(); + }) + } else { + None + } + }); + + let gen = quote! { + let target = message.source_nickname().unwrap(); + + bot.send_notice(target, "COMMANDS:").unwrap(); + #(#command_help)* + + bot.send_notice(target, "MATCHERS:").unwrap(); + #(#matcher_help)* + + bot.send_notice(target, "HOOKS:").unwrap(); + #(#hook_help)* + }; + gen.into() +} + +#[proc_macro] +pub fn catinator(tokens: TokenStream) -> TokenStream { + let items = parse_macro_input!(tokens as Items); + + let hooks = items.inner.iter().filter_map(|x| { + if let Item::Hook(hook) = x { + Some(hook.to_call()) + } else { + None + } + }); + + let commands = items.inner.iter().filter_map(|x| { + if let Item::Command(command) = x { + Some(command.to_call()) + } else { + None + } + }); + + let matchers = items.inner.iter().filter_map(|x| { + if let Item::Matcher(command) = x { + Some(command.to_call()) + } else { + None + } + }); + + let matchers_regex = items.inner.iter().filter_map(|x| { + if let Item::Matcher(matcher) = x { + let name = &matcher.name; + let regex = &matcher.matcher; + + let ident = Ident::new(&name.value(), Span::call_site()); + + Some(quote! { + let #ident = regex::Regex::new(#regex).unwrap(); + }) + } else { + None + } + }); + + let help = generate_help(&items); + + let gen = quote! { + use std::env; + + use futures::prelude::*; + use tracing::{info, debug, trace}; + + use irc::client::prelude::*; + use catinator::Bot; + + let config_path = env::var("CATINATOR_CONFIG").unwrap_or("config.toml".to_string()); + info!("starting bot with config file {}", config_path); + let mut bot = Bot::new(&config_path).await.unwrap(); + + if bot.config.server.sasl { + info!("initializing sasl"); + bot.sasl_init().await.unwrap() + } + + #(#matchers_regex)* + + info!("starting main event loop"); + let mut stream = bot.irc_client.stream().unwrap(); + + while let Some(message) = stream.next().await.transpose().unwrap() { + trace!("{:?}", message); + + let command = message.clone().command; + + #(#hooks)* + + match &command { + Command::PRIVMSG(_target, text) => { + let mut word = text.split_ascii_whitespace().next().unwrap().chars(); + let prefix = word.next().unwrap(); + let rest: String = word.collect(); + + if prefix == bot.config.settings.prefix { + if "help" == rest { + #help + } + + #(#commands)* + } else { + #(#matchers)* + } + } + _ => (), + } + } + }; + + return gen.into(); +} diff --git a/macros/src/macro_types/mod.rs b/macros/src/macro_types/mod.rs new file mode 100644 index 0000000..262b3df --- /dev/null +++ b/macros/src/macro_types/mod.rs @@ -0,0 +1,249 @@ +use proc_macro2::{Ident, Span}; +use quote::quote; + +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + Path, +}; +use syn::{LitStr, Token}; + +pub trait IrcItem { + fn to_call(&self) -> proc_macro2::TokenStream; + fn help(&self) -> String; +} + +#[derive(Debug)] +pub struct Items { + pub inner: Vec<Item>, +} + +impl Parse for Items { + fn parse(input: ParseStream) -> syn::Result<Self> { + let mut items: Vec<Item> = Vec::new(); + + while !input.is_empty() { + if input.peek(syn::Ident) && input.peek2(syn::token::Paren) { + items.push(input.parse()?) + } else { + return Err(input.error("line was not of expected format")); + } + } + + Ok(Self { inner: items }) + } +} + +#[derive(Debug)] +pub enum Item { + Command(Command), + Hook(Hook), + Matcher(Matcher), +} + +impl Parse for Item { + fn parse(input: ParseStream) -> syn::Result<Self> { + if input.peek(syn::Ident) { + let item: Ident = input.parse()?; + match item.to_string().as_str() { + "command" => input.parse().map(Item::Command), + "hook" => input.parse().map(Item::Hook), + "matcher" => input.parse().map(Item::Matcher), + _ => Err(input.error(format!( + "expected one of: command, hook or matcher not {}", + item.to_string() + ))), + } + } else { + Err(input.error("wrong type")) + } + } +} + +#[derive(Debug)] +pub struct Command { + pub name: LitStr, + pub description: LitStr, + pub function: Path, +} + +impl IrcItem for Command { + fn to_call(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let function = &self.function; + + quote! { + if #name == rest { + debug!(target: "command", "{} with {:?}", #name, message); + let result = #function(message.clone()); + } + } + } + + fn help(&self) -> String { + format!(" {}: {}", self.name.value(), self.description.value()) + } +} + +impl Parse for Command { + fn parse(input: ParseStream) -> syn::Result<Self> { + let content; + parenthesized!(content in input); + + let mut _token: Token![,]; + + if input.peek(Token![,]) { + _token = input.parse()? + } + + let name = content.parse()?; + _token = content.parse()?; + let description = content.parse()?; + _token = content.parse()?; + let function = content.parse()?; + + Ok(Self { + name, + description, + function, + }) + } +} + +#[derive(Debug)] +pub struct Hook { + pub name: LitStr, + pub description: LitStr, + pub kind: Ident, + pub function: Path, +} + +impl IrcItem for Hook { + fn to_call(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let kind = &self.kind; + let kind_str = &self.kind.to_string(); + let function = &self.function; + + quote! { + if let Command::#kind(..) = &command { + debug!(target: "hook", "{} of kind {} with {:?}", #name, #kind_str, message); + let result = #function(&bot, message.clone()); + } + } + } + + fn help(&self) -> String { + format!(" {}: {}", self.name.value(), self.description.value()) + } +} + +impl Parse for Hook { + fn parse(input: ParseStream) -> syn::Result<Self> { + let content; + parenthesized!(content in input); + + let mut _token: Token![,]; + + if input.peek(Token![,]) { + _token = input.parse()?; + } + + let name = content.parse()?; + _token = content.parse()?; + let description = content.parse()?; + _token = content.parse()?; + let kind: Ident = content.parse()?; + match kind.to_string().as_str() { + "PASS" | "NICK" | "USER" | "OPER" | "UserMODE" | "SERVICE" | "QUIT" | "SQUIT" + | "JOIN" | "PART" | "ChannelMODE" | "TOPIC" | "NAMES" | "LIST" | "INVITE" | "KICK" + | "PRIVMSG" | "NOTICE" | "MOTD" | "LUSERS" | "VERSION" | "STATS" | "LINKS" | "TIME" + | "CONNECT" | "TRACE" | "ADMIN" | "INFO" | "SERVLIST" | "SQUERY" | "WHO" | "WHOIS" + | "WHOWAS" | "KILL" | "PING" | "PONG" | "ERROR" | "AWAY" | "REHASH" | "DIE" + | "RESTART" | "SUMMON" | "USERS" | "WALLOPS" | "USERHOST" | "ISON" | "SAJOIN" + | "SAMODE" | "SANICK" | "SAPART" | "SAQUIT" | "NICKSERV" | "CHANSERV" | "OPERSERV" + | "BOTSERV" | "HOSTSERV" | "MEMOSERV" | "CAP" | "AUTHENTICATE" | "ACCOUNT" + | "METADATA" | "MONITOR" | "BATCH" | "CHGHOST" | "Response" | "Raw" => (), + _ => { + return Err(content.error(format!( + "expected one of: PASS, NICK, USER, OPER, UserMODE, SERVICE, QUIT, SQUIT + , JOIN, PART, ChannelMODE, TOPIC, NAMES, LIST, INVITE, KICK + , PRIVMSG, NOTICE, MOTD, LUSERS, VERSION, STATS, LINKS, TIME + , CONNECT, TRACE, ADMIN, INFO, SERVLIST, SQUERY, WHO, WHOIS + , WHOWAS, KILL, PING, PONG, ERROR, AWAY, REHASH, DIE + , RESTART, SUMMON, USERS, WALLOPS, USERHOST, ISON, SAJOIN + , SAMODE, SANICK, SAPART, SAQUIT, NICKSERV, CHANSERV, OPERSERV + , BOTSERV, HOSTSERV, MEMOSERV, CAP, AUTHENTICATE, ACCOUNT + , METADATA, MONITOR, BATCH, CHGHOST, Response, Raw not {}", + kind.to_string() + ))) + } + } + + _token = content.parse()?; + let function = content.parse()?; + + Ok(Self { + name, + description, + kind, + function, + }) + } +} + +#[derive(Debug)] +pub struct Matcher { + pub name: LitStr, + pub description: LitStr, + pub matcher: LitStr, + pub function: Path, +} + +impl IrcItem for Matcher { + fn to_call(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let function = &self.function; + + let ident = Ident::new(&name.value(), Span::call_site()); + + quote! { + if #ident.is_match(text) { + debug!(target: "matcher", "{} with {:?}", #name, message); + let result = #function(&bot, message.clone()); + } + } + } + + fn help(&self) -> String { + format!(" {} ({}): {}", self.name.value(), self.matcher.value(), self.description.value()) + } +} + +impl Parse for Matcher { + fn parse(input: ParseStream) -> syn::Result<Self> { + let content; + parenthesized!(content in input); + + let mut _token: Token![,]; + + if input.peek(Token![,]) { + _token = input.parse()?; + } + + let name = content.parse()?; + _token = content.parse()?; + let description = content.parse()?; + _token = content.parse()?; + let matcher = content.parse()?; + _token = content.parse()?; + let function = content.parse()?; + + Ok(Self { + name, + description, + matcher, + function, + }) + } +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..32a9786 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +edition = "2018" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..f7a0934 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,46 @@ +use serde::Deserialize; + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)] +pub struct Config { + pub user: User, + pub server: Server, + pub settings: Settings, +} + +impl From<Config> for irc::client::prelude::Config { + fn from(input: Config) -> Self { + Self { + nickname: Some(input.user.nickname), + username: Some(input.user.username), + realname: Some(input.user.realname), + nick_password: Some(input.user.password), + server: Some(input.server.hostname), + port: Some(input.server.port), + use_tls: Some(input.server.tls), + channels: input.server.channels, + ..irc::client::prelude::Config::default() + } + } +} + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)] +pub struct User { + pub nickname: String, + pub username: String, + pub password: String, + pub realname: String, +} + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)] +pub struct Server { + pub hostname: String, + pub port: u16, + pub tls: bool, + pub sasl: bool, + pub channels: Vec<String>, +} + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)] +pub struct Settings { + pub prefix: char, +} diff --git a/src/hooks/mod.rs b/src/hooks/mod.rs new file mode 100644 index 0000000..ba4a98d --- /dev/null +++ b/src/hooks/mod.rs @@ -0,0 +1,39 @@ +use anyhow::Result; +use irc::client::prelude::*; + +pub mod sed; +pub mod shifty_eyes; + +pub use sed::*; +pub use shifty_eyes::shifty_eyes; + +pub fn sasl(bot: &crate::Bot, msg: Message) -> Result<()> { + match msg.command { + Command::AUTHENTICATE(text) => { + use sasl::client::mechanisms::Plain; + use sasl::client::Mechanism; + use sasl::common::Credentials; + + if text == "+" { + let creds = Credentials::default() + .with_username(bot.config.clone().user.username) + .with_password(bot.config.clone().user.password); + + let mut mechanism = Plain::from_credentials(creds).unwrap(); + + let initial_data = mechanism.initial(); + + bot.irc_client.send_sasl(base64::encode(initial_data))?; + bot.irc_client.send(Command::CAP( + None, + irc_proto::command::CapSubCommand::END, + None, + None, + ))?; + } + } + _ => (), + } + + Ok(()) +} diff --git a/src/hooks/sed.rs b/src/hooks/sed.rs new file mode 100644 index 0000000..095eb14 --- /dev/null +++ b/src/hooks/sed.rs @@ -0,0 +1,175 @@ +use anyhow::{anyhow, Result}; +use irc::client::prelude::*; + +use sedregex::ReplaceCommand; + +use std::cell::RefCell; + +static LOG_MAX_SIZE: usize = 10000; + +thread_local!(static LOG: RefCell<Vec::<(String, String)>> = RefCell::new(Vec::with_capacity(LOG_MAX_SIZE))); +thread_local!(static RE: regex::Regex = regex::Regex::new(r"^s/").unwrap()); + +pub fn log(_bot: &crate::Bot, msg: Message) -> Result<()> { + log_msg(msg) +} + +fn log_msg(msg: Message) -> Result<()> { + if let Command::PRIVMSG(_, text) = msg.command.clone() { + LOG.with(|log_cell| { + let mut log = log_cell.borrow_mut(); + if log.len() >= LOG_MAX_SIZE { + let _ = log.pop(); + } + log.push((msg.source_nickname().unwrap().to_string(), text)) + }); + } + Ok(()) +} + +pub fn replace(bot: &crate::Bot, msg: Message) -> Result<()> { + match find_and_replace(&msg) { + Ok(res) => { + bot.send_privmsg(msg.response_target().unwrap(), res.as_str()) + .unwrap(); + Ok(()) + } + Err(_) => Ok(()), + } +} + +fn find_and_replace(msg: &Message) -> Result<String> { + if let Command::PRIVMSG(_, text) = msg.command.clone() { + let cmd = match ReplaceCommand::new(text.as_str()) { + Ok(cmd) => cmd, + Err(_) => return Err(anyhow!("building replace command failed")), + }; + + return LOG.with(|log_cell| { + log_cell + .borrow() + .iter() + .rev() + .find(|(_, text)| cmd.expr.is_match(text) && !RE.with(|re| re.is_match(text))) + .and_then(|(nick, text)| Some(format!("<{}> {}", nick, cmd.execute(text)))) + .map_or(Err(anyhow!("replace failed")), |v| Ok(v)) + }); + } + + Err(anyhow!("not a privmsg")) +} + +#[cfg(test)] +mod tests { + use super::*; + use test::Bencher; + + fn populate_log() { + LOG.with(|log_cell| { + let mut log = log_cell.borrow_mut(); + log.push(( + "user".to_string(), + "this is a long message which will be replaced".to_string(), + )); + for _ in 0..LOG_MAX_SIZE-1 { + log.push(( + "user".to_string(), + "this is a long message which doesn't matter".to_string(), + )) + } + }); + } + + #[test] + fn test_log_limit() { + populate_log(); + + LOG.with(|log_cell| { + let log = log_cell.borrow(); + assert_eq!(log.len(), LOG_MAX_SIZE) + }); + + log_msg(Message { + tags: None, + prefix: Some(Prefix::Nickname( + "user".to_string(), + "username".to_string(), + "userhost".to_string(), + )), + command: Command::PRIVMSG( + "#channel".to_string(), + "this is the 10001th message".to_string(), + ), + }) + .unwrap(); + + LOG.with(|log_cell| { + let log = log_cell.borrow(); + assert_eq!(log.len(), LOG_MAX_SIZE) + }); + } + + #[test] + fn test_replace() { + populate_log(); + assert_eq!( + find_and_replace(&Message { + tags: None, + prefix: None, + command: Command::PRIVMSG( + "#channel".to_string(), + "s/will be/has been/".to_string(), + ), + }) + .unwrap(), + "<user> this is a long message which has been replaced" + ) + } + + #[test] + fn test_replace_complex() { + populate_log(); + assert_eq!( + find_and_replace(&Message { + tags: None, + prefix: None, + command: Command::PRIVMSG( + "#channel".to_string(), + "s/(will).*(be)/$2 $1/".to_string(), + ), + }) + .unwrap(), + "<user> this is a long message which be will replaced" + ) + } + + #[bench] + fn bench_replace(b: &mut Bencher) { + populate_log(); + b.iter(|| { + find_and_replace(&Message { + tags: None, + prefix: None, + command: Command::PRIVMSG( + "#channel".to_string(), + "s/will be/has been/".to_string(), + ), + }) + }); + } + + #[bench] + fn bench_replace_complex(b: &mut Bencher) { + populate_log(); + b.iter(|| { + find_and_replace(&Message { + tags: None, + prefix: None, + command: Command::PRIVMSG( + "#channel".to_string(), + "s/will be/has been/".to_string(), + ), + }) + }); + } +} diff --git a/src/hooks/shifty_eyes.rs b/src/hooks/shifty_eyes.rs new file mode 100644 index 0000000..beb3a9e --- /dev/null +++ b/src/hooks/shifty_eyes.rs @@ -0,0 +1,46 @@ +use anyhow::{anyhow, Result}; +use irc::client::prelude::*; + +const EYES: [char; 10] = ['^', 'v', 'V', '>', '<', 'x', 'X', '-', 'o', 'O']; +const NOSE: [char; 7] = ['.', '_', '-', ';', '\'', '"', '~']; + +pub fn shifty_eyes(bot: &crate::Bot, msg: Message) -> Result<()> { + if let Command::PRIVMSG(_, text) = msg.command.clone() { + if text.len() == 3 { + let mut chars = text.chars(); + let mut left = chars.next().unwrap(); + let middle = chars.next().unwrap(); + let mut right = chars.next().unwrap(); + + if EYES.contains(&left) && NOSE.contains(&middle) && EYES.contains(&right) { + left = invert(left)?; + right = invert(right)?; + + let mut result = String::new(); + result.push(left); + result.push(middle); + result.push(right); + + bot.send_privmsg(msg.response_target().unwrap(), result.as_str())?; + } + } + } + + Ok(()) +} + +fn invert(input: char) -> Result<char> { + match input { + '^' => Ok('v'), + 'v' => Ok('^'), + 'V' => Ok('^'), + '>' => Ok('<'), + '<' => Ok('>'), + 'x' => Ok('o'), + 'X' => Ok('O'), + '-' => Ok('o'), + 'o' => Ok('-'), + 'O' => Ok('-'), + _ => Err(anyhow!("not a valid char")), + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1e3be91 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,70 @@ +#![feature(test)] +extern crate test; + +use anyhow::Result; + +use irc::client::prelude::*; + +pub mod config; +pub mod hooks; + +pub use macros::catinator; + +#[macro_export] +macro_rules! reply { + ( $msg:expr, $text:expr ) => { + bot.send_privmsg($msg.response_target().unwrap(), $text.as_str())?; + }; +} + + +pub struct Bot { + pub config: config::Config, + pub irc_client: irc::client::Client, +} + +impl Bot { + pub async fn new(config_path: &str) -> Result<Bot> { + use std::fs; + + let config_str = fs::read_to_string(config_path)?; + let config: config::Config = toml::from_str(&config_str)?; + + let irc_config: Config = config.clone().into(); + + let irc_client = Client::from_config(irc_config.clone()).await?; + + Ok(Bot { irc_client, config }) + } + + pub async fn sasl_init(&self) -> Result<()> { + self.irc_client + .send_cap_req(&vec![irc::client::prelude::Capability::Sasl])?; + self.irc_client + .send(Command::NICK(self.config.user.nickname.clone()))?; + self.irc_client.send(Command::USER( + self.config.user.nickname.clone(), + "0".to_owned(), + self.config.user.realname.clone(), + ))?; + self.irc_client.send_sasl_plain()?; + + Ok(()) + } + + pub fn send_privmsg( + &self, + target: &str, + message: &str, + ) -> std::result::Result<(), irc::error::Error> { + self.irc_client.send_privmsg(target, message) + } + + pub fn send_notice( + &self, + target: &str, + message: &str, + ) -> std::result::Result<(), irc::error::Error> { + self.irc_client.send_notice(target, message) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..10082c3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,38 @@ +#[tokio::main] +async fn main() { + use catinator::catinator; + + tracing_subscriber::fmt() + .compact() + .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL) + .with_max_level(tracing::Level::DEBUG) + .with_thread_ids(true) + .init(); + + catinator!( + hook( + "sasl", + "Handle Authentication.", + AUTHENTICATE, + catinator::hooks::sasl + ), + hook( + "sed_log", + "Log messages for use with sed replace, max 10k lines.", + PRIVMSG, + catinator::hooks::sed::log + ), + matcher( + "shifty_eyes", + ">.>", + r"^\S{3}$", + catinator::hooks::shifty_eyes + ), + matcher( + "replace", + "sed style replace with regex support. i/g/U/x sed flags available", + r"^s/", + catinator::hooks::sed::replace + ), + ); +} |
