ssut/telegram-rb

View on GitHub
lib/telegram/api.rb

Summary

Maintainability
B
5 hrs
Test Coverage
module Telegram
  # Telegram API Implementation
  # 
  # @note You must avoid doing direct calls or initializes
  # @see Client
  # @version 0.1.0
  class API
    # Update user profile, contacts and chats
    #
    # @api private
    def update!(&cb)
      done = false
      EM.synchrony do
        multi = EM::Synchrony::Multi.new
        multi.add :profile, update_profile!
        multi.add :contacts, update_contacts!
        multi.add :chats, update_chats!
        multi.perform
        done = true
      end

      check_done = Proc.new {
        if done
          @starts_at = Time.now
          cb.call unless cb.nil?
          logger.info("Successfully loaded all information")
        else
          EM.next_tick(&check_done)
        end
      }
      EM.add_timer(0, &check_done)
    end

    # Update user profile
    #
    # @api private
    def update_profile!
      assert!
      callback = Callback.new
      @profile = nil
      @connection.communicate('get_self') do |success, data|
        if success
          callback.trigger(:success)
          contact = TelegramContact.pick_or_new(self, data)
          @contacts << contact unless self.contacts.include?(contact)
          @profile = contact
        else
          raise "Couldn't fetch the user profile."
        end
      end
      callback
    end

    # Update user contacts
    #
    # @api private
    def update_contacts!
      assert!
      callback = Callback.new
      @contacts = []
      @connection.communicate('contact_list') do |success, data|
        if success and data.class == Array
          callback.trigger(:success)
          data.each { |contact|
            contact = TelegramContact.pick_or_new(self, contact)
            @contacts << contact unless self.contacts.include?(contact)
          }
        else
          raise "Couldn't fetch the contact list."
        end
      end
      callback
    end

    # Update user chats
    #
    # @api private
    def update_chats!
      assert!
      callback = Callback.new
      
      collected = 0
      collect_done = Proc.new do |id, data, count|
        collected += 1
        @chats << TelegramChat.new(self, data)
        callback.trigger(:success) if collected == count
      end
      collect = Proc.new do |id, count|
        @connection.communicate(['chat_info', "chat\##{id}"]) do |success, data|
          collect_done.call(id, data, count) if success
        end
      end

      @chats = []
      @connection.communicate('dialog_list') do |success, data|
        if success and data.class == Array
          chatsize = data.count { |chat| chat['peer_type'] == 'chat' }
          data.each do |chat|
            if chat['peer_type'] == 'chat'
              collect.call(chat['peer_id'], chatsize)
            elsif chat['peer_type'] == 'user'
              @chats << TelegramChat.new(self, chat)
            end
          end
          callback.trigger(:success) if chatsize == 0
        else
          raise "Couldn't fetch the dialog(chat) list."
        end
      end
      callback
    end

    # Send a message to specific user or chat
    #
    # @param [String] target Target to send a message
    # @param [String] text Message content to be sent
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The data of the request
    # @since [0.1.0]
    # @example
    #   telegram.msg('user#1234567', 'hello!') do |success, data|
    #     puts success # => true
    #     puts data # => {"event": "message", "out": true, ...}
    #   end
    def msg(target, text, &callback)
      assert!
      @connection.communicate(['msg', target, text], &callback)
    end

    # Mark as read all received messages with specific user
    #
    # @param [String] target Target to mark read messages
    # @example
    #   telegram.mark_read('user#1234567')
    def mark_read(target)
      assert!
      @connection.communicate(['mark_read', target])
    end

    # Add a user to the chat group
    #
    # @param [String] chat Target chat group to add a user
    # @param [String] user User who would be added
    # @param [Block] callback Callback block that will be called when finished
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The raw data of the request
    # @since [0.1.0]
    # @example
    #   telegram.chat_add_user('chat#1234567', 'user#1234567') do |success, data|
    #     puts success # => true
    #     puts data # => {"event": "service", ...}
    #   end
    def chat_add_user(chat, user, &callback)
      assert!
      @connection.communicate(['chat_add_user', chat, user], &callback)
    end

    # Remove a user from the chat group
    # You can leave a group by this method (Set a user identifier to your identifier)
    #
    # @param [String] chat Target chat group to remove a user
    # @param [String] user User who would be removed from the chat
    # @param [Block] callback Callback block that will be called when finished
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The raw data of the request
    # @since [0.1.0]
    # @example
    #   telegram.chat_del_user('chat#1234567', 'user#1234567') do |success, data|
    #     puts success # => true
    #     puts data # => {"event": "service", ...}
    #   end
    def chat_del_user(chat, user, &callback)
      assert!
      @connection.communicate(['chat_del_user', chat, user], &callback)
    end

    # Send typing signal to the chat
    #
    # @param [String] chat Target chat group to send typing signal
    # @param [Block] callback Callback block that will be called when finished
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The raw data of the request
    # @since [0.1.1]
    # @example
    #   telegram.send_typing('chat#1234567') do |success, data|
    #     puts success # => true
    #     puts data # => {"result": "SUCCESS"}
    #   end
    def send_typing(chat, &callback)
      assert!
      @connection.communicate(['send_typing', chat], &callback)
    end

    # Send contact to peer chat
    #
    # @param [String] peer Target chat to which contact will be send
    # @param [String] contact phone number
    # @param [String] contact first name
    # @param [String] contact last name
    # @example
    #   telegram.send_contact('chat#1234567', '9329232332', 'Foo', 'Bar')
    def send_contact(peer, phone, first_name, last_name)
      assert!
      @connection.communicate(['send_contact', peer, phone, first_name, last_name])
    end

    # Abort sendign typing signal
    #
    # @param [String] chat Target chat group to stop sending typing signal
    # @param [Block] callback Callback block that will be called when finished
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The raw data of the request
    # @since [0.1.1]
    # @example
    #   telegram.send_typing_abort('chat#1234567') do |success, data|
    #     puts success # => true
    #     puts data # => {"result": "SUCCESS"}
    #   end
    def send_typing_abort(chat, &callback)
      assert!
      @connection.communicate(['send_typing_abort', chat], &callback)
    end

    # Send a photo to the chat
    #
    # @param [String] chat Target chat group to send a photo
    # @param [String] path The path of the image you want to send
    # @param [Block] callback Callback block that will be called when finished
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The raw data of the request
    # @since [0.1.1]
    # @example
    #   telegram.send_photo('chat#1234567') do |success, data|
    #     puts "there was a problem during the sending" unless success
    #     puts success # => true
    #     puts data # => {"event": "message", "media": {"type": "photo", ...}, ...}
    #   end
    def send_photo(chat, path, &callback)
      assert!
      @connection.communicate(['send_photo', chat, path], &callback)
    end

    # Send a video to the chat
    #
    # @param [String] chat Target chat group to send a video
    # @param [String] path The path of the video you want to send
    # @param [Block] callback Callback block that will be called when finished
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The raw data of the request
    # @since [0.1.1]
    # @example
    #   telegram.send_photo('chat#1234567') do |success, data|
    #     puts "there was a problem during the sending" unless success
    #     puts success # => true
    #     puts data # => {"event": "message", "media": {"type": "video", ...}, ...}
    #   end
    def send_video(chat, path, &callback)
      assert!
      @connection.communicate(['send_video', chat, path], &callback)
    end

    # Send a file to the chat
    #
    # @param [String] chat Target chat group to send a file
    # @param [String] path The path of the file you want to send
    # @param [Block] callback Callback block that will be called when finished
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The raw data of the request
    # @example
    #   telegram.send_file('chat#1234567', file_path) do |success, data|
    #     puts "there was a problem during the sending" unless success
    #     puts success # => true
    #     puts data # => {"event": "message", "media": {"type": "document", ...}, ...}
    #   end
    def send_file(chat, path, &callback)
      assert!
      @connection.communicate(['send_file', chat, path], &callback)
    end

    def create_group_chat(chat_topic, *users, &callback)
      assert!
      members = users.join(" ")
      @connection.communicate(['create_group_chat', chat_topic.escape, members], &callback)
    end

    def add_contact(phone_number, first_name, last_name, &callback)
      assert!
      @connection.communicate(['add_contact', phone_number, first_name.escape, last_name.escape], &callback)
    end
    # Closes the telegram CLI app (used in case of app shutdown to kill the child process)
    #
    def disconnect(&callback)
      assert!
      @connection.communicate(['quit'], &callback)
    end

    # Download an attachment from a message
    #
    # @param [type] type The type of an attachment (:photo, :video, :audio)
    # @param [String] seq Message sequence number
    # @param [Block] callback Callback block that will be called when finished
    # @yieldparam [Bool] success The result of the request (true or false)
    # @yieldparam [Hash] data The raw data of the request
    # @since [0.1.1]
    def download_attachment(type, seq, &callback)
      assert!
      raise "Type mismatch" unless %w(photo video audio).include?(type)
      @connection.communicate(["load_#{type.to_s}", seq], &callback)
    end

    protected
    # Check the availability of the telegram-cli daemon
    #
    # @since [0.1.0]
    # @api private
    def assert!
      raise "It appears that the connection to the telegram-cli is disconnected." unless connected?
    end
  end
end