telegem 0.2.0 → 0.2.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fb8c1bced00447821d2b879718c2ab162cea8ff348970307d620ca31b106df7
4
- data.tar.gz: 48034a23e5079b3335bac812bb8a2643478a628e25eee322af6aa16927cbeb41
3
+ metadata.gz: dc3dc81c673fa1128b108e02c3401f4bac483d7173759b4c901c3db625fa8dd8
4
+ data.tar.gz: 4cb6de9fa5d9b8c5babbbd83ff86b5b7f6c0161628e74dbb7416ee5eeaa10e70
5
5
  SHA512:
6
- metadata.gz: dab8827b4606a40ea10f882b774a400c9c32685461b4147a1879987009b4d77ed9efd83571c15923915cdd40e61848533db2c09e74cc3535c00df8bcb1f78ebc
7
- data.tar.gz: a5e90efd58279b2cb47b04a7933e42b9281f4e8aba197f12783339984c9b238a85e0f3a34faf75b3fe71137efd9941da4ffa5405b8c76da632fcea62b395d7c5
6
+ metadata.gz: 32211bca88485aa5d19b41c8bc400b1af8b790de8ec4cf9761537a2f59de299e4ee6c5dca2de3e5561f58fe0280238c1452cff10429aa876d2fad2426556819d
7
+ data.tar.gz: fef73c4f8d976614ec815cee3c230409bf471564f38a46acb5fc7093c80d2154186a621f68068098cc1072cdb144184c92a683be83e42107f7ea409ea3175b5e
data/lib/api/client.rb CHANGED
@@ -1,156 +1,163 @@
1
- module Telegem
2
- module API
3
- class Client
4
- BASE_URL = 'https://api.telegram.org'
5
-
6
- attr_reader :token, :logger
7
-
8
- def initialize(token, endpoint: nil, logger: nil)
9
- @token = token
10
- @logger = logger || Logger.new($stdout)
11
- @endpoint = endpoint || Async::HTTP::Endpoint.parse("#{BASE_URL}/bot#{token}")
12
- @client = nil
13
- @semaphore = Async::Semaphore.new(30)
14
- end
15
-
16
- def call(method, params = {})
17
- Async do |task|
18
- @semaphore.async do
19
- make_request(method, clean_params(params))
20
- end
21
- end
22
- end
23
-
24
- def upload(method, params)
25
- Async do |task|
26
- @semaphore.async do
27
- make_multipart_request(method, params)
28
- end
29
- end
30
- end
31
-
32
- def get_updates(offset: nil, timeout: 30, limit: 100)
33
- params = { timeout: timeout, limit: limit }
34
- params[:offset] = offset if offset
35
- call('getUpdates', params)
36
- end
37
-
38
- def close
39
- @client&.close
40
- end
41
-
42
- private
43
-
44
- def make_request(method, params)
45
- with_client do |client|
46
- headers = { 'content-type' => 'application/json' }
47
- body = params.to_json
48
-
49
- response = client.post("/bot#{@token}/#{method}", headers, body)
50
- handle_response(response)
51
- end
52
- end
53
-
54
- def make_multipart_request(method, params)
55
- with_client do |client|
56
- form = build_multipart(params)
57
- headers = form.headers
58
-
59
- response = client.post("/bot#{@token}/#{method}", headers, form.body)
60
- handle_response(response)
61
- end
62
- end
63
-
64
- def with_client(&block)
65
- @client ||= Async::HTTP::Client.new(@endpoint)
66
- yield @client
67
- end
68
-
69
- def clean_params(params)
70
- params.reject { |_, v| v.nil? }
71
- end
72
-
73
- def build_multipart(params)
74
- # Build multipart form data for file uploads
75
- boundary = SecureRandom.hex(16)
76
- parts = []
77
-
78
- params.each do |key, value|
79
- if file?(value)
80
- parts << part_from_file(key, value, boundary)
81
- else
82
- parts << part_from_field(key, value, boundary)
83
- end
84
- end
85
-
86
- parts << "--#{boundary}--\r\n"
87
-
88
- body = parts.join
89
- headers = {
90
- 'content-type' => "multipart/form-data; boundary=#{boundary}",
91
- 'content-length' => body.bytesize.to_s
92
- }
93
-
94
- OpenStruct.new(headers: headers, body: body)
95
- end
96
-
97
- def file?(value)
98
- value.is_a?(File) ||
99
- value.is_a?(StringIO) ||
100
- (value.is_a?(String) && File.exist?(value))
101
- end
102
-
103
- def part_from_file(key, file, boundary)
104
- filename = File.basename(file.path) if file.respond_to?(:path)
105
- filename ||= "file"
106
-
107
- mime_type = MIME::Types.type_for(filename).first || 'application/octet-stream'
108
-
109
- content = if file.is_a?(String)
110
- File.read(file)
111
- else
112
- file.read
113
- end
114
-
115
- <<~PART
116
- --#{boundary}\r
117
- Content-Disposition: form-data; name="#{key}"; filename="#{filename}"\r
118
- Content-Type: #{mime_type}\r
119
- \r
120
- #{content}\r
121
- PART
122
- end
123
-
124
- def part_from_field(key, value, boundary)
125
- <<~PART
126
- --#{boundary}\r
127
- Content-Disposition: form-data; name="#{key}"\r
128
- \r
129
- #{value}\r
130
- PART
131
- end
132
-
133
- def handle_response(response)
134
- body = response.read
135
- json = JSON.parse(body)
136
-
137
- if json['ok']
138
- json['result']
139
- else
140
- raise APIError.new(json['description'], json['error_code'])
141
- end
142
- rescue JSON::ParserError
143
- raise APIError, "Invalid JSON response: #{body[0..100]}"
144
- end
1
+ require 'async'
2
+ require 'async/http'
3
+ require 'json'
4
+ require 'logger'
5
+ require 'mime/types'
6
+ require 'securerandom'
7
+ require 'ostruct'
8
+
9
+ module Telegem
10
+ module API
11
+ class Client
12
+ BASE_URL = 'https://api.telegram.org'
13
+
14
+ attr_reader :token, :logger
15
+
16
+ def initialize(token, endpoint: nil, logger: nil)
17
+ @token = token
18
+ @logger = logger || Logger.new($stdout)
19
+ @endpoint = endpoint || Async::HTTP::Endpoint.parse("#{BASE_URL}/bot#{token}")
20
+ @client = nil
21
+ @semaphore = Async::Semaphore.new(30)
22
+ end
23
+
24
+ def call(method, params = {})
25
+ Async do |task|
26
+ @semaphore.async do
27
+ make_request(method, clean_params(params))
145
28
  end
29
+ end
30
+ end
31
+
32
+ def upload(method, params)
33
+ Async do |task|
34
+ @semaphore.async do
35
+ make_multipart_request(method, params)
36
+ end
37
+ end
38
+ end
39
+
40
+ def get_updates(offset: nil, timeout: 30, limit: 100)
41
+ params = { timeout: timeout, limit: limit }
42
+ params[:offset] = offset if offset
43
+ call('getUpdates', params)
44
+ end
45
+
46
+ def close
47
+ @client&.close
48
+ end
49
+
50
+ private
146
51
 
147
- class APIError < StandardError
148
- attr_reader :code
52
+ def make_request(method, params)
53
+ with_client do |client|
54
+ headers = { 'content-type' => 'application/json' }
55
+ body = params.to_json
149
56
 
150
- def initialize(message, code = nil)
151
- super(message)
152
- @code = code
153
- end
57
+ response = client.post("/bot#{@token}/#{method}", headers, body)
58
+ handle_response(response)
59
+ end
60
+ end
61
+
62
+ def make_multipart_request(method, params)
63
+ with_client do |client|
64
+ form = build_multipart(params)
65
+ headers = form.headers
66
+
67
+ response = client.post("/bot#{@token}/#{method}", headers, form.body)
68
+ handle_response(response)
69
+ end
70
+ end
71
+
72
+ def with_client(&block)
73
+ @client ||= Async::HTTP::Client.new(@endpoint)
74
+ yield @client
75
+ end
76
+
77
+ def clean_params(params)
78
+ params.reject { |_, v| v.nil? }
79
+ end
80
+
81
+ def build_multipart(params)
82
+ boundary = SecureRandom.hex(16)
83
+ parts = []
84
+
85
+ params.each do |key, value|
86
+ if file?(value)
87
+ parts << part_from_file(key, value, boundary)
88
+ else
89
+ parts << part_from_field(key, value, boundary)
154
90
  end
155
91
  end
156
- end
92
+
93
+ parts << "--#{boundary}--\r\n"
94
+
95
+ body = parts.join
96
+ headers = {
97
+ 'content-type' => "multipart/form-data; boundary=#{boundary}",
98
+ 'content-length' => body.bytesize.to_s
99
+ }
100
+
101
+ OpenStruct.new(headers: headers, body: body)
102
+ end
103
+
104
+ def file?(value)
105
+ value.is_a?(File) ||
106
+ value.is_a?(StringIO) ||
107
+ (value.is_a?(String) && File.exist?(value))
108
+ end
109
+
110
+ def part_from_file(key, file, boundary)
111
+ filename = File.basename(file.path) if file.respond_to?(:path)
112
+ filename ||= "file"
113
+
114
+ mime_type = MIME::Types.type_for(filename).first || 'application/octet-stream'
115
+
116
+ content = if file.is_a?(String)
117
+ File.read(file)
118
+ else
119
+ file.read
120
+ end
121
+
122
+ <<~PART
123
+ --#{boundary}\r
124
+ Content-Disposition: form-data; name="#{key}"; filename="#{filename}"\r
125
+ Content-Type: #{mime_type}\r
126
+ \r
127
+ #{content}\r
128
+ PART
129
+ end
130
+
131
+ def part_from_field(key, value, boundary)
132
+ <<~PART
133
+ --#{boundary}\r
134
+ Content-Disposition: form-data; name="#{key}"\r
135
+ \r
136
+ #{value}\r
137
+ PART
138
+ end
139
+
140
+ def handle_response(response)
141
+ body = response.read
142
+ json = JSON.parse(body)
143
+
144
+ if json['ok']
145
+ json['result']
146
+ else
147
+ raise APIError.new(json['description'], json['error_code'])
148
+ end
149
+ rescue JSON::ParserError
150
+ raise APIError, "Invalid JSON response: #{body[0..100]}"
151
+ end
152
+ end
153
+
154
+ class APIError < StandardError
155
+ attr_reader :code
156
+
157
+ def initialize(message, code = nil)
158
+ super(message)
159
+ @code = code
160
+ end
161
+ end
162
+ end
163
+ end
data/lib/core/bot.rb CHANGED
@@ -71,7 +71,8 @@ module Telegem
71
71
  begin
72
72
  loop do
73
73
  updates_task = fetch_updates(offset, **options)
74
- updates = updates_task.wait
74
+ updates_data = updates_task.wait
75
+ updates = updates_data.map { |data| Types::Update.new(data) }
75
76
 
76
77
  updates.each do |update|
77
78
  parent.async do |child|
@@ -152,17 +153,11 @@ module Telegem
152
153
  private
153
154
 
154
155
  def fetch_updates(offset, timeout: 30, limit: 100, allowed_updates: nil)
155
- Async do
156
- params = { timeout: timeout, limit: limit }
157
- params[:offset] = offset if offset
158
- params[:allowed_updates] = allowed_updates if allowed_updates
159
-
160
- updates = await @api.get_updates(**params)
161
- updates.map { |data| Types::Update.new(data) }
162
- rescue API::APIError => e
163
- @logger.error "Failed to fetch updates: #{e.message}"
164
- []
165
- end
156
+ params = { timeout: timeout, limit: limit }
157
+ params[:offset] = offset if offset
158
+ params[:allowed_updates] = allowed_updates if allowed_updates
159
+
160
+ @api.get_updates(**params)
166
161
  end
167
162
 
168
163
  def process_update(update)
data/lib/telegem.rb CHANGED
@@ -6,7 +6,7 @@ require 'logger'
6
6
  require 'json'
7
7
 
8
8
  module Telegem
9
- VERSION = '0.2.0'.freeze
9
+ VERSION = '0.2.5'.freeze
10
10
 
11
11
  # Define module structure
12
12
  module API; end
data/telegem.gemspec CHANGED
@@ -4,7 +4,7 @@ Gem::Specification.new do |spec|
4
4
 
5
5
  # Read version from lib/telegem.rb
6
6
  version_file = File.read('lib/telegem.rb').match(/VERSION\s*=\s*['"]([^'"]+)['"]/)
7
- spec.version = version_file ? version_file[1] : "0.1.6"
7
+ spec.version = version_file ? version_file[1] : "0.2.5"
8
8
 
9
9
  spec.authors = ["Phantom"]
10
10
  spec.email = ["ynghosted@icloud.com"]
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: 0.2.0
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phantom