diff options
| -rw-r--r-- | Cargo.lock | 206 | ||||
| -rw-r--r-- | Cargo.toml | 36 | ||||
| -rw-r--r-- | configs/config.toml | 4 | ||||
| -rw-r--r-- | frippy_derive/src/lib.rs | 4 | ||||
| -rw-r--r-- | migrations/2017-11-03-164322_create_factoids/down.sql | 1 | ||||
| -rw-r--r-- | migrations/2017-11-03-164322_create_factoids/up.sql | 8 | ||||
| -rw-r--r-- | src/lib.rs | 11 | ||||
| -rw-r--r-- | src/main.rs | 51 | ||||
| -rw-r--r-- | src/plugins/factoids/database.rs | 151 | ||||
| -rw-r--r-- | src/plugins/factoids/mod.rs | 330 | ||||
| -rw-r--r-- | src/plugins/factoids/sandbox.lua | 51 | ||||
| -rw-r--r-- | src/plugins/factoids/utils.rs | 21 | ||||
| -rw-r--r-- | src/plugins/mod.rs | 3 | ||||
| -rw-r--r-- | src/plugins/url.rs | 61 | ||||
| -rw-r--r-- | src/utils.rs | 59 |
15 files changed, 906 insertions, 91 deletions
@@ -19,7 +19,7 @@ dependencies = [ "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -144,7 +144,7 @@ dependencies = [ "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -181,6 +181,63 @@ dependencies = [ ] [[package]] +name = "derive-error-chain" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "diesel" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_derives 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "mysqlclient-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "diesel_derives" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "diesel_infer_schema" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "infer_schema_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "diesel_migrations" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "migrations_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "migrations_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dotenv" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "derive-error-chain 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "dtoa" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -248,6 +305,22 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "encoding_rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "error-chain" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "failure" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -283,14 +356,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "frippy" version = "0.3.1" dependencies = [ + "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.186 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_infer_schema 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_migrations 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dotenv 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "frippy_derive 0.1.0", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "irc 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "irc 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rlua 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "select 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -344,6 +423,11 @@ dependencies = [ ] [[package]] +name = "gcc" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "getopts" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -372,7 +456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hyper" -version = "0.11.18" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -400,7 +484,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.18 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -424,6 +508,25 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "infer_schema_internals" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "infer_schema_macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dotenv 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "infer_schema_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -434,7 +537,7 @@ dependencies = [ [[package]] name = "irc" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -560,6 +663,24 @@ dependencies = [ ] [[package]] +name = "migrations_internals" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "migrations_macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "migrations_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "mime" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -608,13 +729,22 @@ dependencies = [ ] [[package]] +name = "mysqlclient-sys" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "native-tls" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.9.23 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "schannel 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -683,19 +813,19 @@ dependencies = [ [[package]] name = "openssl" -version = "0.9.23" +version = "0.9.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-sys" -version = "0.9.24" +version = "0.9.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -834,12 +964,13 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.18 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libflate 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -851,13 +982,23 @@ dependencies = [ "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "rlua" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "rustc-demangle" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -969,7 +1110,7 @@ dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1236,7 +1377,7 @@ dependencies = [ [[package]] name = "url" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1339,6 +1480,12 @@ dependencies = [ "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" "checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7" "checksum debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a032eac705ca39214d169f83e3d3da290af06d8d1d344d1baad2fd002dca4b3" +"checksum derive-error-chain 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9ca9ade651388daad7c993f005d0d20c4f6fe78c1cdc93e95f161c6f5ede4a" +"checksum diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925325c57038f2f14c0413bdf6a92ca72acff644959d0a1a9ebf8d19be7e9c01" +"checksum diesel_derives 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "28e2b2605ac6a3b9a586383f5f8b2b5f1108f07a421ade965b266289d2805e79" +"checksum diesel_infer_schema 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd41decf55679a8486a3ea5e5de20e8f2a48c76d57177cd05f37f4d166f48647" +"checksum diesel_migrations 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0928a7d6f27c849954185416bd59439837de55fbc89e2985b0e46e756ae4e3da" +"checksum dotenv 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d6f0e2bb24d163428d8031d3ebd2d2bd903ad933205a97d0f18c7c1aade380f3" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" "checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" @@ -1348,6 +1495,8 @@ dependencies = [ "checksum encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" "checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" "checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" +"checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d" +"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" "checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" @@ -1357,16 +1506,19 @@ dependencies = [ "checksum futf 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "51f93f3de6ba1794dcd5810b3546d004600a59a98266487c8407bc4b24e398f3" "checksum futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0bab5b5e94f5c31fc764ba5dd9ad16568aae5d4825538c01d6bca680c9bf94a7" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "b900c08c1939860ce8b54dc6a89e26e00c04c380fd0e09796799bd7f12861e05" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum html5ever 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a49d5001dd1bddf042ea41ed4e0a671d50b1bf187e66b349d7ec613bdce4ad90" "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" -"checksum hyper 0.11.18 (registry+https://github.com/rust-lang/crates.io-index)" = "c4f9b276c87e3fc1902a8bdfcce264c3f7c8a1c35e5e0c946062739f55026664" +"checksum hyper 0.11.19 (registry+https://github.com/rust-lang/crates.io-index)" = "47659bb1cb7ef3cd7b4f9bd2a11349b8d92097d34f9597a3c09e9bcefaf92b61" "checksum hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c81fa95203e2a6087242c38691a0210f23e9f3f8f944350bd676522132e2985" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "61bb90bdd39e3af69b0172dfc6130f6cd6332bf040fbb9bdd4401d37adbd48b8" +"checksum infer_schema_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8269258ae59744e7d5bdc44144c4b2319a355ff2ba34a3f629ae8888508a67c7" +"checksum infer_schema_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fcc56d0b579656088758b435c63ad286be3ec5de53f18b2bbe161e85fb3fd0b" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum irc 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cf38b935ba382932a429f918888568d1f94c24e2d6d2289b96b817085624a39d" +"checksum irc 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)" = "de55365493063dcc2f8193ff63b2c2852684275469be8c1524848a6ceda96044" "checksum itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3f2be4da1690a039e9ae5fd575f706a63ad5a2120f161b1d653c9da3930dd21" "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" @@ -1382,10 +1534,13 @@ dependencies = [ "checksum markup5ever 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff834ac7123c6a37826747e5ca09db41fd7a83126792021c2e636ad174bb77d3" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum migrations_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd916de6df9ac7e811e7e1ac28e0abfebe5205f3b29a7bda9ec8a41ee980a4eb" +"checksum migrations_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a550cfd76f6cfdf15a7b541893d7c79b68277b0b309f12179211a373a56e617" "checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd" "checksum mime_guess 2.0.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "013572795763289e14710c7b279461295f2673b2b338200c235082cd7ca9e495" "checksum mio 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "7da01a5e23070d92d99b1ecd1cd0af36447c6fd44b0fe283c2db199fa136724f" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum mysqlclient-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "879ce08e38739c54d87b7f8332a476004fe2a095f40a142a36f889779d9942b7" "checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" @@ -1394,8 +1549,8 @@ dependencies = [ "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" -"checksum openssl 0.9.23 (registry+https://github.com/rust-lang/crates.io-index)" = "169a4b9160baf9b9b1ab975418c673686638995ba921683a7f1e01470dcb8854" -"checksum openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "14ba54ac7d5a4eabd1d5f2c1fdeb7e7c14debfa669d94b983d01b465e767ba9e" +"checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" +"checksum openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5a41ce2f5f2d939c80decde8fcfcf5837c203ca6c06a553510a2fcb84fa3ef1" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" @@ -1413,8 +1568,9 @@ dependencies = [ "checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d2f806b0fcdabd98acd380dc8daef485e22bcb7cddc811d1337967f2528cf5" -"checksum reqwest 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "449c45f593ce9af9417c91e22f274fb8cea013bcf3d37ec1b5fb534b623bc708" -"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e" +"checksum reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "241faa9a8ca28a03cbbb9815a5d085f271d4c0168a19181f106aa93240c22ddb" +"checksum rlua 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4d6a9d2d1da31dd5cb4878789b924e46a600bdca4895b30f2efd6370d0dfc80e" +"checksum rustc-demangle 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f312457f8a4fa31d3581a6f423a70d6c33a10b95291985df55f1ff670ec10ce8" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum schannel 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "acece75e0f987c48863a6c792ec8b7d6c4177d4a027f8ccc72f849794f437016" @@ -1460,7 +1616,7 @@ dependencies = [ "checksum unicode_names 0.1.7 (git+https://github.com/Jokler/unicode_names?branch=update-to-latest-unicode)" = "<none>" "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2" +"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" "checksum utf-8 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" @@ -13,19 +13,49 @@ description = "An IRC Bot" name = "frippy" doc = false +[features] +mysql = ["diesel", "diesel_infer_schema", "diesel_migrations", "dotenv"] + [dependencies] irc = "0.13.2" log = "0.4.1" time = "0.1.39" +rlua = "0.12.2" reqwest = "0.8.4" select = "0.4.2" regex = "0.2.6" -lazy_static = "1.0.0" +lazy_static = "0.2.9" serde = "1.0.27" serde_json = "1.0.9" +chrono = "0.4.0" glob = "0.2.11" frippy_derive = { path = "frippy_derive" } -unicode_names = { git = 'https://github.com/Jokler/unicode_names', branch = 'update-to-latest-unicode' } -clippy = {version = "*", optional = true} +[dependencies.unicode_names] +git = 'https://github.com/Jokler/unicode_names' +branch = 'update-to-latest-unicode' + + +[dependencies.diesel] +version = "1.0.0-rc1" +optional = true +features = ["mysql", "chrono"] + +[dependencies.diesel_infer_schema] +version = "1.0.0-rc1" +optional = true +features = ["mysql"] + +[dependencies.diesel_migrations] +version = "1.0.0-rc1" +optional = true +features = ["mysql"] + +[dependencies.dotenv] +version = "0.10.1" +optional = true + +[dependencies.clippy] +version = "*" +optional = true diff --git a/configs/config.toml b/configs/config.toml index 02f86fd..56e5240 100644 --- a/configs/config.toml +++ b/configs/config.toml @@ -24,5 +24,7 @@ source = "https://github.com/Mavulp/frippy" #[channel_keys] #"#frippy" = "" -#[options] +[options] #disabled_plugins = "Url" +# If no database url is set a HashMap is used +#mysql_url = "mysql://user:password@address/db" diff --git a/frippy_derive/src/lib.rs b/frippy_derive/src/lib.rs index 704d6ef..2622f0b 100644 --- a/frippy_derive/src/lib.rs +++ b/frippy_derive/src/lib.rs @@ -17,8 +17,10 @@ pub fn derive_plugin(data: TokenStream) -> TokenStream { fn expand_plugin(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; + let generics = &ast.generics; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { - impl PluginName for #name { + impl #impl_generics PluginName for #name #ty_generics #where_clause { fn name(&self) -> &str { stringify!(#name) } diff --git a/migrations/2017-11-03-164322_create_factoids/down.sql b/migrations/2017-11-03-164322_create_factoids/down.sql new file mode 100644 index 0000000..58c2110 --- /dev/null +++ b/migrations/2017-11-03-164322_create_factoids/down.sql @@ -0,0 +1 @@ +DROP TABLE factoids diff --git a/migrations/2017-11-03-164322_create_factoids/up.sql b/migrations/2017-11-03-164322_create_factoids/up.sql new file mode 100644 index 0000000..784a3f4 --- /dev/null +++ b/migrations/2017-11-03-164322_create_factoids/up.sql @@ -0,0 +1,8 @@ +CREATE TABLE factoids ( + name VARCHAR(32) NOT NULL, + idx INTEGER NOT NULL, + content TEXT NOT NULL, + author VARCHAR(32) NOT NULL, + created TIMESTAMP NOT NULL, + PRIMARY KEY (name, idx) +) @@ -29,6 +29,10 @@ //! Frippy uses the [log](https://docs.rs/log) crate so you can log events //! which might be of interest. +#[cfg(feature = "mysql")] +#[macro_use] +extern crate diesel; + #[macro_use] extern crate frippy_derive; #[macro_use] @@ -37,12 +41,15 @@ extern crate lazy_static; extern crate log; extern crate irc; +extern crate chrono; +extern crate time; pub mod plugin; pub mod plugins; +pub mod utils; -use std::fmt; use std::collections::HashMap; +use std::fmt; use std::thread::spawn; use std::sync::Arc; @@ -138,7 +145,7 @@ impl Bot { Err(e) => return Err(format!("Failed to connect: {}", e)), }; - info!("Connected to server"); + info!("Connected to IRC server"); match client.identify() { Ok(_) => info!("Identified"), diff --git a/src/main.rs b/src/main.rs index 9a96791..cb4e384 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,16 @@ extern crate glob; extern crate irc; extern crate time; +#[cfg(feature = "mysql")] +#[macro_use] +extern crate diesel_migrations; +#[cfg(feature = "mysql")] +extern crate diesel; + #[macro_use] extern crate log; +use std::collections::HashMap; use log::{Level, LevelFilter, Metadata, Record}; use irc::client::reactor::IrcReactor; @@ -17,6 +24,9 @@ use glob::glob; use frippy::plugins; use frippy::Config; +#[cfg(feature = "mysql")] +embed_migrations!(); + struct Logger; impl log::Log for Logger { @@ -86,10 +96,16 @@ fn main() { // Open a connection and add work for each config for config in configs { let mut disabled_plugins = None; + let mut mysql_url = None; if let Some(ref options) = config.options { if let Some(disabled) = options.get("disabled_plugins") { - disabled_plugins = Some(disabled.split(',').map(|p| p.trim()).collect::<Vec<_>>()); + disabled_plugins = Some(disabled + .split(",") + .map(|p| p.trim()) + .collect::<Vec<_>>()); } + + mysql_url = options.get("mysql_url"); } let mut bot = frippy::Bot::new(); @@ -100,6 +116,39 @@ fn main() { bot.add_plugin(plugins::KeepNick::new()); bot.add_plugin(plugins::Tell::new()); + #[cfg(feature = "mysql")] + { + if let Some(url) = mysql_url { + use diesel; + use diesel::Connection; + match diesel::mysql::MysqlConnection::establish(url) { + Ok(conn) => { + match embedded_migrations::run(&conn) { + Ok(_) => { + bot.add_plugin(plugins::Factoids::new(conn)); + info!("Connected to MySQL server") + } + Err(e) => { + bot.add_plugin(plugins::Factoids::new(HashMap::new())); + error!("Failed to run migrations: {}", e); + } + } + } + Err(e) => error!("Failed to connect to database: {}", e), + } + } else { + bot.add_plugin(plugins::Factoids::new(HashMap::new())); + } + } + #[cfg(not(feature = "mysql"))] + { + if let Some(_) = mysql_url { + error!("frippy was not built with the mysql feature") + } + bot.add_plugin(plugins::Factoids::new(HashMap::new())); + } + + if let Some(disabled_plugins) = disabled_plugins { for name in disabled_plugins { if bot.remove_plugin(name).is_none() { diff --git a/src/plugins/factoids/database.rs b/src/plugins/factoids/database.rs new file mode 100644 index 0000000..b612d6f --- /dev/null +++ b/src/plugins/factoids/database.rs @@ -0,0 +1,151 @@ +#[cfg(feature = "mysql")] +extern crate dotenv; + +use std::collections::HashMap; + +#[cfg(feature = "mysql")] +use diesel::prelude::*; + +#[cfg(feature = "mysql")] +use diesel::mysql::MysqlConnection; + +use chrono::NaiveDateTime; + +pub enum DbResponse { + Success, + Failed(&'static str), +} + +#[cfg_attr(feature = "mysql", derive(Queryable))] +#[derive(Clone, Debug)] +pub struct Factoid { + pub name: String, + pub idx: i32, + pub content: String, + pub author: String, + pub created: NaiveDateTime, +} + +#[cfg(feature = "mysql")] +use self::mysql::factoids; +#[cfg_attr(feature = "mysql", derive(Insertable))] +#[cfg_attr(feature = "mysql", table_name="factoids")] +pub struct NewFactoid<'a> { + pub name: &'a str, + pub idx: i32, + pub content: &'a str, + pub author: &'a str, + pub created: NaiveDateTime, +} + + +pub trait Database: Send { + fn insert(&mut self, factoid: &NewFactoid) -> DbResponse; + fn get(&self, name: &str, idx: i32) -> Option<Factoid>; + fn delete(&mut self, name: &str, idx: i32) -> DbResponse; + fn count(&self, name: &str) -> Result<i32, &'static str>; +} + +// HashMap +impl Database for HashMap<(String, i32), Factoid> { + fn insert(&mut self, factoid: &NewFactoid) -> DbResponse { + let factoid = Factoid { + name: String::from(factoid.name), + idx: factoid.idx, + content: factoid.content.to_string(), + author: factoid.author.to_string(), + created: factoid.created, + }; + + let name = String::from(factoid.name.clone()); + match self.insert((name, factoid.idx), factoid) { + None => DbResponse::Success, + Some(_) => DbResponse::Failed("Factoid was overwritten"), + } + } + + fn get(&self, name: &str, idx: i32) -> Option<Factoid> { + self.get(&(String::from(name), idx)).map(|f| f.clone()) + } + + fn delete(&mut self, name: &str, idx: i32) -> DbResponse { + match self.remove(&(String::from(name), idx)) { + Some(_) => DbResponse::Success, + None => DbResponse::Failed("Factoid not found"), + } + } + + fn count(&self, name: &str) -> Result<i32, &'static str> { + Ok(self.iter() + .filter(|&(&(ref n, _), _)| n == name) + .count() as i32) + } +} + +// Diesel automatically define the factoids module as public. +// For now this is how we keep it private. +#[cfg(feature = "mysql")] +mod mysql { + table! { + factoids (name, idx) { + name -> Varchar, + idx -> Integer, + content -> Text, + author -> Varchar, + created -> Timestamp, + } + } +} + +#[cfg(feature = "mysql")] +impl Database for MysqlConnection { + fn insert(&mut self, factoid: &NewFactoid) -> DbResponse { + use diesel; + match diesel::insert_into(factoids::table) + .values(factoid) + .execute(self) { + Ok(_) => DbResponse::Success, + Err(e) => { + error!("DB Insertion Error: \"{}\"", e); + DbResponse::Failed("Failed to add factoid") + } + } + } + + fn get(&self, name: &str, idx: i32) -> Option<Factoid> { + factoids::table.find((name, idx)).first(self).ok() + } + + fn delete(&mut self, name: &str, idx: i32) -> DbResponse { + use diesel; + use self::factoids::columns; + match diesel::delete(factoids::table + .filter(columns::name.eq(name)) + .filter(columns::idx.eq(idx))) + .execute(self) { + Ok(v) => { + if v > 0 { + DbResponse::Success + } else { + DbResponse::Failed("Could not find any factoid with that name") + } + } + Err(e) => { + error!("DB Deletion Error: \"{}\"", e); + DbResponse::Failed("Failed to delete factoid") + } + } + } + + fn count(&self, name: &str) -> Result<i32, &'static str> { + let count: Result<i64, _> = factoids::table + .filter(factoids::columns::name.eq(name)) + .count() + .first(self); + + match count { + Ok(c) => Ok(c as i32), + Err(_) => Err("Database Error"), + } + } +} diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs new file mode 100644 index 0000000..49ace10 --- /dev/null +++ b/src/plugins/factoids/mod.rs @@ -0,0 +1,330 @@ +extern crate rlua; + +use std::fmt; +use std::str::FromStr; +use std::sync::Mutex; +use self::rlua::prelude::*; +use irc::client::prelude::*; +use irc::error::IrcError; + +use time; +use chrono::NaiveDateTime; + +use plugin::*; +pub mod database; +use self::database::{Database, DbResponse}; + +mod utils; +use self::utils::*; + +static LUA_SANDBOX: &'static str = include_str!("sandbox.lua"); + +#[derive(PluginName)] +pub struct Factoids<T: Database> { + factoids: Mutex<T>, +} + +macro_rules! try_lock { + ( $m:expr ) => { + match $m.lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + } + } +} + +impl<T: Database> Factoids<T> { + pub fn new(db: T) -> Factoids<T> { + Factoids { factoids: Mutex::new(db) } + } + + fn create_factoid(&self, name: &str, content: &str, author: &str) -> Result<&str, &str> { + let count = try_lock!(self.factoids).count(&name)?; + let tm = time::now().to_timespec(); + + let factoid = database::NewFactoid { + name: name, + idx: count, + content: content, + author: author, + created: NaiveDateTime::from_timestamp(tm.sec, tm.nsec as u32), + }; + + match try_lock!(self.factoids).insert(&factoid) { + DbResponse::Success => Ok("Successfully added"), + DbResponse::Failed(e) => Err(e), + } + } + + fn add(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<(), IrcError> { + if command.tokens.len() < 2 { + return self.invalid_command(client, command); + } + + let name = command.tokens.remove(0); + let content = command.tokens.join(" "); + + match self.create_factoid(&name, &content, &command.source) { + Ok(v) => client.send_notice(&command.source, v), + Err(e) => client.send_notice(&command.source, e), + } + } + + fn from_url(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<(), IrcError> { + if command.tokens.len() < 2 { + return self.invalid_command(client, command); + } + + let name = command.tokens.remove(0); + let url = &command.tokens[0]; + if let Some(content) = ::utils::download(1024, url) { + match self.create_factoid(&name, &content, &command.source) { + Ok(v) => client.send_notice(&command.source, v), + Err(e) => client.send_notice(&command.source, e), + } + } else { + client.send_notice(&command.source, "Failed to download.") + } + } + + fn remove(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<(), IrcError> { + if command.tokens.len() < 1 { + return self.invalid_command(client, command); + } + + let name = command.tokens.remove(0); + let count = match try_lock!(self.factoids).count(&name) { + Ok(c) => c, + Err(e) => return client.send_notice(&command.source, e), + }; + + match try_lock!(self.factoids).delete(&name, count - 1) { + DbResponse::Success => client.send_notice(&command.source, "Successfully removed"), + DbResponse::Failed(e) => client.send_notice(&command.source, &e), + } + } + + fn get(&self, client: &IrcClient, command: &PluginCommand) -> Result<(), IrcError> { + + let (name, idx) = match command.tokens.len() { + 0 => return self.invalid_command(client, command), + 1 => { + let name = &command.tokens[0]; + let count = match try_lock!(self.factoids).count(name) { + Ok(c) => c, + Err(e) => return client.send_notice(&command.source, e), + }; + + if count < 1 { + return client.send_notice(&command.source, &format!("{} does not exist", name)); + } + + (name, count - 1) + } + _ => { + let name = &command.tokens[0]; + let idx = match i32::from_str(&command.tokens[1]) { + Ok(i) => i, + Err(_) => return client.send_notice(&command.source, "Invalid index"), + }; + + (name, idx) + } + }; + + let factoid = match try_lock!(self.factoids).get(name, idx) { + Some(v) => v, + None => { + return client.send_notice(&command.source, + &format!("{}~{} does not exist", name, idx)) + } + }; + + let message = factoid.content.replace("\n", "|").replace("\r", ""); + + client.send_privmsg(&command.target, + &format!("{}: {}", factoid.name, message)) + } + + fn info(&self, client: &IrcClient, command: &PluginCommand) -> Result<(), IrcError> { + + match command.tokens.len() { + 0 => self.invalid_command(client, command), + 1 => { + let name = &command.tokens[0]; + let count = match try_lock!(self.factoids).count(name) { + Ok(c) => c, + Err(e) => return client.send_notice(&command.source, e), + }; + + match count { + 0 => client.send_notice(&command.source, &format!("{} does not exist", name)), + 1 => { + client.send_privmsg(&command.target, + &format!("There is 1 version of {}", name)) + } + _ => { + client.send_privmsg(&command.target, + &format!("There are {} versions of {}", count, name)) + } + } + } + _ => { + let name = &command.tokens[0]; + let idx = match i32::from_str(&command.tokens[1]) { + Ok(i) => i, + Err(_) => return client.send_notice(&command.source, "Invalid index"), + }; + + let factoid = match try_lock!(self.factoids).get(name, idx) { + Some(v) => v, + None => { + return client.send_notice(&command.source, + &format!("{}~{} does not exist", name, idx)) + } + }; + + client.send_privmsg(&command.target, + &format!("{}: Added by {} at {} UTC", + name, + factoid.author, + factoid.created)) + } + + } + } + + fn exec(&self, + client: &IrcClient, + mut command: PluginCommand, + error: bool) + -> Result<(), IrcError> { + if command.tokens.len() < 1 { + self.invalid_command(client, &command) + + } else { + let name = command.tokens.remove(0); + let count = match try_lock!(self.factoids).count(&name) { + Ok(c) => c, + Err(e) => return client.send_notice(&command.source, e), + }; + + let factoid = match try_lock!(self.factoids).get(&name, count - 1) { + Some(v) => v.content, + None if error => return self.invalid_command(client, &command), + None => return Ok(()), + }; + + let value = &if factoid.starts_with(">") { + let factoid = String::from(&factoid[1..]); + + if factoid.starts_with(">") { + factoid + } else { + match self.run_lua(&name, &factoid, &command) { + Ok(v) => v, + Err(e) => format!("\"{}\"", e), + } + } + } else { + factoid + }; + + client.send_privmsg(&command.target, &value.replace("\n", "|").replace("\r", "")) + } + } + + fn run_lua(&self, + name: &str, + code: &str, + command: &PluginCommand) + -> Result<String, rlua::Error> { + + let args = command + .tokens + .iter() + .filter(|x| !x.is_empty()) + .map(ToOwned::to_owned) + .collect::<Vec<String>>(); + + let lua = unsafe { Lua::new_with_debug() }; + let globals = lua.globals(); + + globals.set("factoid", code)?; + globals.set("download", lua.create_function(download)?)?; + globals.set("sleep", lua.create_function(sleep)?)?; + globals.set("args", args)?; + globals.set("input", command.tokens.join(" "))?; + globals.set("user", command.source.clone())?; + globals.set("channel", command.target.clone())?; + globals.set("output", lua.create_table()?)?; + + lua.exec::<()>(LUA_SANDBOX, Some(name))?; + let output: Vec<String> = globals.get::<_, Vec<String>>("output")?; + + Ok(output.join("|")) + } + + fn invalid_command(&self, client: &IrcClient, command: &PluginCommand) -> Result<(), IrcError> { + client.send_notice(&command.source, "Invalid Command") + } +} + +impl<T: Database> Plugin for Factoids<T> { + fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus { + match message.command { + Command::PRIVMSG(_, ref content) => if content.starts_with('!') { + ExecutionStatus::RequiresThread + } else { + ExecutionStatus::Done + }, + _ => ExecutionStatus::Done, + } + } + + fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), IrcError> { + if let Command::PRIVMSG(_, mut content) = message.command.clone() { + content.remove(0); + + let t: Vec<String> = content.split(' ').map(ToOwned::to_owned).collect(); + + let c = PluginCommand { + source: message.source_nickname().unwrap().to_string(), + target: message.response_target().unwrap().to_string(), + tokens: t, + }; + + self.exec(client, c, false) + + } else { + Ok(()) + } + } + + fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), IrcError> { + if command.tokens.is_empty() { + return self.invalid_command(client, &command); + } + + let sub_command = command.tokens.remove(0); + match sub_command.as_ref() { + "add" => self.add(client, &mut command), + "fromurl" => self.from_url(client, &mut command), + "remove" => self.remove(client, &mut command), + "get" => self.get(client, &command), + "info" => self.info(client, &command), + "exec" => self.exec(client, command, true), + _ => self.invalid_command(client, &command), + } + } + + fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> { + Err(String::from("Evaluation of commands is not implemented for Factoids at this time")) + } +} + +impl<T: Database> fmt::Debug for Factoids<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Factoids {{ ... }}") + } +} diff --git a/src/plugins/factoids/sandbox.lua b/src/plugins/factoids/sandbox.lua new file mode 100644 index 0000000..3fde65e --- /dev/null +++ b/src/plugins/factoids/sandbox.lua @@ -0,0 +1,51 @@ +function send(text) + local text = tostring(text) + local len = #output + if len < 1 then + output = { text } + else + output[len] = output[len] .. text + end +end + +function sendln(text) + send(text) + table.insert(output, "") +end + +local env = { print = send, + println = sendln, + args = args, + input = input, + user = user, + channel = channel, + request = download, + pairs = pairs, + table = table, + string = string, + tostring = tostring, + tonumber = tonumber, + math = math } + +local f, e = load(factoid, nil, nil, env) + +-- Check if the factoid timed out +function checktime(event, line) + if os.time() - time >= timeout then + error("Timed out after " .. timeout .. " seconds", 0) + else + -- Limit the cpu usage of factoids + sleep(1) + end +end + +-- Add timeout hook +time = os.time() +timeout = 30 +debug.sethook(checktime, "l") + +if f then + f() +else + error(e) +end diff --git a/src/plugins/factoids/utils.rs b/src/plugins/factoids/utils.rs new file mode 100644 index 0000000..fc86fb3 --- /dev/null +++ b/src/plugins/factoids/utils.rs @@ -0,0 +1,21 @@ +extern crate reqwest; + +use std::thread; +use std::time::Duration; + +use utils; +use super::rlua::prelude::*; + +use self::LuaError::RuntimeError; + +pub fn download(_: &Lua, url: String) -> Result<String, LuaError> { + match utils::download(1024, &url) { + Some(v) => Ok(v), + None => Err(RuntimeError(format!("Failed to download {}", url))), + } +} + +pub fn sleep(_: &Lua, dur: u64) -> Result<(), LuaError> { + thread::sleep(Duration::from_millis(dur)); + Ok(()) +} diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 834e5b1..5b32efd 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -4,6 +4,7 @@ mod url; mod emoji; mod tell; mod currency; +mod factoids; mod keepnick; pub use self::help::Help; @@ -11,4 +12,6 @@ pub use self::url::Url; pub use self::emoji::Emoji; pub use self::tell::Tell; pub use self::currency::Currency; +pub use self::factoids::Factoids; +pub use self::factoids::database; pub use self::keepnick::KeepNick; diff --git a/src/plugins/url.rs b/src/plugins/url.rs index 455aa4e..df4fdf2 100644 --- a/src/plugins/url.rs +++ b/src/plugins/url.rs @@ -1,5 +1,4 @@ extern crate regex; -extern crate reqwest; extern crate select; use irc::client::prelude::*; @@ -7,15 +6,11 @@ use irc::error::IrcError; use self::regex::Regex; -use std::str; -use std::io::{self, Read}; -use self::reqwest::Client; -use self::reqwest::header::Connection; - use self::select::document::Document; use self::select::predicate::Name; use plugin::*; +use utils; lazy_static! { static ref RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap(); @@ -43,64 +38,14 @@ impl Url { } } - fn download(&self, url: &str) -> Option<String> { - let response = Client::new().get(url).header(Connection::close()).send(); - - match response { - Ok(mut response) => { - let mut body = String::new(); - - // 500 kilobyte buffer - let mut buf = [0; 500 * 1000]; - let mut written = 0; - // Read until we reach EOF or max_kib KiB - loop { - let len = match response.read(&mut buf) { - Ok(0) => break, - Ok(len) => len, - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue, - Err(e) => { - debug!("Download from {:?} failed: {}", url, e); - return None; - } - }; - - let slice = match str::from_utf8(&buf[..len]) { - Ok(slice) => slice, - Err(e) => { - debug!("Failed to read bytes from {:?} as UTF8: {}", url, e); - return None; - } - }; - - body.push_str(slice); - written += len; - - // Check if the file is too large to download - if written > self.max_kib * 1024 { - debug!( - "Stopping download - File from {:?} is larger than {} KiB", - url, self.max_kib - ); - return None; - } - } - Some(body) // once told me - } - Err(e) => { - debug!("Bad response from {:?}: ({})", url, e); - None - } - } - } - fn url(&self, text: &str) -> Result<String, &str> { let url = match self.grep_url(text) { Some(url) => url, None => return Err("No Url was found."), }; - match self.download(&url) { + + match utils::download(self.max_kib, &url) { Some(body) => { let doc = Document::from(body.as_ref()); if let Some(title) = doc.find(Name("title")).next() { diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..59a5a54 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,59 @@ +extern crate reqwest; + +use std::str; +use std::io::{self, Read}; + +use self::reqwest::Client; +use self::reqwest::header::Connection; + +pub fn download(max_kib: usize, url: &str) -> Option<String> { + let response = Client::new() + .get(url) + .header(Connection::close()) + .send(); + + match response { + Ok(mut response) => { + let mut body = String::new(); + + // 500 kilobyte buffer + let mut buf = [0; 500 * 1000]; + let mut written = 0; + // Read until we reach EOF or max_kib KiB + loop { + let len = match response.read(&mut buf) { + Ok(0) => break, + Ok(len) => len, + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue, + Err(e) => { + debug!("Download from {:?} failed: {}", url, e); + return None; + } + }; + + let slice = match str::from_utf8(&buf[..len]) { + Ok(slice) => slice, + Err(e) => { + debug!("Failed to read bytes from {:?} as UTF8: {}", url, e); + return None; + } + }; + + body.push_str(slice); + written += len; + + // Check if the file is too large to download + if written > max_kib * 1024 { + debug!("Stopping download - File from {:?} is larger than {} KiB", url, max_kib); + return None; + } + + } + Some(body) + } + Err(e) => { + debug!("Bad response from {:?}: ({})", url, e); + return None; + } + } +} |
