telegram-bot 0.6.0 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8bb8d3d1265ef9c71cb7dd500ca18ca98e8bdbc6
4
- data.tar.gz: 478eb9dde35d1ef651f1e889b66125017674c21d
3
+ metadata.gz: 5f435d8f9b79fbaf3bd5f5b2fce82c96c64be1d6
4
+ data.tar.gz: 29fbc530b43dca4b39d27ad69fd2ef4cade4ce15
5
5
  SHA512:
6
- metadata.gz: 9168018578598dc1ba6cefa04695ce7b2f98de74591cc82f301a8440c31fbef66cf5e7a51ec545f3937ef25a68278d573dfbc6aa22cbf54f9f79d2d520629f11
7
- data.tar.gz: 7489fc137abd149da5d7dc8819f736363753c3a6aeccc0c5c925d541656f846f03455a60d0c8db4553d7a2e5cfaf4edd5c3c18217dfdd7dd1b6cb48f4cc58270
6
+ metadata.gz: 50b1cf3174f36b9c0dbfa2eb6b33cdc5a9b9352b18c6b24c7e7acca0e5fb29bddfd50c0b4199f7f33fb71f91c771ffb0b0894c99d287664ba48d9dc39f7fa39a
7
+ data.tar.gz: 06ebe8d059e5ddf23e2648d5cdbdae3f2d85096789a7b93b893a9e96b19debcd6e338ac531a1a67282a7611b443487b33720caf52688938656dcedb793c0200a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ # 0.7.2
2
+
3
+ - Bot API 2.1
4
+ - Fixed possible crashes when payload type is not supported.
5
+ Provides empty session when neither `from` nor `chat` is defined.
6
+
7
+ # 0.7.0
8
+
9
+ - New Bot API methods.
10
+ - Helpers for inline keyboards, support for callback_query (with contextual actions).
11
+ - Changed action methods signature
12
+ - `#inline_query(payload) -> #inline_query(query, offset)`
13
+ - `#chosen_inline_result(payload)` -> `#chosen_inline_result(result_id, query)`
14
+ - MessageContext doesn't use second #process call to run contextual action.
15
+ - Botan.io metrics.
16
+
1
17
  # 0.6.0
2
18
 
3
19
  - StaleChat error.
data/README.md CHANGED
@@ -17,6 +17,10 @@ Package contains:
17
17
  - Poller with automatic source-reloader for development env.
18
18
  - Rake tasks to update webhook urls.
19
19
 
20
+ Here is sample [telegram_bot_app](https://github.com/telegram-bot-rb/telegram_bot_app)
21
+ with session, keyboards and inline queries.
22
+ Run it on your local machine in 1 minute!
23
+
20
24
  ## Installation
21
25
 
22
26
  Add this line to your application's Gemfile:
@@ -99,7 +103,8 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
99
103
  # use callbacks like in any other controllers
100
104
  around_action :with_locale
101
105
 
102
- # Every update can have one of: message, inline_query & chosen_inline_result.
106
+ # Every update can have one of: message, inline_query, chosen_inline_result,
107
+ # callback_query.
103
108
  # Define method with same name to respond to this updates.
104
109
  def message(message)
105
110
  # message can be also accessed via instance method
@@ -107,6 +112,13 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
107
112
  # store_message(message['text'])
108
113
  end
109
114
 
115
+ # This basic methods receives commonly used params:
116
+ #
117
+ # message(payload)
118
+ # inline_query(query, offset)
119
+ # chosen_inline_result(result_id, query)
120
+ # callback_query(data)
121
+
110
122
  # Define public methods to respond to commands.
111
123
  # Command arguments will be parsed and passed to the method.
112
124
  # Be sure to use splat args and default values to not get errors when
@@ -140,6 +152,8 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
140
152
  end
141
153
  ```
142
154
 
155
+ #### Optional typecasting
156
+
143
157
  You can enable typecasting of `update` with `telegram-bot-types` by including
144
158
  `Telegram::Bot::UpdatesPoller::TypedUpdate`:
145
159
 
@@ -153,6 +167,8 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
153
167
  end
154
168
  ```
155
169
 
170
+ #### Session
171
+
156
172
  There is support for sessions using `ActiveSupport::Cache` stores.
157
173
 
158
174
  ```ruby
@@ -186,6 +202,8 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
186
202
  end
187
203
  ```
188
204
 
205
+ #### Message context
206
+
189
207
  It's usual to support chain of messages like BotFather: after receiving command
190
208
  it asks you for additional argument. There is `MessageContext` for this:
191
209
 
@@ -200,8 +218,8 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
200
218
  end
201
219
 
202
220
  # register context handlers to handle this context
203
- context_handler :rename do |message|
204
- update_name message[:text]
221
+ context_handler :rename do |*words|
222
+ update_name words[0]
205
223
  reply_with :message, text: 'Renamed!'
206
224
  end
207
225
 
@@ -225,6 +243,14 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
225
243
  end
226
244
  ```
227
245
 
246
+ You can use `CallbackQueyContext` in the similar way to split `#callback_query` into
247
+ several specific methods. It doesn't require session support, and takes context from
248
+ data. If data has a prefix with colon like this `my_ctx:smth...` it'll call
249
+ `my_ctx_callback_query('smth...')` when there is such action method. Otherwise
250
+ it'll call `callback_query('my_ctx:smth...')` as usual.
251
+
252
+ #### Processesing updates
253
+
228
254
  To process update run:
229
255
 
230
256
  ```ruby
@@ -266,7 +292,8 @@ telegram_webhooks bot => TelegramChatController,
266
292
  # You can override this options or specify others:
267
293
  telegram_webhooks TelegramController, as: :my_webhook
268
294
  telegram_webhooks bot => [TelegramChatController, as: :chat_webhook],
269
- other_bot => [TelegramAuctionController,
295
+ other_bot => TelegramAuctionController,
296
+ admin_chat: TelegramAdminChatController
270
297
  ```
271
298
 
272
299
  For Rack applications you can also use `Telegram::Bot::Middleware` or just
@@ -309,6 +336,42 @@ Check out `telegram/bot/updates_controller/rspec_helpers` and
309
336
  Use `rake telegram:bot:set_webhook` to update webhook url for all configured bots.
310
337
  Certificate can be specified with `CERT=path/to/cert`.
311
338
 
339
+ ### Botan.io metrics
340
+
341
+ Initialize with `bot = Telegram::Bot::Client.new(token, botan: 'botan token')`
342
+ or just add `botan` key in `secrets.yml`:
343
+
344
+ ```yml
345
+ telegram:
346
+ bot:
347
+ token: bot_token
348
+ botan: botan_token
349
+ ```
350
+
351
+ Access to Botan client with `bot.botan`.
352
+ Use `bot.botan.track(event, uid, payload)` to track events.
353
+
354
+ There are some helpers for controllers in `Telegram::Bot::UpdatesController::Botan`:
355
+
356
+ ```ruby
357
+ class Telegram::WebhookController < Telegram::Bot::UpdatesController
358
+ include Telegram::Bot::UpdatesController::Botan
359
+
360
+ # This will track with event: action_name & data: payload
361
+ before_action :botan_track_action
362
+
363
+ def smth(*)
364
+ # This will track event for current user only when botan is configured.
365
+ botan_track :my_event, custom_data
366
+
367
+ # or get access directly to botan client:
368
+ botan.track(...)
369
+ end
370
+ end
371
+ ```
372
+
373
+ There is no stubbing for botan clients, so don't set botan token in tests.
374
+
312
375
  ## Development
313
376
 
314
377
  After checking out the repo, run `bin/setup` to install dependencies.
data/lib/telegram/bot.rb CHANGED
@@ -26,6 +26,7 @@ module Telegram
26
26
  end
27
27
  end
28
28
 
29
+ autoload :Botan, 'telegram/bot/botan'
29
30
  autoload :Client, 'telegram/bot/client'
30
31
  autoload :ClientStub, 'telegram/bot/client_stub'
31
32
  autoload :Middleware, 'telegram/bot/middleware'
@@ -0,0 +1,36 @@
1
+ module Telegram
2
+ module Bot
3
+ class Botan
4
+ TRACK_URI = 'https://api.botan.io/track'.freeze
5
+
6
+ class Error < Bot::Error; end
7
+
8
+ include DebugClient
9
+
10
+ attr_reader :client, :token
11
+
12
+ def initialize(token)
13
+ @client = HTTPClient.new
14
+ @token = token
15
+ end
16
+
17
+ def track(event, uid, payload = {})
18
+ res = http_request(
19
+ :post,
20
+ TRACK_URI,
21
+ {token: token, name: event, uid: uid},
22
+ payload.to_json,
23
+ )
24
+ status = res.status
25
+ return JSON.parse(res.body) if 300 > status
26
+ result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
27
+ err_msg = "#{res.reason}: #{result && result['info'] || '-'}"
28
+ raise Error, err_msg
29
+ end
30
+
31
+ def http_request(method, uri, query, body)
32
+ client.request(method, uri, query, body)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -2,14 +2,16 @@ require 'json'
2
2
  require 'httpclient'
3
3
  require 'active_support/core_ext/string/inflections'
4
4
  require 'active_support/core_ext/hash/keys'
5
+ require 'telegram/bot/debug_client'
5
6
 
6
7
  module Telegram
7
8
  module Bot
8
9
  class Client
9
- autoload :TypedResponse, 'telegram/bot/client/typed_response'
10
-
11
10
  URL_TEMPLATE = 'https://api.telegram.org/bot%s/'.freeze
12
11
 
12
+ autoload :TypedResponse, 'telegram/bot/client/typed_response'
13
+ include DebugClient
14
+
13
15
  class << self
14
16
  # Accepts different options to initialize bot.
15
17
  def wrap(input)
@@ -18,7 +20,7 @@ module Telegram
18
20
  when Array then input.map(&method(__callee__))
19
21
  when Hash then
20
22
  input = input.stringify_keys
21
- new input['token'], input['username']
23
+ new input['token'], input['username'], botan: input['botan']
22
24
  when Symbol
23
25
  Telegram.bots[input] or
24
26
  raise "Bot #{input} not configured, check Telegram.bots_config."
@@ -41,31 +43,14 @@ module Telegram
41
43
  end
42
44
  end
43
45
 
44
- attr_reader :client, :token, :username, :base_uri
46
+ attr_reader :client, :token, :username, :base_uri, :botan
45
47
 
46
- def initialize(token, username = nil)
48
+ def initialize(token, username = nil, botan: nil)
47
49
  @client = HTTPClient.new
48
50
  @token = token
49
51
  @username = username
50
52
  @base_uri = format URL_TEMPLATE, token
51
- end
52
-
53
- def debug!(dev = STDOUT)
54
- if block_given?
55
- begin
56
- old_dev = client.debug_dev
57
- client.debug_dev = dev
58
- yield
59
- ensure
60
- client.debug_dev = old_dev
61
- end
62
- else
63
- client.debug_dev = dev
64
- end
65
- end
66
-
67
- def debug_off!
68
- client.debug_dev = nil
53
+ @botan = Botan.new(botan) if botan
69
54
  end
70
55
 
71
56
  def request(action, body = {}) # rubocop:disable PerceivedComplexity
@@ -83,22 +68,32 @@ module Telegram
83
68
  end
84
69
 
85
70
  %w(
71
+ answerCallbackQuery
86
72
  answerInlineQuery
87
73
  forwardMessage
74
+ getChat
75
+ getChatAdministrators
76
+ getChatMember
77
+ getChatMembersCount
88
78
  getFile
89
79
  getMe
90
80
  getUpdates
91
81
  getUserProfilePhotos
82
+ kickChatMember
83
+ leaveChat
92
84
  sendAudio
93
85
  sendChatAction
86
+ sendContact
94
87
  sendDocument
95
88
  sendLocation
96
89
  sendMessage
97
90
  sendPhoto
98
91
  sendSticker
92
+ sendVenue
99
93
  sendVideo
100
94
  sendVoice
101
95
  setWebhook
96
+ unbanChatMember
102
97
  ).each do |method|
103
98
  define_method(method.underscore) { |*args| request(method, *args) }
104
99
  end
@@ -15,7 +15,8 @@ module Telegram
15
15
  end
16
16
 
17
17
  class << self
18
- # Makes all
18
+ # Any call to Client.new will return ClientStub instance when `enabled` is true.
19
+ # Can be used with a block.
19
20
  def stub_all!(enabled = true)
20
21
  Client.extend(StubbedConstructor) unless Client < StubbedConstructor
21
22
  return @_stub_all = enabled unless block_given?
@@ -0,0 +1,23 @@
1
+ module Telegram
2
+ module Bot
3
+ module DebugClient
4
+ def debug!(dev = STDOUT)
5
+ if block_given?
6
+ begin
7
+ old_dev = client.debug_dev
8
+ client.debug_dev = dev
9
+ yield
10
+ ensure
11
+ client.debug_dev = old_dev
12
+ end
13
+ else
14
+ client.debug_dev = dev
15
+ end
16
+ end
17
+
18
+ def debug_off!
19
+ client.debug_dev = nil
20
+ end
21
+ end
22
+ end
23
+ end
@@ -50,13 +50,16 @@ 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 # rubocop:disable ClassLength
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
+ require 'telegram/bot/updates_controller/reply_helpers'
60
+ autoload :CallbackQueyContext, 'telegram/bot/updates_controller/callback_query_context'
59
61
  autoload :MessageContext, 'telegram/bot/updates_controller/message_context'
62
+ autoload :Botan, 'telegram/bot/updates_controller/botan'
60
63
 
61
64
  include AbstractController::Callbacks
62
65
  # Redefine callbacks with default terminator.
@@ -70,6 +73,7 @@ module Telegram
70
73
  end
71
74
 
72
75
  include AbstractController::Translation
76
+ include ReplyHelpers
73
77
  prepend Instrumentation
74
78
  extend Session::ConfigMethods
75
79
 
@@ -79,6 +83,8 @@ module Telegram
79
83
  message
80
84
  inline_query
81
85
  chosen_inline_result
86
+ callback_query
87
+ edited_message
82
88
  ).freeze
83
89
  CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
84
90
  CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)")
@@ -138,13 +144,13 @@ module Telegram
138
144
  # Accessor to `'chat'` field of payload. Can be overriden with `chat` option
139
145
  # for #initialize.
140
146
  def chat
141
- @_chat || payload && payload['chat']
147
+ @_chat ||= payload && payload['chat']
142
148
  end
143
149
 
144
150
  # Accessor to `'from'` field of payload. Can be overriden with `from` option
145
151
  # for #initialize.
146
152
  def from
147
- @_from || payload && payload['from']
153
+ @_from ||= payload && payload['from']
148
154
  end
149
155
 
150
156
  # Processes current update.
@@ -154,47 +160,47 @@ module Telegram
154
160
  end
155
161
 
156
162
  # Calculates action name and args for payload.
157
- # If payload is a message with command, then returned action is an
158
- # action for this command. Otherwise it's the same as payload type.
163
+ # Uses `action_for_#{payload_type}` methods.
164
+ # If this method doesn't return anything
165
+ # it uses fallback with action same as payload type.
159
166
  # Returns array `[is_command?, action, args]`.
160
167
  def action_for_payload
161
- case payload_type
162
- when 'message'
163
- cmd, args = self.class.command_from_text(payload['text'], bot_username)
164
- cmd &&= self.class.action_for_command(cmd)
165
- [true, cmd, args] if cmd
166
- end || [false, payload_type, [payload]]
168
+ if payload_type
169
+ send("action_for_#{payload_type}") || [false, payload_type, [payload]]
170
+ else
171
+ [false, :unsupported_payload_type, []]
172
+ end
167
173
  end
168
174
 
169
- # Silently ignore unsupported messages.
170
- # Params are `action, *args`.
171
- def action_missing(*)
175
+ # If payload is a message with command, then returned action is an
176
+ # action for this command.
177
+ # Separate method, so it can be easily overriden (ex. MessageContext).
178
+ def action_for_message
179
+ cmd, args = self.class.command_from_text(payload['text'], bot_username)
180
+ cmd &&= self.class.action_for_command(cmd)
181
+ [true, cmd, args] if cmd
182
+ end
183
+
184
+ # It doesn't extract commands from edited messages. Just process
185
+ # them as usual ones.
186
+ def action_for_edited_message
172
187
  end
173
188
 
174
- # Helper to call bot's `send_#{type}` method with already set `chat_id` and
175
- # `reply_to_message_id`:
176
- #
177
- # reply_with :message, text: 'Hello!'
178
- # reply_with :message, text: '__Hello!__', parse_mode: :Markdown
179
- # reply_with :photo, photo: File.open(photo_to_send), caption: "It's incredible!"
180
- def reply_with(type, params)
181
- method = "send_#{type}"
182
- chat = self.chat
183
- payload = self.payload
184
- params = params.merge(
185
- chat_id: (chat && chat['id'] or raise 'Can not reply_with when chat is not present'),
186
- reply_to_message: payload && payload['message_id'],
187
- )
188
- bot.public_send(method, params)
189
+ def action_for_inline_query
190
+ [false, payload_type, [payload['query'], payload['offset']]]
189
191
  end
190
192
 
191
- # Same as reply_with, but for inline queries.
192
- def answer_inline_query(results, params = {})
193
- params = params.merge(
194
- inline_query_id: payload['id'],
195
- results: results,
196
- )
197
- bot.answer_inline_query(params)
193
+ def action_for_chosen_inline_result
194
+ [false, payload_type, [payload['result_id'], payload['query']]]
195
+ end
196
+
197
+ def action_for_callback_query
198
+ [false, payload_type, [payload['data']]]
199
+ end
200
+
201
+ # Silently ignore unsupported messages.
202
+ # Params are `action, *args`.
203
+ def action_missing(*)
198
204
  end
199
205
 
200
206
  ActiveSupport.run_load_hooks('telegram.bot.updates_controller', self)
@@ -0,0 +1,33 @@
1
+ module Telegram
2
+ module Bot
3
+ class UpdatesController
4
+ # Helpers for botan.io metrics.
5
+ module Botan
6
+ class MissingFrom < Error; end
7
+
8
+ protected
9
+
10
+ def botan
11
+ @botan ||= bot.try!(:botan)
12
+ end
13
+
14
+ # Track custom event for user taken from `from` field:
15
+ #
16
+ # botan_track :my_event, {data: :val}
17
+ #
18
+ def botan_track(event, data = {})
19
+ raise MissingFrom, 'Can not track without user' unless from
20
+ botan.try! { |x| x.track(event, from['id'], data) }
21
+ end
22
+
23
+ # Track current action and payload for current user. Best used with `before_action`:
24
+ #
25
+ # before_action :botan_track_action
26
+ #
27
+ def botan_track_action
28
+ botan_track(action_name, payload)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Telegram
2
+ module Bot
3
+ class UpdatesController
4
+ # Use separate actions for different callback queries.
5
+ # It doesn't require session support. Simply add `%{context}:` prefix to data.
6
+ module CallbackQueyContext
7
+ protected
8
+
9
+ # Uses #context_from_callback_query as context name.
10
+ # If context is present checks if `%context%_callback_query` is valid
11
+ # action method and returns it if so. Context is stripped from data
12
+ # in this case. Otherwise returns `super`.
13
+ #
14
+ # It wont raise ActionNotFound as MessageContext does,
15
+ # because `data` param is controlled by client.
16
+ def action_for_callback_query
17
+ context, new_data = context_from_callback_query
18
+ # binding.pry
19
+ if context
20
+ action_name = "#{context}_callback_query"
21
+ [false, action_name, [new_data]] if action_method?(action_name)
22
+ end || super
23
+ end
24
+
25
+ def context_from_callback_query
26
+ data = payload['data']
27
+ return unless data
28
+ parts = data.split(':', 2)
29
+ parts if parts.size > 1
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -39,6 +39,12 @@ module Telegram
39
39
  Instrumentation.instrument(:reply_with, type: type) { super }
40
40
  end
41
41
 
42
+ %i(answer_callback_query answer_inline_query).each do |type|
43
+ define_method(type) do |*args|
44
+ Instrumentation.instrument(:reply_with, type: type) { super(*args) }
45
+ end
46
+ end
47
+
42
48
  private
43
49
 
44
50
  # A hook invoked every time a before callback is halted.
@@ -10,7 +10,6 @@ module Telegram
10
10
  included do
11
11
  # As we use before_action context is cleared anyway,
12
12
  # no matter we used it or not.
13
- before_action :fetch_context
14
13
  singleton_class.send :attr_reader, :context_handlers, :context_to_action
15
14
  @context_handlers = {}
16
15
  end
@@ -18,26 +17,26 @@ module Telegram
18
17
  module ClassMethods
19
18
  # Registers handler for context.
20
19
  #
21
- # context_handler :rename do |message|
22
- # resource.update!(name: message['text'])
20
+ # context_handler :rename do |*|
21
+ # resource.update!(name: payload['text'])
23
22
  # end
24
23
  #
25
24
  # # To run other action with all the callbacks:
26
- # context_handler :rename do |message|
27
- # process(:rename, *m['text'].split)
25
+ # context_handler :rename do |*words|
26
+ # process(:rename, *words)
28
27
  # end
29
28
  #
30
29
  # # Or just
31
30
  # context_handler :rename, :your_action_to_call
32
31
  # context_handler :rename # to call :rename
33
32
  #
34
- # # For messages without context use this instead of `message` method:
35
- # context_handle do |message|
36
- # end
37
- #
38
33
  def context_handler(context = nil, action = nil, &block)
39
34
  context &&= context.to_sym
40
- context_handlers[context] = block || action || context
35
+ if block
36
+ action = "_context_handler_#{context}"
37
+ define_method(action, &block)
38
+ end
39
+ context_handlers[context] = action || context
41
40
  end
42
41
 
43
42
  # Use it to use context value as action name for all contexts
@@ -49,20 +48,9 @@ module Telegram
49
48
  end
50
49
  end
51
50
 
52
- # Finds handler for current context and processes message with it.
53
- def message(message)
54
- handler = handler_for_context
55
- return unless handler
56
- if handler.respond_to?(:call)
57
- instance_exec(message, &handler)
58
- else
59
- process(handler, *message['text'].split)
60
- end
61
- end
62
-
63
51
  # Action to clear context.
64
52
  def cancel
65
- # Context is already cleared in before_action
53
+ # Context is already cleared in action_for_message
66
54
  end
67
55
 
68
56
  private
@@ -71,11 +59,15 @@ module Telegram
71
59
  # according to previous request.
72
60
  attr_reader :context
73
61
 
74
- # Fetches and removes context from session.
75
- def fetch_context
62
+ # Fetches context and finds handler for it. If message has new command,
63
+ # it has higher priority than contextual action.
64
+ def action_for_message
76
65
  val = session.delete(:context)
77
66
  @context = val && val.to_sym
78
- true # TODO: remove in Rails 5.0
67
+ super || context && begin
68
+ handler = handler_for_context
69
+ [true, handler, payload['text'].try!(:split) || []] if handler
70
+ end
79
71
  end
80
72
 
81
73
  # Save context for the next request.
@@ -0,0 +1,42 @@
1
+ module Telegram
2
+ module Bot
3
+ class UpdatesController
4
+ module ReplyHelpers
5
+ # Helper to call bot's `send_#{type}` method with already set `chat_id` and
6
+ # `reply_to_message_id`:
7
+ #
8
+ # reply_with :message, text: 'Hello!'
9
+ # reply_with :message, text: '__Hello!__', parse_mode: :Markdown
10
+ # reply_with :photo, photo: File.open(photo_to_send), caption: "It's incredible!"
11
+ def reply_with(type, params)
12
+ method = "send_#{type}"
13
+ chat = self.chat
14
+ payload = self.payload
15
+ params = params.merge(
16
+ chat_id: (chat && chat['id'] or raise 'Can not reply_with when chat is not present'),
17
+ reply_to_message: payload && payload['message_id'],
18
+ )
19
+ bot.public_send(method, params)
20
+ end
21
+
22
+ # Same as reply_with, but for inline queries.
23
+ def answer_inline_query(results, params = {})
24
+ params = params.merge(
25
+ inline_query_id: payload['id'],
26
+ results: results,
27
+ )
28
+ bot.answer_inline_query(params)
29
+ end
30
+
31
+ # Same as reply_with, but for callback queries.
32
+ def answer_callback_query(text, params = {})
33
+ params = params.merge(
34
+ callback_query_id: payload['id'],
35
+ text: text,
36
+ )
37
+ bot.answer_callback_query(params)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -17,11 +17,15 @@ module Telegram
17
17
  protected
18
18
 
19
19
  def session
20
- @_session ||= SessionHash.new(self.class.session_store, session_key)
20
+ @_session ||= begin
21
+ key = session_key
22
+ key ? SessionHash.new(self.class.session_store, key) : NullSessionHash.new
23
+ end
21
24
  end
22
25
 
23
26
  def session_key
24
- "#{bot.username}:#{from ? "from:#{from['id']}" : "chat:#{chat['id']}"}"
27
+ subject = from || chat
28
+ "#{bot.username}:#{subject['id']}" if subject
25
29
  end
26
30
 
27
31
  # Rack::Session::Abstract::SessionHash is taken to provide lazy loading.
@@ -59,6 +63,18 @@ module Telegram
59
63
  end
60
64
  end
61
65
 
66
+ class NullSessionHash < Session::SessionHash
67
+ def initialize
68
+ @data = {}
69
+ @loaded = true
70
+ @exists = true
71
+ end
72
+
73
+ alias_method :destroy, :clear
74
+ alias_method :load!, :id
75
+ alias_method :commit, :id
76
+ end
77
+
62
78
  module ConfigMethods
63
79
  delegate :session_store, to: :config
64
80
 
@@ -27,19 +27,7 @@ module Telegram
27
27
 
28
28
  # Stubs session.
29
29
  def session
30
- @_session ||= Testing::SessionHash.new
31
- end
32
-
33
- class SessionHash < Session::SessionHash
34
- def initialize
35
- @data = {}
36
- @loaded = true
37
- @exists = true
38
- end
39
-
40
- alias_method :destroy, :clear
41
- alias_method :load!, :id
42
- alias_method :commit, :id
30
+ @_session ||= Session::NullSessionHash.new
43
31
  end
44
32
  end
45
33
  end
@@ -1,6 +1,6 @@
1
1
  module Telegram
2
2
  module Bot
3
- VERSION = '0.6.0'.freeze
3
+ VERSION = '0.7.2'.freeze
4
4
 
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION
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.6.0
4
+ version: 0.7.2
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-03-24 00:00:00.000000000 Z
11
+ date: 2016-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -102,17 +102,22 @@ files:
102
102
  - bin/setup
103
103
  - lib/tasks/telegram-bot.rake
104
104
  - lib/telegram/bot.rb
105
+ - lib/telegram/bot/botan.rb
105
106
  - lib/telegram/bot/client.rb
106
107
  - lib/telegram/bot/client/typed_response.rb
107
108
  - lib/telegram/bot/client_stub.rb
108
109
  - lib/telegram/bot/config_methods.rb
110
+ - lib/telegram/bot/debug_client.rb
109
111
  - lib/telegram/bot/middleware.rb
110
112
  - lib/telegram/bot/railtie.rb
111
113
  - lib/telegram/bot/routes_helper.rb
112
114
  - lib/telegram/bot/updates_controller.rb
115
+ - lib/telegram/bot/updates_controller/botan.rb
116
+ - lib/telegram/bot/updates_controller/callback_query_context.rb
113
117
  - lib/telegram/bot/updates_controller/instrumentation.rb
114
118
  - lib/telegram/bot/updates_controller/log_subscriber.rb
115
119
  - lib/telegram/bot/updates_controller/message_context.rb
120
+ - lib/telegram/bot/updates_controller/reply_helpers.rb
116
121
  - lib/telegram/bot/updates_controller/rspec_helpers.rb
117
122
  - lib/telegram/bot/updates_controller/session.rb
118
123
  - lib/telegram/bot/updates_controller/testing.rb
@@ -140,9 +145,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
145
  version: '0'
141
146
  requirements: []
142
147
  rubyforge_project:
143
- rubygems_version: 2.4.6
148
+ rubygems_version: 2.5.1
144
149
  signing_key:
145
150
  specification_version: 4
146
151
  summary: Library for building Telegram Bots with Rails integration
147
152
  test_files: []
148
- has_rdoc: