kybus-bot 0.8.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7c076fdf29d45a346d07aa5639349a66cff1a83ed15f0cfbafc4e6d4698cbd9
4
- data.tar.gz: 0fa6891cd37efd3a37b87b59e3dc90fd1628384c43863237e70ea6f757f2086a
3
+ metadata.gz: 5133b5a89dc9947680ecdbd8ec25befed895f57e017d19121278617b6bc0ff4d
4
+ data.tar.gz: '08e309bdf344e7066a5354c516487b12764b85cca7ea38165054471e5280cba7'
5
5
  SHA512:
6
- metadata.gz: f620f4d3674eb392f61f7a50abf378c1fa2fef5b718a93cf4ec1c340f906b1d924948f374fc0214d817664c26a97e4d5cdfa301c6588ba0c2e30bf5af8d7fb75
7
- data.tar.gz: f2b708234bb392bfaf98d75f29720ea081ead5e3ea4cba36a7703abd9dcc7516e8328eb8f2583604c0de819e15c391ba1a7066cb3b74bf4ef39c0713186f6e96
6
+ metadata.gz: da4828437e972ecd01dab962800ae67f011a7ed8ceb88b9f1484d7c4caef740811dfd816f578620a5679a86768b21a00a976750d206c25cdcd7df66e4e3f4a6c
7
+ data.tar.gz: c7bc12f54fb1352f774cbf607c12bbcae4802edc0d0058e99a38a7bb1554a8083b721a4e715e2185fd92fab668ece278e83ce4a2a15d43f28ddd23f5175cccfa
@@ -70,6 +70,10 @@ module Kybus
70
70
  def is_private?
71
71
  true
72
72
  end
73
+
74
+ def to_s
75
+ @text
76
+ end
73
77
  end
74
78
 
75
79
  # This class simulates a message chat with a user.
@@ -205,7 +209,7 @@ module Kybus
205
209
 
206
210
  # changes echo config
207
211
  def echo=(toogle)
208
- @channels.each { |_, channel| channel.echo = toogle }
212
+ @channels.each_value { |channel| channel.echo = toogle }
209
213
  end
210
214
  end
211
215
 
@@ -11,7 +11,7 @@ module Kybus
11
11
  class DiscordMessage < Kybus::Bot::Message
12
12
  # It receives a string with the raw text and the id of the channel
13
13
  def initialize(msg)
14
- super
14
+ super()
15
15
  @message = msg
16
16
  end
17
17
 
@@ -20,6 +20,18 @@ module Kybus
20
20
  @message.channel.id
21
21
  end
22
22
 
23
+ def message_id
24
+ nil
25
+ end
26
+
27
+ def has_attachment?
28
+ !!attachment
29
+ end
30
+
31
+ def attachment
32
+ @message.file
33
+ end
34
+
23
35
  # Returns the message contents
24
36
  def raw_message
25
37
  @message.content
@@ -80,7 +92,7 @@ module Kybus
80
92
  end
81
93
 
82
94
  # interface for sending messages
83
- def send_message(channel_name, contents, _caption = nil)
95
+ def send_message(contents, channel_name, _caption = nil)
84
96
  puts "#{channel_name} => #{contents}" if @config['debug']
85
97
  channel = @client.channel(channel_name)
86
98
  if channel
@@ -90,23 +102,27 @@ module Kybus
90
102
  end
91
103
  end
92
104
 
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
105
+ def message_builder(raw_message)
106
+ DiscordMessage.new(raw_message)
107
+ end
108
+
109
+ def send_file(channel_name, file, _caption = nil)
110
+ @client.send_file(channel_name, File.open(file, 'r'))
111
+ end
112
+
113
+ def send_video(channel_name, file, _caption = nil)
114
+ @client.send_file(channel_name, File.open(file, 'r'))
115
+ end
116
+
117
+ # interface for sending uadio
118
+ def send_audio(channel_name, file, _caption = nil)
119
+ @client.send_file(channel_name, File.open(file, 'r'))
120
+ end
121
+
122
+ # interface for sending image
123
+ def send_image(channel_name, file, _caption = nil)
124
+ @client.send_file(channel_name, File.open(file, 'r'))
125
+ end
110
126
  end
111
127
 
112
128
  register('discord', Discord)
@@ -42,17 +42,60 @@ module Kybus
42
42
  end
43
43
  end
44
44
 
45
+ def handle_message(body)
46
+ chat_id = body.dig('message', 'chat', 'id')
47
+ message_id = body.dig('message', 'message_id')
48
+ user = body.dig('message', 'from', 'username') || body.dig('message', 'from', 'first_name')
49
+ raw_message = body.dig('message', 'text')
50
+
51
+ replied_message = body.dig('message', 'reply_to_message')
52
+ is_private = body.dig('message', 'chat', 'type') == 'private'
53
+
54
+ # Check if the message has an attachment
55
+ has_attachment = body.dig('message',
56
+ 'photo') || body.dig('message', 'document') || body.dig('message', 'video')
57
+ attachment = if has_attachment
58
+ body.dig('message',
59
+ 'photo')&.last || body.dig('message', 'document') || body.dig('message', 'video')
60
+ end
61
+
62
+ # Serialize replied_message if it exists
63
+ serialized_replied_message = if replied_message
64
+ SerializedMessage.new(
65
+ provider: 'telegram',
66
+ channel_id: replied_message.dig('chat', 'id'),
67
+ message_id: replied_message['message_id'],
68
+ user: replied_message.dig('from',
69
+ 'username') || replied_message.dig('from',
70
+ 'first_name'),
71
+ raw_message: replied_message['text'],
72
+ is_private?: replied_message.dig('chat', 'type') == 'private'
73
+ ).serialize
74
+ end
75
+
76
+ SerializedMessage.new(
77
+ provider: 'telegram',
78
+ channel_id: chat_id,
79
+ message_id:,
80
+ user:,
81
+ replied_message: serialized_replied_message,
82
+ raw_message:,
83
+ is_private?: is_private,
84
+ attachment:
85
+ )
86
+ end
87
+
45
88
  def mention(id)
46
89
  "[user](tg://user?id=#{id})"
47
90
  end
48
91
 
49
92
  # interface for sending messages
50
93
  def send_message(contents, channel_name)
51
- puts "#{channel_name} => #{contents}" if @config['debug']
94
+ log_debug('Sending message', channel_name:, message: contents)
52
95
  @client.api.send_message(chat_id: channel_name.to_i, text: contents, parse_mode: @config['parse_mode'])
53
96
  # :nocov:
54
97
  rescue ::Telegram::Bot::Exceptions::ResponseError => e
55
- return if e.error_code == '403'
98
+ nil if e.error_code == '403'
56
99
  end
57
100
  # :nocov:
58
101
 
@@ -17,6 +17,7 @@ module Kybus
17
17
  # provider and the state storage inside an object.
18
18
  class Base
19
19
  class BotError < StandardError; end
20
+ class AbortError < BotError; end
20
21
 
21
22
  class EmptyMessageError < BotError
22
23
  def initialize
@@ -29,6 +30,17 @@ module Kybus
29
30
 
30
31
  attr_reader :provider, :executor, :pool_size, :pool, :definitions
31
32
 
33
+ DYNAMOID_FIELDS = {
34
+ 'channel_id' => :string,
35
+ 'user' => :string,
36
+ 'params' => :string,
37
+ 'metadata' => :string,
38
+ 'files' => :string,
39
+ 'cmd' => :string,
40
+ 'requested_param' => :string,
41
+ 'last_message' => :string
42
+ }.freeze
43
+
32
44
  def_delegators :executor, :state, :precommand_hook
33
45
  def_delegators :definitions, :registered_commands
34
46
 
@@ -38,19 +50,40 @@ module Kybus
38
50
  # See supported adapters
39
51
  # - name: The bot name
40
52
  # - repository: Configurations about the state storage
53
+
41
54
  def initialize(configs)
42
55
  build_pool(configs['pool_size'])
43
56
  @provider = Kybus::Bot::Adapter.from_config(configs['provider'])
44
57
  # TODO: move this to config
45
- repository = Kybus::Storage::Repository.from_config(
46
- nil,
47
- configs['state_repository'].merge('primary_key' => 'channel_id', 'table' => 'bot_sessions'),
48
- {}
49
- )
58
+ repository = make_repository(configs)
50
59
  @definitions = Kybus::Bot::CommandDefinition.new
51
60
  command_factory = CommandStateFactory.new(repository, @definitions)
52
- @executor = Kybus::Bot::CommandExecutor.new(self, command_factory, configs['inline_args'])
61
+ @executor = if configs['sidekiq']
62
+ require_relative 'sidekiq_command_executor'
63
+ Kybus::Bot::SidekiqCommandExecutor.new(self, command_factory, configs)
64
+ else
65
+ Kybus::Bot::CommandExecutor.new(self, command_factory, configs['inline_args'])
66
+ end
53
67
  register_command('default') { nil }
68
+ rescue_from(::Kybus::Bot::Base::AbortError) do
69
+ msg = params[:_last_exception]&.message
70
+ send_message(msg) if msg && msg != 'Kybus::Bot::Base::AbortError'
71
+ end
72
+ end
73
+
74
+ def make_repository(configs)
75
+ repository_config = configs['state_repository'].merge('primary_key' => 'channel_id', 'table' => 'bot_sessions')
76
+ repository_config.merge!('fields' => DYNAMOID_FIELDS) if repository_config['name'] == 'dynamoid'
77
+ Kybus::Storage::Repository.from_config(nil, repository_config, {})
78
+ end
79
+
80
+ def extend(*args)
81
+ DSLMethods.include(*args)
82
+ end
83
+
84
+ def self.helpers(mod = nil, &)
85
+ DSLMethods.include(mod) if mod
86
+ DSLMethods.class_eval(&) if block_given?
54
87
  end
55
88
 
56
89
  def build_pool(pool_size)
@@ -67,6 +100,11 @@ module Kybus
67
100
  @executor.dsl
68
101
  end
69
102
 
103
+ def handle_message(msg)
104
+ parsed = @provider.handle_message(msg)
105
+ @executor.process_message(parsed)
106
+ end
107
+
70
108
  # Starts the bot execution, this is a blocking call.
71
109
  def run
72
110
  # TODO: Implement an interface for killing the process
@@ -77,7 +115,7 @@ module Kybus
77
115
  end
78
116
 
79
117
  def redirect(command, *params)
80
- @executor.invoke(command, params)
118
+ @executor.redirect(command, params)
81
119
  end
82
120
 
83
121
  def send_message(contents, channel)
@@ -85,12 +123,18 @@ module Kybus
85
123
  provider.message_builder(@provider.send_message(contents, channel))
86
124
  end
87
125
 
88
- def register_command(klass, params = [], &block)
89
- definitions.register_command(klass, params, &block)
126
+ def register_command(klass, params = [], &)
127
+ definitions.register_command(klass, params, &)
90
128
  end
91
129
 
92
- def rescue_from(klass, &block)
93
- definitions.register_command(klass, [], &block)
130
+ def rescue_from(klass, &)
131
+ definitions.register_command(klass, [], &)
132
+ end
133
+
134
+ def method_missing(method, ...)
135
+ raise unless dsl.respond_to?(method)
136
+
137
+ dsl.send(method, ...)
94
138
  end
95
139
  end
96
140
  end
@@ -8,16 +8,16 @@ module Kybus
8
8
  end
9
9
 
10
10
  # Stores an operation definition
11
- def register_command(name, params, &block)
12
- @commands[name] = Command.new(name, params, &block)
11
+ def register_command(name, params, &)
12
+ @commands[name] = Command.new(name, params, &)
13
13
  end
14
14
 
15
15
  def registered_commands
16
16
  @commands.keys
17
17
  end
18
18
 
19
- def each(&block)
20
- @commands.each(&block)
19
+ def each(&)
20
+ @commands.each(&)
21
21
  end
22
22
 
23
23
  # Returns a command with the name
@@ -7,11 +7,27 @@ module Kybus
7
7
 
8
8
  def initialize(data, command)
9
9
  @command = command
10
- data[:params] = JSON.parse(data[:params] || '{}', symbolize_names: true)
11
- data[:files] = JSON.parse(data[:files] || '{}', symbolize_names: true)
10
+ data = JSON.parse(data, symbolize_names: true) if data.is_a?(String)
11
+ (data[:params] = JSON.parse(data[:params] || '{}', symbolize_names: true)) if data[:params].is_a?(String)
12
+ (data[:metadata] = JSON.parse(data[:metadata] || '{}', symbolize_names: true)) if data[:metadata].is_a?(String)
13
+ (data[:files] = JSON.parse(data[:files] || '{}', symbolize_names: true)) if data[:files].is_a?(String)
14
+ (data[:last_message] = data[:last_message] && SerializedMessage.from_json(data[:last_message]))
12
15
  @data = data
13
16
  end
14
17
 
18
+ def self.from_json(str, commands_provider)
19
+ data = JSON.parse(str, symbolize_names: true)
20
+ new(data[:data], commands_provider.command(data[:command]))
21
+ end
22
+
23
+ def to_json(*_args)
24
+ to_h.to_json
25
+ end
26
+
27
+ def to_h
28
+ { command: command.name, data: @data.to_h.merge(last_message: @data[:last_message]&.to_h) }
29
+ end
30
+
15
31
  def clear_command
16
32
  @data[:cmd] = nil
17
33
  end
@@ -25,6 +41,14 @@ module Kybus
25
41
  command.next_missing_param(params)
26
42
  end
27
43
 
44
+ def set_last_message(msg)
45
+ @data[:last_message] = msg
46
+ end
47
+
48
+ def last_message
49
+ @data[:last_message]
50
+ end
51
+
28
52
  def command=(cmd)
29
53
  @command = cmd
30
54
  @data[:cmd] = cmd.name
@@ -46,7 +70,7 @@ module Kybus
46
70
 
47
71
  def save_file(identifier, file)
48
72
  files[identifier] = file
49
- store_param("_#{@data[:requested_param]}_filename".to_sym, file.file_name)
73
+ store_param(:"_#{@data[:requested_param]}_filename", file.file_name)
50
74
  end
51
75
 
52
76
  def requested_param=(param)
@@ -61,13 +85,17 @@ module Kybus
61
85
  @data[:params][param] = value
62
86
  end
63
87
 
88
+ def metadata
89
+ @data[:metadata] || {}
90
+ end
91
+
64
92
  def save!
65
93
  backup = @data.clone
66
- %i[params files].each do |param|
94
+ %i[params files last_message metadata].each do |param|
67
95
  @data[param] = @data[param].to_json
68
96
  end
69
97
  @data.store
70
- %i[params files].each do |param|
98
+ %i[params files last_message metadata].each do |param|
71
99
  @data[param] = backup[param]
72
100
  end
73
101
  end
@@ -62,7 +62,7 @@ module Kybus
62
62
  data = factory.get(channel.to_s)
63
63
  CommandState.new(data, command(data[:cmd]))
64
64
  rescue Kybus::Storage::Exceptions::ObjectNotFound
65
- CommandState.new(factory.create(channel_id: channel.to_s, params: '{}'), nil)
65
+ CommandState.new(factory.create(channel_id: channel.to_s, params: '{}', metadata: '{}', last_message: nil), nil)
66
66
  end
67
67
  end
68
68
  end
@@ -9,7 +9,7 @@ module Kybus
9
9
  attr_reader :state
10
10
 
11
11
  def_delegator :state, :requested_param=, :next_param=
12
- def_delegators :state, :clear_command, :save!, :ready?, :next_missing_param
12
+ def_delegators :state, :clear_command, :save!, :ready?, :next_missing_param, :set_last_message
13
13
 
14
14
  def block
15
15
  state.command.block
@@ -55,7 +55,7 @@ module Kybus
55
55
 
56
56
  # stores the command into state
57
57
  def command=(cmd)
58
- log_debug('Message set as command', command: cmd)
58
+ log_debug('Message set as command', command: cmd.name)
59
59
  state.command = cmd
60
60
  end
61
61
  end
@@ -19,6 +19,10 @@ module Kybus
19
19
  execution_context&.state
20
20
  end
21
21
 
22
+ def last_message
23
+ state&.last_message
24
+ end
25
+
22
26
  def initialize(bot, channel_factory, inline_args)
23
27
  @bot = bot
24
28
  @channel_factory = channel_factory
@@ -46,7 +50,7 @@ module Kybus
46
50
  end
47
51
 
48
52
  def search_command_with_inline_arg(message)
49
- command, values = @channel_factory.command_with_inline_arg(message.raw_message)
53
+ command, values = @channel_factory.command_with_inline_arg(message.raw_message || '')
50
54
  if command
51
55
  execution_context.command = command
52
56
  values.each do |value|
@@ -59,6 +63,7 @@ module Kybus
59
63
  end
60
64
 
61
65
  def save_token!(message)
66
+ execution_context.set_last_message(message.serialize)
62
67
  if execution_context.expecting_command?
63
68
  command = @channel_factory.command(message.command)
64
69
  if @inline_args && !command
@@ -86,8 +91,8 @@ module Kybus
86
91
  end
87
92
  end
88
93
 
89
- def precommand_hook(&block)
90
- @precommand_hook = proc(&block)
94
+ def precommand_hook(&)
95
+ @precommand_hook = proc(&)
91
96
  end
92
97
 
93
98
  def fallback(error)
@@ -106,12 +111,7 @@ module Kybus
106
111
  retry
107
112
  end
108
113
 
109
- def invoke(command_name, args)
110
- command = @channel_factory.command(command_name)
111
- if command.nil? || command.params_size != args.size
112
- raise "Wrong redirect #{command_name}, #{bot.registered_commands}"
113
- end
114
-
114
+ def invoke(command, args)
115
115
  state.command = command
116
116
  command.params.zip(args).each do |param, value|
117
117
  state.store_param(param, value)
@@ -119,11 +119,19 @@ module Kybus
119
119
  run_command_or_prepare!
120
120
  end
121
121
 
122
+ def redirect(command_name, args)
123
+ command = @channel_factory.command(command_name)
124
+ if command.nil? || command.params_size != args.size
125
+ raise "Wrong redirect #{command_name}, #{bot.registered_commands}"
126
+ end
127
+
128
+ invoke(command, args)
129
+ end
130
+
122
131
  # Sends a message to get the next parameter from the user
123
132
  def ask_param(param, label = nil)
124
- provider = bot.provider
125
133
  msg = label || "I need you to tell me #{param}"
126
- bot.send_message(msg, provider.last_message.channel_id)
134
+ bot.send_message(msg, last_message.channel_id)
127
135
  execution_context.next_param = param
128
136
  end
129
137
  end
@@ -3,6 +3,8 @@
3
3
  module Kybus
4
4
  module Bot
5
5
  class DSLMethods
6
+ include Kybus::Logger
7
+
6
8
  attr_accessor :state
7
9
  attr_reader :provider
8
10
 
@@ -42,6 +44,10 @@ module Kybus
42
44
  state.files
43
45
  end
44
46
 
47
+ def metadata
48
+ state.metadata
49
+ end
50
+
45
51
  def file(name)
46
52
  (file = files[name]) && provider.file_builder(file)
47
53
  end
@@ -51,28 +57,32 @@ module Kybus
51
57
  end
52
58
 
53
59
  def current_user
54
- provider.last_message.user
60
+ last_message.user
55
61
  end
56
62
 
57
63
  def is_private?
58
- provider.last_message.is_private?
64
+ last_message.is_private?
59
65
  end
60
66
 
61
67
  def last_message
62
- provider.last_message
68
+ state.last_message
63
69
  end
64
70
 
65
71
  # returns the current_channel from where the message was sent
66
72
  def current_channel
67
- state.channel_id
73
+ last_message.channel_id
68
74
  end
69
75
 
70
76
  def command_name
71
77
  state&.command&.name
72
78
  end
73
79
 
74
- def method_missing(method, *args, &block)
75
- @bot.send(method, *args, &block)
80
+ def redirect(*args)
81
+ @bot.redirect(*args)
82
+ end
83
+
84
+ def abort(msg = nil)
85
+ raise ::Kybus::Bot::Base::AbortError, msg
76
86
  end
77
87
  end
78
88
  end
@@ -7,6 +7,7 @@ module Kybus
7
7
  module Bot
8
8
  # Base implementation for messages from distinct providers
9
9
  class Message
10
+ require_relative 'serialized_message'
10
11
  # Converts the messages into a hash
11
12
  # Returns true when the received message is a command. Convention states
12
13
  # that messages should start with '/' to be considered commands
@@ -15,7 +16,20 @@ module Kybus
15
16
  end
16
17
 
17
18
  def command
18
- raw_message&.split(' ')&.first
19
+ raw_message&.split&.first
20
+ end
21
+
22
+ def serialize
23
+ SerializedMessage.new({
24
+ provider: self.class.name,
25
+ channel_id:,
26
+ message_id:,
27
+ user:,
28
+ replied_message: reply? ? replied_message.serialize : nil,
29
+ raw_message:,
30
+ is_private?: is_private?,
31
+ attachment: has_attachment? ? attachment : nil
32
+ })
19
33
  end
20
34
  end
21
35
  end
@@ -1,21 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'sequel'
4
- require 'sequel/extensions/migration'
5
3
  module Kybus
6
4
  module Bot
7
5
  module Migrator
8
6
  class << self
9
- def run_migrations!(conn)
7
+ def run_migrations!(config)
8
+ case config['name']
9
+ when 'sequel'
10
+ run_sequel_migrations(config)
11
+ when 'dynamoid'
12
+ run_dynamoid_migrations(config)
13
+ else
14
+ raise "Provider not supported #{config['name']}"
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def run_sequel_migrations(config)
21
+ require 'sequel'
22
+ require 'sequel/extensions/migration'
23
+
24
+ conn = Sequel.connect(config['endpoint'])
10
25
  conn.create_table?(:bot_sessions) do
11
26
  String :channel_id
12
27
  String :user
13
28
  String :params, text: true
29
+ String :metadata, text: true
14
30
  String :files, text: true
15
31
  String :cmd
16
32
  String :requested_param
33
+ String :last_message, text: true
17
34
  end
18
35
  end
36
+
37
+ def run_dynamoid_migrations(config)
38
+ repository = Kybus::Storage::Datasource::DynamoidRepository.from_config(
39
+ 'name' => 'dynamoid',
40
+ 'dynamoid_config' => true,
41
+ 'access_key' => config['access_key'],
42
+ 'secret_key' => config['secret_key'],
43
+ 'region' => config['region'],
44
+ 'endpoint' => config['endpoint'],
45
+ 'namespace' => config['namespace'],
46
+ 'table' => 'bot_sessions',
47
+ 'primary_key' => 'channel_id',
48
+ 'fields' => Base::DYNAMOID_FIELDS,
49
+ 'read_capacity' => config['read_capacity'] || 1,
50
+ 'write_capacity' => config['write_capacity'] || 1
51
+ )
52
+
53
+ # Ensure the table is created
54
+ return if Dynamoid.adapter.list_tables.include?('bot_sessions')
55
+
56
+ repository.model_class.create_table(sync: true)
57
+ end
19
58
  end
20
59
  end
21
60
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Bot
5
+ # Base iplementation for messages from distinct providers
6
+ class SerializedMessage < Message
7
+ MANDATORY_FIELDS = %i[channel_id provider message_id user raw_message].freeze
8
+
9
+ def initialize(data)
10
+ data = data.to_h if data.is_a?(SerializedMessage)
11
+ raise 'BadSerializedMessage: nil message' if data.nil?
12
+
13
+ data = data[:data] if data.is_a?(Hash) && data.key?(:data)
14
+
15
+ missing_keys = MANDATORY_FIELDS.reject { |k| data.keys.include?(k) }
16
+ raise "BadSerializedMessage: Missing keys `#{missing_keys}', got: #{data}" unless missing_keys.empty?
17
+
18
+ @data = data.is_a?(String) ? JSON.parse(data, symbolize_names: true) : data
19
+ @data[:replied_message] = SerializedMessage.new(@data[:replied_message]) if @data[:replied_message]
20
+ end
21
+
22
+ def self.from_json(json)
23
+ data = json.is_a?(String) ? JSON.parse(json, symbolize_names: true) : json
24
+ new(data)
25
+ end
26
+
27
+ def reply?
28
+ @data[:replied_message].is_a?(SerializedMessage)
29
+ end
30
+
31
+ def has_attachment?
32
+ !@data[attachment].nil?
33
+ end
34
+
35
+ def method_missing(method, *_args)
36
+ @data[method]
37
+ end
38
+
39
+ def to_h
40
+ @data.dup
41
+ end
42
+
43
+ def to_json(*args)
44
+ @data.to_json(*args)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sidekiq'
4
+ require_relative 'sidekiq_dsl_methods'
5
+
6
+ module Kybus
7
+ module Bot
8
+ class SidekiqWorker
9
+ include Sidekiq::Worker
10
+ extend Kybus::DRY::ResourceInjector
11
+ include Kybus::Logger
12
+
13
+ def provider
14
+ SidekiqWorker.resource(:provider)
15
+ end
16
+
17
+ def factory
18
+ SidekiqWorker.resource(:factory)
19
+ end
20
+
21
+ def bot
22
+ SidekiqWorker.resource(:bot)
23
+ end
24
+
25
+ def build_context(details_json)
26
+ state = CommandState.from_json(details_json, factory)
27
+ log_debug('Loaded message into worker', state: state.to_h)
28
+ [SidekiqDSLMethods.new(provider, state, bot, factory), state]
29
+ end
30
+
31
+ def perform(details_json)
32
+ dsl, state = build_context(details_json)
33
+ dsl.instance_eval(&state.command.block)
34
+ rescue StandardError => e
35
+ log_error('Error in worker', error: e.class, msg: e.message, trace: e.backtrace)
36
+ end
37
+ end
38
+
39
+ class SidekiqCommandExecutor < CommandExecutor
40
+ def initialize(bot, channel_factory, configs)
41
+ super(bot, channel_factory, configs['inline_args'])
42
+ SidekiqWorker.register(:factory, @channel_factory)
43
+ SidekiqWorker.register(:provider, bot.provider)
44
+ SidekiqWorker.register(:bot, bot)
45
+ end
46
+
47
+ def run_command!
48
+ log_debug(msg: 'Enqueued process to sidekiq', state: state.to_h)
49
+ SidekiqWorker.perform_async(state.to_json)
50
+ nil
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kybus
4
+ module Bot
5
+ class SidekiqDSLMethods < DSLMethods
6
+ def initialize(provider, state, bot, factory)
7
+ super(provider, state, bot)
8
+ @factory = factory
9
+ end
10
+
11
+ def redirect(command_name, args = {})
12
+ command = @factory.command(command_name)
13
+ raise "Wrong redirect #{command_name}" if command.nil? || command.params_size != args.size
14
+
15
+ state.command = command
16
+ command.params.zip(args).each do |param, value|
17
+ state.store_param(param, value)
18
+ end
19
+
20
+ instance_eval(&state.command.block)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -31,7 +31,7 @@ module Kybus
31
31
  def receives(msg, attachments = nil)
32
32
  attachments = Adapter::DebugMessage::DebugFile.new(attachments) if attachments
33
33
  msg = Adapter::DebugMessage.new(msg, @default_channel_id, attachments)
34
- log_info('Received message', channel: @default_channel_id, msg:)
34
+ log_info('Received message', channel: @default_channel_id, msg: msg.raw_message)
35
35
  provider.last_message = msg
36
36
  executor.process_message(msg)
37
37
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Kybus
4
4
  module Bot
5
- VERSION = '0.8.2'
5
+ VERSION = '0.10.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kybus-bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gilberto Vargas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-18 00:00:00.000000000 Z
11
+ date: 2024-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kybus-core
@@ -24,188 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
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
27
  description: Provides a framework for building bots with ruby
210
28
  email:
211
29
  - tachoguitar@gmail.com
@@ -230,6 +48,9 @@ files:
230
48
  - lib/kybus/bot/dsl_methods.rb
231
49
  - lib/kybus/bot/message.rb
232
50
  - lib/kybus/bot/migrator.rb
51
+ - lib/kybus/bot/serialized_message.rb
52
+ - lib/kybus/bot/sidekiq_command_executor.rb
53
+ - lib/kybus/bot/sidekiq_dsl_methods.rb
233
54
  - lib/kybus/bot/test.rb
234
55
  - lib/kybus/bot/testing.rb
235
56
  - lib/kybus/bot/version.rb
@@ -253,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
253
74
  - !ruby/object:Gem::Version
254
75
  version: '0'
255
76
  requirements: []
256
- rubygems_version: 3.4.10
77
+ rubygems_version: 3.5.14
257
78
  signing_key:
258
79
  specification_version: 4
259
80
  summary: Provides a framework for building bots with ruby