telegram_workflow 1.3.0 → 1.7.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: 9f33398c3b33397236392a5b645e39b58827df8e453727fd06496ce0b4479e11
4
- data.tar.gz: 9155e202748c8242adc4f4789623b8c42156658616e545d1aafd2aa4f2f776f7
3
+ metadata.gz: b7d04e49cdc1dbb680c62e9293eba4e3e8bc42aeb19f071a18e7461bb12cc85f
4
+ data.tar.gz: cfa4a8f4c2bfe5b8cca0b3f6d3ba425b87ee3da3458fe851aba0fe94cc6d8273
5
5
  SHA512:
6
- metadata.gz: 4f85e7ab9b297deab054185d03863c8183bf7fbd6a7f1325ec612fa0042ee0999a700e80d91cb5cbed3b945605595904ec1716192b341d39f2402df805ea69f1
7
- data.tar.gz: 67bf8b6d8abc59f26c2e302f989aaa17777303bc3efdfb29844686d35f62994a2257557e7838188292e2fbbecc7eb7d1ad872c3f123eb9eb1b61b686b046a5ba
6
+ metadata.gz: '058d9f15e33a4dccfc331a44dbaef37f41d2b03a83ebf4d8949613f160e3c7e7458423b81ac3171dec0b520464c15f37619480b87ffc347ab3fd387fef1b3de0'
7
+ data.tar.gz: 04f6bfc69e83f2cfc47e1ed1f88708d74ff899c4e6d49184ec0c10815799b1dfa7e1b42a0dcb9bf6afde3fe566e518c34210f3d0190d488ef2d062297db7e2de
@@ -0,0 +1,41 @@
1
+ name: Ruby
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ test:
7
+
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ matrix:
11
+ ruby-version: ['2.6', '2.7', '3.0', '3.1']
12
+
13
+ steps:
14
+ - uses: actions/checkout@v2
15
+ - name: Install packages
16
+ run: |
17
+ sudo apt update -qy
18
+ sudo apt install libdb-dev
19
+ - name: Set up Ruby
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby-version }}
23
+ bundler-cache: true
24
+ - name: Run tests
25
+ run: bundle exec rake
26
+ - name: Coveralls Parallel
27
+ uses: coverallsapp/github-action@master
28
+ with:
29
+ github-token: ${{ secrets.github_token }}
30
+ flag-name: run-${{ matrix.ruby-version }}
31
+ parallel: true
32
+
33
+ finish:
34
+ needs: test
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - name: Coveralls Finished
38
+ uses: coverallsapp/github-action@master
39
+ with:
40
+ github-token: ${{ secrets.github_token }}
41
+ parallel-finished: true
data/Gemfile CHANGED
@@ -3,9 +3,11 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in telegram_workflow.gemspec
4
4
  gemspec
5
5
 
6
- gem "rake", "~> 12.0"
6
+ gem "rake", "~> 13.0"
7
7
  gem "rspec", "~> 3.0"
8
8
 
9
9
  group :test do
10
- gem "coveralls", require: false
10
+ gem "simplecov"
11
+ gem "simplecov-lcov", "~> 0.8.0"
12
+ gem "dbm"
11
13
  end
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TelegramWorkflow
2
2
 
3
- [![Build Status](https://travis-ci.org/rsamoilov/telegram_workflow.svg?branch=master)](https://travis-ci.org/rsamoilov/telegram_workflow)
3
+ ![Build Status](https://github.com/rsamoilov/telegram_workflow/actions/workflows/ruby.yml/badge.svg)
4
4
  [![Coverage Status](https://coveralls.io/repos/github/rsamoilov/telegram_workflow/badge.svg?branch=master)](https://coveralls.io/github/rsamoilov/telegram_workflow?branch=master)
5
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/fd13239262e3550c2193/maintainability)](https://codeclimate.com/github/rsamoilov/telegram_workflow/maintainability)
6
6
 
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  class TelegramWorkflow::Client
2
- API_VERSION = "4.9"
3
- WebhookFilePath = Pathname.new("tmp/telegram_workflow/webhook_url.txt")
2
+ API_VERSION = "5.5"
3
+ WebhookConfigPath = Pathname.new("tmp/telegram_workflow/webhook_config.txt")
4
4
 
5
5
  AVAILABLE_ACTIONS = %i(
6
6
  getUpdates
@@ -9,6 +9,7 @@ class TelegramWorkflow::Client
9
9
  getMe
10
10
  sendMessage
11
11
  forwardMessage
12
+ copyMessage
12
13
  sendPhoto
13
14
  sendAudio
14
15
  sendDocument
@@ -28,27 +29,36 @@ class TelegramWorkflow::Client
28
29
  getUserProfilePhotos
29
30
  getFile
30
31
  kickChatMember
32
+ banChatMember
31
33
  unbanChatMember
32
34
  restrictChatMember
33
35
  promoteChatMember
34
36
  setChatAdministratorCustomTitle
37
+ banChatSenderChat
38
+ unbanChatSenderChat
35
39
  setChatPermissions
36
40
  exportChatInviteLink
41
+ createChatInviteLink
42
+ editChatInviteLink
43
+ revokeChatInviteLink
37
44
  setChatPhoto
38
45
  deleteChatPhoto
39
46
  setChatTitle
40
47
  setChatDescription
41
48
  pinChatMessage
42
49
  unpinChatMessage
50
+ unpinAllChatMessages
43
51
  leaveChat
44
52
  getChat
45
53
  getChatAdministrators
46
54
  getChatMembersCount
55
+ getChatMemberCount
47
56
  getChatMember
48
57
  setChatStickerSet
49
58
  deleteChatStickerSet
50
59
  answerCallbackQuery
51
60
  setMyCommands
61
+ deleteMyCommands
52
62
  getMyCommands
53
63
 
54
64
  editMessageText
@@ -78,57 +88,80 @@ class TelegramWorkflow::Client
78
88
  sendGame
79
89
  setGameScore
80
90
  getGameHighScores
91
+
92
+ logOut
93
+ close
81
94
  )
82
95
 
96
+ DEPRECATED_ACTIONS = {
97
+ kickChatMember: :banChatMember,
98
+ getChatMembersCount: :getChatMemberCount
99
+ }
100
+
83
101
  AVAILABLE_ACTIONS.each do |action|
84
102
  method_name = action.to_s.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
85
103
 
86
104
  define_method(method_name) do |params = {}|
87
- make_request(action, params)
105
+ if deprecated_in_favor_of = DEPRECATED_ACTIONS[action]
106
+ TelegramWorkflow.config.logger.warn "[TelegramWorkflow] #{action} action is deprecated. Use #{deprecated_in_favor_of} action instead."
107
+ end
108
+
109
+ @inline ?
110
+ save_request(action, params) :
111
+ make_request(action, params)
88
112
  end
89
113
  end
90
114
 
115
+ attr_accessor :inline, :inline_request
116
+ attr_reader :api_url
117
+
91
118
  def initialize(chat_id = nil)
92
119
  @chat_id = chat_id
93
- @webhook_url = TelegramWorkflow.config.webhook_url
94
120
  @api_url = "https://api.telegram.org/bot#{TelegramWorkflow.config.api_token}"
95
121
  end
96
122
 
97
123
  def set_webhook(params = {})
98
124
  make_request("setWebhook", params)
99
- cached_webhook_url(new_url: @webhook_url)
125
+ cached_webhook_config(params)
100
126
  end
101
127
 
102
- def delete_webhook
103
- make_request("deleteWebhook", {})
104
- cached_webhook_url(new_url: "")
128
+ def delete_webhook(params = {})
129
+ make_request("deleteWebhook", params)
130
+ cached_webhook_config(params)
105
131
  end
106
132
 
107
- def __setup_webhook
133
+ def __setup_webhook(webhook_url = TelegramWorkflow.config.webhook_url, params = {})
108
134
  TelegramWorkflow.config.logger.info "[TelegramWorkflow] Checking webhook setup..."
109
135
 
110
- if cached_webhook_url != @webhook_url
136
+ webhook_params = { url: webhook_url, allowed_updates: [], **params }
137
+
138
+ if cached_webhook_config != webhook_params
111
139
  TelegramWorkflow.config.logger.info "[TelegramWorkflow] Setting up a new webhook..."
112
- set_webhook(url: @webhook_url)
140
+ set_webhook(webhook_params)
113
141
  end
114
142
  end
115
143
 
116
144
  private
117
145
 
118
- def cached_webhook_url(new_url: nil)
119
- unless WebhookFilePath.exist?
120
- WebhookFilePath.dirname.mkpath
121
- WebhookFilePath.write("")
146
+ def cached_webhook_config(new_config = nil)
147
+ unless WebhookConfigPath.exist?
148
+ WebhookConfigPath.dirname.mkpath
149
+ WebhookConfigPath.write(Marshal.dump({}))
122
150
  end
123
151
 
124
- if new_url.nil?
125
- WebhookFilePath.read
152
+ if new_config.nil?
153
+ Marshal.load(WebhookConfigPath.read)
126
154
  else
127
- WebhookFilePath.write(new_url)
155
+ WebhookConfigPath.write(Marshal.dump(new_config))
128
156
  end
129
157
  end
130
158
 
131
- def make_request(action, params)
159
+ def save_request(action, params = {})
160
+ raise TelegramWorkflow::Errors::DoubleInlineRequest if @inline_request
161
+ @inline_request = { method: action, chat_id: @chat_id, **params }
162
+ end
163
+
164
+ def make_request(action, params = {})
132
165
  has_file_params = params.any? { |_, param| param.is_a?(TelegramWorkflow::InputFile) }
133
166
  request_type = has_file_params ? :form : :json
134
167
 
@@ -21,12 +21,14 @@ module TelegramWorkflow
21
21
  end
22
22
 
23
23
  class Configuration
24
- attr_accessor :session_store, :logger, :client, :start_action, :webhook_url, :api_token
24
+ attr_accessor :session_store, :logger, :client, :start_action, :webhook_url, :api_token,
25
+ :webhook_params
25
26
 
26
27
  REQUIRED_PARAMS = %i(session_store start_action api_token)
27
28
 
28
29
  def initialize
29
30
  @client = TelegramWorkflow::Client
31
+ @webhook_params = {}
30
32
 
31
33
  if defined?(Rails)
32
34
  @session_store = Rails.cache
@@ -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
 
@@ -16,7 +16,8 @@ class TelegramWorkflow::Params
16
16
  @params.dig("shipping_query", "from") ||
17
17
  @params.dig("inline_query", "from") ||
18
18
  @params.dig("chosen_inline_result", "from") ||
19
- @params.dig("poll_answer", "user")
19
+ @params.dig("poll_answer", "user") ||
20
+ @params.dig("chat_join_request", "from")
20
21
  end
21
22
 
22
23
  def language_code
@@ -32,11 +33,15 @@ class TelegramWorkflow::Params
32
33
  end
33
34
 
34
35
  def chat_id
35
- @params.dig("message", "chat", "id") ||
36
- @params.dig("callback_query", "message", "chat", "id") ||
37
- @params.dig("edited_message", "chat", "id") ||
38
- @params.dig("channel_post", "chat", "id") ||
39
- @params.dig("edited_channel_post", "chat", "id")
36
+ chat&.dig("id")
37
+ end
38
+
39
+ def chat
40
+ @params.dig("message", "chat") ||
41
+ @params.dig("callback_query", "message", "chat") ||
42
+ @params.dig("edited_message", "chat") ||
43
+ @params.dig("channel_post", "chat") ||
44
+ @params.dig("edited_channel_post", "chat")
40
45
  end
41
46
 
42
47
  def message
@@ -1,3 +1,3 @@
1
1
  module TelegramWorkflow
2
- VERSION = "1.3.0"
2
+ VERSION = "1.7.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)
@@ -39,6 +39,6 @@ require "telegram_workflow/stores/file"
39
39
 
40
40
  TelegramWorkflow.__after_configuration do |config|
41
41
  if config.webhook_url
42
- TelegramWorkflow::Client.new.__setup_webhook
42
+ TelegramWorkflow::Client.new.__setup_webhook(config.webhook_url, config.webhook_params)
43
43
  end
44
44
  end
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  # Specify which files should be added to the gem when it is released.
22
22
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
23
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|example)/}) }
25
25
  end
26
26
  spec.bindir = "exe"
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
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.3.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Samoilov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-29 00:00:00.000000000 Z
11
+ date: 2022-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -66,15 +66,15 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 5.2.0
69
- description:
70
- email:
69
+ description:
70
+ email:
71
71
  executables: []
72
72
  extensions: []
73
73
  extra_rdoc_files: []
74
74
  files:
75
+ - ".github/workflows/ruby.yml"
75
76
  - ".gitignore"
76
77
  - ".rspec"
77
- - ".travis.yml"
78
78
  - CODE_OF_CONDUCT.md
79
79
  - Gemfile
80
80
  - LICENSE.txt
@@ -82,17 +82,6 @@ files:
82
82
  - Rakefile
83
83
  - bin/console
84
84
  - bin/setup
85
- - example/Gemfile
86
- - example/README.md
87
- - example/actions/create_appointment.rb
88
- - example/actions/list_actions.rb
89
- - example/actions/list_appointments.rb
90
- - example/actions/select_doctor.rb
91
- - example/actions/start.rb
92
- - example/client.rb
93
- - example/environment.rb
94
- - example/flow.jpg
95
- - example/main.rb
96
85
  - lib/telegram_workflow.rb
97
86
  - lib/telegram_workflow/action.rb
98
87
  - lib/telegram_workflow/client.rb
@@ -114,7 +103,7 @@ licenses:
114
103
  metadata:
115
104
  homepage_uri: https://github.com/rsamoilov/telegram_workflow
116
105
  source_code_uri: https://github.com/rsamoilov/telegram_workflow
117
- post_install_message:
106
+ post_install_message:
118
107
  rdoc_options: []
119
108
  require_paths:
120
109
  - lib
@@ -130,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
119
  version: '0'
131
120
  requirements: []
132
121
  rubygems_version: 3.1.2
133
- signing_key:
122
+ signing_key:
134
123
  specification_version: 4
135
124
  summary: A simple library to create Telegram Bots in Ruby.
136
125
  test_files: []
data/.travis.yml DELETED
@@ -1,9 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.4.9
6
- - 2.5.7
7
- - 2.6.5
8
- - 2.7.0
9
- before_install: gem install bundler
data/example/Gemfile DELETED
@@ -1,2 +0,0 @@
1
- source "https://rubygems.org"
2
- gem "telegram_workflow"
data/example/README.md DELETED
@@ -1,79 +0,0 @@
1
- ## Description
2
-
3
- This is a fully working example of a bot which allows you to book an appointment with a doctor.
4
-
5
- The following diagram will help you understand the bot's flow:
6
- <p>
7
- <img src="https://github.com/rsamoilov/telegram_workflow/blob/master/example/flow.jpg" width="400">
8
- </p>
9
-
10
- ## Running the bot
11
-
12
- First, open [main.rb](main.rb) and configure the bot with your API token:
13
-
14
- ```diff
15
- TelegramWorkflow.configure do |config|
16
- - config.api_token = <YOUR_TOKEN>
17
- + config.api_token = "123456780:ABCDE_my-token"
18
- end
19
- ```
20
-
21
- Next, run the bot:
22
-
23
- ```
24
- bundle
25
- ruby main.rb
26
- ```
27
-
28
- ## Configuration
29
-
30
- Every Bot workflow begins with **`on_message`** callback in a `start_action`.
31
- There's no need to store current user data in session in this example, so we simply redirect to the `ListActions` action, which will be our "main" action.
32
-
33
- ```ruby
34
- class Actions::Start < TelegramWorkflow::Action
35
- def initial
36
- on_message do
37
- redirect_to Actions::ListActions
38
- end
39
- end
40
- end
41
- ```
42
-
43
- Next, the Telegram client can be customized. We want to use Telegram's [InlineKeyboard](https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating) to provide a user with a list of available actions.
44
-
45
- Let's encapsulate this inside our custom client class:
46
-
47
- ```ruby
48
- class Client < TelegramWorkflow::Client
49
- def send_actions(message, actions)
50
- send_message text: message, reply_markup: { inline_keyboard: actions }
51
- end
52
- end
53
- ```
54
-
55
- Now, let's configure the gem:
56
-
57
- ```ruby
58
- TelegramWorkflow.configure do |config|
59
- config.start_action = Actions::Start
60
- config.client = Client
61
- config.session_store = TelegramWorkflow::Stores::File.new
62
- end
63
- ```
64
-
65
- The last configuration parameter here is `session_store`. We are using `TelegramWorkflow::Stores::File` - a built-in implementation of persistent file store.
66
-
67
- [getUpdates](https://core.telegram.org/bots/api#getupdates) method is used in this example to receive the updates:
68
-
69
- ```ruby
70
- TelegramWorkflow.updates.each do |params|
71
- TelegramWorkflow.process(params)
72
- end
73
- ```
74
-
75
- After a user has sent a message, `TelegramWorkflow.process` will initialize the last processed action and step and then call `on_message` callback on it.
76
-
77
- ## Actions
78
-
79
- Check out the bot's code under [actions](actions) folder.
@@ -1,56 +0,0 @@
1
- #
2
- # this is an action to create an appointment;
3
- # each method represents one of the steps for appointment creation;
4
- # for the sake of simplicity, all created appointments are stored in the session
5
- #
6
- class Actions::CreateAppointment < TelegramWorkflow::Action
7
- def initial
8
- on_redirect do
9
- client.send_message text: "Enter patient's name:"
10
- end
11
-
12
- on_message do
13
- flash[:name] = params.message_text
14
- redirect_to :reason
15
- end
16
- end
17
-
18
- def reason
19
- on_redirect do
20
- client.send_message text: "What is the reason for visit?"
21
- end
22
-
23
- on_message do
24
- flash[:reason] = params.message_text
25
- redirect_to :date
26
- end
27
- end
28
-
29
- def date
30
- on_redirect do
31
- client.send_message text: "What date works best for you?"
32
- end
33
-
34
- on_message do
35
- date = Date.parse(params.message_text) rescue nil
36
-
37
- # there's no redirect in case the date is invalid;
38
- # this means that next time a user sends a message, the current action will be executed again
39
- if date
40
- flash[:date] = date
41
- redirect_to :done
42
- else
43
- client.send_message text: "Invalid date format. Please try again."
44
- end
45
- end
46
- end
47
-
48
- # `specialty` parameter is added to flash in previous `Actions::SelectDoctor` action
49
- def done
50
- on_redirect do
51
- (session[:appointments] ||= []) << flash.slice(:name, :reason, :date, :specialty)
52
- client.send_message text: "Your appointment has been created!"
53
- redirect_to Actions::ListActions
54
- end
55
- end
56
- end
@@ -1,28 +0,0 @@
1
- class Actions::ListActions < TelegramWorkflow::Action
2
- def initial
3
- on_redirect do
4
- available_actions = [
5
- [{ text: "Make an appointment", callback_data: "create" }]
6
- ]
7
-
8
- if session[:appointments]&.any?
9
- available_actions.unshift [{ text: "List my appointments", callback_data: "list" }]
10
- end
11
-
12
- # this is the customized client object
13
- client.send_actions "Select an action:", available_actions
14
- end
15
-
16
- # `params.callback_data` here will be one of the identifiers defined as `callback_data` in `available_actions` array
17
- # refer to https://core.telegram.org/bots/api#inlinekeyboardbutton
18
- on_message do
19
- next_action = if params.callback_data == "create"
20
- Actions::SelectDoctor
21
- elsif params.callback_data == "list"
22
- Actions::ListAppointments
23
- end
24
-
25
- redirect_to next_action
26
- end
27
- end
28
- end
@@ -1,13 +0,0 @@
1
- class Actions::ListAppointments < TelegramWorkflow::Action
2
- def initial
3
- on_redirect do
4
- appointments = session[:appointments].map do |app|
5
- "<b>#{app[:name]}</b> on #{app[:date].to_s}"
6
- end
7
-
8
- # refer to https://core.telegram.org/bots/api#sendmessage for the list of available parameters
9
- client.send_message text: appointments.join("\n"), parse_mode: "HTML"
10
- redirect_to Actions::ListActions
11
- end
12
- end
13
- end
@@ -1,19 +0,0 @@
1
- class Actions::SelectDoctor < TelegramWorkflow::Action
2
- def initial
3
- on_redirect do
4
- available_doctors = [
5
- [{ text: "Family Medicine", callback_data: "family" }],
6
- [{ text: "Emergency Medicine", callback_data: "emergency" }],
7
- [{ text: "Pediatrics", callback_data: "pediatrics" }]
8
- ]
9
-
10
- client.send_actions "Select a doctor:", available_doctors
11
- end
12
-
13
- on_message do
14
- # pass `specialty` parameter to the next action
15
- # https://github.com/rsamoilov/telegram_workflow#redirect_toaction_or_class-flash_params--
16
- redirect_to Actions::CreateAppointment, specialty: params.callback_data
17
- end
18
- end
19
- end
@@ -1,9 +0,0 @@
1
- class Actions::Start < TelegramWorkflow::Action
2
- def initial
3
- # we don't need to store current user id in session or make any other setup,
4
- # so just redirect to the `ListActions` action
5
- on_message do
6
- redirect_to Actions::ListActions
7
- end
8
- end
9
- end
data/example/client.rb DELETED
@@ -1,5 +0,0 @@
1
- class Client < TelegramWorkflow::Client
2
- def send_actions(message, actions)
3
- send_message text: message, reply_markup: { inline_keyboard: actions }
4
- end
5
- end
@@ -1,13 +0,0 @@
1
- require "telegram_workflow"
2
- require "date"
3
-
4
- module Actions
5
- end
6
-
7
- dependencies = %w(
8
- ./actions/*.rb
9
- ./client.rb
10
- )
11
- dependencies.each do |path|
12
- Dir[path].each { |path| require_relative path }
13
- end
data/example/flow.jpg DELETED
Binary file
data/example/main.rb DELETED
@@ -1,17 +0,0 @@
1
- require_relative "environment"
2
-
3
- TelegramWorkflow.configure do |config|
4
- config.start_action = Actions::Start
5
- config.client = Client
6
- config.session_store = TelegramWorkflow::Stores::File.new
7
- config.api_token = <YOUR_TOKEN>
8
- end
9
-
10
- trap "SIGINT" do
11
- puts "Exiting..."
12
- TelegramWorkflow.stop_updates
13
- end
14
-
15
- TelegramWorkflow.updates(timeout: 5).each do |params|
16
- TelegramWorkflow.process(params)
17
- end