qbot 0.1.3 → 0.1.10
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 +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.
|