slack-bot-manager 0.1.0pre1 → 0.1.0pre2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.gitmodules +0 -3
- data/.rubocop.yml +6 -0
- data/.rubocop_todo.yml +81 -0
- data/.travis.yml +26 -0
- data/CHANGELOG.md +1 -1
- data/Gemfile +6 -0
- data/README.md +62 -11
- data/Rakefile +17 -0
- data/examples/dm-bot/Gemfile +5 -0
- data/examples/dm-bot/Gemfile.lock +40 -0
- data/examples/dm-bot/README.md +23 -0
- data/examples/dm-bot/dm-bot.rb +98 -0
- data/examples/dm-bot/tokens.yml.sample +2 -0
- data/lib/slack-bot-manager/client/base.rb +50 -14
- data/lib/slack-bot-manager/client/commands.rb +5 -7
- data/lib/slack-bot-manager/config.rb +41 -13
- data/lib/slack-bot-manager/errors.rb +10 -13
- data/lib/slack-bot-manager/extend.rb +13 -2
- data/lib/slack-bot-manager/logger.rb +12 -8
- data/lib/slack-bot-manager/manager/base.rb +5 -3
- data/lib/slack-bot-manager/manager/connection.rb +88 -87
- data/lib/slack-bot-manager/manager/storage/dalli.rb +63 -0
- data/lib/slack-bot-manager/manager/storage/redis.rb +55 -0
- data/lib/slack-bot-manager/manager/storage.rb +6 -0
- data/lib/slack-bot-manager/manager/tokens.rb +26 -20
- data/lib/slack-bot-manager/version.rb +1 -1
- data/lib/slack-bot-manager.rb +2 -0
- data/slack-bot-manager.gemspec +10 -9
- data/spec/fixtures/slack-bot-manager/web/rtm_start.yml +59 -0
- data/spec/integration/client_spec.rb +62 -0
- data/spec/integration/manager_spec.rb +121 -0
- data/spec/slack-bot-manager/client/base_spec.rb +96 -0
- data/spec/slack-bot-manager/config_spec.rb +19 -0
- data/spec/slack-bot-manager/errors_spec.rb +5 -0
- data/spec/slack-bot-manager/extend_spec.rb +5 -0
- data/spec/slack-bot-manager/logger_spec.rb +5 -0
- data/spec/slack-bot-manager/version_spec.rb +7 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/vcr.rb +8 -0
- metadata +153 -6
@@ -1,24 +1,23 @@
|
|
1
1
|
module SlackBotManager
|
2
2
|
module Config
|
3
|
-
|
4
3
|
extend self
|
5
4
|
|
6
5
|
MANAGER_ATTRIBUTES = [
|
7
6
|
:tokens_key,
|
8
7
|
:teams_key,
|
9
8
|
:check_interval,
|
10
|
-
:
|
9
|
+
:storage_method,
|
10
|
+
:storage_options,
|
11
11
|
:logger,
|
12
12
|
:log_level,
|
13
13
|
:verbose
|
14
|
-
]
|
14
|
+
].freeze
|
15
15
|
|
16
16
|
CLIENT_ATTRIBUTES = [
|
17
|
-
:redis,
|
18
17
|
:logger,
|
19
18
|
:log_level,
|
20
19
|
:verbose
|
21
|
-
]
|
20
|
+
].freeze
|
22
21
|
|
23
22
|
WEB_CLIENT_ATTRIBUTES = [
|
24
23
|
:user_agent,
|
@@ -26,17 +25,28 @@ module SlackBotManager
|
|
26
25
|
:ca_path,
|
27
26
|
:ca_file,
|
28
27
|
:endpoint
|
29
|
-
]
|
28
|
+
].freeze
|
30
29
|
|
31
30
|
RTM_CLIENT_ATTRIBUTES = [
|
32
31
|
:websocket_ping,
|
33
32
|
:websocket_proxy
|
34
|
-
]
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
].freeze
|
34
|
+
|
35
|
+
RTM_CLIENT_METHODS = [
|
36
|
+
:url,
|
37
|
+
:team,
|
38
|
+
:self,
|
39
|
+
:users,
|
40
|
+
:channels,
|
41
|
+
:groups,
|
42
|
+
:ims,
|
43
|
+
:bots
|
44
|
+
].freeze
|
45
|
+
|
46
|
+
attr_accessor(*Config::MANAGER_ATTRIBUTES)
|
47
|
+
attr_accessor(*Config::CLIENT_ATTRIBUTES)
|
48
|
+
attr_accessor(*Config::WEB_CLIENT_ATTRIBUTES)
|
49
|
+
attr_accessor(*Config::RTM_CLIENT_ATTRIBUTES)
|
40
50
|
|
41
51
|
def reset
|
42
52
|
# Slack web and realtime config options
|
@@ -46,7 +56,8 @@ module SlackBotManager
|
|
46
56
|
self.tokens_key = 'tokens:statuses'
|
47
57
|
self.teams_key = 'tokens:teams'
|
48
58
|
self.check_interval = 5 # seconds
|
49
|
-
self.
|
59
|
+
self.storage_method = method(:detect_storage_method)
|
60
|
+
self.storage_options = {}
|
50
61
|
self.logger = defined?(Rails) ? Rails.logger : ::Logger.new(STDOUT)
|
51
62
|
self.log_level = ::Logger::INFO
|
52
63
|
self.logger.formatter = SlackBotManager::Logger::Formatter.new
|
@@ -72,6 +83,10 @@ module SlackBotManager
|
|
72
83
|
end
|
73
84
|
end
|
74
85
|
|
86
|
+
def storage_method
|
87
|
+
(val = @storage_method).respond_to?(:call) ? val.call : val
|
88
|
+
end
|
89
|
+
|
75
90
|
def verbose=(val)
|
76
91
|
@verbose = val
|
77
92
|
self.log_level = val ? ::Logger::DEBUG : ::Logger::INFO
|
@@ -94,6 +109,19 @@ module SlackBotManager
|
|
94
109
|
self.logger.formatter = formatter
|
95
110
|
end
|
96
111
|
|
112
|
+
private
|
113
|
+
|
114
|
+
def detect_storage_method
|
115
|
+
[:Redis, :Dalli].each do |storage_method|
|
116
|
+
begin
|
117
|
+
return SlackBotManager::Storage.const_get(storage_method)
|
118
|
+
rescue LoadError, NameError
|
119
|
+
false
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
fail NoStorageMethod, 'Missing storage method. Add redis or dalli to your Gemfile.'
|
124
|
+
end
|
97
125
|
end
|
98
126
|
|
99
127
|
class << self
|
@@ -1,19 +1,17 @@
|
|
1
1
|
module SlackBotManager
|
2
|
-
|
3
2
|
class ConnectionClosed < StandardError; end
|
4
3
|
class ConnectionRateLimited < StandardError; end
|
5
4
|
class InvalidToken < StandardError; end
|
5
|
+
class NoStorageMethod < StandardError; end
|
6
6
|
class TokenAlreadyConnected < StandardError; end
|
7
7
|
class TokenNotConnected < StandardError; end
|
8
8
|
class TokenRevoked < StandardError; end
|
9
9
|
|
10
|
-
|
11
10
|
module Errors
|
12
|
-
|
13
11
|
# Mapping of error classes to type
|
14
12
|
CLASS_ERROR_TYPES = {
|
15
13
|
token_revoked: [
|
16
|
-
SlackBotManager::InvalidToken,
|
14
|
+
SlackBotManager::InvalidToken,
|
17
15
|
SlackBotManager::TokenRevoked
|
18
16
|
],
|
19
17
|
rate_limited: [
|
@@ -22,29 +20,28 @@ module SlackBotManager
|
|
22
20
|
closed: [
|
23
21
|
SlackBotManager::ConnectionClosed,
|
24
22
|
Slack::RealTime::Client::ClientNotStartedError
|
25
|
-
]
|
26
|
-
}
|
23
|
+
]
|
24
|
+
}.freeze
|
27
25
|
|
28
26
|
# Regexp mapping of error keywords to type
|
29
27
|
STRING_ERROR_TYPES = {
|
30
28
|
token_revoked: /token_revoked|account_inactive|invalid_auth/i,
|
31
29
|
rate_limited: /rate_limit|status 429/i,
|
32
|
-
closed: /closed/i
|
33
|
-
}
|
30
|
+
closed: /closed/i
|
31
|
+
}.freeze
|
34
32
|
|
35
33
|
def determine_error_type(err)
|
36
34
|
# Check known error types, unless string
|
37
|
-
CLASS_ERROR_TYPES.each{|k,v| return k if v.include?(err) } unless err.is_a?(String)
|
35
|
+
CLASS_ERROR_TYPES.each { |k, v| return k if v.include?(err) } unless err.is_a?(String)
|
38
36
|
|
39
|
-
# Check string matches
|
40
|
-
STRING_ERROR_TYPES.each{|k,v| return k if v.match(err.to_s) }
|
37
|
+
# Check string matches for code responses or capture something inside it
|
38
|
+
STRING_ERROR_TYPES.each { |k, v| return k if v.match(err.to_s) }
|
41
39
|
|
42
40
|
:error
|
43
41
|
end
|
44
42
|
|
45
|
-
def on_error(err,
|
43
|
+
def on_error(err, _ = nil)
|
46
44
|
error(err)
|
47
45
|
end
|
48
|
-
|
49
46
|
end
|
50
47
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# via https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/array/extract_options.rb
|
2
2
|
|
3
3
|
# Add extract_options! used in ActiveSupport
|
4
|
-
|
5
4
|
unless {}.respond_to?(:extractable_options?)
|
6
5
|
class Hash
|
7
6
|
def extractable_options?
|
@@ -16,4 +15,16 @@ unless [].respond_to?(:extract_options!)
|
|
16
15
|
last.is_a?(Hash) && last.extractable_options? ? pop : {}
|
17
16
|
end
|
18
17
|
end
|
19
|
-
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Allow removing methods from Slack::RealTime::Client
|
21
|
+
module Slack
|
22
|
+
module RealTime
|
23
|
+
class Client
|
24
|
+
def off(type)
|
25
|
+
type = type.to_s
|
26
|
+
callbacks.delete(type) if callbacks.key?(type)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module SlackBotManager
|
2
2
|
module Logger
|
3
|
-
|
4
3
|
def info(msg)
|
5
4
|
logger.info(@id) { msg }
|
6
5
|
end
|
@@ -17,28 +16,33 @@ module SlackBotManager
|
|
17
16
|
logger.error(@id) { msg }
|
18
17
|
end
|
19
18
|
|
20
|
-
|
21
19
|
class Formatter
|
22
|
-
SEVERITY_TO_COLOR_MAP
|
20
|
+
SEVERITY_TO_COLOR_MAP = {
|
21
|
+
'DEBUG' => '0;37',
|
22
|
+
'INFO' => '32',
|
23
|
+
'WARN' => '33',
|
24
|
+
'ERROR' => '31',
|
25
|
+
'FATAL' => '31',
|
26
|
+
'UNKNOWN' => '37'
|
27
|
+
}.freeze
|
23
28
|
|
24
29
|
def call(severity, timeat, progname, message)
|
25
|
-
formatted_severity =
|
26
|
-
formatted_time = timeat.strftime(
|
30
|
+
formatted_severity = format('%-5s', severity).strip
|
31
|
+
formatted_time = timeat.strftime('%Y-%m-%d %H:%M:%S.') << timeat.usec.to_s[0..2].rjust(3)
|
27
32
|
color = SEVERITY_TO_COLOR_MAP[severity]
|
28
33
|
|
29
34
|
# Handle backtrace, if any
|
30
35
|
msg = message.to_s
|
31
|
-
message.backtrace.each{|n| msg << "\n #{n}"} if message.respond_to?(:backtrace)
|
36
|
+
message.backtrace.each { |n| msg << "\n #{n}" } if message.respond_to?(:backtrace)
|
32
37
|
|
33
38
|
[
|
34
39
|
"\033[0;37m#{formatted_time}\033[0m", # Formatted time
|
35
40
|
"[\033[#{color}m#{formatted_severity}\033[0m]", # Level
|
36
|
-
"[PID:#{
|
41
|
+
"[PID:#{$PID}]", # PID
|
37
42
|
progname && progname != '' && "(#{progname})", # Progname (team ID), if exists
|
38
43
|
msg.strip # Message
|
39
44
|
].compact.join(' ') + "\n"
|
40
45
|
end
|
41
46
|
end
|
42
|
-
|
43
47
|
end
|
44
48
|
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module SlackBotManager
|
2
2
|
class Manager
|
3
|
-
|
4
3
|
include Tokens
|
5
4
|
include Connection
|
6
5
|
include Errors
|
7
6
|
include Logger
|
8
7
|
|
9
|
-
attr_accessor :connections
|
8
|
+
attr_accessor :connections, :storage
|
10
9
|
attr_accessor(*Config::MANAGER_ATTRIBUTES)
|
11
10
|
|
12
11
|
def initialize(*args)
|
@@ -19,8 +18,12 @@ module SlackBotManager
|
|
19
18
|
SlackBotManager::Config::MANAGER_ATTRIBUTES.each do |key|
|
20
19
|
send("#{key}=", options[key] || SlackBotManager.config.send(key))
|
21
20
|
end
|
21
|
+
|
22
|
+
# Set token storage method
|
23
|
+
@storage = storage_method.new(storage_options)
|
22
24
|
end
|
23
25
|
|
26
|
+
# Include config helpers
|
24
27
|
class << self
|
25
28
|
def configure
|
26
29
|
block_given? ? yield(config) : config
|
@@ -30,6 +33,5 @@ module SlackBotManager
|
|
30
33
|
Config
|
31
34
|
end
|
32
35
|
end
|
33
|
-
|
34
36
|
end
|
35
37
|
end
|
@@ -1,133 +1,137 @@
|
|
1
1
|
module SlackBotManager
|
2
2
|
module Connection
|
3
|
-
|
4
3
|
# Monitor our connections, while connections is {...}
|
5
4
|
def monitor
|
6
|
-
while connections
|
5
|
+
while connections
|
7
6
|
sleep 1 # Pause momentarily
|
8
7
|
|
9
8
|
# On occasion, check our connection statuses
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
9
|
+
next unless Time.now.to_i % check_interval == 0
|
10
|
+
|
11
|
+
# Get tokens and connection statuses
|
12
|
+
tokens_status = storage.get_all(tokens_key)
|
13
|
+
rtm_status = storage.get_all(teams_key)
|
14
|
+
|
15
|
+
# Manage connections
|
16
|
+
connections.each do |cid, conn|
|
17
|
+
id, _s = cid.split(':')
|
18
|
+
|
19
|
+
# Remove/continue if connection is/will close or no longer connected
|
20
|
+
if !conn
|
21
|
+
warning("Removing: #{id} (Reason: rtm_not_connection)")
|
22
|
+
destroy(cid: cid)
|
23
|
+
|
24
|
+
elsif !conn.connected?
|
25
|
+
warning("Removing: #{conn.id} (Reason: #{conn.status})")
|
26
|
+
to_remove = ['token_revoked'].include?((conn.status || '').to_s)
|
27
|
+
destroy(cid: cid, remove_token: to_remove)
|
28
|
+
|
29
|
+
# Team is no longer valid, remove
|
30
|
+
elsif tokens_status[conn.id].nil? || tokens_status[conn.id].empty?
|
31
|
+
warning("Removing: #{conn.id} (Reason: token_missing)")
|
32
|
+
destroy(cid: cid, remove_token: true)
|
33
|
+
|
34
|
+
elsif rtm_status[conn.id] == 'destroy'
|
35
|
+
warning("Removing: #{conn.id} (Reason: token_destroy)")
|
36
|
+
destroy(cid: cid)
|
37
|
+
|
38
|
+
# Kill connection if token has changed, will re-create below
|
39
|
+
elsif tokens_status[conn.id] != conn.token
|
40
|
+
warning("Removing: #{conn.id} (Reason: new_token)")
|
41
|
+
destroy(cid: cid)
|
42
|
+
|
43
|
+
# Connection should re-create unless active, will update below
|
44
|
+
elsif rtm_status[conn.id] != 'active'
|
45
|
+
reason = rtm_status[conn.id] || '(unknown)'
|
46
|
+
warning("Restarting: #{conn.id} (Reason: #{reason})")
|
47
|
+
destroy(cid: cid)
|
48
|
+
storage.set(tokens_key, conn.id, tokens_status[conn.id])
|
48
49
|
end
|
50
|
+
end
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
+
# Pause before reconnects, as destroy might be processing in threads
|
53
|
+
sleep 1
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
# Check for new tokens / reconnections
|
56
|
+
# (reload keys since we might modify if bad). Kill and recreate
|
57
|
+
tokens_status = storage.get_all(tokens_key)
|
58
|
+
rtm_status = storage.get_all(teams_key)
|
59
|
+
tokens_diff = (tokens_status.keys - rtm_status.keys) + (rtm_status.keys - tokens_status.keys)
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
61
|
+
unless tokens_diff.empty?
|
62
|
+
tokens_diff.uniq.each do |id|
|
63
|
+
warning("Starting: #{id}")
|
64
|
+
destroy(id: id)
|
65
|
+
create(id, tokens_status[id])
|
63
66
|
end
|
64
|
-
|
65
|
-
info("Active Connections: [#{connections.count}]")
|
66
67
|
end
|
68
|
+
|
69
|
+
info("Active Connections: [#{connections.count}]")
|
67
70
|
end
|
68
71
|
end
|
69
72
|
|
70
73
|
# Create websocket connections for active tokens
|
71
74
|
def start
|
72
75
|
# Clear RTM connections
|
73
|
-
|
76
|
+
storage.delete_all(teams_key)
|
74
77
|
|
75
78
|
# Start a new connection for each team
|
76
|
-
|
77
|
-
create(id,token)
|
79
|
+
storage.get_all(tokens_key).each do |id, token|
|
80
|
+
create(id, token)
|
78
81
|
end
|
79
82
|
end
|
80
83
|
|
81
84
|
# Remove all connections from app, will disconnect in monitor loop
|
82
85
|
def stop
|
83
86
|
# Thread wrapped to ensure no lock issues on shutdown
|
84
|
-
thr = Thread.new
|
85
|
-
conns =
|
86
|
-
|
87
|
-
conns.each{|k,
|
87
|
+
thr = Thread.new do
|
88
|
+
conns = storage.get_all(teams_key)
|
89
|
+
storage.pipeline do
|
90
|
+
conns.each { |k, _| storage.set(teams_key, k, 'destroy') }
|
88
91
|
end
|
89
|
-
info(
|
90
|
-
|
92
|
+
info('Stopped.')
|
93
|
+
end
|
91
94
|
thr.join
|
92
95
|
end
|
93
96
|
|
94
|
-
# Issue restart status on all RTM connections
|
97
|
+
# Issue restart status on all RTM connections
|
98
|
+
# will re-connect in monitor loop
|
95
99
|
def restart
|
96
|
-
conns =
|
97
|
-
|
98
|
-
conns.each{|k,
|
100
|
+
conns = storage.get_all(teams_key)
|
101
|
+
storage.pipeline do
|
102
|
+
conns.each { |k, _| storage.set(teams_key, k, 'restart') }
|
99
103
|
end
|
100
104
|
end
|
101
105
|
|
102
106
|
# Get status of current connections
|
103
107
|
def status
|
104
|
-
info("Active connections: [#{
|
108
|
+
info("Active connections: [#{storage.get_all(teams_key).count}]")
|
105
109
|
end
|
106
110
|
|
107
|
-
|
108
|
-
protected
|
111
|
+
protected
|
109
112
|
|
110
113
|
# Find the connection based on id and has active connection
|
111
114
|
def find_connection(id)
|
112
|
-
connections.each do |
|
115
|
+
connections.each do |_, conn|
|
113
116
|
return (conn.connected? ? conn : false) if conn && conn.id == id
|
114
117
|
end
|
115
118
|
false
|
116
119
|
end
|
117
120
|
|
118
121
|
# Create new connection if not exist
|
119
|
-
def create(id,token)
|
120
|
-
|
122
|
+
def create(id, token)
|
123
|
+
fail SlackBotManager::TokenAlreadyConnected if find_connection(id)
|
121
124
|
|
122
125
|
# Create connection
|
123
|
-
conn = SlackBotManager::Client.new(
|
126
|
+
conn = SlackBotManager::Client.new(token)
|
127
|
+
conn.connect
|
124
128
|
|
125
|
-
# Add to connections using a uniq token
|
129
|
+
# Add to connections using a uniq token
|
126
130
|
if conn
|
127
|
-
cid = [id,Time.now.to_i].join(':')
|
131
|
+
cid = [id, Time.now.to_i].join(':')
|
128
132
|
connections[cid] = conn
|
129
133
|
info("Connected: #{id} (Connection: #{cid})")
|
130
|
-
|
134
|
+
storage.set(teams_key, id, 'active')
|
131
135
|
end
|
132
136
|
rescue => err
|
133
137
|
on_error(err)
|
@@ -139,29 +143,26 @@ module SlackBotManager
|
|
139
143
|
|
140
144
|
# Get connection or search for connection with cid
|
141
145
|
if options[:cid]
|
142
|
-
conn
|
146
|
+
conn = connections[options[:cid]]
|
147
|
+
cid = options[:cid]
|
143
148
|
elsif options[:id]
|
144
149
|
conn, cid = find_connection(options[:id])
|
145
150
|
end
|
146
151
|
return false unless conn && cid
|
147
152
|
|
148
|
-
# Kill connection, remove from
|
153
|
+
# Kill connection, remove from keys, and delete from list
|
149
154
|
begin
|
150
|
-
thr = Thread.new
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
self.redis.hdel(tokens_key, conn.id) rescue nil
|
155
|
-
end
|
156
|
-
}
|
155
|
+
thr = Thread.new do
|
156
|
+
storage.delete(teams_key, conn.id) rescue nil
|
157
|
+
storage.delete(tokens_key, conn.id) rescue nil if options[:remove_token]
|
158
|
+
end
|
157
159
|
thr.join
|
158
160
|
connections.delete(cid)
|
159
|
-
rescue
|
161
|
+
rescue
|
160
162
|
nil
|
161
163
|
end
|
162
164
|
rescue => err
|
163
165
|
on_error(err)
|
164
166
|
end
|
165
|
-
|
166
167
|
end
|
167
168
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'dalli'
|
2
|
+
|
3
|
+
module SlackBotManager
|
4
|
+
module Storage
|
5
|
+
class Dalli
|
6
|
+
attr_accessor :connection
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
if options.is_a?(Dalli)
|
10
|
+
@connecton = options
|
11
|
+
else
|
12
|
+
servers = options.delete(:servers)
|
13
|
+
@connection = ::Dalli::Client.new(servers, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def pipeline(&block)
|
18
|
+
yield block
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_all(type)
|
22
|
+
connection.get(type) || {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(type, key)
|
26
|
+
connection.get(type).try(key)
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(type, key, val)
|
30
|
+
obj = get_all(type).merge(key => val)
|
31
|
+
connection.set(type, obj)
|
32
|
+
end
|
33
|
+
|
34
|
+
def multiset(type, *args)
|
35
|
+
vals = args.extract_options!
|
36
|
+
obj = get_all(type).merge(vals)
|
37
|
+
connection.set(type, obj)
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete(type, key)
|
41
|
+
obj = get_all(type)
|
42
|
+
obj.delete(key)
|
43
|
+
connection.set(type, obj)
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_all(type)
|
47
|
+
connection.delete(type)
|
48
|
+
end
|
49
|
+
|
50
|
+
# def expire(key, len)
|
51
|
+
# connection.expire(key, len)
|
52
|
+
# end
|
53
|
+
|
54
|
+
# def exists(type)
|
55
|
+
# connection.exists(key)
|
56
|
+
# end
|
57
|
+
|
58
|
+
# def incrby(type, key, incr=1)
|
59
|
+
# # TODO
|
60
|
+
# end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
module SlackBotManager
|
4
|
+
module Storage
|
5
|
+
class Redis
|
6
|
+
attr_accessor :connection
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@connection = options.is_a?(Redis) ? options : ::Redis.new(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def pipeline(&block)
|
13
|
+
connection.pipelined do
|
14
|
+
yield block
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_all(type)
|
19
|
+
connection.hgetall(type)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get(type, key)
|
23
|
+
connection.get(type, key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def set(type, key, val)
|
27
|
+
connection.hset(type, key, val)
|
28
|
+
end
|
29
|
+
|
30
|
+
def multiset(type, *args)
|
31
|
+
connection.hmset(type, *args)
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete(type, key)
|
35
|
+
connection.hdel(type, key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete_all(type)
|
39
|
+
connection.del(type)
|
40
|
+
end
|
41
|
+
|
42
|
+
# def expire(key, len)
|
43
|
+
# connection.expire(key, len)
|
44
|
+
# end
|
45
|
+
|
46
|
+
# def exists(type)
|
47
|
+
# connection.exists(key)
|
48
|
+
# end
|
49
|
+
|
50
|
+
# def incrby(type, key, incr=1)
|
51
|
+
# # TODO
|
52
|
+
# end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|