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 +4 -4
- data/README.md +2 -282
- data/lib/telegram_workflow/client.rb +11 -1
- data/lib/telegram_workflow/errors.rb +6 -0
- data/lib/telegram_workflow/version.rb +1 -1
- data/lib/telegram_workflow/workflow.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1247dbbe0bc252c17270b86bc003ebd72f4dac8990a3251817cd99ac75fa85e
|
4
|
+
data.tar.gz: 1d1983843ac32770201da9e3c9475a3394c7f37b19e80ed89f8b8da66d5be2d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
##
|
113
|
+
## Documentation
|
114
114
|
|
115
|
-
|
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
|
-
|
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
|
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
|
+
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-
|
11
|
+
date: 2020-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|