aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml6
-rw-r--r--Cargo.lock989
-rw-r--r--Cargo.toml83
-rw-r--r--README.md8
-rw-r--r--bin/main.rs47
-rw-r--r--configs/config.toml5
-rw-r--r--frippy_derive/Cargo.toml (renamed from plugin_derive/Cargo.toml)7
-rw-r--r--frippy_derive/src/lib.rs109
-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--migrations/2018-02-25-154922_create_tells/down.sql1
-rw-r--r--migrations/2018-02-25-154922_create_tells/up.sql7
-rw-r--r--plugin_derive/src/lib.rs27
-rw-r--r--src/error.rs31
-rw-r--r--src/lib.rs339
-rw-r--r--src/main.rs191
-rw-r--r--src/plugin.rs167
-rw-r--r--src/plugins/currency.rs131
-rw-r--r--src/plugins/emoji.rs62
-rw-r--r--src/plugins/factoids/database.rs163
-rw-r--r--src/plugins/factoids/mod.rs342
-rw-r--r--src/plugins/factoids/sandbox.lua86
-rw-r--r--src/plugins/factoids/utils.rs25
-rw-r--r--src/plugins/help.rs32
-rw-r--r--src/plugins/keepnick.rs70
-rw-r--r--src/plugins/mod.rs15
-rw-r--r--src/plugins/tell/database.rs150
-rw-r--r--src/plugins/tell/mod.rs264
-rw-r--r--src/plugins/url.rs130
-rw-r--r--src/utils.rs65
31 files changed, 2824 insertions, 741 deletions
diff --git a/.gitignore b/.gitignore
index fdcd3a6..f6ed057 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/
-/plugin_derive/target/
+/frippy_derive/target/
# The top level Cargo.lock should handle everything
-/plugin_derive/Cargo.lock
+/frippy_derive/Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
diff --git a/.travis.yml b/.travis.yml
index cae9543..afb7296 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,6 +7,12 @@ matrix:
allow_failures:
- rust: nightly
+script:
+ - cargo test --verbose
+ - cargo test --verbose --features mysql
+
+cache: cargo
+
notifications:
email: false
irc:
diff --git a/Cargo.lock b/Cargo.lock
index a721a1d..2b6a8fa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,34 +4,28 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "advapi32-sys"
-version = "0.2.0"
+name = "aho-corasick"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "aho-corasick"
-version = "0.6.3"
+name = "antidote"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
[[package]]
name = "backtrace"
-version = "0.3.3"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
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)",
- "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (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.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -39,27 +33,27 @@ name = "backtrace-sys"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "cc 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
-version = "0.6.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
-version = "0.7.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
-version = "0.9.1"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -74,16 +68,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
-version = "1.1.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytes"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -91,14 +85,14 @@ name = "cargo_metadata"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 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)",
]
[[package]]
name = "cc"
-version = "1.0.2"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -111,36 +105,38 @@ name = "chrono"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy"
-version = "0.0.166"
+version = "0.0.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "clippy_lints 0.0.166 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clippy_lints 0.0.186 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy_lints"
-version = "0.0.166"
+version = "0.0.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
+ "if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "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.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -149,7 +145,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -157,39 +153,91 @@ name = "core-foundation-sys"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crc"
-version = "1.5.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "crc-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "crc-core"
-version = "0.1.0"
+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 = "crypt32-sys"
-version = "0.2.0"
+name = "derive-error-chain"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (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 = "dbghelp-sys"
-version = "0.2.0"
+name = "diesel"
+version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "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 = "dotenv"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "derive-error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -199,7 +247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "either"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -260,71 +308,132 @@ 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"
+
+[[package]]
+name = "error-chain"
+version = "0.11.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"
dependencies = [
- "backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "failure_derive"
+version = "0.1.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)",
+ "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "foreign-types"
-version = "0.2.0"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "frippy"
version = "0.3.1"
dependencies = [
- "clippy 0.0.166 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "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.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.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.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "plugin_derive 0.1.0",
- "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "reqwest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "humantime 1.1.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 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "r2d2-diesel 1.0.0 (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.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rlua 0.12.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)",
+ "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode_names 0.1.7 (git+https://github.com/Jokler/unicode_names?branch=update-to-latest-unicode)",
]
[[package]]
+name = "frippy_derive"
+version = "0.1.0"
+dependencies = [
+ "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "fuchsia-zircon"
-version = "0.2.1"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
-version = "0.2.0"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
[[package]]
name = "futures"
-version = "0.1.16"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-cpupool"
-version = "0.1.7"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+name = "gcc"
+version = "0.3.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "getopts"
-version = "0.2.15"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -333,31 +442,45 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "htmlescape"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "httparse"
-version = "1.2.3"
+version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "humantime"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "hyper"
-version = "0.11.6"
+version = "0.11.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.39 (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)",
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -365,13 +488,13 @@ name = "hyper-tls"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "hyper 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.3 (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.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)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -385,34 +508,58 @@ dependencies = [
]
[[package]]
+name = "if_chain"
+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.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "irc"
-version = "0.12.5"
+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)",
- "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
- "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.27 (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)",
"tokio-mockstream 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -421,7 +568,7 @@ name = "itertools"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "either 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -445,33 +592,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
-version = "0.2.9"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "lazy_static"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazycell"
-version = "0.5.1"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
-version = "0.2.32"
+version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libflate"
-version = "0.1.12"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "crc 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
-version = "0.3.8"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "log"
+version = "0.4.1"
+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 = "matches"
@@ -480,10 +643,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
-version = "1.0.2"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[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]]
@@ -491,12 +672,12 @@ name = "mime"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime_guess"
-version = "2.0.0-alpha.2"
+version = "2.0.0-alpha.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -507,16 +688,16 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.6.11"
+version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -535,15 +716,26 @@ 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.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "openssl 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "schannel 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "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.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)",
- "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -553,77 +745,85 @@ 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)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num"
-version = "0.1.40"
+version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
-version = "0.1.35"
+version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-iter"
-version = "0.1.34"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
-version = "0.1.40"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num_cpus"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "openssl"
-version = "0.9.20"
+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.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "openssl-sys 0.9.20 (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.26 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "openssl-sys"
-version = "0.9.20"
+version = "0.9.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "cc 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"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 = "percent-encoding"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -649,7 +849,7 @@ version = "0.7.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -667,11 +867,11 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "plugin_derive"
-version = "0.1.0"
+name = "proc-macro2"
+version = "0.2.3"
+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)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -680,10 +880,15 @@ version = "0.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+name = "quick-error"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "quine-mc_cluskey"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -694,79 +899,129 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "quote"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "r2d2"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "r2d2-diesel"
+version = "1.0.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)",
+ "r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand"
+version = "0.3.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "rand"
-version = "0.3.17"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
-version = "0.1.31"
+version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex"
-version = "0.2.2"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "relay"
-version = "0.1.0"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "remove_dir_all"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "reqwest"
-version = "0.8.0"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "hyper 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "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.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.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.4 (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)",
+ "mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.1.5 (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)",
"serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.5.1 (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)",
+ "tokio-tls 0.1.4 (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 = "rustc-demangle"
-version = "0.1.5"
+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_version"
-version = "0.1.7"
+name = "rustc-demangle"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
-]
[[package]]
name = "safemem"
@@ -775,40 +1030,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "schannel"
-version = "0.1.8"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "scoped-tls"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "secur32-sys"
+name = "scheduled-thread-pool"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+name = "scoped-tls"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "security-framework"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -818,16 +1067,11 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
-version = "0.1.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "semver"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -841,22 +1085,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
-version = "1.0.16"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
-version = "1.0.16"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive_internals 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive_internals"
-version = "0.16.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -865,13 +1109,13 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.4"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
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)",
- "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -881,8 +1125,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
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.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -916,6 +1160,16 @@ dependencies = [
]
[[package]]
+name = "syn"
+version = "0.12.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -924,61 +1178,70 @@ dependencies = [
]
[[package]]
+name = "synstructure"
+version = "0.6.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 = "take"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "tempdir"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
-version = "0.1.38"
+version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-core"
-version = "0.1.10"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-io"
-version = "0.1.3"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -986,8 +1249,8 @@ name = "tokio-mockstream"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -995,15 +1258,15 @@ name = "tokio-proto"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.3 (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)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1012,7 +1275,7 @@ name = "tokio-service"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1020,19 +1283,19 @@ name = "tokio-timer"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-tls"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.18 (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)",
]
[[package]]
@@ -1040,7 +1303,7 @@ name = "toml"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1053,10 +1316,10 @@ dependencies = [
[[package]]
name = "unicase"
-version = "2.0.0"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1078,9 +1341,14 @@ version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "unicode_names"
version = "0.1.7"
-source = "git+https://github.com/Jokler/unicode_names?branch=update-to-latest-unicode#6f3ad8f563cbd9a29c2857a451f1bf8cc0e105ee"
+source = "git+https://github.com/Jokler/unicode_names?branch=update-to-latest-unicode#d97b80c3c35b9f1d04085409087ef113c94cde17"
[[package]]
name = "unreachable"
@@ -1092,12 +1360,12 @@ dependencies = [
[[package]]
name = "url"
-version = "1.5.1"
+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)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1110,7 +1378,7 @@ name = "uuid"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1134,11 +1402,30 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "winapi"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1149,31 +1436,36 @@ dependencies = [
[metadata]
"checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45"
-"checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a"
-"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
-"checksum backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99f2ce94e22b8e664d95c57fff45b98a966c2252b60691d0b7aeeccd88d70983"
+"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
+"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
+"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
-"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
-"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
+"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4"
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
+"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f382711e76b9de6c744cc00d0497baba02fb00a787f088c879f01d09468e32"
"checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9"
-"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"
-"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6"
+"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
+"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9"
"checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b"
-"checksum cc 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ef4019bdb99c0c1ddd56c12c2f507c174d729c6915eca6bd9d27c42f3d93b0f4"
+"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9"
-"checksum clippy 0.0.166 (registry+https://github.com/rust-lang/crates.io-index)" = "86c1e9a7fd08987104fcca0ce1ab0a627006ed955ce14a87ffa32754c947fccb"
-"checksum clippy_lints 0.0.166 (registry+https://github.com/rust-lang/crates.io-index)" = "cbde64e1942d299a8ebe242d54567654db22aa06bdaae7aa3eb392db275172e0"
+"checksum clippy 0.0.186 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7b79c57f831e752f3667ae6115d02ed2d9e97a986ff76e5f04d613a8c0842a"
+"checksum clippy_lints 0.0.186 (registry+https://github.com/rust-lang/crates.io-index)" = "a3864104a4e6092e644b985dd7543e5f24e99aa7262f5ee400bcb17cfeec1bf5"
"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d"
-"checksum crc 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fba69ea0e15e720f7e1cfe1cf3bc55007fbd41e32b8ae11cfa343e7e5961e79a"
-"checksum crc-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "003d1170779d405378223470f5864b41b79a91969be1260e4de7b4ec069af69c"
-"checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec"
-"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
+"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
+"checksum derive-error-chain 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9ca9ade651388daad7c993f005d0d20c4f6fe78c1cdc93e95f161c6f5ede4a"
+"checksum derive-error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92183014af72c63aea490e66526c712bf1066ac50f66c9f34824f02483ec1d98"
+"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 dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a70de3c590ce18df70743cace1cf12565637a0b26fd8b04ef10c7d33fdc66cdc"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
-"checksum either 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e311a7479512fbdf858fb54d91ec59f3b9f85bc0113659f46bba12b199d273ce"
+"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"
"checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
"checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
@@ -1181,106 +1473,135 @@ 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 foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d"
-"checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159"
-"checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
-"checksum futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "05a23db7bd162d4e8265968602930c476f688f0c180b44bdaf55e0cb2c687558"
-"checksum futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e86f49cc0d92fe1b97a5980ec32d56208272cbb00f15044ea9e2799dde766fdf"
-"checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9"
+"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
+"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"
+"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
+"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+"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 httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
-"checksum hyper 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b45eac8b696d59491b079bd04fcb0f3488c0f6ed62dcb36bcfea8a543e9cdc3"
+"checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
+"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37"
+"checksum humantime 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5369e01a05e3404c421b5d6dcfea6ecf7d5e65eba8a275948151358cd8282042"
+"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 iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7"
-"checksum irc 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)" = "484fea2147dd042cdb3dd9e707af9cc9d8f46a4adc04e553ff985203a955ed6e"
+"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.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"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
-"checksum lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9e5e58fa1a4c3b915a561a78a22ee0cac6ab97dca2504428bc1cb074375f8d5"
-"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b"
-"checksum libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "56cce3130fd040c28df6f495c8492e5ec5808fb4c9093c310df02b0c8f030148"
-"checksum libflate 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ae46bcdafa496981e996e57c5be82c0a7f130a071323764c6faa4803619f1e67"
-"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
+"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
+"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
+"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
+"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
+"checksum libflate 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1a429b86418868c7ea91ee50e9170683f47fd9d94f5375438ec86ec3adb74e8e"
+"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
+"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
-"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
+"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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27a5e6679a0614e25adc14c6434ba84e41632b765a6d9cb2031a0cca682699ae"
-"checksum mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0e8411968194c7b139e9105bc4ae7db0bae232af087147e72f0616ebf5fdb9cb"
+"checksum mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130ea3c9c1b65dba905ab5a4d9ac59234a9585c24d135f264e187fe7336febbd"
+"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 native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04b781c9134a954c84f0594b9ab3f5606abc516030388e8511887ef4c204a1e5"
+"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.40 (registry+https://github.com/rust-lang/crates.io-index)" = "a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525"
-"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
-"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
-"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0"
-"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
-"checksum openssl 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)" = "8bf434ff6117485dc16478d77a4f5c84eccc9c3645c4da8323b287ad6a15a638"
-"checksum openssl-sys 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)" = "0ad395f1cee51b64a8d07cc8063498dc7554db62d5f3ca87a67f4eed2791d0c8"
-"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
+"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
+"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe"
+"checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593"
+"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.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"
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
+"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
"checksum pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "378e941dbd392c101f2cb88097fa4d7167bc421d4b88de3ff7dbee503bc3233b"
+"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
"checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
-"checksum rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "61efcbcd9fa8d8fbb07c84e34a8af18a1ff177b449689ad38a6e9457ecc7b2ae"
-"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509"
-"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
-"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
-"checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5"
-"checksum reqwest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "194fe0d39dea7f89738707bf70e9f3ed47e8aca47d4b2eeaad6ac7831d2d390b"
-"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
-"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
+"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
+"checksum r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f9078ca6a8a5568ed142083bb2f7dc9295b69d16f867ddcc9849e51b17d8db46"
+"checksum r2d2-diesel 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9c29bad92da76d02bc2c020452ebc3a3fe6fa74cfab91e711c43116e4fb1a3"
+"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
+"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
+"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
+"checksum regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5be5347bde0c48cfd8c3fdc0766cdfe9d8a755ef84d620d6794c778c91de8b2b"
+"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.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 safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
-"checksum schannel 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7554288337c1110e34d7a2433518d889374c1de1a45f856b7bcddb03702131fc"
+"checksum schannel 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "acece75e0f987c48863a6c792ec8b7d6c4177d4a027f8ccc72f849794f437016"
+"checksum scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a2ff3fc5223829be817806c6441279c676e454cc7da608faf03b0ccc09d3889"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
-"checksum secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f412dfa83308d893101dd59c10d6fda8283465976c28c287c5c855bf8d216bc"
"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332"
"checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead"
-"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-"checksum serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "e11a631f964d4e6572712ea12075fb1d65eeef42b0058884195b430ac1e26809"
-"checksum serde_derive 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "1a51d54c805fbc8e12b603d1ba51eaed3195862976be468888ab0e4995d0000e"
-"checksum serde_derive_internals 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd381f6d01a6616cdba8530492d453b7761b456ba974e98768a18cad2cd76f58"
-"checksum serde_json 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ee28c1d94a7745259b767ca9e5b95d55bafbd3205ca3acb978cad84a6ed6bc62"
+"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"
+"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0"
+"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5"
+"checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb"
"checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480"
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d"
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
+"checksum syn 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)" = "517f6da31bc53bf080b9a77b29fbd0ff8da2f5a2ebd24c73c2238274a94ac7cb"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
+"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd"
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
-"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
-"checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14"
-"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520"
-"checksum tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c843a027f7c1df5f81e7734a0df3f67bf329411781ebf36393ce67beef6071e3"
-"checksum tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ab83e7adb5677e42e405fa4ceff75659d93c4d7d7dd22f52fcec59ee9f02af"
+"checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e"
+"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
+"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
+"checksum tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "52b4e32d8edbf29501aabb3570f027c6ceb00ccef6538f4bddba0200503e74e8"
+"checksum tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b9532748772222bf70297ec0e2ad0f17213b4a7dd0e6afb68e0a0768f69f4e4f"
"checksum tokio-mockstream 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41bfc436ef8b7f60c19adf3df086330ae9992385e4d8c53b17a323cad288e155"
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
"checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc"
-"checksum tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d88e411cac1c87e405e4090be004493c5d8072a370661033b1a64ea205ec2e13"
+"checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913"
"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e"
"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
-"checksum unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e01da42520092d0cd2d6ac3ae69eb21a22ad43ff195676b86f8c37f487d6b80"
+"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
+"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unicode_names 0.1.7 (git+https://github.com/Jokler/unicode_names?branch=update-to-latest-unicode)" = "<none>"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
-"checksum url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb819346883532a271eb626deb43c4a1bb4c4dd47c519bd78137c3e72a4fe27"
+"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7"
"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"
"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
diff --git a/Cargo.toml b/Cargo.toml
index eecfa86..192939a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,29 +9,70 @@ keywords = ["irc", "bot"]
categories = ["network-programming"]
description = "An IRC Bot"
-[lib]
-name = "frippy"
-path = "src/lib.rs"
-
[[bin]]
name = "frippy"
-path = "bin/main.rs"
doc = false
+[features]
+mysql = [
+ "diesel",
+ "diesel_infer_schema",
+ "diesel_migrations",
+ "dotenv",
+ "r2d2",
+ "r2d2-diesel"
+]
+
[dependencies]
-irc = "0.12.5"
-tokio-core = "0.1.10"
-futures = "0.1.16"
-log = "0.3.8"
-time = "0.1"
-reqwest = "0.8.0"
-regex = "0.2.2"
-lazy_static = "0.2.9"
-serde = "1.0.15"
-serde_json = "1.0.3"
-glob = "0.2"
-
-plugin_derive = { path = "plugin_derive" }
-unicode_names = { git = 'https://github.com/Jokler/unicode_names', branch = 'update-to-latest-unicode' }
-
-clippy = {version = "*", optional = true}
+irc = "0.13.4"
+log = "0.4.1"
+time = "0.1.39"
+humantime = "1.1.0"
+rlua = "0.12.2"
+reqwest = "0.8.5"
+regex = "0.2.6"
+lazy_static = "1.0.0"
+serde = "1.0.27"
+serde_json = "1.0.9"
+chrono = "0.4.0"
+glob = "0.2.11"
+failure = "0.1.1"
+
+frippy_derive = { path = "frippy_derive" }
+htmlescape = "0.3.1"
+
+[dependencies.unicode_names]
+git = 'https://github.com/Jokler/unicode_names'
+branch = 'update-to-latest-unicode'
+
+
+[dependencies.diesel]
+version = "1.1.1"
+optional = true
+features = ["mysql", "chrono"]
+
+[dependencies.diesel_infer_schema]
+version = "1.1.0"
+optional = true
+features = ["mysql"]
+
+[dependencies.diesel_migrations]
+version = "1.1.0"
+optional = true
+features = ["mysql"]
+
+[dependencies.r2d2]
+version = "0.8.2"
+optional = true
+
+[dependencies.r2d2-diesel]
+version = "1.0.0"
+optional = true
+
+[dependencies.dotenv]
+version = "0.11.0"
+optional = true
+
+[dependencies.clippy]
+version = "*"
+optional = true
diff --git a/README.md b/README.md
index 0376967..0f76302 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,6 @@
-# frippy
-IRC Bot
+# frippy [![Build Status](https://travis-ci.org/Mavulp/frippy.svg?branch=master)](https://travis-ci.org/Mavulp/frippy)
+Frippy is an IRC bot which sends emojis used to the channel they were received from,
+lets you convert from one currency to another and checks the title of html pages
+linked in the channel.
+
+Check the config.toml file to get an idea of how to set the bot up.
diff --git a/bin/main.rs b/bin/main.rs
deleted file mode 100644
index 86910c0..0000000
--- a/bin/main.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-extern crate frippy;
-extern crate log;
-extern crate time;
-
-use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata};
-
-struct Logger;
-
-impl log::Log for Logger {
- fn enabled(&self, metadata: &LogMetadata) -> bool {
- metadata.target().contains("frippy")
- }
-
- fn log(&self, record: &LogRecord) {
- if self.enabled(record.metadata()) {
- if record.metadata().level() >= LogLevel::Debug {
- println!("[{}]({}) {} -> {}",
- time::now().rfc822(),
- record.level(),
- record.target(),
- record.args());
- } else {
- println!("[{}]({}) {}",
- time::now().rfc822(),
- record.level(),
- record.args());
- }
- }
- }
-}
-
-fn main() {
-
- let log_level = if cfg!(debug_assertions) {
- LogLevelFilter::Debug
- } else {
- LogLevelFilter::Info
- };
-
- log::set_logger(|max_log_level| {
- max_log_level.set(log_level);
- Box::new(Logger)
- })
- .unwrap();
-
- frippy::run();
-}
diff --git a/configs/config.toml b/configs/config.toml
index 5ad66e2..56e5240 100644
--- a/configs/config.toml
+++ b/configs/config.toml
@@ -23,3 +23,8 @@ source = "https://github.com/Mavulp/frippy"
#[channel_keys]
#"#frippy" = ""
+
+[options]
+#disabled_plugins = "Url"
+# If no database url is set a HashMap is used
+#mysql_url = "mysql://user:password@address/db"
diff --git a/plugin_derive/Cargo.toml b/frippy_derive/Cargo.toml
index 0b62f9f..b258f57 100644
--- a/plugin_derive/Cargo.toml
+++ b/frippy_derive/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "plugin_derive"
+name = "frippy_derive"
version = "0.1.0"
authors = ["Jokler <jokler.contact@gmail.com>"]
@@ -7,5 +7,6 @@ authors = ["Jokler <jokler.contact@gmail.com>"]
proc-macro = true
[dependencies]
-syn = "0.11.11"
-quote = "0.3.15"
+syn = "0.12.13"
+quote = "0.4.2"
+failure = "0.1.1"
diff --git a/frippy_derive/src/lib.rs b/frippy_derive/src/lib.rs
new file mode 100644
index 0000000..efa349a
--- /dev/null
+++ b/frippy_derive/src/lib.rs
@@ -0,0 +1,109 @@
+//! Provides the plugin derive macro
+#![recursion_limit="128"]
+
+extern crate proc_macro;
+extern crate syn;
+#[macro_use]
+extern crate quote;
+
+extern crate failure;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(PluginName)]
+pub fn derive_plugin(data: TokenStream) -> TokenStream {
+ let ast = syn::parse(data).unwrap();
+ let gen = expand_plugin(&ast);
+ gen.into()
+}
+
+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 #impl_generics PluginName for #name #ty_generics #where_clause {
+ fn name(&self) -> &str {
+ stringify!(#name)
+ }
+ }
+ }
+}
+
+#[proc_macro_derive(Error, attributes(error))]
+pub fn derive_error(data: TokenStream) -> TokenStream {
+ let ast = syn::parse(data).unwrap();
+ let tokens = expand_error(&ast);
+ // panic!("{}", tokens.to_string());
+ tokens.into()
+}
+
+fn expand_error(ast: &syn::DeriveInput) -> quote::Tokens {
+ if let syn::Data::Enum(_) = ast.data {
+ } else {
+ panic!("Error should only be derived on ErrorKind enums");
+ };
+
+ let mut name = None;
+ for attr in &ast.attrs {
+ if let Some(syn::Meta::NameValue(name_value)) = attr.interpret_meta() {
+ if "error" == name_value.ident.to_string() {
+ if let syn::Lit::Str(lit) = name_value.lit {
+ name = Some(lit.value());
+ }
+ }
+ }
+ };
+
+ let struct_name = if let Some(name) = name {
+ syn::Ident::from(name)
+ } else {
+ panic!("Define the error attribute for all Error derives");
+ };
+
+ let enum_name = &ast.ident;
+
+ quote! {
+ #[derive(Debug)]
+ pub struct #struct_name {
+ inner: ::failure::Context<#enum_name>,
+ }
+
+ impl #struct_name {
+ pub fn kind(&self) -> #enum_name {
+ *self.inner.get_context()
+ }
+ }
+
+ impl From<#enum_name> for #struct_name {
+ fn from(kind: #enum_name) -> #struct_name {
+ #struct_name {
+ inner: ::failure::Context::new(kind),
+ }
+ }
+ }
+
+ impl From<::failure::Context<#enum_name>> for #struct_name {
+ fn from(inner: ::failure::Context<#enum_name>) -> #struct_name {
+ #struct_name { inner: inner }
+ }
+ }
+
+ impl ::failure::Fail for #struct_name {
+ fn cause(&self) -> Option<&::failure::Fail> {
+ self.inner.cause()
+ }
+
+ fn backtrace(&self) -> Option<&::failure::Backtrace> {
+ self.inner.backtrace()
+ }
+ }
+
+ impl ::std::fmt::Display for #struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ use std::fmt;
+ fmt::Display::fmt(&self.inner, f)
+ }
+ }
+ }
+}
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/migrations/2018-02-25-154922_create_tells/down.sql b/migrations/2018-02-25-154922_create_tells/down.sql
new file mode 100644
index 0000000..73c9b9b
--- /dev/null
+++ b/migrations/2018-02-25-154922_create_tells/down.sql
@@ -0,0 +1 @@
+DROP TABLE tells
diff --git a/migrations/2018-02-25-154922_create_tells/up.sql b/migrations/2018-02-25-154922_create_tells/up.sql
new file mode 100644
index 0000000..9de037c
--- /dev/null
+++ b/migrations/2018-02-25-154922_create_tells/up.sql
@@ -0,0 +1,7 @@
+CREATE TABLE tells (
+ id SERIAL PRIMARY KEY,
+ sender VARCHAR(32) NOT NULL,
+ receiver VARCHAR(32) NOT NULL,
+ time TIMESTAMP NOT NULL,
+ message VARCHAR(512) NOT NULL
+)
diff --git a/plugin_derive/src/lib.rs b/plugin_derive/src/lib.rs
deleted file mode 100644
index 704d6ef..0000000
--- a/plugin_derive/src/lib.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-
-//! Provides the plugin derive macro
-
-extern crate proc_macro;
-extern crate syn;
-#[macro_use]
-extern crate quote;
-
-use proc_macro::TokenStream;
-
-#[proc_macro_derive(PluginName)]
-pub fn derive_plugin(data: TokenStream) -> TokenStream {
- let ast = syn::parse_derive_input(&data.to_string()).unwrap();
- let gen = expand_plugin(&ast);
- gen.parse().unwrap()
-}
-
-fn expand_plugin(ast: &syn::DeriveInput) -> quote::Tokens {
- let name = &ast.ident;
- quote! {
- impl PluginName for #name {
- fn name(&self) -> &str {
- stringify!(#name)
- }
- }
- }
-}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..36d5724
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,31 @@
+//! Errors for `frippy` crate using `failure`.
+
+use failure::Fail;
+
+pub fn log_error(e: FrippyError) {
+ let text = e.causes()
+ .skip(1)
+ .fold(format!("{}", e), |acc, err| format!("{}: {}", acc, err));
+ error!("{}", text);
+}
+
+/// The main crate-wide error type.
+#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
+#[error = "FrippyError"]
+pub enum ErrorKind {
+ /// Connection error
+ #[fail(display = "A connection error occured")]
+ Connection,
+
+ /// A Url error
+ #[fail(display = "A Url error has occured")]
+ Url,
+
+ /// A Tell error
+ #[fail(display = "A Tell error has occured")]
+ Tell,
+
+ /// A Factoids error
+ #[fail(display = "A Factoids error has occured")]
+ Factoids,
+}
diff --git a/src/lib.rs b/src/lib.rs
index 324e273..ebadb86 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,135 +1,196 @@
-#![cfg_attr(feature="clippy", feature(plugin))]
-#![cfg_attr(feature="clippy", plugin(clippy))]
+#![cfg_attr(feature = "clippy", feature(plugin))]
+#![cfg_attr(feature = "clippy", plugin(clippy))]
//! Frippy is an IRC bot that runs plugins on each message
//! received.
//!
-//! ## Example
+//! ## Examples
//! ```no_run
-//! extern crate frippy;
+//! # extern crate irc;
+//! # extern crate frippy;
+//! # fn main() {
+//! use frippy::{plugins, Config, Bot};
+//! use irc::client::reactor::IrcReactor;
//!
-//! frippy::run();
+//! let config = Config::load("config.toml").unwrap();
+//! let mut reactor = IrcReactor::new().unwrap();
+//! let mut bot = Bot::new();
+//!
+//! bot.add_plugin(plugins::help::Help::new());
+//! bot.add_plugin(plugins::emoji::Emoji::new());
+//! bot.add_plugin(plugins::currency::Currency::new());
+//!
+//! bot.connect(&mut reactor, &config).unwrap();
+//! reactor.run().unwrap();
+//! # }
//! ```
//!
//! # Logging
//! 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 log;
+extern crate diesel;
+#[cfg(feature = "mysql")]
+extern crate r2d2;
+#[cfg(feature = "mysql")]
+extern crate r2d2_diesel;
+
+#[macro_use]
+extern crate failure;
+#[macro_use]
+extern crate frippy_derive;
#[macro_use]
-extern crate plugin_derive;
+extern crate lazy_static;
+#[macro_use]
+extern crate log;
+extern crate chrono;
+extern crate humantime;
extern crate irc;
-extern crate tokio_core;
-extern crate futures;
-extern crate glob;
+extern crate reqwest;
+extern crate time;
-mod plugin;
-mod plugins;
+pub mod plugin;
+pub mod plugins;
+pub mod utils;
+pub mod error;
+use std::collections::HashMap;
+use std::fmt;
+use std::thread::spawn;
use std::sync::Arc;
-use irc::client::prelude::*;
-use irc::error::Error as IrcError;
-
-use tokio_core::reactor::Core;
-use futures::future;
-use glob::glob;
+pub use irc::client::prelude::*;
+pub use irc::error::IrcError;
+use error::*;
+use failure::ResultExt;
use plugin::*;
-/// Runs the bot
-///
-/// # Remarks
-///
-/// This blocks the current thread while the bot is running
-pub fn run() {
-
- // Load all toml files in the configs directory
- let mut configs = Vec::new();
- for toml in glob("configs/*.toml").unwrap() {
- match toml {
- Ok(path) => {
- info!("Loading {}", path.to_str().unwrap());
- match Config::load(path) {
- Ok(v) => configs.push(v),
- Err(e) => error!("Incorrect config file {}", e),
- }
- }
- Err(e) => error!("Failed to read path {}", e),
+/// The bot which contains the main logic.
+#[derive(Default)]
+pub struct Bot {
+ plugins: ThreadedPlugins,
+}
+
+impl Bot {
+ /// Creates a `Bot`.
+ /// By itself the bot only responds to a few simple CTCP commands
+ /// defined per config file.
+ /// Any other functionality has to be provided by plugins
+ /// which need to implement [`Plugin`](plugin/trait.Plugin.html).
+ ///
+ /// # Examples
+ /// ```
+ /// use frippy::Bot;
+ /// let mut bot = Bot::new();
+ /// ```
+ pub fn new() -> Bot {
+ Bot {
+ plugins: ThreadedPlugins::new(),
}
}
- // Without configs the bot would just idle
- if configs.is_empty() {
- error!("No config file found");
- return;
+ /// Adds the [`Plugin`](plugin/trait.Plugin.html).
+ /// These plugins will be used to evaluate incoming messages from IRC.
+ ///
+ /// # Examples
+ /// ```
+ /// use frippy::{plugins, Bot};
+ ///
+ /// let mut bot = frippy::Bot::new();
+ /// bot.add_plugin(plugins::help::Help::new());
+ /// ```
+ pub fn add_plugin<T: Plugin + 'static>(&mut self, plugin: T) {
+ self.plugins.add(plugin);
}
- // The list of plugins in use
- let mut plugins = ThreadedPlugins::new();
- plugins.add(plugins::Help::new());
- plugins.add(plugins::Emoji::new());
- plugins.add(plugins::Currency::new());
- info!("Plugins loaded: {}", plugins);
-
- // Create an event loop to run the connections on.
- let mut reactor = Core::new().unwrap();
-
- // Open a connection and add work for each config
- for config in configs {
- let server =
- match IrcServer::new_future(reactor.handle(), &config).and_then(|f| reactor.run(f)) {
- Ok(v) => v,
- Err(e) => {
- error!("Failed to connect: {}", e);
- return;
- }
- };
+ /// Removes a [`Plugin`](plugin/trait.Plugin.html) based on its name.
+ /// The binary currently uses this to disable plugins
+ /// based on user configuration.
+ ///
+ /// # Examples
+ /// ```
+ /// use frippy::{plugins, Bot};
+ ///
+ /// let mut bot = frippy::Bot::new();
+ /// bot.add_plugin(plugins::help::Help::new());
+ /// bot.remove_plugin("Help");
+ /// ```
+ pub fn remove_plugin(&mut self, name: &str) -> Option<()> {
+ self.plugins.remove(name)
+ }
- info!("Connected to server");
+ /// This connects the `Bot` to IRC and creates a task on the
+ /// [`IrcReactor`](../irc/client/reactor/struct.IrcReactor.html)
+ /// which returns an Ok if the connection was cleanly closed and
+ /// an Err if the connection was lost.
+ ///
+ /// You need to run the [`IrcReactor`](../irc/client/reactor/struct.IrcReactor.html),
+ /// so that the `Bot`
+ /// can actually do its work.
+ ///
+ /// # Examples
+ /// ```no_run
+ /// # extern crate irc;
+ /// # extern crate frippy;
+ /// # fn main() {
+ /// use frippy::{Config, Bot};
+ /// use irc::client::reactor::IrcReactor;
+ ///
+ /// let config = Config::load("config.toml").unwrap();
+ /// let mut reactor = IrcReactor::new().unwrap();
+ /// let mut bot = Bot::new();
+ ///
+ /// bot.connect(&mut reactor, &config).unwrap();
+ /// reactor.run().unwrap();
+ /// # }
+ /// ```
+ pub fn connect(&self, reactor: &mut IrcReactor, config: &Config) -> Result<(), FrippyError> {
+ info!("Plugins loaded: {}", self.plugins);
- match server.identify() {
- Ok(_) => info!("Identified"),
- Err(e) => error!("Failed to identify: {}", e),
- };
+ let client = reactor
+ .prepare_client_and_connect(config)
+ .context(ErrorKind::Connection)?;
+
+ info!("Connected to IRC server");
+
+ client.identify().context(ErrorKind::Connection)?;
+ info!("Identified");
// TODO Verify if we actually need to clone plugins twice
- let plugins = plugins.clone();
+ let plugins = self.plugins.clone();
- let task = server
- .stream()
- .for_each(move |message| process_msg(&server, plugins.clone(), message))
- .map_err(|e| error!("Failed to process message: {}", e));
+ reactor.register_client_with_handler(client, move |client, message| {
+ process_msg(client, plugins.clone(), message)
+ });
- reactor.handle().spawn(task);
+ Ok(())
}
-
- // Run the main loop forever
- reactor.run(future::empty::<(), ()>()).unwrap();
}
-fn process_msg(server: &IrcServer,
- mut plugins: ThreadedPlugins,
- message: Message)
- -> Result<(), IrcError> {
-
+fn process_msg(
+ client: &IrcClient,
+ mut plugins: ThreadedPlugins,
+ message: Message,
+) -> Result<(), IrcError> {
+ // Log any channels we join
if let Command::JOIN(ref channel, _, _) = message.command {
- if message.source_nickname().unwrap() == server.current_nickname() {
+ if message.source_nickname().unwrap() == client.current_nickname() {
info!("Joined {}", channel);
}
}
// Check for possible command and save the result for later
- let command = PluginCommand::from(&server.current_nickname().to_lowercase(), &message);
+ let command = PluginCommand::from(&client.current_nickname().to_lowercase(), &message);
- let message = Arc::new(message);
- plugins.execute_plugins(server, message);
+ plugins.execute_plugins(client, message);
// If the message contained a command, handle it
if let Some(command) = command {
- if let Err(e) = plugins.handle_command(server, command) {
+ if let Err(e) = plugins.handle_command(client, command) {
error!("Failed to handle command: {}", e);
}
}
@@ -137,6 +198,110 @@ fn process_msg(server: &IrcServer,
Ok(())
}
+#[derive(Clone, Default, Debug)]
+struct ThreadedPlugins {
+ plugins: HashMap<String, Arc<Plugin>>,
+}
+
+impl ThreadedPlugins {
+ pub fn new() -> ThreadedPlugins {
+ ThreadedPlugins {
+ plugins: HashMap::new(),
+ }
+ }
+
+ pub fn add<T: Plugin + 'static>(&mut self, plugin: T) {
+ let name = plugin.name().to_lowercase();
+ let safe_plugin = Arc::new(plugin);
-#[cfg(test)]
-mod tests {}
+ self.plugins.insert(name, safe_plugin);
+ }
+
+ pub fn remove(&mut self, name: &str) -> Option<()> {
+ self.plugins.remove(&name.to_lowercase()).map(|_| ())
+ }
+
+ pub fn execute_plugins(&mut self, client: &IrcClient, message: Message) {
+ let message = Arc::new(message);
+
+ for (name, plugin) in self.plugins.clone() {
+ // Send the message to the plugin if the plugin needs it
+ match plugin.execute(client, &message) {
+ ExecutionStatus::Done => (),
+ ExecutionStatus::Err(e) => log_error(e),
+ ExecutionStatus::RequiresThread => {
+ debug!(
+ "Spawning thread to execute {} with {}",
+ name,
+ message.to_string().replace("\r\n", "")
+ );
+
+ // Clone everything before the move - the client uses an Arc internally too
+ let plugin = Arc::clone(&plugin);
+ let message = Arc::clone(&message);
+ let client = client.clone();
+
+ // Execute the plugin in another thread
+ spawn(move || {
+ if let Err(e) = plugin.execute_threaded(&client, &message) {
+ log_error(e);
+ };
+ });
+ }
+ }
+ }
+ }
+
+ pub fn handle_command(
+ &mut self,
+ client: &IrcClient,
+ mut command: PluginCommand,
+ ) -> Result<(), FrippyError> {
+ if !command.tokens.iter().any(|s| !s.is_empty()) {
+ let help = format!("Use \"{} help\" to get help", client.current_nickname());
+ client
+ .send_notice(&command.source, &help)
+ .context(ErrorKind::Connection)?;
+ }
+
+ // Check if the command is for this plugin
+ if let Some(plugin) = self.plugins.get(&command.tokens[0].to_lowercase()) {
+ // The first token contains the name of the plugin
+ let name = command.tokens.remove(0);
+
+ debug!("Sending command \"{:?}\" to {}", command, name);
+
+ // Clone for the move - the client uses an Arc internally
+ let client = client.clone();
+ let plugin = Arc::clone(plugin);
+ spawn(move || {
+ if let Err(e) = plugin.command(&client, command) {
+ log_error(e);
+ };
+ });
+
+ Ok(())
+ } else {
+ let help = format!(
+ "\"{} {}\" is not a command, \
+ try \"{0} help\" instead.",
+ client.current_nickname(),
+ command.tokens[0]
+ );
+
+ Ok(client
+ .send_notice(&command.source, &help)
+ .context(ErrorKind::Connection)?)
+ }
+ }
+}
+
+impl fmt::Display for ThreadedPlugins {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let plugin_names = self.plugins
+ .iter()
+ .map(|(_, p)| p.name().to_owned())
+ .collect::<Vec<String>>();
+ write!(f, "{}", plugin_names.join(", "))
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..b9a4b8f
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,191 @@
+#![cfg_attr(feature = "clippy", feature(plugin))]
+#![cfg_attr(feature = "clippy", plugin(clippy))]
+
+extern crate frippy;
+extern crate glob;
+extern crate irc;
+extern crate time;
+
+#[cfg(feature = "mysql")]
+extern crate diesel;
+#[cfg(feature = "mysql")]
+#[macro_use]
+extern crate diesel_migrations;
+#[cfg(feature = "mysql")]
+extern crate r2d2;
+#[cfg(feature = "mysql")]
+extern crate r2d2_diesel;
+
+#[macro_use]
+extern crate failure;
+#[macro_use]
+extern crate log;
+
+#[cfg(feature = "mysql")]
+use std::sync::Arc;
+use std::collections::HashMap;
+use log::{Level, LevelFilter, Metadata, Record};
+
+use irc::client::reactor::IrcReactor;
+use glob::glob;
+
+pub use frippy::plugins::help::Help;
+pub use frippy::plugins::url::Url;
+pub use frippy::plugins::emoji::Emoji;
+pub use frippy::plugins::tell::Tell;
+pub use frippy::plugins::currency::Currency;
+pub use frippy::plugins::keepnick::KeepNick;
+pub use frippy::plugins::factoids::Factoids;
+
+use frippy::Config;
+use failure::Error;
+
+#[cfg(feature = "mysql")]
+embed_migrations!();
+
+struct Logger;
+
+impl log::Log for Logger {
+ fn enabled(&self, metadata: &Metadata) -> bool {
+ metadata.target().contains("frippy")
+ }
+
+ fn log(&self, record: &Record) {
+ if self.enabled(record.metadata()) {
+ if record.metadata().level() >= Level::Debug {
+ println!(
+ "[{}]({}) {} -> {}",
+ time::now().rfc822(),
+ record.level(),
+ record.target(),
+ record.args()
+ );
+ } else {
+ println!(
+ "[{}]({}) {}",
+ time::now().rfc822(),
+ record.level(),
+ record.args()
+ );
+ }
+ }
+ }
+
+ fn flush(&self) {}
+}
+
+static LOGGER: Logger = Logger;
+
+fn main() {
+ // Print any errors that caused frippy to shut down
+ if let Err(e) = run() {
+ let text = e.causes()
+ .skip(1)
+ .fold(format!("{}", e), |acc, err| format!("{}: {}", acc, err));
+ error!("{}", text);
+ };
+}
+
+fn run() -> Result<(), Error> {
+ log::set_max_level(if cfg!(debug_assertions) {
+ LevelFilter::Debug
+ } else {
+ LevelFilter::Info
+ });
+
+ log::set_logger(&LOGGER).unwrap();
+
+ // Load all toml files in the configs directory
+ let mut configs = Vec::new();
+ for toml in glob("configs/*.toml").unwrap() {
+ match toml {
+ Ok(path) => {
+ info!("Loading {}", path.to_str().unwrap());
+ match Config::load(path) {
+ Ok(v) => configs.push(v),
+ Err(e) => error!("Incorrect config file {}", e),
+ }
+ }
+ Err(e) => error!("Failed to read path {}", e),
+ }
+ }
+
+ // Without configs the bot would just idle
+ if configs.is_empty() {
+ bail!("No config file was found");
+ }
+
+ // Create an event loop to run the connections on.
+ let mut reactor = IrcReactor::new()?;
+
+ // 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<_>>());
+ }
+
+ mysql_url = options.get("mysql_url");
+ }
+
+ let mut bot = frippy::Bot::new();
+ bot.add_plugin(Help::new());
+ bot.add_plugin(Url::new(1024));
+ bot.add_plugin(Emoji::new());
+ bot.add_plugin(Currency::new());
+ bot.add_plugin(KeepNick::new());
+
+ #[cfg(feature = "mysql")]
+ {
+ if let Some(url) = mysql_url {
+ use diesel::MysqlConnection;
+ use r2d2;
+ use r2d2_diesel::ConnectionManager;
+
+ let manager = ConnectionManager::<MysqlConnection>::new(url.clone());
+ match r2d2::Pool::builder().build(manager) {
+ Ok(pool) => match embedded_migrations::run(&*pool.get()?) {
+ Ok(_) => {
+ let pool = Arc::new(pool);
+ bot.add_plugin(Factoids::new(pool.clone()));
+ bot.add_plugin(Tell::new(pool.clone()));
+ info!("Connected to MySQL server")
+ }
+ Err(e) => {
+ bot.add_plugin(Factoids::new(HashMap::new()));
+ bot.add_plugin(Tell::new(HashMap::new()));
+ error!("Failed to run migrations: {}", e);
+ }
+ },
+ Err(e) => error!("Failed to connect to database: {}", e),
+ }
+ } else {
+ bot.add_plugin(Factoids::new(HashMap::new()));
+ bot.add_plugin(Tell::new(HashMap::new()));
+ }
+ }
+ #[cfg(not(feature = "mysql"))]
+ {
+ if mysql_url.is_some() {
+ error!("frippy was not built with the mysql feature")
+ }
+ bot.add_plugin(Factoids::new(HashMap::new()));
+ bot.add_plugin(Tell::new(HashMap::new()));
+ }
+
+ if let Some(disabled_plugins) = disabled_plugins {
+ for name in disabled_plugins {
+ if bot.remove_plugin(name).is_none() {
+ error!("\"{}\" was not found - could not disable", name);
+ }
+ }
+ }
+
+ bot.connect(&mut reactor, &config)?;
+ }
+
+ // Run the bots until they throw an error - an error could be loss of connection
+ Ok(reactor.run()?)
+}
diff --git a/src/plugin.rs b/src/plugin.rs
index d1f849a..bc428d5 100644
--- a/src/plugin.rs
+++ b/src/plugin.rs
@@ -1,49 +1,85 @@
+//! Definitions required for every `Plugin`
use std::fmt;
-use std::collections::HashMap;
-use std::thread::spawn;
-use std::sync::Arc;
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
+use error::FrippyError;
+
+/// Describes if a [`Plugin`](trait.Plugin.html) is done working on a
+/// [`Message`](../../irc/proto/message/struct.Message.html) or if another thread is required.
+#[derive(Debug)]
+pub enum ExecutionStatus {
+ /// The [`Plugin`](trait.Plugin.html) does not need to do any more work on this
+ /// [`Message`](../../irc/proto/message/struct.Message.html).
+ Done,
+ /// An error occured during the execution.
+ Err(FrippyError),
+ /// The execution needs to be done by [`execute_threaded()`](trait.Plugin.html#tymethod.execute_threaded).
+ RequiresThread,
+}
+/// `Plugin` has to be implemented for any struct that should be usable
+/// as a `Plugin` in frippy.
pub trait Plugin: PluginName + Send + Sync + fmt::Debug {
- fn is_allowed(&self, server: &IrcServer, message: &Message) -> bool;
- fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError>;
- fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError>;
+ /// Handles messages which are not commands or returns
+ /// [`RequiresThread`](enum.ExecutionStatus.html#variant.RequiresThread)
+ /// if [`execute_threaded()`](trait.Plugin.html#tymethod.execute_threaded) should be used instead.
+ fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus;
+ /// Handles messages which are not commands in a new thread.
+ fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError>;
+ /// Handles any command directed at this plugin.
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError>;
+ /// Similar to [`command()`](trait.Plugin.html#tymethod.command) but return a String instead of
+ /// sending messages directly to IRC.
+ fn evaluate(&self, client: &IrcClient, command: PluginCommand) -> Result<String, String>;
}
-pub trait PluginName: Send + Sync + fmt::Debug {
+/// `PluginName` is required by [`Plugin`](trait.Plugin.html).
+///
+/// To implement it simply add `#[derive(PluginName)]`
+/// above the definition of the struct.
+///
+/// # Examples
+/// ```ignore
+/// #[macro_use] extern crate frippy_derive;
+///
+/// #[derive(PluginName)]
+/// struct Foo;
+/// ```
+pub trait PluginName {
+ /// Returns the name of the `Plugin`.
fn name(&self) -> &str;
}
+/// Represents a command sent by a user to the bot.
#[derive(Clone, Debug)]
pub struct PluginCommand {
+ /// The sender of the command.
pub source: String,
+ /// If the command was sent to a channel, this will be that channel
+ /// otherwise it is the same as `source`.
pub target: String,
+ /// The remaining part of the message that has not been processed yet - split by spaces.
pub tokens: Vec<String>,
}
impl PluginCommand {
+ /// Creates a `PluginCommand` from [`Message`](../../irc/proto/message/struct.Message.html)
+ /// if it contains a [`PRIVMSG`](../../irc/proto/command/enum.Command.html#variant.PRIVMSG)
+ /// that starts with the provided `nick`.
pub fn from(nick: &str, message: &Message) -> Option<PluginCommand> {
-
// Get the actual message out of PRIVMSG
if let Command::PRIVMSG(_, ref content) = message.command {
-
// Split content by spaces and filter empty tokens
let mut tokens: Vec<String> = content.split(' ').map(ToOwned::to_owned).collect();
// Commands start with our name
if tokens[0].to_lowercase().starts_with(nick) {
-
// Remove the bot's name from the first token
tokens[0].drain(..nick.len());
// We assume that only ':' and ',' are used as suffixes on IRC
// If there are any other chars we assume that it is not ment for the bot
- tokens[0] = tokens[0]
- .chars()
- .filter(|&c| !":,".contains(c))
- .collect();
+ tokens[0] = tokens[0].chars().filter(|&c| !":,".contains(c)).collect();
if !tokens[0].is_empty() {
return None;
}
@@ -52,10 +88,10 @@ impl PluginCommand {
tokens.remove(0);
Some(PluginCommand {
- source: message.source_nickname().unwrap().to_string(),
- target: message.response_target().unwrap().to_string(),
- tokens: tokens,
- })
+ source: message.source_nickname().unwrap().to_string(),
+ target: message.response_target().unwrap().to_string(),
+ tokens: tokens,
+ })
} else {
None
}
@@ -64,96 +100,3 @@ impl PluginCommand {
}
}
}
-
-#[derive(Clone, Debug)]
-pub struct ThreadedPlugins {
- plugins: HashMap<String, Arc<Plugin>>,
-}
-
-impl ThreadedPlugins {
- pub fn new() -> ThreadedPlugins {
- ThreadedPlugins { plugins: HashMap::new() }
- }
-
- pub fn add<T: Plugin + 'static>(&mut self, plugin: T) {
- let name = plugin.name().to_lowercase();
- let safe_plugin = Arc::new(plugin);
-
- self.plugins.insert(name, safe_plugin);
- }
-
- pub fn execute_plugins(&mut self, server: &IrcServer, message: Arc<Message>) {
-
- for (name, plugin) in self.plugins.clone() {
- // Send the message to the plugin if the plugin needs it
- if plugin.is_allowed(server, &message) {
-
- debug!("Executing {} with {}",
- name,
- message.to_string().replace("\r\n", ""));
-
- // Clone everything before the move
- // The server uses an Arc internally too
- let plugin = Arc::clone(&plugin);
- let message = Arc::clone(&message);
- let server = server.clone();
-
- // Execute the plugin in another thread
- spawn(move || {
- if let Err(e) = plugin.execute(&server, &message) {
- error!("Error in {} - {}", name, e);
- };
- });
- }
- }
- }
-
- pub fn handle_command(&mut self,
- server: &IrcServer,
- mut command: PluginCommand)
- -> Result<(), IrcError> {
-
- if !command.tokens.iter().any(|s| !s.is_empty()) {
- let help = format!("Use \"{} help\" to get help", server.current_nickname());
- return server.send_notice(&command.source, &help);
- }
-
- // Check if the command is for this plugin
- if let Some(plugin) = self.plugins.get(&command.tokens[0].to_lowercase()) {
-
- // The first token contains the name of the plugin
- let name = command.tokens.remove(0);
-
- debug!("Sending command \"{:?}\" to {}", command, name);
-
- // Clone for the move - the server uses an Arc internally
- let server = server.clone();
- let plugin = Arc::clone(plugin);
- spawn(move || {
- if let Err(e) = plugin.command(&server, command) {
- error!("Error in {} command - {}", name, e);
- };
- });
-
- Ok(())
-
- } else {
- let help = format!("\"{} {}\" is not a command, \
- try \"{0} help\" instead.",
- server.current_nickname(),
- command.tokens[0]);
-
- server.send_notice(&command.source, &help)
- }
- }
-}
-
-impl fmt::Display for ThreadedPlugins {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let plugin_names = self.plugins
- .iter()
- .map(|(_, p)| p.name().to_string())
- .collect::<Vec<String>>();
- write!(f, "{}", plugin_names.join(", "))
- }
-}
diff --git a/src/plugins/currency.rs b/src/plugins/currency.rs
index d6cf928..53a245c 100644
--- a/src/plugins/currency.rs
+++ b/src/plugins/currency.rs
@@ -6,7 +6,6 @@ use std::io::Read;
use std::num::ParseFloatError;
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
use self::reqwest::Client;
use self::reqwest::header::Connection;
@@ -14,7 +13,11 @@ use self::serde_json::Value;
use plugin::*;
-#[derive(PluginName, Debug)]
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::ResultExt;
+
+#[derive(PluginName, Default, Debug)]
pub struct Currency;
struct ConvertionRequest<'a> {
@@ -23,18 +26,8 @@ struct ConvertionRequest<'a> {
target: &'a str,
}
-macro_rules! try_option {
- ($e:expr) => {
- match $e {
- Some(v) => v,
- None => { return None; }
- }
- }
-}
-
impl<'a> ConvertionRequest<'a> {
fn send(&self) -> Option<f64> {
-
let response = Client::new()
.get("https://api.fixer.io/latest")
.form(&[("base", self.source)])
@@ -44,16 +37,14 @@ impl<'a> ConvertionRequest<'a> {
match response {
Ok(mut response) => {
let mut body = String::new();
- try_option!(response.read_to_string(&mut body).ok());
+ response.read_to_string(&mut body).ok()?;
let convertion_rates: Result<Value, _> = serde_json::from_str(&body);
match convertion_rates {
Ok(convertion_rates) => {
-
- let rates: &Value = try_option!(convertion_rates.get("rates"));
- let target_rate: &Value =
- try_option!(rates.get(self.target.to_uppercase()));
- Some(self.value * try_option!(target_rate.as_f64()))
+ let rates: &Value = convertion_rates.get("rates")?;
+ let target_rate: &Value = rates.get(self.target.to_uppercase())?;
+ Some(self.value * target_rate.as_f64()?)
}
Err(_) => None,
}
@@ -68,7 +59,10 @@ impl Currency {
Currency {}
}
- fn eval_command<'a>(&self, tokens: &'a [String]) -> Result<ConvertionRequest<'a>, ParseFloatError> {
+ fn eval_command<'a>(
+ &self,
+ tokens: &'a [String],
+ ) -> Result<ConvertionRequest<'a>, ParseFloatError> {
Ok(ConvertionRequest {
value: tokens[0].parse()?,
source: &tokens[1],
@@ -76,76 +70,97 @@ impl Currency {
})
}
- fn convert(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
-
+ fn convert(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<String, String> {
if command.tokens.len() < 3 {
- return self.invalid_command(server, &command);
+ return Err(self.invalid_command(client));
}
let request = match self.eval_command(&command.tokens) {
Ok(request) => request,
Err(_) => {
- return self.invalid_command(server, &command);
+ return Err(self.invalid_command(client));
}
};
match request.send() {
Some(response) => {
- let response = format!("{} {} => {:.4} {}",
- request.value,
- request.source.to_lowercase(),
- response / 1.00000000,
- request.target.to_lowercase());
-
- server.send_privmsg(&command.target, &response)
+ let response = format!(
+ "{} {} => {:.4} {}",
+ request.value,
+ request.source.to_lowercase(),
+ response / 1.00000000,
+ request.target.to_lowercase()
+ );
+
+ Ok(response)
}
- None => server.send_notice(&command.source, "Error while converting given currency"),
+ None => Err(String::from(
+ "An error occured during the conversion of the given currency",
+ )),
}
}
- fn help(&self, server: &IrcServer, command: &mut PluginCommand) -> Result<(), IrcError> {
- let help = format!("usage: {} currency value from_currency to_currency\r\n\
- example: 1.5 eur usd\r\n\
- available currencies: AUD, BGN, BRL, CAD, \
- CHF, CNY, CZK, DKK, GBP, HKD, HRK, HUF, \
- IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, \
- NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, \
- TRY, USD, ZAR",
- server.current_nickname());
-
- server.send_notice(&command.source, &help)
+ fn help(&self, client: &IrcClient) -> String {
+ format!(
+ "usage: {} currency value from_currency to_currency\r\n\
+ example: {0} currency 1.5 eur usd\r\n\
+ available currencies: AUD, BGN, BRL, CAD, \
+ CHF, CNY, CZK, DKK, GBP, HKD, HRK, HUF, \
+ IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, \
+ NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, \
+ TRY, USD, ZAR",
+ client.current_nickname()
+ )
}
- fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
- let help = format!("Incorrect Command. \
- Send \"{} currency help\" for help.",
- server.current_nickname());
-
- server.send_notice(&command.source, &help)
+ fn invalid_command(&self, client: &IrcClient) -> String {
+ format!(
+ "Incorrect Command. \
+ Send \"{} currency help\" for help.",
+ client.current_nickname()
+ )
}
}
impl Plugin for Currency {
- fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
- false
+ fn execute(&self, _: &IrcClient, _: &Message) -> ExecutionStatus {
+ ExecutionStatus::Done
}
- fn execute(&self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
panic!("Currency does not implement the execute function!")
}
- fn command(&self, server: &IrcServer, mut command: PluginCommand) -> Result<(), IrcError> {
+ fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> {
+ if command.tokens.is_empty() {
+ return Ok(client
+ .send_notice(&command.source, &self.invalid_command(client))
+ .context(FrippyErrorKind::Connection)?);
+ }
+
+ match command.tokens[0].as_ref() {
+ "help" => Ok(client
+ .send_notice(&command.source, &self.help(client))
+ .context(FrippyErrorKind::Connection)?),
+ _ => match self.convert(client, &mut command) {
+ Ok(msg) => Ok(client
+ .send_privmsg(&command.target, &msg)
+ .context(FrippyErrorKind::Connection)?),
+ Err(msg) => Ok(client
+ .send_notice(&command.source, &msg)
+ .context(FrippyErrorKind::Connection)?),
+ },
+ }
+ }
+ fn evaluate(&self, client: &IrcClient, mut command: PluginCommand) -> Result<String, String> {
if command.tokens.is_empty() {
- return self.invalid_command(server, &command);
+ return Err(self.invalid_command(client));
}
match command.tokens[0].as_ref() {
- "help" => self.help(server, &mut command),
- _ => self.convert(server, command),
+ "help" => Ok(self.help(client)),
+ _ => self.convert(client, &mut command),
}
}
}
-
-#[cfg(test)]
-mod tests {}
diff --git a/src/plugins/emoji.rs b/src/plugins/emoji.rs
index 1bb714c..f1d9376 100644
--- a/src/plugins/emoji.rs
+++ b/src/plugins/emoji.rs
@@ -3,10 +3,14 @@ extern crate unicode_names;
use std::fmt;
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
use plugin::*;
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::Fail;
+use failure::ResultExt;
+
struct EmojiHandle {
symbol: char,
count: i32,
@@ -14,7 +18,6 @@ struct EmojiHandle {
impl fmt::Display for EmojiHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-
let name = match unicode_names::name(self.symbol) {
Some(sym) => sym.to_string().to_lowercase(),
None => String::from("UNKNOWN"),
@@ -28,7 +31,7 @@ impl fmt::Display for EmojiHandle {
}
}
-#[derive(PluginName, Debug)]
+#[derive(PluginName, Default, Debug)]
pub struct Emoji;
impl Emoji {
@@ -36,13 +39,12 @@ impl Emoji {
Emoji {}
}
- fn emoji(&self, server: &IrcServer, content: &str, target: &str) -> Result<(), IrcError> {
- let names = self.return_emojis(content)
+ fn emoji(&self, content: &str) -> String {
+ self.return_emojis(content)
.iter()
.map(|e| e.to_string())
- .collect::<Vec<String>>();
-
- server.send_privmsg(target, &names.join(", "))
+ .collect::<Vec<String>>()
+ .join(", ")
}
fn return_emojis(&self, string: &str) -> Vec<EmojiHandle> {
@@ -53,7 +55,6 @@ impl Emoji {
count: 0,
};
-
for c in string.chars() {
if !self.is_emoji(&c) {
continue;
@@ -61,7 +62,6 @@ impl Emoji {
if current.symbol == c {
current.count += 1;
-
} else {
if current.count > 0 {
emojis.push(current);
@@ -98,27 +98,37 @@ impl Emoji {
}
impl Plugin for Emoji {
- fn is_allowed(&self, _: &IrcServer, message: &Message) -> bool {
+ fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
match message.command {
- Command::PRIVMSG(_, _) => true,
- _ => false,
+ Command::PRIVMSG(_, ref content) => match client
+ .send_privmsg(message.response_target().unwrap(), &self.emoji(content))
+ {
+ Ok(_) => ExecutionStatus::Done,
+ Err(e) => ExecutionStatus::Err(e.context(FrippyErrorKind::Connection).into()),
+ },
+ _ => ExecutionStatus::Done,
}
}
- fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError> {
- match message.command {
- Command::PRIVMSG(_, ref content) => {
- self.emoji(server, content, message.response_target().unwrap())
- }
- _ => Ok(()),
- }
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
+ panic!("Emoji should not use threading")
}
- fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
- server.send_notice(&command.source,
- "This Plugin does not implement any commands.")
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ Ok(client
+ .send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
+ .context(FrippyErrorKind::Connection)?)
}
-}
-#[cfg(test)]
-mod tests {}
+ fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> {
+ let emojis = self.emoji(&command.tokens[0]);
+ if emojis.is_empty() {
+ Ok(emojis)
+ } else {
+ Err(String::from("No emojis were found."))
+ }
+ }
+}
diff --git a/src/plugins/factoids/database.rs b/src/plugins/factoids/database.rs
new file mode 100644
index 0000000..b1fe8dd
--- /dev/null
+++ b/src/plugins/factoids/database.rs
@@ -0,0 +1,163 @@
+#[cfg(feature = "mysql")]
+extern crate dotenv;
+
+#[cfg(feature = "mysql")]
+use std::sync::Arc;
+use std::collections::HashMap;
+
+#[cfg(feature = "mysql")]
+use diesel::prelude::*;
+#[cfg(feature = "mysql")]
+use diesel::mysql::MysqlConnection;
+#[cfg(feature = "mysql")]
+use r2d2::Pool;
+#[cfg(feature = "mysql")]
+use r2d2_diesel::ConnectionManager;
+#[cfg(feature = "mysql")]
+use failure::ResultExt;
+
+use chrono::NaiveDateTime;
+
+use super::error::*;
+
+#[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_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_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError>;
+ fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError>;
+ fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError>;
+ fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError>;
+}
+
+// HashMap
+impl Database for HashMap<(String, i32), Factoid> {
+ fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError> {
+ 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 = factoid.name.clone();
+ match self.insert((name, factoid.idx), factoid) {
+ None => Ok(()),
+ Some(_) => Err(ErrorKind::Duplicate)?,
+ }
+ }
+
+ fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError> {
+ Ok(self.get(&(String::from(name), idx))
+ .cloned()
+ .ok_or(ErrorKind::NotFound)?)
+ }
+
+ fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> {
+ match self.remove(&(String::from(name), idx)) {
+ Some(_) => Ok(()),
+ None => Err(ErrorKind::NotFound)?,
+ }
+ }
+
+ fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError> {
+ Ok(self.iter().filter(|&(&(ref n, _), _)| n == name).count() as i32)
+ }
+}
+
+// Diesel automatically defines the factoids module as public.
+// We create a schema module to keep it private.
+#[cfg(feature = "mysql")]
+mod schema {
+ table! {
+ factoids (name, idx) {
+ name -> Varchar,
+ idx -> Integer,
+ content -> Text,
+ author -> Varchar,
+ created -> Timestamp,
+ }
+ }
+}
+
+#[cfg(feature = "mysql")]
+use self::schema::factoids;
+
+#[cfg(feature = "mysql")]
+impl Database for Arc<Pool<ConnectionManager<MysqlConnection>>> {
+ fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError> {
+ use diesel;
+
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
+ diesel::insert_into(factoids::table)
+ .values(factoid)
+ .execute(conn)
+ .context(ErrorKind::MysqlError)?;
+
+ Ok(())
+ }
+
+ fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError> {
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
+ Ok(factoids::table
+ .find((name, idx))
+ .first(conn)
+ .context(ErrorKind::MysqlError)?)
+ }
+
+ fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> {
+ use diesel;
+ use self::factoids::columns;
+
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
+ match diesel::delete(
+ factoids::table
+ .filter(columns::name.eq(name))
+ .filter(columns::idx.eq(idx)),
+ ).execute(conn)
+ {
+ Ok(v) => {
+ if v > 0 {
+ Ok(())
+ } else {
+ Err(ErrorKind::NotFound)?
+ }
+ }
+ Err(e) => Err(e).context(ErrorKind::MysqlError)?,
+ }
+ }
+
+ fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError> {
+ use diesel;
+
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
+ let count: Result<i64, _> = factoids::table
+ .filter(factoids::columns::name.eq(name))
+ .count()
+ .get_result(conn);
+
+ match count {
+ Ok(c) => Ok(c as i32),
+ Err(diesel::NotFound) => Ok(0),
+ Err(e) => Err(e).context(ErrorKind::MysqlError)?,
+ }
+ }
+}
diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs
new file mode 100644
index 0000000..2f3690f
--- /dev/null
+++ b/src/plugins/factoids/mod.rs
@@ -0,0 +1,342 @@
+extern crate rlua;
+
+use std::fmt;
+use std::str::FromStr;
+use std::sync::Mutex;
+use self::rlua::prelude::*;
+use irc::client::prelude::*;
+
+use time;
+use chrono::NaiveDateTime;
+
+use plugin::*;
+pub mod database;
+use self::database::Database;
+
+mod utils;
+use self::utils::*;
+
+use failure::ResultExt;
+use error::ErrorKind as FrippyErrorKind;
+use error::FrippyError;
+use self::error::*;
+
+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, FactoidsError> {
+ let count = try_lock!(self.factoids).count_factoids(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, 0u32),
+ };
+
+ Ok(try_lock!(self.factoids)
+ .insert_factoid(&factoid)
+ .map(|()| "Successfully added!")?)
+ }
+
+ fn add(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
+ if command.tokens.len() < 2 {
+ Err(ErrorKind::InvalidCommand)?;
+ }
+
+ let name = command.tokens.remove(0);
+ let content = command.tokens.join(" ");
+
+ Ok(self.create_factoid(&name, &content, &command.source)?)
+ }
+
+ fn add_from_url(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
+ if command.tokens.len() < 2 {
+ Err(ErrorKind::InvalidCommand)?;
+ }
+
+ let name = command.tokens.remove(0);
+ let url = &command.tokens[0];
+ let content = ::utils::download(url, Some(1024)).context(ErrorKind::Download)?;
+
+ Ok(self.create_factoid(&name, &content, &command.source)?)
+ }
+
+ fn remove(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
+ if command.tokens.len() < 1 {
+ Err(ErrorKind::InvalidCommand)?;
+ }
+
+ let name = command.tokens.remove(0);
+ let count = try_lock!(self.factoids).count_factoids(&name)?;
+
+ match try_lock!(self.factoids).delete_factoid(&name, count - 1) {
+ Ok(()) => Ok("Successfully removed"),
+ Err(e) => Err(e)?,
+ }
+ }
+
+ fn get(&self, command: &PluginCommand) -> Result<String, FactoidsError> {
+ let (name, idx) = match command.tokens.len() {
+ 0 => Err(ErrorKind::InvalidCommand)?,
+ 1 => {
+ let name = &command.tokens[0];
+ let count = try_lock!(self.factoids).count_factoids(name)?;
+
+ if count < 1 {
+ Err(ErrorKind::NotFound)?;
+ }
+
+ (name, count - 1)
+ }
+ _ => {
+ let name = &command.tokens[0];
+ let idx = match i32::from_str(&command.tokens[1]) {
+ Ok(i) => i,
+ Err(_) => Err(ErrorKind::InvalidCommand)?,
+ };
+
+ (name, idx)
+ }
+ };
+
+ let factoid = try_lock!(self.factoids)
+ .get_factoid(name, idx)
+ .context(ErrorKind::NotFound)?;
+
+ let message = factoid.content.replace("\n", "|").replace("\r", "");
+
+ Ok(format!("{}: {}", factoid.name, message))
+ }
+
+ fn info(&self, command: &PluginCommand) -> Result<String, FactoidsError> {
+ match command.tokens.len() {
+ 0 => Err(ErrorKind::InvalidCommand)?,
+ 1 => {
+ let name = &command.tokens[0];
+ let count = try_lock!(self.factoids).count_factoids(name)?;
+
+ Ok(match count {
+ 0 => Err(ErrorKind::NotFound)?,
+ 1 => format!("There is 1 version of {}", name),
+ _ => format!("There are {} versions of {}", count, name),
+ })
+ }
+ _ => {
+ let name = &command.tokens[0];
+ let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?;
+ let factoid = try_lock!(self.factoids).get_factoid(name, idx)?;
+
+ Ok(format!(
+ "{}: Added by {} at {} UTC",
+ name, factoid.author, factoid.created
+ ))
+ }
+ }
+ }
+
+ fn exec(&self, mut command: PluginCommand) -> Result<String, FactoidsError> {
+ if command.tokens.len() < 1 {
+ Err(ErrorKind::InvalidIndex)?
+ } else {
+ let name = command.tokens.remove(0);
+ let count = try_lock!(self.factoids).count_factoids(&name)?;
+ let factoid = try_lock!(self.factoids).get_factoid(&name, count - 1)?;
+
+ let content = factoid.content;
+ let value = if content.starts_with('>') {
+ let content = String::from(&content[1..]);
+
+ if content.starts_with('>') {
+ content
+ } else {
+ match self.run_lua(&name, &content, &command) {
+ Ok(v) => v,
+ Err(e) => format!("\"{}\"", e),
+ }
+ }
+ } else {
+ content
+ };
+
+ Ok(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("|"))
+ }
+}
+
+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<(), FrippyError> {
+ 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_owned(),
+ target: message.response_target().unwrap().to_owned(),
+ tokens: t,
+ };
+
+ Ok(match self.exec(c) {
+ Ok(f) => client
+ .send_privmsg(&message.response_target().unwrap(), &f)
+ .context(FrippyErrorKind::Connection)?,
+ Err(_) => (),
+ })
+ } else {
+ Ok(())
+ }
+ }
+
+ fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> {
+ if command.tokens.is_empty() {
+ return Ok(client
+ .send_notice(&command.target, "Invalid command")
+ .context(FrippyErrorKind::Connection)?);
+ }
+
+ let target = command.target.clone();
+ let source = command.source.clone();
+
+ let sub_command = command.tokens.remove(0);
+ let result = match sub_command.as_ref() {
+ "add" => self.add(&mut command).map(|s| s.to_owned()),
+ "fromurl" => self.add_from_url(&mut command).map(|s| s.to_owned()),
+ "remove" => self.remove(&mut command).map(|s| s.to_owned()),
+ "get" => self.get(&command),
+ "info" => self.info(&command),
+ "exec" => self.exec(command),
+ _ => Err(ErrorKind::InvalidCommand.into()),
+ };
+
+ Ok(match result {
+ Ok(v) => client
+ .send_privmsg(&target, &v)
+ .context(FrippyErrorKind::Connection)?,
+ Err(e) => {
+ let message = e.to_string();
+ client
+ .send_notice(&source, &message)
+ .context(FrippyErrorKind::Connection)?;
+ Err(e).context(FrippyErrorKind::Factoids)?
+ }
+ })
+ }
+
+ 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 {{ ... }}")
+ }
+}
+
+pub mod error {
+ #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
+ #[error = "FactoidsError"]
+ pub enum ErrorKind {
+ /// Invalid command error
+ #[fail(display = "Invalid Command")]
+ InvalidCommand,
+
+ /// Invalid index error
+ #[fail(display = "Invalid index")]
+ InvalidIndex,
+
+ /// Download error
+ #[fail(display = "Download failed")]
+ Download,
+
+ /// Duplicate error
+ #[fail(display = "Entry already exists")]
+ Duplicate,
+
+ /// Not found error
+ #[fail(display = "Factoid was not found")]
+ NotFound,
+
+ /// MySQL error
+ #[cfg(feature = "mysql")]
+ #[fail(display = "Failed to execute MySQL Query")]
+ MysqlError,
+
+ /// No connection error
+ #[cfg(feature = "mysql")]
+ #[fail(display = "No connection to the database")]
+ NoConnection,
+ }
+}
diff --git a/src/plugins/factoids/sandbox.lua b/src/plugins/factoids/sandbox.lua
new file mode 100644
index 0000000..3fc74cd
--- /dev/null
+++ b/src/plugins/factoids/sandbox.lua
@@ -0,0 +1,86 @@
+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 sandbox_env = {
+ print = send,
+ println = sendln,
+ eval = nil,
+ args = args,
+ input = input,
+ user = user,
+ channel = channel,
+ request = download,
+ string = string,
+ math = math,
+ table = table,
+ pairs = pairs,
+ ipairs = ipairs,
+ next = next,
+ select = select,
+ unpack = unpack,
+ tostring = tostring,
+ tonumber = tonumber,
+ type = type,
+ assert = assert,
+ error = error,
+ pcall = pcall,
+ xpcall = xpcall,
+ _VERSION = _VERSION
+}
+
+sandbox_env.os = {
+ clock = os.clock,
+ time = os.time,
+ difftime = os.difftime
+}
+
+sandbox_env.string.rep = nil
+sandbox_env.string.dump = nil
+sandbox_env.math.randomseed = nil
+
+-- Temporary evaluation function
+function eval(code)
+ local c, e = load(code, nil, nil, sandbox_env)
+ if c then
+ return c()
+ else
+ error(e)
+ end
+end
+
+sandbox_env.eval = eval
+
+-- 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
+
+local f, e = load(factoid, nil, nil, sandbox_env)
+
+-- 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..70ac8a7
--- /dev/null
+++ b/src/plugins/factoids/utils.rs
@@ -0,0 +1,25 @@
+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(&url, Some(1024)) {
+ Ok(v) => Ok(v),
+ Err(e) => Err(RuntimeError(format!(
+ "Failed to download {} - {}",
+ url,
+ e.to_string()
+ ))),
+ }
+}
+
+pub fn sleep(_: &Lua, dur: u64) -> Result<(), LuaError> {
+ thread::sleep(Duration::from_millis(dur));
+ Ok(())
+}
diff --git a/src/plugins/help.rs b/src/plugins/help.rs
index 8f3fb4d..7e3658d 100644
--- a/src/plugins/help.rs
+++ b/src/plugins/help.rs
@@ -1,34 +1,36 @@
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
use plugin::*;
-#[derive(PluginName, Debug)]
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::ResultExt;
+
+#[derive(PluginName, Default, Debug)]
pub struct Help;
impl Help {
pub fn new() -> Help {
Help {}
}
-
- fn help(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
- server.send_notice(&command.source, "Help has not been added yet.")
- }
}
impl Plugin for Help {
- fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
- false
+ fn execute(&self, _: &IrcClient, _: &Message) -> ExecutionStatus {
+ ExecutionStatus::Done
}
- fn execute(&self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
- panic!("Help does not implement the execute function!")
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
+ panic!("Help should not use threading")
}
- fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
- self.help(server, command)
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ Ok(client
+ .send_notice(&command.source, "Help has not been added yet.")
+ .context(FrippyErrorKind::Connection)?)
}
-}
-#[cfg(test)]
-mod tests {}
+ fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
+ Err(String::from("Help has not been added yet."))
+ }
+}
diff --git a/src/plugins/keepnick.rs b/src/plugins/keepnick.rs
new file mode 100644
index 0000000..58ac167
--- /dev/null
+++ b/src/plugins/keepnick.rs
@@ -0,0 +1,70 @@
+use irc::client::prelude::*;
+
+use plugin::*;
+
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::ResultExt;
+
+#[derive(PluginName, Default, Debug)]
+pub struct KeepNick;
+
+impl KeepNick {
+ pub fn new() -> KeepNick {
+ KeepNick {}
+ }
+
+ fn check_nick(&self, client: &IrcClient, leaver: &str) -> ExecutionStatus {
+ let cfg_nick = match client.config().nickname {
+ Some(ref nick) => nick.clone(),
+ None => return ExecutionStatus::Done,
+ };
+
+ if leaver != cfg_nick {
+ return ExecutionStatus::Done;
+ }
+
+ let client_nick = client.current_nickname();
+
+ if client_nick != cfg_nick {
+ info!("Trying to switch nick from {} to {}", client_nick, cfg_nick);
+ match client
+ .send(Command::NICK(cfg_nick))
+ .context(FrippyErrorKind::Connection)
+ {
+ Ok(_) => ExecutionStatus::Done,
+ Err(e) => ExecutionStatus::Err(e.into()),
+ }
+ } else {
+ ExecutionStatus::Done
+ }
+ }
+}
+
+impl Plugin for KeepNick {
+ fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
+ match message.command {
+ Command::QUIT(ref nick) => {
+ self.check_nick(client, &nick.clone().unwrap_or_else(String::new))
+ }
+ _ => ExecutionStatus::Done,
+ }
+ }
+
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
+ panic!("Tell should not use threading")
+ }
+
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ Ok(client
+ .send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
+ .context(FrippyErrorKind::Connection)?)
+ }
+
+ fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
+ Err(String::from("This Plugin does not implement any commands."))
+ }
+}
diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs
index 0dea596..9a3ba2f 100644
--- a/src/plugins/mod.rs
+++ b/src/plugins/mod.rs
@@ -1,7 +1,8 @@
-mod help;
-mod emoji;
-mod currency;
-
-pub use self::help::Help;
-pub use self::emoji::Emoji;
-pub use self::currency::Currency;
+//! Collection of plugins included
+pub mod help;
+pub mod url;
+pub mod emoji;
+pub mod tell;
+pub mod currency;
+pub mod factoids;
+pub mod keepnick;
diff --git a/src/plugins/tell/database.rs b/src/plugins/tell/database.rs
new file mode 100644
index 0000000..98e9fb3
--- /dev/null
+++ b/src/plugins/tell/database.rs
@@ -0,0 +1,150 @@
+#[cfg(feature = "mysql")]
+extern crate dotenv;
+
+#[cfg(feature = "mysql")]
+use std::sync::Arc;
+use std::collections::HashMap;
+
+#[cfg(feature = "mysql")]
+use diesel::prelude::*;
+#[cfg(feature = "mysql")]
+use diesel::mysql::MysqlConnection;
+#[cfg(feature = "mysql")]
+use r2d2::Pool;
+#[cfg(feature = "mysql")]
+use r2d2_diesel::ConnectionManager;
+
+use chrono::NaiveDateTime;
+
+#[cfg(feature = "mysql")]
+use failure::ResultExt;
+
+use super::error::*;
+
+#[cfg_attr(feature = "mysql", derive(Queryable))]
+#[derive(PartialEq, Clone, Debug)]
+pub struct TellMessage {
+ pub id: i64,
+ pub sender: String,
+ pub receiver: String,
+ pub time: NaiveDateTime,
+ pub message: String,
+}
+
+#[cfg_attr(feature = "mysql", derive(Insertable))]
+#[cfg_attr(feature = "mysql", table_name = "tells")]
+pub struct NewTellMessage<'a> {
+ pub sender: &'a str,
+ pub receiver: &'a str,
+ pub time: NaiveDateTime,
+ pub message: &'a str,
+}
+
+pub trait Database: Send {
+ fn insert_tell(&mut self, tell: &NewTellMessage) -> Result<(), TellError>;
+ fn get_tells(&self, receiver: &str) -> Result<Vec<TellMessage>, TellError>;
+ fn get_receivers(&self) -> Result<Vec<String>, TellError>;
+ fn delete_tells(&mut self, receiver: &str) -> Result<(), TellError>;
+}
+
+// HashMap
+impl Database for HashMap<String, Vec<TellMessage>> {
+ fn insert_tell(&mut self, tell: &NewTellMessage) -> Result<(), TellError> {
+ let tell = TellMessage {
+ id: 0,
+ sender: tell.sender.to_string(),
+ receiver: tell.receiver.to_string(),
+ time: tell.time,
+ message: tell.message.to_string(),
+ };
+
+ let receiver = tell.receiver.clone();
+ let tell_messages = self.entry(receiver)
+ .or_insert_with(|| Vec::with_capacity(3));
+ (*tell_messages).push(tell);
+
+ Ok(())
+ }
+
+ fn get_tells(&self, receiver: &str) -> Result<Vec<TellMessage>, TellError> {
+ Ok(self.get(receiver).cloned().ok_or(ErrorKind::NotFound)?)
+ }
+
+ fn get_receivers(&self) -> Result<Vec<String>, TellError> {
+ Ok(self.iter()
+ .map(|(receiver, _)| receiver.to_owned())
+ .collect::<Vec<_>>())
+ }
+
+ fn delete_tells(&mut self, receiver: &str) -> Result<(), TellError> {
+ match self.remove(receiver) {
+ Some(_) => Ok(()),
+ None => Err(ErrorKind::NotFound)?,
+ }
+ }
+}
+
+// Diesel automatically defines the tells module as public.
+// We create a schema module to keep it private.
+#[cfg(feature = "mysql")]
+mod schema {
+ table! {
+ tells (id) {
+ id -> Bigint,
+ sender -> Varchar,
+ receiver -> Varchar,
+ time -> Timestamp,
+ message -> Varchar,
+ }
+ }
+}
+
+#[cfg(feature = "mysql")]
+use self::schema::tells;
+
+#[cfg(feature = "mysql")]
+impl Database for Arc<Pool<ConnectionManager<MysqlConnection>>> {
+ fn insert_tell(&mut self, tell: &NewTellMessage) -> Result<(), TellError> {
+ use diesel;
+
+ let conn = &*self.get().expect("Failed to get connection");
+ diesel::insert_into(tells::table)
+ .values(tell)
+ .execute(conn)
+ .context(ErrorKind::MysqlError)?;
+
+ Ok(())
+ }
+
+ fn get_tells(&self, receiver: &str) -> Result<Vec<TellMessage>, TellError> {
+ use self::tells::columns;
+
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
+ Ok(tells::table
+ .filter(columns::receiver.eq(receiver))
+ .order(columns::time.asc())
+ .load::<TellMessage>(conn)
+ .context(ErrorKind::MysqlError)?)
+ }
+
+ fn get_receivers(&self) -> Result<Vec<String>, TellError> {
+ use self::tells::columns;
+
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
+ Ok(tells::table
+ .select(columns::receiver)
+ .load::<String>(conn)
+ .context(ErrorKind::MysqlError)?)
+ }
+
+ fn delete_tells(&mut self, receiver: &str) -> Result<(), TellError> {
+ use diesel;
+ use self::tells::columns;
+
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
+ diesel::delete(tells::table.filter(columns::receiver.eq(receiver)))
+ .execute(conn)
+ .context(ErrorKind::MysqlError)?;
+ Ok(())
+ }
+}
diff --git a/src/plugins/tell/mod.rs b/src/plugins/tell/mod.rs
new file mode 100644
index 0000000..bdfb55c
--- /dev/null
+++ b/src/plugins/tell/mod.rs
@@ -0,0 +1,264 @@
+use irc::client::prelude::*;
+
+use std::time::Duration;
+use std::sync::Mutex;
+
+use time;
+use chrono::NaiveDateTime;
+use humantime::format_duration;
+
+use plugin::*;
+
+use failure::Fail;
+use failure::ResultExt;
+use error::ErrorKind as FrippyErrorKind;
+use error::FrippyError;
+use self::error::*;
+
+pub mod database;
+use self::database::Database;
+
+macro_rules! try_lock {
+ ( $m:expr ) => {
+ match $m.lock() {
+ Ok(guard) => guard,
+ Err(poisoned) => poisoned.into_inner(),
+ }
+ }
+}
+
+#[derive(PluginName, Default)]
+pub struct Tell<T: Database> {
+ tells: Mutex<T>,
+}
+
+impl<T: Database> Tell<T> {
+ pub fn new(db: T) -> Tell<T> {
+ Tell {
+ tells: Mutex::new(db),
+ }
+ }
+
+ fn tell_command(
+ &self,
+ client: &IrcClient,
+ command: PluginCommand,
+ ) -> Result<String, TellError> {
+ if command.tokens.len() < 2 {
+ return Ok(self.invalid_command(client));
+ }
+
+ let receiver = &command.tokens[0];
+ let sender = command.source;
+
+ if receiver.eq_ignore_ascii_case(client.current_nickname()) {
+ return Ok(String::from("I am right here!"));
+ }
+
+ if receiver.eq_ignore_ascii_case(&sender) {
+ return Ok(String::from("That's your name!"));
+ }
+
+ if let Some(channels) = client.list_channels() {
+ for channel in channels {
+ if let Some(users) = client.list_users(&channel) {
+ if users
+ .iter()
+ .any(|u| u.get_nickname().eq_ignore_ascii_case(&receiver))
+ {
+ return Ok(format!("{} is currently online.", receiver));
+ }
+ }
+ }
+ }
+
+ let tm = time::now().to_timespec();
+ let message = command.tokens[1..].join(" ");
+ let tell = database::NewTellMessage {
+ sender: &sender,
+ receiver: &receiver.to_lowercase(),
+ time: NaiveDateTime::from_timestamp(tm.sec, 0u32),
+ message: &message,
+ };
+
+ try_lock!(self.tells).insert_tell(&tell)?;
+
+ Ok(String::from("Got it!"))
+ }
+
+ fn on_namelist(
+ &self,
+ client: &IrcClient,
+ channel: &str,
+ ) -> Result<(), FrippyError> {
+ let receivers = try_lock!(self.tells)
+ .get_receivers()
+ .context(FrippyErrorKind::Tell)?;
+
+ if let Some(users) = client.list_users(channel) {
+ debug!("Outstanding tells for {:?}", receivers);
+
+ for receiver in users
+ .iter()
+ .map(|u| u.get_nickname())
+ .filter(|u| receivers.iter().any(|r| r == &u.to_lowercase()))
+ {
+ self.send_tells(client, receiver)?;
+ }
+
+ Ok(())
+ } else {
+ Ok(())
+ }
+ }
+ fn send_tells(&self, client: &IrcClient, receiver: &str) -> Result<(), FrippyError> {
+ if client.current_nickname() == receiver {
+ return Ok(());
+ }
+
+ let mut tells = try_lock!(self.tells);
+
+ let tell_messages = match tells.get_tells(&receiver.to_lowercase()) {
+ Ok(t) => t,
+ Err(e) => {
+ // This warning only occurs if frippy is built without a database
+ #[allow(unreachable_patterns)]
+ return match e.kind() {
+ ErrorKind::NotFound => Ok(()),
+ _ => Err(e.context(FrippyErrorKind::Tell))?,
+ };
+ }
+ };
+
+ for tell in tell_messages {
+ let now = Duration::new(time::now().to_timespec().sec as u64, 0);
+ let dur = now - Duration::new(tell.time.timestamp() as u64, 0);
+ let human_dur = format_duration(dur);
+
+ client
+ .send_notice(
+ receiver,
+ &format!(
+ "Tell from {} {} ago: {}",
+ tell.sender, human_dur, tell.message
+ ),
+ )
+ .context(FrippyErrorKind::Connection)?;
+
+ debug!(
+ "Sent {:?} from {:?} to {:?}",
+ tell.message, tell.sender, receiver
+ );
+ }
+
+ tells
+ .delete_tells(&receiver.to_lowercase())
+ .context(FrippyErrorKind::Tell)?;
+
+ Ok(())
+ }
+
+ fn invalid_command(&self, client: &IrcClient) -> String {
+ format!(
+ "Incorrect Command. \
+ Send \"{} tell help\" for help.",
+ client.current_nickname()
+ )
+ }
+
+ fn help(&self, client: &IrcClient) -> String {
+ format!(
+ "usage: {} tell user message\r\n\
+ example: {0} tell Foobar Hello!",
+ client.current_nickname()
+ )
+ }
+}
+
+impl<T: Database> Plugin for Tell<T> {
+ fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
+ let res = match message.command {
+ Command::JOIN(_, _, _) => self.send_tells(client, message.source_nickname().unwrap()),
+ Command::NICK(ref nick) => self.send_tells(client, nick),
+ Command::Response(resp, ref chan_info, _) => {
+ if resp == Response::RPL_NAMREPLY {
+ debug!("NAMREPLY info: {:?}", chan_info);
+
+ self.on_namelist(
+ client,
+ &chan_info[chan_info.len() - 1],
+ )
+ } else {
+ Ok(())
+ }
+ }
+ _ => Ok(()),
+ };
+
+ match res {
+ Ok(_) => ExecutionStatus::Done,
+ Err(e) => ExecutionStatus::Err(e),
+ }
+ }
+
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
+ panic!("Tell should not use threading")
+ }
+
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ if command.tokens.is_empty() {
+ return Ok(client
+ .send_notice(&command.source, &self.invalid_command(client))
+ .context(FrippyErrorKind::Connection)?);
+ }
+
+ let sender = command.source.to_owned();
+
+ Ok(match command.tokens[0].as_ref() {
+ "help" => client
+ .send_notice(&command.source, &self.help(client))
+ .context(FrippyErrorKind::Connection)
+ .into(),
+ _ => match self.tell_command(client, command) {
+ Ok(msg) => client
+ .send_notice(&sender, &msg)
+ .context(FrippyErrorKind::Connection),
+ Err(e) => client
+ .send_notice(&sender, &e.to_string())
+ .context(FrippyErrorKind::Connection)
+ .into(),
+ },
+ }?)
+ }
+
+ fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
+ Err(String::from("This Plugin does not implement any commands."))
+ }
+}
+
+use std::fmt;
+impl<T: Database> fmt::Debug for Tell<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Tell {{ ... }}")
+ }
+}
+
+pub mod error {
+ #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
+ #[error = "TellError"]
+ pub enum ErrorKind {
+ /// Not found command error
+ #[fail(display = "Tell was not found")]
+ NotFound,
+
+ /// MySQL error
+ #[cfg(feature = "mysql")]
+ #[fail(display = "Failed to execute MySQL Query")]
+ MysqlError,
+
+ /// No connection error
+ #[cfg(feature = "mysql")]
+ #[fail(display = "No connection to the database")]
+ NoConnection,
+ }
+}
diff --git a/src/plugins/url.rs b/src/plugins/url.rs
new file mode 100644
index 0000000..bff840f
--- /dev/null
+++ b/src/plugins/url.rs
@@ -0,0 +1,130 @@
+extern crate htmlescape;
+extern crate regex;
+
+use irc::client::prelude::*;
+
+use self::regex::Regex;
+
+use plugin::*;
+use utils;
+
+use self::error::*;
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::Fail;
+use failure::ResultExt;
+
+lazy_static! {
+ static ref RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap();
+}
+
+#[derive(PluginName, Debug)]
+pub struct Url {
+ max_kib: usize,
+}
+
+impl Url {
+ /// If a file is larger than `max_kib` KiB the download is stopped
+ pub fn new(max_kib: usize) -> Url {
+ Url { max_kib: max_kib }
+ }
+
+ fn grep_url(&self, msg: &str) -> Option<String> {
+ let captures = RE.captures(msg)?;
+ debug!("Url captures: {:?}", captures);
+
+ Some(captures.get(2)?.as_str().to_owned())
+ }
+
+ fn get_title<'a>(&self, body: &str) -> Result<String, UrlError> {
+ let title = body.find("<title")
+ .map(|tag| {
+ body[tag..]
+ .find('>')
+ .map(|offset| tag + offset + 1)
+ .map(|start| {
+ body[start..]
+ .find("</title>")
+ .map(|offset| start + offset)
+ .map(|end| &body[start..end])
+ })
+ })
+ .and_then(|s| s.and_then(|s| s))
+ .ok_or(ErrorKind::MissingTitle)?;
+
+ debug!("Title: {:?}", title);
+
+ htmlescape::decode_html(title).map_err(|_| ErrorKind::HtmlDecoding.into())
+ }
+
+ fn url(&self, text: &str) -> Result<String, UrlError> {
+ let url = self.grep_url(text).ok_or(ErrorKind::MissingUrl)?;
+ let body = utils::download(&url, Some(self.max_kib)).context(ErrorKind::Download)?;
+
+ let title = self.get_title(&body)?;
+
+ Ok(title.replace('\n', "|").replace('\r', "|"))
+ }
+}
+
+impl Plugin for Url {
+ fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus {
+ match message.command {
+ Command::PRIVMSG(_, ref msg) => if RE.is_match(msg) {
+ ExecutionStatus::RequiresThread
+ } else {
+ ExecutionStatus::Done
+ },
+ _ => ExecutionStatus::Done,
+ }
+ }
+
+ fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> {
+ Ok(match message.command {
+ Command::PRIVMSG(_, ref content) => match self.url(content) {
+ Ok(title) => client
+ .send_privmsg(message.response_target().unwrap(), &title)
+ .context(FrippyErrorKind::Connection)?,
+ Err(e) => Err(e).context(FrippyErrorKind::Url)?,
+ },
+ _ => (),
+ })
+ }
+
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ Ok(client
+ .send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
+ .context(FrippyErrorKind::Connection)?)
+ }
+
+ fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> {
+ self.url(&command.tokens[0])
+ .map_err(|e| e.cause().unwrap().to_string())
+ }
+}
+
+pub mod error {
+ /// A URL plugin error
+ #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
+ #[error = "UrlError"]
+ pub enum ErrorKind {
+ /// A download error
+ #[fail(display = "A download error occured")]
+ Download,
+
+ /// Missing URL error
+ #[fail(display = "No URL was found")]
+ MissingUrl,
+
+ /// Missing title error
+ #[fail(display = "No title was found")]
+ MissingTitle,
+
+ /// Html decoding error
+ #[fail(display = "Failed to decode Html characters")]
+ HtmlDecoding,
+ }
+}
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..06156be
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,65 @@
+use std::str;
+use std::io::{self, Read};
+
+use reqwest::Client;
+use reqwest::header::Connection;
+
+use failure::ResultExt;
+use self::error::{DownloadError, ErrorKind};
+
+/// Downloads the file and converts it to a String.
+/// Any invalid bytes are converted to a replacement character.
+///
+/// The error indicated either a failed download or that the DownloadLimit was reached
+pub fn download(url: &str, max_kib: Option<usize>) -> Result<String, DownloadError> {
+ let mut response = Client::new()
+ .get(url)
+ .header(Connection::close())
+ .send()
+ .context(ErrorKind::Connection)?;
+
+ // 100 kibibyte buffer
+ let mut buf = [0; 100 * 1024];
+ let mut written = 0;
+ let mut bytes = Vec::new();
+
+ // 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) => Err(e).context(ErrorKind::Read)?,
+ };
+
+ bytes.extend_from_slice(&buf);
+ written += len;
+
+ // Check if the file is too large to download
+ if let Some(max_kib) = max_kib {
+ if written > max_kib * 1024 {
+ Err(ErrorKind::DownloadLimit)?;
+ }
+ }
+ }
+
+ Ok(String::from_utf8_lossy(&bytes).into_owned())
+}
+
+pub mod error {
+ #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
+ #[error = "DownloadError"]
+ pub enum ErrorKind {
+ /// Connection Error
+ #[fail(display = "A connection error has occured")]
+ Connection,
+
+ /// Read Error
+ #[fail(display = "A read error has occured")]
+ Read,
+
+ /// Reached download limit error
+ #[fail(display = "Reached download limit")]
+ DownloadLimit,
+ }
+}