slack-bot-manager 0.1.0pre1 → 0.1.0pre2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|