aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock206
-rw-r--r--Cargo.toml36
-rw-r--r--configs/config.toml4
-rw-r--r--frippy_derive/src/lib.rs4
-rw-r--r--migrations/2017-11-03-164322_create_factoids/down.sql1
-rw-r--r--migrations/2017-11-03-164322_create_factoids/up.sql8
-rw-r--r--src/lib.rs11
-rw-r--r--src/main.rs51
-rw-r--r--src/plugins/factoids/database.rs151
-rw-r--r--src/plugins/factoids/mod.rs330
-rw-r--r--src/plugins/factoids/sandbox.lua51
-rw-r--r--src/plugins/factoids/utils.rs21
-rw-r--r--src/plugins/mod.rs3
-rw-r--r--src/plugins/url.rs61
-rw-r--r--src/utils.rs59
15 files changed, 906 insertions, 91 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ab10e52..09eb74a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index e320c55..44f70d7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)
+)
diff --git a/src/lib.rs b/src/lib.rs
index da14277..bd2e302 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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;
+ }
+ }
+}