telegem 3.1.3 → 3.2.1
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 +4 -4
- data/Readme.md +3 -3
- data/assets/.gitkeep +0 -0
- data/assets/logo.png +0 -0
- data/lib/api/client.rb +89 -80
- data/lib/core/bot.rb +114 -137
- data/lib/core/context.rb +76 -0
- data/lib/core/rate_limit.rb +3 -2
- data/lib/markup/keyboard.rb +7 -10
- data/lib/telegem.rb +2 -2
- metadata +4 -3
- data/.replit +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d61b868c4601e8372d69f3a30af35e5efb2cfe7024c518c2c239804dd2914e7a
|
|
4
|
+
data.tar.gz: 38069437b5d718560e1480cc3846160bbdcd33a1b03380aadf6785692ebdecf3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '095647f0f51efff1afafc5233341506b76780288aafba0dd87aae6a9ffe42f5115aaf0aaf2a0f5db4034d2a053a7ff0d75d966b1fea34aa7e20e082e06b7bf1d'
|
|
7
|
+
data.tar.gz: fbe3e2f72ac63184f8378ca98108b9a16f87cfcf91219260de04a65bf1945dbdcec4c0d5b49bd82295714e478ad2798a7ffec38e86f411f4a13b6b5524a40dbc
|
data/Readme.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Telegem
|
|
1
|
+

|
|
2
2
|
|
|
3
3
|
Modern, blazing-fast async Telegram Bot API for Ruby - Inspired by Telegraf, built for performance.
|
|
4
4
|
|
|
@@ -17,7 +17,7 @@ Blazing-fast, modern Telegram Bot framework for Ruby. Inspired by Telegraf.js, b
|
|
|
17
17
|
|
|
18
18
|
✨ Features
|
|
19
19
|
|
|
20
|
-
- ⚡ True
|
|
20
|
+
- ⚡ True Async I/O - Built on async gem, not blocking threads
|
|
21
21
|
- 🎯 Telegraf-style DSL - Familiar API for JavaScript developers
|
|
22
22
|
- 🔌 Middleware System - Compose behavior like Express.js
|
|
23
23
|
- 🧙 Scene System - Multi-step conversations (wizards/forms)
|
|
@@ -108,7 +108,7 @@ Perfect For:
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
-
📚 Documentation
|
|
111
|
+
[📚 Documentation](https://www.rubydoc.info/gems/telegem/3.2.0/index)
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
|
data/assets/.gitkeep
ADDED
|
File without changes
|
data/assets/logo.png
ADDED
|
Binary file
|
data/lib/api/client.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'async/http'
|
|
2
2
|
require 'json'
|
|
3
3
|
|
|
4
4
|
module Telegem
|
|
@@ -6,112 +6,121 @@ module Telegem
|
|
|
6
6
|
class Client
|
|
7
7
|
BASE_URL = 'https://api.telegram.org'
|
|
8
8
|
|
|
9
|
-
attr_reader :token, :logger
|
|
9
|
+
attr_reader :token, :logger
|
|
10
10
|
|
|
11
11
|
def initialize(token, **options)
|
|
12
12
|
@token = token
|
|
13
|
-
@mutex = Mutex.new
|
|
14
13
|
@logger = options[:logger] || Logger.new($stdout)
|
|
15
|
-
timeout = options[:timeout] || 30
|
|
14
|
+
@timeout = options[:timeout] || 30
|
|
16
15
|
|
|
17
|
-
@
|
|
18
|
-
|
|
19
|
-
request_timeout: timeout,
|
|
20
|
-
connect_timeout: 10,
|
|
21
|
-
write_timeout: 10,
|
|
22
|
-
read_timeout: timeout
|
|
23
|
-
},
|
|
24
|
-
headers: {
|
|
25
|
-
'Content-Type' => 'application/json',
|
|
26
|
-
'User-Agent' => "Telegem/#{Telegem::VERSION}"
|
|
27
|
-
}
|
|
28
|
-
)
|
|
16
|
+
@endpoint = Async::HTTP::Endpoint.parse(BASE_URL)
|
|
17
|
+
@client = Async::HTTP::Client.new(@endpoint)
|
|
29
18
|
end
|
|
30
19
|
|
|
31
|
-
def call(method, params = {})
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if json && json['ok']
|
|
37
|
-
json['result']
|
|
38
|
-
else
|
|
39
|
-
raise APIError.new(json ? json['description'] : "Api Error")
|
|
40
|
-
end
|
|
41
|
-
end
|
|
20
|
+
def call(method, params = {})
|
|
21
|
+
Async do
|
|
22
|
+
make_request(method, params)
|
|
23
|
+
end.wait
|
|
24
|
+
end
|
|
42
25
|
|
|
43
26
|
def call!(method, params = {}, &callback)
|
|
44
|
-
url = "#{BASE_URL}/bot#{@token}/#{method}"
|
|
45
27
|
return unless callback
|
|
46
28
|
|
|
47
|
-
|
|
48
|
-
begin
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
error_code = json['error_code'] if json
|
|
57
|
-
callback.call(nil, APIError.new("API ERROR #{error_msg}", error_code))
|
|
58
|
-
end
|
|
59
|
-
else
|
|
60
|
-
callback.call(nil, NetworkError.new("HTTP #{response.status}"))
|
|
61
|
-
end
|
|
62
|
-
rescue JSON::ParserError
|
|
63
|
-
callback.call(nil, NetworkError.new("Invalid Json response"))
|
|
64
|
-
rescue => e
|
|
65
|
-
callback.call(nil, e)
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
29
|
+
Async do
|
|
30
|
+
begin
|
|
31
|
+
result = make_request(method, params)
|
|
32
|
+
callback.call(result, nil)
|
|
33
|
+
rescue => error
|
|
34
|
+
callback.call(nil, error)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
69
38
|
|
|
70
39
|
def upload(method, params)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
40
|
+
Async do
|
|
41
|
+
url = "/bot#{@token}/#{method}"
|
|
42
|
+
|
|
43
|
+
body = Async::HTTP::Body::Multipart.new
|
|
44
|
+
|
|
45
|
+
params.each do |key, value|
|
|
46
|
+
if file_object?(value)
|
|
47
|
+
body.add(key.to_s, value, filename: File.basename(value))
|
|
48
|
+
else
|
|
49
|
+
body.add(key.to_s, value.to_s)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
response = @client.post(url, {}, body)
|
|
54
|
+
handle_response(response)
|
|
55
|
+
end.wait
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def download(file_id, destination_path = nil)
|
|
59
|
+
Async do
|
|
60
|
+
file_info = call('getFile', file_id: file_id)
|
|
61
|
+
return nil unless file_info && file_info['file_path']
|
|
62
|
+
|
|
63
|
+
file_path = file_info['file_path']
|
|
64
|
+
download_url = "/file/bot#{@token}/#{file_path}"
|
|
65
|
+
|
|
66
|
+
response = @client.get(download_url)
|
|
67
|
+
|
|
68
|
+
if response.status == 200
|
|
69
|
+
content = response.read
|
|
70
|
+
if destination_path
|
|
71
|
+
File.binwrite(destination_path, content)
|
|
72
|
+
destination_path
|
|
73
|
+
else
|
|
74
|
+
content
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
raise NetworkError.new("Download failed: HTTP #{response.status}")
|
|
78
|
+
end
|
|
79
|
+
end.wait
|
|
74
80
|
end
|
|
75
81
|
|
|
76
|
-
def download(file_id, destination_path = nil)
|
|
77
|
-
file_info = call('getFile', file_id: file_id)
|
|
78
|
-
return nil unless file_info && file_info['file_path']
|
|
79
|
-
file_path = file_info['file_path']
|
|
80
|
-
download_url = "#{BASE_URL}/file/bot#{@token}/#{file_path}"
|
|
81
|
-
@logger.debug("downloading.. #{download_url}") if @logger
|
|
82
|
-
response = @http.get(download_url)
|
|
83
|
-
if response.status == 200
|
|
84
|
-
if destination_path
|
|
85
|
-
File.binwrite(destination_path, response.body.to_s)
|
|
86
|
-
@logger.debug("saved to #{destination_path}") if @logger
|
|
87
|
-
destination_path
|
|
88
|
-
else
|
|
89
|
-
response.body.to_s
|
|
90
|
-
end
|
|
91
|
-
else
|
|
92
|
-
raise NetworkError.new("Download failed : #{response.status}")
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
82
|
def get_updates(offset: nil, timeout: 30, limit: 100, allowed_updates: nil)
|
|
97
83
|
params = { timeout: timeout, limit: limit }
|
|
98
84
|
params[:offset] = offset if offset
|
|
99
85
|
params[:allowed_updates] = allowed_updates if allowed_updates
|
|
100
86
|
call('getUpdates', params)
|
|
101
87
|
end
|
|
102
|
-
|
|
88
|
+
|
|
103
89
|
def close
|
|
104
|
-
@
|
|
90
|
+
@client.close
|
|
105
91
|
end
|
|
106
|
-
|
|
92
|
+
|
|
107
93
|
private
|
|
108
|
-
|
|
94
|
+
|
|
95
|
+
def make_request(method, params)
|
|
96
|
+
url = "/bot#{@token}/#{method}"
|
|
97
|
+
@logger.debug("Api call #{method}") if @logger
|
|
98
|
+
|
|
99
|
+
response = @client.post(
|
|
100
|
+
url,
|
|
101
|
+
{ 'content-type' => 'application/json' },
|
|
102
|
+
JSON.dump(params.compact)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
handle_response(response)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def handle_response(response)
|
|
109
|
+
json = JSON.parse(response.read)
|
|
110
|
+
|
|
111
|
+
if json && json['ok']
|
|
112
|
+
json['result']
|
|
113
|
+
else
|
|
114
|
+
raise APIError.new(json ? json['description'] : "Api Error")
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
109
118
|
def file_object?(obj)
|
|
110
119
|
obj.is_a?(File) || obj.is_a?(StringIO) || obj.is_a?(Tempfile) ||
|
|
111
120
|
(obj.is_a?(String) && File.exist?(obj))
|
|
112
121
|
end
|
|
113
122
|
end
|
|
114
|
-
|
|
123
|
+
|
|
115
124
|
class APIError < StandardError
|
|
116
125
|
attr_reader :code
|
|
117
126
|
|
|
@@ -120,7 +129,7 @@ module Telegem
|
|
|
120
129
|
@code = code
|
|
121
130
|
end
|
|
122
131
|
end
|
|
123
|
-
|
|
132
|
+
|
|
124
133
|
class NetworkError < APIError; end
|
|
125
134
|
end
|
|
126
|
-
end
|
|
135
|
+
end
|
data/lib/core/bot.rb
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require 'logger'
|
|
3
|
-
require 'async'
|
|
1
|
+
require 'json'
|
|
4
2
|
|
|
5
3
|
module Telegem
|
|
6
4
|
module Core
|
|
@@ -39,13 +37,13 @@ module Telegem
|
|
|
39
37
|
@polling_options = options.slice(:timeout, :limit, :allowed_updates) || {}
|
|
40
38
|
end
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
def start_polling(**options)
|
|
41
|
+
@running = true
|
|
42
|
+
@polling_options = options
|
|
43
|
+
Async do
|
|
44
|
+
poll_loop
|
|
45
|
+
end
|
|
47
46
|
end
|
|
48
|
-
end
|
|
49
47
|
|
|
50
48
|
def shutdown
|
|
51
49
|
return unless @running
|
|
@@ -54,6 +52,7 @@ module Telegem
|
|
|
54
52
|
@running = false
|
|
55
53
|
sleep 0.1
|
|
56
54
|
end
|
|
55
|
+
|
|
57
56
|
def running?
|
|
58
57
|
@running
|
|
59
58
|
end
|
|
@@ -78,7 +77,7 @@ module Telegem
|
|
|
78
77
|
def contact(**options, &block)
|
|
79
78
|
on(:message, contact: true) do |ctx|
|
|
80
79
|
block.call(ctx)
|
|
81
|
-
|
|
80
|
+
end
|
|
82
81
|
end
|
|
83
82
|
|
|
84
83
|
def poll_answer(&block)
|
|
@@ -87,10 +86,10 @@ module Telegem
|
|
|
87
86
|
end
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
def pre_checkout_query(&block)
|
|
90
|
+
on(:pre_checkout_query) do |ctx|
|
|
91
|
+
block.call(ctx)
|
|
92
|
+
end
|
|
94
93
|
end
|
|
95
94
|
|
|
96
95
|
def shipping_query(&block)
|
|
@@ -100,34 +99,35 @@ module Telegem
|
|
|
100
99
|
end
|
|
101
100
|
|
|
102
101
|
def chat_join_request(&block)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
end
|
|
102
|
+
on(:chat_join_request) do |ctx|
|
|
103
|
+
block.call(ctx)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
def chat_boost(&block)
|
|
108
|
+
on(:chat_boost) do |ctx|
|
|
109
|
+
block.call(ctx)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
def removed_chat_boost(&block)
|
|
114
|
+
on(:removed_chat_boost) do |ctx|
|
|
115
|
+
block.call(ctx)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
def message_reaction(&block)
|
|
120
|
+
on(:message_reaction) do |ctx|
|
|
121
|
+
block.call(ctx)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
125
124
|
|
|
126
|
-
def message_reaction_count(&block)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
end
|
|
125
|
+
def message_reaction_count(&block)
|
|
126
|
+
on(:message_reaction_count) do |ctx|
|
|
127
|
+
block.call(ctx)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
131
|
def web_app_data(&block)
|
|
132
132
|
on(:message, web_app_data: true) do |ctx|
|
|
133
133
|
block.call(ctx)
|
|
@@ -170,106 +170,82 @@ end
|
|
|
170
170
|
end
|
|
171
171
|
|
|
172
172
|
def set_webhook(url, **options, &callback)
|
|
173
|
-
|
|
173
|
+
if callback
|
|
174
|
+
@api.call!('setWebhook', { url: url }.merge(options), &callback)
|
|
175
|
+
else
|
|
176
|
+
@api.call('setWebhook', { url: url }.merge(options))
|
|
177
|
+
end
|
|
174
178
|
end
|
|
175
179
|
|
|
176
180
|
def delete_webhook(&callback)
|
|
177
|
-
|
|
181
|
+
if callback
|
|
182
|
+
@api.call!('deleteWebhook', {}, &callback)
|
|
183
|
+
else
|
|
184
|
+
@api.call('deleteWebhook', {})
|
|
185
|
+
end
|
|
178
186
|
end
|
|
179
187
|
|
|
180
188
|
def get_webhook_info(&callback)
|
|
181
|
-
|
|
189
|
+
if callback
|
|
190
|
+
@api.call!('getWebhookInfo', {}, &callback)
|
|
191
|
+
else
|
|
192
|
+
@api.call('getWebhookInfo', {})
|
|
193
|
+
end
|
|
182
194
|
end
|
|
183
195
|
|
|
184
196
|
def process(update_data)
|
|
185
197
|
update = Types::Update.new(update_data)
|
|
186
198
|
process_update(update)
|
|
187
199
|
end
|
|
200
|
+
|
|
201
|
+
|
|
188
202
|
|
|
189
203
|
private
|
|
190
204
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
params[:offset] = @offset if @offset
|
|
217
|
-
params[:allowed_updates] = @polling_options[:allowed_updates] if @polling_options[:allowed_updates]
|
|
218
|
-
|
|
219
|
-
@logger.debug "Fetching updates with offset: #{@offset}"
|
|
220
|
-
|
|
221
|
-
@api.call!('getUpdates', params) do |updates_array, error|
|
|
222
|
-
if error
|
|
223
|
-
@logger.error "Polling error: #{error.message}"
|
|
224
|
-
completion_callback.call(nil, error) if completion_callback
|
|
225
|
-
else
|
|
226
|
-
|
|
227
|
-
# Success
|
|
228
|
-
if updates_array && updates_array.is_a?(Array)
|
|
229
|
-
result = { 'ok' => true, 'result' => updates_array }
|
|
230
|
-
completion_callback.call(result, nil) if completion_callback
|
|
231
|
-
else
|
|
232
|
-
completion_callback.call(nil, nil) if completion_callback
|
|
205
|
+
def poll_loop
|
|
206
|
+
while @running
|
|
207
|
+
begin
|
|
208
|
+
updates = @api.get_updates(
|
|
209
|
+
offset: @offset,
|
|
210
|
+
timeout: @polling_options[:timeout] || 30,
|
|
211
|
+
limit: @polling_options[:limit] || 100,
|
|
212
|
+
allowed_updates: @polling_options[:allowed_updates]
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
if updates && updates.any?
|
|
216
|
+
updates.each do |update_data|
|
|
217
|
+
Async do
|
|
218
|
+
update = Types::Update.new(update_data)
|
|
219
|
+
process_update(update)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
@offset = updates.last['update_id'] + 1
|
|
224
|
+
@logger.debug("Updated offset to: #{@offset}")
|
|
225
|
+
end
|
|
226
|
+
rescue => e
|
|
227
|
+
@logger.error("Poll loop error: #{e.message}")
|
|
228
|
+
sleep 1
|
|
233
229
|
end
|
|
234
|
-
|
|
235
|
-
end
|
|
230
|
+
end
|
|
236
231
|
end
|
|
237
|
-
|
|
238
|
-
|
|
239
232
|
|
|
240
|
-
def handle_updates_response(api_response)
|
|
241
|
-
if api_response['ok']
|
|
242
|
-
updates = api_response['result'] || []
|
|
243
|
-
updates.each do |data|
|
|
244
|
-
Async do |task|
|
|
245
|
-
update_object = Types::Update.new(data)
|
|
246
|
-
process_update(update_object)
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
if updates.any?
|
|
250
|
-
@offset = updates.last['update_id'] + 1
|
|
251
|
-
@logger.debug "Updated offset to; #{@offset}"
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
233
|
def process_update(update)
|
|
257
234
|
if update.message&.text && @logger
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
235
|
+
user = update.message.from
|
|
236
|
+
cmd = update.message.text.split.first
|
|
237
|
+
@logger.info("#{cmd} - #{user.username}")
|
|
261
238
|
end
|
|
262
|
-
|
|
263
|
-
ctx = Context.new(update, self)
|
|
264
|
-
|
|
265
|
-
begin
|
|
266
|
-
run_middleware_chain(ctx) do |context|
|
|
267
|
-
dispatch_to_handlers(context)
|
|
268
|
-
end
|
|
269
|
-
rescue => e
|
|
270
|
-
handle_error(e, ctx)
|
|
271
|
-
end
|
|
272
239
|
|
|
240
|
+
ctx = Context.new(update, self)
|
|
241
|
+
|
|
242
|
+
begin
|
|
243
|
+
run_middleware_chain(ctx) do |context|
|
|
244
|
+
dispatch_to_handlers(context)
|
|
245
|
+
end
|
|
246
|
+
rescue => e
|
|
247
|
+
handle_error(e, ctx)
|
|
248
|
+
end
|
|
273
249
|
end
|
|
274
250
|
|
|
275
251
|
def run_middleware_chain(ctx, &final)
|
|
@@ -290,19 +266,20 @@ end
|
|
|
290
266
|
end
|
|
291
267
|
|
|
292
268
|
unless @middleware.any? { |m, _, _| m.to_s =~ /Scene/ }
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
269
|
+
begin
|
|
270
|
+
require_relative '../session/scene_middleware'
|
|
271
|
+
chain.use(Telegem::Scene::Middleware.new)
|
|
272
|
+
rescue LoadError => e
|
|
273
|
+
@logger.debug("Scene middleware not available: #{e.message}") if @logger
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
unless @middleware.any? { |m, _, _| m.is_a?(Session::Middleware) }
|
|
278
|
+
chain.use(Session::Middleware.new(@session_store))
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
chain
|
|
282
|
+
end
|
|
306
283
|
|
|
307
284
|
def dispatch_to_handlers(ctx)
|
|
308
285
|
update_type = detect_update_type(ctx.update)
|
|
@@ -347,16 +324,16 @@ end
|
|
|
347
324
|
when :location
|
|
348
325
|
ctx.message&.location != nil
|
|
349
326
|
when :contact
|
|
350
|
-
|
|
327
|
+
ctx.message&.contact != nil
|
|
351
328
|
when :web_app_data
|
|
352
329
|
ctx.message&.web_app_data != nil
|
|
353
330
|
else
|
|
354
|
-
if
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
331
|
+
if ctx.update.respond_to?(key)
|
|
332
|
+
ctx.update.send(key) == value
|
|
333
|
+
else
|
|
334
|
+
false
|
|
335
|
+
end
|
|
336
|
+
end
|
|
360
337
|
end
|
|
361
338
|
end
|
|
362
339
|
|
|
@@ -392,4 +369,4 @@ end
|
|
|
392
369
|
end
|
|
393
370
|
end
|
|
394
371
|
end
|
|
395
|
-
end
|
|
372
|
+
end
|
data/lib/core/context.rb
CHANGED
|
@@ -42,6 +42,82 @@ module Telegem
|
|
|
42
42
|
inline_query&.query
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def message_id
|
|
46
|
+
message&.message_id
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def message_date
|
|
50
|
+
message&.date
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def edit_date
|
|
54
|
+
message&.edit_date
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def command_name
|
|
58
|
+
message&.command_name
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def has_media?
|
|
62
|
+
message&.has_media? || false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def media_type
|
|
66
|
+
message&.media_type
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def entities
|
|
70
|
+
message&.entities || []
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def caption_entities
|
|
74
|
+
message&caption_entities || []
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def caption
|
|
78
|
+
message&.caption
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def reply?
|
|
82
|
+
message&.reply?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def replied_message
|
|
86
|
+
message&.reply_to_message
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def replied_text
|
|
90
|
+
replied_message&.text
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def replied_from
|
|
94
|
+
replied_message&.from
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def replied_chat
|
|
98
|
+
replied_message&.chat
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def update_type
|
|
102
|
+
@update.type
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def is_edited?
|
|
106
|
+
!!@update.edited_message
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def channel_post?
|
|
110
|
+
update_type == :channel_post
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def callback_query?
|
|
114
|
+
update_type == :callback_query
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def inline_query?
|
|
118
|
+
update_type == :inline_query
|
|
119
|
+
end
|
|
120
|
+
|
|
45
121
|
def reply(text, **options)
|
|
46
122
|
return nil unless chat
|
|
47
123
|
|
data/lib/core/rate_limit.rb
CHANGED
|
@@ -88,7 +88,8 @@ module Telegem
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def cleanup_counter(type, key, now)
|
|
91
|
-
|
|
91
|
+
expires = @counters[type].get_ttl(key) || now
|
|
92
|
+
@counters[type].delete(key) if now > expires
|
|
92
93
|
end
|
|
93
94
|
|
|
94
95
|
def rate_limit_response(ctx)
|
|
@@ -96,5 +97,5 @@ module Telegem
|
|
|
96
97
|
ctx.reply("⏳ Please wait a moment before sending another request.") rescue nil
|
|
97
98
|
nil
|
|
98
99
|
end
|
|
99
|
-
end
|
|
100
|
+
end
|
|
100
101
|
end
|
data/lib/markup/keyboard.rb
CHANGED
|
@@ -152,16 +152,13 @@ module Telegem
|
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
def to_h
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
inline_keyboard: clean_rows
|
|
163
|
-
}
|
|
164
|
-
end
|
|
155
|
+
clean_rows = @buttons.compact.map do |row|
|
|
156
|
+
row = Array(row).compact.select { |btn| is_a?(Hash) }
|
|
157
|
+
row.empty? ? nil : row
|
|
158
|
+
end.compact
|
|
159
|
+
{ inline_keyboard: clean_rows}
|
|
160
|
+
end
|
|
161
|
+
|
|
165
162
|
def to_json(*args)
|
|
166
163
|
to_h.to_json(*args)
|
|
167
164
|
end
|
data/lib/telegem.rb
CHANGED
|
@@ -3,7 +3,7 @@ require 'logger'
|
|
|
3
3
|
require 'json'
|
|
4
4
|
|
|
5
5
|
module Telegem
|
|
6
|
-
VERSION = "3.1
|
|
6
|
+
VERSION = "3.2.1".freeze
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
# Load core components
|
|
@@ -82,4 +82,4 @@ if ENV['TELEGEM_GLOBAL'] == 'true'
|
|
|
82
82
|
def Telegem(token, **options)
|
|
83
83
|
::Telegem.new(token, **options)
|
|
84
84
|
end
|
|
85
|
-
end
|
|
85
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: telegem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.1
|
|
4
|
+
version: 3.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- sick_phantom
|
|
@@ -118,7 +118,6 @@ executables:
|
|
|
118
118
|
extensions: []
|
|
119
119
|
extra_rdoc_files: []
|
|
120
120
|
files:
|
|
121
|
-
- ".replit"
|
|
122
121
|
- CHANGELOG
|
|
123
122
|
- CODE_OF_CONDUCT.md
|
|
124
123
|
- Contributing.md
|
|
@@ -127,6 +126,8 @@ files:
|
|
|
127
126
|
- LICENSE
|
|
128
127
|
- Readme.md
|
|
129
128
|
- Starts_HallofFame.md
|
|
129
|
+
- assets/.gitkeep
|
|
130
|
+
- assets/logo.png
|
|
130
131
|
- bin/.gitkeep
|
|
131
132
|
- bin/telegem-ssl
|
|
132
133
|
- examples/.gitkeep
|
|
@@ -158,7 +159,7 @@ metadata:
|
|
|
158
159
|
bug_tracker_uri: https://gitlab.com/ruby-telegem/telegem/-/issues
|
|
159
160
|
documentation_uri: https://gitlab.com/ruby-telegem/telegem/-/tree/main/docs-src?ref_type=heads
|
|
160
161
|
rubygems_mfa_required: 'false'
|
|
161
|
-
post_install_message: "Thanks for installing Telegem 3.1
|
|
162
|
+
post_install_message: "Thanks for installing Telegem 3.2.1!\n\n\U0001F4DA Documentation:
|
|
162
163
|
https://gitlab.com/ruby-telegem/telegem\n\n\U0001F510 For SSL Webhooks:\nRun: telegem-ssl
|
|
163
164
|
your-domain.com\nThis sets up Let's Encrypt certificates automatically.\n\n\U0001F916
|
|
164
165
|
Happy bot building!\n"
|
data/.replit
DELETED