telegem 3.2.4 → 3.3.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.
@@ -1,147 +1,107 @@
1
- # lib/session/memory_store.rb - PRODUCTION READY
1
+ # lib/session/memory_store.rb
2
+ require 'json'
3
+ require 'time'
4
+ require 'fileutils'
5
+
2
6
  module Telegem
3
7
  module Session
4
8
  class MemoryStore
5
- def initialize
9
+ def initialize(
10
+ default_ttl: 300,
11
+ cleanup_interval: 300,
12
+ backup_path: nil,
13
+ backup_interval: 60
14
+ )
6
15
  @store = {}
7
16
  @ttls = {}
8
- @mutex = Mutex.new
9
- @default_ttl = 300 # 5 minutes
10
- @cleanup_interval = 60 # Clean expired every minute
17
+ @default_ttl = default_ttl
18
+ @cleanup_interval = cleanup_interval
19
+ @backup_path = backup_path
20
+ @backup_interval = backup_interval
21
+
11
22
  @last_cleanup = Time.now
23
+ @last_backup = Time.now
24
+
25
+ restore! if @backup_path && File.exist?(@backup_path)
12
26
  end
13
27
 
14
- # Store with optional TTL
15
28
  def set(key, value, ttl: nil)
16
- @mutex.synchronize do
17
- auto_cleanup
18
- key_s = key.to_s
19
- @store[key_s] = value
20
- @ttls[key_s] = Time.now + (ttl || @default_ttl)
21
- value
22
- end
23
- end
29
+ auto_cleanup
30
+ key_s = key.to_s
24
31
 
25
- # Get value if not expired
26
- def get(key)
27
- @mutex.synchronize do
28
- key_s = key.to_s
29
- return nil unless @store.key?(key_s)
30
-
31
- # Auto-clean if expired
32
- if expired?(key_s)
33
- delete(key_s)
34
- return nil
35
- end
36
-
37
- @store[key_s]
38
- end
39
- end
32
+ @store[key_s] = value
33
+ @ttls[key_s] = Time.now + (ttl || @default_ttl)
40
34
 
41
- # Check if key exists and not expired
42
- def exist?(key)
43
- @mutex.synchronize do
44
- key_s = key.to_s
45
- return false unless @store.key?(key_s)
46
- !expired?(key_s)
47
- end
35
+ auto_backup
36
+ value
48
37
  end
49
38
 
50
- # Delete key
51
- def delete(key)
52
- @mutex.synchronize do
53
- key_s = key.to_s
54
- @store.delete(key_s)
55
- @ttls.delete(key_s)
56
- true
57
- end
58
- end
39
+ def get(key)
40
+ key_s = key.to_s
41
+ return nil unless @store.key?(key_s)
59
42
 
60
- # Increment counter (for rate limiting)
61
- def increment(key, amount = 1, ttl: nil)
62
- @mutex.synchronize do
63
- key_s = key.to_s
64
- current = get(key_s) || 0
65
- new_value = current + amount
66
- set(key_s, new_value, ttl: ttl)
67
- new_value
43
+ if expired?(key_s)
44
+ delete(key_s)
45
+ return nil
68
46
  end
69
- end
70
47
 
71
- # Decrement counter
72
- def decrement(key, amount = 1)
73
- increment(key, -amount)
48
+ @store[key_s]
74
49
  end
75
50
 
76
- # Clear expired entries (auto-called)
77
- def cleanup
78
- @mutex.synchronize do
79
- now = Time.now
80
- @ttls.each do |key, expires|
81
- if now > expires
82
- @store.delete(key)
83
- @ttls.delete(key)
84
- end
85
- end
86
- @last_cleanup = now
87
- end
51
+ def delete(key)
52
+ key_s = key.to_s
53
+ @store.delete(key_s)
54
+ @ttls.delete(key_s)
55
+ true
88
56
  end
89
57
 
90
- # Clear everything
91
- def clear
92
- @mutex.synchronize do
58
+ def increment(key, amount = 1, ttl: nil)
59
+ current = get(key) || 0
60
+ # Ensure we are working with numbers
61
+ val = current.to_i rescue 0
62
+ new_val = val + amount
63
+ set(key, new_val, ttl: ttl)
64
+ new_val
65
+ end
66
+
67
+ # --- Persistence Logic (The "Telecr" Way) ---
68
+
69
+ def backup!
70
+ return unless @backup_path
71
+
72
+ # 1. Prepare data
73
+ data = {
74
+ "store" => @store,
75
+ "ttls" => @ttls.transform_values(&:to_i), # Save as Unix timestamp
76
+ "timestamp" => Time.now.to_i
77
+ }
78
+
79
+ # 2. Ensure directory exists
80
+ FileUtils.mkdir_p(File.dirname(@backup_path))
81
+
82
+ # 3. ATOMIC WRITE: Write to temp, then rename
83
+ temp_path = "#{@backup_path}.tmp"
84
+ File.write(temp_path, JSON.generate(data))
85
+ File.rename(temp_path, @backup_path)
86
+
87
+ @last_backup = Time.now
88
+ end
89
+
90
+ def restore!
91
+ return unless @backup_path && File.exist?(@backup_path)
92
+
93
+ begin
94
+ raw = JSON.parse(File.read(@backup_path))
95
+
93
96
  @store.clear
94
97
  @ttls.clear
95
- @last_cleanup = Time.now
96
- end
97
- end
98
-
99
- # Get all keys (non-expired)
100
- def keys
101
- @mutex.synchronize do
102
- auto_cleanup
103
- @store.keys.select { |k| !expired?(k) }
104
- end
105
- end
106
-
107
- # Get size (non-expired entries)
108
- def size
109
- keys.size
110
- end
111
98
 
112
- def empty?
113
- size == 0
114
- end
115
-
116
- # Get TTL remaining in seconds
117
- def ttl(key)
118
- @mutex.synchronize do
119
- key_s = key.to_s
120
- return -1 unless @ttls[key_s]
121
-
122
- remaining = @ttls[key_s] - Time.now
123
- remaining > 0 ? remaining.ceil : -1
124
- end
125
- end
126
-
127
- # Set TTL for existing key
128
- def expire(key, ttl)
129
- @mutex.synchronize do
130
- key_s = key.to_s
131
- return false unless @store.key?(key_s)
132
-
133
- @ttls[key_s] = Time.now + ttl
134
- true
135
- end
136
- end
137
-
138
- # Redis-like scan for pattern matching
139
- def scan(pattern = "*", count: 10)
140
- @mutex.synchronize do
141
- auto_cleanup
142
- regex = pattern_to_regex(pattern)
143
- matching_keys = @store.keys.select { |k| k.match?(regex) && !expired?(k) }
144
- matching_keys.first(count)
99
+ raw["store"].each { |k, v| @store[k] = v }
100
+ raw["ttls"].each do |k, v|
101
+ @ttls[k] = Time.at(v)
102
+ end
103
+ rescue => e
104
+ warn "Telegem: Failed to restore backup: #{e.message}"
145
105
  end
146
106
  end
147
107
 
@@ -152,16 +112,19 @@ module Telegem
152
112
  end
153
113
 
154
114
  def auto_cleanup
155
- if Time.now - @last_cleanup > @cleanup_interval
156
- cleanup
115
+ if (Time.now - @last_cleanup) > @cleanup_interval
116
+ now = Time.now
117
+ expired_keys = @ttls.select { |_, expires| now > expires }.keys
118
+ expired_keys.each { |k| delete(k) }
119
+ @last_cleanup = now
157
120
  end
158
121
  end
159
122
 
160
- def pattern_to_regex(pattern)
161
- # Convert Redis-style pattern to Ruby regex
162
- regex_str = pattern.gsub('*', '.*').gsub('?', '.')
163
- Regexp.new("^#{regex_str}$")
123
+ def auto_backup
124
+ if @backup_path && (Time.now - @last_backup) > @backup_interval
125
+ backup!
126
+ end
164
127
  end
165
128
  end
166
129
  end
167
- end
130
+ 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.2.4".freeze
6
+ VERSION = "3.3.1".freeze
7
7
  end
8
8
 
9
9
  #
@@ -16,6 +16,7 @@ require_relative 'core/scene'
16
16
  require_relative 'session/middleware'
17
17
  require_relative 'session/memory_store'
18
18
  require_relative 'markup/keyboard'
19
+ require_relative 'markup/inline'
19
20
 
20
21
  require_relative 'plugins/file_extract'
21
22
  require_relative 'session/scene_middleware'
@@ -77,4 +78,4 @@ if ENV['TELEGEM_GLOBAL'] == 'true'
77
78
  def Telegem(token, **options)
78
79
  ::Telegem.new(token, **options)
79
80
  end
80
- end
81
+ end
@@ -120,7 +120,7 @@ module Telegem
120
120
  begin
121
121
  body = request.body.read
122
122
  update_data = JSON.parse(body)
123
- Async { process_webhook_update(update_data) }
123
+ process_webhook_update(update_data)
124
124
  [200, {}, ["OK"]]
125
125
  rescue
126
126
  [500, {}, ["Internal Server Error"]]
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telegem
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.4
4
+ version: 3.3.1
5
5
  platform: ruby
6
6
  authors:
7
- - sick_phantom
7
+ - zendrx
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-04-24 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: concurrent-ruby
@@ -129,10 +130,11 @@ email:
129
130
  - ynwghosted@icloud.com
130
131
  executables:
131
132
  - telegem-ssl
133
+ - telegem-init
132
134
  extensions: []
133
135
  extra_rdoc_files: []
134
136
  files:
135
- - CHANGELOG
137
+ - CHANGELOG.md
136
138
  - CODE_OF_CONDUCT.md
137
139
  - Contributing.md
138
140
  - Gemfile
@@ -143,6 +145,7 @@ files:
143
145
  - assets/.gitkeep
144
146
  - assets/logo.png
145
147
  - bin/.gitkeep
148
+ - bin/telegem-init
146
149
  - bin/telegem-ssl
147
150
  - docs/.gitkeep
148
151
  - docs/ctx.md
@@ -156,6 +159,7 @@ files:
156
159
  - lib/core/rate_limit.rb
157
160
  - lib/core/scene.rb
158
161
  - lib/markup/.gitkeep
162
+ - lib/markup/inline.rb
159
163
  - lib/markup/keyboard.rb
160
164
  - lib/plugins/.gitkeep
161
165
  - lib/plugins/file_extract.rb
@@ -166,18 +170,18 @@ files:
166
170
  - lib/webhook/.gitkeep
167
171
  - lib/webhook/server.rb
168
172
  - public/.gitkeep
169
- homepage: https://gitlab.com/ruby-telegem/telegem
173
+ homepage: https://github.com/slick-lab/telegem
170
174
  licenses:
171
175
  - MIT
172
176
  metadata:
173
- homepage_uri: https://gitlab.com/ruby-telegem/telegem/-/blob/main/README.md
174
- source_code_uri: https://gitlab.com/ruby-telegem/telegem
175
- changelog_uri: https://gitlab.com/ruby-telegem/telegem/-/blob/main/CHANGELOG.md
176
- bug_tracker_uri: https://gitlab.com/ruby-telegem/telegem/-/issues
177
+ homepage_uri: https://github.com/slick-lab/telegem/-/blob/main/README.md
178
+ source_code_uri: https://github.com/slick-lab/telegem
179
+ changelog_uri: https://github.com/slick-lab/telegem/-/blob/main/CHANGELOG.md
180
+ bug_tracker_uri: https://github.com/slick-lab/telegem/-/issues
177
181
  documentation_uri: https://gitlab.com/ruby-telegem/telegem/-/tree/main/docs-src?ref_type=heads
178
182
  rubygems_mfa_required: 'false'
179
- post_install_message: "Thanks for installing Telegem 3.2.4!\n\n\U0001F4DA Documentation:
180
- https://gitlab.com/ruby-telegem/telegem\n\n\U0001F510 For SSL Webhooks:\nRun: telegem-ssl
183
+ post_install_message: "Thanks for installing Telegem 3.3.1!\n\n\U0001F4DA Documentation:
184
+ https://github.com/slick-lab/telegem\n\n\U0001F510 For SSL Webhooks:\nRun: telegem-ssl
181
185
  your-domain.com\nThis sets up Let's Encrypt certificates automatically.\n\n\U0001F916
182
186
  Happy bot building!\n"
183
187
  rdoc_options: []
@@ -194,7 +198,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
198
  - !ruby/object:Gem::Version
195
199
  version: '0'
196
200
  requirements: []
197
- rubygems_version: 3.6.7
201
+ rubygems_version: 3.5.22
202
+ signing_key:
198
203
  specification_version: 4
199
204
  summary: Modern, fast Telegram Bot Framework for Ruby
200
205
  test_files: []
data/CHANGELOG DELETED
@@ -1,95 +0,0 @@
1
- Telegem Changelog
2
-
3
- v3.1.1 (current)
4
-
5
- 🚀 New Features
6
-
7
- · FileExtractor Plugin: New plugin for extracting content from various file types (PDF, JSON, HTML, TXT)
8
- · Async File Download: Added download method to API client for downloading Telegram files
9
- · Context File Helpers: Added download_file, download_photo, download_document methods to Context
10
- · Extended File Support: Plugin supports PDF text extraction, JSON parsing, HTML/raw text processing
11
- · Async/Sync Dual Mode: All file operations available in both sync (download) and async (download!) modes
12
-
13
- v3.1.0
14
-
15
- · BREAKING: Rewrote polling system to prevent duplicate messages
16
- · Fixed thread deadlock in async polling loop
17
- · Added scene_middleware.rb for scene-based conversations
18
- · Improved MemoryStore with TTL and thread safety
19
- · Enhanced keyboard markup builder with web_app support
20
- · Added message reaction and chat boost update types
21
- · Fixed callback query handling for inline keyboards
22
-
23
- v3.0.0
24
-
25
- · BREAKING: Complete async rewrite with async gem
26
- · New HTTP client using HTTPX with proper async/await pattern
27
- · Added scene system for multi-step conversations
28
- · Middleware composer system for plugin architecture
29
- · Type system with dynamic accessors for Telegram objects
30
- · Session management with memory store
31
- · Rate limiting middleware
32
- · File upload support via multipart forms
33
-
34
- v2.0.0
35
-
36
- · BREAKING: Ruby 3.0+ requirement
37
- · Added webhook support with Rack middleware
38
- · Inline query and callback query handlers
39
- · Location, contact, and poll answer handlers
40
- · Keyboard markup helpers (Telegem::Markup)
41
- · Improved error handling with custom error classes
42
- · Logging integration with configurable loggers
43
-
44
- v1.5.0
45
-
46
- · Added command argument parsing (ctx.command_args)
47
- · Message entity parsing (mentions, hashtags, bot commands)
48
- · Chat member update handlers
49
- · Pre-checkout and shipping query support
50
- · File download helper methods
51
- · Context helper methods for common API calls
52
-
53
- v1.0.0
54
-
55
- · Stable API release
56
- · Message handlers with text pattern matching
57
- · Command handlers with regex support
58
- · Basic context object with chat/message accessors
59
- · Simple API client with error handling
60
- · Polling and webhook modes
61
- · Configuration options for timeout and limits
62
-
63
- v0.5.0
64
-
65
- · Added callback query support
66
- · Inline keyboard builder
67
- · Message editing and deletion helpers
68
- · Media sending methods (photo, document, audio, video)
69
- · Chat action methods (typing, upload indicators)
70
-
71
- v0.3.0
72
-
73
- · Middleware system with bot.use
74
- · Session management foundation
75
- · Basic rate limiting
76
- · Command filtering by chat type
77
- · Improved logging with debug levels
78
-
79
- v0.2.0
80
-
81
- · Basic polling implementation
82
- · Message type detection (text, photo, document)
83
- · Command parsing with arguments
84
- · Simple reply methods
85
- · Error handling for API calls
86
-
87
- v0.1.0 (Initial Release)
88
-
89
- · Basic Telegram Bot API wrapper
90
- · Send/receive messages
91
- · Simple command handling
92
- · Minimal dependencies (just httparty)
93
- · Support for basic message types
94
-
95
- ---