telegram-bot 0.9.0.alpha2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|