Shimogawa/rubirai

View on GitHub
lib/rubirai/messages/message_chain.rb

Summary

Maintainability
A
35 mins
Test Coverage
# frozen_string_literal: true

require 'rubirai/utils'
require 'rubirai/messages/message'

module Rubirai
  # Message chain
  #
  # @!attribute [r] bot
  #   @return [Bot] the bot object
  # @!attribute [r] id
  #   @return [Integer, nil] the message id, may be `nil`
  # @!attribute [r] raw
  #   @return [Hash{String => Object}, nil] the raw message chain, may be `nil`
  # @!attribute [r] send_time
  #   @return [Integer, nil] the send time of the message chain, may be `nil`
  # @!attribute [r] messages
  #   @return [Array<Message>] the raw message array
  class MessageChain
    include Enumerable

    attr_reader :bot, :id, :raw, :send_time, :messages

    # Makes a message chain from a list of messages
    #
    # @param messages [Array<Rubirai::Message, Rubirai::MessageChain, Hash, String, Object>] a list of messages
    # @param bot [Rubirai::Bot, nil]
    # @return [Rubirai::MessageChain] the message chain
    def self.make(*messages, bot: nil)
      chain = new(bot)
      result = []
      messages.map { |msg| Message.to_message(msg, bot) }.each do |msg|
        if !result.empty? && result[-1].is_a?(PlainMessage) && msg.is_a?(PlainMessage)
          result[-1] = PlainMessage.from(text: result[-1].text + msg.text, bot: bot)
        else
          result.append msg
        end
      end
      chain.extend(*result)
      chain
    end

    # Append messages to this message chain
    #
    # @param messages [Array<Rubirai::Message, Hash>] a list of messages
    # @return [Rubirai::MessageChain] self
    def extend(*messages)
      messages.each do |msg|
        internal_append msg
      end
      self
    end

    alias << extend
    alias append extend

    # Concats this message chain with another one
    #
    # @param msg_chain [MessageChain] another message chain
    # @return [MessageChain] self
    def concat!(msg_chain)
      msg_chain.messages.each do |msg|
        internal_append msg
      end
      self
    end

    def [](idx)
      @messages[idx]
    end

    def each(&block)
      @messages.each(&block)
    end

    def length
      @messages.length
    end

    def size
      @messages.size
    end

    def empty?
      @messages.empty?
    end

    # Don't use the constructor. Use {.make}.
    #
    # @private
    # @param bot [Rubirai::Bot, nil]
    # @param source [Array, nil]
    def initialize(bot = nil, source = nil)
      @bot = bot
      @messages = []
      @has_interpolation = false
      @interpolated_str = nil
      @ipl_objs_map = {}
      @raw = source
      return unless source
      raise(MiraiError, 'source is not array') unless source.is_a? Array
      raise(MiraiError, 'length is zero') if source.empty?

      if source[0]['type'] == 'Source'
        @id = source[0]['id']
        @send_time = source[0]['time']
        extend(*source.drop(1))
      else
        extend(*source)
      end
    end

    # Convert the message chain to an array of hashes.
    #
    # @return [Array<Hash{String => Object}>]
    def to_a
      @messages.map(&:to_h)
    end

    private

    def internal_append(msg)
      msg.must_be! [Message, MessageChain, Hash], RubiraiError, 'msg must be Message, MessageChain, or Hash'

      case msg
      when Message
        @messages.append msg
      when MessageChain
        @messages.append(*msg.messages)
      else
        @messages.append Message.build_from(msg, @bot)
      end

      self
    end
  end

  # Makes a message chain. See {MessageChain#make}.
  #
  # @return [MessageChain] the message chain made.
  # @see MessageChain#make
  def self.MessageChain(*messages, bot: nil)
    MessageChain.make(*messages, bot: bot)
  end
end

require 'rubirai/messages/interpolation'