telegram_on_steroids 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/.document +5 -0
- data/.gitignore +20 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +50 -0
- data/examples/example_action.rb +13 -0
- data/lib/telegram_on_steroids/action.rb +73 -0
- data/lib/telegram_on_steroids/client.rb +173 -0
- data/lib/telegram_on_steroids/configurable.rb +22 -0
- data/lib/telegram_on_steroids/configuration.rb +50 -0
- data/lib/telegram_on_steroids/in_memory_store.rb +13 -0
- data/lib/telegram_on_steroids/keyboard/button.rb +16 -0
- data/lib/telegram_on_steroids/keyboard/inline_keyboard.rb +1 -0
- data/lib/telegram_on_steroids/keyboard/paginatable.rb +55 -0
- data/lib/telegram_on_steroids/keyboard/row.rb +12 -0
- data/lib/telegram_on_steroids/keyboard.rb +32 -0
- data/lib/telegram_on_steroids/params.rb +90 -0
- data/lib/telegram_on_steroids/process.rb +78 -0
- data/lib/telegram_on_steroids/session.rb +52 -0
- data/lib/telegram_on_steroids/updates.rb +24 -0
- data/lib/telegram_on_steroids/version.rb +3 -0
- data/lib/telegram_on_steroids.rb +37 -0
- data/telegram_on_steroids.gemspec +26 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8b199b6308185d9af76185b1c0dd7ae5b796c636413d55699136083a5036dcf0
|
4
|
+
data.tar.gz: 6f6846c55fca2acad53138ac2ee2b1d4d666d74d207c5975433e94b47f4e6d44
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 759c9ec914db75077d7bec322f0def4b4418b4f89e9ea62f0043b87920963ae290278496197c7006dce054092fe458fda02e3ed451fe4a49bcb5583648d1b53e
|
7
|
+
data.tar.gz: 49e7c96cb5eb47e16b8009faf453aea2e262cfa139c263d73f62a08f8afc9c89a0e1959bbdc64922db58f6c1fc7f1e455ab05216505a8cf7bc57667138e351df
|
data/.document
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "shoulda", ">= 0"
|
10
|
+
gem "rdoc", "~> 3.12"
|
11
|
+
gem "bundler", "~> 1.0"
|
12
|
+
gem "juwelier", "~> 2.1.0"
|
13
|
+
gem "simplecov", ">= 0"
|
14
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2024 Slaurmagan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
= telegram_on_steroids
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to telegram_on_steroids
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2024 Slaurmagan. See LICENSE.txt for
|
18
|
+
further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
require 'juwelier'
|
14
|
+
Juwelier::Tasks.new do |gem|
|
15
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
16
|
+
gem.name = "telegram_on_steroids"
|
17
|
+
gem.homepage = "http://github.com/Slaurmagan/telegram_on_steroids"
|
18
|
+
gem.license = "MIT"
|
19
|
+
gem.summary = %Q{TODO: one-line summary of your gem}
|
20
|
+
gem.description = %Q{TODO: longer description of your gem}
|
21
|
+
gem.email = "hrakovich.dev@gmail.com"
|
22
|
+
gem.authors = ["Slaurmagan"]
|
23
|
+
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Juwelier::RubygemsDotOrgTasks.new
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'test'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Code coverage detail"
|
35
|
+
task :simplecov do
|
36
|
+
ENV['COVERAGE'] = "true"
|
37
|
+
Rake::Task['test'].execute
|
38
|
+
end
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'rdoc/task'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
45
|
+
|
46
|
+
rdoc.rdoc_dir = 'rdoc'
|
47
|
+
rdoc.title = "telegram_on_steroids #{version}"
|
48
|
+
rdoc.rdoc_files.include('README*')
|
49
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
50
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class ExampleAction < TelegramOnSteroids::Action
|
2
|
+
on_redirect do |action|
|
3
|
+
action.send_message text: 'Text'
|
4
|
+
end
|
5
|
+
|
6
|
+
on_message do |action|
|
7
|
+
action.send_message text: 'Text'
|
8
|
+
end
|
9
|
+
|
10
|
+
on_callback_query whitelist: [] do |action|
|
11
|
+
action.answer_callback_query
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module TelegramOnSteroids
|
4
|
+
class Action
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :client, :session, :action, :request
|
8
|
+
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def on(name, &block)
|
12
|
+
raise StandardError, "#{name} not allowed callback" unless TelegramOnSteroids::UPDATE_TYPES.include?(name.to_s)
|
13
|
+
|
14
|
+
define_method("on_#{name.to_s}") do
|
15
|
+
instance_variable_set('@on_callback', block)
|
16
|
+
end
|
17
|
+
|
18
|
+
define_method("__run_on_#{name.to_s}") do
|
19
|
+
return respond_with_keyboard if request.params.callback? && pagination_callback? && current_keyboard
|
20
|
+
|
21
|
+
instance_eval(&block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def_delegators :@request, :params
|
27
|
+
def_delegators :@client, :send_message
|
28
|
+
|
29
|
+
def initialize(request:, client:, session:)
|
30
|
+
@request = request
|
31
|
+
@client = client
|
32
|
+
@session = session
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :request, :client, :session
|
36
|
+
|
37
|
+
def respond_with_keyboard(keyboard: current_keyboard)
|
38
|
+
keyboard_instance = keyboard.is_a?(Class) ? keyboard.new(request:, action: self) : keyboard
|
39
|
+
inline_keyboard = keyboard_instance.to_telegram_format
|
40
|
+
message_id = request.params.to_h.dig('callback_query', 'message', 'message_id')
|
41
|
+
text = keyboard_instance.text
|
42
|
+
session.write(:current_page, 1) unless keyboard == current_keyboard
|
43
|
+
|
44
|
+
if message_id
|
45
|
+
client.edit_message_text message_id:, text:, reply_markup: { inline_keyboard: }
|
46
|
+
else
|
47
|
+
client.send_message text:, reply_markup: { inline_keyboard: }
|
48
|
+
end
|
49
|
+
session.write(:keyboard, keyboard.is_a?(Class) ? keyboard.name : keyboard.class.name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def answer_callback_query(**params)
|
53
|
+
client.answer_callback_query callback_query_id: request.params.to_h.dig('callback_query', 'id'), **params
|
54
|
+
end
|
55
|
+
|
56
|
+
def pagination_callback?
|
57
|
+
request.params.callback_data =~ /page/
|
58
|
+
end
|
59
|
+
|
60
|
+
def current_keyboard
|
61
|
+
return unless session.read(:keyboard)
|
62
|
+
|
63
|
+
Object.const_get(session.read(:keyboard))
|
64
|
+
end
|
65
|
+
|
66
|
+
def redirect_to=(klass)
|
67
|
+
@redirect_to = klass
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_accessor :request, :client, :session
|
71
|
+
attr_reader :redirect_to
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
class TelegramOnSteroids::Client
|
2
|
+
API_VERSION = "5.5"
|
3
|
+
# WebhookConfigPath = Pathname.new("tmp/telegram_workflow/webhook_config.txt")
|
4
|
+
|
5
|
+
AVAILABLE_ACTIONS = %i(
|
6
|
+
getUpdates
|
7
|
+
getWebhookInfo
|
8
|
+
|
9
|
+
getMe
|
10
|
+
sendMessage
|
11
|
+
forwardMessage
|
12
|
+
copyMessage
|
13
|
+
sendPhoto
|
14
|
+
sendAudio
|
15
|
+
sendDocument
|
16
|
+
sendVideo
|
17
|
+
sendAnimation
|
18
|
+
sendVoice
|
19
|
+
sendVideoNote
|
20
|
+
sendMediaGroup
|
21
|
+
sendLocation
|
22
|
+
editMessageLiveLocation
|
23
|
+
stopMessageLiveLocation
|
24
|
+
sendVenue
|
25
|
+
sendContact
|
26
|
+
sendPoll
|
27
|
+
sendDice
|
28
|
+
sendChatAction
|
29
|
+
getUserProfilePhotos
|
30
|
+
getFile
|
31
|
+
kickChatMember
|
32
|
+
banChatMember
|
33
|
+
unbanChatMember
|
34
|
+
restrictChatMember
|
35
|
+
promoteChatMember
|
36
|
+
setChatAdministratorCustomTitle
|
37
|
+
banChatSenderChat
|
38
|
+
unbanChatSenderChat
|
39
|
+
setChatPermissions
|
40
|
+
exportChatInviteLink
|
41
|
+
createChatInviteLink
|
42
|
+
editChatInviteLink
|
43
|
+
revokeChatInviteLink
|
44
|
+
setChatPhoto
|
45
|
+
deleteChatPhoto
|
46
|
+
setChatTitle
|
47
|
+
setChatDescription
|
48
|
+
pinChatMessage
|
49
|
+
unpinChatMessage
|
50
|
+
unpinAllChatMessages
|
51
|
+
leaveChat
|
52
|
+
getChat
|
53
|
+
getChatAdministrators
|
54
|
+
getChatMembersCount
|
55
|
+
getChatMemberCount
|
56
|
+
getChatMember
|
57
|
+
setChatStickerSet
|
58
|
+
deleteChatStickerSet
|
59
|
+
answerCallbackQuery
|
60
|
+
setMyCommands
|
61
|
+
deleteMyCommands
|
62
|
+
getMyCommands
|
63
|
+
|
64
|
+
editMessageText
|
65
|
+
editMessageCaption
|
66
|
+
editMessageMedia
|
67
|
+
editMessageReplyMarkup
|
68
|
+
stopPoll
|
69
|
+
deleteMessage
|
70
|
+
|
71
|
+
sendSticker
|
72
|
+
getStickerSet
|
73
|
+
uploadStickerFile
|
74
|
+
createNewStickerSet
|
75
|
+
addStickerToSet
|
76
|
+
setStickerPositionInSet
|
77
|
+
deleteStickerFromSet
|
78
|
+
setStickerSetThumb
|
79
|
+
|
80
|
+
answerInlineQuery
|
81
|
+
|
82
|
+
sendInvoice
|
83
|
+
answerShippingQuery
|
84
|
+
answerPreCheckoutQuery
|
85
|
+
|
86
|
+
setPassportDataErrors
|
87
|
+
|
88
|
+
sendGame
|
89
|
+
setGameScore
|
90
|
+
getGameHighScores
|
91
|
+
|
92
|
+
logOut
|
93
|
+
close
|
94
|
+
)
|
95
|
+
|
96
|
+
DEPRECATED_ACTIONS = {
|
97
|
+
kickChatMember: :banChatMember,
|
98
|
+
getChatMembersCount: :getChatMemberCount
|
99
|
+
}
|
100
|
+
|
101
|
+
AVAILABLE_ACTIONS.each do |action|
|
102
|
+
method_name = action.to_s.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
103
|
+
|
104
|
+
define_method(method_name) do |params = {}|
|
105
|
+
@inline ?
|
106
|
+
save_request(action, params) :
|
107
|
+
make_request(action, params)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
attr_accessor :inline, :inline_request
|
112
|
+
attr_reader :api_url
|
113
|
+
|
114
|
+
def initialize(chat_id = nil)
|
115
|
+
@chat_id = chat_id
|
116
|
+
@api_url = "https://api.telegram.org/bot#{TelegramOnSteroids.config.api_token}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def set_webhook(params = {})
|
120
|
+
make_request("setWebhook", params)
|
121
|
+
cached_webhook_config(params)
|
122
|
+
end
|
123
|
+
|
124
|
+
def delete_webhook(params = {})
|
125
|
+
make_request("deleteWebhook", params)
|
126
|
+
cached_webhook_config(params)
|
127
|
+
end
|
128
|
+
|
129
|
+
def __setup_webhook(webhook_url = TelegramOnSteroids.config.webhook_url, params = {})
|
130
|
+
TelegramOnSteroids.config.logger.info "[TelegramWorkflow] Checking webhook setup..."
|
131
|
+
|
132
|
+
webhook_params = { url: webhook_url, allowed_updates: [], **params }
|
133
|
+
|
134
|
+
if cached_webhook_config != webhook_params
|
135
|
+
TelegramOnSteroids.config.logger.info "[TelegramWorkflow] Setting up a new webhook..."
|
136
|
+
set_webhook(webhook_params)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def cached_webhook_config(new_config = nil)
|
143
|
+
return nil
|
144
|
+
unless WebhookConfigPath.exist?
|
145
|
+
WebhookConfigPath.dirname.mkpath
|
146
|
+
WebhookConfigPath.write(Marshal.dump({}))
|
147
|
+
end
|
148
|
+
|
149
|
+
if new_config.nil?
|
150
|
+
Marshal.load(WebhookConfigPath.read)
|
151
|
+
else
|
152
|
+
WebhookConfigPath.write(Marshal.dump(new_config))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def save_request(action, params = {})
|
157
|
+
@inline_request = { method: action, chat_id: @chat_id, **params }
|
158
|
+
end
|
159
|
+
|
160
|
+
def make_request(action, params = {})
|
161
|
+
request_type = :json
|
162
|
+
|
163
|
+
response = ::Retryable.retryable(tries: 3, on: HTTP::ConnectionError) do
|
164
|
+
::HTTP.post("#{@api_url}/#{action}", request_type => { chat_id: @chat_id, **params })
|
165
|
+
end
|
166
|
+
|
167
|
+
if response.code != 200
|
168
|
+
raise StandardError, response.parse["description"]
|
169
|
+
end
|
170
|
+
|
171
|
+
response.parse
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module TelegramOnSteroids
|
2
|
+
module Configurable
|
3
|
+
def configure(&block)
|
4
|
+
define_method(:configure!) do
|
5
|
+
instance_eval { block.call(self) }
|
6
|
+
end
|
7
|
+
|
8
|
+
define_method(:after_initialize) do
|
9
|
+
configure!
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def callable(name)
|
14
|
+
attr_writer(name)
|
15
|
+
|
16
|
+
define_method(name) do
|
17
|
+
var = instance_variable_get("@#{name.to_s}")
|
18
|
+
var.is_a?(Proc) ? instance_eval(&var) : var
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
unless defined?(Rails)
|
2
|
+
require "logger"
|
3
|
+
end
|
4
|
+
|
5
|
+
module TelegramOnSteroids
|
6
|
+
class << self
|
7
|
+
def config
|
8
|
+
@config ||= Configuration.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def configure
|
12
|
+
yield(config)
|
13
|
+
config.verify!
|
14
|
+
|
15
|
+
@__after_configuration.call(config) if @__after_configuration
|
16
|
+
end
|
17
|
+
|
18
|
+
def __after_configuration(&block)
|
19
|
+
@__after_configuration = block
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Configuration
|
24
|
+
attr_accessor :session_store, :logger, :client, :start_action, :webhook_url, :api_token,
|
25
|
+
:webhook_params, :commands
|
26
|
+
|
27
|
+
REQUIRED_PARAMS = %i(start_action api_token)
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@client = TelegramOnSteroids::Client
|
31
|
+
@webhook_params = {}
|
32
|
+
|
33
|
+
if defined?(Rails)
|
34
|
+
@session_store = Rails.cache
|
35
|
+
@logger = Rails.logger
|
36
|
+
else
|
37
|
+
@session_store = InMemoryStore
|
38
|
+
@logger = Logger.new(STDOUT)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def verify!
|
43
|
+
blank_params = REQUIRED_PARAMS.select { |p| send(p).nil? }
|
44
|
+
|
45
|
+
if blank_params.any?
|
46
|
+
raise StandardError, blank_params
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class TelegramOnSteroids::Keyboard::Button
|
2
|
+
extend TelegramOnSteroids::Configurable
|
3
|
+
|
4
|
+
def initialize(text:, callback_data:, keyboard:)
|
5
|
+
@text = text
|
6
|
+
@callback_data = callback_data
|
7
|
+
@keyboard = keyboard
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_telegram_format
|
11
|
+
text = @text.is_a?(Proc) ? keyboard.instance_eval(&@text) : @text
|
12
|
+
{ text:, callback_data: }
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :text, :callback_data, :keyboard
|
16
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class TelegramOnSteroids::Keyboard::Inline < TelegramOnSteroids::Keyboard; end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module TelegramOnSteroids::Keyboard::Paginatable
|
2
|
+
DEFAULT_PER_PAGE = 2
|
3
|
+
|
4
|
+
def paginatable
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_telegram_format
|
9
|
+
action.session.write(:current_page, current_page)
|
10
|
+
return buttons if first_page? && last_page?
|
11
|
+
|
12
|
+
buttons[offset...offset + per_page].push(navigation_buttons)
|
13
|
+
end
|
14
|
+
|
15
|
+
def navigation_buttons
|
16
|
+
return [prev_button] if last_page?
|
17
|
+
return [next_button] if first_page?
|
18
|
+
|
19
|
+
[prev_button, next_button]
|
20
|
+
end
|
21
|
+
|
22
|
+
def per_page
|
23
|
+
@per_page || DEFAULT_PER_PAGE
|
24
|
+
end
|
25
|
+
|
26
|
+
def per_page=(val)
|
27
|
+
@per_page = val
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_page
|
31
|
+
return action.session.read(:current_page) || 1 unless request.params.callback_data =~ /page/
|
32
|
+
|
33
|
+
request.params.callback_data.scan(/\d+/).first.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
def offset
|
37
|
+
(current_page - 1) * per_page
|
38
|
+
end
|
39
|
+
|
40
|
+
def last_page?
|
41
|
+
current_page * per_page >= buttons.size
|
42
|
+
end
|
43
|
+
|
44
|
+
def first_page?
|
45
|
+
current_page == 1
|
46
|
+
end
|
47
|
+
|
48
|
+
def prev_button
|
49
|
+
{ text: '<< Prev', callback_data: "page_#{current_page - 1}" }
|
50
|
+
end
|
51
|
+
|
52
|
+
def next_button
|
53
|
+
{ text: 'Next >>', callback_data: "page_#{current_page + 1}" }
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class TelegramOnSteroids::Keyboard::Row
|
2
|
+
def initialize(keyboard:)
|
3
|
+
@buttons = []
|
4
|
+
@keyboard = keyboard
|
5
|
+
end
|
6
|
+
|
7
|
+
def button(**button)
|
8
|
+
buttons.push(TelegramOnSteroids::Keyboard::Button.new(**button, keyboard:).to_telegram_format)
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :buttons, :keyboard
|
12
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module TelegramOnSteroids
|
2
|
+
class Keyboard
|
3
|
+
extend Configurable
|
4
|
+
|
5
|
+
callable :text
|
6
|
+
|
7
|
+
def initialize(request:, action:)
|
8
|
+
@request = request
|
9
|
+
@buttons = []
|
10
|
+
@action = action
|
11
|
+
after_initialize
|
12
|
+
end
|
13
|
+
|
14
|
+
def after_initialize; end
|
15
|
+
|
16
|
+
def button(**button)
|
17
|
+
@buttons.push([Button.new(**button, keyboard: self).to_telegram_format])
|
18
|
+
end
|
19
|
+
|
20
|
+
def row
|
21
|
+
row = Row.new(keyboard: self)
|
22
|
+
yield row
|
23
|
+
buttons.push(row.buttons)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_telegram_format
|
27
|
+
buttons
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :buttons, :request, :action
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TelegramOnSteroids::Params
|
4
|
+
def initialize(params)
|
5
|
+
@params = params
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](key)
|
9
|
+
@params[key]
|
10
|
+
end
|
11
|
+
|
12
|
+
def user
|
13
|
+
@user ||= message&.dig("from") ||
|
14
|
+
@params.dig("callback_query", "from") ||
|
15
|
+
@params.dig("pre_checkout_query", "from") ||
|
16
|
+
@params.dig("shipping_query", "from") ||
|
17
|
+
@params.dig("inline_query", "from") ||
|
18
|
+
@params.dig("chosen_inline_result", "from") ||
|
19
|
+
@params.dig("poll_answer", "user") ||
|
20
|
+
@params.dig("chat_join_request", "from")
|
21
|
+
end
|
22
|
+
|
23
|
+
def language_code
|
24
|
+
user&.dig("language_code") || "en"
|
25
|
+
end
|
26
|
+
|
27
|
+
def user_id
|
28
|
+
user&.dig("id")
|
29
|
+
end
|
30
|
+
|
31
|
+
def username
|
32
|
+
user&.dig("username")
|
33
|
+
end
|
34
|
+
|
35
|
+
def chat_id
|
36
|
+
chat&.dig("id")
|
37
|
+
end
|
38
|
+
|
39
|
+
def chat
|
40
|
+
@params.dig("message", "chat") ||
|
41
|
+
@params.dig("callback_query", "message", "chat") ||
|
42
|
+
@params.dig("edited_message", "chat") ||
|
43
|
+
@params.dig("channel_post", "chat") ||
|
44
|
+
@params.dig("edited_channel_post", "chat")
|
45
|
+
end
|
46
|
+
|
47
|
+
def message
|
48
|
+
@params["message"] ||
|
49
|
+
@params["edited_message"] ||
|
50
|
+
@params["channel_post"] ||
|
51
|
+
@params["edited_channel_post"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def type
|
55
|
+
@type ||= (TelegramOnSteroids::UPDATE_TYPES & @params.keys).first
|
56
|
+
end
|
57
|
+
|
58
|
+
def message_text
|
59
|
+
message&.dig("text")
|
60
|
+
end
|
61
|
+
|
62
|
+
def callback_data
|
63
|
+
@params.dig("callback_query", "data")
|
64
|
+
end
|
65
|
+
|
66
|
+
def inline_data
|
67
|
+
@params.dig("inline_query", "query")
|
68
|
+
end
|
69
|
+
|
70
|
+
def callback?
|
71
|
+
!callback_data.nil?
|
72
|
+
end
|
73
|
+
|
74
|
+
def start?
|
75
|
+
!!message_text&.start_with?("/start")
|
76
|
+
end
|
77
|
+
|
78
|
+
def command?
|
79
|
+
!!message_text&.start_with?("/")
|
80
|
+
end
|
81
|
+
|
82
|
+
def deep_link_payload
|
83
|
+
match = /\A\/(startgroup|start) (?<payload>.+)\z/.match(message_text)
|
84
|
+
match["payload"] if match
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_h
|
88
|
+
@params
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class TelegramOnSteroids::Process
|
2
|
+
attr_reader :params, :client, :session
|
3
|
+
|
4
|
+
def initialize(raw_params)
|
5
|
+
@params = TelegramOnSteroids::Params.new(raw_params)
|
6
|
+
@session = TelegramOnSteroids::Session.new(@params)
|
7
|
+
chat_id = @params.chat_id
|
8
|
+
@client = TelegramOnSteroids.config.client.new(chat_id)
|
9
|
+
@logger = TelegramOnSteroids.config.logger
|
10
|
+
|
11
|
+
if TelegramOnSteroids.config.commands.keys.include?(params.message_text)
|
12
|
+
set_current_action(TelegramOnSteroids.config.commands[params.message_text])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_request
|
17
|
+
@logger.info "[TelegramOnSteroids] Processing by #{current_action.class.name}##{current_step}"
|
18
|
+
@logger.info "[TelegramOnSteroids] Params: #{params.to_h}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def process
|
22
|
+
log_request
|
23
|
+
|
24
|
+
if current_action.respond_to?("__run_on_#{params.type}".to_sym)
|
25
|
+
current_action.send("__run_on_#{params.type}".to_sym)
|
26
|
+
else
|
27
|
+
current_action.__run_on_message if current_action.respond_to?(:__run_on_message)
|
28
|
+
end
|
29
|
+
|
30
|
+
session.write(:current_page, 1) unless params.callback?
|
31
|
+
|
32
|
+
if current_action.redirect_to
|
33
|
+
do_redirect
|
34
|
+
end
|
35
|
+
|
36
|
+
@session.dump
|
37
|
+
|
38
|
+
@client.inline_request
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def current_action
|
44
|
+
@current_action ||= begin
|
45
|
+
action_class = if action = session.read(:current_action)
|
46
|
+
Object.const_get(action)
|
47
|
+
else
|
48
|
+
TelegramOnSteroids.config.start_action
|
49
|
+
end
|
50
|
+
|
51
|
+
action_class.new(request: self, client: @client, session:)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_current_action(action_class)
|
56
|
+
session.write(:current_action, action_class.to_s)
|
57
|
+
set_current_step(nil)
|
58
|
+
session.reset_flash
|
59
|
+
|
60
|
+
@current_action = action_class.new(request: self, client: @client, session:)
|
61
|
+
end
|
62
|
+
|
63
|
+
def current_step
|
64
|
+
@session.read(:current_step) || :initial
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_current_step(step)
|
68
|
+
@session.write(:current_step, step)
|
69
|
+
end
|
70
|
+
|
71
|
+
def do_redirect
|
72
|
+
action_or_step = @redirect_to
|
73
|
+
session_params = @session_params
|
74
|
+
|
75
|
+
set_current_action(current_action.redirect_to)
|
76
|
+
current_action.__run_on_redirect
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class TelegramOnSteroids::Session
|
2
|
+
def initialize(params)
|
3
|
+
@session_id = [params.user_id, params.chat_id].join('.')
|
4
|
+
@store = TelegramOnSteroids.config.session_store
|
5
|
+
@session = if serialized_session = @store.read(@session_id)
|
6
|
+
Oj.load(serialized_session)
|
7
|
+
else
|
8
|
+
{}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def read(key)
|
13
|
+
@session[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(key, value)
|
17
|
+
@session[key] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete(key)
|
21
|
+
@session.delete(key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear
|
25
|
+
@session.clear
|
26
|
+
end
|
27
|
+
|
28
|
+
def dump
|
29
|
+
@session_id && @store.write(@session_id, Oj.dump(@session))
|
30
|
+
end
|
31
|
+
|
32
|
+
# this is a user space to store some session data separately from the gem session
|
33
|
+
def user_session
|
34
|
+
@session_id ?
|
35
|
+
@session[:user_session] ||= {} :
|
36
|
+
NullSession.new
|
37
|
+
end
|
38
|
+
|
39
|
+
# this is a temporary per-action store
|
40
|
+
def flash
|
41
|
+
@session_id ?
|
42
|
+
@session[:flash] ||= {} :
|
43
|
+
NullSession.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset_flash
|
47
|
+
flash.clear
|
48
|
+
end
|
49
|
+
|
50
|
+
class NullSession < Hash
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class TelegramOnSteroids::Updates
|
2
|
+
attr_writer :stop
|
3
|
+
|
4
|
+
def initialize(params)
|
5
|
+
@params = params
|
6
|
+
end
|
7
|
+
|
8
|
+
def enum
|
9
|
+
Enumerator.new do |y|
|
10
|
+
loop do
|
11
|
+
break if @stop
|
12
|
+
|
13
|
+
updates = TelegramOnSteroids::Client.new.get_updates(@params)["result"]
|
14
|
+
updates.each do |update|
|
15
|
+
y << update
|
16
|
+
end
|
17
|
+
|
18
|
+
if updates.any?
|
19
|
+
@params.merge! offset: updates.last["update_id"] + 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative './telegram_on_steroids/configurable'
|
2
|
+
require_relative './telegram_on_steroids/keyboard'
|
3
|
+
require_relative './telegram_on_steroids/keyboard/inline_keyboard'
|
4
|
+
require_relative './telegram_on_steroids/keyboard/paginatable'
|
5
|
+
require_relative './telegram_on_steroids/keyboard/button'
|
6
|
+
require_relative './telegram_on_steroids/keyboard/row'
|
7
|
+
require_relative './telegram_on_steroids/action'
|
8
|
+
require_relative './telegram_on_steroids/client'
|
9
|
+
require_relative './telegram_on_steroids/params'
|
10
|
+
require_relative './telegram_on_steroids/updates'
|
11
|
+
require_relative './telegram_on_steroids/configuration'
|
12
|
+
require_relative './telegram_on_steroids/process'
|
13
|
+
require_relative './telegram_on_steroids/session'
|
14
|
+
require_relative './telegram_on_steroids/version'
|
15
|
+
require_relative './telegram_on_steroids/in_memory_store'
|
16
|
+
|
17
|
+
module TelegramOnSteroids
|
18
|
+
UPDATE_TYPES = %w[message edited_message channel_post edited_channel_post message_reaction message_reaction_count inline_query chosen_inline_request callback_query shipping_query poll poll_answer my_chat_member chat_member chat_join_request chat_boost removed_chat_boost]
|
19
|
+
|
20
|
+
def self.process(params)
|
21
|
+
Process.new(params).process
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.updates(offset: nil, limit: nil, timeout: 60, allowed_updates: nil)
|
25
|
+
params = {}
|
26
|
+
params[:offset] = offset if offset
|
27
|
+
params[:limit] = limit if limit
|
28
|
+
params[:timeout] = timeout if timeout
|
29
|
+
params[:allowed_updates] = allowed_updates if allowed_updates
|
30
|
+
|
31
|
+
(@updates = Updates.new(params)).enum
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.stop_updates
|
35
|
+
@updates && @updates.stop = true
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'lib/telegram_on_steroids/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "telegram_on_steroids"
|
5
|
+
spec.version = TelegramOnSteroids::VERSION
|
6
|
+
spec.authors = ["Aliaksandr Hrakovich"]
|
7
|
+
|
8
|
+
spec.summary = %q{A simple library to create Telegram Bots in Ruby.}
|
9
|
+
spec.homepage = "https://github.com/slaurmagan/telegram_on_steroids"
|
10
|
+
spec.license = "MIT"
|
11
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
|
12
|
+
|
13
|
+
spec.add_development_dependency "activesupport", "~> 5.2.0"
|
14
|
+
spec.add_dependency "http"
|
15
|
+
spec.add_dependency "retryable"
|
16
|
+
spec.add_dependency "oj"
|
17
|
+
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|example)/}) }
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.license = "MIT"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: telegram_on_steroids
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aliaksandr Hrakovich
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: http
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: retryable
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: oj
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- ".document"
|
76
|
+
- ".gitignore"
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.rdoc
|
80
|
+
- Rakefile
|
81
|
+
- examples/example_action.rb
|
82
|
+
- lib/telegram_on_steroids.rb
|
83
|
+
- lib/telegram_on_steroids/action.rb
|
84
|
+
- lib/telegram_on_steroids/client.rb
|
85
|
+
- lib/telegram_on_steroids/configurable.rb
|
86
|
+
- lib/telegram_on_steroids/configuration.rb
|
87
|
+
- lib/telegram_on_steroids/in_memory_store.rb
|
88
|
+
- lib/telegram_on_steroids/keyboard.rb
|
89
|
+
- lib/telegram_on_steroids/keyboard/button.rb
|
90
|
+
- lib/telegram_on_steroids/keyboard/inline_keyboard.rb
|
91
|
+
- lib/telegram_on_steroids/keyboard/paginatable.rb
|
92
|
+
- lib/telegram_on_steroids/keyboard/row.rb
|
93
|
+
- lib/telegram_on_steroids/params.rb
|
94
|
+
- lib/telegram_on_steroids/process.rb
|
95
|
+
- lib/telegram_on_steroids/session.rb
|
96
|
+
- lib/telegram_on_steroids/updates.rb
|
97
|
+
- lib/telegram_on_steroids/version.rb
|
98
|
+
- telegram_on_steroids.gemspec
|
99
|
+
homepage: https://github.com/slaurmagan/telegram_on_steroids
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
metadata:
|
103
|
+
homepage_uri: https://github.com/slaurmagan/telegram_on_steroids
|
104
|
+
source_code_uri: https://github.com/slaurmagan/telegram_on_steroids
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 2.4.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.3.3
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: A simple library to create Telegram Bots in Ruby.
|
124
|
+
test_files: []
|