telegram_workflow 1.3.0 → 1.7.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: 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