зеркало из
https://github.com/glebtv/telegram-bot.git
synced 2026-08-28 15:26:18 +03:00
* Update rubopcop [WIP] * Further rubocop fixes * Fix rubocop-rails cops * Fix spec * Disable rubocop suggestions * Run rubocop in github actions rather than in codeclimate
119 строки
3.2 KiB
Ruby
119 строки
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Telegram
|
|
module Bot
|
|
# Supposed to be used in development environments only.
|
|
class UpdatesPoller
|
|
class << self
|
|
@@instances = {} # rubocop:disable Style/ClassVars
|
|
|
|
def instances
|
|
@@instances
|
|
end
|
|
|
|
# Create, start and add poller instnace to tracked instances list.
|
|
def add(bot, controller)
|
|
new(bot, controller).tap { |x| instances[bot] = x }
|
|
end
|
|
|
|
def start(bot_id, controller = nil)
|
|
bot = bot_id.is_a?(Symbol) ? Telegram.bots[bot_id] : Client.wrap(bot_id)
|
|
instance = controller ? new(bot, controller) : instances[bot]
|
|
raise "Poller not found for #{bot_id.inspect}" unless instance
|
|
instance.start
|
|
end
|
|
end
|
|
|
|
DEFAULT_TIMEOUT = 5
|
|
|
|
attr_reader :bot, :controller, :timeout, :offset, :logger, :running, :reload
|
|
|
|
def initialize(bot, controller, **options)
|
|
@logger = options.fetch(:logger) { defined?(Rails.logger) && Rails.logger }
|
|
@bot = bot
|
|
@controller = controller
|
|
@timeout = options.fetch(:timeout) { DEFAULT_TIMEOUT }
|
|
@offset = options[:offset]
|
|
@reload = options.fetch(:reload) { defined?(Rails.env) && Rails.env.development? }
|
|
end
|
|
|
|
def log(&block)
|
|
logger&.info(&block)
|
|
end
|
|
|
|
def start
|
|
return if running
|
|
begin
|
|
@running = true
|
|
log { 'Started bot poller.' }
|
|
run
|
|
rescue Interrupt
|
|
nil # noop
|
|
ensure
|
|
@running = false
|
|
end
|
|
log { 'Stopped polling bot updates.' }
|
|
end
|
|
|
|
def run
|
|
while running
|
|
updates = fetch_updates
|
|
process_updates(updates) if updates&.any?
|
|
end
|
|
end
|
|
|
|
# Method to stop poller from other thread.
|
|
def stop
|
|
return unless running
|
|
log { 'Stopping polling bot updates.' }
|
|
@running = false
|
|
end
|
|
|
|
def fetch_updates(offset = self.offset)
|
|
response = bot.async(false) { bot.get_updates(offset: offset, timeout: timeout) }
|
|
response.is_a?(Array) ? response : response['result']
|
|
rescue Timeout::Error
|
|
log { 'Fetch timeout' }
|
|
nil
|
|
end
|
|
|
|
def process_updates(updates)
|
|
reload! do
|
|
updates.each do |update|
|
|
@offset = update['update_id'] + 1
|
|
process_update(update)
|
|
end
|
|
end
|
|
rescue StandardError => exception
|
|
logger&.error { ([exception.message] + exception.backtrace).join("\n") }
|
|
end
|
|
|
|
# Override this method to setup custom error collector.
|
|
def process_update(update)
|
|
controller.dispatch(bot, update)
|
|
end
|
|
|
|
def reload!
|
|
return yield unless reload
|
|
reloading_code do
|
|
if controller.is_a?(Class) && controller.name
|
|
@controller = Object.const_get(controller.name)
|
|
end
|
|
yield
|
|
end
|
|
end
|
|
|
|
if defined?(Rails.application) && Rails.application.respond_to?(:reloader)
|
|
def reloading_code(&block)
|
|
Rails.application.reloader.wrap(&block)
|
|
end
|
|
else
|
|
def reloading_code
|
|
ActionDispatch::Reloader.prepare!
|
|
yield.tap { ActionDispatch::Reloader.cleanup! }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|