telegem 3.0.6 → 3.1.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 +4 -4
- data/lib/core/bot.rb +15 -7
- data/lib/core/context.rb +43 -18
- data/lib/core/rate_limit.rb +100 -0
- data/lib/core/scene.rb +110 -43
- data/lib/plugins/.gitkeep +0 -0
- data/lib/plugins/file_extract.rb +127 -0
- data/lib/session/scene_middleware.rb +23 -0
- data/lib/telegem.rb +4 -2
- metadata +25 -75
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5551464d15fac54752c3b598184502f34b986ff5450c9afc11dda12e582bc90a
|
|
4
|
+
data.tar.gz: 80e223030e618a3e2f997cb15e8692ea7f74b620035a3fdeebe795c42c8422f6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0604f9c803fdddd15e90f2b187404173bd512a827c262cb82a31a9f642a3595033a8415fadba3197c8bd9b12347686b1bf3d8fdc53ba973b43cce03db56bcc05
|
|
7
|
+
data.tar.gz: 81bd29e6218605f201b98d51d30cfe9650fbafb1b7c1645a257ccb946b6d2bec8e9b7fad2668c6c1daa057bae7f4dbc8711dbd3698ada5dfc402c2fc0f82205c
|
data/lib/core/bot.rb
CHANGED
|
@@ -255,7 +255,7 @@ end
|
|
|
255
255
|
if update.message&.text && @logger
|
|
256
256
|
user = update.message.from
|
|
257
257
|
cmd = update.message.text.split.first
|
|
258
|
-
@logger.info("#{cmd} - #{user.username
|
|
258
|
+
@logger.info("#{cmd} - #{user.username}")
|
|
259
259
|
end
|
|
260
260
|
|
|
261
261
|
ctx = Context.new(update, self)
|
|
@@ -287,12 +287,20 @@ end
|
|
|
287
287
|
end
|
|
288
288
|
end
|
|
289
289
|
|
|
290
|
-
unless @middleware.any? { |m, _, _| m.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
290
|
+
unless @middleware.any? { |m, _, _| m.to_s =~ /Scene/ }
|
|
291
|
+
begin
|
|
292
|
+
require_relative '../session/scene_middleware'
|
|
293
|
+
chain.use(Telegem::Scene::Middleware.new)
|
|
294
|
+
rescue LoadError => e
|
|
295
|
+
@logger.debug("Scene middleware not available: #{e.message}") if @logger
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
unless @middleware.any? { |m, _, _| m.is_a?(Session::Middleware) }
|
|
299
|
+
chain.use(Session::Middleware.new(@session_store))
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
chain
|
|
303
|
+
end
|
|
296
304
|
|
|
297
305
|
def dispatch_to_handlers(ctx)
|
|
298
306
|
update_type = detect_update_type(ctx.update)
|
data/lib/core/context.rb
CHANGED
|
@@ -321,7 +321,43 @@ module Telegem
|
|
|
321
321
|
def uploading_document(**options)
|
|
322
322
|
send_chat_action('upload_document', **options)
|
|
323
323
|
end
|
|
324
|
-
|
|
324
|
+
def scene
|
|
325
|
+
session[:telegem_scene]&.[](:id)
|
|
326
|
+
end
|
|
327
|
+
def ask(question, **options)
|
|
328
|
+
scene_data = session[:telegem_scene]
|
|
329
|
+
if scene_data
|
|
330
|
+
scene_data[:waiting_for_response] = true
|
|
331
|
+
scene_data[:last_question] = question
|
|
332
|
+
end
|
|
333
|
+
reply(question, **options)
|
|
334
|
+
end
|
|
335
|
+
def scene_data
|
|
336
|
+
@session[:telegem_scene]&.[](:data) || {}
|
|
337
|
+
end
|
|
338
|
+
def current_scene
|
|
339
|
+
@session[:telegem_scene]&.[](:id)
|
|
340
|
+
end
|
|
341
|
+
def in_scene?
|
|
342
|
+
!current_scene.nil?
|
|
343
|
+
end
|
|
344
|
+
def leave_scene(**options)
|
|
345
|
+
scene_data = @session[:telegem_scene]
|
|
346
|
+
return unless scene_data
|
|
347
|
+
scene_id = scene_data[:id].to_sym
|
|
348
|
+
scene = @bot.scenes[scene_id]
|
|
349
|
+
result = scene&.leave(self, options[:reason] || :manual)
|
|
350
|
+
@session.delete(:telegem_scene)
|
|
351
|
+
@scene = nil
|
|
352
|
+
result
|
|
353
|
+
end
|
|
354
|
+
def next_step(step_name = nil)
|
|
355
|
+
scene_data = @session[:telegem_scene]
|
|
356
|
+
return unless scene_data
|
|
357
|
+
scene_id = scene_data[:id].to_sym
|
|
358
|
+
scene = @bot.scenes[scene_id]
|
|
359
|
+
scene&.next_step(self, step_name)
|
|
360
|
+
end
|
|
325
361
|
def with_typing(&block)
|
|
326
362
|
typing_request = typing
|
|
327
363
|
|
|
@@ -339,23 +375,12 @@ module Telegem
|
|
|
339
375
|
end
|
|
340
376
|
|
|
341
377
|
def enter_scene(scene_name, **options)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
def leave_scene(**options)
|
|
349
|
-
return nil unless @scene && @bot.scenes[@scene]
|
|
350
|
-
|
|
351
|
-
scene_name = @scene
|
|
352
|
-
@scene = nil
|
|
353
|
-
@bot.scenes[scene_name].leave(self, **options)
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def current_scene
|
|
357
|
-
@bot.scenes[@scene] if @scene
|
|
358
|
-
end
|
|
378
|
+
scene = @bot.scenes[scene_name]
|
|
379
|
+
return nil unless scene
|
|
380
|
+
leave_scene if in_scene?
|
|
381
|
+
scene.enter(self, options[:step], options.except(:step))
|
|
382
|
+
scene_name
|
|
383
|
+
end
|
|
359
384
|
|
|
360
385
|
def logger
|
|
361
386
|
@bot.logger
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
|
|
2
|
+
module Telegem
|
|
3
|
+
class RateLimit
|
|
4
|
+
def initialize(**options)
|
|
5
|
+
@options = {
|
|
6
|
+
global: { max: 30, per: 1 }, # 30 reqs/second globally
|
|
7
|
+
user: { max: 5, per: 10 }, # 5 reqs/10 seconds per user
|
|
8
|
+
chat: { max: 20, per: 60 } # 20 reqs/minute per chat
|
|
9
|
+
}.merge(options)
|
|
10
|
+
|
|
11
|
+
@counters = {
|
|
12
|
+
global: Telegem::Session::MemoryStore.new,
|
|
13
|
+
user: Telegem::Session::MemoryStore.new,
|
|
14
|
+
chat: Telegem::Session::MemoryStore.new
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(ctx, next_middleware)
|
|
19
|
+
return next_middleware.call(ctx) unless should_rate_limit?(ctx)
|
|
20
|
+
|
|
21
|
+
if limit_exceeded?(ctx)
|
|
22
|
+
ctx.logger&.warn("Rate limit exceeded for #{ctx.from&.id}")
|
|
23
|
+
return rate_limit_response(ctx)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
increment_counters(ctx)
|
|
27
|
+
next_middleware.call(ctx)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def should_rate_limit?(ctx)
|
|
33
|
+
|
|
34
|
+
return false if ctx.update.poll?
|
|
35
|
+
return false if ctx.update.chat_member?
|
|
36
|
+
return true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def limit_exceeded?(ctx)
|
|
40
|
+
global_limit?(ctx) || user_limit?(ctx) || chat_limit?(ctx)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def global_limit?(ctx)
|
|
44
|
+
check_limit(:global, "global", ctx)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def user_limit?(ctx)
|
|
48
|
+
return false unless ctx.from&.id
|
|
49
|
+
check_limit(:user, "user:#{ctx.from.id}", ctx)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def chat_limit?(ctx)
|
|
53
|
+
return false unless ctx.chat&.id
|
|
54
|
+
check_limit(:chat, "chat:#{ctx.chat.id}", ctx)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def check_limit(type, key, ctx)
|
|
58
|
+
limit = @options[type]
|
|
59
|
+
return false unless limit
|
|
60
|
+
|
|
61
|
+
counter = @counters[type].get(key) || 0
|
|
62
|
+
counter >= limit[:max]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def increment_counters(ctx)
|
|
66
|
+
now = Time.now.to_i
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if @options[:global]
|
|
70
|
+
key = "global"
|
|
71
|
+
cleanup_counter(:global, key, now)
|
|
72
|
+
@counters[:global].increment(key, 1, ttl: @options[:global][:per])
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if @options[:user] && ctx.from&.id
|
|
77
|
+
key = "user:#{ctx.from.id}"
|
|
78
|
+
cleanup_counter(:user, key, now)
|
|
79
|
+
@counters[:user].increment(key, 1, ttl: @options[:user][:per])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if @options[:chat] && ctx.chat&.id
|
|
84
|
+
key = "chat:#{ctx.chat.id}"
|
|
85
|
+
cleanup_counter(:chat, key, now)
|
|
86
|
+
@counters[:chat].increment(key, 1, ttl: @options[:chat][:per])
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def cleanup_counter(type, key, now)
|
|
91
|
+
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def rate_limit_response(ctx)
|
|
95
|
+
|
|
96
|
+
ctx.reply("⏳ Please wait a moment before sending another request.") rescue nil
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
data/lib/core/scene.rb
CHANGED
|
@@ -1,90 +1,157 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
module Telegem
|
|
3
3
|
module Core
|
|
4
4
|
class Scene
|
|
5
5
|
attr_reader :id, :steps, :enter_callbacks, :leave_callbacks
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
def initialize(id, default_step: :start, &block)
|
|
8
8
|
@id = id
|
|
9
9
|
@steps = {}
|
|
10
10
|
@enter_callbacks = []
|
|
11
11
|
@leave_callbacks = []
|
|
12
12
|
@default_step = default_step
|
|
13
|
+
@timeout = 300 # 5 minutes default timeout
|
|
14
|
+
|
|
13
15
|
instance_eval(&block) if block_given?
|
|
14
16
|
end
|
|
15
|
-
|
|
17
|
+
|
|
16
18
|
def step(name, &action)
|
|
17
19
|
@steps[name.to_sym] = action
|
|
18
20
|
self
|
|
19
21
|
end
|
|
20
|
-
|
|
22
|
+
|
|
21
23
|
def on_enter(&callback)
|
|
22
24
|
@enter_callbacks << callback
|
|
23
25
|
self
|
|
24
26
|
end
|
|
25
|
-
|
|
27
|
+
|
|
26
28
|
def on_leave(&callback)
|
|
27
29
|
@leave_callbacks << callback
|
|
28
30
|
self
|
|
29
31
|
end
|
|
30
|
-
|
|
31
|
-
def
|
|
32
|
+
|
|
33
|
+
def timeout(seconds)
|
|
34
|
+
@timeout = seconds
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def enter(ctx, step_name = nil, **initial_data)
|
|
32
40
|
step_name ||= @default_step
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
ctx.
|
|
42
|
+
|
|
43
|
+
ctx.session[:telegem_scene] = {
|
|
44
|
+
id: @id.to_s,
|
|
45
|
+
step: step_name.to_s,
|
|
46
|
+
data: initial_data,
|
|
47
|
+
entered_at: Time.now.to_i,
|
|
48
|
+
timeout: @timeout,
|
|
49
|
+
waiting_for_response: false,
|
|
50
|
+
last_question: nil
|
|
51
|
+
}
|
|
36
52
|
|
|
37
|
-
|
|
53
|
+
ctx.instance_variable_set(:@current_scene, self)
|
|
54
|
+
|
|
38
55
|
@enter_callbacks.each { |cb| cb.call(ctx) }
|
|
39
56
|
|
|
40
|
-
# Enter the first step
|
|
41
|
-
process_step(ctx, step_name)
|
|
42
|
-
end
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
# Run leave callbacks
|
|
46
|
-
@leave_callbacks.each { |cb| cb.call(ctx) }
|
|
47
|
-
|
|
48
|
-
# Clear scene from context
|
|
49
|
-
ctx.scene = nil
|
|
50
|
-
nil
|
|
58
|
+
execute_step(ctx, step_name)
|
|
51
59
|
end
|
|
60
|
+
|
|
52
61
|
|
|
53
|
-
def
|
|
54
|
-
|
|
62
|
+
def process(ctx)
|
|
63
|
+
scene_data = ctx.session[:telegem_scene]
|
|
64
|
+
return unless scene_data
|
|
55
65
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
|
|
67
|
+
if Time.now.to_i - scene_data[:entered_at] > scene_data[:timeout]
|
|
68
|
+
leave(ctx, :timeout)
|
|
69
|
+
return
|
|
59
70
|
end
|
|
60
71
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
ctx.scene[:step] = step_name.to_sym
|
|
72
|
+
if scene_data[:waiting_for_response] && ctx.message&.text
|
|
73
|
+
process_response(ctx, scene_data)
|
|
74
|
+
else
|
|
75
|
+
|
|
76
|
+
execute_step(ctx, scene_data[:step])
|
|
67
77
|
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def leave(ctx, reason = :manual)
|
|
81
|
+
scene_data = ctx.session.delete(:telegem_scene)
|
|
82
|
+
ctx.instance_variable_set(:@current_scene, nil)
|
|
68
83
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
nil
|
|
84
|
+
|
|
85
|
+
@leave_callbacks.each { |cb| cb.call(ctx, reason, scene_data[:data]) }
|
|
86
|
+
|
|
87
|
+
scene_data[:data]
|
|
74
88
|
end
|
|
89
|
+
|
|
75
90
|
|
|
76
91
|
def current_step(ctx)
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
scene_data = ctx.session[:telegem_scene]
|
|
93
|
+
scene_data[:step] if scene_data
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def scene_data(ctx)
|
|
98
|
+
ctx.session[:telegem_scene]&.[](:data) || {}
|
|
79
99
|
end
|
|
100
|
+
|
|
101
|
+
private
|
|
80
102
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
|
|
103
|
+
def execute_step(ctx, step_name)
|
|
104
|
+
action = @steps[step_name.to_sym]
|
|
105
|
+
return leave(ctx, :invalid_step) unless action
|
|
106
|
+
|
|
107
|
+
scene_data = ctx.session[:telegem_scene]
|
|
108
|
+
scene_data[:step] = step_name.to_s
|
|
109
|
+
scene_data[:waiting_for_response] = false
|
|
110
|
+
scene_data[:last_question] = nil
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
result = action.call(ctx, scene_data[:data])
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if result.is_a?(Symbol) || result.is_a?(String)
|
|
117
|
+
next_step(ctx, result)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
result
|
|
84
121
|
end
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def process_response(ctx, scene_data)
|
|
85
125
|
|
|
86
|
-
|
|
87
|
-
|
|
126
|
+
current_step_name = scene_data[:step]
|
|
127
|
+
scene_data[:data][current_step_name] = ctx.message.text
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
scene_data[:waiting_for_response] = false
|
|
131
|
+
|
|
132
|
+
next_step(ctx)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def next_step(ctx, specific_step = nil)
|
|
137
|
+
scene_data = ctx.session[:telegem_scene]
|
|
138
|
+
return unless scene_data
|
|
139
|
+
|
|
140
|
+
current_step = scene_data[:step].to_sym
|
|
141
|
+
step_names = @steps.keys
|
|
142
|
+
|
|
143
|
+
if specific_step
|
|
144
|
+
next_step_name = specific_step.to_sym
|
|
145
|
+
else
|
|
146
|
+
current_index = step_names.index(current_step)
|
|
147
|
+
next_step_name = step_names[current_index + 1] if current_index
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if next_step_name
|
|
151
|
+
execute_step(ctx, next_step_name)
|
|
152
|
+
else
|
|
153
|
+
leave(ctx, :completed)
|
|
154
|
+
end
|
|
88
155
|
end
|
|
89
156
|
end
|
|
90
157
|
end
|
|
File without changes
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
require 'tempfile'
|
|
3
|
+
require 'pdf-reader'
|
|
4
|
+
require 'docx'
|
|
5
|
+
|
|
6
|
+
module Telegem
|
|
7
|
+
module Plugins
|
|
8
|
+
class FileExtractor
|
|
9
|
+
SUPPORTED_TYPES = {
|
|
10
|
+
pdf: 'application/pdf',
|
|
11
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
12
|
+
txt: 'text/plain',
|
|
13
|
+
csv: 'text/csv',
|
|
14
|
+
html: 'text/html',
|
|
15
|
+
rtf: 'application/rtf',
|
|
16
|
+
odt: 'application/vnd.oasis.opendocument.text'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def initialize(**options)
|
|
20
|
+
@options = {
|
|
21
|
+
max_size: 10 * 1024 * 1024,
|
|
22
|
+
timeout: 30,
|
|
23
|
+
cache: true
|
|
24
|
+
}.merge(options)
|
|
25
|
+
|
|
26
|
+
@cache = Telegem::Session::MemoryStore.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def call(ctx, next_middleware)
|
|
30
|
+
ctx.define_singleton_method(:extract_file_text) do |file_id = nil, **opts|
|
|
31
|
+
extract_from_context(self, file_id, opts)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ctx.define_singleton_method(:file_content) do
|
|
35
|
+
@_file_content
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
ctx.define_singleton_method(:file_metadata) do
|
|
39
|
+
@_file_metadata
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
next_middleware.call(ctx)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
def extract_from_context(ctx, file_id = nil, **options)
|
|
47
|
+
message = ctx.update.message
|
|
48
|
+
return nil unless message
|
|
49
|
+
|
|
50
|
+
file_info = find_file_info(message, file_id)
|
|
51
|
+
return nil unless file_info
|
|
52
|
+
|
|
53
|
+
return file_info[:content] if file_info[:type] == :text
|
|
54
|
+
|
|
55
|
+
file_path = download_file(ctx.bot.api, file_info[:file_id], options)
|
|
56
|
+
return nil unless file_path
|
|
57
|
+
|
|
58
|
+
text = extract_text(file_path, file_info[:type])
|
|
59
|
+
|
|
60
|
+
if options.fetch(:cache, true)
|
|
61
|
+
ctx.instance_variable_set(:@_file_content, text)
|
|
62
|
+
ctx.instance_variable_set(:@_file_metadata, file_info)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
text
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def find_file_info(message, file_id)
|
|
71
|
+
return { file_id: file_id, type: :unknown } if file_id
|
|
72
|
+
|
|
73
|
+
if message.document
|
|
74
|
+
mime = message.document.mime_type || 'application/octet-stream'
|
|
75
|
+
type = SUPPORTED_TYPES.key(mime) || :unknown
|
|
76
|
+
{ file_id: message.document.file_id, type: type, mime: mime, name: message.document.file_name }
|
|
77
|
+
elsif message.photo&.any?
|
|
78
|
+
{ file_id: message.photo.last.file_id, type: :image, mime: 'image/jpeg' }
|
|
79
|
+
elsif message.video
|
|
80
|
+
{ file_id: message.video.file_id, type: :video, mime: message.video.mime_type }
|
|
81
|
+
elsif message.audio
|
|
82
|
+
{ file_id: message.audio.file_id, type: :audio, mime: message.audio.mime_type }
|
|
83
|
+
elsif message.voice
|
|
84
|
+
{ file_id: message.voice.file_id, type: :audio, mime: 'audio/ogg' }
|
|
85
|
+
elsif message.text
|
|
86
|
+
{ type: :text, content: message.text }
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def download_file(api, file_id, options)
|
|
91
|
+
file = api.call('getFile', file_id: file_id)
|
|
92
|
+
return nil unless file && file['ok']
|
|
93
|
+
|
|
94
|
+
file_path = file['result']['file_path']
|
|
95
|
+
url = "https://api.telegram.org/file/bot#{api.token}/#{file_path}"
|
|
96
|
+
|
|
97
|
+
temp_file = Tempfile.new(['telegem', File.extname(file_path)])
|
|
98
|
+
|
|
99
|
+
URI.open(url, read_timeout: options[:timeout] || 30) do |remote|
|
|
100
|
+
temp_file.binmode
|
|
101
|
+
temp_file.write(remote.read)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
temp_file.close
|
|
105
|
+
temp_file.path
|
|
106
|
+
rescue => e
|
|
107
|
+
nil
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def extract_text(file_path, type)
|
|
111
|
+
case type
|
|
112
|
+
when :pdf
|
|
113
|
+
PDF::Reader.new(file_path).pages.map(&:text).join("\n")
|
|
114
|
+
when :docx
|
|
115
|
+
Docx::Document.open(file_path).text
|
|
116
|
+
when :txt, :html, :csv, :rtf, :odt
|
|
117
|
+
File.read(file_path, encoding: 'utf-8')
|
|
118
|
+
else
|
|
119
|
+
""
|
|
120
|
+
end
|
|
121
|
+
rescue => e
|
|
122
|
+
"Error: #{e.message}"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
module Telegem
|
|
3
|
+
module Scene
|
|
4
|
+
class Middleware
|
|
5
|
+
def call(ctx, next_middleware)
|
|
6
|
+
# Check if user is in a scene
|
|
7
|
+
if ctx.session[:telegem_scene]
|
|
8
|
+
scene_data = ctx.session[:telegem_scene]
|
|
9
|
+
scene = ctx.bot.scenes[scene_data[:id].to_sym]
|
|
10
|
+
|
|
11
|
+
if scene
|
|
12
|
+
# Let scene handle it
|
|
13
|
+
scene.process(ctx)
|
|
14
|
+
return # Don't call regular handlers
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Not in scene, proceed normally
|
|
19
|
+
next_middleware.call(ctx)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
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.0
|
|
6
|
+
VERSION = "3.1.0".freeze
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
# Load core components
|
|
@@ -17,6 +17,8 @@ require_relative 'session/middleware'
|
|
|
17
17
|
require_relative 'session/memory_store'
|
|
18
18
|
require_relative 'markup/keyboard'
|
|
19
19
|
# Webhook is loaded lazily when needed
|
|
20
|
+
require_relative 'plugins/file_extract'
|
|
21
|
+
require_relative 'session/scene_middleware'
|
|
20
22
|
|
|
21
23
|
module Telegem
|
|
22
24
|
# Main entry point: Telegem.new(token)
|
|
@@ -80,4 +82,4 @@ if ENV['TELEGEM_GLOBAL'] == 'true'
|
|
|
80
82
|
def Telegem(token, **options)
|
|
81
83
|
::Telegem.new(token, **options)
|
|
82
84
|
end
|
|
83
|
-
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.0
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- sick_phantom
|
|
@@ -13,98 +13,72 @@ dependencies:
|
|
|
13
13
|
name: httpx
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: '0'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- - "
|
|
23
|
+
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
25
|
+
version: '0'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: concurrent-ruby
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '0'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: async
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
47
|
-
- - "<"
|
|
48
|
-
- !ruby/object:Gem::Version
|
|
49
|
-
version: '4.0'
|
|
46
|
+
version: '0'
|
|
50
47
|
type: :runtime
|
|
51
48
|
prerelease: false
|
|
52
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
53
50
|
requirements:
|
|
54
51
|
- - ">="
|
|
55
52
|
- !ruby/object:Gem::Version
|
|
56
|
-
version: '
|
|
57
|
-
- - "<"
|
|
58
|
-
- !ruby/object:Gem::Version
|
|
59
|
-
version: '4.0'
|
|
53
|
+
version: '0'
|
|
60
54
|
- !ruby/object:Gem::Dependency
|
|
61
|
-
name:
|
|
55
|
+
name: pdf-reader
|
|
62
56
|
requirement: !ruby/object:Gem::Requirement
|
|
63
57
|
requirements:
|
|
64
58
|
- - ">="
|
|
65
59
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '
|
|
67
|
-
- - "<"
|
|
68
|
-
- !ruby/object:Gem::Version
|
|
69
|
-
version: '7.0'
|
|
60
|
+
version: '0'
|
|
70
61
|
type: :runtime
|
|
71
62
|
prerelease: false
|
|
72
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
73
64
|
requirements:
|
|
74
65
|
- - ">="
|
|
75
66
|
- !ruby/object:Gem::Version
|
|
76
|
-
version: '
|
|
77
|
-
- - "<"
|
|
78
|
-
- !ruby/object:Gem::Version
|
|
79
|
-
version: '7.0'
|
|
67
|
+
version: '0'
|
|
80
68
|
- !ruby/object:Gem::Dependency
|
|
81
|
-
name:
|
|
69
|
+
name: docx
|
|
82
70
|
requirement: !ruby/object:Gem::Requirement
|
|
83
71
|
requirements:
|
|
84
|
-
- - "
|
|
72
|
+
- - ">="
|
|
85
73
|
- !ruby/object:Gem::Version
|
|
86
|
-
version: '
|
|
74
|
+
version: '0'
|
|
87
75
|
type: :runtime
|
|
88
76
|
prerelease: false
|
|
89
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
90
78
|
requirements:
|
|
91
|
-
- - "
|
|
92
|
-
- !ruby/object:Gem::Version
|
|
93
|
-
version: '2.1'
|
|
94
|
-
- !ruby/object:Gem::Dependency
|
|
95
|
-
name: rake
|
|
96
|
-
requirement: !ruby/object:Gem::Requirement
|
|
97
|
-
requirements:
|
|
98
|
-
- - "~>"
|
|
99
|
-
- !ruby/object:Gem::Version
|
|
100
|
-
version: '13.0'
|
|
101
|
-
type: :development
|
|
102
|
-
prerelease: false
|
|
103
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
104
|
-
requirements:
|
|
105
|
-
- - "~>"
|
|
79
|
+
- - ">="
|
|
106
80
|
- !ruby/object:Gem::Version
|
|
107
|
-
version: '
|
|
81
|
+
version: '0'
|
|
108
82
|
- !ruby/object:Gem::Dependency
|
|
109
83
|
name: rspec
|
|
110
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -119,34 +93,6 @@ dependencies:
|
|
|
119
93
|
- - "~>"
|
|
120
94
|
- !ruby/object:Gem::Version
|
|
121
95
|
version: '3.12'
|
|
122
|
-
- !ruby/object:Gem::Dependency
|
|
123
|
-
name: simplecov
|
|
124
|
-
requirement: !ruby/object:Gem::Requirement
|
|
125
|
-
requirements:
|
|
126
|
-
- - "~>"
|
|
127
|
-
- !ruby/object:Gem::Version
|
|
128
|
-
version: '0.22'
|
|
129
|
-
type: :development
|
|
130
|
-
prerelease: false
|
|
131
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
132
|
-
requirements:
|
|
133
|
-
- - "~>"
|
|
134
|
-
- !ruby/object:Gem::Version
|
|
135
|
-
version: '0.22'
|
|
136
|
-
- !ruby/object:Gem::Dependency
|
|
137
|
-
name: webmock
|
|
138
|
-
requirement: !ruby/object:Gem::Requirement
|
|
139
|
-
requirements:
|
|
140
|
-
- - "~>"
|
|
141
|
-
- !ruby/object:Gem::Version
|
|
142
|
-
version: '3.18'
|
|
143
|
-
type: :development
|
|
144
|
-
prerelease: false
|
|
145
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
146
|
-
requirements:
|
|
147
|
-
- - "~>"
|
|
148
|
-
- !ruby/object:Gem::Version
|
|
149
|
-
version: '3.18'
|
|
150
96
|
- !ruby/object:Gem::Dependency
|
|
151
97
|
name: rubocop
|
|
152
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -197,11 +143,15 @@ files:
|
|
|
197
143
|
- lib/core/bot.rb
|
|
198
144
|
- lib/core/composer.rb
|
|
199
145
|
- lib/core/context.rb
|
|
146
|
+
- lib/core/rate_limit.rb
|
|
200
147
|
- lib/core/scene.rb
|
|
201
148
|
- lib/markup/.gitkeep
|
|
202
149
|
- lib/markup/keyboard.rb
|
|
150
|
+
- lib/plugins/.gitkeep
|
|
151
|
+
- lib/plugins/file_extract.rb
|
|
203
152
|
- lib/session/memory_store.rb
|
|
204
153
|
- lib/session/middleware.rb
|
|
154
|
+
- lib/session/scene_middleware.rb
|
|
205
155
|
- lib/telegem.rb
|
|
206
156
|
- lib/webhook/.gitkeep
|
|
207
157
|
- lib/webhook/server.rb
|
|
@@ -216,7 +166,7 @@ metadata:
|
|
|
216
166
|
bug_tracker_uri: https://gitlab.com/ruby-telegem/telegem/-/issues
|
|
217
167
|
documentation_uri: https://gitlab.com/ruby-telegem/telegem/-/tree/main/docs-src?ref_type=heads
|
|
218
168
|
rubygems_mfa_required: 'false'
|
|
219
|
-
post_install_message: "Thanks for installing Telegem 3.0
|
|
169
|
+
post_install_message: "Thanks for installing Telegem 3.1.0!\n\n\U0001F4DA Documentation:
|
|
220
170
|
https://gitlab.com/ruby-telegem/telegem\n\n\U0001F510 For SSL Webhooks:\nRun: telegem-ssl
|
|
221
171
|
your-domain.com\nThis sets up Let's Encrypt certificates automatically.\n\n\U0001F916
|
|
222
172
|
Happy bot building!\n"
|