telegem 3.0.6 → 3.1.3

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.
data/lib/core/scene.rb CHANGED
@@ -1,90 +1,157 @@
1
- # lib/core/scene.rb - HTTPX VERSION (NO ASYNC GEM)
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 enter(ctx, step_name = nil)
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
- # Store scene state in context
35
- ctx.scene = { id: @id, step: step_name.to_sym }
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
- # Run enter callbacks
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
- def leave(ctx)
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 process_step(ctx, step_name)
54
- step = @steps[step_name.to_sym]
62
+ def process(ctx)
63
+ scene_data = ctx.session[:telegem_scene]
64
+ return unless scene_data
55
65
 
56
- unless step
57
- ctx.logger.error("Unknown step #{step_name} in scene #{@id}")
58
- return nil
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
- # Execute the step
62
- result = step.call(ctx)
63
-
64
- # Update step in context (unless step changed it)
65
- if ctx.scene && ctx.scene[:id] == @id
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
- result
70
- rescue => e
71
- ctx.logger.error("Error processing step #{step_name} in scene #{@id}: #{e.message}")
72
- ctx.logger.error(e.backtrace.join("\n")) if e.backtrace
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
- return nil unless ctx.scene && ctx.scene[:id] == @id
78
- ctx.scene[:step]
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 reset(ctx)
82
- ctx.scene = { id: @id, step: @default_step }
83
- self
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
- def to_s
87
- "#<Scene #{@id}>"
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
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+ require 'pdf/reader'
3
+
4
+ module Telegem
5
+ module Plugins
6
+ class FileExtractor
7
+ def initialize(bot, file_id, **options)
8
+ @bot = bot
9
+ @file_id = file_id
10
+ @options = {
11
+ timeout: 15,
12
+ auto_delete: true,
13
+ file_type: nil
14
+ }.merge(options)
15
+ file_path = "temp/#{@file_id}"
16
+ download_file(file_path)
17
+ end
18
+
19
+ def download_file(file_path)
20
+ if @options[:file_type] == :pdf
21
+ extract_pdf(file_path)
22
+ elsif @options[:file_type] == :json
23
+ extract_json(file_path)
24
+ elsif @options[:file_type] == :html
25
+ extract_html(file_path)
26
+ elsif @options[:file_type] == :txt
27
+ extract_text(file_path)
28
+ else
29
+ { error: "unsupported file type" }
30
+ end
31
+ end
32
+
33
+ def extract_pdf(file_path)
34
+ begin
35
+ @bot.api.download(@file_id, "#{file_path}.pdf")
36
+ reader = PDF::Reader.new("#{file_path}.pdf")
37
+ text = reader.pages.map(&:text).join("\n").strip
38
+
39
+ if text.empty?
40
+ { error: "pdf is empty" }
41
+ else
42
+ result = {
43
+ success: true,
44
+ content: text,
45
+ pages: reader.page_count,
46
+ }
47
+ File.delete("#{file_path}.pdf") if @options[:auto_delete]
48
+ result
49
+ end
50
+ rescue PDF::Reader::MalformedPDFError
51
+ { error: "malformed pdf format" }
52
+ rescue PDF::Reader::EncryptedPDFError
53
+ { error: "encrypted pdf" }
54
+ rescue => e
55
+ { error: "pdf processing failed #{e.message}" }
56
+ ensure
57
+ File.delete("#{file_path}.pdf") if @options[:auto_delete] && File.exist?("#{file_path}.pdf")
58
+ end
59
+ end
60
+
61
+ def extract_json(file_path)
62
+ @bot.api.download(@file_id, "#{file_path}.json")
63
+ json_data = JSON.parse(File.read("#{file_path}.json"))
64
+ if File.exist?("#{file_path}.json")
65
+ {
66
+ success: true,
67
+ content: json_data
68
+ }
69
+ else
70
+ {
71
+ error: "no json file"
72
+ }
73
+ end
74
+ end
75
+
76
+ def extract_html(file_path)
77
+ destination = "#{file_path}.html"
78
+ @bot.api.download(@file_id, destination)
79
+ html = File.read(destination)
80
+ if html.empty?
81
+ {
82
+ error: "html content is empty"
83
+ }
84
+ else
85
+ {
86
+ success: true,
87
+ content: html
88
+ }
89
+ end
90
+ rescue => e
91
+ {
92
+ error: "error #{e.message}"
93
+ }
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,22 @@
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
+ end
15
+ end
16
+
17
+ # Not in scene, proceed normally
18
+ next_middleware.call(ctx)
19
+ end
20
+ end
21
+ end
22
+ 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".freeze
6
+ VERSION = "3.1.3".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)
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.6
4
+ version: 3.1.3
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: 1.7.0
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: 1.7.0
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: '1.2'
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: '1.2'
39
+ version: '0'
40
40
  - !ruby/object:Gem::Dependency
41
- name: rack
41
+ name: async
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '2.0'
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: '2.0'
57
- - - "<"
58
- - !ruby/object:Gem::Version
59
- version: '4.0'
53
+ version: '0'
60
54
  - !ruby/object:Gem::Dependency
61
- name: puma
55
+ name: pdf-reader
62
56
  requirement: !ruby/object:Gem::Requirement
63
57
  requirements:
64
58
  - - ">="
65
59
  - !ruby/object:Gem::Version
66
- version: '5.0'
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: '5.0'
77
- - - "<"
78
- - !ruby/object:Gem::Version
79
- version: '7.0'
67
+ version: '0'
80
68
  - !ruby/object:Gem::Dependency
81
- name: async
69
+ name: docx
82
70
  requirement: !ruby/object:Gem::Requirement
83
71
  requirements:
84
- - - "~>"
72
+ - - ">="
85
73
  - !ruby/object:Gem::Version
86
- version: '2.1'
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: '13.0'
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
@@ -174,6 +120,7 @@ extra_rdoc_files: []
174
120
  files:
175
121
  - ".replit"
176
122
  - CHANGELOG
123
+ - CODE_OF_CONDUCT.md
177
124
  - Contributing.md
178
125
  - Gemfile
179
126
  - Gemfile.lock
@@ -182,26 +129,21 @@ files:
182
129
  - Starts_HallofFame.md
183
130
  - bin/.gitkeep
184
131
  - bin/telegem-ssl
185
- - docs-src/.gitkeep
186
- - docs-src/Bot-registration_.PNG
187
- - docs-src/bot.md
188
- - docs-src/context|ctx|.md
189
- - docs-src/ctx.md
190
- - docs-src/getting-started.md
191
- - docs-src/keyboard_inline.md
192
- - docs-src/scene.md
193
- - docs-src/webhook.md
194
132
  - examples/.gitkeep
195
133
  - lib/api/client.rb
196
134
  - lib/api/types.rb
197
135
  - lib/core/bot.rb
198
136
  - lib/core/composer.rb
199
137
  - lib/core/context.rb
138
+ - lib/core/rate_limit.rb
200
139
  - lib/core/scene.rb
201
140
  - lib/markup/.gitkeep
202
141
  - lib/markup/keyboard.rb
142
+ - lib/plugins/.gitkeep
143
+ - lib/plugins/file_extract.rb
203
144
  - lib/session/memory_store.rb
204
145
  - lib/session/middleware.rb
146
+ - lib/session/scene_middleware.rb
205
147
  - lib/telegem.rb
206
148
  - lib/webhook/.gitkeep
207
149
  - lib/webhook/server.rb
@@ -216,7 +158,7 @@ metadata:
216
158
  bug_tracker_uri: https://gitlab.com/ruby-telegem/telegem/-/issues
217
159
  documentation_uri: https://gitlab.com/ruby-telegem/telegem/-/tree/main/docs-src?ref_type=heads
218
160
  rubygems_mfa_required: 'false'
219
- post_install_message: "Thanks for installing Telegem 3.0.6!\n\n\U0001F4DA Documentation:
161
+ post_install_message: "Thanks for installing Telegem 3.1.3!\n\n\U0001F4DA Documentation:
220
162
  https://gitlab.com/ruby-telegem/telegem\n\n\U0001F510 For SSL Webhooks:\nRun: telegem-ssl
221
163
  your-domain.com\nThis sets up Let's Encrypt certificates automatically.\n\n\U0001F916
222
164
  Happy bot building!\n"
File without changes