telegram-bot 0.9.0.alpha2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -1
- data/README.md +38 -2
- data/lib/telegram/bot.rb +1 -0
- data/lib/telegram/bot/client.rb +7 -0
- data/lib/telegram/bot/client_stub.rb +1 -1
- data/lib/telegram/bot/rspec.rb +7 -0
- data/lib/telegram/bot/rspec/client_matchers.rb +151 -0
- data/lib/telegram/bot/updates_controller.rb +7 -5
- data/lib/telegram/bot/updates_controller/callback_query_context.rb +1 -1
- data/lib/telegram/bot/updates_controller/rspec_helpers.rb +9 -1
- data/lib/telegram/bot/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aac15a5abf98702f6b99efe128d52133aca33901
|
4
|
+
data.tar.gz: 18c94d0ee3d719931ad7fe5c848c53afc7c55665
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: efb85157e8fdaceab87411e9e582f6f4ff22d480ec0a7ac134b4e89a9e43da4e8fd022af541a5280991c0d6b6c998722d424b224ab6fc3c0d68a02da8d9f4c5a
|
7
|
+
data.tar.gz: 31218771276a4a3d7920b68acfc6f3676d2778a96f4766a6d78db4a6d4c87a78e63e7fc7da8b117bd7f68042dae4377c28abe98d118e2c5dbbc92a71b41a327a
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# 0.9.0
|
2
2
|
|
3
|
+
- Async API requests.
|
3
4
|
- One more description for StaleChat error.
|
5
|
+
- edit_message_* methods.
|
6
|
+
- API methods from 2016-10-03 update
|
7
|
+
- Fix typo in module name: CallbackQueyContext -> CallbackQueryContext.
|
8
|
+
- Take `chat` from `message` for callback queries
|
9
|
+
- RSpec matchers.
|
4
10
|
|
5
11
|
# 0.8.0
|
6
12
|
|
data/README.md
CHANGED
@@ -17,7 +17,8 @@ Package contains:
|
|
17
17
|
- Middleware and routes helpers for production env.
|
18
18
|
- Poller with automatic source-reloader for development env.
|
19
19
|
- Rake tasks to update webhook urls.
|
20
|
-
- Async
|
20
|
+
- __[Async mode](#async-mode)__ for Telegram and/or Botan API.
|
21
|
+
Let the queue adapter handle network errors!
|
21
22
|
|
22
23
|
Here is sample [telegram_bot_app](https://github.com/telegram-bot-rb/telegram_bot_app)
|
23
24
|
with session, keyboards and inline queries.
|
@@ -132,6 +133,7 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
|
132
133
|
# do_smth_with(data)
|
133
134
|
|
134
135
|
# There are `chat` & `from` shortcut methods.
|
136
|
+
# For callback queries `chat` if taken from `message` when it's available.
|
135
137
|
response = from ? "Hello #{from['username']}!" : 'Hi there!'
|
136
138
|
# There is `respond_with` helper to set `chat_id` from received message:
|
137
139
|
respond_with :message, text: response
|
@@ -246,7 +248,7 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
|
|
246
248
|
end
|
247
249
|
```
|
248
250
|
|
249
|
-
You can use `
|
251
|
+
You can use `CallbackQueryContext` in the similar way to split `#callback_query` into
|
250
252
|
several specific methods. It doesn't require session support, and takes context from
|
251
253
|
data. If data has a prefix with colon like this `my_ctx:smth...` it'll call
|
252
254
|
`my_ctx_callback_query('smth...')` when there is such action method. Otherwise
|
@@ -334,6 +336,23 @@ There are also some helpers for controller tests.
|
|
334
336
|
Check out `telegram/bot/updates_controller/rspec_helpers` and
|
335
337
|
`telegram/bot/updates_controller/testing`.
|
336
338
|
|
339
|
+
Built-in RSpec matchers will help you to write tests fast:
|
340
|
+
|
341
|
+
```ruby
|
342
|
+
include Telegram::Bot::RSpec::ClientMatchers # no need if you already use controller herlpers
|
343
|
+
|
344
|
+
expect(&process_update).to send_telegram_message(bot, /msg regexp/, some: :option)
|
345
|
+
expect(&process_update).
|
346
|
+
to make_telegram_request(bot, :sendMessage, hash_including(text: 'msg text'))
|
347
|
+
|
348
|
+
# controller specs are even simplier:
|
349
|
+
describe '#start' do
|
350
|
+
subject { -> { dispatch_message '/start' } }
|
351
|
+
it { should respond_with_message(/Hello/) }
|
352
|
+
end
|
353
|
+
# See sample app for more examples.
|
354
|
+
```
|
355
|
+
|
337
356
|
### Deploying
|
338
357
|
|
339
358
|
Use `rake telegram:bot:set_webhook` to update webhook url for all configured bots.
|
@@ -384,11 +403,28 @@ you can implement your own worker class to handle such requests. This allows:
|
|
384
403
|
- Handle and retry network and other errors with queue adapter.
|
385
404
|
- ???
|
386
405
|
|
406
|
+
Instead of performing request instantly client serializes it, pushes to queue,
|
407
|
+
and immediately return control back. The job is then fetched with a worker
|
408
|
+
and real API request is performed. And this all is absolutely transparent for the app.
|
409
|
+
|
387
410
|
To enable this mode add `async: true` to bot's and botan's config.
|
388
411
|
For more information and custom configuration check out
|
389
412
|
[docs](http://www.rubydoc.info/github/telegram-bot-rb/telegram-bot/master/Telegram/Bot/Async) or
|
390
413
|
[source](https://github.com/telegram-bot-rb/telegram-bot/blob/master/lib/telegram/bot/async.rb).
|
391
414
|
|
415
|
+
If you want async mode, but don't want to setup queue, know that Rails 5 are shipped
|
416
|
+
with Async adapter by default, and there is
|
417
|
+
[Sucker Punch](https://github.com/brandonhilkert/sucker_punch) for Rails 4.
|
418
|
+
|
419
|
+
Be aware of some limitations:
|
420
|
+
|
421
|
+
- Client will not return API response.
|
422
|
+
- Sending files is not available in async mode [now],
|
423
|
+
because them can not be serialized.
|
424
|
+
|
425
|
+
To disable async mode for the block of code use `bot.async(false) { bot.send_photo }`.
|
426
|
+
Yes, it's threadsafe too.
|
427
|
+
|
392
428
|
## Development
|
393
429
|
|
394
430
|
After checking out the repo, run `bin/setup` to install dependencies.
|
data/lib/telegram/bot.rb
CHANGED
@@ -34,6 +34,7 @@ module Telegram
|
|
34
34
|
autoload :DebugClient, 'telegram/bot/debug_client'
|
35
35
|
autoload :Initializers, 'telegram/bot/initializers'
|
36
36
|
autoload :Middleware, 'telegram/bot/middleware'
|
37
|
+
autoload :RSpec, 'telegram/bot/rspec'
|
37
38
|
autoload :UpdatesController, 'telegram/bot/updates_controller'
|
38
39
|
autoload :UpdatesPoller, 'telegram/bot/updates_poller'
|
39
40
|
end
|
data/lib/telegram/bot/client.rb
CHANGED
@@ -63,21 +63,27 @@ module Telegram
|
|
63
63
|
%w(
|
64
64
|
answerCallbackQuery
|
65
65
|
answerInlineQuery
|
66
|
+
editMessageCaption
|
67
|
+
editMessageReplyMarkup
|
68
|
+
editMessageText
|
66
69
|
forwardMessage
|
67
70
|
getChat
|
68
71
|
getChatAdministrators
|
69
72
|
getChatMember
|
70
73
|
getChatMembersCount
|
71
74
|
getFile
|
75
|
+
getGameHighScores
|
72
76
|
getMe
|
73
77
|
getUpdates
|
74
78
|
getUserProfilePhotos
|
79
|
+
getWebhookInfo
|
75
80
|
kickChatMember
|
76
81
|
leaveChat
|
77
82
|
sendAudio
|
78
83
|
sendChatAction
|
79
84
|
sendContact
|
80
85
|
sendDocument
|
86
|
+
sendGame
|
81
87
|
sendLocation
|
82
88
|
sendMessage
|
83
89
|
sendPhoto
|
@@ -85,6 +91,7 @@ module Telegram
|
|
85
91
|
sendVenue
|
86
92
|
sendVideo
|
87
93
|
sendVoice
|
94
|
+
setGameScore
|
88
95
|
setWebhook
|
89
96
|
unbanChatMember
|
90
97
|
).each do |method|
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Telegram
|
2
|
+
module Bot
|
3
|
+
module RSpec
|
4
|
+
# Proxy that uses RSpec::Mocks::ArgListMatcher when it's available.
|
5
|
+
# Otherwise just performs `#==` match.
|
6
|
+
#
|
7
|
+
# Also allows to check argumets with custom block.
|
8
|
+
class ArgListMatcher
|
9
|
+
attr_reader :expected, :expected_proc
|
10
|
+
|
11
|
+
def initialize(*args, &block)
|
12
|
+
@expected_proc = block if block_given?
|
13
|
+
@expected =
|
14
|
+
if mocks_matcher?
|
15
|
+
::RSpec::Mocks::ArgumentListMatcher.new(*args)
|
16
|
+
else
|
17
|
+
args
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def args_match?(*actual)
|
22
|
+
if expected_proc
|
23
|
+
expected_proc[*actual]
|
24
|
+
true
|
25
|
+
elsif mocks_matcher?
|
26
|
+
expected.args_match?(*actual)
|
27
|
+
else
|
28
|
+
expected == actual
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def args
|
33
|
+
mocks_matcher? ? expected.args : expected
|
34
|
+
end
|
35
|
+
|
36
|
+
def mocks_matcher?
|
37
|
+
defined?(::RSpec::Mocks::ArgumentListMatcher)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
if mocks_matcher?
|
42
|
+
expected.expected_args.inspect
|
43
|
+
elsif expected_proc
|
44
|
+
'(proc matcher)'
|
45
|
+
else
|
46
|
+
expected.inspect
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Matchers to test requests to Telegram API.
|
52
|
+
#
|
53
|
+
# Complex matchers requires `rspec-mocks` to be installed.
|
54
|
+
module ClientMatchers
|
55
|
+
class MakeTelegramRequest < ::RSpec::Matchers::BuiltIn::BaseMatcher
|
56
|
+
EXPECTATION_TYPES = {
|
57
|
+
exactly: :==,
|
58
|
+
at_most: :>=,
|
59
|
+
at_least: :<=,
|
60
|
+
}.freeze
|
61
|
+
|
62
|
+
attr_reader :performed_requests, :description
|
63
|
+
|
64
|
+
def initialize(bot, action, description: nil)
|
65
|
+
@bot = bot
|
66
|
+
@action = action
|
67
|
+
@description = description || "make #{action} telegram request"
|
68
|
+
exactly(1)
|
69
|
+
end
|
70
|
+
|
71
|
+
def matches?(proc) # rubocop:disable AbcSize
|
72
|
+
raise ArgumentError, 'matcher only supports block expectations' unless proc.is_a?(Proc)
|
73
|
+
original_requests_count = bot.requests[action].count
|
74
|
+
proc.call
|
75
|
+
@performed_requests = bot.requests[action].drop(original_requests_count)
|
76
|
+
@matching_requests_count = performed_requests.count do |request|
|
77
|
+
!arg_list_matcher || arg_list_matcher.args_match?(request)
|
78
|
+
end
|
79
|
+
expectation_method = EXPECTATION_TYPES[expectation_type]
|
80
|
+
expected_number.public_send(expectation_method, matching_requests_count)
|
81
|
+
end
|
82
|
+
|
83
|
+
def with(*args, &block)
|
84
|
+
@arg_list_matcher = ArgListMatcher.new(*args, &block)
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
EXPECTATION_TYPES.each_key do |type|
|
89
|
+
define_method type do |count|
|
90
|
+
@expectation_type = type
|
91
|
+
@expected_number = Integer(count)
|
92
|
+
self
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def times
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def failure_message
|
101
|
+
"expected to #{base_message}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def failure_message_when_negated
|
105
|
+
"expected not to #{base_message}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def supports_block_expectations?
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
attr_reader :bot, :action, :expectation_type, :expected_number,
|
115
|
+
:arg_list_matcher, :matching_requests_count
|
116
|
+
|
117
|
+
def base_message
|
118
|
+
"make #{expectation_type.to_s.tr('_', ' ')} #{expected_number} " \
|
119
|
+
"#{bot.inspect}.#{action} requests,".tap do |msg|
|
120
|
+
msg << " with #{arg_list_matcher}," if arg_list_matcher
|
121
|
+
msg << " but made #{matching_requests_count}"
|
122
|
+
if performed_requests
|
123
|
+
actual_args = performed_requests.map(&:inspect).join(', ')
|
124
|
+
msg << ", and #{performed_requests.count} with #{actual_args}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Check that bot performed request to telegram API:
|
131
|
+
#
|
132
|
+
# expect { dispatch_message('Hi!') }.
|
133
|
+
# to make_telegram_request(bot, :sendMessage).
|
134
|
+
# with(text: 'Hello!', chat_id: chat_id)
|
135
|
+
def make_telegram_request(bot, action)
|
136
|
+
MakeTelegramRequest.new(bot, action)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Helper for asserting message is sent. Note that options are checked
|
140
|
+
# with `hash_including`. For strict checks use #make_telegram_request.
|
141
|
+
def send_telegram_message(bot, text = nil, options = {})
|
142
|
+
text = a_string_matching(text) if text.is_a?(Regexp)
|
143
|
+
options = options.merge(text: text) if text
|
144
|
+
description = "send telegram message #{text.inspect}"
|
145
|
+
MakeTelegramRequest.new(bot, :sendMessage, description: description).
|
146
|
+
with(hash_including(options))
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -50,14 +50,14 @@ module Telegram
|
|
50
50
|
# ControllerClass.new(bot, from: telegram_user, chat: telegram_chat).
|
51
51
|
# process(:help, *args)
|
52
52
|
#
|
53
|
-
class UpdatesController < AbstractController::Base
|
53
|
+
class UpdatesController < AbstractController::Base
|
54
54
|
abstract!
|
55
55
|
|
56
56
|
require 'telegram/bot/updates_controller/session'
|
57
57
|
require 'telegram/bot/updates_controller/log_subscriber'
|
58
58
|
require 'telegram/bot/updates_controller/instrumentation'
|
59
59
|
require 'telegram/bot/updates_controller/reply_helpers'
|
60
|
-
autoload :
|
60
|
+
autoload :CallbackQueryContext, 'telegram/bot/updates_controller/callback_query_context'
|
61
61
|
autoload :MessageContext, 'telegram/bot/updates_controller/message_context'
|
62
62
|
|
63
63
|
include AbstractController::Callbacks
|
@@ -140,10 +140,12 @@ module Telegram
|
|
140
140
|
@_payload, @_payload_type = payload_data
|
141
141
|
end
|
142
142
|
|
143
|
-
# Accessor to `'chat'` field of payload.
|
144
|
-
#
|
143
|
+
# Accessor to `'chat'` field of payload. Also tries `'chat'` in `'message'`
|
144
|
+
# when there is no such field in payload.
|
145
|
+
#
|
146
|
+
# Can be overriden with `chat` option for #initialize.
|
145
147
|
def chat
|
146
|
-
@_chat ||= payload &&
|
148
|
+
@_chat ||= payload.try! { |x| x['chat'] || x['message'] && x['message']['chat'] }
|
147
149
|
end
|
148
150
|
|
149
151
|
# Accessor to `'from'` field of payload. Can be overriden with `from` option
|
@@ -3,7 +3,7 @@ module Telegram
|
|
3
3
|
class UpdatesController
|
4
4
|
# Use separate actions for different callback queries.
|
5
5
|
# It doesn't require session support. Simply add `%{context}:` prefix to data.
|
6
|
-
module
|
6
|
+
module CallbackQueryContext
|
7
7
|
protected
|
8
8
|
|
9
9
|
# Uses #context_from_callback_query as context name.
|
@@ -14,11 +14,14 @@ RSpec.shared_context 'telegram/bot/updates_controller' do
|
|
14
14
|
let(:bot_name) { 'bot' }
|
15
15
|
let(:session) { controller.send(:session) }
|
16
16
|
|
17
|
+
include Telegram::Bot::RSpec::ClientMatchers
|
18
|
+
|
17
19
|
def dispatch(bot = self.bot, update = self.update)
|
18
20
|
controller.dispatch_again(bot, update)
|
19
21
|
end
|
20
22
|
|
21
|
-
def dispatch_message(text, options =
|
23
|
+
def dispatch_message(text, options = nil)
|
24
|
+
options ||= respond_to?(:default_message_options) ? default_message_options : {}
|
22
25
|
update = build_update :message, options.merge(text: text)
|
23
26
|
dispatch bot, update
|
24
27
|
end
|
@@ -34,4 +37,9 @@ RSpec.shared_context 'telegram/bot/updates_controller' do
|
|
34
37
|
else input
|
35
38
|
end
|
36
39
|
end
|
40
|
+
|
41
|
+
# Matcher to check response. Make sure to define `let(:chat_id)`.
|
42
|
+
def respond_with_message(expected)
|
43
|
+
send_telegram_message(bot, expected, chat_id: chat_id)
|
44
|
+
end
|
37
45
|
end
|
data/lib/telegram/bot/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: telegram-bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.0
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max Melentiev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -127,6 +127,8 @@ files:
|
|
127
127
|
- lib/telegram/bot/middleware.rb
|
128
128
|
- lib/telegram/bot/railtie.rb
|
129
129
|
- lib/telegram/bot/routes_helper.rb
|
130
|
+
- lib/telegram/bot/rspec.rb
|
131
|
+
- lib/telegram/bot/rspec/client_matchers.rb
|
130
132
|
- lib/telegram/bot/updates_controller.rb
|
131
133
|
- lib/telegram/bot/updates_controller/callback_query_context.rb
|
132
134
|
- lib/telegram/bot/updates_controller/instrumentation.rb
|
@@ -155,12 +157,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
155
157
|
version: '2.0'
|
156
158
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
159
|
requirements:
|
158
|
-
- - "
|
160
|
+
- - ">="
|
159
161
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
162
|
+
version: '0'
|
161
163
|
requirements: []
|
162
164
|
rubyforge_project:
|
163
|
-
rubygems_version: 2.
|
165
|
+
rubygems_version: 2.5.1
|
164
166
|
signing_key:
|
165
167
|
specification_version: 4
|
166
168
|
summary: Library for building Telegram Bots with Rails integration
|