зеркало из
https://github.com/glebtv/telegram-bot.git
synced 2026-09-03 09:46:17 +03:00
Сравнить коммиты
32 Коммитов
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
384d203b59 | ||
|
|
9e396cabc4 | ||
|
|
81c18034a5 | ||
|
|
f332d27ec8 | ||
|
|
fa9b01d9d2 | ||
|
|
e06882f7af | ||
|
|
1a30b430df | ||
|
|
da93abaaf7 | ||
|
|
0ae17fb34e | ||
|
|
7e41bd4f9b | ||
|
|
e529a79339 | ||
|
|
ef4f3caa5f | ||
|
|
df7039326d | ||
|
|
4f47506e22 | ||
|
|
6abbe5489d | ||
|
|
ff7de976d0 | ||
|
|
aaba27eb70 | ||
|
|
88a4a9aab3 | ||
|
|
9387043ec1 | ||
|
|
3dca35ba9a | ||
|
|
6879618c00 | ||
|
|
6e4a69b6b1 | ||
|
|
b8db576452 | ||
|
|
2fffdeb475 | ||
|
|
9defa42f40 | ||
|
|
0320507abe | ||
|
|
cd3d3739a4 | ||
|
|
a054ac704c | ||
|
|
23aa92e614 | ||
|
|
a49dca66fa | ||
|
|
2d5ca981ce | ||
|
|
cf9bd87cf2 |
3
.gitignore
поставляемый
3
.gitignore
поставляемый
@@ -1,9 +1,12 @@
|
|||||||
/.bundle/
|
/.bundle/
|
||||||
/.yardoc
|
/.yardoc
|
||||||
/Gemfile.lock
|
/Gemfile.lock
|
||||||
|
/gemfiles/*.lock
|
||||||
|
/gemfiles/.bundle
|
||||||
/_yardoc/
|
/_yardoc/
|
||||||
/coverage/
|
/coverage/
|
||||||
/doc/
|
/doc/
|
||||||
/pkg/
|
/pkg/
|
||||||
/spec/reports/
|
/spec/reports/
|
||||||
|
/log/
|
||||||
/tmp/
|
/tmp/
|
||||||
|
|||||||
39
.rubocop.yml
39
.rubocop.yml
@@ -1,29 +1,52 @@
|
|||||||
Rails: {Enabled: true}
|
Rails: {Enabled: true}
|
||||||
|
|
||||||
Style/Alias: {Enabled: false}
|
Layout/AlignParameters:
|
||||||
Style/AlignParameters:
|
|
||||||
# Disable, till rubocop supports combination of styles.
|
# Disable, till rubocop supports combination of styles.
|
||||||
# Use one of this styles where appropriate, keep it clean, compact and readable.
|
# Use one of this styles where appropriate, keep it clean, compact and readable.
|
||||||
Enabled: false
|
Enabled: false
|
||||||
# EnforcedStyle:
|
# EnforcedStyle:
|
||||||
# - with_first_parameter
|
# - with_first_parameter
|
||||||
# - with_fixed_indentation
|
# - with_fixed_indentation
|
||||||
|
|
||||||
|
# Breaks
|
||||||
|
#
|
||||||
|
# I18n.t(key,
|
||||||
|
# param: val,
|
||||||
|
# # ...
|
||||||
|
# )
|
||||||
|
Layout/ClosingParenthesisIndentation: {Enabled: false}
|
||||||
|
Layout/DotPosition: {EnforcedStyle: trailing}
|
||||||
|
Layout/FirstParameterIndentation: {EnforcedStyle: consistent}
|
||||||
|
# Same as Layout/ClosingParenthesisIndentation
|
||||||
|
Layout/MultilineMethodCallBraceLayout: {Enabled: false}
|
||||||
|
Layout/MultilineMethodCallIndentation: {EnforcedStyle: indented}
|
||||||
|
Layout/MultilineOperationIndentation: {EnforcedStyle: indented}
|
||||||
|
Layout/SpaceInsideHashLiteralBraces: {EnforcedStyle: no_space}
|
||||||
|
|
||||||
|
# Offences named scopes and `expect {}.to change {}`.
|
||||||
|
Lint/AmbiguousBlockAssociation: {Enabled: false}
|
||||||
|
|
||||||
|
Naming/PredicateName: {Enabled: false}
|
||||||
|
Naming/VariableNumber: {EnforcedStyle: snake_case}
|
||||||
|
|
||||||
|
Style/Alias: {Enabled: false}
|
||||||
Style/AndOr: {EnforcedStyle: conditionals}
|
Style/AndOr: {EnforcedStyle: conditionals}
|
||||||
Style/ClosingParenthesisIndentation: {Enabled: false}
|
|
||||||
Style/Documentation: {Enabled: false}
|
Style/Documentation: {Enabled: false}
|
||||||
Style/DotPosition: {EnforcedStyle: trailing}
|
|
||||||
Style/FirstParameterIndentation: {EnforcedStyle: consistent}
|
|
||||||
Style/IfUnlessModifier: {Enabled: false}
|
Style/IfUnlessModifier: {Enabled: false}
|
||||||
|
|
||||||
|
# Consistent to other definitions.
|
||||||
|
Style/EmptyMethod: {EnforcedStyle: expanded}
|
||||||
|
|
||||||
Style/ModuleFunction: {Enabled: false}
|
Style/ModuleFunction: {Enabled: false}
|
||||||
Style/MultilineOperationIndentation: {EnforcedStyle: indented}
|
|
||||||
Style/NestedParenthesizedCalls: {Enabled: false}
|
Style/NestedParenthesizedCalls: {Enabled: false}
|
||||||
Style/PredicateName: {Enabled: false}
|
|
||||||
Style/SignalException: {EnforcedStyle: only_raise}
|
Style/SignalException: {EnforcedStyle: only_raise}
|
||||||
Style/SpaceInsideHashLiteralBraces: {EnforcedStyle: no_space}
|
|
||||||
Style/TrailingCommaInArguments: {Enabled: false}
|
Style/TrailingCommaInArguments: {Enabled: false}
|
||||||
Style/TrailingCommaInLiteral: {EnforcedStyleForMultiline: comma}
|
Style/TrailingCommaInLiteral: {EnforcedStyleForMultiline: comma}
|
||||||
|
|
||||||
Metrics/AbcSize: {Max: 21}
|
Metrics/AbcSize: {Max: 21}
|
||||||
|
# Other metrics are just enough.
|
||||||
|
# This one offences all specs, routes and some initializers.
|
||||||
|
Metrics/BlockLength: {Enabled: false}
|
||||||
Metrics/LineLength: {Max: 100}
|
Metrics/LineLength: {Max: 100}
|
||||||
Metrics/MethodLength: {Max: 30}
|
Metrics/MethodLength: {Max: 30}
|
||||||
Metrics/CyclomaticComplexity: {Max: 8}
|
Metrics/CyclomaticComplexity: {Max: 8}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ language: ruby
|
|||||||
cache: bundler
|
cache: bundler
|
||||||
rvm:
|
rvm:
|
||||||
- 2.2.3
|
- 2.2.3
|
||||||
env:
|
gemfile:
|
||||||
- RAILS=4
|
- gemfiles/rails_42.gemfile
|
||||||
- RAILS=5
|
- gemfiles/rails_50.gemfile
|
||||||
|
- gemfiles/rails_51.gemfile
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|||||||
14
Appraisals
Обычный файл
14
Appraisals
Обычный файл
@@ -0,0 +1,14 @@
|
|||||||
|
appraise 'rails-51' do
|
||||||
|
gem 'actionpack', '~> 5.1'
|
||||||
|
gem 'railties', '~> 5.1'
|
||||||
|
end
|
||||||
|
|
||||||
|
appraise 'rails-50' do
|
||||||
|
gem 'actionpack', '~> 5.0'
|
||||||
|
gem 'railties', '~> 5.0'
|
||||||
|
end
|
||||||
|
|
||||||
|
appraise 'rails-42' do
|
||||||
|
gem 'actionpack', '~> 4.2'
|
||||||
|
gem 'railties', '~> 4.2'
|
||||||
|
end
|
||||||
38
CHANGELOG.md
38
CHANGELOG.md
@@ -1,6 +1,42 @@
|
|||||||
|
# Unreleased
|
||||||
|
|
||||||
|
# 0.12.4
|
||||||
|
|
||||||
|
- Fix spec helpers for callback queries.
|
||||||
|
|
||||||
|
# 0.12.3
|
||||||
|
|
||||||
|
- New methods from Bot API v3.5
|
||||||
|
- Collect all api helper-methods in Client::ApiHelper module.
|
||||||
|
- Add `bin/fetch-telegram-methods` to update API methods list from website.
|
||||||
|
|
||||||
|
# 0.12.1
|
||||||
|
|
||||||
|
- Fix `set_webhook` rake task for async bots with self-issued certificates.
|
||||||
|
- Make `#session` raise error when store is not configured,
|
||||||
|
don't use Rails.cache as fallback for session_store.
|
||||||
|
- Allow use different sessions for MessageContext.
|
||||||
|
|
||||||
|
# 0.12.0
|
||||||
|
|
||||||
|
- New API methods and payload types (up to Bot API 3.2).
|
||||||
|
- Fix rails 5.1.x support.
|
||||||
|
- RSpec context for callback_query updates.
|
||||||
|
- `edit_message` helper.
|
||||||
|
- ClientStub saves and returns token. Fixes testing multiple bots.
|
||||||
|
- Raise descriptive error when accessing not-configured bot.
|
||||||
|
|
||||||
|
# 0.11.3
|
||||||
|
|
||||||
|
- Release dependencies for Rails 5.1.
|
||||||
|
|
||||||
|
# 0.11.2
|
||||||
|
|
||||||
|
- Rails 5.1 deep symbolized secrets support.
|
||||||
|
|
||||||
# 0.11.1
|
# 0.11.1
|
||||||
|
|
||||||
# Fixed poller for typed response.
|
- Fixed poller for typed response.
|
||||||
|
|
||||||
# 0.11.0
|
# 0.11.0
|
||||||
|
|
||||||
|
|||||||
14
Gemfile
14
Gemfile
@@ -1,24 +1,20 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
case ENV['RAILS']
|
|
||||||
when '5'
|
|
||||||
gem 'actionpack', '5.0.0.rc1'
|
|
||||||
when '4'
|
|
||||||
gem 'actionpack', '~> 4.2'
|
|
||||||
end
|
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'sdoc', '~> 0.4.1'
|
gem 'appraisal', '~> 2.2'
|
||||||
|
|
||||||
gem 'pry', '~> 0.10.1'
|
gem 'pry', '~> 0.10.1'
|
||||||
gem 'pry-byebug', '~> 3.2.0'
|
gem 'pry-byebug', '~> 3.2.0'
|
||||||
|
gem 'sdoc', '~> 0.4.1'
|
||||||
|
|
||||||
gem 'telegram-bot-types', '~> 0.3.0'
|
gem 'telegram-bot-types', '~> 0.3.0'
|
||||||
|
|
||||||
gem 'rspec', '~> 3.5.0'
|
gem 'rspec', '~> 3.5.0'
|
||||||
gem 'rspec-its', '~> 1.1.0'
|
gem 'rspec-its', '~> 1.1.0'
|
||||||
|
gem 'rspec-rails', '~> 3.5.0'
|
||||||
|
|
||||||
gem 'rubocop', '~> 0.37.0'
|
gem 'rubocop', '~> 0.51.0'
|
||||||
|
|
||||||
gem 'coveralls', '~> 0.8.2', require: false
|
gem 'coveralls', '~> 0.8.2', require: false
|
||||||
end
|
end
|
||||||
|
|||||||
303
README.md
303
README.md
@@ -4,8 +4,8 @@
|
|||||||
[](https://codeclimate.com/github/telegram-bot-rb/telegram-bot)
|
[](https://codeclimate.com/github/telegram-bot-rb/telegram-bot)
|
||||||
[](https://travis-ci.org/telegram-bot-rb/telegram-bot)
|
[](https://travis-ci.org/telegram-bot-rb/telegram-bot)
|
||||||
|
|
||||||
Tools for developing bot for Telegram. Best used with Rails, but can be be used in
|
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/Non-rails-application).
|
[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
|
Supposed to be used in webhook-mode in production, and poller-mode
|
||||||
in development, but you can use poller in production if you want.
|
in development, but you can use poller in production if you want.
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ Package contains:
|
|||||||
|
|
||||||
- Ligthweight client for bot API (with fast and thread-safe
|
- Ligthweight client for bot API (with fast and thread-safe
|
||||||
[httpclient](https://github.com/nahi/httpclient) under the hood).
|
[httpclient](https://github.com/nahi/httpclient) under the hood).
|
||||||
- Controller with message parser. Allows to write separate methods for each command.
|
- Controller with message parser: define methods for commands, not `case` branches.
|
||||||
- Middleware and routes helpers for production env.
|
- Middleware and routes helpers for production env.
|
||||||
- Poller with automatic source-reloader for development env.
|
- Poller with automatic source-reloader for development env.
|
||||||
- Rake tasks to update webhook urls.
|
- Rake tasks to update webhook urls.
|
||||||
@@ -24,9 +24,11 @@ Here is sample [telegram_bot_app](https://github.com/telegram-bot-rb/telegram_bo
|
|||||||
with session, keyboards and inline queries.
|
with session, keyboards and inline queries.
|
||||||
Run it on your local machine in 1 minute!
|
Run it on your local machine in 1 minute!
|
||||||
|
|
||||||
And here is [app teamplate](https://github.com/telegram-bot-rb/rails_template)
|
And here is [app template](https://github.com/telegram-bot-rb/rails_template)
|
||||||
to generate clean app in seconds.
|
to generate clean app in seconds.
|
||||||
|
|
||||||
|
Examples and cookbook in [wiki](https://github.com/telegram-bot-rb/telegram-bot/wiki).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Add this line to your application's Gemfile:
|
Add this line to your application's Gemfile:
|
||||||
@@ -43,51 +45,70 @@ Or install it yourself as:
|
|||||||
|
|
||||||
$ gem install telegram-bot
|
$ gem install telegram-bot
|
||||||
|
|
||||||
|
Require if necessary:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'telegram/bot'
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Configuration
|
### Configuration in Rails app
|
||||||
|
|
||||||
Add `telegram` section into `secrets.yml`:
|
Add `telegram` section into `secrets.yml`:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
telegram:
|
development:
|
||||||
bots:
|
telegram:
|
||||||
# just set the token
|
# Single bot can be specified like this
|
||||||
chat: TOKEN_1
|
bot: TOKEN
|
||||||
# or add username to support commands with mentions (/help@ChatBot)
|
# or
|
||||||
auction:
|
bot:
|
||||||
token: TOKEN_2
|
token: TOKEN
|
||||||
username: ChatBot
|
username: SomeBot
|
||||||
|
|
||||||
# Single bot can be specified like this
|
# For multiple bots in single app use hash of `internal_bot_id => settings`
|
||||||
bot: TOKEN
|
bots:
|
||||||
# or
|
# just set the bot token
|
||||||
bot:
|
chat: TOKEN_1
|
||||||
token: TOKEN
|
# or add username to support commands with mentions (/help@ChatBot)
|
||||||
username: SomeBot
|
auction:
|
||||||
|
token: TOKEN_2
|
||||||
|
username: ChatBot
|
||||||
```
|
```
|
||||||
|
|
||||||
### Client
|
|
||||||
|
|
||||||
From now clients will be accessible with `Telegram.bots[:chat]` or `Telegram.bots[:auction]`.
|
From now clients will be accessible with `Telegram.bots[:chat]` or `Telegram.bots[:auction]`.
|
||||||
Single bot can be accessed with `Telegram.bot` or `Telegram.bots[:default]`.
|
Single bot can be accessed with `Telegram.bot` or `Telegram.bots[:default]`.
|
||||||
|
|
||||||
You can create clients manually with `Telegram::Bot::Client.new(token, username)`.
|
### Client
|
||||||
|
|
||||||
|
Client is instantiated with `Telegram::Bot::Client.new(token, username)`.
|
||||||
Username is optional and used only to parse commands with mentions.
|
Username is optional and used only to parse commands with mentions.
|
||||||
|
|
||||||
There is `request(path_suffix, body)` method to perform any query.
|
There is `request(path_suffix, body)` method to perform any query.
|
||||||
And there are also shortcuts for available queries in underscored style
|
And there are shortcuts for all available requests in underscored style
|
||||||
(`answer_inline_query` instead of `answerInlineQuery`).
|
(`answer_inline_query(params)` instead of `answerInlineQuery`).
|
||||||
All this methods just post given params to specific URL.
|
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
bot.request(:getMe) or bot.get_me
|
bot.request(:getMe) or bot.get_me
|
||||||
bot.request(:getupdates, offset: 1) or bot.get_updates(offset: 1)
|
bot.request(:getupdates, offset: 1) or bot.get_updates(offset: 1)
|
||||||
bot.send_message chat_id: chat_id, text: 'Test'
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Typed responses
|
||||||
|
|
||||||
By default client will return parsed json responses. You can enable
|
By default client will return parsed json responses. You can enable
|
||||||
response typecasting to virtus models using `telegram-bot-types` gem:
|
response typecasting to virtus models using
|
||||||
|
[`telegram-bot-types`](https://github.com/telegram-bot-rb/telegram-bot-types) gem:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# Add to your gemfile:
|
# Add to your gemfile:
|
||||||
gem 'telegram-bot-types', '~> x.x.x'
|
gem 'telegram-bot-types', '~> x.x.x'
|
||||||
@@ -99,18 +120,23 @@ bot.extend Telegram::Bot::Client::TypedResponse
|
|||||||
bot.get_me.class # => Telegram::Bot::Types::User
|
bot.get_me.class # => Telegram::Bot::Types::User
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### Controller
|
### Controller
|
||||||
|
|
||||||
|
Controller makes it easy to keep bot's code readable.
|
||||||
|
It does nothing more than finding out action name for update and invoking it.
|
||||||
|
So there is almost no overhead comparing to large `switch`, while you
|
||||||
|
can represent actions as separate methods keeping source much more readable and supportable.
|
||||||
|
|
||||||
|
New instance of controller is instantiated for each update.
|
||||||
|
This way every update is processed in isolation from others.
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
||||||
# use callbacks like in any other controllers
|
# use callbacks like in any other controllers
|
||||||
around_action :with_locale
|
around_action :with_locale
|
||||||
|
|
||||||
# Every update can have one of: message, inline_query, chosen_inline_result,
|
# Every update can have one of: message, inline_query, chosen_inline_result,
|
||||||
# callback_query.
|
# callback_query, etc.
|
||||||
# Define method with same name to respond to this updates.
|
# Define method with same name to respond to this updates.
|
||||||
def message(message)
|
def message(message)
|
||||||
# message can be also accessed via instance method
|
# message can be also accessed via instance method
|
||||||
@@ -160,6 +186,20 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Reply helpers
|
||||||
|
|
||||||
|
There are helpers to respond for basic actions. They just set chat/message/query
|
||||||
|
identifiers from 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:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
def respond_with(type, params); end
|
||||||
|
def reply_with(type, params); end
|
||||||
|
def answer_inline_query(results, params = {}); end
|
||||||
|
def answer_callback_query(text, params = {}); end
|
||||||
|
def edit_message(type, params = {}); end
|
||||||
|
```
|
||||||
|
|
||||||
#### Optional typecasting
|
#### Optional typecasting
|
||||||
|
|
||||||
You can enable typecasting of `update` with `telegram-bot-types` by including
|
You can enable typecasting of `update` with `telegram-bot-types` by including
|
||||||
@@ -177,35 +217,56 @@ end
|
|||||||
|
|
||||||
#### Session
|
#### Session
|
||||||
|
|
||||||
There is support for sessions using `ActiveSupport::Cache` stores.
|
This API is very close to ActiveController's session API, but works different
|
||||||
|
under the hood. Cookies can not be used to store session id or
|
||||||
|
whole session (like CookieStore does). So it uses key-value store and `session_key`
|
||||||
|
method to build identifier from update.
|
||||||
|
|
||||||
|
Store can be one of numerous `ActiveSupport::Cache` stores.
|
||||||
|
While `:file_store` is suitable for development and single-server deployments
|
||||||
|
without heavy load, it doesn't scale well. Key-value databases with persistance
|
||||||
|
like Redis are more appropriate for production use.
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# configure store in env files:
|
# In rails app store can be configured in env files:
|
||||||
config.telegram_updates_controller.session_store = :redis_store, {expires_in: 1.month}
|
config.telegram_updates_controller.session_store = :redis_store, {expires_in: 1.month}
|
||||||
|
|
||||||
|
# In other app it can be done for all controllers with:
|
||||||
|
Telegram::Bot::UpdatesController.session_store = :redis_store, {expires_in: 1.month}
|
||||||
|
# or for specific one:
|
||||||
|
OneOfUpdatesController.session_store = :redis_store, {expires_in: 1.month}
|
||||||
|
```
|
||||||
|
|
||||||
|
Default session id is made from bot's username and `(from || chat)['id']`.
|
||||||
|
It means that session will be the same for updates from user in every chat,
|
||||||
|
and different for every user in the same group chat.
|
||||||
|
To change this behavior you can override `session_key` method, or even
|
||||||
|
define [multiple sessions](https://github.com/telegram-bot-rb/telegram-bot/wiki/Multiple-session-objects)
|
||||||
|
in single controller. For details see `Session` module.
|
||||||
|
|
||||||
|
```ruby
|
||||||
class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
||||||
include Telegram::Bot::UpdatesController::Session
|
include Telegram::Bot::UpdatesController::Session
|
||||||
# or just shortcut:
|
# or just shortcut:
|
||||||
use_session!
|
use_session!
|
||||||
|
|
||||||
# You can override global config
|
# You can override global config for this controller.
|
||||||
self.session_store = :file_store
|
self.session_store = :file_store
|
||||||
|
|
||||||
def write(text = nil, *)
|
def write(text = nil, *)
|
||||||
session[:text] = text
|
session[:text] = text
|
||||||
end
|
end
|
||||||
|
|
||||||
def read
|
def read(*)
|
||||||
respond_with :message, text: session[:text]
|
respond_with :message, text: session[:text]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
# By default it uses bot's username and user's id as a session key.
|
|
||||||
# Chat's id is used only when `from` field is empty.
|
# In this case session will persist for user only in specific chat.
|
||||||
# Override `session_key` method to change this behavior.
|
# Same user in other chat will have different session.
|
||||||
def session_key
|
def session_key
|
||||||
# In this case session will persist for user only in specific chat:
|
"#{bot.username}:#{chat['id']}:#{from['id']}" if chat && from
|
||||||
"#{bot.username}:#{chat['id']}:#{from['id']}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@@ -245,77 +306,80 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
|||||||
# This will call #rename like if it is called with message '/rename %text%'
|
# This will call #rename like if it is called with message '/rename %text%'
|
||||||
context_handler :rename
|
context_handler :rename
|
||||||
|
|
||||||
# If you have a lot of such methods you can use
|
# If you have a lot of such methods you can call this method
|
||||||
|
# to use context value as action name for all contexts which miss handlers:
|
||||||
context_to_action!
|
context_to_action!
|
||||||
# It'll use context value as action name for all contexts which miss handlers.
|
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
You can use `CallbackQueryContext` in the similar way to split `#callback_query` into
|
#### Callback queries
|
||||||
several specific methods. It doesn't require session support, and takes context from
|
|
||||||
data. If data has a prefix with colon like this `my_ctx:smth...` it'll call
|
You can include `CallbackQueryContext` module to split `#callback_query` into
|
||||||
`my_ctx_callback_query('smth...')` when there is such action method. Otherwise
|
several methods. It doesn't require session support, and takes context from
|
||||||
it'll call `callback_query('my_ctx:smth...')` as usual.
|
data: if data has a prefix with colon like this `my_ctx:smth...` it invokes
|
||||||
|
`my_ctx_callback_query('smth...')` when such action method is defined. Otherwise
|
||||||
|
it invokes `callback_query('my_ctx:smth...')` as usual.
|
||||||
|
Callback queries without prefix stay untouched.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# This one handles `set_value:%{something}`.
|
||||||
|
def set_value_callback_query(new_value = nil, *)
|
||||||
|
save_this(value)
|
||||||
|
answer_callback_query('Saved!)
|
||||||
|
end
|
||||||
|
|
||||||
|
# And this one is for `make_cool:%{something}`
|
||||||
|
def make_cool_callback_query(thing = nil, *)
|
||||||
|
do_it(thing)
|
||||||
|
answer_callback_query("#{thing} is cool now! Like a callback query context.")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Routes in Rails app
|
||||||
|
|
||||||
|
There is `telegram_webhooks` helper for rails app to define routes for webhooks.
|
||||||
|
It defines routes at `telegram/#{bot.token}` and connects bots with controller.
|
||||||
|
For more options see [examples in wiki](https://github.com/telegram-bot-rb/telegram-bot/wiki/Routes-helpers-in-details).
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Create routes for all Telegram.bots using single controller:
|
||||||
|
telegram_webhooks TelegramController
|
||||||
|
|
||||||
|
# Use different controllers for each bot:
|
||||||
|
telegram_webhooks chat: TelegramChatController,
|
||||||
|
auction: TelegramAuctionController
|
||||||
|
```
|
||||||
|
|
||||||
#### Processesing updates
|
#### Processesing updates
|
||||||
|
|
||||||
To process update run:
|
To process update with controller call `.dispatch(bot, update)` on it.
|
||||||
|
There are several options to run it automatically:
|
||||||
|
|
||||||
|
- Use webhooks with routes helper (described above).
|
||||||
|
- Use `Telegram::Bot::Middleware` with rack ([example in wiki](https://github.com/telegram-bot-rb/telegram-bot/wiki/Not-rails-application)).
|
||||||
|
- Use poller (described in the next section).
|
||||||
|
|
||||||
|
To run action without update (ex., send notifications from jobs),
|
||||||
|
you can call `#process` directly. In this case controller can be initialized
|
||||||
|
with `:from` and/or `:chat` options instead of `update` object:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
ControllerClass.dispatch(bot, update)
|
|
||||||
```
|
|
||||||
|
|
||||||
There is also ability to run action without update:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Most likely you'll want to pass :from and :chat
|
|
||||||
controller = ControllerClass.new(bot, from: telegram_user, chat: telegram_chat)
|
controller = ControllerClass.new(bot, from: telegram_user, chat: telegram_chat)
|
||||||
controller.process(:help, *args)
|
controller.process(:welcome, *args)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Routes
|
|
||||||
|
|
||||||
Use `telegram_webhooks` helper to add routes. It will create routes for bots
|
|
||||||
at "telegram/#{bot.token}" path.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Create routes for all Telegram.bots to use same controller:
|
|
||||||
telegram_webhooks TelegramController
|
|
||||||
|
|
||||||
# Or pass custom bots usin any of supported config options:
|
|
||||||
telegram_webhooks TelegramController,
|
|
||||||
bot,
|
|
||||||
{token: token, username: username},
|
|
||||||
other_bot_token
|
|
||||||
|
|
||||||
# Use different controllers for each bot:
|
|
||||||
telegram_webhooks bot => TelegramChatController,
|
|
||||||
other_bot => TelegramAuctionController
|
|
||||||
|
|
||||||
# telegram_webhooks creates named routes.
|
|
||||||
# Route name depends on `Telegram.bots`.
|
|
||||||
# When there is single bot it will use 'telegram_webhook'.
|
|
||||||
# When there are it will use bot's key in the `Telegram.bots` as prefix
|
|
||||||
# (eg. `chat_telegram_webhook`).
|
|
||||||
# You can override this options or specify others:
|
|
||||||
telegram_webhooks TelegramController, as: :my_webhook
|
|
||||||
telegram_webhooks bot => [TelegramChatController, as: :chat_webhook],
|
|
||||||
other_bot => TelegramAuctionController,
|
|
||||||
admin_chat: TelegramAdminChatController
|
|
||||||
```
|
|
||||||
|
|
||||||
For Rack applications you can also use `Telegram::Bot::Middleware` or just
|
|
||||||
call `.dispatch(bot, update)` on controller.
|
|
||||||
|
|
||||||
### Development & Debugging
|
### Development & Debugging
|
||||||
|
|
||||||
Use `rake telegram:bot:poller` to run poller. It'll automatically load
|
Use `rake telegram:bot:poller` to run poller in rails app. It automatically loads
|
||||||
changes without restart in development env. Optionally specify bot to run poller for
|
changes without restart in development env.
|
||||||
with `BOT` envvar (`BOT=chat`).
|
Optionally pass bot id in `BOT` envvar (`BOT=chat`) to specify bot to run poller for.
|
||||||
|
|
||||||
This task will not work if you don't use `telegram_webhooks`.
|
This task requires `telegram_webhooks` helper to be used as it connects bots with controller.
|
||||||
You can run poller manually with
|
To run poller in other cases use:
|
||||||
`Telegram::Bot::UpdatesPoller.start(bot, controller_class)`.
|
|
||||||
|
```ruby
|
||||||
|
Telegram::Bot::UpdatesPoller.start(bot, controller_class)
|
||||||
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
@@ -323,11 +387,11 @@ There is `Telegram::Bot::ClientStub` class to stub client for tests.
|
|||||||
Instead of performing API requests it stores them in `requests` hash.
|
Instead of performing API requests it stores them in `requests` hash.
|
||||||
|
|
||||||
To stub all possible clients use `Telegram::Bot::ClientStub.stub_all!` before
|
To stub all possible clients use `Telegram::Bot::ClientStub.stub_all!` before
|
||||||
initializing clients. Most likely you'll want something like this:
|
initializing clients. Here is template for RSpec:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# environments/test.rb
|
# environments/test.rb
|
||||||
# Make sure to run it before defining routes or storing bot to some place in app!
|
# Make sure to run it before defining routes or accessing any bot in the app!
|
||||||
Telegram.reset_bots
|
Telegram.reset_bots
|
||||||
Telegram::Bot::ClientStub.stub_all!
|
Telegram::Bot::ClientStub.stub_all!
|
||||||
|
|
||||||
@@ -335,6 +399,8 @@ Telegram::Bot::ClientStub.stub_all!
|
|||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
# ...
|
# ...
|
||||||
config.after { Telegram.bot.reset }
|
config.after { Telegram.bot.reset }
|
||||||
|
# or for multiple bots:
|
||||||
|
config.after { Telegram.bots.each_value(&:reset) }
|
||||||
# ...
|
# ...
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@@ -353,6 +419,13 @@ RSpec.describe TelegramWebhooksController, :telegram_bot do
|
|||||||
subject { -> { dispatch_command :start } }
|
subject { -> { dispatch_command :start } }
|
||||||
it { should respond_with_message 'Hi there!' }
|
it { should respond_with_message 'Hi there!' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# There is context for callback queries with related matchers.
|
||||||
|
describe '#hey_callback_query', :callback_query do
|
||||||
|
let(:data) { "hey:#{name}" }
|
||||||
|
let(:name) { 'Joe' }
|
||||||
|
it { should answer_callback_query('Hey Joe') }
|
||||||
|
it { should edit_current_message :text, text: 'Done' }
|
||||||
end
|
end
|
||||||
|
|
||||||
# For controller specs use
|
# For controller specs use
|
||||||
@@ -370,12 +443,17 @@ expect(&process_update).
|
|||||||
to make_telegram_request(bot, :sendMessage, hash_including(text: 'msg text'))
|
to make_telegram_request(bot, :sendMessage, hash_including(text: 'msg text'))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Place integration tests inside `spec/requests`
|
||||||
|
when using RSpec's `infer_spec_type_from_file_location!`,
|
||||||
|
or just add `type: :request` to `describe`.
|
||||||
|
|
||||||
See sample app for more examples.
|
See sample app for more examples.
|
||||||
|
|
||||||
### Deploying
|
### Deployment
|
||||||
|
|
||||||
Use `rake telegram:bot:set_webhook` to update webhook url for all configured bots.
|
While webhooks-mode is prefered, poller still can be used in production.
|
||||||
Certificate can be specified with `CERT=path/to/cert`.
|
See [comparison and examples](https://github.com/telegram-bot-rb/telegram-bot/wiki/Deployment)
|
||||||
|
for details.
|
||||||
|
|
||||||
### Botan.io metrics
|
### Botan.io metrics
|
||||||
|
|
||||||
@@ -435,19 +513,18 @@ If you want async mode, but don't want to setup queue, know that Rails 5 are shi
|
|||||||
with Async adapter by default, and there is
|
with Async adapter by default, and there is
|
||||||
[Sucker Punch](https://github.com/brandonhilkert/sucker_punch) for Rails 4.
|
[Sucker Punch](https://github.com/brandonhilkert/sucker_punch) for Rails 4.
|
||||||
|
|
||||||
Be aware of some limitations:
|
|
||||||
|
|
||||||
- Client will not return API response.
|
|
||||||
- Sending files is not available in async mode [now],
|
|
||||||
because them can not be serialized.
|
|
||||||
|
|
||||||
To disable async mode for the block of code use `bot.async(false) { bot.send_photo }`.
|
To disable async mode for the block of code use `bot.async(false) { bot.send_photo }`.
|
||||||
Yes, it's threadsafe too.
|
Yes, it's threadsafe too.
|
||||||
|
|
||||||
|
#### Limitations
|
||||||
|
|
||||||
|
- Client will not return API response.
|
||||||
|
- Sending files is not available in async mode, because they can not be serialized.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
After checking out the repo, run `bin/setup` to install dependencies.
|
After checking out the repo, run `bin/setup` to install dependencies.
|
||||||
Then, run `rake spec` to run the tests.
|
Then, run `appraisal rake spec` to run the tests.
|
||||||
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
||||||
|
|
||||||
To install this gem onto your local machine, run `bundle exec rake install`.
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
||||||
@@ -455,15 +532,7 @@ To release a new version, update the version number in `version.rb`,
|
|||||||
and then run `bundle exec rake release`, which will create a git tag for the version,
|
and then run `bundle exec rake release`, which will create a git tag for the version,
|
||||||
push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
||||||
|
|
||||||
### Different Rails versions
|
Use `bin/fetch-telegram-methods` to update API methods list from Telegram website.
|
||||||
|
|
||||||
To setup development for specific major Rails version use:
|
|
||||||
|
|
||||||
```
|
|
||||||
RAILS=5 bundle install
|
|
||||||
# or
|
|
||||||
RAILS=5 bundle update
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
41
bin/fetch-telegram-methods
Исполняемый файл
41
bin/fetch-telegram-methods
Исполняемый файл
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
#
|
||||||
|
# Fetch list of methods from Telegram docs.
|
||||||
|
# Use it to update client.rb.
|
||||||
|
|
||||||
|
require 'net/http'
|
||||||
|
require 'nokogiri'
|
||||||
|
|
||||||
|
DOCS_URL = 'https://core.telegram.org/bots/api'.freeze
|
||||||
|
|
||||||
|
page_html = Net::HTTP.get(URI(DOCS_URL))
|
||||||
|
doc = Nokogiri::HTML(page_html)
|
||||||
|
|
||||||
|
# Select `h4`s, but use `h3`s to group them.
|
||||||
|
headers = doc.css('h3, h4').
|
||||||
|
chunk_while { |_, x| x.name == 'h4' }.
|
||||||
|
map { |g| g.select { |x| x.name == 'h4' } }.
|
||||||
|
map { |g| g.map(&:text) }
|
||||||
|
|
||||||
|
# Method starts with lowercase and does not have spaces.
|
||||||
|
NOT_METHOD_REGEXP = /(\A[^a-z])|\s/
|
||||||
|
|
||||||
|
# Filter method names.
|
||||||
|
method_list = headers.
|
||||||
|
map { |g| g.reject { |x| x.match?(NOT_METHOD_REGEXP) } }.
|
||||||
|
reject(&:empty?)
|
||||||
|
|
||||||
|
api_version = doc.text.match(/^(Bot API ([\d\.]+))\.?$/)
|
||||||
|
|
||||||
|
result = ['# Generated with bin/fetch-telegram-methods']
|
||||||
|
result << "# #{api_version[1]}" if api_version
|
||||||
|
result << ''
|
||||||
|
result << method_list.map { |g| g.join("\n") }.join("\n\n")
|
||||||
|
result << ''
|
||||||
|
result_txt = result.join("\n")
|
||||||
|
|
||||||
|
puts result_txt
|
||||||
|
|
||||||
|
API_METHODS_FILE = File.expand_path('../lib/telegram/bot/client/api_methods.txt', __dir__).freeze
|
||||||
|
File.write(API_METHODS_FILE, result_txt)
|
||||||
|
puts '', "Updated #{API_METHODS_FILE}"
|
||||||
@@ -3,6 +3,7 @@ set -euo pipefail
|
|||||||
IFS=$'\n\t'
|
IFS=$'\n\t'
|
||||||
|
|
||||||
bundle install
|
bundle install
|
||||||
|
appraisal install
|
||||||
bin/install_git_hooks
|
bin/install_git_hooks
|
||||||
|
|
||||||
# Do any other automated setup that you need to do here
|
# Do any other automated setup that you need to do here
|
||||||
|
|||||||
21
gemfiles/rails_42.gemfile
Обычный файл
21
gemfiles/rails_42.gemfile
Обычный файл
@@ -0,0 +1,21 @@
|
|||||||
|
# This file was generated by Appraisal
|
||||||
|
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "actionpack", "~> 4.2"
|
||||||
|
gem "railties", "~> 4.2"
|
||||||
|
|
||||||
|
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.3.0"
|
||||||
|
gem "rspec", "~> 3.5.0"
|
||||||
|
gem "rspec-its", "~> 1.1.0"
|
||||||
|
gem "rspec-rails", "~> 3.5.0"
|
||||||
|
gem "rubocop", "~> 0.51.0"
|
||||||
|
gem "coveralls", "~> 0.8.2", require: false
|
||||||
|
end
|
||||||
|
|
||||||
|
gemspec path: "../"
|
||||||
21
gemfiles/rails_50.gemfile
Обычный файл
21
gemfiles/rails_50.gemfile
Обычный файл
@@ -0,0 +1,21 @@
|
|||||||
|
# This file was generated by Appraisal
|
||||||
|
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "actionpack", "~> 5.0"
|
||||||
|
gem "railties", "~> 5.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.3.0"
|
||||||
|
gem "rspec", "~> 3.5.0"
|
||||||
|
gem "rspec-its", "~> 1.1.0"
|
||||||
|
gem "rspec-rails", "~> 3.5.0"
|
||||||
|
gem "rubocop", "~> 0.51.0"
|
||||||
|
gem "coveralls", "~> 0.8.2", require: false
|
||||||
|
end
|
||||||
|
|
||||||
|
gemspec path: "../"
|
||||||
21
gemfiles/rails_51.gemfile
Обычный файл
21
gemfiles/rails_51.gemfile
Обычный файл
@@ -0,0 +1,21 @@
|
|||||||
|
# This file was generated by Appraisal
|
||||||
|
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "actionpack", "~> 5.1"
|
||||||
|
gem "railties", "~> 5.1"
|
||||||
|
|
||||||
|
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.3.0"
|
||||||
|
gem "rspec", "~> 3.5.0"
|
||||||
|
gem "rspec-its", "~> 1.1.0"
|
||||||
|
gem "rspec-rails", "~> 3.5.0"
|
||||||
|
gem "rubocop", "~> 0.51.0"
|
||||||
|
gem "coveralls", "~> 0.8.2", require: false
|
||||||
|
end
|
||||||
|
|
||||||
|
gemspec path: "../"
|
||||||
@@ -21,7 +21,7 @@ namespace :telegram do
|
|||||||
route_name = Telegram::Bot::RoutesHelper.route_name_for_bot(bot)
|
route_name = Telegram::Bot::RoutesHelper.route_name_for_bot(bot)
|
||||||
url = routes.send("#{route_name}_url")
|
url = routes.send("#{route_name}_url")
|
||||||
puts "Setting webhook for #{key}..."
|
puts "Setting webhook for #{key}..."
|
||||||
bot.set_webhook(url: url, certificate: cert)
|
bot.async(false) { bot.set_webhook(url: url, certificate: cert) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ module Telegram
|
|||||||
def request(method, uri, query = {}, body = nil)
|
def request(method, uri, query = {}, body = nil)
|
||||||
res = http_request(method, uri, query.merge(token: token), body)
|
res = http_request(method, uri, query.merge(token: token), body)
|
||||||
status = res.status
|
status = res.status
|
||||||
return JSON.parse(res.body) if 300 > status
|
return JSON.parse(res.body) if status < 300
|
||||||
result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
|
result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
|
||||||
err_msg = "#{res.reason}: #{result && result['info'] || '-'}"
|
err_msg = "#{res.reason}: #{result && result['info'] || '-'}"
|
||||||
raise Error, err_msg
|
raise Error, err_msg
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
require 'json'
|
require 'json'
|
||||||
require 'httpclient'
|
require 'httpclient'
|
||||||
require 'active_support/core_ext/string/inflections'
|
|
||||||
require 'active_support/core_ext/hash/keys'
|
|
||||||
|
|
||||||
module Telegram
|
module Telegram
|
||||||
module Bot
|
module Bot
|
||||||
@@ -14,6 +12,9 @@ module Telegram
|
|||||||
prepend Botan::ClientHelpers
|
prepend Botan::ClientHelpers
|
||||||
include DebugClient
|
include DebugClient
|
||||||
|
|
||||||
|
require 'telegram/bot/client/api_helper'
|
||||||
|
include ApiHelper
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def by_id(id)
|
def by_id(id)
|
||||||
Telegram.bots[id]
|
Telegram.bots[id]
|
||||||
@@ -49,7 +50,7 @@ module Telegram
|
|||||||
def request(action, body = {})
|
def request(action, body = {})
|
||||||
res = http_request("#{base_uri}#{action}", self.class.prepare_body(body))
|
res = http_request("#{base_uri}#{action}", self.class.prepare_body(body))
|
||||||
status = res.status
|
status = res.status
|
||||||
return JSON.parse(res.body) if 300 > status
|
return JSON.parse(res.body) if status < 300
|
||||||
result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
|
result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
|
||||||
err_msg = result && result['description'] || '-'
|
err_msg = result && result['description'] || '-'
|
||||||
if result
|
if result
|
||||||
@@ -62,50 +63,6 @@ module Telegram
|
|||||||
raise Error, "#{res.reason}: #{err_msg}"
|
raise Error, "#{res.reason}: #{err_msg}"
|
||||||
end
|
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.
|
# Endpoint for low-level request. For easy host highjacking & instrumentation.
|
||||||
# Params are not used directly but kept for instrumentation purpose.
|
# Params are not used directly but kept for instrumentation purpose.
|
||||||
# You probably don't want to use this method directly.
|
# You probably don't want to use this method directly.
|
||||||
|
|||||||
31
lib/telegram/bot/client/api_helper.rb
Обычный файл
31
lib/telegram/bot/client/api_helper.rb
Обычный файл
@@ -0,0 +1,31 @@
|
|||||||
|
require 'active_support/core_ext/string/inflections'
|
||||||
|
|
||||||
|
module Telegram
|
||||||
|
module Bot
|
||||||
|
class Client
|
||||||
|
module ApiHelper
|
||||||
|
METHODS_LIST_FILE = File.expand_path('../api_methods.txt', __FILE__)
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def methods_list(file = METHODS_LIST_FILE)
|
||||||
|
File.read(file).lines.
|
||||||
|
map(&:strip).
|
||||||
|
reject { |x| x.empty? || x.start_with?('#') }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Defines method with underscored name to post to specific endpoint:
|
||||||
|
#
|
||||||
|
# define_method :getMe
|
||||||
|
# # defines #get_me
|
||||||
|
def define_helpers(*list)
|
||||||
|
list.map(&:to_s).each do |method|
|
||||||
|
define_method(method.underscore) { |*args| request(method, *args) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define_helpers(*methods_list)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
68
lib/telegram/bot/client/api_methods.txt
Обычный файл
68
lib/telegram/bot/client/api_methods.txt
Обычный файл
@@ -0,0 +1,68 @@
|
|||||||
|
# Generated with bin/fetch-telegram-methods
|
||||||
|
# Bot API 3.5.
|
||||||
|
|
||||||
|
getUpdates
|
||||||
|
setWebhook
|
||||||
|
deleteWebhook
|
||||||
|
getWebhookInfo
|
||||||
|
|
||||||
|
getMe
|
||||||
|
sendMessage
|
||||||
|
forwardMessage
|
||||||
|
sendPhoto
|
||||||
|
sendAudio
|
||||||
|
sendDocument
|
||||||
|
sendVideo
|
||||||
|
sendVoice
|
||||||
|
sendVideoNote
|
||||||
|
sendMediaGroup
|
||||||
|
sendLocation
|
||||||
|
editMessageLiveLocation
|
||||||
|
stopMessageLiveLocation
|
||||||
|
sendVenue
|
||||||
|
sendContact
|
||||||
|
sendChatAction
|
||||||
|
getUserProfilePhotos
|
||||||
|
getFile
|
||||||
|
kickChatMember
|
||||||
|
unbanChatMember
|
||||||
|
restrictChatMember
|
||||||
|
promoteChatMember
|
||||||
|
exportChatInviteLink
|
||||||
|
setChatPhoto
|
||||||
|
deleteChatPhoto
|
||||||
|
setChatTitle
|
||||||
|
setChatDescription
|
||||||
|
pinChatMessage
|
||||||
|
unpinChatMessage
|
||||||
|
leaveChat
|
||||||
|
getChat
|
||||||
|
getChatAdministrators
|
||||||
|
getChatMembersCount
|
||||||
|
getChatMember
|
||||||
|
setChatStickerSet
|
||||||
|
deleteChatStickerSet
|
||||||
|
answerCallbackQuery
|
||||||
|
|
||||||
|
editMessageText
|
||||||
|
editMessageCaption
|
||||||
|
editMessageReplyMarkup
|
||||||
|
deleteMessage
|
||||||
|
|
||||||
|
sendSticker
|
||||||
|
getStickerSet
|
||||||
|
uploadStickerFile
|
||||||
|
createNewStickerSet
|
||||||
|
addStickerToSet
|
||||||
|
setStickerPositionInSet
|
||||||
|
deleteStickerFromSet
|
||||||
|
|
||||||
|
answerInlineQuery
|
||||||
|
|
||||||
|
sendInvoice
|
||||||
|
answerShippingQuery
|
||||||
|
answerPreCheckoutQuery
|
||||||
|
|
||||||
|
sendGame
|
||||||
|
setGameScore
|
||||||
|
getGameHighScores
|
||||||
@@ -35,6 +35,7 @@ module Telegram
|
|||||||
end
|
end
|
||||||
|
|
||||||
def initialize(token = nil, username = nil, **options)
|
def initialize(token = nil, username = nil, **options)
|
||||||
|
@token = token || options[:token]
|
||||||
@username = username || options[:username] || token
|
@username = username || options[:username] || token
|
||||||
reset
|
reset
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
require 'active_support/core_ext/hash/keys'
|
require 'active_support/core_ext/hash/keys'
|
||||||
require 'active_support/core_ext/hash/transform_values'
|
require 'active_support/core_ext/hash/transform_values'
|
||||||
|
require 'active_support/core_ext/hash/indifferent_access'
|
||||||
|
|
||||||
module Telegram
|
module Telegram
|
||||||
module Bot
|
module Bot
|
||||||
@@ -33,7 +34,11 @@ module Telegram
|
|||||||
|
|
||||||
# Default bot.
|
# Default bot.
|
||||||
def bot
|
def bot
|
||||||
@bot ||= bots[:default]
|
@bot ||= bots.fetch(:default) do
|
||||||
|
raise 'Default bot is not configured.' \
|
||||||
|
' Add :default to bots_config' \
|
||||||
|
' or use telegram.bot/telegram.bots.default section in secrets.yml.'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Hash of botan clients made from #bots.
|
# Hash of botan clients made from #bots.
|
||||||
@@ -48,9 +53,10 @@ module Telegram
|
|||||||
def bots_config
|
def bots_config
|
||||||
@bots_config ||=
|
@bots_config ||=
|
||||||
if defined?(Rails.application)
|
if defined?(Rails.application)
|
||||||
telegram_config = Rails.application.secrets[:telegram] || {}
|
secrets = Rails.application.secrets.
|
||||||
(telegram_config['bots'] || {}).symbolize_keys.tap do |config|
|
fetch(:telegram, {}).with_indifferent_access
|
||||||
default = telegram_config['bot']
|
secrets.fetch(:bots, {}).symbolize_keys.tap do |config|
|
||||||
|
default = secrets[:bot]
|
||||||
config[:default] = default if default
|
config[:default] = default if default
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
require 'active_support/core_ext/hash/keys'
|
||||||
|
|
||||||
module Telegram
|
module Telegram
|
||||||
module Bot
|
module Bot
|
||||||
module Initializers
|
module Initializers
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ module Telegram
|
|||||||
|
|
||||||
ActiveSupport.on_load('telegram.bot.updates_controller') do
|
ActiveSupport.on_load('telegram.bot.updates_controller') do
|
||||||
self.logger = options.logger || Rails.logger
|
self.logger = options.logger || Rails.logger
|
||||||
self.session_store = options.session_store || Rails.cache
|
self.session_store = options.session_store if options.session_store
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
RSpec.shared_context 'telegram/bot/integration' do
|
RSpec.shared_context 'telegram/bot/integration' do
|
||||||
let(:bot) { Telegram.bot }
|
let(:bot) { Telegram.bot }
|
||||||
|
let(:default_message_options) { {from: from, chat: chat} }
|
||||||
|
let(:from) { {id: from_id} }
|
||||||
let(:from_id) { 123 }
|
let(:from_id) { 123 }
|
||||||
|
let(:chat) { {id: chat_id} }
|
||||||
let(:chat_id) { 456 }
|
let(:chat_id) { 456 }
|
||||||
let(:default_message_options) { {from: {id: from_id}, chat: {id: chat_id}} }
|
|
||||||
let(:controller_path) do
|
let(:controller_path) do
|
||||||
route_name = Telegram::Bot::RoutesHelper.route_name_for_bot(bot)
|
route_name = Telegram::Bot::RoutesHelper.route_name_for_bot(bot)
|
||||||
Rails.application.routes.url_helpers.public_send("#{route_name}_path")
|
Rails.application.routes.url_helpers.public_send("#{route_name}_path")
|
||||||
@@ -14,7 +16,7 @@ RSpec.shared_context 'telegram/bot/integration' do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
let(:clear_session?) { described_class.respond_to?(:session_store) }
|
let(:clear_session?) { described_class.respond_to?(:session_store) }
|
||||||
before { described_class.session_store.clear if clear_session? }
|
before { described_class.session_store.try!(:clear) if clear_session? }
|
||||||
|
|
||||||
include Telegram::Bot::RSpec::ClientMatchers
|
include Telegram::Bot::RSpec::ClientMatchers
|
||||||
|
|
||||||
@@ -36,14 +38,48 @@ RSpec.shared_context 'telegram/bot/integration' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Matcher to check response. Make sure to define `let(:chat_id)`.
|
# Matcher to check response. Make sure to define `let(:chat_id)`.
|
||||||
def respond_with_message(expected)
|
def respond_with_message(expected = Regexp.new(''))
|
||||||
raise 'Define chat_id to use respond_with_message' unless defined?(chat_id)
|
raise 'Define chat_id to use respond_with_message' unless defined?(chat_id)
|
||||||
send_telegram_message(bot, expected, chat_id: chat_id)
|
send_telegram_message(bot, expected, chat_id: chat_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
RSpec.shared_context 'telegram/bot/callback_query', callback_query: true do
|
||||||
|
include_context 'telegram/bot/integration'
|
||||||
|
|
||||||
|
subject { -> { dispatch callback_query: payload } }
|
||||||
|
let(:payload) { {id: 11, from: from, message: message, data: data} }
|
||||||
|
let(:message) { {message_id: 22, chat: chat, text: 'message text'} }
|
||||||
|
|
||||||
|
# Matcher to check that origin message got edited.
|
||||||
|
def edit_current_message(type, options = {})
|
||||||
|
description = 'edit current message'
|
||||||
|
options = options.merge(
|
||||||
|
message_id: message[:message_id],
|
||||||
|
chat_id: chat_id,
|
||||||
|
)
|
||||||
|
Telegram::Bot::RSpec::ClientMatchers::MakeTelegramRequest.new(
|
||||||
|
bot, :"editMessage#{type.to_s.camelize}", description: description
|
||||||
|
).with(hash_including(options))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Matcher to check that callback query is answered.
|
||||||
|
def answer_callback_query(text = Regexp.new(''), options = {})
|
||||||
|
description = "answer callback query with #{text.inspect}"
|
||||||
|
text = a_string_matching(text) if text.is_a?(Regexp)
|
||||||
|
options = options.merge(
|
||||||
|
callback_query_id: payload[:id],
|
||||||
|
text: text,
|
||||||
|
)
|
||||||
|
Telegram::Bot::RSpec::ClientMatchers::MakeTelegramRequest.new(
|
||||||
|
bot, :answerCallbackQuery, description: description
|
||||||
|
).with(hash_including(options))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
if config.respond_to?(:include_context)
|
if config.respond_to?(:include_context)
|
||||||
config.include_context 'telegram/bot/integration', :telegram_bot
|
config.include_context 'telegram/bot/integration', :telegram_bot
|
||||||
|
config.include_context 'telegram/bot/callback_query', :telegram_bot, :callback_query
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ module Telegram
|
|||||||
|
|
||||||
autoload :TypedUpdate, 'telegram/bot/updates_controller/typed_update'
|
autoload :TypedUpdate, 'telegram/bot/updates_controller/typed_update'
|
||||||
|
|
||||||
PAYLOAD_TYPES = %w(
|
PAYLOAD_TYPES = %w[
|
||||||
message
|
message
|
||||||
edited_message
|
edited_message
|
||||||
channel_post
|
channel_post
|
||||||
@@ -86,7 +86,9 @@ module Telegram
|
|||||||
inline_query
|
inline_query
|
||||||
chosen_inline_result
|
chosen_inline_result
|
||||||
callback_query
|
callback_query
|
||||||
).freeze
|
shipping_query
|
||||||
|
pre_checkout_query
|
||||||
|
].freeze
|
||||||
CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
|
CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
|
||||||
CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)")
|
CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)")
|
||||||
|
|
||||||
@@ -169,15 +171,21 @@ module Telegram
|
|||||||
# Returns array `[is_command?, action, args]`.
|
# Returns array `[is_command?, action, args]`.
|
||||||
def action_for_payload
|
def action_for_payload
|
||||||
if payload_type
|
if payload_type
|
||||||
send("action_for_#{payload_type}") || [false, payload_type, [payload]]
|
send("action_for_#{payload_type}") || action_for_default_payload
|
||||||
else
|
else
|
||||||
[false, :unsupported_payload_type, []]
|
[false, :unsupported_payload_type, []]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def action_for_default_payload
|
||||||
|
[false, payload_type, [payload]]
|
||||||
|
end
|
||||||
|
|
||||||
# If payload is a message with command, then returned action is an
|
# If payload is a message with command, then returned action is an
|
||||||
# action for this command.
|
# action for this command.
|
||||||
# Separate method, so it can be easily overriden (ex. MessageContext).
|
# Separate method, so it can be easily overriden (ex. MessageContext).
|
||||||
|
#
|
||||||
|
# This is not used for edited messages/posts. It process them as basic updates.
|
||||||
def action_for_message
|
def action_for_message
|
||||||
cmd, args = self.class.command_from_text(payload['text'], bot_username)
|
cmd, args = self.class.command_from_text(payload['text'], bot_username)
|
||||||
cmd &&= self.class.action_for_command(cmd)
|
cmd &&= self.class.action_for_command(cmd)
|
||||||
@@ -185,12 +193,6 @@ module Telegram
|
|||||||
end
|
end
|
||||||
alias_method :action_for_channel_post, :action_for_message
|
alias_method :action_for_channel_post, :action_for_message
|
||||||
|
|
||||||
# It doesn't extract commands from edited messages. Just process
|
|
||||||
# them as usual ones.
|
|
||||||
def action_for_edited_message
|
|
||||||
end
|
|
||||||
alias_method :action_for_edited_channel_post, :action_for_edited_message
|
|
||||||
|
|
||||||
def action_for_inline_query
|
def action_for_inline_query
|
||||||
[false, payload_type, [payload['query'], payload['offset']]]
|
[false, payload_type, [payload['query'], payload['offset']]]
|
||||||
end
|
end
|
||||||
@@ -208,6 +210,11 @@ module Telegram
|
|||||||
def action_missing(*)
|
def action_missing(*)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
PAYLOAD_TYPES.each do |type|
|
||||||
|
method = :"action_for_#{type}"
|
||||||
|
alias_method method, :action_for_default_payload unless instance_methods.include?(method)
|
||||||
|
end
|
||||||
|
|
||||||
ActiveSupport.run_load_hooks('telegram.bot.updates_controller', self)
|
ActiveSupport.run_load_hooks('telegram.bot.updates_controller', self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ module Telegram
|
|||||||
# because `data` param is controlled by client.
|
# because `data` param is controlled by client.
|
||||||
def action_for_callback_query
|
def action_for_callback_query
|
||||||
context, new_data = context_from_callback_query
|
context, new_data = context_from_callback_query
|
||||||
# binding.pry
|
|
||||||
if context
|
if context
|
||||||
action_name = "#{context}_callback_query"
|
action_name = "#{context}_callback_query"
|
||||||
[false, action_name, [new_data]] if action_method?(action_name)
|
[false, action_name, [new_data]] if action_method?(action_name)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ module Telegram
|
|||||||
Instrumentation.instrument(:respond_with, type: type) { super }
|
Instrumentation.instrument(:respond_with, type: type) { super }
|
||||||
end
|
end
|
||||||
|
|
||||||
%i(answer_callback_query answer_inline_query).each do |type|
|
%i[answer_callback_query answer_inline_query].each do |type|
|
||||||
define_method(type) do |*args|
|
define_method(type) do |*args|
|
||||||
Instrumentation.instrument(:respond_with, type: type) { super(*args) }
|
Instrumentation.instrument(:respond_with, type: type) { super(*args) }
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ module Telegram
|
|||||||
payload = event.payload
|
payload = event.payload
|
||||||
additions = UpdatesController.log_process_action(payload)
|
additions = UpdatesController.log_process_action(payload)
|
||||||
message = "Completed in #{event.duration.round}ms"
|
message = "Completed in #{event.duration.round}ms"
|
||||||
message << " (#{additions.join(' | ')})" unless additions.blank?
|
message << " (#{additions.join(' | ')})" if additions.present?
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,14 +7,11 @@ module Telegram
|
|||||||
|
|
||||||
include Session
|
include Session
|
||||||
|
|
||||||
included do
|
|
||||||
# As we use before_action context is cleared anyway,
|
|
||||||
# no matter we used it or not.
|
|
||||||
singleton_class.send :attr_reader, :context_handlers, :context_to_action
|
|
||||||
@context_handlers = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
def context_handlers
|
||||||
|
@_context_handlers ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
# Registers handler for context.
|
# Registers handler for context.
|
||||||
#
|
#
|
||||||
# context_handler :rename do |*|
|
# context_handler :rename do |*|
|
||||||
@@ -39,6 +36,8 @@ module Telegram
|
|||||||
context_handlers[context] = action || context
|
context_handlers[context] = action || context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_reader :context_to_action
|
||||||
|
|
||||||
# Use it to use context value as action name for all contexts
|
# Use it to use context value as action name for all contexts
|
||||||
# which miss handlers.
|
# which miss handlers.
|
||||||
# For security reasons it supports only action methods and will
|
# For security reasons it supports only action methods and will
|
||||||
@@ -59,10 +58,16 @@ module Telegram
|
|||||||
# according to previous request.
|
# according to previous request.
|
||||||
attr_reader :context
|
attr_reader :context
|
||||||
|
|
||||||
|
# Controller may have multiple sessions, let it be possible
|
||||||
|
# to select session for message context.
|
||||||
|
def message_context_session
|
||||||
|
session
|
||||||
|
end
|
||||||
|
|
||||||
# Fetches context and finds handler for it. If message has new command,
|
# Fetches context and finds handler for it. If message has new command,
|
||||||
# it has higher priority than contextual action.
|
# it has higher priority than contextual action.
|
||||||
def action_for_message
|
def action_for_message
|
||||||
val = session.delete(:context)
|
val = message_context_session.delete(:context)
|
||||||
@context = val && val.to_sym
|
@context = val && val.to_sym
|
||||||
super || context && begin
|
super || context && begin
|
||||||
handler = handler_for_context
|
handler = handler_for_context
|
||||||
@@ -72,7 +77,7 @@ module Telegram
|
|||||||
|
|
||||||
# Save context for the next request.
|
# Save context for the next request.
|
||||||
def save_context(context)
|
def save_context(context)
|
||||||
session[:context] = context
|
message_context_session[:context] = context
|
||||||
end
|
end
|
||||||
|
|
||||||
def handler_for_context
|
def handler_for_context
|
||||||
|
|||||||
@@ -38,6 +38,19 @@ module Telegram
|
|||||||
)
|
)
|
||||||
bot.answer_callback_query(params)
|
bot.answer_callback_query(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Edit message from callback query.
|
||||||
|
def edit_message(type, params = {})
|
||||||
|
params =
|
||||||
|
if message_id = payload['inline_message_id'] # rubocop:disable AssignmentInCondition
|
||||||
|
params.merge(inline_message_id: message_id)
|
||||||
|
elsif message = payload['message'] # rubocop:disable AssignmentInCondition
|
||||||
|
params.merge(chat_id: message['chat']['id'], message_id: message['message_id'])
|
||||||
|
else
|
||||||
|
raise 'Can not edit message without `inline_message_id` or `message`'
|
||||||
|
end
|
||||||
|
bot.public_send("edit_message_#{type}", params)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,19 +8,26 @@ module Telegram
|
|||||||
module Session
|
module Session
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
# Builds session with given key and optional store (default to session_store).
|
||||||
|
# This way it's easier to define multiple custom sessions,
|
||||||
|
# ex. one for group chat and one for user.
|
||||||
|
def build_session(key, store = session_store)
|
||||||
|
raise 'session_store is not configured' unless store
|
||||||
|
key ? SessionHash.new(store, key) : NullSessionHash.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def process_action(*)
|
def process_action(*)
|
||||||
super
|
super
|
||||||
ensure
|
ensure
|
||||||
session.commit
|
session.commit if @_session
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def session
|
def session
|
||||||
@_session ||= begin
|
@_session ||= self.class.build_session(session_key)
|
||||||
key = session_key
|
|
||||||
key ? SessionHash.new(self.class.session_store, key) : NullSessionHash.new
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def session_key
|
def session_key
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module Telegram
|
|||||||
module Bot
|
module Bot
|
||||||
class UpdatesController
|
class UpdatesController
|
||||||
module Testing
|
module Testing
|
||||||
IVARS_TO_KEEP = %i(@_session).freeze
|
IVARS_TO_KEEP = %i[@_session].freeze
|
||||||
|
|
||||||
# Perform multiple dispatches on same instance.
|
# Perform multiple dispatches on same instance.
|
||||||
def dispatch_again(bot = nil, update = nil)
|
def dispatch_again(bot = nil, update = nil)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ module Telegram
|
|||||||
end
|
end
|
||||||
rescue Interrupt
|
rescue Interrupt
|
||||||
@running = false
|
@running = false
|
||||||
rescue => e
|
rescue StandardError => e
|
||||||
logger.error { ([e.message] + e.backtrace).join("\n") } if logger
|
logger.error { ([e.message] + e.backtrace).join("\n") } if logger
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module Telegram
|
module Telegram
|
||||||
module Bot
|
module Bot
|
||||||
VERSION = '0.11.1'.freeze
|
VERSION = '0.12.4'.freeze
|
||||||
|
|
||||||
def self.gem_version
|
def self.gem_version
|
||||||
Gem::Version.new VERSION
|
Gem::Version.new VERSION
|
||||||
|
|||||||
13
spec/integration/requests/default_bot_spec.rb
Обычный файл
13
spec/integration/requests/default_bot_spec.rb
Обычный файл
@@ -0,0 +1,13 @@
|
|||||||
|
require 'integration_helper'
|
||||||
|
|
||||||
|
RSpec.describe DefaultBotController, :telegram_bot, type: :request do
|
||||||
|
describe '#start' do
|
||||||
|
subject { -> { dispatch_command :start } }
|
||||||
|
it { should respond_with_message 'from default' }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#load_session' do
|
||||||
|
subject { -> { dispatch_command :load_session } }
|
||||||
|
it { should_not raise_error }
|
||||||
|
end
|
||||||
|
end
|
||||||
9
spec/integration/requests/named_bot_spec.rb
Обычный файл
9
spec/integration/requests/named_bot_spec.rb
Обычный файл
@@ -0,0 +1,9 @@
|
|||||||
|
require 'integration_helper'
|
||||||
|
|
||||||
|
RSpec.describe NamedBotController, :telegram_bot, type: :request do
|
||||||
|
let(:bot) { Telegram.bots[:named] }
|
||||||
|
describe '#start' do
|
||||||
|
subject { -> { dispatch_command :start } }
|
||||||
|
it { should respond_with_message 'from named' }
|
||||||
|
end
|
||||||
|
end
|
||||||
14
spec/integration/requests/other_bot_spec.rb
Обычный файл
14
spec/integration/requests/other_bot_spec.rb
Обычный файл
@@ -0,0 +1,14 @@
|
|||||||
|
require 'integration_helper'
|
||||||
|
|
||||||
|
RSpec.describe OtherBotController, :telegram_bot, type: :request do
|
||||||
|
let(:bot) { Telegram.bots[:other] }
|
||||||
|
describe '#start' do
|
||||||
|
subject { -> { dispatch_command :start } }
|
||||||
|
it { should respond_with_message 'from other' }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#load_session' do
|
||||||
|
subject { -> { dispatch_command :load_session } }
|
||||||
|
it { should raise_error(/session_store is not configured/) }
|
||||||
|
end
|
||||||
|
end
|
||||||
76
spec/integration_helper.rb
Обычный файл
76
spec/integration_helper.rb
Обычный файл
@@ -0,0 +1,76 @@
|
|||||||
|
require 'telegram/bot/rspec/integration'
|
||||||
|
require 'action_controller'
|
||||||
|
require 'action_dispatch'
|
||||||
|
require 'action_dispatch/testing/integration'
|
||||||
|
|
||||||
|
require 'rails'
|
||||||
|
require 'telegram/bot/railtie'
|
||||||
|
require 'rspec/rails/adapters'
|
||||||
|
require 'rspec/rails/fixture_support'
|
||||||
|
require 'rspec/rails/example/rails_example_group'
|
||||||
|
require 'rspec/rails/example/request_example_group'
|
||||||
|
|
||||||
|
ENV['RAILS_ENV'] = 'test'
|
||||||
|
class TestApplication < Rails::Application
|
||||||
|
config.eager_load = false
|
||||||
|
config.log_level = :debug
|
||||||
|
config.action_dispatch.show_exceptions = false
|
||||||
|
secrets[:secret_key_base] = 'test'
|
||||||
|
secrets[:telegram] = {
|
||||||
|
bot: 'default_token',
|
||||||
|
bots: {
|
||||||
|
other: {token: 'other_token'},
|
||||||
|
named: {token: 'named_token', username: 'TestBot'},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
Rails.application.initialize!
|
||||||
|
|
||||||
|
# # Controllers
|
||||||
|
%w[default other named].each do |bot_name|
|
||||||
|
controller = Class.new(Telegram::Bot::UpdatesController) do
|
||||||
|
define_method :start do |*|
|
||||||
|
respond_with :message, text: "from #{bot_name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Object.const_set("#{bot_name}_bot_controller".camelize, controller)
|
||||||
|
end
|
||||||
|
|
||||||
|
[DefaultBotController, OtherBotController].each do |klass|
|
||||||
|
klass.class_eval do
|
||||||
|
use_session!
|
||||||
|
|
||||||
|
define_method :load_session do |*|
|
||||||
|
session[:test]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
DefaultBotController.session_store = :memory_store
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.include RSpec::Rails::RequestExampleGroup, type: :request
|
||||||
|
|
||||||
|
config.around type: :request do |ex|
|
||||||
|
begin
|
||||||
|
Telegram.reset_bots
|
||||||
|
Telegram::Bot::ClientStub.stub_all!
|
||||||
|
ex.run
|
||||||
|
ensure
|
||||||
|
Telegram.reset_bots
|
||||||
|
Telegram::Bot::ClientStub.stub_all!(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
config.before type: :request do
|
||||||
|
# Redefine routes before every example, so it does not depent on order.
|
||||||
|
Rails.application.routes.draw do
|
||||||
|
require 'telegram/bot/routes_helper'
|
||||||
|
extend Telegram::Bot::RoutesHelper
|
||||||
|
|
||||||
|
telegram_webhooks default: DefaultBotController,
|
||||||
|
other: OtherBotController,
|
||||||
|
named: NamedBotController
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -19,7 +19,7 @@ RSpec.describe Telegram::Bot::Client do
|
|||||||
|
|
||||||
it 'encodes nested hashes to json' do
|
it 'encodes nested hashes to json' do
|
||||||
expected = input.dup
|
expected = input.dup
|
||||||
%i(c d e).each { |x| expected[x] = expected[x].to_json }
|
%i[c d e].each { |x| expected[x] = expected[x].to_json }
|
||||||
should eq expected
|
should eq expected
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -35,7 +35,7 @@ RSpec.describe Telegram::Bot::Client do
|
|||||||
subject { described_class.new(*args) }
|
subject { described_class.new(*args) }
|
||||||
|
|
||||||
context 'when multiple args are given' do
|
context 'when multiple args are given' do
|
||||||
let(:args) { %w(secret superbot) }
|
let(:args) { %w[secret superbot] }
|
||||||
its(:token) { should eq args[0] }
|
its(:token) { should eq args[0] }
|
||||||
its(:username) { should eq args[1] }
|
its(:username) { should eq args[1] }
|
||||||
its(:base_uri) { should include args[0] }
|
its(:base_uri) { should include args[0] }
|
||||||
|
|||||||
@@ -45,9 +45,15 @@ RSpec.describe Telegram::Bot::ClientStub do
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'when username and token are given' do
|
context 'when username and token are given' do
|
||||||
let(:args) { %w(token superbot) }
|
let(:args) { %w[token superbot] }
|
||||||
its(:token) { should eq nil }
|
its(:token) { should eq args[0] }
|
||||||
its(:username) { should eq args[1] }
|
its(:username) { should eq args[1] }
|
||||||
end
|
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] }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,8 +22,13 @@ RSpec.describe Telegram::Bot::ConfigMethods do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#bot' do
|
describe '#bot' do
|
||||||
subject { registry.bot }
|
subject { -> { registry.bot } }
|
||||||
it { should eq registry.bots[:default] }
|
its(:call) { should eq registry.bots[:default] }
|
||||||
|
|
||||||
|
context 'when bot is not configured' do
|
||||||
|
let(:config) { super().except(:default) }
|
||||||
|
it { should raise_error(/bot is not configured/) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#bots' do
|
describe '#bots' do
|
||||||
@@ -67,6 +72,7 @@ RSpec.describe Telegram::Bot::ConfigMethods do
|
|||||||
|
|
||||||
context 'when not configured' do
|
context 'when not configured' do
|
||||||
let(:registry) { Object.new.tap { |x| x.extend described_class } }
|
let(:registry) { Object.new.tap { |x| x.extend described_class } }
|
||||||
|
before { hide_const('Rails') }
|
||||||
it { should eq({}) }
|
it { should eq({}) }
|
||||||
|
|
||||||
context 'in rails environment' do
|
context 'in rails environment' do
|
||||||
@@ -87,6 +93,12 @@ RSpec.describe Telegram::Bot::ConfigMethods do
|
|||||||
end
|
end
|
||||||
it { should include default: config[:bot] }
|
it { should include default: config[:bot] }
|
||||||
it { should include config[:bots] }
|
it { should include config[:bots] }
|
||||||
|
|
||||||
|
context 'on rails >5.1 (deep symbolized keys)' do
|
||||||
|
let(:secrets) { {telegram: config.deep_symbolize_keys} }
|
||||||
|
it { should include default: config[:bot] }
|
||||||
|
it { should include config[:bots] }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ require 'action_dispatch/testing/integration'
|
|||||||
|
|
||||||
RSpec.describe 'Integrations helper', :telegram_bot do
|
RSpec.describe 'Integrations helper', :telegram_bot do
|
||||||
include ActionDispatch::Integration::Runner
|
include ActionDispatch::Integration::Runner
|
||||||
def reset_template_assertion; end
|
def reset_template_assertion
|
||||||
|
end
|
||||||
|
|
||||||
let(:app) do
|
let(:app) do
|
||||||
app = Telegram::Bot::Middleware.new(bot, controller)
|
app = Telegram::Bot::Middleware.new(bot, controller)
|
||||||
@@ -38,7 +39,7 @@ RSpec.describe 'Integrations helper', :telegram_bot do
|
|||||||
|
|
||||||
describe '#dispatch_message' do
|
describe '#dispatch_message' do
|
||||||
subject { -> { dispatch_message "/start #{args.join ' '}", options } }
|
subject { -> { dispatch_message "/start #{args.join ' '}", options } }
|
||||||
let(:args) { %w(asd qwe) }
|
let(:args) { %w[asd qwe] }
|
||||||
let(:options) { {} }
|
let(:options) { {} }
|
||||||
it { should respond_with_message "Start: #{args.inspect}, option: " }
|
it { should respond_with_message "Start: #{args.inspect}, option: " }
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ RSpec.describe 'Integrations helper', :telegram_bot do
|
|||||||
it { should respond_with_message "Start: #{args.inspect}, option: " }
|
it { should respond_with_message "Start: #{args.inspect}, option: " }
|
||||||
|
|
||||||
context 'with args' do
|
context 'with args' do
|
||||||
let(:args) { %w(asd qwe) }
|
let(:args) { %w[asd qwe] }
|
||||||
it { should respond_with_message "Start: #{args.inspect}, option: " }
|
it { should respond_with_message "Start: #{args.inspect}, option: " }
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -68,4 +69,44 @@ RSpec.describe 'Integrations helper', :telegram_bot do
|
|||||||
it { should respond_with_message "Start: #{args[0...-1].inspect}, option: 1" }
|
it { should respond_with_message "Start: #{args[0...-1].inspect}, option: 1" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'callback queries', :callback_query do
|
||||||
|
let(:controller) do
|
||||||
|
Class.new(Telegram::Bot::UpdatesController) do
|
||||||
|
include Telegram::Bot::UpdatesController::CallbackQueryContext
|
||||||
|
|
||||||
|
def callback_query(data = nil, *)
|
||||||
|
answer_callback_query "data: #{data}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def context_callback_query(data = nil, *)
|
||||||
|
answer_callback_query "data: #{data}", extra: :param
|
||||||
|
end
|
||||||
|
|
||||||
|
def answer_and_edit_callback_query(data = nil, *)
|
||||||
|
answer_callback_query "data: #{data}"
|
||||||
|
edit_message :text, text: 'edited-text', extra: :param
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#callback_query' do
|
||||||
|
let(:data) { 'unknown:command' }
|
||||||
|
it { should answer_callback_query("data: #{data}") }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#context_callback_query' do
|
||||||
|
let(:data) { 'context:test:payload' }
|
||||||
|
it { should answer_callback_query('data: test:payload', extra: :param) }
|
||||||
|
it { should_not edit_current_message(:text) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#answer_and_edit_callback_query' do
|
||||||
|
let(:data) { 'answer_and_edit:test:payload' }
|
||||||
|
it do
|
||||||
|
should answer_callback_query(/test:payload/).
|
||||||
|
and edit_current_message(:text, text: /edited/, extra: :param)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ RSpec.describe Telegram::Bot::UpdatesController::MessageContext do
|
|||||||
|
|
||||||
context 'when message has new command' do
|
context 'when message has new command' do
|
||||||
let(:text) { '/action a s d' }
|
let(:text) { '/action a s d' }
|
||||||
its(:call) { should eq [:action_result, *%w(a s d)] }
|
its(:call) { should eq [:action_result, 'a', 's', 'd'] }
|
||||||
it { should change { session[:context] }.to nil }
|
it { should change { session[:context] }.to nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -48,4 +48,32 @@ RSpec.describe Telegram::Bot::UpdatesController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#edit_message' do
|
||||||
|
subject { controller.edit_message(type, params) }
|
||||||
|
let(:type) { :reply_markup }
|
||||||
|
|
||||||
|
it { expect { subject }.to raise_error(/Can not edit message without/) }
|
||||||
|
|
||||||
|
context 'when inline_message_id is present' do
|
||||||
|
let(:payload) { {inline_message_id: double(:message_id)} }
|
||||||
|
it 'sets inline_message_id' do
|
||||||
|
expect(bot).to receive("edit_message_#{type}").with(params.merge(
|
||||||
|
inline_message_id: payload[:inline_message_id],
|
||||||
|
)) { result }
|
||||||
|
should eq result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when message is present' do
|
||||||
|
let(:payload) { {message: super().merge(chat: chat)} }
|
||||||
|
it 'sets chat_id & message_id' do
|
||||||
|
expect(bot).to receive("edit_message_#{type}").with(params.merge(
|
||||||
|
message_id: payload[:message][:message_id],
|
||||||
|
chat_id: payload[:message][:chat]['id'],
|
||||||
|
)) { result }
|
||||||
|
should eq result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -54,4 +54,45 @@ RSpec.describe Telegram::Bot::UpdatesController::Session do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.build_session' do
|
||||||
|
subject { controller_class.build_session(key, *args) }
|
||||||
|
let(:key) {}
|
||||||
|
let(:args) { [] }
|
||||||
|
it { expect { subject }.to raise_error(/session_store is not configured/) }
|
||||||
|
|
||||||
|
shared_examples 'NullSessionHash when key is not present' do |store_proc|
|
||||||
|
it { should be_instance_of(described_class::NullSessionHash) }
|
||||||
|
|
||||||
|
context 'and key is present' do
|
||||||
|
let(:key) { :test_key }
|
||||||
|
it 'is valid SessionHash' do
|
||||||
|
expect(subject).to be_instance_of(described_class::SessionHash)
|
||||||
|
expect(subject.id).to eq key
|
||||||
|
expect(subject.instance_variable_get(:@store)).to be(instance_exec(&store_proc))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when store configured' do
|
||||||
|
before { controller_class.session_store = nil }
|
||||||
|
include_examples 'NullSessionHash when key is not present',
|
||||||
|
-> { controller_class.session_store }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when store is given' do
|
||||||
|
let(:args) { [double(:store)] }
|
||||||
|
include_examples 'NullSessionHash when key is not present', -> { args[0] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.session_store=' do
|
||||||
|
subject { ->(val) { controller_class.session_store = val } }
|
||||||
|
it 'casts to AS::Cache' do
|
||||||
|
expect { subject[:null_store] }.to change(controller_class, :session_store).
|
||||||
|
to(instance_of(ActiveSupport::Cache::NullStore))
|
||||||
|
expect { subject[nil] }.to change(controller_class, :session_store).
|
||||||
|
to(instance_of(ActiveSupport::Cache::MemoryStore))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ RSpec.describe Telegram::Bot::UpdatesController::TypedUpdate do
|
|||||||
|
|
||||||
context 'when `update` is a virtus model' do
|
context 'when `update` is a virtus model' do
|
||||||
subject { controller }
|
subject { controller }
|
||||||
%w(
|
%w[
|
||||||
message
|
message
|
||||||
inline_query
|
inline_query
|
||||||
chosen_inline_result
|
chosen_inline_result
|
||||||
).each do |type|
|
].each do |type|
|
||||||
context "with #{type}" do
|
context "with #{type}" do
|
||||||
type_class = Telegram::Bot::Types.const_get(type.camelize)
|
type_class = Telegram::Bot::Types.const_get(type.camelize)
|
||||||
let(:payload_type) { type }
|
let(:payload_type) { type }
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ RSpec.describe Telegram::Bot::UpdatesController do
|
|||||||
it { should eq [false, :unsupported_payload_type, []] }
|
it { should eq [false, :unsupported_payload_type, []] }
|
||||||
end
|
end
|
||||||
|
|
||||||
%w(message channel_post).each do |type|
|
%w[message channel_post].each do |type|
|
||||||
context 'when payload is edited_message' do
|
context "when payload is edited_#{type}" do
|
||||||
let(:payload_type) { "edited_#{type}" }
|
let(:payload_type) { "edited_#{type}" }
|
||||||
it { should eq [false, payload_type, [payload]] }
|
it { should eq [false, payload_type, [payload]] }
|
||||||
end
|
end
|
||||||
@@ -123,11 +123,11 @@ RSpec.describe Telegram::Bot::UpdatesController do
|
|||||||
context 'with command' do
|
context 'with command' do
|
||||||
let(:text) { "/test#{"@#{mention}" if mention} arg 1 2" }
|
let(:text) { "/test#{"@#{mention}" if mention} arg 1 2" }
|
||||||
let(:mention) {}
|
let(:mention) {}
|
||||||
it { should eq [true, 'test', %w(arg 1 2)] }
|
it { should eq [true, 'test', %w[arg 1 2]] }
|
||||||
|
|
||||||
context 'with mention' do
|
context 'with mention' do
|
||||||
let(:mention) { bot.username }
|
let(:mention) { bot.username }
|
||||||
it { should eq [true, 'test', %w(arg 1 2)] }
|
it { should eq [true, 'test', %w[arg 1 2]] }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with mention for other bot' do
|
context 'with mention for other bot' do
|
||||||
@@ -142,16 +142,32 @@ RSpec.describe Telegram::Bot::UpdatesController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
custom_payload_types = %w[
|
||||||
|
message
|
||||||
|
edited_message
|
||||||
|
channel_post
|
||||||
|
edited_channel_post
|
||||||
|
inline_query
|
||||||
|
chosen_inline_result
|
||||||
|
callback_query
|
||||||
|
]
|
||||||
|
(described_class::PAYLOAD_TYPES - custom_payload_types).each do |type|
|
||||||
|
context "when payload is #{type}" do
|
||||||
|
let(:payload_type) { type }
|
||||||
|
it { should eq [false, payload_type, [payload]] }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when `update` is a virtus model' do
|
context 'when `update` is a virtus model' do
|
||||||
subject { controller }
|
subject { controller }
|
||||||
let(:update) { Telegram::Bot::Types::Update.new(super()) }
|
let(:update) { Telegram::Bot::Types::Update.new(super()) }
|
||||||
%w(
|
%w[
|
||||||
message
|
message
|
||||||
inline_query
|
inline_query
|
||||||
chosen_inline_result
|
chosen_inline_result
|
||||||
).each do |type|
|
].each do |type|
|
||||||
context "with #{type}" do
|
context "with #{type}" do
|
||||||
type_class = Telegram::Bot::Types.const_get(type.camelize)
|
type_class = Telegram::Bot::Types.const_get(type.camelize)
|
||||||
let(:payload_type) { type }
|
let(:payload_type) { type }
|
||||||
@@ -178,7 +194,7 @@ RSpec.describe Telegram::Bot::UpdatesController do
|
|||||||
|
|
||||||
describe '#process' do
|
describe '#process' do
|
||||||
subject { -> { controller.process(:action, *args) } }
|
subject { -> { controller.process(:action, *args) } }
|
||||||
let(:args) { [:arg1, :arg2] }
|
let(:args) { %i[arg1 arg2] }
|
||||||
let(:controller_class) do
|
let(:controller_class) do
|
||||||
Class.new(described_class) do
|
Class.new(described_class) do
|
||||||
attr_reader :acted, :hooked
|
attr_reader :acted, :hooked
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
# coding: utf-8
|
|
||||||
lib = File.expand_path('../lib', __FILE__)
|
lib = File.expand_path('../lib', __FILE__)
|
||||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||||
require 'telegram/bot/version'
|
require 'telegram/bot/version'
|
||||||
@@ -20,9 +19,10 @@ Gem::Specification.new do |spec|
|
|||||||
|
|
||||||
spec.required_ruby_version = '~> 2.0'
|
spec.required_ruby_version = '~> 2.0'
|
||||||
|
|
||||||
spec.add_dependency 'activesupport', '>= 4.0', '< 5.1'
|
spec.add_dependency 'actionpack', '>= 4.0', '< 6.0'
|
||||||
spec.add_dependency 'actionpack', '>= 4.0', '< 5.1'
|
spec.add_dependency 'activesupport', '>= 4.0', '< 6.0'
|
||||||
spec.add_dependency 'httpclient', '~> 2.7'
|
spec.add_dependency 'httpclient', '~> 2.7'
|
||||||
|
|
||||||
spec.add_development_dependency 'bundler', '~> 1.11'
|
spec.add_development_dependency 'bundler', '~> 1.11'
|
||||||
spec.add_development_dependency 'rake', '~> 10.0'
|
spec.add_development_dependency 'rake', '~> 10.0'
|
||||||
end
|
end
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user