1
0
зеркало из https://github.com/glebtv/telegram-bot.git synced 2026-09-04 02:05:52 +03:00
Этот коммит содержится в:
Max Melentiev
2016-05-19 16:29:01 +03:00
родитель 85ff07de9b
Коммит 71f736e0a2
14 изменённых файлов: 421 добавлений и 15 удалений

138
lib/telegram/bot/async.rb Обычный файл
Просмотреть файл

@@ -0,0 +1,138 @@
module Telegram
module Bot
# Telegram & Botan clients can perform requests in async way with
# any job adapter (ActiveJob by default). Using Rails you don't need any
# additional configuration. However you may want to enable async requests
# by default with `async: true` in `secrets.yml`. Botan client doesn't inherit
# async setting from client and must be configured separately.
#
# telegram:
# bots:
# chat_async:
# token: secret
# async: true # enable async mode for client
# botan: botan_token # in this way botan will not be async
# botan: # in this way - it's in async mode
# token: botan_token
# async: true
#
# Without Rails To start using async requests
# initialize client with `id` kwarg and make sure the client is
# accessible via `Teletgram.bots[id]` in job worker. Or just use
# `Telegram.bots_config=` for configuration.
#
# Being in async mode `#request` enqueues job instead to perform
# http request instead of performing it directly.
# Async behavior is controlled with `#async=` writer
# and can be enabled/disabled for the block with `#async`:
#
# client = Telegram::Bot::Client.new(**config, async: true)
# client.send_message(message)
# client.async(false) { client.send_message(other_one) }
#
# It can be set with custom job class or classname. By default it defines
# job classes for every client class, inherited from ApplicationRecord, which
# can be accessed via `.default_async_job`. You can integrate it with any
# other job provider by defining a class with `.perform_later(bot_id, *args)`
# method. See Async::Job for implemetation.
module Async
module Job
class << self
def included(base)
base.singleton_class.send :attr_accessor, :client_class
end
end
def perform(client_id, *args)
client = self.class.client_class.wrap(client_id.to_sym)
client.async(false) { client.request(*args) }
end
end
module ClassMethods
def default_async_job
@default_async_job ||= begin
begin
ApplicationJob
rescue NameError
raise 'Define ApplicationJob class or setup #async= with custom job class'
end
klass = Class.new(ApplicationJob) { include Job }
klass.client_class = self
const_set(:AsyncJob, klass)
end
end
# This is used in specs.
def default_async_job=(val)
@default_async_job = val
remove_const(:AsyncJob) if const_defined?(:AsyncJob, false)
end
# Prepares argments for async job. ActiveJob doesn't support
# Symbol in argumens. Also we can encode json bodies only once here,
# so it would not be unnecessarily serialized-deserialized.
#
# This is stub method, which returns input. Every client class
# must prepare args itself.
def prepare_async_args(*args)
args
end
end
class << self
def prepended(base)
base.extend(ClassMethods)
end
# Transforms symbols to strings in hash values.
def prepare_hash(hash)
return hash unless hash.is_a?(Hash)
hash = hash.dup
hash.each { |key, val| hash[key] = val.to_s if val.is_a?(Symbol) }
end
end
attr_reader :id
def initialize(*, id: nil, async: nil, **options)
@id = id
self.async = async
super
end
# Sets `@async` to `self.class.default_async_job` if `true` is given
# or uses given value.
# Pass custom job class to perform async calls with.
def async=(val)
@async =
case val
when true then self.class.default_async_job
when String then const_get(val)
else val
end
end
# Returns value of `@async` if no block is given. Otherwise sets this value
# for a block.
def async(val = true)
return @async unless block_given?
begin
old_val = @async
self.async = val
yield
ensure
@async = old_val
end
end
# Uses job if #async is set.
def request(*args)
job_class = async
return super unless job_class
raise 'Can not enqueue job without client id' unless id
job_class.perform_later(id.to_s, *self.class.prepare_async_args(*args))
end
end
end
end

Просмотреть файл

@@ -8,12 +8,17 @@ module Telegram
class Error < Bot::Error; end
extend Initializers
prepend Async
include DebugClient
class << self
def by_id(id)
Telegram.botans[id]
end
def prepare_async_args(method, uri, query = {}, body = nil)
[method.to_s, uri.to_s, Async.prepare_hash(query), body]
end
end
attr_reader :client, :token
@@ -24,12 +29,11 @@ module Telegram
end
def track(event, uid, payload = {})
res = http_request(
:post,
TRACK_URI,
{token: token, name: event, uid: uid},
payload.to_json,
)
request(:post, TRACK_URI, {name: event, uid: uid}, payload.to_json)
end
def request(method, uri, query = {}, body = nil)
res = http_request(method, uri, query.merge(token: token), body)
status = res.status
return JSON.parse(res.body) if 300 > status
result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
@@ -40,6 +44,10 @@ module Telegram
def http_request(method, uri, query, body)
client.request(method, uri, query, body)
end
def inspect
"#<#{self.class.name}##{object_id}(#{@id})>"
end
end
end
end

Просмотреть файл

@@ -7,7 +7,7 @@ module Telegram
def initialize(*, botan: nil, **)
super
@botan = Botan.wrap(botan) if botan
@botan = Botan.wrap(botan, id: id) if botan
end
end
end

Просмотреть файл

@@ -10,6 +10,7 @@ module Telegram
autoload :TypedResponse, 'telegram/bot/client/typed_response'
extend Initializers
prepend Async
prepend Botan::ClientHelpers
include DebugClient
@@ -30,6 +31,10 @@ module Telegram
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

Просмотреть файл

@@ -26,7 +26,9 @@ module Telegram
# Hash of bots made with bots_config.
def bots
@bots ||= bots_config.transform_values(&Client.method(:wrap))
@bots ||= bots_config.each_with_object({}) do |(id, config), h|
h[id] = Client.wrap(config, id: id)
end
end
# Default bot.
@@ -44,12 +46,16 @@ module Telegram
#
# Can be overwritten with .bots_config=
def bots_config
return @bots_config if @bots_config
telegram_config = Rails.application.secrets[:telegram]
(telegram_config['bots'] || {}).symbolize_keys.tap do |config|
default = telegram_config['bot']
config[:default] = default if default
end
@bots_config ||=
if defined?(Rails)
telegram_config = Rails.application.secrets[:telegram] || {}
(telegram_config['bots'] || {}).symbolize_keys.tap do |config|
default = telegram_config['bot']
config[:default] = default if default
end
else
{}
end
end
# Resets all cached bots and their configs.

Просмотреть файл

@@ -64,7 +64,7 @@ module Telegram
end
def fetch_updates
response = bot.get_updates(offset: offset, timeout: timeout)
response = bot.async(false) { bot.get_updates(offset: offset, timeout: timeout) }
return unless response['ok'] && response['result'].any?
reload! do
response['result'].each do |update|