telegem 0.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 +7 -0
- data/lib/api/client.rb +156 -0
- data/lib/api/types.rb +190 -0
- data/lib/core/bot.rb +275 -0
- data/lib/core/composer.rb +40 -0
- data/lib/core/context.rb +395 -0
- data/lib/core/scene.rb +81 -0
- data/lib/markup/keyboard.rb +294 -0
- data/lib/session/memory_store.rb +49 -0
- data/lib/session/middleware.rb +53 -0
- data/telegem.rb +55 -0
- data/version.rb +4 -0
- data/webhook/server.rb +86 -0
- metadata +123 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
module Telegem
|
|
2
|
+
module Markup
|
|
3
|
+
class Keyboard
|
|
4
|
+
attr_reader :buttons, :options
|
|
5
|
+
|
|
6
|
+
def initialize(buttons = [], **options)
|
|
7
|
+
@buttons = buttons
|
|
8
|
+
@options = {
|
|
9
|
+
resize_keyboard: true,
|
|
10
|
+
one_time_keyboard: false,
|
|
11
|
+
selective: false
|
|
12
|
+
}.merge(options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Create from array
|
|
16
|
+
def self.[](*rows)
|
|
17
|
+
new(rows)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Builder pattern
|
|
21
|
+
def self.build(&block)
|
|
22
|
+
builder = Builder.new
|
|
23
|
+
builder.instance_eval(&block) if block_given?
|
|
24
|
+
builder.keyboard
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Add a row
|
|
28
|
+
def row(*buttons)
|
|
29
|
+
@buttons << buttons.flatten
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Add a button
|
|
34
|
+
def button(text, **options)
|
|
35
|
+
last_row = @buttons.last || []
|
|
36
|
+
|
|
37
|
+
if last_row.is_a?(Array)
|
|
38
|
+
last_row << { text: text }.merge(options)
|
|
39
|
+
else
|
|
40
|
+
@buttons << [{ text: text }.merge(options)]
|
|
41
|
+
end
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Chainable options
|
|
46
|
+
def resize(resize = true)
|
|
47
|
+
@options[:resize_keyboard] = resize
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def one_time(one_time = true)
|
|
52
|
+
@options[:one_time_keyboard] = one_time
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def selective(selective = true)
|
|
57
|
+
@options[:selective] = selective
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Convert to Telegram format
|
|
62
|
+
def to_h
|
|
63
|
+
{
|
|
64
|
+
keyboard: @buttons.map { |row| row.is_a?(Array) ? row : [row] },
|
|
65
|
+
**@options
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def to_json(*args)
|
|
70
|
+
to_h.to_json(*args)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Remove keyboard
|
|
74
|
+
def self.remove(selective: false)
|
|
75
|
+
{
|
|
76
|
+
remove_keyboard: true,
|
|
77
|
+
selective: selective
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Force reply
|
|
82
|
+
def self.force_reply(selective: false, input_field_placeholder: nil)
|
|
83
|
+
markup = {
|
|
84
|
+
force_reply: true,
|
|
85
|
+
selective: selective
|
|
86
|
+
}
|
|
87
|
+
markup[:input_field_placeholder] = input_field_placeholder if input_field_placeholder
|
|
88
|
+
markup
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class InlineKeyboard
|
|
93
|
+
attr_reader :buttons
|
|
94
|
+
|
|
95
|
+
def initialize(buttons = [])
|
|
96
|
+
@buttons = buttons
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Create from array
|
|
100
|
+
def self.[](*rows)
|
|
101
|
+
new(rows)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Builder pattern
|
|
105
|
+
def self.build(&block)
|
|
106
|
+
builder = InlineBuilder.new
|
|
107
|
+
builder.instance_eval(&block) if block_given?
|
|
108
|
+
builder.keyboard
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Add a row
|
|
112
|
+
def row(*buttons)
|
|
113
|
+
@buttons << buttons.flatten
|
|
114
|
+
self
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Add a button
|
|
118
|
+
def button(text, **options)
|
|
119
|
+
last_row = @buttons.last || []
|
|
120
|
+
|
|
121
|
+
if last_row.is_a?(Array)
|
|
122
|
+
last_row << { text: text }.merge(options)
|
|
123
|
+
else
|
|
124
|
+
@buttons << [{ text: text }.merge(options)]
|
|
125
|
+
end
|
|
126
|
+
self
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# URL button
|
|
130
|
+
def url(text, url)
|
|
131
|
+
button(text, url: url)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Callback button
|
|
135
|
+
def callback(text, data)
|
|
136
|
+
button(text, callback_data: data)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Web app button
|
|
140
|
+
def web_app(text, url)
|
|
141
|
+
button(text, web_app: { url: url })
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Login button
|
|
145
|
+
def login(text, url, **options)
|
|
146
|
+
button(text, login_url: { url: url, **options })
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Switch inline query button
|
|
150
|
+
def switch_inline(text, query = "")
|
|
151
|
+
button(text, switch_inline_query: query)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Switch inline query current chat button
|
|
155
|
+
def switch_inline_current(text, query = "")
|
|
156
|
+
button(text, switch_inline_query_current_chat: query)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Convert to Telegram format
|
|
160
|
+
def to_h
|
|
161
|
+
{
|
|
162
|
+
inline_keyboard: @buttons.map { |row| row.is_a?(Array) ? row : [row] }
|
|
163
|
+
}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def to_json(*args)
|
|
167
|
+
to_h.to_json(*args)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Builder DSL for keyboards
|
|
172
|
+
class Builder
|
|
173
|
+
attr_reader :keyboard
|
|
174
|
+
|
|
175
|
+
def initialize
|
|
176
|
+
@keyboard = Keyboard.new
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def row(*buttons, &block)
|
|
180
|
+
if block_given?
|
|
181
|
+
sub_builder = Builder.new
|
|
182
|
+
sub_builder.instance_eval(&block)
|
|
183
|
+
@keyboard.row(*sub_builder.keyboard.buttons.flatten(1))
|
|
184
|
+
else
|
|
185
|
+
@keyboard.row(*buttons)
|
|
186
|
+
end
|
|
187
|
+
self
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def button(text, **options)
|
|
191
|
+
@keyboard.button(text, **options)
|
|
192
|
+
self
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def method_missing(name, *args, &block)
|
|
196
|
+
if @keyboard.respond_to?(name)
|
|
197
|
+
@keyboard.send(name, *args, &block)
|
|
198
|
+
else
|
|
199
|
+
super
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def respond_to_missing?(name, include_private = false)
|
|
204
|
+
@keyboard.respond_to?(name) || super
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Builder DSL for inline keyboards
|
|
209
|
+
class InlineBuilder
|
|
210
|
+
attr_reader :keyboard
|
|
211
|
+
|
|
212
|
+
def initialize
|
|
213
|
+
@keyboard = InlineKeyboard.new
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def row(*buttons, &block)
|
|
217
|
+
if block_given?
|
|
218
|
+
sub_builder = InlineBuilder.new
|
|
219
|
+
sub_builder.instance_eval(&block)
|
|
220
|
+
@keyboard.row(*sub_builder.keyboard.buttons.flatten(1))
|
|
221
|
+
else
|
|
222
|
+
@keyboard.row(*buttons)
|
|
223
|
+
end
|
|
224
|
+
self
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def button(text, **options)
|
|
228
|
+
@keyboard.button(text, **options)
|
|
229
|
+
self
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def url(text, url)
|
|
233
|
+
@keyboard.url(text, url)
|
|
234
|
+
self
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def callback(text, data)
|
|
238
|
+
@keyboard.callback(text, data)
|
|
239
|
+
self
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def web_app(text, url)
|
|
243
|
+
@keyboard.web_app(text, url)
|
|
244
|
+
self
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def login(text, url, **options)
|
|
248
|
+
@keyboard.login(text, url, **options)
|
|
249
|
+
self
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def switch_inline(text, query = "")
|
|
253
|
+
@keyboard.switch_inline(text, query)
|
|
254
|
+
self
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def switch_inline_current(text, query = "")
|
|
258
|
+
@keyboard.switch_inline_current(text, query)
|
|
259
|
+
self
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def method_missing(name, *args, &block)
|
|
263
|
+
if @keyboard.respond_to?(name)
|
|
264
|
+
@keyboard.send(name, *args, &block)
|
|
265
|
+
else
|
|
266
|
+
super
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def respond_to_missing?(name, include_private = false)
|
|
271
|
+
@keyboard.respond_to?(name) || super
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Shortcuts for common use
|
|
276
|
+
class << self
|
|
277
|
+
def keyboard(&block)
|
|
278
|
+
Keyboard.build(&block)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def inline(&block)
|
|
282
|
+
InlineKeyboard.build(&block)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def remove(**options)
|
|
286
|
+
Keyboard.remove(**options)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def force_reply(**options)
|
|
290
|
+
Keyboard.force_reply(**options)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Telegem
|
|
2
|
+
module Session
|
|
3
|
+
class MemoryStore
|
|
4
|
+
def initialize
|
|
5
|
+
@store = {}
|
|
6
|
+
@mutex = Mutex.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def get(key)
|
|
10
|
+
@mutex.synchronize do
|
|
11
|
+
@store[key.to_s]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def set(key, value)
|
|
16
|
+
@mutex.synchronize do
|
|
17
|
+
@store[key.to_s] = value
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(key)
|
|
22
|
+
@mutex.synchronize do
|
|
23
|
+
@store.delete(key.to_s)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def clear
|
|
28
|
+
@mutex.synchronize do
|
|
29
|
+
@store.clear
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def keys
|
|
34
|
+
@mutex.synchronize do
|
|
35
|
+
@store.keys
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def size
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
@store.size
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def empty?
|
|
46
|
+
@mutex.synchronize do
|
|
47
|
+
@store.empty?
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Telegem
|
|
2
|
+
module Session
|
|
3
|
+
class Middleware
|
|
4
|
+
def initialize(store = nil)
|
|
5
|
+
@store = store || MemoryStore.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def call(ctx, next_middleware)
|
|
9
|
+
user_id = get_user_id(ctx)
|
|
10
|
+
return next_middleware.call(ctx) unless user_id
|
|
11
|
+
|
|
12
|
+
ctx.session = @store.get(user_id) || {}
|
|
13
|
+
|
|
14
|
+
begin
|
|
15
|
+
result = next_middleware.call(ctx)
|
|
16
|
+
# Handle async result
|
|
17
|
+
result.is_a?(Async::Task) ? result : Async::Task.new(result)
|
|
18
|
+
ensure
|
|
19
|
+
@store.set(user_id, ctx.session)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def get_user_id(ctx)
|
|
25
|
+
return nil unless ctx.from
|
|
26
|
+
|
|
27
|
+
ctx.from.id
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class MemoryStore
|
|
32
|
+
def initialize
|
|
33
|
+
@store = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def get(key)
|
|
37
|
+
@store[key]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def set(key, value)
|
|
41
|
+
@store[key] = value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete(key)
|
|
45
|
+
@store.delete(key)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def clear
|
|
49
|
+
@store.clear
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
data/telegem.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Telegem
|
|
2
|
+
VERSION = '0.1.0'.freeze
|
|
3
|
+
|
|
4
|
+
# Define module structure
|
|
5
|
+
module API; end
|
|
6
|
+
module Core; end
|
|
7
|
+
module Session; end
|
|
8
|
+
module Markup; end
|
|
9
|
+
module Webhook; end
|
|
10
|
+
module Types; end
|
|
11
|
+
|
|
12
|
+
# Shortcut for creating a new bot
|
|
13
|
+
def self.new(token, **options)
|
|
14
|
+
require_relative 'lib/core/bot'
|
|
15
|
+
Core::Bot.new(token, **options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Configure global settings
|
|
19
|
+
def self.configure(&block)
|
|
20
|
+
yield(config) if block_given?
|
|
21
|
+
config
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.config
|
|
25
|
+
@config ||= Configuration.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Configuration
|
|
29
|
+
attr_accessor :logger, :default_adapter, :default_concurrency,
|
|
30
|
+
:default_session_store, :default_webhook_port
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
@logger = Logger.new($stdout)
|
|
34
|
+
@default_adapter = :async_http
|
|
35
|
+
@default_concurrency = 10
|
|
36
|
+
@default_session_store = :memory
|
|
37
|
+
@default_webhook_port = 3000
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Error base class
|
|
42
|
+
class Error < StandardError; end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Load all components
|
|
46
|
+
require_relative 'lib/api/client'
|
|
47
|
+
require_relative 'lib/api/types'
|
|
48
|
+
require_relative 'lib/core/bot'
|
|
49
|
+
require_relative 'lib/core/context'
|
|
50
|
+
require_relative 'lib/core/composer'
|
|
51
|
+
require_relative 'lib/core/scene'
|
|
52
|
+
require_relative 'lib/session/middleware'
|
|
53
|
+
require_relative 'lib/session/memory_store'
|
|
54
|
+
require_relative 'lib/markup/keyboard'
|
|
55
|
+
require_relative 'webhook/server'
|
data/version.rb
ADDED
data/webhook/server.rb
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Telegem
|
|
2
|
+
module Webhook
|
|
3
|
+
class Server
|
|
4
|
+
attr_reader :bot, :endpoint, :server, :logger
|
|
5
|
+
|
|
6
|
+
def initialize(bot, endpoint: nil, logger: nil)
|
|
7
|
+
@bot = bot
|
|
8
|
+
@endpoint = endpoint || Async::HTTP::Endpoint.parse("http://0.0.0.0:3000")
|
|
9
|
+
@logger = logger || Logger.new($stdout)
|
|
10
|
+
@server = nil
|
|
11
|
+
@running = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
Async do |task|
|
|
16
|
+
@server = Async::HTTP::Server.new(app, @endpoint)
|
|
17
|
+
@running = true
|
|
18
|
+
|
|
19
|
+
@logger.info "Starting webhook server on #{@endpoint}"
|
|
20
|
+
@logger.info "Set your Telegram webhook to: #{webhook_url}"
|
|
21
|
+
|
|
22
|
+
@server.run
|
|
23
|
+
rescue => e
|
|
24
|
+
@logger.error "Webhook server error: #{e.message}"
|
|
25
|
+
raise
|
|
26
|
+
ensure
|
|
27
|
+
@running = false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def stop
|
|
32
|
+
Async do
|
|
33
|
+
@server&.close
|
|
34
|
+
@running = false
|
|
35
|
+
@logger.info "Webhook server stopped"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def running?
|
|
40
|
+
@running
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def webhook_url
|
|
44
|
+
"#{@endpoint.url}/webhook/#{@bot.token}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def app
|
|
48
|
+
proc do |req|
|
|
49
|
+
handle_request(req)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def process_webhook_request(req)
|
|
54
|
+
Async do
|
|
55
|
+
body = req.body.read
|
|
56
|
+
data = JSON.parse(body)
|
|
57
|
+
await @bot.process(data)
|
|
58
|
+
[200, {}, ["OK"]]
|
|
59
|
+
rescue JSON::ParserError => e
|
|
60
|
+
@logger.error "Invalid JSON in webhook request: #{e.message}"
|
|
61
|
+
[400, {}, ["Bad Request"]]
|
|
62
|
+
rescue => e
|
|
63
|
+
@logger.error "Error processing webhook request: #{e.message}"
|
|
64
|
+
[500, {}, ["Internal Server Error"]]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def handle_request(req)
|
|
69
|
+
Async do
|
|
70
|
+
case req.path
|
|
71
|
+
when "/webhook/#{@bot.token}"
|
|
72
|
+
process_webhook_request(req)
|
|
73
|
+
when "/health"
|
|
74
|
+
[200, {}, ["OK"]]
|
|
75
|
+
else
|
|
76
|
+
[404, {}, ["Not Found"]]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def call(req)
|
|
82
|
+
handle_request(req)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: telegem
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Phantom
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: async
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: async-http
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.60'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.60'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: mime-types
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.4'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.4'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rake
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '13.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '13.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rspec
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.0'
|
|
82
|
+
description: A Telegraf-inspired Telegram Bot framework with async I/O
|
|
83
|
+
email:
|
|
84
|
+
- ynghosted@icloud.com
|
|
85
|
+
executables: []
|
|
86
|
+
extensions: []
|
|
87
|
+
extra_rdoc_files: []
|
|
88
|
+
files:
|
|
89
|
+
- lib/api/client.rb
|
|
90
|
+
- lib/api/types.rb
|
|
91
|
+
- lib/core/bot.rb
|
|
92
|
+
- lib/core/composer.rb
|
|
93
|
+
- lib/core/context.rb
|
|
94
|
+
- lib/core/scene.rb
|
|
95
|
+
- lib/markup/keyboard.rb
|
|
96
|
+
- lib/session/memory_store.rb
|
|
97
|
+
- lib/session/middleware.rb
|
|
98
|
+
- telegem.rb
|
|
99
|
+
- version.rb
|
|
100
|
+
- webhook/server.rb
|
|
101
|
+
homepage: https://gitlab.com/ruby-telegem/telegem
|
|
102
|
+
licenses:
|
|
103
|
+
- MIT
|
|
104
|
+
metadata: {}
|
|
105
|
+
rdoc_options: []
|
|
106
|
+
require_paths:
|
|
107
|
+
- lib
|
|
108
|
+
- webhook
|
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
110
|
+
requirements:
|
|
111
|
+
- - ">="
|
|
112
|
+
- !ruby/object:Gem::Version
|
|
113
|
+
version: '3.0'
|
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - ">="
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '0'
|
|
119
|
+
requirements: []
|
|
120
|
+
rubygems_version: 3.6.9
|
|
121
|
+
specification_version: 4
|
|
122
|
+
summary: Modern, async Telegram Bot API for Ruby
|
|
123
|
+
test_files: []
|