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

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

...

24 Коммитов

Автор SHA1 Сообщение Дата
Max Melentiev
7d8a19d3c8 Pass action parameter to RequestBodyFormatter 2022-03-13 21:39:55 +00:00
Max Melentiev
380f6686fc Update to Bot API 5.7 2022-03-12 19:37:53 +00:00
Max Melentiev
ea64230eab Merge pull request #168 from telegram-bot-rb/nested_files
Fix sending File objects in nested objects (ex., in sendMediaGroup)
2022-03-12 19:34:57 +00:00
Max Melentiev
5b9706ed47 Fix sending File objects in nested objects in sendMediaGroup 2022-03-12 19:33:17 +00:00
Max Melentiev
2d73d1c879 Merge pull request #174 from telegram-bot-rb/rails-7
Rails 7.0 support
2022-01-04 22:54:17 +00:00
Max Melentiev
88af1152cf Rails 7.0 support 2022-01-04 22:49:53 +00:00
Max Melentiev
586217585a Merge pull request #164 from KonstantinReido/patch-1
Fix typo in README.md
2021-08-20 12:36:46 +01:00
Konstantin Reido
f83bf92601 Fix typo in README.md 2021-08-20 11:26:00 +03:00
Max Melentiev
3049ced903 Fix tests 2021-06-26 21:25:43 +01:00
Max Melentiev
07faf87ed7 Merge pull request #163 from telegram-bot-rb/update_api
Update to Bot API 5.3
2021-06-25 19:43:17 +01:00
Max Melentiev
ac45940937 Switch from travis-ci.org to travis-ci.com 2021-06-25 19:42:59 +01:00
Max Melentiev
a49ef51da9 Update to Bot API 5.3 2021-06-25 19:31:28 +01:00
Max Melentiev
4f669fccc4 Merge pull request #157 from AlexVPopov/table-of-contents
Add a table of contents to README
2021-03-31 20:56:59 +01:00
Alexander Popov
e06252907a Add a table of contents to README 2021-03-31 21:11:08 +03:00
Max Melentiev
af229c8bcc Fix typos in readme, make some points more clear 2021-02-19 09:38:54 +00:00
Max Melentiev
a7cf3754be v0.15.3 2021-02-15 13:54:10 +00:00
Max Melentiev
783d9c13e9 Merge pull request #156 from telegram-bot-rb/ruby-3
Fix translation helpers to support ruby 3
2021-02-15 13:52:09 +00:00
Max Melentiev
5417511f06 Fix translation helpers to support ruby 3 2021-02-15 09:44:19 +00:00
Max Melentiev
a0ca383bab v0.15.2 2021-01-26 08:37:02 +00:00
Max Melentiev
1c141f782e Merge pull request #153 from telegram-bot-rb/ruby-3.0
Ruby 3.0 support
2021-01-26 08:30:24 +00:00
Max Melentiev
791c4233ca Ruby 3.0 support, drop support for Ruby < 2.4 2021-01-09 11:35:13 +00:00
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
28 изменённых файлов: 397 добавлений и 166 удалений

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

@@ -1,13 +1,21 @@
os: linux
dist: xenial
language: ruby
cache: bundler
rvm:
- 2.6
- 2.5
gemfile:
- gemfiles/rails_61.gemfile
- gemfiles/rails_60.gemfile
- gemfiles/rails_52.gemfile
matrix:
jobs:
include:
# ruby 3.x
- rvm: 3.0
gemfile: gemfiles/rails_61.gemfile
- rvm: 3.0
gemfile: gemfiles/rails_60.gemfile
# old rails
- rvm: 2.5
gemfile: gemfiles/rails_51.gemfile
@@ -18,14 +26,5 @@ matrix:
# 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_61.gemfile
# Allow rails 6.1 to fail while master branch is used
allow_failures:
- rvm: 2.5
gemfile: gemfiles/rails_61.gemfile
notifications:
email: false

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

@@ -1,8 +1,11 @@
appraise 'rails-70' do
gem 'actionpack', '~> 7.0.0'
gem 'railties', '~> 7.0.0'
end
appraise 'rails-61' do
git 'https://github.com/rails/rails.git' do
gem 'actionpack', '~> 6.1.0.alpha'
gem 'railties', '~> 6.1.0.alpha'
end
gem 'actionpack', '~> 6.1.0'
gem 'railties', '~> 6.1.0'
end
appraise 'rails-60' do

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

@@ -1,5 +1,27 @@
# Unreleased
# 0.15.6
- Rails 7.0 support.
- Fix sending File objects in nested objects in sendMediaGroup.
- Update to Bot API 5.7
# 0.15.4
- Update to Bot API 5.3
# 0.15.3
- Ruby 3.0 support: fix translation helpers.
# 0.15.2
- Ruby 3.0 support. Drop support for Ruby < 2.4.
# 0.15.1
- Rails 6.1 support.
# 0.15.0
- Add `:path` option to `telegram_webhook` route helper.

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

@@ -4,17 +4,17 @@ gemspec
group :development do
gem 'appraisal', '~> 2.2'
gem 'pry', '~> 0.10.1'
gem 'pry-byebug', '~> 3.2.0'
gem 'sdoc', '~> 0.4.1'
gem 'pry', '~> 0.13.1'
gem 'pry-byebug', '~> 3.9.0'
gem 'sdoc', '~> 2.0.3'
gem 'telegram-bot-types', '~> 0.6.2'
gem 'telegram-bot-types', '~> 0.6.3'
gem 'rspec', '~> 3.5.0'
gem 'rspec-its', '~> 1.1.0'
gem 'rspec-rails', '~> 3.5.0'
gem 'rspec', '~> 3.10.0'
gem 'rspec-its', '~> 1.3.0'
gem 'rspec-rails', '~> 4.0.2'
gem 'rubocop', '~> 0.52.1'
gem 'coveralls', '~> 0.8.2', require: false
gem 'coveralls', '~> 0.8.23', require: false
end

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

@@ -2,7 +2,7 @@
[![Gem Version](https://badge.fury.io/rb/telegram-bot.svg)](http://badge.fury.io/rb/telegram-bot)
[![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)
[![Build Status](https://app.travis-ci.com/telegram-bot-rb/telegram-bot.svg)](https://app.travis-ci.com/github/telegram-bot-rb/telegram-bot)
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).
@@ -25,9 +25,33 @@ with session, keyboards and inline queries.
Run it on your local machine in 1 minute!
And here is [app template](https://github.com/telegram-bot-rb/rails_template)
to generate clean app in seconds.
to generate new rails app in seconds.
Examples and cookbook in [wiki](https://github.com/telegram-bot-rb/telegram-bot/wiki).
See examples and cookbook in [the wiki](https://github.com/telegram-bot-rb/telegram-bot/wiki).
## Table of Contents
* [Installation](#installation)
* [Usage](#usage)
* [Configuration](#configuration)
* [Configuration in Rails app](#configuration-in-rails-app)
* [Client](#client)
* [Typed responses](#typed-responses)
* [Controller](#controller)
* [Reply helpers](#reply-helpers)
* [Optional typecasting](#optional-typecasting)
* [Session](#session)
* [Message context](#message-context)
* [Callback queries](#callback-queries)
* [Routes in Rails app](#routes-in-rails-app)
* [Processing updates](#processing-updates)
* [Development & Debugging](#development--debugging)
* [Testing](#testing)
* [Deployment](#deployment)
* [Async mode](#async-mode)
* [Limitations](#limitations)
* [Development](#development)
* [Contributing](#contributing)
## Installation
@@ -39,11 +63,15 @@ gem 'telegram-bot'
And then execute:
$ bundle
```
bundle
```
Or install it yourself as:
$ gem install telegram-bot
```
gem install telegram-bot
```
Require if necessary:
@@ -131,9 +159,9 @@ bot.send_message(chat_id: chat_id, text: 'Test')
```
There is no magic, they just pass params as is and set `path_suffix`.
See [`Client`](https://github.com/telegram-bot-rb/telegram-bot/blob/master/lib/telegram/bot/client.rb)
class for list of available methods. Please open PR or issue if it misses methods from
new API versions.
See [list of available methods](https://github.com/telegram-bot-rb/telegram-bot/blob/master/lib/telegram/bot/client/api_methods.txt)
fetched from [API docs](https://core.telegram.org/bots/api).
Please open PR or issue if it misses methods from new API versions.
Any API request error will raise `Telegram::Bot::Error` with description in its message.
Special `Telegram::Bot::Forbidden` is raised when bot can't post messages to the chat anymore.
@@ -170,19 +198,18 @@ Bot controllers like usual rails controllers provides features like callbacks,
```ruby
class Telegram::WebhookController < Telegram::Bot::UpdatesController
# use callbacks like in any other controllers
# use callbacks like in any other controller
around_action :with_locale
# Every update can have one of: message, inline_query, chosen_inline_result,
# Every update has one of: message, inline_query, chosen_inline_result,
# callback_query, etc.
# Define method with same name to respond to this updates.
# Define method with the same name to handle this type of update.
def message(message)
# message can be also accessed via instance method
message == self.payload # true
# store_message(message['text'])
end
# This basic methods receives commonly used params:
# For the following types of updates commonly used params are passed as arguments,
# full payload object is available with `payload` instance method.
#
# message(payload)
# inline_query(query, offset)
@@ -193,14 +220,19 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
# Command arguments will be parsed and passed to the method.
# Be sure to use splat args and default values to not get errors when
# someone passed more or less arguments in the message.
def start!(data = nil, *)
# do_smth_with(data)
def start!(word = nil, *other_words)
# do_smth_with(word)
# full message object is also available via `payload` instance method:
# process_raw_message(payload['text'])
# There are `chat` & `from` shortcut methods.
# For callback queries `chat` if taken from `message` when it's available.
# For callback queries `chat` is taken from `message` when it's available.
response = from ? "Hello #{from['username']}!" : 'Hi there!'
# There is `respond_with` helper to set `chat_id` from received message:
respond_with :message, text: response
# `reply_with` also sets `reply_to_message_id`:
reply_with :photo, photo: File.open('party.jpg')
end
@@ -223,10 +255,10 @@ end
#### Reply helpers
There are helpers to respond for basic actions. They just set chat/message/query
identifiers from update. See
There are helpers for basic responses. They just set chat/message/query
identifiers from the update. See
[`ReplyHelpers`](https://github.com/telegram-bot-rb/telegram-bot/blob/master/lib/telegram/bot/updates_controller/reply_helpers.rb)
module for more information. Here are this methods signatures:
module for more information. Here are these methods signatures:
```ruby
def respond_with(type, params); end
@@ -389,7 +421,7 @@ telegram_webhook TelegramAuctionController, :auction
telegram_webhook TelegramController, as: :custom_telegram_webhook
```
#### Processesing updates
#### Processing updates
To process update with controller call `.dispatch(bot, update)` on it.
There are several options to run it automatically:
@@ -507,7 +539,7 @@ end
There is a context for testing bot controller in the way similar to Rails controller tests.
It's supposed to be a low-level alternative for integration tests. Among the differences is
that controller tests use a single controller instance for all dispatches in specific exaple,
that controller tests use a single controller instance for all dispatches in specific example,
session is stubbed (does not use configured store engine), and update is not serialized
so it also supports mocks. This can be useful for unit testing, but should not be used as
the default way to test the bot.

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

@@ -7,15 +7,15 @@ 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.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"
gem "pry", "~> 0.13.1"
gem "pry-byebug", "~> 3.9.0"
gem "sdoc", "~> 2.0.3"
gem "telegram-bot-types", "~> 0.6.3"
gem "rspec", "~> 3.10.0"
gem "rspec-its", "~> 1.3.0"
gem "rspec-rails", "~> 4.0.2"
gem "rubocop", "~> 0.52.1"
gem "coveralls", "~> 0.8.2", require: false
gem "coveralls", "~> 0.8.23", require: false
end
gemspec path: "../"

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

@@ -7,15 +7,15 @@ 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.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"
gem "pry", "~> 0.13.1"
gem "pry-byebug", "~> 3.9.0"
gem "sdoc", "~> 2.0.3"
gem "telegram-bot-types", "~> 0.6.3"
gem "rspec", "~> 3.10.0"
gem "rspec-its", "~> 1.3.0"
gem "rspec-rails", "~> 4.0.2"
gem "rubocop", "~> 0.52.1"
gem "coveralls", "~> 0.8.2", require: false
gem "coveralls", "~> 0.8.23", require: false
end
gemspec path: "../"

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

@@ -7,15 +7,15 @@ 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.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"
gem "pry", "~> 0.13.1"
gem "pry-byebug", "~> 3.9.0"
gem "sdoc", "~> 2.0.3"
gem "telegram-bot-types", "~> 0.6.3"
gem "rspec", "~> 3.10.0"
gem "rspec-its", "~> 1.3.0"
gem "rspec-rails", "~> 4.0.2"
gem "rubocop", "~> 0.52.1"
gem "coveralls", "~> 0.8.2", require: false
gem "coveralls", "~> 0.8.23", require: false
end
gemspec path: "../"

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

@@ -7,15 +7,15 @@ 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.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"
gem "pry", "~> 0.13.1"
gem "pry-byebug", "~> 3.9.0"
gem "sdoc", "~> 2.0.3"
gem "telegram-bot-types", "~> 0.6.3"
gem "rspec", "~> 3.10.0"
gem "rspec-its", "~> 1.3.0"
gem "rspec-rails", "~> 4.0.2"
gem "rubocop", "~> 0.52.1"
gem "coveralls", "~> 0.8.2", require: false
gem "coveralls", "~> 0.8.23", require: false
end
gemspec path: "../"

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

@@ -7,15 +7,15 @@ 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.2"
gem "rspec", "~> 3.5.0"
gem "rspec-its", "~> 1.1.0"
gem "rspec-rails", "~> 3.5.0"
gem "pry", "~> 0.13.1"
gem "pry-byebug", "~> 3.9.0"
gem "sdoc", "~> 2.0.3"
gem "telegram-bot-types", "~> 0.6.3"
gem "rspec", "~> 3.10.0"
gem "rspec-its", "~> 1.3.0"
gem "rspec-rails", "~> 4.0.2"
gem "rubocop", "~> 0.52.1"
gem "coveralls", "~> 0.8.2", require: false
gem "coveralls", "~> 0.8.23", require: false
end
gemspec path: "../"

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

@@ -2,22 +2,20 @@
source "https://rubygems.org"
git "https://github.com/rails/rails.git" do
gem "actionpack", "~> 6.1.0.alpha"
gem "railties", "~> 6.1.0.alpha"
end
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 "pry", "~> 0.13.1"
gem "pry-byebug", "~> 3.9.0"
gem "sdoc", "~> 2.0.3"
gem "telegram-bot-types", "~> 0.6.3"
gem "rspec", "~> 3.10.0"
gem "rspec-its", "~> 1.3.0"
gem "rspec-rails", "~> 4.0.2"
gem "rubocop", "~> 0.52.1"
gem "coveralls", "~> 0.8.2", require: false
gem "coveralls", "~> 0.8.23", require: false
end
gemspec path: "../"

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

@@ -0,0 +1,21 @@
# This file was generated by Appraisal
source "https://rubygems.org"
gem "actionpack", "~> 7.0.0"
gem "railties", "~> 7.0.0"
group :development do
gem "appraisal", "~> 2.2"
gem "pry", "~> 0.13.1"
gem "pry-byebug", "~> 3.9.0"
gem "sdoc", "~> 2.0.3"
gem "telegram-bot-types", "~> 0.6.3"
gem "rspec", "~> 3.10.0"
gem "rspec-its", "~> 1.3.0"
gem "rspec-rails", "~> 4.0.2"
gem "rubocop", "~> 0.52.1"
gem "coveralls", "~> 0.8.23", require: false
end
gemspec path: "../"

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

@@ -1,5 +1,4 @@
require 'active_support/core_ext/hash/keys'
require 'json'
require 'httpclient'
module Telegram
@@ -8,7 +7,9 @@ module Telegram
SERVER = 'https://api.telegram.org'.freeze
URL_TEMPLATE = '%<server>s/bot%<token>s/'.freeze
autoload :RequestBodyFormatter, 'telegram/bot/client/request_body_formatter'
autoload :TypedResponse, 'telegram/bot/client/typed_response'
prepend Async
include DebugClient
@@ -35,16 +36,8 @@ module Telegram
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))]
[action.to_s, Async.prepare_hash(RequestBodyFormatter.format(body, action))]
end
def error_for_response(response)
@@ -70,7 +63,7 @@ module Telegram
end
def request(action, body = {})
response = http_request("#{base_uri}#{action}", self.class.prepare_body(body))
response = http_request("#{base_uri}#{action}", RequestBodyFormatter.format(body, action))
raise self.class.error_for_response(response) if response.status >= 300
JSON.parse(response.body)
end

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

@@ -1,5 +1,5 @@
# Generated with bin/fetch-telegram-methods
# Bot API 5.0
# Bot API 5.7
getUpdates
setWebhook
@@ -30,13 +30,20 @@ sendDice
sendChatAction
getUserProfilePhotos
getFile
kickChatMember
banChatMember
unbanChatMember
restrictChatMember
promoteChatMember
setChatAdministratorCustomTitle
banChatSenderChat
unbanChatSenderChat
setChatPermissions
exportChatInviteLink
createChatInviteLink
editChatInviteLink
revokeChatInviteLink
approveChatJoinRequest
declineChatJoinRequest
setChatPhoto
deleteChatPhoto
setChatTitle
@@ -47,12 +54,13 @@ unpinAllChatMessages
leaveChat
getChat
getChatAdministrators
getChatMembersCount
getChatMemberCount
getChatMember
setChatStickerSet
deleteChatStickerSet
answerCallbackQuery
setMyCommands
deleteMyCommands
getMyCommands
editMessageText

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

@@ -0,0 +1,48 @@
require 'json'
module Telegram
module Bot
class Client
# Encodes nested hashes and arrays as json and extract File objects from them
# to the top level. Top-level File objects are handled by httpclient.
# More details: https://core.telegram.org/bots/api#sending-files
module RequestBodyFormatter
extend self
def format(body, action)
body = body.dup
if action.to_s == 'sendMediaGroup'
body = extract_files_from_array!(body, :media)
end
body.each do |key, val|
body[key] = val.to_json if val.is_a?(Hash) || val.is_a?(Array)
end
end
private
def extract_files_from_array!(body, field_name)
field_name = [field_name.to_sym, field_name.to_s].find { |x| body.key?(x) }
return body unless field_name && body[field_name].is_a?(Array)
files = {}
body[field_name] = body[field_name].map { |x| extract_files_from_hash(x, files) }
body.merge!(files)
end
# Replace File objects with `attach` URIs. File objects are added into `files` hash.
def extract_files_from_hash(hash, files)
return hash unless hash.is_a?(Hash)
hash.transform_values do |value|
if value.is_a?(File)
arg_name = "_file#{files.size}"
files[arg_name] = value
"attach://#{arg_name}"
else
value
end
end
end
end
end
end
end

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

@@ -5,11 +5,11 @@ module Telegram
attr_reader :requests
module StubbedConstructor
def new(*args)
def new(*args, **kwargs)
if self == ClientStub || !ClientStub.stub_all?
super
else
ClientStub.new(*args)
ClientStub.new(*args, **kwargs)
end
end
end

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

@@ -99,6 +99,8 @@ module Telegram
pre_checkout_query
poll
poll_answer
my_chat_member
chat_member
].freeze
class << self

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

@@ -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

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

@@ -12,15 +12,15 @@ module Telegram
module ClassMethods
# Class-level helper for lazy translations.
def translate(key, options = {})
def translate(key, **options)
key = "#{controller_path.tr('/', '.')}#{key}" if key.to_s.start_with?('.')
I18n.translate(key, options)
I18n.translate(key, **options)
end
alias :t :translate
end
# See toplevel description.
def translate(key, options = {})
def translate(key, **options)
if key.to_s.start_with?('.')
path = controller_path.tr('/', '.')
defaults = [:"#{path}#{key}"]
@@ -28,7 +28,7 @@ module Telegram
options[:default] = defaults.flatten
key = "#{path}.#{action_name_i18n_key}#{key}"
end
I18n.translate(key, options)
I18n.translate(key, **options)
end
alias :t :translate

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

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

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

@@ -1,4 +1,4 @@
RSpec.shared_examples 'async' do |request_args: -> {}|
RSpec.shared_examples 'async' do |request_args:|
let(:instance) { described_class.new(token: token, id: id, async: async) }
let(:id) { :default_bot }
let(:async) { true }
@@ -103,7 +103,7 @@ RSpec.shared_examples 'async' do |request_args: -> {}|
end
describe '#request' do
subject { ->(*args) { instance.request(*(args.empty? ? self.args : args)) } }
subject { ->(*args) { instance.request(*args) } }
let(:args, &request_args)
let(:result) { double(:result) }
@@ -112,7 +112,7 @@ RSpec.shared_examples 'async' do |request_args: -> {}|
expect(instance).to_not receive(:http_request)
expect(described_class).to receive(:prepare_async_args).with(*args) { args }
expect(instance.async).to receive(:perform_later).with(id.to_s, *args) { result }
expect(subject.call).to eq result
expect(subject.call(*args)).to eq result
end
end
@@ -131,12 +131,11 @@ RSpec.shared_examples 'async' do |request_args: -> {}|
context 'when async is disabled' do
let(:async) { false }
let(:result) { double(status: 200, body: '{"test":"ok"}') }
let(:args, &request_args)
it 'performs request immediately' do
expect(instance).to receive(:request).with(*args).and_call_original
expect(instance).to receive(:http_request) { result }
expect(subject[]).to eq 'test' => 'ok'
expect(subject.call(*args)).to eq 'test' => 'ok'
end
end
end

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

@@ -0,0 +1,62 @@
RSpec.describe Telegram::Bot::Client::RequestBodyFormatter do
describe '.format' do
subject { described_class.format(input, action) }
let(:action) { :sendMessage }
context 'when plain hash is given' do
let(:input) { {a: 1, b: '2', c: nil} }
it { should eq input }
end
context 'when nested hash is given' do
let(:input) { {a: 1, b: '2', c: [1, 2], d: {a: 1}, e: {b: []}} }
it 'encodes nested hashes to json' do
expected = input.dup
%i[c d e].each { |x| expected[x] = expected[x].to_json }
should eq expected
end
end
context 'with sendMediaGroup action' do
let(:action) { :sendMediaGroup }
let(:input) { {media: [{a: file_1}, {'b' => file_2, c: 123}, {d: 456}], x: 789} }
let(:file_1) { File.new(__FILE__) }
let(:file_2) { File.new(__FILE__) }
it 'extracts files to the top-level' do
should eq(
media: [
{a: 'attach://_file0'},
{b: 'attach://_file1', c: 123},
{d: 456},
].to_json,
x: 789,
'_file0' => file_1,
'_file1' => file_2,
)
end
context 'and input has string keys' do
let(:input) { super().stringify_keys }
it 'extracts files to the top-level' do
should eq(
'media' => [
{a: 'attach://_file0'},
{b: 'attach://_file1', c: 123},
{d: 456},
].to_json,
'x' => 789,
'_file0' => file_1,
'_file1' => file_2,
)
end
end
context 'without media' do
let(:input) { {a: 1, b: '2', c: nil} }
it { should eq input }
end
end
end
end

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

@@ -14,7 +14,10 @@ RSpec.describe Telegram::Bot::Client do
let(:input) { token }
it 'treats string as token' do
expect(described_class).to receive(:new).with(token, {}) { result }
expect(described_class).to receive(:new) do |*args, **kwargs|
expect([*args, kwargs]).to eq([token, {}]) # not using .with to support ruby 2.x and 3.x
result
end
should eq result
end
@@ -68,25 +71,6 @@ RSpec.describe Telegram::Bot::Client do
end
end
describe '.prepare_body' do
subject { described_class.prepare_body(input) }
context 'when plain hash is given' do
let(:input) { {a: 1, b: '2', c: nil} }
it { should eq input }
end
context 'when nested hash is given' do
let(:input) { {a: 1, b: '2', c: [1, 2], d: {a: 1}, e: {b: []}} }
it 'encodes nested hashes to json' do
expected = input.dup
%i[c d e].each { |x| expected[x] = expected[x].to_json }
should eq expected
end
end
end
describe '.prepare_async_args' do
subject { described_class.prepare_async_args(*input) }
let(:input) { [:action, a: 1, b: :sym, c: [:other], 'd' => 'str'] }
@@ -94,7 +78,9 @@ RSpec.describe Telegram::Bot::Client do
end
describe '.new' do
subject { described_class.new(*args) }
subject { described_class.new(*args, **kwargs) }
let(:args) { [] }
let(:kwargs) { {} }
let(:token) { 'secret' }
let(:username) { 'superbot' }
@@ -106,7 +92,7 @@ RSpec.describe Telegram::Bot::Client do
end
context 'when hash is given' do
let(:args) { [token: 'secret', username: 'superbot'] }
let(:kwargs) { {token: 'secret', username: 'superbot'} }
its(:token) { should eq token }
its(:username) { should eq username }
its(:base_uri) { should eq "#{described_class::SERVER}/bot#{token}/" }
@@ -114,11 +100,12 @@ RSpec.describe Telegram::Bot::Client do
context 'with custom server' do
let(:server) { 'http://my.server' }
let(:args) { [token, username, server: server] }
let(:args) { [token, username] }
let(:kwargs) { {server: server} }
its(:base_uri) { should eq "#{server}/bot#{token}/" }
context 'and hash options' do
let(:args) { [token: token, username: username, server: server] }
let(:kwargs) { {token: token, username: username, server: server} }
its(:base_uri) { should eq "#{server}/bot#{token}/" }
end
end
@@ -142,7 +129,8 @@ RSpec.describe Telegram::Bot::Client do
let(:body) { body_json.to_json }
let(:body_json) { {'param' => 'val', 'description' => 'some description'} }
before do
expect(described_class).to receive(:prepare_body).with(request_body) { prepared_body }
expect(Telegram::Bot::Client::RequestBodyFormatter).
to receive(:format).with(request_body, action) { prepared_body }
expect(instance).to receive(:http_request).with(url, prepared_body) { response }
end
around { |ex| Telegram::Bot::ClientStub.stub_all!(false) { ex.run } }

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

@@ -37,11 +37,13 @@ RSpec.describe Telegram::Bot::ClientStub do
end
describe '#new' do
subject { described_class.new(*args) }
subject { described_class.new(*args, **kwargs) }
let(:args) { [] }
let(:kwargs) { {} }
context 'when only username is given' do
let(:args) { 'superbot' }
its(:username) { should eq args }
let(:args) { ['superbot'] }
its(:username) { should eq args[0] }
end
context 'when username and token are given' do
@@ -51,9 +53,9 @@ RSpec.describe Telegram::Bot::ClientStub do
end
context 'when hash config is given' do
let(:args) { [token: 'token', username: 'superbot'] }
its(:token) { should eq args[0][:token] }
its(:username) { should eq args[0][:username] }
let(:kwargs) { {token: 'token', username: 'superbot'} }
its(:token) { should eq kwargs[:token] }
its(:username) { should eq kwargs[:username] }
end
end
end

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

@@ -41,7 +41,7 @@ RSpec.describe Telegram::Bot::RoutesHelper do
end
describe '#telegram_webhook' do
subject { ->(*args) { mapper.telegram_webhook(*args) } }
subject { ->(*args, **kwargs) { mapper.telegram_webhook(*args, **kwargs) } }
let(:mapper) { double(:mapper).tap { |x| x.extend described_class } }
let(:controller) { double(:controller, name: :controller) }
before { allow(Telegram).to receive(:bots) { bots } }

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

@@ -1,5 +1,26 @@
RSpec.describe Telegram::Bot::UpdatesController::Translation do
def self.stub_translations(translations)
around do |ex|
backend = I18n.backend
I18n.backend = I18n::Backend::Simple.new
begin
I18n.backend.store_translations 'en', telegram: {webhooks: translations}
ex.run
ensure
I18n.backend = backend
end
end
end
let(:result) { double(:result) }
describe '#translate' do
stub_translations(
start: {
hello: 'Hello from start!',
hi: 'Hi %{name} from start', # rubocop:disable Style/FormatStringToken
},
)
let(:controller) do
double(
controller_path: 'telegram/webhooks',
@@ -10,12 +31,28 @@ RSpec.describe Telegram::Bot::UpdatesController::Translation do
it 'uses action_name without ! for lazy translations' do
expect(I18n).to receive(:translate).with('telegram.webhooks.start.hello',
default: [:'telegram.webhooks.hello'],
)
controller.t('.hello')
) { result }
expect(controller.t('.hello')).to eq(result)
expect(I18n).to receive(:translate).with('telegram.webhooks.start.hi',
default: [:'telegram.webhooks.hi'],
name: 'Dude',
) { result }
expect(controller.t('.hi', name: 'Dude')).to eq(result)
end
it 'translates' do
expect(controller.t('.hello')).to eq('Hello from start!')
expect(controller.t('.hi', name: 'Dude')).to eq('Hi Dude from start')
end
end
describe described_class::ClassMethods do
stub_translations(
hello: 'Hello!',
hi: 'Hi %{name}', # rubocop:disable Style/FormatStringToken
)
describe '#translate' do
let(:controller_class) do
double(controller_path: 'telegram/webhooks').
@@ -23,8 +60,20 @@ RSpec.describe Telegram::Bot::UpdatesController::Translation do
end
it 'uses controller_path for lazy translations' do
expect(I18n).to receive(:translate).with('telegram.webhooks.hello', {})
controller_class.t('.hello')
expect(I18n).to receive(:translate) do |*args, **kwargs|
# not using .with to support ruby 2.x and 3.x
expect([*args, kwargs]).to eq(['telegram.webhooks.hello', {}])
result
end
expect(controller_class.t('.hello')).to eq(result)
expect(I18n).to receive(:translate).with('telegram.webhooks.hi', name: 'Dude') { result }
expect(controller_class.t('.hi', name: 'Dude')).to eq(result)
end
it 'translates' do
expect(controller_class.t('.hello')).to eq('Hello!')
expect(controller_class.t('.hi', name: 'Dude')).to eq('Hi Dude')
end
end
end

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

@@ -9,14 +9,19 @@ RSpec.describe Telegram::Bot::UpdatesController::TypedUpdate do
context 'when `update` is a virtus model' do
subject { controller }
unique_types = Telegram::Bot::UpdatesController::PAYLOAD_TYPES - %w[
unique_types = (Telegram::Bot::UpdatesController::PAYLOAD_TYPES - %w[
edited_message
channel_post
edited_channel_post
]
unique_types.each do |type|
my_chat_member
chat_member
]).
map { |x| [x, Telegram::Bot::Types.const_get(x.camelize)] }.to_h.
merge(
'chat_member' => Telegram::Bot::Types::ChatMemberUpdated
)
unique_types.each do |type, type_class|
context "with #{type}" do
type_class = Telegram::Bot::Types.const_get(type.camelize)
let(:payload_type) { type }
let(:payload) do
{}.tap do |result|

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

@@ -17,10 +17,10 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']
spec.required_ruby_version = '~> 2.0'
spec.required_ruby_version = '>= 2.4'
spec.add_dependency 'actionpack', '>= 4.0', '< 6.1'
spec.add_dependency 'activesupport', '>= 4.0', '< 6.1'
spec.add_dependency 'actionpack', '>= 4.0', '< 7.1'
spec.add_dependency 'activesupport', '>= 4.0', '< 7.1'
spec.add_dependency 'httpclient', '~> 2.7'
spec.add_development_dependency 'bundler', '> 1.16'