qbot 0.1.3 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/qbot.rb +1 -0
- data/lib/qbot/adapter.rb +3 -7
- data/lib/qbot/adapter/mattermost.rb +19 -21
- data/lib/qbot/adapter/shell.rb +2 -6
- data/lib/qbot/adapter/slack.rb +17 -21
- data/lib/qbot/app.rb +5 -4
- data/lib/qbot/autorun.rb +4 -1
- data/lib/qbot/base.rb +31 -15
- data/lib/qbot/embed/cron.rb +12 -16
- data/lib/qbot/embed/echo.rb +2 -6
- data/lib/qbot/embed/help.rb +18 -0
- data/lib/qbot/embed/ping.rb +2 -2
- data/lib/qbot/message.rb +29 -0
- data/lib/qbot/storage/memory.rb +1 -1
- data/lib/qbot/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4c0dd3c3d9a34d8eae7415eb10ddefa307ebca4bd200321e18fee452de9de157
|
4
|
+
data.tar.gz: 3a4f94dda22cd3aa76a83efd0c6c664ab365ed18946bec7b3e3bd7862728a421
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf435067c01baa076842a9ce672291897668eeef96b4e33fb216c7149feb6481139b79944725ba0c4816223925648521c7ebedd64ad909601e9b3a89e2d8c502
|
7
|
+
data.tar.gz: b61c2f73b417f4d6eb08dbef13386385d7a221847ef9a7c52607f1f29fb519d11eb7e227fe91b0beb35d3055de81c0082664eee0a42080b3fcce7308c6e7892a
|
data/lib/qbot.rb
CHANGED
data/lib/qbot/adapter.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
1
|
+
require 'qbot/message'
|
2
2
|
|
3
|
-
|
3
|
+
module Qbot
|
4
4
|
|
5
5
|
module Adapter
|
6
6
|
|
@@ -20,7 +20,7 @@ module Qbot
|
|
20
20
|
|
21
21
|
def run(bots)
|
22
22
|
listen do |message|
|
23
|
-
bots.each { |bot| bot.
|
23
|
+
bots.each { |bot| bot.listen(message.dup) }
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -32,10 +32,6 @@ module Qbot
|
|
32
32
|
raise 'Not implemented'
|
33
33
|
end
|
34
34
|
|
35
|
-
def reply_to(message, text, **options)
|
36
|
-
raise 'Not implemented'
|
37
|
-
end
|
38
|
-
|
39
35
|
end
|
40
36
|
|
41
37
|
end
|
@@ -20,6 +20,8 @@ module Qbot
|
|
20
20
|
|
21
21
|
access_token(resp.headers['token'])
|
22
22
|
raise 'Login failed' unless @token
|
23
|
+
|
24
|
+
@bot_id = me['username']
|
23
25
|
end
|
24
26
|
|
25
27
|
def access_token(token)
|
@@ -35,22 +37,19 @@ module Qbot
|
|
35
37
|
end
|
36
38
|
|
37
39
|
def post(text, **options)
|
38
|
-
resp = api_call(:post, "/posts", body: options.merge(message: text))
|
39
|
-
Qbot.app.logger.info("#{self.class} - Post message: #{resp.status} - '#{text}'")
|
40
|
-
end
|
41
|
-
|
42
|
-
def reply_to(message, text, **options)
|
43
40
|
if options[:channel_id]
|
44
|
-
|
41
|
+
# do nothing
|
45
42
|
elsif options[:channel_name]
|
46
|
-
channel = channel(options
|
47
|
-
channel_id = channel['id'] if channel
|
43
|
+
channel = channel(options.delete(:channel_name))
|
44
|
+
options[:channel_id] = channel['id'] if channel
|
45
|
+
elsif options[:reply_to]
|
46
|
+
message = options.delete(:reply_to)
|
47
|
+
options[:channel_id] = message.data['channel_id']
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
post(text, **options.merge(channel_id: channel_id))
|
50
|
+
return unless options[:channel_id]
|
51
|
+
Qbot.app.logger.info("#{self.class} - Post message: '#{text}'")
|
52
|
+
api_call(:post, "/posts", body: options.merge(message: text))
|
54
53
|
end
|
55
54
|
|
56
55
|
private
|
@@ -59,10 +58,9 @@ module Qbot
|
|
59
58
|
end
|
60
59
|
|
61
60
|
def start_connection(&block)
|
62
|
-
running = true
|
63
61
|
ws_url = URI.join(@server.gsub(/^http(s?):/, 'ws\1:'), endpoint('/websocket')).to_s
|
64
62
|
|
65
|
-
ws = Faye::WebSocket::Client.new(ws_url)
|
63
|
+
ws = Faye::WebSocket::Client.new(ws_url, nil, {ping: 60})
|
66
64
|
ws.send({seq: 1, action: 'authentication_challenge', data: {token: @token}}.to_json)
|
67
65
|
|
68
66
|
ws.on :message do |e|
|
@@ -76,12 +74,12 @@ module Qbot
|
|
76
74
|
|
77
75
|
ws.on :close do |e|
|
78
76
|
Qbot.app.logger.info("#{self.class} - Websocket connection closed: #{e.code} #{e.reason}")
|
79
|
-
|
77
|
+
sleep 30
|
78
|
+
start_connection(&block)
|
80
79
|
end
|
81
80
|
|
82
81
|
ws.on :error do |e|
|
83
82
|
Qbot.app.logger.error("#{self.class} - Websocket encountered error: #{e.message}")
|
84
|
-
running = false
|
85
83
|
end
|
86
84
|
end
|
87
85
|
|
@@ -91,16 +89,16 @@ module Qbot
|
|
91
89
|
|
92
90
|
case type
|
93
91
|
when 'posted'
|
94
|
-
|
92
|
+
data = JSON.parse(data['data']['post'])
|
95
93
|
|
96
|
-
message = Qbot::Message.new
|
97
|
-
message.
|
98
|
-
message.
|
94
|
+
message = Qbot::Message.new(data)
|
95
|
+
message.text = data['message']
|
96
|
+
message.mention(/^\s*@#{@bot_id}\b/)
|
99
97
|
|
100
98
|
callback.call(message)
|
101
99
|
end
|
102
100
|
rescue => err
|
103
|
-
Qbot.app.logger.error("#{self.class} - ERROR
|
101
|
+
Qbot.app.logger.error("#{self.class} - ERROR: #{err}")
|
104
102
|
end
|
105
103
|
|
106
104
|
def api_call(method, path, **options, &block)
|
data/lib/qbot/adapter/shell.rb
CHANGED
@@ -18,9 +18,9 @@ module Qbot
|
|
18
18
|
def receive_line(text)
|
19
19
|
exit 0 if text.strip == 'exit'
|
20
20
|
|
21
|
-
message = Qbot::Message.new
|
22
|
-
message.data = text
|
21
|
+
message = Qbot::Message.new(text)
|
23
22
|
message.text = text
|
23
|
+
message.mention(/^\s*bot\b/)
|
24
24
|
|
25
25
|
@callback.call(message)
|
26
26
|
$stdout.print ">> "
|
@@ -39,10 +39,6 @@ module Qbot
|
|
39
39
|
$stdout.puts text
|
40
40
|
end
|
41
41
|
|
42
|
-
def reply_to(message, text, **options)
|
43
|
-
post(text, **options)
|
44
|
-
end
|
45
|
-
|
46
42
|
end
|
47
43
|
|
48
44
|
end
|
data/lib/qbot/adapter/slack.rb
CHANGED
@@ -12,9 +12,9 @@ module Qbot
|
|
12
12
|
SLACK_API_URL = 'https://slack.com/api'
|
13
13
|
|
14
14
|
def initialize(api_token: nil)
|
15
|
-
@server = URI.join(SLACK_API_URL, '/').to_s
|
16
|
-
|
17
15
|
access_token(api_token || ENV['QBOT_SLACK_API_TOKEN'])
|
16
|
+
@server = URI.join(SLACK_API_URL, '/').to_s
|
17
|
+
@bot_id = me['user_id']
|
18
18
|
end
|
19
19
|
|
20
20
|
def access_token(token)
|
@@ -26,26 +26,23 @@ module Qbot
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def close
|
29
|
-
EM.
|
29
|
+
EM.stop_event_loop
|
30
30
|
end
|
31
31
|
|
32
32
|
def post(text, **options)
|
33
|
-
resp = api_call(:post, "/chat.postMessage", options.merge(text: text))
|
34
|
-
Qbot.app.logger.info("#{self.class} - Post message: #{resp.status} - '#{text}'")
|
35
|
-
end
|
36
|
-
|
37
|
-
def reply_to(message, text, **options)
|
38
33
|
if options[:channel_id]
|
39
|
-
|
34
|
+
options[:channel] = options.delete(:channel_id)
|
40
35
|
elsif options[:channel_name]
|
41
|
-
channel = channel(options
|
42
|
-
|
36
|
+
channel = channel(options.delete(:channel_name))
|
37
|
+
options[:channel] = channel['id'] if channel
|
38
|
+
elsif options[:reply_to]
|
39
|
+
message = options.delete(:reply_to)
|
40
|
+
options[:channel] = message.data['channel']
|
43
41
|
end
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
post(text, **options.merge(channel: channel_id))
|
43
|
+
return unless options[:channel]
|
44
|
+
Qbot.app.logger.info("#{self.class} - Post message: '#{text}'")
|
45
|
+
api_call(:post, "/chat.postMessage", options.merge(text: text))
|
49
46
|
end
|
50
47
|
|
51
48
|
private
|
@@ -57,7 +54,6 @@ module Qbot
|
|
57
54
|
resp = api_call(:get, '/rtm.start')
|
58
55
|
data = JSON.parse(resp.body)
|
59
56
|
|
60
|
-
running = true
|
61
57
|
ws_url = data['url']
|
62
58
|
ws = Faye::WebSocket::Client.new(ws_url, nil, {ping: 60})
|
63
59
|
|
@@ -72,26 +68,26 @@ module Qbot
|
|
72
68
|
|
73
69
|
ws.on :close do |e|
|
74
70
|
Qbot.app.logger.info("#{self.class} - Websocket connection closed: #{e.code} #{e.reason}")
|
75
|
-
|
71
|
+
sleep 30
|
72
|
+
start_connection(&block)
|
76
73
|
end
|
77
74
|
|
78
75
|
ws.on :error do |e|
|
79
76
|
Qbot.app.logger.error("#{self.class} - Websocket encountered error: #{e.message}")
|
80
|
-
running = false
|
81
77
|
end
|
82
78
|
end
|
83
79
|
|
84
80
|
def emit_event(data, callback)
|
85
|
-
return unless type = data['
|
81
|
+
return unless type = data['type']
|
86
82
|
Qbot.app.logger.debug("#{self.class} - Event '#{type}' recieved")
|
87
83
|
|
88
84
|
case type
|
89
85
|
when 'message'
|
90
86
|
return if data['subtype']
|
91
87
|
|
92
|
-
message = Qbot::Message.new
|
93
|
-
message.data = data
|
88
|
+
message = Qbot::Message.new(data)
|
94
89
|
message.text = data['text']
|
90
|
+
message.mention(/^\s*<@#{@bot_id}>/)
|
95
91
|
|
96
92
|
callback.call(message)
|
97
93
|
end
|
data/lib/qbot/app.rb
CHANGED
@@ -26,24 +26,25 @@ module Qbot
|
|
26
26
|
|
27
27
|
def initialize
|
28
28
|
@bots = []
|
29
|
+
@help = {}
|
29
30
|
@threads = []
|
30
31
|
@timers = Timers::Group.new
|
31
32
|
@logger = Logger.new(STDOUT)
|
32
33
|
end
|
33
34
|
|
34
|
-
def
|
35
|
+
def add_bot(bot)
|
35
36
|
@bots << bot
|
36
37
|
end
|
37
38
|
|
38
|
-
def
|
39
|
-
@
|
39
|
+
def help_text(usages = {})
|
40
|
+
@help.update(usages)
|
40
41
|
end
|
41
42
|
|
42
43
|
def start
|
43
44
|
@logger.info("Booting #{self.class}.")
|
44
45
|
@logger.info("#{storage.class} - Storage driver loaded.")
|
45
46
|
@logger.info("#{adapter.class} - Adapter driver loaded.")
|
46
|
-
bots.each { |bot| @logger.info("
|
47
|
+
@bots.map { |bot| bot.class.name }.uniq.each { |bot| @logger.info("#{bot} loaded.") }
|
47
48
|
|
48
49
|
Thread.abort_on_exception = true
|
49
50
|
@threads << Thread.start { loop { @timers.wait } }
|
data/lib/qbot/autorun.rb
CHANGED
data/lib/qbot/base.rb
CHANGED
@@ -13,15 +13,20 @@ module Qbot
|
|
13
13
|
|
14
14
|
class << self
|
15
15
|
|
16
|
-
def on(pattern, &block)
|
17
|
-
pattern
|
18
|
-
|
16
|
+
def on(pattern, **options, &block)
|
17
|
+
pattern = Regexp.new("\b#{pattern}\b") unless Regexp === pattern
|
18
|
+
instance = new(pattern, **options, &block)
|
19
|
+
Qbot.app.add_bot(instance)
|
19
20
|
end
|
20
21
|
|
21
22
|
def cron(pattern, &block)
|
22
23
|
schedule(pattern, &block)
|
23
24
|
end
|
24
25
|
|
26
|
+
def help(usages)
|
27
|
+
Qbot.app.help_text(usages)
|
28
|
+
end
|
29
|
+
|
25
30
|
private
|
26
31
|
def schedule(pattern, &block)
|
27
32
|
parser = CronParser.new(pattern)
|
@@ -29,32 +34,43 @@ module Qbot
|
|
29
34
|
delay = parser.next(current) - current
|
30
35
|
|
31
36
|
Qbot.app.timers.after(delay) do
|
32
|
-
|
37
|
+
begin
|
38
|
+
instance = new(pattern)
|
39
|
+
instance.instance_eval(&block)
|
40
|
+
rescue => e
|
41
|
+
Qbot.app.logger.error("#{instance.class} - Error: #{e}")
|
42
|
+
end
|
43
|
+
|
33
44
|
schedule(pattern, &block)
|
34
45
|
end
|
35
46
|
end
|
36
47
|
|
37
48
|
end
|
38
49
|
|
39
|
-
def initialize(pattern, &block)
|
50
|
+
def initialize(pattern, **options, &block)
|
40
51
|
@pattern = pattern
|
52
|
+
@options = options
|
41
53
|
@callback = block
|
42
54
|
end
|
43
55
|
|
44
|
-
def
|
45
|
-
@
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
instance_exec($~, &@callback)
|
51
|
-
rescue => e
|
52
|
-
Qbot.app.logger.error("#{self.class} - Error: #{e}")
|
56
|
+
def listen(message)
|
57
|
+
if @options[:global]
|
58
|
+
return unless message.match(@pattern)
|
59
|
+
else
|
60
|
+
return unless message.mentioned?
|
61
|
+
return unless message.match(@pattern, prefix: message.mention)
|
53
62
|
end
|
63
|
+
|
64
|
+
Qbot.app.logger.debug("#{self.class} - Recieve message: '#{message.text}'")
|
65
|
+
|
66
|
+
@message = message
|
67
|
+
instance_exec(@message, &@callback)
|
68
|
+
rescue => e
|
69
|
+
Qbot.app.logger.error("#{self.class} - Error: #{e}")
|
54
70
|
end
|
55
71
|
|
56
72
|
def post(text, **options)
|
57
|
-
Qbot.app.adapter.reply_to
|
73
|
+
Qbot.app.adapter.post(text, reply_to: @message, **options)
|
58
74
|
end
|
59
75
|
|
60
76
|
def cache
|
data/lib/qbot/embed/cron.rb
CHANGED
@@ -2,25 +2,22 @@ module Qbot
|
|
2
2
|
|
3
3
|
class Cron < Qbot::Base
|
4
4
|
|
5
|
+
help "cron add <pattern> <message>" => "Add a cron task."
|
5
6
|
on /^cron add (\S+ \S+ \S+ \S+ \S+) (.+)$/ do |msg|
|
6
|
-
post start(unique_id, msg[1], msg[2])
|
7
|
+
post start(unique_id, msg.captures[1], msg.captures[2])
|
7
8
|
end
|
8
9
|
|
10
|
+
help "cron del <cron-id>" => "Remove a cron task."
|
9
11
|
on /^cron del (\d+)/ do |msg|
|
10
|
-
post stop(msg[1].to_i)
|
12
|
+
post stop(msg.captures[1].to_i)
|
11
13
|
end
|
12
14
|
|
15
|
+
help "cron list" => "List all cron tasks."
|
13
16
|
on /^cron list\b/ do |msg|
|
14
17
|
post list_all
|
15
18
|
end
|
16
19
|
|
17
|
-
|
18
|
-
Usage:
|
19
|
-
`cron add <pattern> <message>` - add a cron task.
|
20
|
-
`cron del <cron-id>` - remove a cron task.
|
21
|
-
`cron list` - list all cron tasks.
|
22
|
-
EOL
|
23
|
-
|
20
|
+
private
|
24
21
|
def self.timers
|
25
22
|
@timers ||= {}
|
26
23
|
end
|
@@ -29,26 +26,25 @@ module Qbot
|
|
29
26
|
schedule(id, cron, text) rescue return
|
30
27
|
|
31
28
|
cache[id] = [cron, text]
|
32
|
-
"ADD
|
29
|
+
"ADD #{format(id, cron, text)}"
|
33
30
|
end
|
34
31
|
|
35
32
|
def stop(id)
|
36
33
|
Cron.timers[id].cancel if Cron.timers[id]
|
37
34
|
cron, text = cache[id]
|
38
|
-
return unless cron && text
|
39
35
|
|
40
|
-
cache[id]
|
41
|
-
"DEL
|
36
|
+
cache[id] = nil
|
37
|
+
"DEL #{format(id, cron, text)}"
|
42
38
|
end
|
43
39
|
|
44
40
|
def list_all
|
45
41
|
resp = StringIO.new
|
46
42
|
cache.each do |id, (cron, text)|
|
47
43
|
next unless cron && text
|
48
|
-
resp.puts
|
44
|
+
resp.puts format(id, cron, text)
|
49
45
|
end
|
50
46
|
|
51
|
-
resp.string
|
47
|
+
resp.string.chomp
|
52
48
|
end
|
53
49
|
|
54
50
|
private
|
@@ -63,7 +59,7 @@ module Qbot
|
|
63
59
|
end
|
64
60
|
end
|
65
61
|
|
66
|
-
def
|
62
|
+
def format(id, cron, text)
|
67
63
|
"#{id.to_s.rjust(3, '0')}: #{cron} #{text}"
|
68
64
|
end
|
69
65
|
|
data/lib/qbot/embed/echo.rb
CHANGED
@@ -2,15 +2,11 @@ module Qbot
|
|
2
2
|
|
3
3
|
class Echo < Qbot::Base
|
4
4
|
|
5
|
+
help "echo <text>" => "Echo back the given <text>."
|
5
6
|
on /^echo( .+)$/ do |msg|
|
6
|
-
post msg[1].strip
|
7
|
+
post msg.captures[1].strip
|
7
8
|
end
|
8
9
|
|
9
|
-
usage <<~EOL
|
10
|
-
Usage:
|
11
|
-
`echo <text>` - Reflect the given <text>.
|
12
|
-
EOL
|
13
|
-
|
14
10
|
end
|
15
11
|
|
16
12
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Qbot
|
4
|
+
|
5
|
+
class Help < Qbot::Base
|
6
|
+
|
7
|
+
on /^help\b/i do |msg|
|
8
|
+
usage = Qbot.app.help_text
|
9
|
+
|
10
|
+
text = StringIO.new
|
11
|
+
text.puts 'Usage:'
|
12
|
+
text.puts usage.map { |key, val| "`#{key}` - #{usage[key]}" }
|
13
|
+
post text.string.chomp
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/lib/qbot/embed/ping.rb
CHANGED
data/lib/qbot/message.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Qbot
|
2
|
+
|
3
|
+
class Message
|
4
|
+
|
5
|
+
attr_accessor :text
|
6
|
+
attr_reader :data, :captures
|
7
|
+
|
8
|
+
def initialize(data, text = '')
|
9
|
+
@data = data
|
10
|
+
@text = text
|
11
|
+
end
|
12
|
+
|
13
|
+
def match(regexp, prefix: nil)
|
14
|
+
text = @text.dup
|
15
|
+
text.sub!(/^#{prefix}/, '') if prefix
|
16
|
+
@captures = text.strip.match(regexp)
|
17
|
+
end
|
18
|
+
|
19
|
+
def mention(regexp = nil)
|
20
|
+
@mention = text.slice(regexp) if regexp
|
21
|
+
@mention
|
22
|
+
end
|
23
|
+
|
24
|
+
def mentioned?
|
25
|
+
!!mention
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/qbot/storage/memory.rb
CHANGED
data/lib/qbot/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- haccht
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -175,7 +175,9 @@ files:
|
|
175
175
|
- lib/qbot/base.rb
|
176
176
|
- lib/qbot/embed/cron.rb
|
177
177
|
- lib/qbot/embed/echo.rb
|
178
|
+
- lib/qbot/embed/help.rb
|
178
179
|
- lib/qbot/embed/ping.rb
|
180
|
+
- lib/qbot/message.rb
|
179
181
|
- lib/qbot/storage.rb
|
180
182
|
- lib/qbot/storage/leveldb.rb
|
181
183
|
- lib/qbot/storage/memory.rb
|
@@ -201,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
203
|
version: '0'
|
202
204
|
requirements: []
|
203
205
|
rubyforge_project:
|
204
|
-
rubygems_version: 2.6
|
206
|
+
rubygems_version: 2.7.6
|
205
207
|
signing_key:
|
206
208
|
specification_version: 4
|
207
209
|
summary: Tiny chatbot flamework.
|