kybus-bot 0.4.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 189f024f3c6411b1c2c7d918a7ccfca6afd2cffe096c53f76522932c74be1b17
4
+ data.tar.gz: 9f9b3928b68ea6b1fc1e6ae204d5e0a34223c3e909b2986bae0c477b848d73ca
5
+ SHA512:
6
+ metadata.gz: 234bf15cd1c11549de4b8d3513a7abc50f76ffaae56aeb2248a112007b540fccf0e6a42f195c67b7a2a5032d13930e20f2384fb117ab86194d564c30ebb1d423
7
+ data.tar.gz: f3c6e1743cea030340a677d6b7674932ded040e2ac4febfcc24eacfb76e7f36d39bbec85a908c52fe7e06bf782364a11abebc4f941b1d314b60e48fed7ed7766
data/lib/kybus/bot.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kybus/core'
4
+ require 'kybus/storage'
5
+
6
+ require_relative 'bot/base'
7
+ require_relative 'bot/message'
8
+ require_relative 'bot/version'
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Bot
5
+ # Implements a factory singleton for building bot adapters
6
+ module Adapter
7
+ extend Kybus::DRY::ResourceInjector
8
+
9
+ # builds the abstract adapter
10
+ def self.from_config(configs)
11
+ require_relative configs['name']
12
+ resource(configs['name']).new(configs)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Bot
5
+ # :nodoc: #
6
+ module Adapter
7
+ # :nodoc: #
8
+ # Wraps a debugging message inside a class.
9
+ class DebugMessage < Kybus::Bot::Message
10
+ # It receives a string with the raw text and the id of the channel
11
+ def initialize(text, channel)
12
+ @text = text
13
+ @channel = channel
14
+ end
15
+
16
+ # Returns the channel id
17
+ def channel_id
18
+ "debug_message__#{@channel}"
19
+ end
20
+
21
+ # Returns the message contents
22
+ def raw_message
23
+ @text
24
+ end
25
+ end
26
+
27
+ # This class simulates a message chat with a user.
28
+ class Channel
29
+ # It is build from
30
+ # an array of raw messages, the name of the channel and the config
31
+ # to enable debug messages
32
+ def initialize(messages, name, echo)
33
+ @state = :open
34
+ @pending_messages = messages.dup
35
+ @name = name
36
+ @echo = echo
37
+ end
38
+
39
+ # Checks if there are messages open or that has not been answered
40
+ def open?
41
+ @state == :open
42
+ end
43
+
44
+ # Checks if there are still messages in the channel
45
+ def empty?
46
+ @pending_messages.empty?
47
+ end
48
+
49
+ # returns the next message in the buffer
50
+ def read_message
51
+ @state = :closed
52
+ DebugMessage.new(@pending_messages.shift, @name)
53
+ end
54
+
55
+ def send_data(message)
56
+ return unless @echo
57
+
58
+ puts "Sending message to channel: #{@name}"
59
+ puts message
60
+ end
61
+
62
+ attr_writer :echo
63
+
64
+ # receives the answer from the bot
65
+ def answer(message)
66
+ send_data(message)
67
+ @state = :open
68
+ end
69
+ end
70
+
71
+ ##
72
+ # This adapter is intended to be used on unit tests and development.
73
+ class Debug
74
+ # Exception for stoping the loop of messages
75
+ class NoMoreMessageException < Kybus::Exceptions::AntError
76
+ def initialize
77
+ super('There are no messages left')
78
+ end
79
+ end
80
+
81
+ # It receives a hash with the configurations:
82
+ # - name: the name of the channel
83
+ # - channels a key value, where the key is a name and the value the
84
+ # list of the messages in the channel.
85
+ # - echo: a flag to enable debug messages.
86
+ def initialize(configs)
87
+ @channels = {}
88
+ configs['channels'].each do |name, messages|
89
+ @channels[name] = Channel.new(messages, name, configs['echo'])
90
+ end
91
+ end
92
+
93
+ # Interface for receiving message
94
+ def read_message
95
+ # take the first message from the first open message,
96
+ # then rotate the array
97
+ loop do
98
+ raise NoMoreMessageException if @channels.values.all?(&:empty?)
99
+
100
+ msg = @channels.values.find(&:open?)
101
+ return msg.read_message if msg
102
+
103
+ # :nocov: #
104
+ sleep(1)
105
+ # :nocov: #
106
+ end
107
+ end
108
+
109
+ # removes prefix from channel id
110
+ def channel(name)
111
+ @channels[name.gsub('debug_message__', '')]
112
+ end
113
+
114
+ # interface for sending messages
115
+ def send_message(channel_name, contents)
116
+ channel(channel_name).answer(contents)
117
+ end
118
+
119
+ # interface for sending video
120
+ def send_video(channel_name, video_url)
121
+ channel(channel_name).answer("VIDEO: #{video_url}")
122
+ end
123
+
124
+ # interface for sending uadio
125
+ def send_audio(channel_name, audio_url)
126
+ channel(channel_name).answer("AUDIO: #{audio_url}")
127
+ end
128
+
129
+ # interface for sending image
130
+ def send_image(channel_name, image_url)
131
+ channel(channel_name).answer("IMG: #{image_url}")
132
+ end
133
+
134
+ # changes echo config
135
+ def echo=(toogle)
136
+ @channels.each { |_, channel| channel.echo = toogle }
137
+ end
138
+ end
139
+
140
+ register('debug', Debug)
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'discordrb'
4
+
5
+ module Kybus
6
+ module Bot
7
+ # :nodoc: #
8
+ module Adapter
9
+ # :nodoc: #
10
+ # Wraps a debugging message inside a class.
11
+ class DiscordMessage < Kybus::Bot::Message
12
+ # It receives a string with the raw text and the id of the channel
13
+ def initialize(msg)
14
+ @message = msg
15
+ end
16
+
17
+ # Returns the channel id
18
+ def channel_id
19
+ @message.channel.id
20
+ end
21
+
22
+ # Returns the message contents
23
+ def raw_message
24
+ @message.content
25
+ end
26
+
27
+ def user
28
+ @message.author.id
29
+ end
30
+
31
+ def is_private?
32
+ @message.channel.private?
33
+ end
34
+
35
+ def reply?
36
+ @message.message.reply?
37
+ end
38
+
39
+ def replied_message
40
+ DiscordMessage.new(@message.message.referenced_message)
41
+ end
42
+ end
43
+
44
+ ##
45
+ # This adapter is intended to be used on unit tests and development.
46
+ class Discord
47
+ include ::Kybus::Logger
48
+ # It receives a hash with the configurations:
49
+ # - name: the name of the channel
50
+ # - channels a key value, where the key is a name and the value the
51
+ # list of the messages in the channel.
52
+ # - echo: a flag to enable debug messages.
53
+ def initialize(configs)
54
+ @config = configs
55
+ @client = Discordrb::Bot.new(token: @config['token'])
56
+ @pool = []
57
+ @client.message do |msg|
58
+ @pool << msg
59
+ end
60
+ @client.run(:async)
61
+ end
62
+
63
+ attr_reader :client
64
+
65
+ def mention(id)
66
+ "<@!#{id}>"
67
+ end
68
+
69
+ # Interface for receiving message
70
+ def read_message
71
+ # take the first message from the first open message,
72
+ loop do
73
+ if @pool.empty?
74
+ sleep(0.1)
75
+ else
76
+ break
77
+ end
78
+ end
79
+ DiscordMessage.new(@pool.shift)
80
+ end
81
+
82
+ # interface for sending messages
83
+ def send_message(channel_name, contents)
84
+ puts "#{channel_name} => #{contents}" if @config['debug']
85
+ channel = @client.channel(channel_name)
86
+ if channel
87
+ channel.send_message(contents)
88
+ else
89
+ @client.user(channel_name).pm(contents)
90
+ end
91
+ end
92
+
93
+ # # interface for sending video
94
+ # def send_video(channel_name, video_url)
95
+ # file = Faraday::UploadIO.new(video_url, 'video/mp4')
96
+ # @client.api.send_video(chat_id: channel_name, audio: file)
97
+ # end
98
+ #
99
+ # # interface for sending uadio
100
+ # def send_audio(channel_name, audio_url)
101
+ # file = Faraday::UploadIO.new(audio_url, 'audio/mp3')
102
+ # @client.api.send_audio(chat_id: channel_name, audio: file)
103
+ # end
104
+ #
105
+ # # interface for sending image
106
+ # def send_image(channel_name, image_url)
107
+ # file = Faraday::UploadIO.new(image_url, 'image/jpeg')
108
+ # @client.api.send_photo(chat_id: channel_name, photo: file)
109
+ # end
110
+ end
111
+
112
+ register('discord', Discord)
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'telegram/bot'
4
+
5
+ module Kybus
6
+ module Bot
7
+ # :nodoc: #
8
+ module Adapter
9
+ # :nodoc: #
10
+ # Wraps a debugging message inside a class.
11
+ class TelegramMessage < Kybus::Bot::Message
12
+ # It receives a string with the raw text and the id of the channel
13
+ def initialize(message)
14
+ @message = message
15
+ end
16
+
17
+ def reply?
18
+ !!@message.reply_to_message
19
+ end
20
+
21
+ def replied_message
22
+ TelegramMessage.new(@message.reply_to_message)
23
+ end
24
+
25
+ # Returns the channel id
26
+ def channel_id
27
+ @message.chat.id
28
+ end
29
+
30
+ # Returns the message contents
31
+ def raw_message
32
+ @message.to_s
33
+ end
34
+
35
+ def is_private?
36
+ @message.chat.type == 'private'
37
+ end
38
+
39
+ def user
40
+ @message.from.id
41
+ end
42
+ end
43
+
44
+ ##
45
+ # This adapter is intended to be used on unit tests and development.
46
+ class Telegram
47
+ include ::Kybus::Logger
48
+ # It receives a hash with the configurations:
49
+ # - name: the name of the channel
50
+ # - channels a key value, where the key is a name and the value the
51
+ # list of the messages in the channel.
52
+ # - echo: a flag to enable debug messages.
53
+ def initialize(configs)
54
+ @config = configs
55
+ @client = ::Telegram::Bot::Client.new(@config['token'])
56
+ end
57
+
58
+ # Interface for receiving message
59
+ def read_message
60
+ # take the first message from the first open message,
61
+ loop do
62
+ @client.listen do |message|
63
+ log_info('Received message', message: message.to_h,
64
+ from: message.from.to_h)
65
+ return TelegramMessage.new(message)
66
+ end
67
+ rescue Telegram::Bot::Exceptions::ResponseError => e
68
+ log_error('An error ocurred while calling to Telegram API', e)
69
+ end
70
+ end
71
+
72
+ def mention(id)
73
+ "[user](tg://user?id=#{id})"
74
+ end
75
+
76
+
77
+ # interface for sending messages
78
+ def send_message(channel_name, contents)
79
+ puts "#{channel_name} => #{contents}" if @config['debug']
80
+ @client.api.send_message(chat_id: channel_name, text: contents)
81
+ rescue Telegram::Bot::Exceptions::ResponseError => err
82
+ return if err[:error_code] == '403'
83
+ end
84
+
85
+ # interface for sending video
86
+ def send_video(channel_name, video_url)
87
+ file = Faraday::UploadIO.new(video_url, 'video/mp4')
88
+ @client.api.send_video(chat_id: channel_name, audio: file)
89
+ end
90
+
91
+ # interface for sending uadio
92
+ def send_audio(channel_name, audio_url)
93
+ file = Faraday::UploadIO.new(audio_url, 'audio/mp3')
94
+ @client.api.send_audio(chat_id: channel_name, audio: file)
95
+ end
96
+
97
+ # interface for sending image
98
+ def send_image(channel_name, image_url)
99
+ file = Faraday::UploadIO.new(image_url, 'image/jpeg')
100
+ @client.api.send_photo(chat_id: channel_name, photo: file)
101
+ end
102
+ end
103
+
104
+ register('telegram', Telegram)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,220 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kybus/dry/daemon'
4
+ require 'kybus/bot/adapters/base'
5
+ require 'kybus/storage'
6
+ require_relative 'command_definition'
7
+
8
+ require 'kybus/logger'
9
+
10
+ module Kybus
11
+ module Bot
12
+ # Base class for bot implementation. It wraps the threads execution, the
13
+ # provider and the state storage inside an object.
14
+ class Base
15
+ include Kybus::Storage::Datasource
16
+ include Kybus::Logger
17
+ attr_reader :provider
18
+
19
+ def send_message(content, channel = nil)
20
+ @provider.send_message(channel || current_channel, content)
21
+ end
22
+
23
+ def rescue_from(klass, &block)
24
+ @commands.register_command(klass, [], block)
25
+ end
26
+
27
+ def send_image(content, channel = nil)
28
+ @provider.send_image(channel || current_channel, content)
29
+ end
30
+
31
+ def send_audio(content, channel = nil)
32
+ @provider.send_audio(channel || current_channel, content)
33
+ end
34
+
35
+ # Configurations needed:
36
+ # - pool_size: number of threads created in execution
37
+ # - provider: a configuration for a thread provider.
38
+ # See supported adapters
39
+ # - name: The bot name
40
+ # - repository: Configurations about the state storage
41
+ def initialize(configs)
42
+ @pool_size = configs['pool_size']
43
+ @provider = Kybus::Bot::Adapter.from_config(configs['provider'])
44
+ @commands = Kybus::Bot::CommandDefinition.new
45
+
46
+ # TODO: move this to config
47
+ @repository = Kybus::Storage::Repository.from_config(
48
+ nil,
49
+ configs['state_repository']
50
+ .merge('primary_key' => 'channel_id',
51
+ 'table' => 'bot_sessions'),
52
+ {}
53
+ )
54
+ @factory = Kybus::Storage::Factory.new(EmptyModel)
55
+ @factory.register(:default, :json)
56
+ @factory.register(:json, @repository)
57
+ end
58
+
59
+ # Starts the bot execution, this is a blocking call.
60
+ def run
61
+ @pool = Array.new(@pool_size) do
62
+ # TODO: Create a subclass with the context execution
63
+ Kybus::DRY::Daemon.new(@pool_size, true) do
64
+ message = @provider.read_message
65
+ @last_message = message
66
+ process_message(message)
67
+ end
68
+ end
69
+ # TODO: Implement an interface for killing the process
70
+ @pool.each(&:run)
71
+ # :nocov: #
72
+ @pool.each(&:await)
73
+ # :nocov: #
74
+ end
75
+
76
+ # Process a single message, this method can be overwriten to enable
77
+ # more complex implementations of commands. It receives a message object.
78
+ def process_message(message)
79
+ run_simple_command!(message)
80
+ end
81
+
82
+ # Executes a command with the easiest definition. It runs a state machine:
83
+ # - If the message is a command, set the status to asking params
84
+ # - If the message is a param, stores it
85
+ # - If the command is ready to be executed, trigger it.
86
+ def run_simple_command!(message)
87
+ load_state!(message.channel_id)
88
+ log_debug('loaded state', message: message.to_h, state: @state.to_h)
89
+ if message.command?
90
+ self.command = message.raw_message
91
+ else
92
+ add_param(message.raw_message)
93
+ end
94
+ if command_ready?
95
+ run_command!
96
+ else
97
+ ask_param(next_missing_param)
98
+ end
99
+ save_state!
100
+ rescue StandardError => e
101
+ catch = @commands[e.class]
102
+ raise if catch.nil?
103
+
104
+ instance_eval(&catch.block)
105
+ clear_command
106
+ end
107
+
108
+ # DSL method for adding simple commands
109
+ def register_command(name, params = [], &block)
110
+ @commands.register_command(name, params, block)
111
+ end
112
+
113
+ # Method for triggering command
114
+ def run_command!
115
+ instance_eval(&current_command_object.block)
116
+ clear_command
117
+ end
118
+
119
+ def clear_command
120
+ @state[:cmd] = nil
121
+ end
122
+
123
+ # Checks if the command is ready to be executed
124
+ def command_ready?
125
+ cmd = current_command_object
126
+ cmd.ready?(current_params)
127
+ end
128
+
129
+ # loads parameters from state
130
+ def current_params
131
+ @state[:params] || {}
132
+ end
133
+
134
+ def params
135
+ current_params
136
+ end
137
+
138
+ def mention(name)
139
+ @provider.mention(name)
140
+ end
141
+
142
+ # Loads command from state
143
+ def current_command_object
144
+ command = @state[:cmd]
145
+ @commands[command] || @commands['default']
146
+ end
147
+
148
+ # returns the current_channel from where the message was sent
149
+ def current_channel
150
+ @state[:channel_id]
151
+ end
152
+
153
+ def current_user
154
+ @last_message.user
155
+ end
156
+
157
+ def is_private?
158
+ @last_message.is_private?
159
+ end
160
+
161
+ # stores the command into state
162
+ def command=(cmd)
163
+ log_debug('Message set as command', command: cmd)
164
+
165
+ @state[:cmd] = cmd.split(' ').first
166
+ @state[:params] = {}
167
+ end
168
+
169
+ # validates which is the following parameter required
170
+ def next_missing_param
171
+ current_command_object.next_missing_param(current_params)
172
+ end
173
+
174
+ # Sends a message to get the next parameter from the user
175
+ def ask_param(param)
176
+ log_debug('I\'m going to ask the next param', param: param)
177
+ @provider.send_message(current_channel,
178
+ "I need you to tell me #{param}")
179
+ @state[:requested_param] = param.to_s
180
+ end
181
+
182
+ # Stores a parameter into the status
183
+ def add_param(value)
184
+ return if @state[:requested_param].nil?
185
+
186
+ log_debug('Received new param',
187
+ param: @state[:requested_param].to_sym,
188
+ value: value)
189
+
190
+ @state[:params][@state[:requested_param].to_sym] = value
191
+ end
192
+
193
+ # Loads the state from storage
194
+ def load_state!(channel)
195
+ @state = load_state(channel)
196
+ end
197
+
198
+ # Private implementation for load message
199
+ def load_state(channel)
200
+ data = @factory.get(channel)
201
+ data[:params] = JSON.parse(data[:params], symbolize_names: true)
202
+ data
203
+ rescue Kybus::Storage::Exceptions::ObjectNotFound
204
+ @factory.create(channel_id: channel, params: {}.to_json)
205
+ end
206
+
207
+ # Saves the state into storage
208
+ def save_state!
209
+ json = @state[:params]
210
+ @state[:params] = json.to_json
211
+ @state.store
212
+ @state[:params] = json
213
+ end
214
+
215
+ def session
216
+ @repository
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Bot
5
+ # Object that wraps a command, it is analogus to a route definition.
6
+ # it currently only gets a param list, but it will be extended to a more
7
+ # complex DSL.
8
+ class Command
9
+ attr_reader :block
10
+
11
+ # Receives a list of params as symbols and the lambda with the block.
12
+ def initialize(params, block)
13
+ @params = params
14
+ @block = block
15
+ end
16
+
17
+ # Checks if the params object given contains all the needed values
18
+ def ready?(current_params)
19
+ @params.all? { |key| current_params.key?(key) }
20
+ end
21
+
22
+ # Finds the first empty param from the given parameter
23
+ def next_missing_param(current_params)
24
+ @params.find { |key| !current_params.key?(key) }
25
+ end
26
+ end
27
+
28
+ # Wraps a collection of commands.
29
+ class CommandDefinition
30
+ def initialize
31
+ @commands = {}
32
+ end
33
+
34
+ # Stores an operation definition
35
+ def register_command(name, params, block)
36
+ @commands[name] = Command.new(params, block)
37
+ end
38
+
39
+ # Returns a command with the name
40
+ def [](name)
41
+ @commands[name]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kybus/dry/daemon'
4
+ require 'kybus/bot/adapters/base'
5
+
6
+ module Kybus
7
+ module Bot
8
+ # Base implementation for messages from distinct providers
9
+ class Message
10
+ # Converts the messages into a hash
11
+ def to_h
12
+ {
13
+ text: raw_message,
14
+ channel: channel_id
15
+ }
16
+ end
17
+
18
+ # Returns true when the received message is a command. Convention states
19
+ # that messages should start with '/' to be considered commands
20
+ def command?
21
+ raw_message&.split(' ')&.first&.start_with?('/')
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sequel'
4
+ require 'sequel/extensions/migration'
5
+ module Kybus
6
+ module Bot
7
+ module Migrator
8
+ class << self
9
+ def run_migrations!(conn)
10
+ conn.create_table?(:bot_sessions) do
11
+ String :channel_id
12
+ String :user
13
+ String :params, text: true
14
+ String :cmd
15
+ String :requested_param
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'adapters/debug'
4
+ require_relative 'base'
5
+ module Kybus
6
+ module Bot
7
+ class NonDebugAdapterInTesting < StandardError
8
+ end
9
+
10
+ # Base class for bot implementation. It wraps the threads execution, the
11
+ # provider and the state storage inside an object.
12
+ class Base
13
+ include Kybus::Bot::Adapter
14
+ def stub_channels(messages)
15
+ raise(NonDebugAdapterInTesting) unless @provider.is_a?(Debug)
16
+
17
+ @provider = Debug.new('channels' => messages)
18
+ end
19
+
20
+ def run_test
21
+ run
22
+ rescue Debug::NoMoreMessageException
23
+ true
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Bot
5
+ VERSION = '0.4.1'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,250 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kybus-bot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.1
5
+ platform: ruby
6
+ authors:
7
+ - Gilberto Vargas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-06-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kybus-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kybus-logger
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: kybus-storage
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mocha
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.8'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.8'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.12'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.12'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack-minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '12.3'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '12.3'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rdoc
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '6.1'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '6.1'
139
+ - !ruby/object:Gem::Dependency
140
+ name: sequel
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: simplecov
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.16'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.16'
167
+ - !ruby/object:Gem::Dependency
168
+ name: sqlite3
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: telegram-bot-ruby
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: webmock
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '3.5'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '3.5'
209
+ description: Provides a framework for building bots with ruby
210
+ email:
211
+ - tachoguitar@gmail.com
212
+ executables: []
213
+ extensions: []
214
+ extra_rdoc_files: []
215
+ files:
216
+ - lib/kybus/bot.rb
217
+ - lib/kybus/bot/adapters/base.rb
218
+ - lib/kybus/bot/adapters/debug.rb
219
+ - lib/kybus/bot/adapters/discord.rb
220
+ - lib/kybus/bot/adapters/telegram.rb
221
+ - lib/kybus/bot/base.rb
222
+ - lib/kybus/bot/command_definition.rb
223
+ - lib/kybus/bot/message.rb
224
+ - lib/kybus/bot/migrator.rb
225
+ - lib/kybus/bot/testing.rb
226
+ - lib/kybus/bot/version.rb
227
+ homepage: https://github.com/tachomex/kybus
228
+ licenses:
229
+ - MIT
230
+ metadata: {}
231
+ post_install_message:
232
+ rdoc_options: []
233
+ require_paths:
234
+ - lib
235
+ required_ruby_version: !ruby/object:Gem::Requirement
236
+ requirements:
237
+ - - ">="
238
+ - !ruby/object:Gem::Version
239
+ version: '0'
240
+ required_rubygems_version: !ruby/object:Gem::Requirement
241
+ requirements:
242
+ - - ">="
243
+ - !ruby/object:Gem::Version
244
+ version: '0'
245
+ requirements: []
246
+ rubygems_version: 3.1.4
247
+ signing_key:
248
+ specification_version: 4
249
+ summary: Provides a framework for building bots with ruby
250
+ test_files: []