about summary refs log tree commit diff
path: root/fun/paroxysm
diff options
context:
space:
mode:
Diffstat (limited to 'fun/paroxysm')
-rw-r--r--fun/paroxysm/.gitignore5
-rw-r--r--fun/paroxysm/Cargo.lock1639
-rw-r--r--fun/paroxysm/Cargo.toml22
-rw-r--r--fun/paroxysm/OWNERS3
-rw-r--r--fun/paroxysm/README.md19
-rw-r--r--fun/paroxysm/default.nix14
-rw-r--r--fun/paroxysm/docker/default.nix7
-rw-r--r--fun/paroxysm/migrations/20181209140247_initial/down.sql2
-rw-r--r--fun/paroxysm/migrations/20181209140247_initial/up.sql15
-rw-r--r--fun/paroxysm/migrations/20181218142013_fix_unique/down.sql1
-rw-r--r--fun/paroxysm/migrations/20181218142013_fix_unique/up.sql1
-rw-r--r--fun/paroxysm/src/cfg.rs12
-rw-r--r--fun/paroxysm/src/keyword.rs219
-rw-r--r--fun/paroxysm/src/main.rs395
-rw-r--r--fun/paroxysm/src/models.rs36
-rw-r--r--fun/paroxysm/src/schema.rs18
16 files changed, 2408 insertions, 0 deletions
diff --git a/fun/paroxysm/.gitignore b/fun/paroxysm/.gitignore
new file mode 100644
index 0000000000..8cb01f2e3d
--- /dev/null
+++ b/fun/paroxysm/.gitignore
@@ -0,0 +1,5 @@
+/target
+irc.toml
+paroxysm-irc.toml
+paroxysm.toml
+**/*.rs.bk
diff --git a/fun/paroxysm/Cargo.lock b/fun/paroxysm/Cargo.lock
new file mode 100644
index 0000000000..70177ce9bb
--- /dev/null
+++ b/fun/paroxysm/Cargo.lock
@@ -0,0 +1,1639 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if 1.0.0",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bufstream"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
+dependencies = [
+ "byteorder",
+ "iovec",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[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 0.2.15",
+ "time",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "cloudabi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "config"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9107d78ed62b3fa5a86e7d18e647abed48cfd8f8fab6c72f4cdb982d196f7e6"
+dependencies = [
+ "lazy_static 1.4.0",
+ "nom",
+ "rust-ini",
+ "serde 1.0.137",
+ "serde-hjson",
+ "serde_json",
+ "toml",
+ "yaml-rust",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "crimp"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe8f9a320ad9c1a2e3bacedaa281587bd297fb10a10179fd39f777049d04794"
+dependencies = [
+ "curl",
+ "serde 1.0.137",
+ "serde_json",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+ "maybe-uninit",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
+dependencies = [
+ "autocfg",
+ "cfg-if 0.1.10",
+ "crossbeam-utils",
+ "lazy_static 1.4.0",
+ "maybe-uninit",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
+dependencies = [
+ "cfg-if 0.1.10",
+ "crossbeam-utils",
+ "maybe-uninit",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
+dependencies = [
+ "autocfg",
+ "cfg-if 0.1.10",
+ "lazy_static 1.4.0",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.55+curl-7.83.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "diesel"
+version = "1.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "chrono",
+ "diesel_derives",
+ "pq-sys",
+ "r2d2",
+]
+
+[[package]]
+name = "diesel_derives"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[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 = "env_logger"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "failure"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
+dependencies = [
+ "backtrace",
+ "failure_derive",
+]
+
+[[package]]
+name = "failure_derive"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "fuchsia-zircon"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
+dependencies = [
+ "bitflags",
+ "fuchsia-zircon-sys",
+]
+
+[[package]]
+name = "fuchsia-zircon-sys"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+
+[[package]]
+name = "futures"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "gimli"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "humantime"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
+dependencies = [
+ "quick-error",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "iovec"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "irc"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eb7666c9ae95dc77b874467e347bde3789773b6f48887fb3384bfe29552b466"
+dependencies = [
+ "bufstream",
+ "bytes",
+ "chrono",
+ "encoding",
+ "failure",
+ "futures",
+ "log",
+ "native-tls",
+ "serde 1.0.137",
+ "serde_derive",
+ "tokio-codec",
+ "tokio-core",
+ "tokio-io",
+ "tokio-mockstream",
+ "tokio-timer 0.1.2",
+ "tokio-tls",
+ "toml",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+dependencies = [
+ "winapi 0.2.8",
+ "winapi-build",
+]
+
+[[package]]
+name = "lazy_static"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
+
+[[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.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
+
+[[package]]
+name = "libz-sys"
+version = "1.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linked-hash-map"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
+dependencies = [
+ "serde 0.8.23",
+ "serde_test",
+]
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
+[[package]]
+name = "lock_api"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "maybe-uninit"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.6.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
+dependencies = [
+ "cfg-if 0.1.10",
+ "fuchsia-zircon",
+ "fuchsia-zircon-sys",
+ "iovec",
+ "kernel32-sys",
+ "libc",
+ "log",
+ "miow",
+ "net2",
+ "slab 0.4.6",
+ "winapi 0.2.8",
+]
+
+[[package]]
+name = "mio-uds"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
+dependencies = [
+ "iovec",
+ "libc",
+ "mio",
+]
+
+[[package]]
+name = "miow"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
+dependencies = [
+ "kernel32-sys",
+ "net2",
+ "winapi 0.2.8",
+ "ws2_32-sys",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
+dependencies = [
+ "lazy_static 1.4.0",
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[package]]
+name = "net2"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "nom"
+version = "4.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
+dependencies = [
+ "memchr",
+ "version_check",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits 0.2.15",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
+dependencies = [
+ "num-traits 0.2.15",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.28.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+
+[[package]]
+name = "openssl"
+version = "0.10.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e"
+dependencies = [
+ "bitflags",
+ "cfg-if 1.0.0",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0"
+dependencies = [
+ "autocfg",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
+dependencies = [
+ "lock_api 0.3.4",
+ "parking_lot_core 0.6.2",
+ "rustc_version",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api 0.4.7",
+ "parking_lot_core 0.8.5",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
+dependencies = [
+ "cfg-if 0.1.10",
+ "cloudabi",
+ "libc",
+ "redox_syscall 0.1.57",
+ "rustc_version",
+ "smallvec 0.6.14",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+dependencies = [
+ "cfg-if 1.0.0",
+ "instant",
+ "libc",
+ "redox_syscall 0.2.13",
+ "smallvec 1.8.0",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "paroxysm"
+version = "0.1.0"
+dependencies = [
+ "chrono",
+ "config",
+ "crimp",
+ "diesel",
+ "env_logger",
+ "failure",
+ "irc",
+ "lazy_static 1.4.0",
+ "log",
+ "rand",
+ "regex",
+ "serde 1.0.137",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
+name = "pq-sys"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda"
+dependencies = [
+ "vcpkg",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r2d2"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
+dependencies = [
+ "log",
+ "parking_lot 0.11.2",
+ "scheduled-thread-pool",
+]
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom",
+ "libc",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[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",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "rust-ini"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
+
+[[package]]
+name = "schannel"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
+dependencies = [
+ "lazy_static 1.4.0",
+ "windows-sys",
+]
+
+[[package]]
+name = "scheduled-thread-pool"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
+dependencies = [
+ "parking_lot 0.11.2",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "security-framework"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "serde"
+version = "0.8.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
+
+[[package]]
+name = "serde"
+version = "1.0.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-hjson"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b833c5ad67d52ced5f5938b2980f32a9c1c5ef047f0b4fb3127e7a423c76153"
+dependencies = [
+ "lazy_static 0.2.11",
+ "linked-hash-map 0.3.0",
+ "num-traits 0.1.43",
+ "regex",
+ "serde 0.8.23",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde 1.0.137",
+]
+
+[[package]]
+name = "serde_test"
+version = "0.8.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5"
+dependencies = [
+ "serde 0.8.23",
+]
+
+[[package]]
+name = "slab"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
+
+[[package]]
+name = "slab"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
+
+[[package]]
+name = "smallvec"
+version = "0.6.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
+dependencies = [
+ "maybe-uninit",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+
+[[package]]
+name = "socket2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
+dependencies = [
+ "libc",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "unicode-xid",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if 1.0.0",
+ "fastrand",
+ "libc",
+ "redox_syscall 0.2.13",
+ "remove_dir_all",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "time"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "tokio"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
+dependencies = [
+ "bytes",
+ "futures",
+ "mio",
+ "num_cpus",
+ "tokio-codec",
+ "tokio-current-thread",
+ "tokio-executor",
+ "tokio-fs",
+ "tokio-io",
+ "tokio-reactor",
+ "tokio-sync",
+ "tokio-tcp",
+ "tokio-threadpool",
+ "tokio-timer 0.2.13",
+ "tokio-udp",
+ "tokio-uds",
+]
+
+[[package]]
+name = "tokio-codec"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b"
+dependencies = [
+ "bytes",
+ "futures",
+ "tokio-io",
+]
+
+[[package]]
+name = "tokio-core"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4"
+dependencies = [
+ "bytes",
+ "futures",
+ "iovec",
+ "log",
+ "mio",
+ "scoped-tls",
+ "tokio",
+ "tokio-executor",
+ "tokio-io",
+ "tokio-reactor",
+ "tokio-timer 0.2.13",
+]
+
+[[package]]
+name = "tokio-current-thread"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e"
+dependencies = [
+ "futures",
+ "tokio-executor",
+]
+
+[[package]]
+name = "tokio-executor"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
+dependencies = [
+ "crossbeam-utils",
+ "futures",
+]
+
+[[package]]
+name = "tokio-fs"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4"
+dependencies = [
+ "futures",
+ "tokio-io",
+ "tokio-threadpool",
+]
+
+[[package]]
+name = "tokio-io"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
+dependencies = [
+ "bytes",
+ "futures",
+ "log",
+]
+
+[[package]]
+name = "tokio-mockstream"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41bfc436ef8b7f60c19adf3df086330ae9992385e4d8c53b17a323cad288e155"
+dependencies = [
+ "futures",
+ "tokio-io",
+]
+
+[[package]]
+name = "tokio-reactor"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
+dependencies = [
+ "crossbeam-utils",
+ "futures",
+ "lazy_static 1.4.0",
+ "log",
+ "mio",
+ "num_cpus",
+ "parking_lot 0.9.0",
+ "slab 0.4.6",
+ "tokio-executor",
+ "tokio-io",
+ "tokio-sync",
+]
+
+[[package]]
+name = "tokio-sync"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
+dependencies = [
+ "fnv",
+ "futures",
+]
+
+[[package]]
+name = "tokio-tcp"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
+dependencies = [
+ "bytes",
+ "futures",
+ "iovec",
+ "mio",
+ "tokio-io",
+ "tokio-reactor",
+]
+
+[[package]]
+name = "tokio-threadpool"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-queue",
+ "crossbeam-utils",
+ "futures",
+ "lazy_static 1.4.0",
+ "log",
+ "num_cpus",
+ "slab 0.4.6",
+ "tokio-executor",
+]
+
+[[package]]
+name = "tokio-timer"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc"
+dependencies = [
+ "futures",
+ "slab 0.3.0",
+]
+
+[[package]]
+name = "tokio-timer"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
+dependencies = [
+ "crossbeam-utils",
+ "futures",
+ "slab 0.4.6",
+ "tokio-executor",
+]
+
+[[package]]
+name = "tokio-tls"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c"
+dependencies = [
+ "futures",
+ "native-tls",
+ "tokio-io",
+]
+
+[[package]]
+name = "tokio-udp"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82"
+dependencies = [
+ "bytes",
+ "futures",
+ "log",
+ "mio",
+ "tokio-codec",
+ "tokio-io",
+ "tokio-reactor",
+]
+
+[[package]]
+name = "tokio-uds"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0"
+dependencies = [
+ "bytes",
+ "futures",
+ "iovec",
+ "libc",
+ "log",
+ "mio",
+ "mio-uds",
+ "tokio-codec",
+ "tokio-io",
+ "tokio-reactor",
+]
+
+[[package]]
+name = "toml"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
+dependencies = [
+ "serde 1.0.137",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
+
+[[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 = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+
+[[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-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+
+[[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-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+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 = "ws2_32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
+dependencies = [
+ "winapi 0.2.8",
+ "winapi-build",
+]
+
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map 0.5.4",
+]
diff --git a/fun/paroxysm/Cargo.toml b/fun/paroxysm/Cargo.toml
new file mode 100644
index 0000000000..4d282285fd
--- /dev/null
+++ b/fun/paroxysm/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+authors = ["eeeeeta <eta@theta.eu.org>"]
+edition = "2018"
+name = "paroxysm"
+version = "0.1.0"
+
+[dependencies]
+chrono = "0.4"
+config = "0.9"
+crimp = "0.2"
+env_logger = "0.7"
+failure = "0.1"
+irc = "0.13"
+lazy_static = "1.4"
+log = "0.4"
+rand = "0.7"
+regex = "1.3"
+serde = { version = "1.0", features = [ "derive" ] }
+
+[dependencies.diesel]
+features = [ "postgres", "chrono", "r2d2" ]
+version = "1.4"
diff --git a/fun/paroxysm/OWNERS b/fun/paroxysm/OWNERS
new file mode 100644
index 0000000000..7f8beb1aa7
--- /dev/null
+++ b/fun/paroxysm/OWNERS
@@ -0,0 +1,3 @@
+inherited: true
+owners:
+  - eta
diff --git a/fun/paroxysm/README.md b/fun/paroxysm/README.md
new file mode 100644
index 0000000000..a595530982
--- /dev/null
+++ b/fun/paroxysm/README.md
@@ -0,0 +1,19 @@
+paroxysm
+========
+
+`paroxysm` is a bot for [internet relay chat
+(IRC)](https://en.wikipedia.org/wiki/Internet_Relay_Chat) that lets you store
+small pieces of information, called *factoids*, and retrieve them later. It's
+useful for organising frequently-used information to avoid repeating oneself in
+a busy chatroom, as well as making little todo lists or notes to self in a
+private chatroom.
+
+It was directly inspired by the
+[LearnDB](https://github.com/crawl/sequell/blob/master/docs/learndb.md)
+functionality offered in `##crawl` on chat.freenode.net, and uses similar
+syntax.
+
+## Usage instructions
+
+Will come soon; the project is very much still in beta, and is subject to
+change.
diff --git a/fun/paroxysm/default.nix b/fun/paroxysm/default.nix
new file mode 100644
index 0000000000..e4ce4df1ae
--- /dev/null
+++ b/fun/paroxysm/default.nix
@@ -0,0 +1,14 @@
+{ depot, pkgs, ... }:
+
+depot.third_party.naersk.buildPackage {
+  name = "paroxysm";
+  version = "0.0.2";
+  src = ./.;
+
+  buildInputs = with pkgs; [
+    openssl
+    pkgconfig
+    postgresql.lib
+    curl
+  ];
+}
diff --git a/fun/paroxysm/docker/default.nix b/fun/paroxysm/docker/default.nix
new file mode 100644
index 0000000000..cb5b2758ec
--- /dev/null
+++ b/fun/paroxysm/docker/default.nix
@@ -0,0 +1,7 @@
+{ depot, pkgs, ... }:
+
+pkgs.dockerTools.buildLayeredImage {
+  name = "paroxysm";
+  contents = [ depot.fun.paroxysm ];
+  config.Entrypoint = [ "${depot.fun.paroxysm}/bin/paroxysm" ];
+}
diff --git a/fun/paroxysm/migrations/20181209140247_initial/down.sql b/fun/paroxysm/migrations/20181209140247_initial/down.sql
new file mode 100644
index 0000000000..aa02f4f63f
--- /dev/null
+++ b/fun/paroxysm/migrations/20181209140247_initial/down.sql
@@ -0,0 +1,2 @@
+DROP TABLE entries;
+DROP TABLE keywords;
diff --git a/fun/paroxysm/migrations/20181209140247_initial/up.sql b/fun/paroxysm/migrations/20181209140247_initial/up.sql
new file mode 100644
index 0000000000..e8b52d5a9b
--- /dev/null
+++ b/fun/paroxysm/migrations/20181209140247_initial/up.sql
@@ -0,0 +1,15 @@
+CREATE TABLE keywords (
+	id SERIAL PRIMARY KEY,
+	name VARCHAR UNIQUE NOT NULL,
+	chan VARCHAR NOT NULL,
+	UNIQUE(name, chan)
+);
+
+CREATE TABLE entries (
+	id SERIAL PRIMARY KEY,
+	keyword_id INT NOT NULL REFERENCES keywords ON DELETE CASCADE,
+	idx INT NOT NULL,
+	text VARCHAR NOT NULL,
+	creation_ts TIMESTAMP NOT NULL,
+	created_by VARCHAR NOT NULL
+);
diff --git a/fun/paroxysm/migrations/20181218142013_fix_unique/down.sql b/fun/paroxysm/migrations/20181218142013_fix_unique/down.sql
new file mode 100644
index 0000000000..291a97c5ce
--- /dev/null
+++ b/fun/paroxysm/migrations/20181218142013_fix_unique/down.sql
@@ -0,0 +1 @@
+-- This file should undo anything in `up.sql`
\ No newline at end of file
diff --git a/fun/paroxysm/migrations/20181218142013_fix_unique/up.sql b/fun/paroxysm/migrations/20181218142013_fix_unique/up.sql
new file mode 100644
index 0000000000..4885ae5ede
--- /dev/null
+++ b/fun/paroxysm/migrations/20181218142013_fix_unique/up.sql
@@ -0,0 +1 @@
+ALTER TABLE keywords DROP CONSTRAINT IF EXISTS keywords_name_key;
diff --git a/fun/paroxysm/src/cfg.rs b/fun/paroxysm/src/cfg.rs
new file mode 100644
index 0000000000..cfb2e2073e
--- /dev/null
+++ b/fun/paroxysm/src/cfg.rs
@@ -0,0 +1,12 @@
+use serde::Deserialize;
+use std::collections::HashSet;
+
+#[derive(Deserialize)]
+pub struct Config {
+    pub database_url: String,
+    pub irc_config_path: String,
+    #[serde(default)]
+    pub admins: HashSet<String>,
+    #[serde(default)]
+    pub log_filter: Option<String>,
+}
diff --git a/fun/paroxysm/src/keyword.rs b/fun/paroxysm/src/keyword.rs
new file mode 100644
index 0000000000..fa40f5347a
--- /dev/null
+++ b/fun/paroxysm/src/keyword.rs
@@ -0,0 +1,219 @@
+use crate::models::{Entry, Keyword, NewEntry, NewKeyword};
+use diesel::pg::PgConnection;
+use diesel::prelude::*;
+use failure::{format_err, Error};
+use std::borrow::Cow;
+
+/// Maximum number of times we'll follow a `see: ` pointer.
+const RECURSION_LIMIT: usize = 5;
+
+pub struct KeywordDetails {
+    pub keyword: Keyword,
+    pub entries: Vec<Entry>,
+}
+
+impl KeywordDetails {
+    pub fn learn(&mut self, nick: &str, text: &str, dbc: &PgConnection) -> Result<usize, Error> {
+        let now = ::chrono::Utc::now().naive_utc();
+        let ins = NewEntry {
+            keyword_id: self.keyword.id,
+            idx: (self.entries.len() + 1) as _,
+            text,
+            creation_ts: now,
+            created_by: nick,
+        };
+        let new = {
+            use crate::schema::entries;
+            ::diesel::insert_into(entries::table)
+                .values(ins)
+                .get_result(dbc)?
+        };
+        self.entries.push(new);
+        Ok(self.entries.len())
+    }
+
+    pub fn process_moves(&mut self, moves: &[(i32, i32)], dbc: &PgConnection) -> Result<(), Error> {
+        for (oid, new_idx) in moves {
+            {
+                use crate::schema::entries::dsl::*;
+                ::diesel::update(entries.filter(id.eq(oid)))
+                    .set(idx.eq(new_idx))
+                    .execute(dbc)?;
+            }
+        }
+        self.entries = Self::get_entries(self.keyword.id, dbc)?;
+        Ok(())
+    }
+
+    pub fn swap(&mut self, idx_a: usize, idx_b: usize, dbc: &PgConnection) -> Result<(), Error> {
+        let mut moves = vec![];
+        for ent in self.entries.iter() {
+            if ent.idx == idx_a as i32 {
+                moves.push((ent.id, idx_b as i32));
+            }
+            if ent.idx == idx_b as i32 {
+                moves.push((ent.id, idx_a as i32));
+            }
+        }
+        if moves.len() != 2 {
+            Err(format_err!("Invalid swap operation."))?;
+        }
+        self.process_moves(&moves, dbc)?;
+        Ok(())
+    }
+
+    pub fn update(&mut self, idx: usize, val: &str, dbc: &PgConnection) -> Result<(), Error> {
+        let ent = self
+            .entries
+            .get_mut(idx.saturating_sub(1))
+            .ok_or(format_err!("No such element to update."))?;
+        {
+            use crate::schema::entries::dsl::*;
+            ::diesel::update(entries.filter(id.eq(ent.id)))
+                .set(text.eq(val))
+                .execute(dbc)?;
+        }
+        ent.text = val.to_string();
+        Ok(())
+    }
+
+    pub fn delete(&mut self, idx: usize, dbc: &PgConnection) -> Result<(), Error> {
+        // step 1: delete the element
+        {
+            let ent = self
+                .entries
+                .get(idx.saturating_sub(1))
+                .ok_or(format_err!("No such element to delete."))?;
+            {
+                use crate::schema::entries::dsl::*;
+                ::diesel::delete(entries.filter(id.eq(ent.id))).execute(dbc)?;
+            }
+        }
+        // step 2: move all the elements in front of it back one
+        let mut moves = vec![];
+        for ent in self.entries.iter() {
+            if idx > ent.idx as _ {
+                moves.push((ent.id, ent.idx.saturating_sub(1)));
+            }
+        }
+        self.process_moves(&moves, dbc)?;
+        Ok(())
+    }
+
+    pub fn add_zwsp_to_name(name: &str) -> Option<String> {
+        let second_index = name.char_indices().nth(1).map(|(i, _)| i)?;
+        let (start, end) = name.split_at(second_index);
+        Some(format!("{}​{}", start, end))
+    }
+
+    pub fn format_entry(&self, idx: usize) -> Option<String> {
+        self.format_entry_colours(idx, true)
+    }
+
+    pub fn format_entry_colours(&self, idx: usize, with_colours: bool) -> Option<String> {
+        if let Some(ent) = self.entries.get(idx.saturating_sub(1)) {
+            let gen_clr = if self.keyword.chan == "*" && with_colours {
+                "\x0307"
+            } else {
+                ""
+            };
+            let zwsp_name = Self::add_zwsp_to_name(&self.keyword.name)
+                .unwrap_or_else(|| self.keyword.name.clone());
+            Some(format!(
+                "{}{}{name}{}[{idx}/{total}]{}: {text} {}[{date}]{}",
+                if with_colours { "\x02" } else { "" },
+                gen_clr,
+                if with_colours { "\x0f\x0315" } else { "" },
+                if with_colours { "\x0f" } else { "" },
+                if with_colours { "\x0f\x0314" } else { "" },
+                if with_colours { "\x0f" } else { "" },
+                name = zwsp_name,
+                idx = idx,
+                total = self.entries.len(),
+                text = ent.text,
+                date = ent.creation_ts.date()
+            ))
+        } else {
+            None
+        }
+    }
+
+    pub fn get_or_create(word: &str, c: &str, dbc: &PgConnection) -> Result<Self, Error> {
+        if let Some(ret) = Self::get(word, c, dbc)? {
+            Ok(ret)
+        } else {
+            Ok(Self::create(word, c, dbc)?)
+        }
+    }
+
+    pub fn create(word: &str, c: &str, dbc: &PgConnection) -> Result<Self, Error> {
+        let val = NewKeyword {
+            name: word,
+            chan: c,
+        };
+        let ret: Keyword = {
+            use crate::schema::keywords;
+            ::diesel::insert_into(keywords::table)
+                .values(val)
+                .get_result(dbc)?
+        };
+        Ok(KeywordDetails {
+            keyword: ret,
+            entries: vec![],
+        })
+    }
+
+    fn get_entries(kid: i32, dbc: &PgConnection) -> Result<Vec<Entry>, Error> {
+        let entries: Vec<Entry> = {
+            use crate::schema::entries::dsl::*;
+            entries
+                .filter(keyword_id.eq(kid))
+                .order_by(idx.asc())
+                .load(dbc)?
+        };
+        Ok(entries)
+    }
+
+    fn get_inner<'a, T: Into<Cow<'a, str>>>(
+        word: T,
+        c: &str,
+        dbc: &PgConnection,
+        recursion_count: usize,
+    ) -> Result<Option<Self>, Error> {
+        let word = word.into();
+        let keyword: Option<Keyword> = {
+            use crate::schema::keywords::dsl::*;
+            keywords
+                .filter(name.ilike(word).and(chan.eq(c).or(chan.eq("*"))))
+                .first(dbc)
+                .optional()?
+        };
+        if let Some(k) = keyword {
+            let entries = Self::get_entries(k.id, dbc)?;
+            if let Some(e0) = entries.get(0) {
+                if e0.text.starts_with("see: ") {
+                    if recursion_count > RECURSION_LIMIT {
+                        // Oh dear.
+                        Err(format_err!("Halt. You're having a bit too much fun."))?
+                    }
+                    let new_word = e0.text.replace("see: ", "");
+                    return Self::get_inner(new_word, c, dbc, recursion_count + 1);
+                }
+            }
+            Ok(Some(KeywordDetails {
+                keyword: k,
+                entries,
+            }))
+        } else {
+            Ok(None)
+        }
+    }
+
+    pub fn get<'a, T: Into<Cow<'a, str>>>(
+        word: T,
+        c: &str,
+        dbc: &PgConnection,
+    ) -> Result<Option<Self>, Error> {
+        Self::get_inner(word, c, dbc, 0)
+    }
+}
diff --git a/fun/paroxysm/src/main.rs b/fun/paroxysm/src/main.rs
new file mode 100644
index 0000000000..46a215c225
--- /dev/null
+++ b/fun/paroxysm/src/main.rs
@@ -0,0 +1,395 @@
+// TODO(tazjin): Upgrade to a Diesel version with public derive
+// macros.
+#[macro_use]
+extern crate diesel;
+
+use crate::cfg::Config;
+use crate::keyword::KeywordDetails;
+use diesel::pg::PgConnection;
+use diesel::r2d2::{ConnectionManager, Pool};
+use failure::{format_err, Error};
+use irc::client::prelude::*;
+use lazy_static::lazy_static;
+use log::{debug, info, warn};
+use rand::rngs::ThreadRng;
+use rand::{thread_rng, Rng};
+use regex::{Captures, Regex};
+use std::collections::HashMap;
+use std::fmt::Display;
+
+mod cfg;
+mod keyword;
+mod models;
+mod schema;
+
+lazy_static! {
+    static ref LEARN_RE: Regex =
+        Regex::new(r#"^\?\?(?P<gen>!)?\s*(?P<subj>[^\[:]*):\s*(?P<val>.*)"#).unwrap();
+    static ref QUERY_RE: Regex =
+        Regex::new(r#"^\?\?\s*(?P<subj>[^\[:]*)(?P<idx>\[[^\]]+\])?"#).unwrap();
+    static ref QLAST_RE: Regex = Regex::new(r#"^\?\?\s*(?P<subj>[^\[:]*)!"#).unwrap();
+    static ref INCREMENT_RE: Regex =
+        Regex::new(r#"^\?\?(?P<gen>!)?\s*(?P<subj>[^\[:]*)(?P<incrdecr>\+\+|\-\-)"#).unwrap();
+    static ref MOVE_RE: Regex =
+        Regex::new(r#"^\?\?(?P<gen>!)?\s*(?P<subj>[^\[:]*)(?P<idx>\[[^\]]+\])->(?P<new_idx>.*)"#)
+            .unwrap();
+}
+
+pub struct App {
+    client: IrcClient,
+    pg: Pool<ConnectionManager<PgConnection>>,
+    rng: ThreadRng,
+    cfg: Config,
+    last_msgs: HashMap<String, HashMap<String, String>>,
+}
+
+impl App {
+    pub fn report_error<T: Display>(
+        &mut self,
+        nick: &str,
+        chan: &str,
+        msg: T,
+    ) -> Result<(), Error> {
+        self.client
+            .send_notice(nick, format!("[{}] \x0304Error:\x0f {}", chan, msg))?;
+        Ok(())
+    }
+
+    pub fn keyword_from_captures(
+        &mut self,
+        learn: &::regex::Captures,
+        nick: &str,
+        chan: &str,
+    ) -> Result<KeywordDetails, Error> {
+        let db = self.pg.get()?;
+        debug!("Fetching keyword for captures: {:?}", learn);
+        let subj = &learn["subj"];
+        let learn_chan = if learn.name("gen").is_some() {
+            "*"
+        } else {
+            chan
+        };
+        if !chan.starts_with("#") && learn_chan != "*" {
+            Err(format_err!("Only general entries may be taught via PM."))?;
+        }
+        debug!("Fetching keyword '{}' for chan {}", subj, learn_chan);
+        let kwd = KeywordDetails::get_or_create(subj, learn_chan, &db)?;
+        if kwd.keyword.chan == "*" && !self.cfg.admins.contains(nick) {
+            Err(format_err!(
+                "Only administrators can create or modify general entries."
+            ))?;
+        }
+        Ok(kwd)
+    }
+
+    pub fn handle_move(
+        &mut self,
+        target: &str,
+        nick: &str,
+        chan: &str,
+        mv: Captures,
+    ) -> Result<(), Error> {
+        let db = self.pg.get()?;
+        let idx = &mv["idx"];
+        let idx = match idx[1..(idx.len() - 1)].parse::<usize>() {
+            Ok(i) => i,
+            Err(e) => Err(format_err!("Could not parse index: {}", e))?,
+        };
+        let new_idx = match mv["new_idx"].parse::<i32>() {
+            Ok(i) => i,
+            Err(e) => Err(format_err!("Could not parse target index: {}", e))?,
+        };
+        let mut kwd = self.keyword_from_captures(&mv, nick, chan)?;
+        if new_idx < 0 {
+            kwd.delete(idx, &db)?;
+            self.client.send_notice(
+                target,
+                format!("\x02{}\x0f: Deleted entry {}.", kwd.keyword.name, idx),
+            )?;
+        } else {
+            kwd.swap(idx, new_idx as _, &db)?;
+            self.client.send_notice(
+                target,
+                format!(
+                    "\x02{}\x0f: Swapped entries {} and {}.",
+                    kwd.keyword.name, idx, new_idx
+                ),
+            )?;
+        }
+        Ok(())
+    }
+
+    pub fn handle_learn(
+        &mut self,
+        target: &str,
+        nick: &str,
+        chan: &str,
+        learn: Captures,
+    ) -> Result<(), Error> {
+        let db = self.pg.get()?;
+        let val = &learn["val"];
+        let mut kwd = self.keyword_from_captures(&learn, nick, chan)?;
+        let idx = kwd.learn(nick, val, &db)?;
+        self.client
+            .send_notice(target, kwd.format_entry(idx).unwrap())?;
+        Ok(())
+    }
+
+    pub fn handle_insert_last_quote(
+        &mut self,
+        target: &str,
+        nick: &str,
+        chan: &str,
+        qlast: Captures,
+    ) -> Result<(), Error> {
+        let db = self.pg.get()?;
+        let nick_to_grab = &qlast["subj"].to_ascii_lowercase();
+        let mut kwd = self.keyword_from_captures(&qlast, nick, chan)?;
+        let chan_lastmsgs = self
+            .last_msgs
+            .entry(chan.to_string())
+            .or_insert(HashMap::new());
+        // Use `nick` here, so things like "grfn: see glittershark" work.
+        let val = if let Some(last) = chan_lastmsgs.get(nick_to_grab) {
+            if last.starts_with("\x01ACTION ") {
+                // Yes, this is inefficient, but it's better than writing some hacky CTCP parsing
+                // code I guess (also, characters are hard, so just blindly slicing
+                // seems like a bad idea)
+                format!(
+                    "* {} {}",
+                    nick_to_grab,
+                    last.replace("\x01ACTION ", "").replace("\x01", "")
+                )
+            } else {
+                format!("<{}> {}", nick_to_grab, last)
+            }
+        } else {
+            Err(format_err!("I dunno what {} said...", kwd.keyword.name))?
+        };
+        let idx = kwd.learn(nick, &val, &db)?;
+        self.client
+            .send_notice(target, kwd.format_entry(idx).unwrap())?;
+        Ok(())
+    }
+
+    pub fn handle_increment(
+        &mut self,
+        target: &str,
+        nick: &str,
+        chan: &str,
+        icr: Captures,
+    ) -> Result<(), Error> {
+        let db = self.pg.get()?;
+        let mut kwd = self.keyword_from_captures(&icr, nick, chan)?;
+        let is_incr = &icr["incrdecr"] == "++";
+        let now = chrono::Utc::now().naive_utc().date();
+        let mut idx = None;
+        for (i, ent) in kwd.entries.iter().enumerate() {
+            if ent.creation_ts.date() == now {
+                if let Ok(val) = ent.text.parse::<i32>() {
+                    let val = if is_incr { val + 1 } else { val - 1 };
+                    idx = Some((i + 1, val));
+                }
+            }
+        }
+        if let Some((i, val)) = idx {
+            kwd.update(i, &val.to_string(), &db)?;
+            self.client
+                .send_notice(target, kwd.format_entry(i).unwrap())?;
+        } else {
+            let val = if is_incr { 1 } else { -1 };
+            let idx = kwd.learn(nick, &val.to_string(), &db)?;
+            self.client
+                .send_notice(target, kwd.format_entry(idx).unwrap())?;
+        }
+        Ok(())
+    }
+
+    pub fn handle_query(
+        &mut self,
+        target: &str,
+        _nick: &str,
+        chan: &str,
+        query: Captures,
+    ) -> Result<(), Error> {
+        let db = self.pg.get()?;
+        let subj = &query["subj"];
+        let idx = match query.name("idx") {
+            Some(i) => {
+                let i = i.as_str();
+                match &i[1..(i.len() - 1)] {
+                    "*" => Some(-1),
+                    x => x.parse::<usize>().map(|x| x as i32).ok(),
+                }
+            }
+            None => None,
+        };
+        debug!("Querying {} with idx {:?}", subj, idx);
+        match KeywordDetails::get(subj, chan, &db)? {
+            Some(kwd) => {
+                if let Some(mut idx) = idx {
+                    if idx == -1 {
+                        // 'get all entries' ('*' parses into this)
+                        // step 1: make a blob of all the quotes
+                        let mut data_to_upload = String::new();
+                        for i in 0..kwd.entries.len() {
+                            data_to_upload
+                                .push_str(&kwd.format_entry_colours(i + 1, false).unwrap());
+                            data_to_upload.push('\n');
+                        }
+                        // step 2: attempt to POST it to eta's pastebin
+                        // TODO(eta): make configurable
+                        let response = crimp::Request::put("https://eta.st/lx/upload")
+                            .user_agent("paroxysm/0.0.2 crimp/0.2")?
+                            .header("Linx-Expiry", "7200")? // 2 hours
+                            .body("text/plain", data_to_upload.as_bytes())
+                            .timeout(std::time::Duration::from_secs(2))?
+                            .send()?
+                            .as_string()?;
+                        // step 3: tell the world about it
+                        if response.status != 200 {
+                            Err(format_err!(
+                                "upload returned {}: {}",
+                                response.status,
+                                response.body
+                            ))?
+                        }
+                        self.client.send_notice(
+                            target,
+                            format!(
+                                "\x02{}\x0f: uploaded {} quotes to \x02\x0311{}\x0f (will expire in \x0224\x0f hours)",
+                                subj,
+                                kwd.entries.len(),
+                                response.body
+                            )
+                        )?;
+                    } else {
+                        if idx == 0 {
+                            idx = 1;
+                        }
+                        if let Some(ent) = kwd.format_entry(idx as _) {
+                            self.client.send_notice(target, ent)?;
+                        } else {
+                            let pluralised = if kwd.entries.len() == 1 {
+                                "entry"
+                            } else {
+                                "entries"
+                            };
+                            self.client.send_notice(
+                                target,
+                                format!(
+                                    "\x02{}\x0f: only has \x02\x0304{}\x0f {}",
+                                    subj,
+                                    kwd.entries.len(),
+                                    pluralised
+                                ),
+                            )?;
+                        }
+                    }
+                } else {
+                    let entry = if kwd.entries.len() < 2 {
+                        1 // because [1, 1) does not a range make
+                    } else {
+                        self.rng.gen_range(1, kwd.entries.len())
+                    };
+                    if let Some(ent) = kwd.format_entry(entry) {
+                        self.client.send_notice(target, ent)?;
+                    } else {
+                        self.client
+                            .send_notice(target, format!("\x02{}\x0f: no entries yet", subj))?;
+                    }
+                }
+            }
+            None => {
+                // If someone just posts "??????????", don't spam the channel with
+                // an error message (but do allow joke entries to appear if set).
+                if !subj.chars().all(|c| c == '?' || c == ' ') {
+                    self.client
+                        .send_notice(target, format!("\x02{}\x0f: never heard of it", subj))?;
+                }
+            }
+        }
+        Ok(())
+    }
+
+    pub fn handle_privmsg(&mut self, from: &str, chan: &str, msg: &str) -> Result<(), Error> {
+        let nick = from.split("!").next().ok_or(format_err!(
+            "Received PRIVMSG from a source without nickname (failed to split n!u@h)"
+        ))?;
+        let target = if chan.starts_with("#") { chan } else { nick };
+        debug!("[{}] <{}> {}", chan, nick, msg);
+        if let Some(learn) = LEARN_RE.captures(msg) {
+            self.handle_learn(target, nick, chan, learn)?;
+        } else if let Some(qlast) = QLAST_RE.captures(msg) {
+            self.handle_insert_last_quote(target, nick, chan, qlast)?;
+        } else if let Some(mv) = MOVE_RE.captures(msg) {
+            self.handle_move(target, nick, chan, mv)?;
+        } else if let Some(icr) = INCREMENT_RE.captures(msg) {
+            self.handle_increment(target, nick, chan, icr)?;
+        } else if let Some(query) = QUERY_RE.captures(msg) {
+            self.handle_query(target, nick, chan, query)?;
+        } else {
+            let chan_lastmsgs = self
+                .last_msgs
+                .entry(chan.to_string())
+                .or_insert(HashMap::new());
+            chan_lastmsgs.insert(nick.to_string().to_ascii_lowercase(), msg.to_string());
+        }
+        Ok(())
+    }
+
+    pub fn handle_msg(&mut self, m: Message) -> Result<(), Error> {
+        match m.command {
+            Command::PRIVMSG(channel, message) => {
+                if let Some(src) = m.prefix {
+                    if let Err(e) = self.handle_privmsg(&src, &channel, &message) {
+                        warn!("error handling command in {} (src {}): {}", channel, src, e);
+                        if let Some(nick) = src.split("!").next() {
+                            self.report_error(nick, &channel, e)?;
+                        }
+                    }
+                }
+            }
+            Command::INVITE(nick, channel) => {
+                if self.cfg.admins.contains(&nick) {
+                    info!("Joining {} after admin invite", channel);
+                    self.client.send_join(channel)?;
+                }
+            }
+            _ => {}
+        }
+        Ok(())
+    }
+}
+
+fn main() -> Result<(), Error> {
+    println!("[+] loading configuration");
+    let default_log_filter = "paroxysm=info".to_string();
+    let mut settings = config::Config::default();
+    settings.merge(config::Environment::with_prefix("PARX"))?;
+    let cfg: Config = settings.try_into()?;
+    let env = env_logger::Env::new()
+        .default_filter_or(cfg.log_filter.clone().unwrap_or(default_log_filter));
+    env_logger::init_from_env(env);
+    info!("paroxysm starting up");
+    info!("connecting to database at {}", cfg.database_url);
+    let pg = Pool::new(ConnectionManager::new(&cfg.database_url))?;
+    info!("connecting to IRC using config {}", cfg.irc_config_path);
+    let client = IrcClient::new(&cfg.irc_config_path)?;
+    client.identify()?;
+    let st = client.stream();
+    let mut app = App {
+        client,
+        pg,
+        cfg,
+        rng: thread_rng(),
+        last_msgs: HashMap::new(),
+    };
+    info!("running!");
+    st.for_each_incoming(|m| {
+        if let Err(e) = app.handle_msg(m) {
+            warn!("Error processing message: {}", e);
+        }
+    })?;
+    Ok(())
+}
diff --git a/fun/paroxysm/src/models.rs b/fun/paroxysm/src/models.rs
new file mode 100644
index 0000000000..721efbbb2e
--- /dev/null
+++ b/fun/paroxysm/src/models.rs
@@ -0,0 +1,36 @@
+use crate::schema::{entries, keywords};
+use chrono::NaiveDateTime;
+
+#[derive(Queryable)]
+pub struct Keyword {
+    pub id: i32,
+    pub name: String,
+    pub chan: String,
+}
+
+#[derive(Queryable)]
+pub struct Entry {
+    pub id: i32,
+    pub keyword_id: i32,
+    pub idx: i32,
+    pub text: String,
+    pub creation_ts: NaiveDateTime,
+    pub created_by: String,
+}
+
+#[derive(Insertable)]
+#[table_name = "keywords"]
+pub struct NewKeyword<'a> {
+    pub name: &'a str,
+    pub chan: &'a str,
+}
+
+#[derive(Insertable)]
+#[table_name = "entries"]
+pub struct NewEntry<'a> {
+    pub keyword_id: i32,
+    pub idx: i32,
+    pub text: &'a str,
+    pub creation_ts: NaiveDateTime,
+    pub created_by: &'a str,
+}
diff --git a/fun/paroxysm/src/schema.rs b/fun/paroxysm/src/schema.rs
new file mode 100644
index 0000000000..ef4044531e
--- /dev/null
+++ b/fun/paroxysm/src/schema.rs
@@ -0,0 +1,18 @@
+table! {
+    entries (id) {
+        id -> Int4,
+        keyword_id -> Int4,
+        idx -> Int4,
+        text -> Varchar,
+        creation_ts -> Timestamp,
+        created_by -> Varchar,
+    }
+}
+
+table! {
+    keywords (id) {
+        id -> Int4,
+        name -> Varchar,
+        chan -> Varchar,
+    }
+}