telegram-bot-ruby 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +10 -0
  3. data/.github/workflows/ci.yml +0 -3
  4. data/.github/workflows/stale.yml +19 -0
  5. data/.gitignore +1 -0
  6. data/.rspec +1 -0
  7. data/.rubocop.yml +12 -7
  8. data/CHANGELOG.md +10 -0
  9. data/Gemfile +7 -3
  10. data/LICENSE +0 -1
  11. data/README.md +64 -20
  12. data/Rakefile +10 -8
  13. data/bin/console +4 -1
  14. data/lib/telegram/bot/api/endpoints.rb +114 -0
  15. data/lib/telegram/bot/api.rb +20 -44
  16. data/lib/telegram/bot/client.rb +8 -9
  17. data/lib/telegram/bot/exceptions/response_error.rb +2 -8
  18. data/lib/telegram/bot/types/animation.rb +1 -1
  19. data/lib/telegram/bot/types/audio.rb +1 -1
  20. data/lib/telegram/bot/types/bot_command_scope_all_chat_administrators.rb +1 -1
  21. data/lib/telegram/bot/types/bot_command_scope_all_group_chats.rb +1 -1
  22. data/lib/telegram/bot/types/bot_command_scope_all_private_chats.rb +1 -1
  23. data/lib/telegram/bot/types/bot_command_scope_chat.rb +2 -2
  24. data/lib/telegram/bot/types/bot_command_scope_chat_administrators.rb +2 -2
  25. data/lib/telegram/bot/types/bot_command_scope_chat_member.rb +2 -2
  26. data/lib/telegram/bot/types/bot_command_scope_default.rb +1 -1
  27. data/lib/telegram/bot/types/bot_description.rb +11 -0
  28. data/lib/telegram/bot/types/bot_name.rb +11 -0
  29. data/lib/telegram/bot/types/bot_short_description.rb +11 -0
  30. data/lib/telegram/bot/types/callback_query.rb +1 -1
  31. data/lib/telegram/bot/types/chat.rb +16 -9
  32. data/lib/telegram/bot/types/chat_administrator_rights.rb +3 -0
  33. data/lib/telegram/bot/types/chat_boost.rb +14 -0
  34. data/lib/telegram/bot/types/chat_boost_removed.rb +14 -0
  35. data/lib/telegram/bot/types/chat_boost_source.rb +13 -0
  36. data/lib/telegram/bot/types/chat_boost_source_gift_code.rb +12 -0
  37. data/lib/telegram/bot/types/chat_boost_source_giveaway.rb +14 -0
  38. data/lib/telegram/bot/types/chat_boost_source_premium.rb +12 -0
  39. data/lib/telegram/bot/types/chat_boost_updated.rb +12 -0
  40. data/lib/telegram/bot/types/chat_member.rb +11 -2
  41. data/lib/telegram/bot/types/chat_member_administrator.rb +8 -5
  42. data/lib/telegram/bot/types/chat_member_banned.rb +2 -2
  43. data/lib/telegram/bot/types/chat_member_left.rb +2 -2
  44. data/lib/telegram/bot/types/chat_member_member.rb +2 -2
  45. data/lib/telegram/bot/types/chat_member_owner.rb +2 -2
  46. data/lib/telegram/bot/types/chat_member_restricted.rb +2 -2
  47. data/lib/telegram/bot/types/chat_member_updated.rb +1 -0
  48. data/lib/telegram/bot/types/compactable.rb +2 -7
  49. data/lib/telegram/bot/types/document.rb +1 -1
  50. data/lib/telegram/bot/types/external_reply_info.rb +33 -0
  51. data/lib/telegram/bot/types/force_reply.rb +1 -1
  52. data/lib/telegram/bot/types/giveaway.rb +18 -0
  53. data/lib/telegram/bot/types/giveaway_completed.rb +13 -0
  54. data/lib/telegram/bot/types/giveaway_created.rb +10 -0
  55. data/lib/telegram/bot/types/giveaway_winners.rb +21 -0
  56. data/lib/telegram/bot/types/inaccessible_message.rb +13 -0
  57. data/lib/telegram/bot/types/inline_keyboard_button.rb +1 -0
  58. data/lib/telegram/bot/types/inline_query_result_article.rb +4 -4
  59. data/lib/telegram/bot/types/inline_query_result_audio.rb +1 -1
  60. data/lib/telegram/bot/types/inline_query_result_cached_audio.rb +1 -1
  61. data/lib/telegram/bot/types/inline_query_result_cached_document.rb +1 -1
  62. data/lib/telegram/bot/types/inline_query_result_cached_gif.rb +1 -1
  63. data/lib/telegram/bot/types/inline_query_result_cached_mpeg4_gif.rb +1 -1
  64. data/lib/telegram/bot/types/inline_query_result_cached_photo.rb +1 -1
  65. data/lib/telegram/bot/types/inline_query_result_cached_sticker.rb +1 -1
  66. data/lib/telegram/bot/types/inline_query_result_cached_video.rb +1 -1
  67. data/lib/telegram/bot/types/inline_query_result_cached_voice.rb +1 -1
  68. data/lib/telegram/bot/types/inline_query_result_contact.rb +4 -4
  69. data/lib/telegram/bot/types/inline_query_result_document.rb +4 -4
  70. data/lib/telegram/bot/types/inline_query_result_game.rb +1 -1
  71. data/lib/telegram/bot/types/inline_query_result_gif.rb +3 -3
  72. data/lib/telegram/bot/types/inline_query_result_location.rb +4 -4
  73. data/lib/telegram/bot/types/inline_query_result_mpeg4_gif.rb +3 -3
  74. data/lib/telegram/bot/types/inline_query_result_photo.rb +2 -2
  75. data/lib/telegram/bot/types/inline_query_result_venue.rb +4 -4
  76. data/lib/telegram/bot/types/inline_query_result_video.rb +2 -2
  77. data/lib/telegram/bot/types/inline_query_result_voice.rb +1 -1
  78. data/lib/telegram/bot/types/input_media_animation.rb +2 -2
  79. data/lib/telegram/bot/types/input_media_audio.rb +2 -2
  80. data/lib/telegram/bot/types/input_media_document.rb +2 -2
  81. data/lib/telegram/bot/types/input_media_photo.rb +1 -1
  82. data/lib/telegram/bot/types/input_media_video.rb +2 -2
  83. data/lib/telegram/bot/types/input_text_message_content.rb +1 -1
  84. data/lib/telegram/bot/types/keyboard_button.rb +1 -1
  85. data/lib/telegram/bot/types/{keyboard_button_request_user.rb → keyboard_button_request_users.rb} +2 -1
  86. data/lib/telegram/bot/types/link_preview_options.rb +15 -0
  87. data/lib/telegram/bot/types/maybe_inaccessible_message.rb +12 -0
  88. data/lib/telegram/bot/types/menu_button_commands.rb +1 -1
  89. data/lib/telegram/bot/types/menu_button_default.rb +1 -1
  90. data/lib/telegram/bot/types/menu_button_web_app.rb +1 -1
  91. data/lib/telegram/bot/types/message.rb +19 -16
  92. data/lib/telegram/bot/types/message_id.rb +11 -0
  93. data/lib/telegram/bot/types/message_origin.rb +14 -0
  94. data/lib/telegram/bot/types/message_origin_channel.rb +15 -0
  95. data/lib/telegram/bot/types/message_origin_chat.rb +14 -0
  96. data/lib/telegram/bot/types/message_origin_hidden_user.rb +13 -0
  97. data/lib/telegram/bot/types/message_origin_user.rb +13 -0
  98. data/lib/telegram/bot/types/message_reaction_count_updated.rb +14 -0
  99. data/lib/telegram/bot/types/message_reaction_updated.rb +17 -0
  100. data/lib/telegram/bot/types/passport_element_error_data_field.rb +1 -1
  101. data/lib/telegram/bot/types/passport_element_error_file.rb +1 -1
  102. data/lib/telegram/bot/types/passport_element_error_files.rb +2 -2
  103. data/lib/telegram/bot/types/passport_element_error_front_side.rb +1 -1
  104. data/lib/telegram/bot/types/passport_element_error_reverse_side.rb +1 -1
  105. data/lib/telegram/bot/types/passport_element_error_selfie.rb +1 -1
  106. data/lib/telegram/bot/types/passport_element_error_translation_file.rb +1 -1
  107. data/lib/telegram/bot/types/passport_element_error_translation_files.rb +2 -2
  108. data/lib/telegram/bot/types/passport_element_error_unspecified.rb +1 -1
  109. data/lib/telegram/bot/types/poll_answer.rb +2 -1
  110. data/lib/telegram/bot/types/reaction_count.rb +12 -0
  111. data/lib/telegram/bot/types/reaction_type.rb +12 -0
  112. data/lib/telegram/bot/types/reaction_type_custom_emoji.rb +12 -0
  113. data/lib/telegram/bot/types/reaction_type_emoji.rb +12 -0
  114. data/lib/telegram/bot/types/reply_keyboard_remove.rb +1 -1
  115. data/lib/telegram/bot/types/reply_parameters.rb +17 -0
  116. data/lib/telegram/bot/types/sticker.rb +2 -1
  117. data/lib/telegram/bot/types/sticker_set.rb +1 -1
  118. data/lib/telegram/bot/types/story.rb +9 -0
  119. data/lib/telegram/bot/types/switch_inline_query_chosen_chat.rb +15 -0
  120. data/lib/telegram/bot/types/text_quote.rb +14 -0
  121. data/lib/telegram/bot/types/update.rb +4 -0
  122. data/lib/telegram/bot/types/user.rb +2 -2
  123. data/lib/telegram/bot/types/user_chat_boosts.rb +11 -0
  124. data/lib/telegram/bot/types/{user_shared.rb → users_shared.rb} +2 -2
  125. data/lib/telegram/bot/types/video.rb +1 -1
  126. data/lib/telegram/bot/types/video_note.rb +1 -1
  127. data/lib/telegram/bot/types/webhook_info.rb +1 -1
  128. data/lib/telegram/bot/types/write_access_allowed.rb +3 -0
  129. data/lib/telegram/bot/version.rb +1 -1
  130. data/lib/telegram/bot.rb +1 -0
  131. data/telegram-bot-ruby.gemspec +1 -4
  132. metadata +43 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 849ef276ecc1cfd4531ccd49474ca72e21e6a737f4ee49af374fd0f2cb7a6368
4
- data.tar.gz: 8d451387892328aff6a1205f0b31b9221a07319c0b8d927c655d40283d2efc09
3
+ metadata.gz: 5b61e5af1f53cd133496f945bc7642b2a79e12a1d2be92faca3e45246b51dba5
4
+ data.tar.gz: de0024292072dfa7dcc74ffa6c66ba4e30d470652bcdcd5d9b193a8cea66f420
5
5
  SHA512:
6
- metadata.gz: ef343c2f15ee929777e6f03b5cc2e9af8f593e49a15bb751301476996bff8bcc3c79b99c962c5e865a379f23ddf8d505b0fabe22a3033a6e950a703ea54a9fa3
7
- data.tar.gz: a9add629f338919e2584cccb8c7b206eaef726df39021ac0f9b42e26e7cf2c1cb65a28735e0010c3f390aee0f90f29fcb3a5d2c1981f0b8ba4829d55b14fd421
6
+ metadata.gz: dab03a9d65d7582dce03bb6f5a8aea981a507f8011e2e618264177182c132c4fda82d1fc80da0590d3196e07e145e586c665af1fa8e9fd5fdfbcd8c36439bad2
7
+ data.tar.gz: 1b165086c50ee02e77298c575712551462c58bdb3f963a70ab823d2c9cffffdf23551af8caa3ad484e47bca3d89505f5656ae05c15648b37e6e84cc73e308011
data/.editorconfig ADDED
@@ -0,0 +1,10 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ indent_size = 2
7
+ indent_style = space
8
+ insert_final_newline = true
9
+ max_line_length = 120
10
+ trim_trailing_whitespace = true
@@ -32,9 +32,6 @@ jobs:
32
32
  ruby:
33
33
  - 2.7
34
34
  - 3.2
35
- env:
36
- BOT_API_ENV: test
37
- BOT_API_TOKEN: ${{ secrets.BOT_API_TOKEN }}
38
35
  steps:
39
36
  - name: Checkout
40
37
  uses: actions/checkout@v3
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: Close stale issues and PRs
3
+ on:
4
+ schedule:
5
+ - cron: 40 * * * *
6
+
7
+ jobs:
8
+ stale:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/stale@v7
12
+ with:
13
+ days-before-close: 7
14
+ days-before-stale: 60
15
+ stale-issue-label: stale
16
+ stale-issue-message: This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.
17
+ stale-pr-label: stale
18
+ stale-pr-message: This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.
19
+ operations-per-run: 500
data/.gitignore CHANGED
@@ -3,4 +3,5 @@ Gemfile.lock
3
3
  .bundle/
4
4
  .env
5
5
  pkg/
6
+ tmp/
6
7
  vendor/bundle/
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --require spec_helper
2
2
  --color
3
+ --warnings
data/.rubocop.yml CHANGED
@@ -2,6 +2,7 @@
2
2
  require:
3
3
  - rubocop-rake
4
4
  - rubocop-rspec
5
+ - rubocop-performance
5
6
 
6
7
  AllCops:
7
8
  NewCops: enable
@@ -10,18 +11,22 @@ AllCops:
10
11
  Style/Documentation:
11
12
  Enabled: false
12
13
 
13
- Style/FrozenStringLiteralComment:
14
- Exclude:
15
- - bin/console
16
-
17
14
  Metrics/BlockLength:
18
15
  AllowedMethods:
19
16
  - context
20
17
  - describe
21
18
  - task
19
+ Metrics/ClassLength:
20
+ Exclude:
21
+ - lib/telegram/bot/api/endpoints.rb
22
+ Metrics/MethodLength:
23
+ Exclude:
24
+ - spec/**/*
22
25
 
23
26
  Layout/LineLength:
24
27
  Max: 120
25
- Exclude:
26
- - telegram-bot-ruby.gemspec
27
- - examples/*.rb
28
+
29
+ RSpec/MultipleMemoizedHelpers:
30
+ Enabled: false
31
+ RSpec/NestedGroups:
32
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Added
6
+
7
+ - [Bot API 7.0](https://core.telegram.org/bots/api#december-29-2023)
8
+
9
+ ### Changed
10
+
11
+ - API method calls now return corresponding response objects ([#285](https://github.com/atipugin/telegram-bot-ruby/pull/285), thx [@AlexWayfer](https://github.com/AlexWayfer))
12
+
3
13
  ## 1.0.0
4
14
 
5
15
  - Replace [virtus](https://github.com/solnic/virtus) with [dry-struct](https://github.com/dry-rb/dry-struct)
data/Gemfile CHANGED
@@ -8,7 +8,11 @@ gem 'dotenv', '~> 2.8'
8
8
  gem 'nokogiri', '~> 1.13'
9
9
  gem 'pry'
10
10
  gem 'rake', '~> 13.0'
11
+
11
12
  gem 'rspec', '~> 3.4'
12
- gem 'rubocop', '~> 1.27'
13
- gem 'rubocop-rake'
14
- gem 'rubocop-rspec', '~> 2.10'
13
+ gem 'vcr', '~> 6.0'
14
+
15
+ gem 'rubocop', '~> 1.54.1'
16
+ gem 'rubocop-performance', '~> 1.18'
17
+ gem 'rubocop-rake', '~> 0.6.0'
18
+ gem 'rubocop-rspec', '~> 2.22.0'
data/LICENSE CHANGED
@@ -11,4 +11,3 @@
11
11
  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
12
 
13
13
  0. You just DO WHAT THE FUCK YOU WANT TO.
14
-
data/README.md CHANGED
@@ -9,7 +9,9 @@ Ruby wrapper for [Telegram's Bot API](https://core.telegram.org/bots/api).
9
9
 
10
10
  ## 🚧 Upgrading to 1.0
11
11
 
12
- Since v1.0 telegram-bot-ruby uses [dry-struct](https://github.com/dry-rb/dry-struct) instead of [virtus](https://github.com/solnic/virtus). This means that type objects are now immutable and you can't change them after initialization:
12
+ Since v1.0 `telegram-bot-ruby` uses [`dry-struct`](https://github.com/dry-rb/dry-struct)
13
+ instead of [`virtus`](https://github.com/solnic/virtus).
14
+ This means that type objects are now immutable and you can't change them after initialization:
13
15
 
14
16
  ```ruby
15
17
  # This won't work
@@ -27,7 +29,7 @@ Please make sure it doesn't break your existing code before upgrading to 1.0.
27
29
  Add following line to your Gemfile:
28
30
 
29
31
  ```ruby
30
- gem 'telegram-bot-ruby'
32
+ gem 'telegram-bot-ruby', '~> 1.0'
31
33
  ```
32
34
 
33
35
  And then execute:
@@ -44,7 +46,8 @@ gem install telegram-bot-ruby
44
46
 
45
47
  ## Usage
46
48
 
47
- First things first, you need to [obtain a token](https://core.telegram.org/bots#6-botfather) for your bot. Then create your Telegram bot like this:
49
+ First things first, you need to [obtain a token](https://core.telegram.org/bots#6-botfather) for your bot.
50
+ Then create your Telegram bot like this:
48
51
 
49
52
  ```ruby
50
53
  require 'telegram/bot'
@@ -63,7 +66,10 @@ Telegram::Bot::Client.run(token) do |bot|
63
66
  end
64
67
  ```
65
68
 
66
- Note that `bot.api` object implements [Telegram Bot API methods](https://core.telegram.org/bots/api#available-methods) as is. So you can invoke any method inside the block without any problems. All methods are available in both *snake_case* and *camelCase* notations.
69
+ Note that `bot.api` object implements
70
+ [Telegram Bot API methods](https://core.telegram.org/bots/api#available-methods) as is.
71
+ So you can invoke any method inside the block without any problems.
72
+ All methods are available in both *snake_case* and *camelCase* notations.
67
73
 
68
74
  If you need to start a bot in development mode you have to pass `environment: :test`:
69
75
 
@@ -73,15 +79,35 @@ Telegram::Bot::Client.run(token, environment: :test) do |bot|
73
79
  end
74
80
  ```
75
81
 
76
- Same thing about `message` object - it implements [Message](https://core.telegram.org/bots/api#message) spec, so you always know what to expect from it.
82
+ Same thing about `message` object: it implements [Message](https://core.telegram.org/bots/api#message) spec,
83
+ so you always know what to expect from it.
84
+
85
+ To gracefully stop the bot, for example by `INT` signal (Ctrl-C), call the `bot.stop` method:
86
+
87
+ ```ruby
88
+ bot = Telegram::Bot::Client.new(token)
89
+
90
+ Signal.trap('INT') do
91
+ bot.stop
92
+ end
93
+
94
+ bot.listen do |message|
95
+ # it will be in an infinity loop until `bot.stop` command
96
+ # (with a small delay for the current `fetch_updates` request)
97
+ end
98
+ ```
77
99
 
78
100
  ## Webhooks
79
101
 
80
- If you are going to use [webhooks](https://core.telegram.org/bots/api#setwebhook) instead of [long polling](https://core.telegram.org/bots/api#getupdates), you need to implement your own webhook callbacks server. Take a look at [this repo](https://github.com/solyaris/BOTServer) as an example.
102
+ If you are going to use [webhooks](https://core.telegram.org/bots/api#setwebhook)
103
+ instead of [long polling](https://core.telegram.org/bots/api#getupdates),
104
+ you need to implement your own webhook callbacks server.
105
+ Take a look at [this repo](https://github.com/solyaris/BOTServer) as an example.
81
106
 
82
107
  ## Proxy
83
108
 
84
- As some countries block access to Telegram, you can set up your own proxy and use it to access Telegram API. In this case you need to configure API url:
109
+ As some countries block access to Telegram, you can set up your own proxy and use it to access Telegram API.
110
+ In this case you need to configure API URL:
85
111
 
86
112
  ```ruby
87
113
  Telegram::Bot::Client.run(token, url: 'https://proxy.example.com') do |bot|
@@ -91,7 +117,8 @@ end
91
117
 
92
118
  ## Custom keyboards
93
119
 
94
- You can use your own [custom keyboards](https://core.telegram.org/bots#keyboards). Here is an example:
120
+ You can use your own [custom keyboards](https://core.telegram.org/bots#keyboards).
121
+ Here is an example:
95
122
 
96
123
  ```ruby
97
124
  bot.listen do |message|
@@ -131,7 +158,8 @@ end
131
158
 
132
159
  ## Inline keyboards
133
160
 
134
- [Bot API 2.0](https://core.telegram.org/bots/2-0-intro) brought us new inline keyboards. Example:
161
+ [Bot API 2.0](https://core.telegram.org/bots/2-0-intro) brought us new inline keyboards.
162
+ Example:
135
163
 
136
164
  ```ruby
137
165
  bot.listen do |message|
@@ -179,13 +207,25 @@ bot.listen do |message|
179
207
  end
180
208
  ```
181
209
 
182
- Now, with `inline` mode enabled, your `message` object can be an instance of [Message](https://core.telegram.org/bots/api#message), [InlineQuery](https://core.telegram.org/bots/api#inlinequery) or [ChosenInlineResult](https://core.telegram.org/bots/api#choseninlineresult). That's why you need to check type of each message and decide how to handle it.
210
+ Now, with `inline` mode enabled, your `message` object can be an instance of
211
+ [Message](https://core.telegram.org/bots/api#message),
212
+ [InlineQuery](https://core.telegram.org/bots/api#inlinequery) or
213
+ [ChosenInlineResult](https://core.telegram.org/bots/api#choseninlineresult).
214
+ That's why you need to check type of each message and decide how to handle it.
183
215
 
184
- Using `answer_inline_query` you can send query results to user. `results` field must be an array of [query result objects](https://core.telegram.org/bots/api#inlinequeryresult).
216
+ Using `answer_inline_query` you can send query results to user.
217
+ `results` field must be an array of [query result objects](https://core.telegram.org/bots/api#inlinequeryresult).
185
218
 
186
219
  ## File upload
187
220
 
188
- Your bot can even upload files ([photos](https://core.telegram.org/bots/api#sendphoto), [audio](https://core.telegram.org/bots/api#sendaudio), [documents](https://core.telegram.org/bots/api#senddocument), [stickers](https://core.telegram.org/bots/api#sendsticker), [video](https://core.telegram.org/bots/api#sendvideo)) to Telegram servers. Just like this:
221
+ Your bot can even upload files
222
+ ([photos](https://core.telegram.org/bots/api#sendphoto),
223
+ [audio](https://core.telegram.org/bots/api#sendaudio),
224
+ [documents](https://core.telegram.org/bots/api#senddocument),
225
+ [stickers](https://core.telegram.org/bots/api#sendsticker),
226
+ [video](https://core.telegram.org/bots/api#sendvideo))
227
+ to Telegram servers.
228
+ Just like this:
189
229
 
190
230
  ```ruby
191
231
  bot.listen do |message|
@@ -199,7 +239,9 @@ end
199
239
 
200
240
  ## Logging
201
241
 
202
- By default, bot doesn't log anything (uses `NullLoger`). You can change this behavior and provide your own logger class. See example below:
242
+ By default, bot doesn't log anything (uses `NullLoger`).
243
+ You can change this behavior and provide your own logger class.
244
+ See example below:
203
245
 
204
246
  ```ruby
205
247
  Telegram::Bot::Client.run(token, logger: Logger.new($stderr)) do |bot|
@@ -212,7 +254,8 @@ end
212
254
 
213
255
  ## Connection adapters
214
256
 
215
- Since version `0.5.0` we rely on [faraday](https://github.com/lostisland/faraday) under the hood. You can use any of supported adapters (for example, `net/http/persistent`):
257
+ Since version `0.5.0` we rely on [faraday](https://github.com/lostisland/faraday) under the hood.
258
+ You can use any of supported adapters (for example, `net/http/persistent`):
216
259
 
217
260
  ```ruby
218
261
  require 'net/http/persistent'
@@ -224,14 +267,15 @@ end
224
267
 
225
268
  ## Boilerplates
226
269
 
227
- If you don't know how to setup database for your bot or how to use it with different languages here are some boilerplates which can help you to start faster:
270
+ If you don't know how to setup database for your bot or how to use it with different languages
271
+ here are some boilerplates which can help you to start faster:
228
272
 
229
273
  - [Ruby Telegram Bot boilerplate](https://github.com/telegram-bots/ruby-telegram-bot-boilerplate)
230
274
 
231
275
  ## Contributing
232
276
 
233
- 1. Fork it
234
- 2. Create your feature branch (git checkout -b my-new-feature)
235
- 3. Commit your changes (git commit -am 'Add some feature')
236
- 4. Push to the branch (git push origin my-new-feature)
237
- 5. Create new Pull Request
277
+ 1. Fork it.
278
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
279
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
280
+ 4. Push to the branch (`git push origin my-new-feature`).
281
+ 5. Create a new Pull Request.
data/Rakefile CHANGED
@@ -18,7 +18,7 @@ task default: :spec
18
18
 
19
19
  desc 'Dump type definitions from docs to YAML'
20
20
  task :dump_type_attributes do
21
- require File.expand_path('lib/telegram/bot', __dir__)
21
+ require_relative 'lib/telegram/bot'
22
22
  require 'nokogiri'
23
23
  require 'open-uri'
24
24
  require 'yaml'
@@ -30,6 +30,8 @@ task :dump_type_attributes do
30
30
  # Fetch and parse docs
31
31
  doc = Nokogiri::HTML(URI.open('https://core.telegram.org/bots/api').read)
32
32
 
33
+ next_type_element_names = %w[table h4]
34
+
33
35
  result = types.to_h do |type|
34
36
  # This is very hacky but working way to find table containing attributes for
35
37
  # given type. Basic idea is to find heading with type and then iterate until
@@ -38,23 +40,23 @@ task :dump_type_attributes do
38
40
  element = doc.at_xpath(%{//h4[text() = "#{type}"]})
39
41
  loop do
40
42
  element = element.next_element
41
- break if %w[table h4].include?(element.name)
43
+ break if next_type_element_names.include?(element.name)
42
44
  end
43
45
 
44
46
  attributes = element.xpath('.//tbody//tr').map do |el|
45
47
  cells = el.children.select { |c| c.name == 'td' }
46
48
  {
47
49
  'name' => cells[0].text,
48
- 'required' => !cells[2].text.start_with?('Optional.')
49
- }
50
+ 'type' => cells[1].text,
51
+ 'required' => !cells[2].text.start_with?('Optional.'),
52
+ 'required_value' =>
53
+ cells[2].text.match(/^.+, (?:must be (?<found_type>\w+)|always “(?<found_type>\w+)”)$/)&.[](:found_type)
54
+ }.compact
50
55
  end
51
56
 
52
57
  [type, attributes]
53
58
  end
54
59
 
55
60
  # Write everything to fixture file
56
- File.write(
57
- File.expand_path('spec/support/type_attributes.yml', __dir__),
58
- result.to_yaml
59
- )
61
+ File.write "#{__dir__}/spec/support/type_attributes.yml", result.to_yaml
60
62
  end
data/bin/console CHANGED
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
2
5
  require 'bundler/setup'
3
6
  require 'pry'
4
7
 
5
- require File.expand_path('../lib/telegram/bot', __dir__)
8
+ require_relative '../lib/telegram/bot'
6
9
 
7
10
  Pry.start
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Telegram
4
+ module Bot
5
+ class Api
6
+ ENDPOINTS = {
7
+ 'getUpdates' => Types::Array.of(Types::Update),
8
+ 'setWebhook' => Types::Bool,
9
+ 'deleteWebhook' => Types::Bool,
10
+ 'getWebhookInfo' => Types::WebhookInfo,
11
+ 'getMe' => Types::User,
12
+ 'sendMessage' => Types::Message,
13
+ 'forwardMessage' => Types::Message,
14
+ 'forwardMessages' => Types::Array.of(Types::MessageId),
15
+ 'sendPhoto' => Types::Message,
16
+ 'sendAudio' => Types::Message,
17
+ 'sendDocument' => Types::Message,
18
+ 'sendVideo' => Types::Message,
19
+ 'sendVoice' => Types::Message,
20
+ 'sendVideoNote' => Types::Message,
21
+ 'sendMediaGroup' => Types::Array.of(Types::Message),
22
+ 'sendLocation' => Types::Message,
23
+ 'editMessageLiveLocation' => Types::Message | Types::Bool,
24
+ 'stopMessageLiveLocation' => Types::Message | Types::Bool,
25
+ 'sendVenue' => Types::Message,
26
+ 'sendContact' => Types::Message,
27
+ 'sendChatAction' => Types::Bool,
28
+ 'setMessageReaction' => Types::Bool,
29
+ 'getUserProfilePhotos' => Types::UserProfilePhotos,
30
+ 'getFile' => Types::File,
31
+ 'banChatMember' => Types::Bool,
32
+ 'unbanChatMember' => Types::Bool,
33
+ 'restrictChatMember' => Types::Bool,
34
+ 'promoteChatMember' => Types::Bool,
35
+ 'leaveChat' => Types::Bool,
36
+ 'getChat' => Types::Chat,
37
+ 'getChatAdministrators' => Types::Array.of(Types::ChatMember),
38
+ 'exportChatInviteLink' => Types::String,
39
+ 'setChatPhoto' => Types::Bool,
40
+ 'deleteChatPhoto' => Types::Bool,
41
+ 'setChatTitle' => Types::Bool,
42
+ 'setChatDescription' => Types::Bool,
43
+ 'pinChatMessage' => Types::Bool,
44
+ 'unpinChatMessage' => Types::Bool,
45
+ 'getChatMembersCount' => Types::Integer,
46
+ 'getChatMember' => Types::ChatMember,
47
+ 'setChatStickerSet' => Types::Bool,
48
+ 'deleteChatStickerSet' => Types::Bool,
49
+ 'answerCallbackQuery' => Types::Bool,
50
+ 'getUserChatBoosts' => Types::UserChatBoosts,
51
+ 'editMessageText' => Types::Message | Types::Bool,
52
+ 'editMessageCaption' => Types::Message | Types::Bool,
53
+ 'editMessageReplyMarkup' => Types::Message | Types::Bool,
54
+ 'deleteMessage' => Types::Bool,
55
+ 'deleteMessages' => Types::Bool,
56
+ 'sendSticker' => Types::Message,
57
+ 'getStickerSet' => Types::StickerSet,
58
+ 'uploadStickerFile' => Types::File,
59
+ 'createNewStickerSet' => Types::Bool,
60
+ 'addStickerToSet' => Types::Bool,
61
+ 'setStickerPositionInSet' => Types::Bool,
62
+ 'deleteStickerFromSet' => Types::Bool,
63
+ 'answerInlineQuery' => Types::Bool,
64
+ 'sendInvoice' => Types::Message,
65
+ 'answerShippingQuery' => Types::Bool,
66
+ 'answerPreCheckoutQuery' => Types::Bool,
67
+ 'sendGame' => Types::Message,
68
+ 'setGameScore' => Types::Message | Types::Bool,
69
+ 'getGameHighScores' => Types::Array.of(Types::GameHighScore),
70
+ 'setPassportDataErrors' => Types::Bool,
71
+ 'editMessageMedia' => Types::Message | Types::Bool,
72
+ 'sendAnimation' => Types::Message,
73
+ 'sendPoll' => Types::Message,
74
+ 'stopPoll' => Types::Poll,
75
+ 'setChatPermissions' => Types::Bool,
76
+ 'setChatAdministratorCustomTitle' => Types::Bool,
77
+ 'sendDice' => Types::Message,
78
+ 'getMyCommands' => Types::Array.of(Types::BotCommand),
79
+ 'setMyCommands' => Types::Bool,
80
+ 'deleteMyCommands' => Types::Bool,
81
+ 'setStickerSetThumbnail' => Types::Bool,
82
+ 'logOut' => Types::Bool,
83
+ 'close' => Types::Bool,
84
+ 'copyMessage' => Types::MessageId,
85
+ 'copyMessages' => Types::Array.of(Types::MessageId),
86
+ 'createChatInviteLink' => Types::ChatInviteLink,
87
+ 'editChatInviteLink' => Types::ChatInviteLink,
88
+ 'revokeChatInviteLink' => Types::ChatInviteLink,
89
+ 'approveChatJoinRequest' => Types::Bool,
90
+ 'declineChatJoinRequest' => Types::Bool,
91
+ 'banChatSenderChat' => Types::Bool,
92
+ 'unbanChatSenderChat' => Types::Bool,
93
+ 'answerWebAppQuery' => Types::SentWebAppMessage,
94
+ 'setChatMenuButton' => Types::Bool,
95
+ 'getChatMenuButton' => Types::MenuButtonDefault | Types::MenuButtonCommands | Types::MenuButtonWebApp,
96
+ 'setMyDefaultAdministratorRights' => Types::Bool,
97
+ 'getMyDefaultAdministratorRights' => Types::ChatAdministratorRights,
98
+ 'createInvoiceLink' => Types::String,
99
+ 'editGeneralForumTopic' => Types::Bool,
100
+ 'closeGeneralForumTopic' => Types::Bool,
101
+ 'reopenGeneralForumTopic' => Types::Bool,
102
+ 'hideGeneralForumTopic' => Types::Bool,
103
+ 'unhideGeneralForumTopic' => Types::Bool,
104
+ 'unpinAllGeneralForumTopicMessages' => Types::Bool,
105
+ 'setMyName' => Types::Bool,
106
+ 'getMyName' => Types::BotName,
107
+ 'setMyDescription' => Types::Bool,
108
+ 'getMyDescription' => Types::BotDescription,
109
+ 'setMyShortDescription' => Types::Bool,
110
+ 'getMyShortDescription' => Types::BotShortDescription
111
+ }.freeze
112
+ end
113
+ end
114
+ end
@@ -3,34 +3,6 @@
3
3
  module Telegram
4
4
  module Bot
5
5
  class Api
6
- ENDPOINTS = %w[
7
- getUpdates setWebhook deleteWebhook getWebhookInfo getMe sendMessage
8
- forwardMessage sendPhoto sendAudio sendDocument sendVideo sendVoice
9
- sendVideoNote sendMediaGroup sendLocation editMessageLiveLocation
10
- stopMessageLiveLocation sendVenue sendContact sendChatAction
11
- getUserProfilePhotos getFile kickChatMember unbanChatMember
12
- restrictChatMember promoteChatMember leaveChat getChat
13
- getChatAdministrators exportChatInviteLink setChatPhoto deleteChatPhoto
14
- setChatTitle setChatDescription pinChatMessage unpinChatMessage
15
- getChatMembersCount getChatMember setChatStickerSet deleteChatStickerSet
16
- answerCallbackQuery editMessageText editMessageCaption
17
- editMessageReplyMarkup deleteMessage sendSticker getStickerSet
18
- uploadStickerFile createNewStickerSet addStickerToSet
19
- setStickerPositionInSet deleteStickerFromSet answerInlineQuery
20
- sendInvoice answerShippingQuery answerPreCheckoutQuery
21
- sendGame setGameScore getGameHighScores setPassportDataErrors
22
- editMessageMedia sendAnimation sendPoll stopPoll setChatPermissions
23
- setChatAdministratorCustomTitle sendDice getMyCommands setMyCommands
24
- deleteMyCommands setStickerSetThumb logOut close copyMessage
25
- createChatInviteLink editChatInviteLink revokeChatInviteLink
26
- approveChatJoinRequest declineChatJoinRequest banChatSenderChat
27
- unbanChatSenderChat answerWebAppQuery setChatMenuButton
28
- getChatMenuButton setMyDefaultAdministratorRights
29
- getMyDefaultAdministratorRights createInvoiceLink editGeneralForumTopic
30
- closeGeneralForumTopic reopenGeneralForumTopic hideGeneralForumTopic
31
- unhideGeneralForumTopic
32
- ].freeze
33
-
34
6
  attr_reader :token, :url, :environment
35
7
 
36
8
  def initialize(token, url: 'https://api.telegram.org', environment: :production)
@@ -39,27 +11,41 @@ module Telegram
39
11
  @environment = environment.downcase.to_sym
40
12
  end
41
13
 
14
+ def connection
15
+ @connection ||= Faraday.new(url: url) do |faraday|
16
+ faraday.request :multipart
17
+ faraday.request :url_encoded
18
+ faraday.adapter Telegram::Bot.configuration.adapter
19
+ faraday.options.timeout = Telegram::Bot.configuration.connection_timeout
20
+ faraday.options.open_timeout = Telegram::Bot.configuration.connection_open_timeout
21
+ end
22
+ end
23
+
42
24
  def method_missing(method_name, *args, &block)
43
25
  endpoint = method_name.to_s
44
26
  endpoint = camelize(endpoint) if endpoint.include?('_')
45
27
 
46
- ENDPOINTS.include?(endpoint) ? call(endpoint, *args) : super
28
+ return super unless ENDPOINTS.key?(endpoint)
29
+
30
+ result = call(endpoint, *args)
31
+
32
+ return result['ok'] unless (result = result['result'])
33
+
34
+ ENDPOINTS[endpoint].call(result)
47
35
  end
48
36
 
49
37
  def respond_to_missing?(*args)
50
38
  method_name = args[0].to_s
51
39
  method_name = camelize(method_name) if method_name.include?('_')
52
40
 
53
- ENDPOINTS.include?(method_name) || super
41
+ ENDPOINTS.key?(method_name) || super
54
42
  end
55
43
 
56
44
  def call(endpoint, raw_params = {})
57
45
  params = build_params(raw_params)
58
46
  path = build_path(endpoint)
59
- response = conn.post(path, params)
60
- unless response.status == 200
61
- raise Exceptions::ResponseError.new(response), 'Telegram API has returned the error.'
62
- end
47
+ response = connection.post(path, params)
48
+ raise Exceptions::ResponseError.new(response: response) unless response.status == 200
63
49
 
64
50
  JSON.parse(response.body)
65
51
  end
@@ -95,16 +81,6 @@ module Telegram
95
81
  words.drop(1).map(&:capitalize!)
96
82
  words.join
97
83
  end
98
-
99
- def conn
100
- @conn ||= Faraday.new(url: url) do |faraday|
101
- faraday.request :multipart
102
- faraday.request :url_encoded
103
- faraday.adapter Telegram::Bot.configuration.adapter
104
- faraday.options.timeout = Telegram::Bot.configuration.connection_timeout
105
- faraday.options.open_timeout = Telegram::Bot.configuration.connection_open_timeout
106
- end
107
- end
108
84
  end
109
85
  end
110
86
  end
@@ -22,18 +22,17 @@ module Telegram
22
22
 
23
23
  def listen(&block)
24
24
  logger.info('Starting bot')
25
- running = true
26
- Signal.trap('INT') { running = false }
27
- fetch_updates(&block) while running
28
- exit
25
+ @running = true
26
+ fetch_updates(&block) while @running
29
27
  end
30
28
 
31
- def fetch_updates
32
- response = api.getUpdates(options)
33
- return unless response['ok']
29
+ def stop
30
+ @running = false
31
+ end
34
32
 
35
- response['result'].each do |data|
36
- yield handle_update(Types::Update.new(data))
33
+ def fetch_updates
34
+ api.getUpdates(options).each do |update|
35
+ yield handle_update(update)
37
36
  end
38
37
  rescue Faraday::TimeoutError, Faraday::ConnectionFailed
39
38
  retry
@@ -6,22 +6,16 @@ module Telegram
6
6
  class ResponseError < Base
7
7
  attr_reader :response
8
8
 
9
- def initialize(response)
9
+ def initialize(response:)
10
10
  @response = response
11
- super
12
- end
13
11
 
14
- def to_s
15
- super +
16
- format(' (%s)', data.map { |k, v| %(#{k}: "#{v}") }.join(', '))
12
+ super "Telegram API has returned the error. (#{data.map { |k, v| %(#{k}: #{v.inspect}) }.join(', ')})"
17
13
  end
18
14
 
19
15
  def error_code
20
16
  data[:error_code] || data['error_code']
21
17
  end
22
18
 
23
- private
24
-
25
19
  def data
26
20
  @data ||= begin
27
21
  JSON.parse(response.body)
@@ -9,7 +9,7 @@ module Telegram
9
9
  attribute :width, Types::Integer
10
10
  attribute :height, Types::Integer
11
11
  attribute :duration, Types::Integer
12
- attribute? :thumb, PhotoSize
12
+ attribute? :thumbnail, PhotoSize
13
13
  attribute? :file_name, Types::String
14
14
  attribute? :mime_type, Types::String
15
15
  attribute? :file_size, Types::Integer
@@ -12,7 +12,7 @@ module Telegram
12
12
  attribute? :file_name, Types::String
13
13
  attribute? :mime_type, Types::String
14
14
  attribute? :file_size, Types::Integer
15
- attribute? :thumb, PhotoSize
15
+ attribute? :thumbnail, PhotoSize
16
16
  end
17
17
  end
18
18
  end