1
0
зеркало из https://github.com/glebtv/telegram-bot.git synced 2026-08-28 15:26:18 +03:00

Сравнить коммиты

..

23 Коммитов

Автор SHA1 Сообщение Дата
Max Melentiev
036fbd20d5 v0.15.1 2020-12-20 08:25:35 +03:00
Max Melentiev
0590967694 Merge pull request #150 from telegram-bot-rb/rails-61
Add rails 6.1 support
2020-12-20 05:23:57 +00:00
Max Melentiev
4ab1f07bed Add rails 6.1 support 2020-12-11 21:00:15 +03:00
Max Melentiev
8cd948a544 v0.15.0 2020-12-06 18:37:47 +03:00
Max Melentiev
863e838533 Remove deprecated rspec/integration 2020-12-06 18:35:10 +03:00
Max Melentiev
2a2fafa536 Merge pull request #147 from telegram-bot-rb/update_api
Update to Bot API 5.0
2020-12-06 15:32:58 +00:00
Max Melentiev
4b9eedb5a2 Add server option for client to support local bot API servers 2020-11-27 18:47:47 +00:00
Max Melentiev
f654071c4c Drop Initializer module
It was required for Botan support.
2020-11-27 09:39:40 +00:00
Max Melentiev
e178f808b6 Update to Bot API 5.0, add rake tasks for deleteWebhook, close & logOut 2020-11-27 09:33:39 +00:00
Max Melentiev
d173443c56 Merge pull request #142 from telegram-bot-rb/path_with_hashed_token
Use token hash in routes instead of plain value, support :path option
2020-09-26 16:14:04 +01:00
Max Melentiev
97e45d94f2 Use token hash in routes instead of plain value, support :path option 2020-09-06 20:13:08 +01:00
Max Melentiev
03472e3d46 Merge pull request #141 from astrophena/updates-poller-typo
Fix a typo in updates poller
2020-07-25 15:29:59 +03:00
Ilya Mateyko
bc2b09a3b0 Fix a typo in updates poller 2020-07-23 01:09:13 +03:00
Max Melentiev
8494c7df0c v0.14.4 2020-04-15 20:40:55 +03:00
Max Melentiev
a2659e00c2 Bump rake version to resolve vulnerability notification 2020-04-04 22:36:41 +03:00
Max Melentiev
e43c611d9d Merge pull request #131 from telegram-bot-rb/update_api
Update to Bot API 4.7
2020-04-04 22:35:33 +03:00
Max Melentiev
b84c48bc1d Update to Bot API 4.7 2020-04-04 22:26:26 +03:00
Max Melentiev
43cd096a21 Fix spelling in Async docs 2020-01-25 15:49:28 +03:00
Maximilian Haack
903e14a3e5 Fix make_telegram_request examples in README (#129) 2020-01-25 13:34:48 +03:00
Max Melentiev
da31cd148a v0.14.3 2019-08-24 19:34:34 +03:00
Max Melentiev
552f3b3353 Update to Bot API 4.4 2019-08-24 19:33:46 +03:00
Max Melentiev
e5f93f608b Merge pull request #121 from telegram-bot-rb/rails_60
Require tests against rails 6.0, run tests against rails 6.1.alpha
2019-08-19 22:10:50 +03:00
Max Melentiev
7e823b4354 Require tests against rails 6.0, run tests against rails 6.1.alpha
Rails 6.0 requires ruby >= 2.5, so ruby 2.4 is not tested by default
and replaced with 2.6
2019-08-19 11:06:48 +03:00
33 изменённых файлов: 436 добавлений и 220 удалений

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

@@ -40,6 +40,7 @@ Style/IfUnlessModifier: {Enabled: false}
# Consistent to other definitions.
Style/EmptyMethod: {EnforcedStyle: expanded}
Style/Lambda: {EnforcedStyle: literal}
Style/ModuleFunction: {Enabled: false}
Style/NestedParenthesizedCalls: {Enabled: false}
Style/SignalException: {EnforcedStyle: only_raise}

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

@@ -1,27 +1,25 @@
language: ruby
cache: bundler
rvm:
- 2.6
- 2.5
- 2.4
gemfile:
- gemfiles/rails_61.gemfile
- gemfiles/rails_60.gemfile
- gemfiles/rails_52.gemfile
- gemfiles/rails_51.gemfile
matrix:
include:
# old rails
- rvm: 2.5
gemfile: gemfiles/rails_51.gemfile
- rvm: 2.5
gemfile: gemfiles/rails_50.gemfile
- rvm: 2.5
gemfile: gemfiles/rails_42.gemfile
# old rubies
- rvm: 2.4
gemfile: gemfiles/rails_52.gemfile
- rvm: 2.3
gemfile: gemfiles/rails_52.gemfile
# future rails
- rvm: 2.5
gemfile: gemfiles/rails_60.gemfile
# Allow rails 6 to fail while master branch is used
allow_failures:
- rvm: 2.5
gemfile: gemfiles/rails_60.gemfile
notifications:
email: false

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

@@ -1,26 +1,29 @@
appraise 'rails-61' do
gem 'actionpack', '~> 6.1.0'
gem 'railties', '~> 6.1.0'
end
appraise 'rails-60' do
git 'https://github.com/rails/rails.git' do
gem 'actionpack', '~> 6.0.0.alpha'
gem 'railties', '~> 6.0.0.alpha'
end
gem 'actionpack', '~> 6.0.0'
gem 'railties', '~> 6.0.0'
end
appraise 'rails-52' do
gem 'actionpack', '~> 5.2.0.beta2'
gem 'railties', '~> 5.2.0.beta2'
gem 'actionpack', '~> 5.2.0'
gem 'railties', '~> 5.2.0'
end
appraise 'rails-51' do
gem 'actionpack', '~> 5.1'
gem 'railties', '~> 5.1'
gem 'actionpack', '~> 5.1.0'
gem 'railties', '~> 5.1.0'
end
appraise 'rails-50' do
gem 'actionpack', '~> 5.0'
gem 'railties', '~> 5.0'
gem 'actionpack', '~> 5.0.0'
gem 'railties', '~> 5.0.0'
end
appraise 'rails-42' do
gem 'actionpack', '~> 4.2'
gem 'railties', '~> 4.2'
gem 'actionpack', '~> 4.2.0'
gem 'railties', '~> 4.2.0'
end

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

@@ -1,5 +1,26 @@
# Unreleased
# 0.15.1
- Rails 6.1 support.
# 0.15.0
- Add `:path` option to `telegram_webhook` route helper.
- __Breaking change!__ Default route is generated using hashed bot token.
Please reconfigure webhook after update (`rake telegram:bot:set_webhook`).
- Update to Bot API 5.0, add rake tasks for `deleteWebhook`, `close` & `logOut`.
- Add `server` option for client to support local bot API servers.
# 0.14.4
- Update to Bot API 4.7
# 0.14.3
- Rails 6.0 support.
- Update to Bot API 4.4.
# 0.14.2
- Add reply helpers: `answer_pre_checkout_query`, `answer_shipping_query`.

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

@@ -8,7 +8,7 @@ group :development do
gem 'pry-byebug', '~> 3.2.0'
gem 'sdoc', '~> 0.4.1'
gem 'telegram-bot-types', '~> 0.6.0'
gem 'telegram-bot-types', '~> 0.6.2'
gem 'rspec', '~> 3.5.0'
gem 'rspec-its', '~> 1.1.0'

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

@@ -4,8 +4,6 @@
[![Code Climate](https://codeclimate.com/github/telegram-bot-rb/telegram-bot/badges/gpa.svg)](https://codeclimate.com/github/telegram-bot-rb/telegram-bot)
[![Build Status](https://travis-ci.org/telegram-bot-rb/telegram-bot.svg)](https://travis-ci.org/telegram-bot-rb/telegram-bot)
__Breaking changes in v0.14!__ See [upgrade guide](https://github.com/telegram-bot-rb/telegram-bot/wiki/Upgrading-to-0.14).
Tools for developing Telegram bots. Best used with Rails, but can be used in
[standalone app](https://github.com/telegram-bot-rb/telegram-bot/wiki/Not-rails-application).
Supposed to be used in webhook-mode in production, and poller-mode
@@ -65,7 +63,11 @@ which is used for `Telegram.bot`.
```ruby
Telegram.bots_config = {
default: DEFAULT_BOT_TOKEN,
chat: {token: CHAT_BOT_TOKEN, username: 'chatbot'},
chat: {
token: CHAT_BOT_TOKEN,
username: 'ChatBot', # to support commands with mentions (/help@ChatBot)
server: 'http://local.bot.api.server', # for Local Bot API Server
},
}
Telegram.bot.get_updates
@@ -87,6 +89,7 @@ development:
bot:
token: TOKEN
username: SomeBot
server: http://local.bot.api.server
# For multiple bots in single app use hash of `internal_bot_id => settings`
bots:
@@ -366,7 +369,7 @@ end
### Routes in Rails app
There is `telegram_webhook` helper for rails app to define routes for webhooks.
It defines routes at `telegram/#{bot.token}` and connects bots with controller.
It defines routes at `telegram/#{hash_of(bot.token)}` and connects bots with controller.
```ruby
# Most off apps would require
@@ -475,10 +478,11 @@ RSpec.describe TelegramWebhooksController, telegram_bot: :rails do
# dispatch_command(cmd, *args)
# Available matchers can be found in Telegram::Bot::RSpec::ClientMatchers.
it 'shows usage of basic matchers'
# The most basic one is #make_telegram_request(bot, endpoint, params_matcher)
expect { dispatch_command(:start) }.
to make_telegram_request(bot, :sendMessage, hash_including(text: 'msg text'))
it 'shows usage of basic matchers' do
# The most basic one is #make_telegram_request(bot, action).
# It works similar to `receive` matcher and supports chaining `.with(args).exactly(n).times`.
expect { dispatch_command(:start) }.to make_telegram_request(bot, :sendMessage).
with(hash_including(text: 'msg text'))
# There are some shortcuts for dispatching basic updates and testing responses.
expect { dispatch_message('Hi') }.to send_telegram_message(bot, /msg regexp/, some: :option)

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

@@ -25,7 +25,7 @@ method_list = headers.
map { |g| g.reject { |x| x.match?(NOT_METHOD_REGEXP) } }.
reject(&:empty?)
api_version = doc.text.match(/^(Bot API ([\d\.]+))\.?$/)
api_version = doc.text.match(/^(?:Introducing )?(Bot API ([\d\.]+))\.?$/)
result = ['# Generated with bin/fetch-telegram-methods']
result << "# #{api_version[1]}" if api_version

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

@@ -2,15 +2,15 @@
source "https://rubygems.org"
gem "actionpack", "~> 4.2"
gem "railties", "~> 4.2"
gem "actionpack", "~> 4.2.0"
gem "railties", "~> 4.2.0"
group :development do
gem "appraisal", "~> 2.2"
gem "pry", "~> 0.10.1"
gem "pry-byebug", "~> 3.2.0"
gem "sdoc", "~> 0.4.1"
gem "telegram-bot-types", "~> 0.6.0"
gem "telegram-bot-types", "~> 0.6.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"

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

@@ -2,15 +2,15 @@
source "https://rubygems.org"
gem "actionpack", "~> 5.0"
gem "railties", "~> 5.0"
gem "actionpack", "~> 5.0.0"
gem "railties", "~> 5.0.0"
group :development do
gem "appraisal", "~> 2.2"
gem "pry", "~> 0.10.1"
gem "pry-byebug", "~> 3.2.0"
gem "sdoc", "~> 0.4.1"
gem "telegram-bot-types", "~> 0.6.0"
gem "telegram-bot-types", "~> 0.6.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"

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

@@ -2,15 +2,15 @@
source "https://rubygems.org"
gem "actionpack", "~> 5.1"
gem "railties", "~> 5.1"
gem "actionpack", "~> 5.1.0"
gem "railties", "~> 5.1.0"
group :development do
gem "appraisal", "~> 2.2"
gem "pry", "~> 0.10.1"
gem "pry-byebug", "~> 3.2.0"
gem "sdoc", "~> 0.4.1"
gem "telegram-bot-types", "~> 0.6.0"
gem "telegram-bot-types", "~> 0.6.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"

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

@@ -2,15 +2,15 @@
source "https://rubygems.org"
gem "actionpack", "~> 5.2.0.beta2"
gem "railties", "~> 5.2.0.beta2"
gem "actionpack", "~> 5.2.0"
gem "railties", "~> 5.2.0"
group :development do
gem "appraisal", "~> 2.2"
gem "pry", "~> 0.10.1"
gem "pry-byebug", "~> 3.2.0"
gem "sdoc", "~> 0.4.1"
gem "telegram-bot-types", "~> 0.6.0"
gem "telegram-bot-types", "~> 0.6.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"

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

@@ -2,17 +2,15 @@
source "https://rubygems.org"
git "https://github.com/rails/rails.git" do
gem "actionpack", "~> 6.0.0.alpha"
gem "railties", "~> 6.0.0.alpha"
end
gem "actionpack", "~> 6.0.0"
gem "railties", "~> 6.0.0"
group :development do
gem "appraisal", "~> 2.2"
gem "pry", "~> 0.10.1"
gem "pry-byebug", "~> 3.2.0"
gem "sdoc", "~> 0.4.1"
gem "telegram-bot-types", "~> 0.6.0"
gem "telegram-bot-types", "~> 0.6.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"

21
gemfiles/rails_61.gemfile Обычный файл
Просмотреть файл

@@ -0,0 +1,21 @@
# This file was generated by Appraisal
source "https://rubygems.org"
gem "actionpack", "~> 6.1.0"
gem "railties", "~> 6.1.0"
group :development do
gem "appraisal", "~> 2.2"
gem "pry", "~> 0.10.1"
gem "pry-byebug", "~> 3.2.0"
gem "sdoc", "~> 0.4.1"
gem "telegram-bot-types", "~> 0.6.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"
gem "rubocop", "~> 0.52.1"
gem "coveralls", "~> 0.8.2", require: false
end
gemspec path: "../"

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

@@ -14,15 +14,22 @@ namespace :telegram do
desc 'Set webhook urls for all bots'
task set_webhook: :environment do
routes = Rails.application.routes.url_helpers
cert_file = ENV['CERT']
cert = File.open(cert_file) if cert_file
Telegram.bots.each do |key, bot|
route_name = Telegram::Bot::RoutesHelper.route_name_for_bot(bot)
url = routes.send("#{route_name}_url")
puts "Setting webhook for #{key}..."
bot.async(false) { bot.set_webhook(url: url, certificate: cert) }
end
Telegram::Bot::Tasks.set_webhook
end
desc 'Delete webhooks for all or specific BOT'
task :delete_webhook do
Telegram::Bot::Tasks.delete_webhook
end
desc 'Perform logOut command for all or specific BOT'
task :log_out do
Telegram::Bot::Tasks.log_out
end
desc 'Perform `close` command for all or specific BOT'
task :close do
Telegram::Bot::Tasks.close
end
end
end

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

@@ -14,10 +14,10 @@ module Telegram
module_function
def deprecation_0_15
def deprecation_0_16
@deprecation ||= begin
require 'active_support/deprecation'
ActiveSupport::Deprecation.new('0.15', 'Telegram::Bot')
ActiveSupport::Deprecation.new('0.16', 'Telegram::Bot')
end
end
@@ -25,9 +25,9 @@ module Telegram
autoload :Client, 'telegram/bot/client'
autoload :ClientStub, 'telegram/bot/client_stub'
autoload :DebugClient, 'telegram/bot/debug_client'
autoload :Initializers, 'telegram/bot/initializers'
autoload :Middleware, 'telegram/bot/middleware'
autoload :RSpec, 'telegram/bot/rspec'
autoload :Tasks, 'telegram/bot/tasks'
autoload :UpdatesController, 'telegram/bot/updates_controller'
autoload :UpdatesPoller, 'telegram/bot/updates_poller'
end

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

@@ -16,8 +16,8 @@ module Telegram
# 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.
# Being in async mode `#request` enqueues job to perform
# http request instead of performing it immediately.
# Async behavior is controlled with `#async=` writer
# and can be enabled/disabled for the block with `#async`:
#
@@ -29,7 +29,7 @@ module Telegram
# while `#async(val, &block)` is thread-safe.
#
# It can be set with custom job class or classname. By default it defines
# job classes for every client class, inherited from ApplicationRecord, which
# job classes inherited from ApplicationJob, 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.

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

@@ -1,13 +1,14 @@
require 'active_support/core_ext/hash/keys'
require 'json'
require 'httpclient'
module Telegram
module Bot
class Client
URL_TEMPLATE = 'https://api.telegram.org/bot%<token>s/'.freeze
SERVER = 'https://api.telegram.org'.freeze
URL_TEMPLATE = '%<server>s/bot%<token>s/'.freeze
autoload :TypedResponse, 'telegram/bot/client/typed_response'
extend Initializers
prepend Async
include DebugClient
@@ -15,6 +16,16 @@ module Telegram
include ApiHelper
class << self
# Accepts different options to initialize bot.
def wrap(input, **options)
case input
when Symbol then by_id(input) or raise "#{name} #{input.inspect} not configured"
when self then input
when Hash then new(**input.symbolize_keys, **options)
else new(input, **options)
end
end
def by_id(id)
Telegram.bots[id]
end
@@ -51,11 +62,11 @@ module Telegram
attr_reader :client, :token, :username, :base_uri
def initialize(token = nil, username = nil, **options)
def initialize(token = nil, username = nil, server: SERVER, **options)
@client = HTTPClient.new
@token = token || options[:token]
@username = username || options[:username]
@base_uri = format(URL_TEMPLATE, token: self.token)
@base_uri = format(URL_TEMPLATE, server: server, token: self.token)
end
def request(action, body = {})

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

@@ -1,5 +1,5 @@
# Generated with bin/fetch-telegram-methods
# Bot API 4.3
# Bot API 5.0
getUpdates
setWebhook
@@ -7,8 +7,11 @@ deleteWebhook
getWebhookInfo
getMe
logOut
close
sendMessage
forwardMessage
copyMessage
sendPhoto
sendAudio
sendDocument
@@ -23,6 +26,7 @@ stopMessageLiveLocation
sendVenue
sendContact
sendPoll
sendDice
sendChatAction
getUserProfilePhotos
getFile
@@ -30,6 +34,8 @@ kickChatMember
unbanChatMember
restrictChatMember
promoteChatMember
setChatAdministratorCustomTitle
setChatPermissions
exportChatInviteLink
setChatPhoto
deleteChatPhoto
@@ -37,6 +43,7 @@ setChatTitle
setChatDescription
pinChatMessage
unpinChatMessage
unpinAllChatMessages
leaveChat
getChat
getChatAdministrators
@@ -45,6 +52,8 @@ getChatMember
setChatStickerSet
deleteChatStickerSet
answerCallbackQuery
setMyCommands
getMyCommands
editMessageText
editMessageCaption
@@ -60,6 +69,7 @@ createNewStickerSet
addStickerToSet
setStickerPositionInSet
deleteStickerFromSet
setStickerSetThumb
answerInlineQuery

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

@@ -1,21 +0,0 @@
require 'active_support/core_ext/hash/keys'
module Telegram
module Bot
module Initializers
# Accepts different options to initialize bot.
def wrap(input, **options)
case input
when Symbol then by_id(input) or raise "#{name} #{input.inspect} not configured"
when self then input
when Hash then new(**input.symbolize_keys, **options)
else new(input, **options)
end
end
def by_id(_id)
raise 'Not implemented'
end
end
end
end

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

@@ -1,3 +1,6 @@
require 'base64'
require 'openssl'
require 'telegram/bot'
require 'active_support/core_ext/array/wrap'
@@ -17,10 +20,8 @@ module Telegram
end || 'telegram_webhook'
end
# Replaces colon with underscore so rails don't treat it as
# route parameter.
def escape_token(token)
token && token.tr(':', '_')
def token_hash(token)
Base64.urlsafe_encode64(OpenSSL::Digest::SHA1.digest(token), padding: false)
end
end
@@ -34,14 +35,17 @@ module Telegram
#
# # pass additional options
# telegram_webhook TelegramController, :default, as: :custom_route_name
def telegram_webhook(controller, bot = :default, **options)
#
# # Default path is generated using hashed bot token. Override it using:
# telegram_webhook TelegramController, :default, path: 'top/secret'
def telegram_webhook(controller, bot = :default, path: nil, **options)
bot = Client.wrap(bot)
params = {
to: Middleware.new(bot, controller),
as: RoutesHelper.route_name_for_bot(bot),
format: false,
}.merge!(options)
post("telegram/#{RoutesHelper.escape_token bot.token}", params)
post(path || "telegram/#{bot.token && RoutesHelper.token_hash(bot.token)}", params)
UpdatesPoller.add(bot, controller) if Telegram.bot_poller_mode?
end
end

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

@@ -1,10 +0,0 @@
require 'telegram/bot'
Telegram::Bot.deprecation_0_15.warn(
"`require 'telegram/bot/rspec/integration'` is deprecated in favor of " \
"`require 'telegram/bot/rspec/integration/rails'`"
)
require 'telegram/bot/rspec/integration/rails'
Telegram::Bot::RSpec.with_include_context do |config|
config.include_context 'telegram/bot/integration/rails', telegram_bot: true
end

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

@@ -0,0 +1,63 @@
# frozen_string_literal: true
module Telegram
module Bot
module Tasks
extend self
def set_webhook
routes = Rails.application.routes.url_helpers
cert_file = ENV['CERT']
cert = File.open(cert_file) if cert_file
each_bot do |key, bot|
route_name = RoutesHelper.route_name_for_bot(bot)
url = routes.send("#{route_name}_url")
say("Setting webhook for #{key}...")
bot.set_webhook(
url: url,
certificate: cert,
ip_address: ENV['IP_ADDRESS'],
drop_pending_updates: drop_pending_updates,
)
end
end
def delete_webhook
each_bot do |key, bot|
say("Deleting webhook for #{key}...")
bot.delete_webhook(drop_pending_updates: drop_pending_updates)
end
end
def log_out
each_bot do |key, bot|
say("Logging out #{key}...")
bot.log_out
end
end
def close
each_bot do |key, bot|
say("Closing #{key}...")
bot.close
end
end
private
def say(text)
puts(text) unless Rails.env.test? # rubocop:disable Rails/Output
end
def each_bot(&block)
id = ENV['BOT'].try!(:to_sym)
bots = id ? {id => Client.by_id(id)} : Telegram.bots
bots.each { |key, bot| bot.async(false) { block[key, bot] } }
end
def drop_pending_updates
ENV['DROP_PENDING_UPDATES'].try!(:downcase) == 'true'
end
end
end
end

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

@@ -97,6 +97,8 @@ module Telegram
callback_query
shipping_query
pre_checkout_query
poll
poll_answer
].freeze
class << self
@@ -148,7 +150,7 @@ module Telegram
# Accessor to `'from'` field of payload. Can be overriden with `from` option
# for #initialize.
def from
@_from ||= payload && payload['from']
@_from ||= payload.is_a?(Hash) ? payload['from'] : payload.try(:from)
end
# Processes current update.
@@ -206,6 +208,10 @@ module Telegram
[payload_type, [payload['data']]]
end
def action_for_poll_answer
[payload_type, [payload['poll_id'], payload['option_ids']]]
end
# Silently ignore unsupported messages to not fail when user crafts
# an update with usupported command, callback query context, etc.
def action_missing(action, *_args)

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

@@ -49,7 +49,7 @@ module Telegram
private
# A hook invoked every time a before callback is halted.
def halted_callback_hook(filter)
def halted_callback_hook(filter, *)
Instrumentation.instrument(:halted_callback, filter: filter)
end

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

@@ -50,7 +50,7 @@ module Telegram
ensure
@running = false
end
log { 'Stoped polling bot updates.' }
log { 'Stopped polling bot updates.' }
end
def run

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

@@ -1,6 +1,6 @@
module Telegram
module Bot
VERSION = '0.14.2'.freeze
VERSION = '0.15.1'.freeze
def self.gem_version
Gem::Version.new VERSION

74
spec/integration/tasks_spec.rb Обычный файл
Просмотреть файл

@@ -0,0 +1,74 @@
# frozen_string_literal: true
require 'integration_helper'
RSpec.describe Telegram::Bot::Tasks, type: :request do
include Telegram::Bot::RSpec::ClientMatchers
def stub_env(new_values)
new_values.stringify_keys!
old_values = ENV.to_h.slice(new_values.keys)
new_values.each { |k, v| ENV[k] = v.to_s }
yield
ensure
new_values.each_key { |k| ENV[k] = old_values[k] }
end
shared_examples 'uses BOT envar' do |bot_matcher|
it 'runs for all bots by default' do
matcher = Telegram.bots.values.map { |x| instance_exec(x, &bot_matcher) }.reduce(&:and)
expect(subject).to matcher
end
context 'when BOT specified' do
around { |ex| stub_env(BOT: :other) { ex.run } }
it 'runs for this bot only' do
expect(subject).to instance_exec(Telegram.bots[:other], &bot_matcher)
end
end
end
describe '#log_out' do
subject { -> { described_class.log_out } }
include_examples 'uses BOT envar', ->(bot) { make_telegram_request(bot, :logOut) }
end
describe '#close' do
subject { -> { described_class.close } }
include_examples 'uses BOT envar', ->(bot) { make_telegram_request(bot, :close) }
end
describe '#set_webhook' do
subject { -> { described_class.set_webhook } }
include_examples 'uses BOT envar', ->(bot) { make_telegram_request(bot, :setWebhook) }
context 'with options in env' do
around do |ex|
stub_env(IP_ADDRESS: '1.2.3.4', DROP_PENDING_UPDATES: 'TrUe') do
ex.run
end
end
include_examples 'uses BOT envar', ->(bot) {
make_telegram_request(bot, :setWebhook).with(hash_including(
ip_address: '1.2.3.4',
drop_pending_updates: true,
))
}
end
end
describe '#delete_webhook' do
subject { -> { described_class.delete_webhook } }
include_examples 'uses BOT envar', ->(bot) { make_telegram_request(bot, :deleteWebhook) }
context 'with options in env' do
around { |ex| stub_env(DROP_PENDING_UPDATES: 'TrUe') { ex.run } }
include_examples 'uses BOT envar', ->(bot) {
make_telegram_request(bot, :deleteWebhook).with(hash_including(
drop_pending_updates: true,
))
}
end
end
end

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

@@ -14,6 +14,7 @@ class TestApplication < Rails::Application
config.eager_load = false
config.log_level = :debug
config.action_dispatch.show_exceptions = false
routes.default_url_options = {host: 'test.rpsec'}
telegram_config = {
bot: 'default_token',

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

@@ -1,65 +0,0 @@
RSpec.shared_examples 'initializers' do |config_method = :bots|
describe '.wrap' do
subject { described_class.wrap(input, **options) }
let(:options) { {} }
let(:result) { double(:result) }
let(:username) { 'username' }
context 'when input is a string' do
let(:input) { token }
it 'treats string as token' do
expect(described_class).to receive(:new).with(token, {}) { result }
should eq result
end
context 'and additional options are given' do
let(:options) { {id: :test} }
it 'passes them to initializer' do
expect(described_class).to receive(:new).with(input, **options) { result }
should eq result
end
end
end
context 'when input is a hash' do
let(:input) { {token: token, 'username' => username, other: :options} }
it 'passes it with symbolized keys' do
expect(described_class).to receive(:new).with(**input.symbolize_keys) { result }
should eq result
end
context 'and additional options are given' do
let(:options) { {id: :test} }
it 'passes them to initializer' do
expect(described_class).to receive(:new).
with(**input.symbolize_keys, **options) { result }
should eq result
end
end
end
context 'when input is an instance of described_class' do
let!(:input) { instance }
it 'returns input' do
expect(described_class).to_not receive(:new)
should eq input
end
end
context 'when input is a Symbol' do
let(:input) { :client_1 }
before { allow(Telegram).to receive(config_method) { {client_1: instance} } }
it { should eq Telegram.send(config_method)[:client_1] }
context 'and there is no such bot' do
let(:input) { :invalid }
it { expect { subject }.to raise_error(/not configured/) }
end
end
end
end

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

@@ -2,9 +2,72 @@ RSpec.describe Telegram::Bot::Client do
let(:instance) { described_class.new 'token' }
let(:token) { 'token' }
include_examples 'initializers'
it_behaves_like 'async', request_args: -> { [double(:action), {body: :content}] }
describe '.wrap' do
subject { described_class.wrap(input, **options) }
let(:options) { {} }
let(:result) { double(:result) }
let(:username) { 'username' }
context 'when input is a string' do
let(:input) { token }
it 'treats string as token' do
expect(described_class).to receive(:new).with(token, {}) { result }
should eq result
end
context 'and additional options are given' do
let(:options) { {id: :test} }
it 'passes them to initializer' do
expect(described_class).to receive(:new).with(input, **options) { result }
should eq result
end
end
end
context 'when input is a hash' do
let(:input) { {token: token, 'username' => username, other: :options} }
it 'passes it with symbolized keys' do
expect(described_class).to receive(:new).with(**input.symbolize_keys) { result }
should eq result
end
context 'and additional options are given' do
let(:options) { {id: :test} }
it 'passes them to initializer' do
expect(described_class).to receive(:new).
with(**input.symbolize_keys, **options) { result }
should eq result
end
end
end
context 'when input is an instance of described_class' do
let!(:input) { instance }
it 'returns input' do
expect(described_class).to_not receive(:new)
should eq input
end
end
context 'when input is a Symbol' do
let(:input) { :client_1 }
before { allow(Telegram).to receive(:bots) { {client_1: instance} } }
it { should eq Telegram.bots[:client_1] }
context 'and there is no such bot' do
let(:input) { :invalid }
it { expect { subject }.to raise_error(/not configured/) }
end
end
end
describe '.prepare_body' do
subject { described_class.prepare_body(input) }
@@ -32,26 +95,45 @@ RSpec.describe Telegram::Bot::Client do
describe '.new' do
subject { described_class.new(*args) }
let(:token) { 'secret' }
let(:username) { 'superbot' }
context 'when multiple args are given' do
let(:args) { %w[secret superbot] }
its(:token) { should eq args[0] }
its(:username) { should eq args[1] }
its(:base_uri) { should include args[0] }
let(:args) { [token, username] }
its(:token) { should eq token }
its(:username) { should eq username }
its(:base_uri) { should eq "#{described_class::SERVER}/bot#{token}/" }
end
context 'when hash is given' do
let(:args) { [token: 'secret', username: 'superbot'] }
its(:token) { should eq args[0][:token] }
its(:username) { should eq args[0][:username] }
its(:base_uri) { should include args[0][:token] }
its(:token) { should eq token }
its(:username) { should eq username }
its(:base_uri) { should eq "#{described_class::SERVER}/bot#{token}/" }
end
context 'with custom server' do
let(:server) { 'http://my.server' }
let(:args) { [token, username, server: server] }
its(:base_uri) { should eq "#{server}/bot#{token}/" }
context 'and hash options' do
let(:args) { [token: token, username: username, server: server] }
its(:base_uri) { should eq "#{server}/bot#{token}/" }
end
end
end
describe '#request' do
subject { -> { instance.request(action, request_body) } }
let(:action) { :some_action }
let(:url) { "#{format(described_class::URL_TEMPLATE, token: token)}#{action}" }
let(:url) do
base_uri = format(described_class::URL_TEMPLATE,
server: described_class::SERVER,
token: token,
)
"#{base_uri}#{action}"
end
let(:request_body) { double(:body) }
let(:prepared_body) { double(:prepared_body) }
let(:response) { HTTP::Message.new_response(body).tap { |x| x.status = status } }

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

@@ -41,57 +41,61 @@ RSpec.describe Telegram::Bot::RoutesHelper do
end
describe '#telegram_webhook' do
subject { mapper.telegram_webhook(*input) }
subject { ->(*args) { mapper.telegram_webhook(*args) } }
let(:mapper) { double(:mapper).tap { |x| x.extend described_class } }
let(:bots) { {default: bot, other: other_bot} }
let(:controller) { double(:controller, name: :controller) }
let(:other_controller) { double(:other_controller, name: :other_controller) }
before { allow(Telegram).to receive(:bots) { bots } }
def assert_routes(bot, controller, route_name, options) # rubocop:disable AbcSize
expected_path = options.delete(:path) || "telegram/#{bot.token}"
expect(mapper).to receive(:post) do |path, params|
expect(path).to eq expected_path
middleware = params[:to]
def assert_route(bot, controller, path: nil, **expected_options) # rubocop:disable AbcSize
path ||= "telegram/#{described_class.token_hash(bot.token)}"
expect(mapper).to receive(:post) do |actual_path, actual_options|
expect(actual_path).to eq(path)
middleware = actual_options[:to]
expect(middleware.controller).to eq(controller)
expect(middleware.bot.token).to eq(bot.token)
expect(middleware.bot.username).to eq(bot.username)
expect(params[:as]).to eq route_name
expect(params).to include(options) if options
expect(actual_options).to include(expected_options)
end
subject
yield
end
context 'when called with controller' do
let(:input) { [controller, option: :val] }
it 'creates routes for default bot and this controller' do
assert_routes bot, controller, 'default_telegram_webhook', option: :val
it 'creates routes for default bot' do
assert_route(bot, controller, as: 'default_telegram_webhook') do
subject[controller]
end
context 'and bot does not have configured token' do
let(:bot) { create_bot(nil) }
it 'creates routes for default bot and this controller' do
assert_routes bot, controller, 'default_telegram_webhook', option: :val
end
other_controller = double(:other_controller, name: :other_controller)
assert_route(bot, other_controller, as: 'custom_route') do
subject[other_controller, as: 'custom_route']
end
end
context 'and bot has colon in token' do
let(:bot) { create_bot('some:token') }
it 'replaces colon with underscore' do
assert_routes bot, controller, 'default_telegram_webhook',
option: :val,
path: 'telegram/some_token'
it 'passes extra options' do
assert_route(bot, controller, as: 'default_telegram_webhook', option: :val, other: 2) do
subject[controller, option: :val, other: 2]
end
end
it 'uses :path param to override default path' do
assert_route(bot, controller, as: 'default_telegram_webhook', path: 'custom/path') do
subject[controller, path: 'custom/path']
end
end
context 'when bot does not have configured token' do
let(:bot) { create_bot(nil) }
it 'creates route anyway' do
assert_route(bot, controller, as: 'default_telegram_webhook', path: 'telegram/') do
subject[controller]
end
end
end
context 'when called with controller and smth castable to bot' do
let(:input) { [controller, 'custom_token', option: :val] }
it 'creates routes for every created bot and controller' do
assert_routes create_bot('custom_token'), controller, 'telegram_webhook',
option: :val
assert_route(create_bot('custom_token'), controller, as: 'telegram_webhook') do
subject[controller, 'custom_token']
end
end
end
end

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

@@ -26,6 +26,12 @@ RSpec.describe Telegram::Bot::UpdatesController do
it { should eq [payload_type, payload.values_at(:data)] }
end
context 'when payload is poll_answer' do
let(:payload_type) { 'poll_answer' }
let(:payload) { stub_payload(:poll_id, :user, :option_ids) }
it { should eq [payload_type, payload.values_at(:poll_id, :option_ids)] }
end
context 'when payload is not supported' do
let(:payload_type) { '_unsupported_' }
it { should eq [:unsupported_payload_type, []] }
@@ -39,6 +45,7 @@ RSpec.describe Telegram::Bot::UpdatesController do
inline_query
chosen_inline_result
callback_query
poll_answer
]
(described_class::PAYLOAD_TYPES - custom_payload_types).each do |type|
context "when payload is #{type}" do

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

@@ -17,15 +17,12 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']
spec.post_install_message = 'Breaking changes in v0.14! ' \
'See upgrade guide at https://github.com/telegram-bot-rb/telegram-bot/wiki/Upgrading-to-0.14'
spec.required_ruby_version = '~> 2.0'
spec.add_dependency 'actionpack', '>= 4.0', '< 6.0'
spec.add_dependency 'activesupport', '>= 4.0', '< 6.0'
spec.add_dependency 'actionpack', '>= 4.0', '< 6.2'
spec.add_dependency 'activesupport', '>= 4.0', '< 6.2'
spec.add_dependency 'httpclient', '~> 2.7'
spec.add_development_dependency 'bundler', '> 1.16'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rake', '~> 13.0'
end