lib/telegram/models.rb
module Telegram
# Base class for Telegram models
#
# @see TelegramChat
# @see TelegramRoom
# @since [0.1.0]
class TelegramBase
include Logging
# @return [Client] Root client instance
attr_reader :client
# @return [Integer] Identifier
attr_reader :id
# Return an instance if exists, or else create a new instance and return
#
# @param [Client] client Root client instance
# @param [Integer] raw Raw data on either of telegram objects
# @since [0.1.0]
def self.pick_or_new(client, raw)
# where to search for
where = if self == TelegramChat
client.chats
elsif self == TelegramContact
client.contacts
end
# pick a first item if exists, or else create
where.find { |obj| obj.id == raw['peer_id'] } or self.new(client, raw)
end
# Convert to telegram-cli target format from {TelegramChat} or {TelegramContact}
#
# @since [0.1.1]
def targetize
@type == 'encr_chat' ? @title : to_tg
end
# Execute a callback block with failure result
#
# @since [0.1.1]
# @api private
def fail_back(&callback)
callback.call(false, {}) unless callback.nil?
end
# Send typing signal
#
# @param [Block] callback Callback block that will be called when finished
# @since [0.1.1]
def send_typing(&callback)
if @type == 'encr_chat'
logger.warn("Currently telegram-cli has a bug with send_typing, then prevent this for safety")
return
end
@client.send_typing(targetize, &callback)
end
# Abort sending typing signal
#
# @param [Block] callback Callback block that will be called when finished
# @since [0.1.1]
def send_typing_abort(&callback)
if @type == 'encr_chat'
logger.warn("Currently telegram-cli has a bug with send_typing, then prevent this for safety")
return
end
@client.send_typing_abort(targetize, &callback)
end
# Send a message with given text
#
# @param [String] text text you want to send for
# @param [TelegramMessage] refer referrer of the method call
# @param [Block] callback Callback block that will be called when finished
# @since [0.1.0]
def send_message(text, refer, &callback)
@client.msg(targetize, text, &callback)
end
# @abstract Send a sticker
def send_sticker()
end
# Send an image
#
# @param [String] path The absoulte path of the image you want to send
# @param [TelegramMessage] refer referral of the method call
# @param [Block] callback Callback block that will be called when finished
# @since [0.1.1]
def send_image(path, refer, &callback)
if @type == 'encr_chat'
logger.warn("Currently telegram-cli has a bug with send_typing, then prevent this for safety")
return
end
fail_back(&callback) if not File.exist?(path)
@client.send_photo(targetize, path, &callback)
end
# Send an image with given url, not implemen
#
# @param [String] url The URL of the image you want to send
# @param [TelegramMessage] refer referral of the method call
# @param [Block] callback Callback block that will be called when finished
# @since [0.1.1]
def send_image_url(url, opt, refer, &callback)
begin
opt = {} if opt.nil?
http = EM::HttpRequest.new(url, :connect_timeout => 2, :inactivity_timeout => 5).get opt
file = Tempfile.new(['image', 'jpg'])
http.stream { |chunk|
file.write(chunk)
}
http.callback {
file.close
type = FastImage.type(file.path)
if %i(jpeg png gif).include?(type)
send_image(file.path, refer, &callback)
else
fail_back(&callback)
end
}
rescue Exception => e
logger.error("An error occurred during the image downloading: #{e.inspect} #{e.backtrace}")
fail_back(&callback)
end
end
# Send a video
#
# @param [String] path The absoulte path of the video you want to send
# @param [TelegramMessage] refer referral of the method call
# @param [Block] callback Callback block that will be called when finished
# @since [0.1.1]
def send_video(path, refer, &callback)
fail_back(&callback) if not File.exist?(path)
@client.send_video(targetize, path, &callback)
end
end
# Telegram Chat Model
#
# @see TelegramBase
# @since [0.1.0]
class TelegramChat < TelegramBase
# @return [String] The name of the chat
attr_reader :name
# @return [Array<TelegramContact>] The members of the chat
attr_reader :members
# @return [String] The type of the chat (chat, encr_chat, user and etc)
attr_reader :type
# Create a new chat instance
#
# @param [Client] client Root client instance
# @param [Integer] chat Raw chat data
# @since [0.1.0]
def initialize(client, chat)
@client = client
@chat = chat
@id = chat['peer_id']
@name = @title = chat.has_key?('title') ? chat['title'] : chat['print_name']
@type = chat['peer_type']
@members = []
if chat.has_key?('members')
chat['members'].each { |user|
@members << TelegramContact.pick_or_new(client, user)
}
elsif @type == 'user' and chat['user']
@members << TelegramContact.pick_or_new(client, chat)
end
end
# Leave from current chat
#
# @since [0.1.0]
def leave!
@client.chat_del_user(self, @client.profile.to_tg)
end
# @return [String] A chat identifier formatted with type
def to_tg
"#{@type}\##{@id}"
end
# Convert {Event} instance to the string format
#
# @return [String]
def to_s
"<TelegramChat #{@title}(#{@type}\##{@id}) members=#{@members.size}>"
end
end
# Telegram Contact Model
#
# @see TelegramBase
# @since [0.1.0]
class TelegramContact < TelegramBase
# @return [String] The name of the contact
attr_reader :name
# @return [String] The username of the contact
attr_reader :username
# @return [Array<TelegramContact>] The phone number of the contact
attr_reader :phone
# @return [String] The type of the contact # => "user"
attr_reader :type
# Create a new contact instance
#
# @param [Client] client Root client instance
# @param [Integer] contact Raw chat contact
# @since [0.1.0]
def initialize(client, contact)
@client = client
@contact = contact
@id = contact['peer_id']
@type = 'user'
@username = contact.has_key?('username') ? contact['username'] : ''
@name = contact['print_name']
@phone = contact.has_key?('phone') ? contact['phone'] : ''
@client.contacts << self unless @client.contacts.include?(self)
end
# @return [Array<TelegramChat>] Chats that contact participates
# @since [0.1.0]
def chats
@client.chats.select { |c| c.member.include?(self) }
end
# @return [String] A chat identifier formatted with type
def to_tg
"#{@type}\##{@id}"
end
# Convert {Event} instance to the string format
#
# @return [String]
def to_s
"<TelegramContact #{@name}(#{@id}) username=#{@username}>"
end
end
# Telegram Message Model
#
# @see Event
# @since [0.1.0]
class TelegramMessage
# @return [Client] Root client instance
attr_reader :client
# @return [String] Raw string of the text
attr_reader :raw
# @return [Integer] Message identifier
attr_reader :id
# @return [Time] Time message received
attr_reader :time
# @return [TelegramContact] The contact who sent this message
attr_reader :user
# @return [String]
attr_reader :raw_target
# @return [String] Content type
attr_reader :content_type
# @return [TelegramChat] if you were talking in a chat group
# @return [TelegramContact] if you were talking with contact
attr_reader :target
# Create a new tgmessage instance
#
# @param [Client] client Root client instance
# @param [Event] event Root event instance
# @see Event
# @since [0.1.0]
def initialize(client, event)
@event = event
@id = event.id
@raw = event.message.text
@time = event.time
@content_type = event.message.type
@raw_sender = event.message.raw_from
@raw_receiver = event.message.raw_to
@user = @sender = event.message.from
@receiver = event.message.to
@target =
begin
case @receiver.type
when 'user'
@sender
when 'chat', 'encr_chat'
@receiver
end
rescue NoMethodError
@sender
end
end
# @abstract Reply a message to the sender (peer to peer)
#
# @param [Symbol] type Type of the message (either of :text, :sticker, :image)
# @param [String] content Content to send a message
# @param [Block] callback Callback block that will be called when finished
def reply_user(type, content, &callback)
end
# Reply a message to the chat
#
# @param [Symbol] type Type of the message (either of :text, :sticker, :image)
# @param [String] content Content to send a message
# @param [TelegramChat] target Specify a TelegramChat to send
# @param [TelegramContact] target Specify a TelegramContact to send
# @param [Block] callback Callback block that will be called when finished
# @since [0.1.0]
def reply(type, content, target=nil, &callback)
target = @target if target.nil?
case type
when :text
target.send_message(content, self, &callback)
when :image
option = nil
content, option = content if content.class == Array
if content.include?('http')
target.method(:send_image_url).call(content, option, self, &callback)
else
target.method(:send_image).call(content, self, &callback)
end
when :video
target.send_video(content, self, &callback)
end
end
def members
contact_list = []
if @target.class == TelegramContact
contact_list << @target
else
contact_list = @target.members
end
contact_list
end
# Convert {TelegramMessage} instance to the string format
#
# @return [String]
def to_s
"<TelegramMessage id=#{@id} raw=#{@raw} time=#{@time} user=#{@user} target=#{@target} raw_target=#{@raw_target}>"
end
end
end