telegram_workflow 1.4.0 → 1.5.0

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
  SHA256:
3
- metadata.gz: b9e4c2c5c0d7ee686ea3961a0af8c50ae6511c26cbf5ee704f1c3e27dad82e65
4
- data.tar.gz: 80ce424f3627fb918810020c7811dbbe98713bd87e59280815ca6f7e37d6550f
3
+ metadata.gz: f1247dbbe0bc252c17270b86bc003ebd72f4dac8990a3251817cd99ac75fa85e
4
+ data.tar.gz: 1d1983843ac32770201da9e3c9475a3394c7f37b19e80ed89f8b8da66d5be2d7
5
5
  SHA512:
6
- metadata.gz: 9ff7be3ead93112c1cb5debbe5f8a62399948057b445b896c4d3a585b660dfa9707569a5e214faa5fa75230b180db76b27167ae4c097ff687f7cec297dda5fa8
7
- data.tar.gz: 04b307ae0e32fc0a5a76019372d8fed3fc5e70229818a90dbc134da3a5a9da1ce7d01e5ee50f3c41f905931f2fe5157b8bcff5d4c93a59009d9c6ce29dc4ea13
6
+ metadata.gz: 6dbf47965a1c443b3e433d649424c2b8d3492da048c978c8bbdae94c79e302030751ccf0ef8f0591d7ff51573cb47907c39626561357d246b9bfcdbb53124d85
7
+ data.tar.gz: 9ed28f1dc116340f93073d7b164904d352d23547392f4897bc7ba9ea1895de400851d1694f6adea815f71516c4d57f611197ddb3b944bfdcf47377ae7b2d59fe
data/README.md CHANGED
@@ -110,289 +110,9 @@ end
110
110
  Here you can see an example of redirection to another action.
111
111
  Having a bot logic split over such small actions improves code maintanability and allows to follow SRP.
112
112
 
113
- ## Global Objects
113
+ ## Documentation
114
114
 
115
- Each action has a set of globally accessible objects:
116
-
117
- Object | Description
118
- -------|------------
119
- [params](README.md#params) | Instance of `TelegramWorkflow::Params`.
120
- [client](README.md#client) | Instance of `TelegramWorkflow::Client`. Can be [customized](README.md#client-customization).
121
- [session](README.md#session) | Persistent store to keep session data. Instance of `Hash`.
122
- [flash](README.md#flash) | Temporary store to keep some data between different steps. Instance of `Hash`.
123
-
124
- ## Public API
125
-
126
- ### params
127
-
128
- The `params` object encapsulates the logic to parse Telegram params.
129
- It implements useful methods, like `message_text`, `callback_data` or `deep_link_payload` to fetch user submitted data from the params.
130
-
131
- ### client
132
-
133
- This is an instance of `TelegramWorkflow::Client` class, which implements a complete Telegram Bot API.
134
- The methods to access the API are called after raw Telegram API methods.
135
- For example, if you needed to call a [sendLocation](https://core.telegram.org/bots/api#sendlocation) method, you would use the following code:
136
-
137
- ```ruby
138
- client.send_location latitude: 40.748, longitude: -73.985, live_period: 120
139
- ```
140
-
141
- `chat_id` parameter should be omitted.
142
-
143
- Use `TelegramWorkflow::InputFile` to upload a file:
144
-
145
- ```ruby
146
- client.send_photo photo: TelegramWorkflow::InputFile.new("/Users/telegram/images/image1.jpg")
147
- ```
148
-
149
- `filename` and `content_type` fields can be customized:
150
-
151
- ```ruby
152
- file = StringIO.new("hello!")
153
- client.send_document document: TelegramWorkflow::InputFile.new(file, filename: "hello.txt")
154
- ```
155
-
156
- ### session
157
-
158
- This is a persistent store to save the data associated with a user, e.g. current user's id, some settings or anything you would store in a session in a regular web application.
159
-
160
- ### flash
161
-
162
- This is a temporary store to save the data between the steps. The data persists while redirecting between the steps, but **gets deleted automatically when redirecting to another action**.
163
-
164
- ### redirect_to(action_or_class, flash_params = {})
165
-
166
- As you already know, this function allows to build complex workflows by redirecting between actions and steps.
167
- The function expects either a symbol or instance of `Class` as a first argument. Passing a symbol will redirect to another step inside the current action. Passing instance of `Class` will redirect to another action.
168
-
169
- ```ruby
170
- # redirect to a step
171
- redirect_to :suggest
172
-
173
- # redirect to an action
174
- redirect_to RateMovie
175
- ```
176
-
177
- Sometimes you will need to share some data between the actions. You could use `session` for this, but a more appropriate solution would be to have `redirect_to` function to preserve the flash between actions.
178
- Check out this example:
179
-
180
- ```ruby
181
- class AskForBirthday < TelegramWorkflow::Action
182
- def initial
183
- on_redirect do
184
- client.send_message text: "What year is your birthday?"
185
- end
186
-
187
- on_message do
188
- birthday = params.message_text.to_i
189
- redirect_to DisplayAge, birthday: birthday
190
- end
191
- end
192
- end
193
-
194
- class DisplayAge < TelegramWorkflow::Action
195
- def initial
196
- on_redirect do
197
- age = Date.today.year - flash[:birthday]
198
- client.send_message text: "You are #{age}!"
199
- end
200
- end
201
- end
202
- ```
203
-
204
- You can see that despite the fact that flash is being cleared when redirecting to another action, passing `birthday` value to the `redirect_to` call made it accessible via flash in the action we redirected to.
205
-
206
- ## Configuration
207
-
208
- Configure the gem using the `TelegramWorkflow.configure` call.
209
- The two required parameters are `start_action` and `api_token`.
210
-
211
- ```ruby
212
- TelegramWorkflow.configure do |config|
213
- config.start_action = <Start Action Class>
214
- config.api_token = <Your Token>
215
- end
216
- ```
217
-
218
- Method | Default | Description
219
- -------|---------|------------
220
- api_token | | This is the token you get from `@botfather` to access the Telegram API.
221
- start_action | | This is an entry-point action, which is called every time `/start` or `/startgroup` command is sent to the chat. You cannnot redirect to this action or call it manually. Use it to set the things up, e.g. create a user record or store current user's id in the session.
222
- session_store | `Rails.cache` or `InMemoryStore.new` | This is the session store. Default implementation stores session in memory, which means it will be reset after server shutdown. Can be [customized](README.md#customization). Use `TelegramWorkflow::Stores::File` for persistent file store.
223
- logger | `Rails.logger` or `STDOUT` | Logger object. Can be [customized](README.md#customization).
224
- client | `TelegramWorkflow::Client` | The object which implements Telegram API. Can be [customized](README.md#client-customization).
225
- webhook_url | nil | The webhook url. Set it only if you are using webhooks for getting updates. TelegramWorkflow will create a webhook subscription automatically.
226
-
227
- ## Updates
228
-
229
- The gem implements both methods of getting updates from the Telegram API.
230
-
231
- ### Webhooks
232
-
233
- * Configure the gem with `webhook_url` value.
234
- * Process the updates with the following code in your controller:
235
-
236
- ```ruby
237
- class TelegramWebhooksController < ApplicationController
238
- def create
239
- TelegramWorkflow.process(params)
240
- end
241
- end
242
- ```
243
-
244
- ### Long polling
245
-
246
- * Make sure you don't configure the gem with `webhook_url` value.
247
- * Run the following code:
248
-
249
- ```ruby
250
- TelegramWorkflow.updates.each do |params|
251
- TelegramWorkflow.process(params)
252
- end
253
- ```
254
-
255
- Be aware that `TelegramWorkflow.updates.each` call is blocking.
256
-
257
- `TelegramWorkflow` accepts all the parameters [getUpdates](https://core.telegram.org/bots/api#getupdates) does.
258
-
259
- ```ruby
260
- TelegramWorkflow.updates(timeout: 60, allowed_updates: %w(channel_post edited_channel_post)).each do |params|
261
- ...
262
- end
263
- ```
264
-
265
- Since most of the time will be spent on waiting for the Telegram API to respond, you might also want to process the updates in parallel:
266
-
267
- ```ruby
268
- require "concurrent-ruby"
269
-
270
- pool = Concurrent::CachedThreadPool.new
271
-
272
- TelegramWorkflow.updates.each do |params|
273
- pool.post { TelegramWorkflow.process(params) }
274
- end
275
- ```
276
-
277
- Use `stop_updates` call to exit the updates loop:
278
-
279
- ```ruby
280
- trap "SIGINT" do
281
- TelegramWorkflow.stop_updates
282
- end
283
-
284
- # decrease the timeout to wait no more than 10 seconds when exiting
285
- TelegramWorkflow.updates(timeout: 10).each do |params|
286
- TelegramWorkflow.process(params)
287
- end
288
-
289
- ```
290
-
291
- ## Customization
292
-
293
- Object | Customization
294
- -------|--------------
295
- logger | An object that responds to `info` and `error` methods.
296
- session_store | An object that responds to `read` and `write` methods. Refer to [InMemoryStore](lib/telegram_workflow/stores/in_memory.rb) class definition.
297
- client | An object that responds to `new(chat_id)` method.
298
-
299
- ### Client Customization
300
-
301
- Use this customization to abstract your action's code from the Telegram API implementation details.
302
-
303
- Create a customized client:
304
-
305
- ```ruby
306
- class MyClient < TelegramWorkflow::Client
307
- def send_prize_location(user)
308
- # this is an example call
309
- prize = user.find_last_prize
310
-
311
- send_venue latitude: prize.latitude,
312
- longitude: prize.longitude,
313
- address: prize.address
314
- title: "Collect the last prize here!",
315
- reply_markup: { keyboard: [[{ text: "Give me a hint" }], [{ text: "Give me anohter hint" }]] }
316
- end
317
- end
318
- ```
319
-
320
- Now, configure the gem to use the customized client:
321
-
322
- ```ruby
323
- TelegramWorkflow.configure do |config|
324
- config.client = MyClient
325
- end
326
- ```
327
-
328
- Then, in your action:
329
-
330
- ```ruby
331
- class FindPrize < TelegramWorkflow::Action
332
- def initial
333
- on_redirect do
334
- client.send_prize_location(current_user)
335
- end
336
- end
337
- end
338
- ```
339
-
340
- ## Testing
341
-
342
- Testing utility provides `send_message` helper to emulate messages sent into the chat.
343
-
344
- ```ruby
345
- # send a message
346
- send_message message_text: "text"
347
-
348
- # send CallbackQuery data
349
- send_message callback_data: "data"
350
-
351
- # send InlineQuery data
352
- send_message inline_data: "data"
353
-
354
- # customize the params
355
- send_message { |params| params[:edited_channel_post] = { text: "message" } }
356
- ```
357
-
358
- Also, `subject.client` and `subject.flow` spies are available to track redirects and calls to the API client inside your actions.
359
- Store your tests under `spec/telegram_actions` or tag them with `type: :telegram_action`.
360
-
361
- Suppose we have the following action:
362
-
363
- ```ruby
364
- class AskForBirthday < TelegramWorkflow::Action
365
- def initial
366
- on_redirect do
367
- client.send_message text: "What year is your birthday?"
368
- end
369
-
370
- on_message do
371
- Birthday.create! date: params.message_text
372
- redirect_to DisplayAge
373
- end
374
- end
375
- end
376
- ```
377
-
378
- Now, let's add some tests for this action:
379
-
380
- ```ruby
381
- require "telegram_workflow/rspec"
382
-
383
- RSpec.describe AskForBirthday, type: :telegram_action do
384
- it "asks for user's birthday" do
385
- expect(subject.client).to have_received(:send_message).with(text: "What year is your birthday?")
386
- expect {
387
- send_message message_text: "10/10/2000"
388
- }.to change { Birthday.count }.by(1)
389
-
390
- expect(subject.flow).to have_received(:redirect_to).with(DisplayAge)
391
- end
392
- end
393
- ```
394
-
395
- As you can see, testing utility starts the flow automatically, calling `initial` step on `described_class`.
115
+ Please see the [TelegramWorkflow wiki](https://github.com/rsamoilov/telegram_workflow/wiki) for more detailed documentation.
396
116
 
397
117
  ## Example
398
118
 
@@ -84,10 +84,15 @@ class TelegramWorkflow::Client
84
84
  method_name = action.to_s.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
85
85
 
86
86
  define_method(method_name) do |params = {}|
87
- make_request(action, params)
87
+ @inline ?
88
+ save_request(action, params) :
89
+ make_request(action, params)
88
90
  end
89
91
  end
90
92
 
93
+ attr_accessor :inline, :inline_request
94
+ attr_reader :api_url
95
+
91
96
  def initialize(chat_id = nil)
92
97
  @chat_id = chat_id
93
98
  @api_url = "https://api.telegram.org/bot#{TelegramWorkflow.config.api_token}"
@@ -129,6 +134,11 @@ class TelegramWorkflow::Client
129
134
  end
130
135
  end
131
136
 
137
+ def save_request(action, params = {})
138
+ raise TelegramWorkflow::Errors::DoubleInlineRequest if @inline_request
139
+ @inline_request = { method: action, chat_id: @chat_id, **params }
140
+ end
141
+
132
142
  def make_request(action, params = {})
133
143
  has_file_params = params.any? { |_, param| param.is_a?(TelegramWorkflow::InputFile) }
134
144
  request_type = has_file_params ? :form : :json
@@ -23,6 +23,12 @@ module TelegramWorkflow::Errors
23
23
  end
24
24
  end
25
25
 
26
+ class DoubleInlineRequest < StandardError
27
+ def initialize(msg = "Cannot send more than one request in a row in inline mode.")
28
+ super
29
+ end
30
+ end
31
+
26
32
  class ApiError < StandardError
27
33
  end
28
34
 
@@ -1,3 +1,3 @@
1
1
  module TelegramWorkflow
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -31,6 +31,8 @@ class TelegramWorkflow::Workflow
31
31
  end
32
32
 
33
33
  @session.dump
34
+
35
+ @client.inline_request
34
36
  end
35
37
 
36
38
  def redirect_to(action_or_step, session_params = nil)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telegram_workflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Samoilov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-09 00:00:00.000000000 Z
11
+ date: 2020-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http