telegram-bot 0.6.0 → 0.7.2

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