Module: Rubirai

Defined in:
lib/rubirai.rb,
lib/rubirai/auth.rb,
lib/rubirai/utils.rb,
lib/rubirai/errors.rb,
lib/rubirai/listing.rb,
lib/rubirai/message.rb,
lib/rubirai/retcode.rb,
lib/rubirai/session.rb,
lib/rubirai/version.rb,
lib/rubirai/listener.rb,
lib/rubirai/multipart.rb,
lib/rubirai/event_recv.rb,
lib/rubirai/event_resp.rb,
lib/rubirai/management.rb,
lib/rubirai/plugin_info.rb,
lib/rubirai/events/event.rb,
lib/rubirai/objects/user.rb,
lib/rubirai/objects/group.rb,
lib/rubirai/messages/message.rb,
lib/rubirai/events/bot_events.rb,
lib/rubirai/objects/group_info.rb,
lib/rubirai/events/message_events.rb,
lib/rubirai/events/request_events.rb,
lib/rubirai/events/rubirai_events.rb,
lib/rubirai/messages/interpolation.rb,
lib/rubirai/messages/message_chain.rb

Defined Under Namespace

Modules: FriendRequestOperation, GroupInviteRequestOperation, JoinGroupRequestOperation, Utils Classes: AppMessage, AtAllMessage, AtMessage, Bot, BotActiveLeaveEvent, BotActiveOfflineEvent, BotDroppedEvent, BotEvent, BotForcedOfflineEvent, BotGroupPermissionChangedEvent, BotInvitedToGroupEvent, BotJoinGroupEvent, BotKickedEvent, BotMutedEvent, BotOnlineEvent, BotReloginEvent, BotUnmutedEvent, Event, FaceMessage, FileMessage, FlashImageMessage, ForwardMessage, FriendMessageEvent, FriendRecallEvent, Group, GroupConfig, GroupFile, GroupFileSimple, GroupInfo, GroupMessageEvent, GroupRecallEvent, GroupUser, HttpResponseError, ImageMessage, JoinGroupRequestEvent, JsonMessage, MemberInfo, Message, MessageChain, MessageEvent, MiraiError, MusicShareMessage, NewFriendRequestEvent, PlainMessage, PokeMessage, QuoteMessage, RecallEvent, RequestEvent, RubiraiError, RubiraiErrorEvent, SourceMessage, TempMessageEvent, User, VoiceMessage, XmlMessage

Constant Summary collapse

RETURN_CODE =

The return codes and their explanations

{
  0 => 'OK',
  1 => 'Wrong auth key',
  2 => 'No such bot',
  3 => 'Session disappeared',
  4 => 'Session not verified',
  5 => 'No such receiver',
  6 => 'No such file',
  10 => 'No permission',
  20 => 'Bot muted',
  30 => 'Message too long',
  400 => 'Bad request'
}.freeze
VERSION =

Rubirai version

'0.2.0'
MIRAI_API_VERSION =

mirai-api-http version

'2.5.0'

Class Method Summary collapse

Class Method Details

.AppMessage(**kwargs) ⇒ Rubirai::AppMessage

Form an AppMessage.

Options Hash (**kwargs):

  • :content (String)

    the app body

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.AtAllMessageRubirai::AtAllMessage

Form an AtAllMessage.

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.AtMessage(**kwargs) ⇒ Rubirai::AtMessage

Form an AtMessage. The display option has no effect when sending at messages.

Options Hash (**kwargs):

  • :target (Integer)

    the target id

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.FaceMessage(**kwargs) ⇒ Rubirai::FaceMessage

Form a FaceMessage. Only needs to give one of the two arguments.

Options Hash (**kwargs):

  • :face_id (Integer)

    the face id (high priority)

  • :name (String)

    the face's name (low priority)

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.FlashImageMessage(**kwargs) ⇒ Rubirai::FlashImageMessage

Form a FlashImageMessage. Only needs to give one of the three arguments.

Options Hash (**kwargs):

  • :image_id (String)

    the image id

  • :url (String)

    the url of the image

  • :path (String)

    the local path of the image

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.ImageMessage(**kwargs) ⇒ Rubirai::ImageMessage

Form an ImageMessage. Only needs to give one of the three arguments.

Options Hash (**kwargs):

  • :image_id (String)

    the image id

  • :url (String)

    the url of the image

  • :path (String)

    the local path of the image

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.JsonMessage(**kwargs) ⇒ Rubirai::JsonMessage

Form a JsonMessage.

Options Hash (**kwargs):

  • :json (String)

    the json body

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.Message(obj, bot = nil) ⇒ Message

Objects to Message

Parameters:

  • obj (Message, Hash{String => Object}, Object)

    the object

Returns:

See Also:



207
208
209
# File 'lib/rubirai/messages/message.rb', line 207

def self.Message(obj, bot = nil)
  Message.to_message obj, bot
end

.MessageChain(*messages, bot: nil) ⇒ MessageChain

Makes a message chain. See Rubirai::MessageChain.make.

Returns:

See Also:



142
143
144
# File 'lib/rubirai/messages/message_chain.rb', line 142

def self.MessageChain(*messages, bot: nil)
  MessageChain.make(*messages, bot: bot)
end

.PlainMessage(**kwargs) ⇒ Rubirai::PlainMessage

Returns the message object.

Options Hash (**kwargs):

  • :text (String)

    the plain text

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.PokeMessage(**kwargs) ⇒ Rubirai::PokeMessage

Form a PokeMessage.

Options Hash (**kwargs):

  • :name (String)

    the poke name

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.QuoteMessage(**kwargs) ⇒ Rubirai::QuoteMessage

Form a QuoteMessage.

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.VoiceMessage(**kwargs) ⇒ Rubirai::VoiceMessage

Form a VoiceMessage. Only needs to give one of the three arguments.

Options Hash (**kwargs):

  • :voice_id (String)

    the voice id

  • :url (String)

    the url of the voice

  • :path (String)

    the local path of the voice

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end

.XmlMessage(**kwargs) ⇒ Rubirai::XmlMessage

Form a XmlMessage.

Options Hash (**kwargs):

  • :xml (String)

    the xml body

Returns:

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rubirai/messages/message.rb', line 70

module Rubirai
  # The message abstract class.
  #
  # @abstract
  class Message
    # @!attribute [r] bot
    #   @return [Bot] the bot
    # @!attribute [r] type
    #   @return [Symbol] the type
    attr_reader :bot, :type

    # Objects to {Rubirai::Message}
    #
    # @param msg [Rubirai::Message, Hash{String => Object}, Object] the object to transform to a message
    # @return [Rubirai::Message] the message
    def self.to_message(msg, bot = nil)
      # noinspection RubyYardReturnMatch
      case msg
      when Message, MessageChain
        msg
      when Hash
        Message.build_from(msg, bot)
      else
        PlainMessage.from(text: msg.to_s, bot: bot)
      end
    end

    # Get all message types (subclasses)
    #
    # @return [Array<Symbol>] all message types
    def self.all_types
      %i[
        Source Quote At AtAll Face Plain Image
        FlashImage Voice Xml Json App Poke Forward
        File MusicShare
      ]
    end

    # Check if a type is in all message types
    # @param type [Symbol] the type to check
    # @return whether the type is in all message types
    def self.check_type(type)
      raise(RubiraiError, 'type not in all message types') unless Message.all_types.include? type
    end

    # @private
    def self.get_msg_klass(type)
      Object.const_get "Rubirai::#{type}Message"
    end

    # @private
    def self.build_from(hash, bot = nil)
      hash = hash.stringify_keys
      raise(RubiraiError, 'not a valid message') unless hash.key? 'type'

      type = hash['type'].to_sym
      check_type type
      klass = get_msg_klass type
      klass.new hash, bot
    end

    # @private
    def self.set_message(type, *attr_keys, &initialize_block)
      attr_reader(*attr_keys)

      metaclass.instance_eval do
        define_method(:keys) do
          attr_keys
        end
        break if attr_keys.empty?
        define_method(:from) do |bot: nil, **kwargs|
          res = get_msg_klass(type).new({}, bot)
          attr_keys.each do |key|
            res.instance_variable_set "@#{key}", kwargs[key]
          end
          res
        end
        s = +"def from_with_param(#{attr_keys.join('= nil, ')} = nil)\n"
        s << "res = Rubirai::#{type}Message.new({})\n"
        attr_keys.each do |key|
          s << %[res.instance_variable_set("@#{key}", #{key})\n]
        end
        s << "res\nend"
        class_eval s
      end

      class_eval do
        define_method(:initialize) do |hash, bot = nil|
          # noinspection RubySuperCallWithoutSuperclassInspection
          super type, bot
          initialize_block&.call(hash)
          hash = hash.stringify_keys
          attr_keys.each do |k|
            instance_variable_set("@#{k}", hash[k.to_s.snake_to_camel(lower: true)])
          end
        end
      end
    end

    # @private
    def initialize(type, bot = nil)
      Message.check_type type
      @bot = bot
      @type = type
    end

    # Convert the message to a hash
    #
    # @return [Hash{String => Object}]
    def to_h
      res = self.class.keys.to_h do |k|
        v = instance_variable_get("@#{k}")
        k = k.to_s.snake_to_camel(lower: true)
        if v.is_a? MessageChain
          [k, v.to_a]
        elsif v&.respond_to?(:to_h)
          [k, v.to_h]
        else
          [k, v]
        end
      end
      res[:type] = @type.to_s
      res.compact.stringify_keys
    end

    # @private
    def self.metaclass
      class << self
        self
      end
    end
  end

  # {include:Rubirai::Message.to_message}
  # @param obj [Message, Hash{String => Object}, Object] the object
  # @return [Message] the message
  # @see Rubirai::Message.to_message
  def self.Message(obj, bot = nil)
    Message.to_message obj, bot
  end

  Message.all_types.each do |type|
    self.class.define_method("#{type}Message".to_sym) do |**kwargs|
      Message.get_msg_klass(type).from(**kwargs)
    end
  end

  # The source message type
  class SourceMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the message (chain) id
    # @!attribute [r] time
    #   @return [Integer] the timestamp
    # @!method from(**kwargs)
    #   @option kwargs [Integer] :id the id
    #   @option kwargs [Integer] :time the timestamp
    #   @!scope class
    set_message :Source, :id, :time
  end

  # The quote message type
  class QuoteMessage < Message
    # @!attribute [r] id
    #   @return [Integer] the original (quoted) message chain id
    # @!attribute [r] group_id
    #   @return [Integer] the group id. `0` if from friend.
    # @!attribute [r] sender_id
    #   @return [Integer] the original sender's id
    # @!attribute [r] target_id
    #   @return [Integer] the original receiver's (group or user) id
    # @!attribute [r] origin
    #   @return [MessageChain] the original message chain
    # @!method from(**kwargs)
    #   Form a {QuoteMessage}.
    set_message :Quote, :id, :group_id, :sender_id, :target_id, :origin, :origin_raw

    # @private
    def initialize(hash, bot = nil)
      super :Quote, bot
      @id = hash['id']
      @group_id = hash['groupId']
      @sender_id = hash['senderId']
      @target_id = hash['targetId']
      @origin = MessageChain.make(*hash['origin'], bot: bot)
      @origin_raw = hash['origin']
    end

    def to_h
      {
        'type' => 'Quote',
        'id' => @id,
        'groupId' => @group_id,
        'senderId' => @sender_id,
        'targetId' => @target_id,
        'origin' => @origin_raw || @origin.to_a
      }.compact
    end
  end

  # The At message type
  class AtMessage < Message
    # @!attribute [r] target
    #   @return [Integer] the target group user's id
    # @!attribute [r] display
    #   @return [String] the displayed name (not used when sending)
    # @!method from(**kwargs)
    #   Form an {AtMessage}. The `display` option has no effect when
    #   sending at messages.
    #   @option kwargs [Integer] :target the target id
    #   @return [AtMessage] the message object
    set_message :At, :target, :display
  end

  # The At All message type
  class AtAllMessage < Message
    # @!method from()
    #   @return [AtAllMessage] the message object
    set_message :AtAll
  end

  # The QQ Face Emoji message type
  class FaceMessage < Message
    # @!attribute [r] face_id
    #   @return [Integer] the face's id
    # @!attribute [r] name
    #   @return [String, nil] the face's name
    # @!method from(**kwargs)
    #   Form a {Rubirai::FaceMessage}. Only needs to give one of the two arguments.
    #   @option kwargs [Integer] :face_id the face id (high priority)
    #   @option kwargs [String] :name the face's name (low priority)
    #   @return [Rubirai::FaceMessage] the message object
    #   @!scope class
    set_message :Face, :face_id, :name
  end

  # The plain text message type
  class PlainMessage < Message
    # @!attribute [r] text
    #   @return [String] the text
    # @!method from(**kwargs)
    #   @option kwargs [String] :text the plain text
    #   @return [PlainMessage] the message object
    #   @!scope class
    set_message :Plain, :text
  end

  # The image message type.
  # Only one out of the three fields is needed to form the message.
  class ImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form an {Rubirai::ImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::ImageMessage] the message object
    #   @!scope class
    set_message :Image, :image_id, :url, :path
  end

  # The flash image message type
  class FlashImageMessage < Message
    # @!attribute [r] image_id
    #   @return [String, nil] the image id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the image
    # @!attribute [r] path
    #   @return [String, nil] the local path of the image
    # @!method from(**kwargs)
    #   Form a {Rubirai::FlashImageMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :image_id the image id
    #   @option kwargs [String] :url the url of the image
    #   @option kwargs [String] :path the local path of the image
    #   @return [Rubirai::FlashImageMessage] the message object
    #   @!scope class
    set_message :FlashImage, :image_id, :url, :path
  end

  # The voice message type
  class VoiceMessage < Message
    # @!attribute [r] voice_id
    #   @return [String, nil] the voice id from mirai
    # @!attribute [r] url
    #   @return [String, nil] the url of the voice
    # @!attribute [r] path
    #   @return [String, nil] the local path of the voice
    # @!method from(**kwargs)
    #   Form a {Rubirai::VoiceMessage}. Only needs to give one of the three arguments.
    #   @option kwargs [String] :voice_id the voice id
    #   @option kwargs [String] :url the url of the voice
    #   @option kwargs [String] :path the local path of the voice
    #   @return [Rubirai::VoiceMessage] the message object
    #   @!scope class
    set_message :Voice, :voice_id, :url, :path
  end

  # The xml message type
  class XmlMessage < Message
    # @!attribute [r] xml
    #   @return [String] the xml content
    # @!method from(**kwargs)
    #   Form a {Rubirai::XmlMessage}.
    #   @option kwargs [String] :xml the xml body
    #   @return [Rubirai::XmlMessage] the message object
    #   @!scope class
    set_message :Xml, :xml
  end

  # The json message type
  class JsonMessage < Message
    # @!attribute [r] json
    #   @return [String] the json content
    # @!method from(**kwargs)
    #   Form a {Rubirai::JsonMessage}.
    #   @option kwargs [String] :json the json body
    #   @return [Rubirai::JsonMessage] the message object
    #   @!scope class
    set_message :Json, :json
  end

  # The app message type
  class AppMessage < Message
    # @!attribute [r] content
    #   @return [String] the app content
    # @!method from(**kwargs)
    #   Form an {Rubirai::AppMessage}.
    #   @option kwargs [String] :content the app body
    #   @return [Rubirai::AppMessage] the message object
    #   @!scope class
    set_message :App, :content
  end

  # The poke message type
  class PokeMessage < Message
    # @!attribute [r] name
    #   @return [String] type (name) of the poke
    # @!method from(**kwargs)
    #   Form an {Rubirai::PokeMessage}.
    #   @option kwargs [String] :name the name (type) of poke
    #   @return [Rubirai::PokeMessage] the message object
    #   @!scope class
    set_message :Poke, :name
  end

  # The forward message type
  class ForwardMessage < Message
    # A message node in the forward message list
    #
    # @!attribute [r] sender_id
    #   @return [Integer] sender id
    # @!attribute [r] time
    #   @return [Integer] send timestamp (second)
    # @!attribute [r] sender_name
    #   @return [String] the sender name
    # @!attribute [r] message_chain
    #   @return [MessageChain] the message chain
    class Node
      attr_reader :sender_id, :time, :sender_name, :message_chain

      # @private
      def initialize(hash, bot = nil)
        return unless hash
        @sender_id = hash['senderId']
        @time = hash['time']
        @sender_name = hash['senderName']
        @message_chain = MessageChain.make(*hash['messageChain'], bot: bot)
      end

      def self.from(**kwargs)
        n = new({})
        %i[sender_id time sender_name message_chain].each do |attr|
          n.instance_variable_set("@#{attr}", kwargs[attr])
        end
      end
    end

    # @!attribute [r] title
    #   @return [String] the title
    # @!attribute [r] brief
    #   @return [String] the brief text
    # @!attribute [r] source
    #   @return [String] the source text
    # @!attribute [r] summary
    #   @return [String] the summary text
    # @!attribute [r] node_list
    #   @return [Array<Node>] the node list
    #   @see Node
    set_message :Forward, :title, :brief, :source, :summary, :node_list

    # @private
    def initialize(hash, bot = nil)
      super :Forward, bot
      @title = hash['title']
      @brief = hash['brief']
      @source = hash['source']
      @summary = hash['summary']
      @node_list = hash['nodeList'].each do |chain|
        Node.new chain, bot
      end
    end
  end

  # The file message type
  class FileMessage < Message
    # @!attribute [r] id
    #   @return [String] the file id
    # @!attribute [r] internal_id
    #   @return [Integer] the internal id needed by server
    # @!attribute [r] name
    #   @return [String] the filename
    # @!attribute [r] size
    #   @return [Integer] the file size
    set_message :File, :id, :internal_id, :name, :size
  end

  # The music share card message
  class MusicShareMessage < Message
    # List all kinds of music providers
    #
    # @return [Array<String>] kinds
    def self.all_kinds
      %w[NeteaseCloudMusic QQMusic MiguMusic]
    end

    # @!attribute [r] kind
    #   @return [String] the kind of music provider
    # @!attribute [r] title
    #   @return [String] the music card title
    # @!attribute [r] summary
    #   @return [String] the music card summary
    # @!attribute [r] jump_url
    #   @return [String] the jump url
    # @!attribute [r] picture_url
    #   @return [String] the picture's url
    # @!attribute [r] music_url
    #   @return [String] the music's url
    # @!attribute [r] brief
    #   @return [String, nil] the brief message (optional)
    set_message :MusicShare, :kind, :title, :summary, :jump_url, :picture_url, :music_url, :brief do |hash|
      raise(RubiraiError, 'non valid music type') unless all_kinds.include? hash['kind']
    end
  end
end