require 'json' require 'httpclient' require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/hash/keys' module Telegram module Bot class Client URL_TEMPLATE = 'https://api.telegram.org/bot%s/'.freeze autoload :TypedResponse, 'telegram/bot/client/typed_response' extend Initializers prepend Async prepend Botan::ClientHelpers include DebugClient class << self def by_id(id) Telegram.bots[id] end # Prepend TypedResponse module. def typed_response! prepend TypedResponse end # Encodes nested hashes as json. def prepare_body(body) body = body.dup body.each do |k, val| body[k] = val.to_json if val.is_a?(Hash) || val.is_a?(Array) end end def prepare_async_args(action, body = {}) [action.to_s, Async.prepare_hash(prepare_body(body))] end end attr_reader :client, :token, :username, :base_uri def initialize(token = nil, username = nil, **options) @client = HTTPClient.new @token = token || options[:token] @username = username || options[:username] @base_uri = format URL_TEMPLATE, self.token end def request(action, body = {}) res = http_request("#{base_uri}#{action}", self.class.prepare_body(body)) status = res.status return JSON.parse(res.body) if 300 > status result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier err_msg = result && result['description'] || '-' if result # This errors are raised only for valid responses from Telegram case status when 403 then raise Forbidden, err_msg when 404 then raise NotFound, err_msg end end raise Error, "#{res.reason}: #{err_msg}" end # Splited to the sections similar to API docs. %w( deleteWebhook getUpdates getWebhookInfo setWebhook answerCallbackQuery forwardMessage getChat getChatAdministrators getChatMember getChatMembersCount getFile getMe getUserProfilePhotos kickChatMember leaveChat sendAudio sendChatAction sendContact sendDocument sendLocation sendMessage sendPhoto sendSticker sendVenue sendVideo sendVoice unbanChatMember editMessageCaption editMessageReplyMarkup editMessageText answerInlineQuery getGameHighScores sendGame setGameScore ).each do |method| define_method(method.underscore) { |*args| request(method, *args) } end # Endpoint for low-level request. For easy host highjacking & instrumentation. # Params are not used directly but kept for instrumentation purpose. # You probably don't want to use this method directly. def http_request(uri, body) client.post(uri, body) end def inspect "#<#{self.class.name}##{object_id}(#{@username})>" end end end end