kybus-bot 0.8.2 → 0.10.0

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 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