telegram_workflow 1.4.0 → 1.5.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 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