telegram_on_steroids 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ # rcov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # yard generated
9
+ doc
10
+ .yardoc
11
+
12
+ # bundler
13
+ .bundle
14
+
15
+ # juwelier generated
16
+ pkg
17
+
18
+ .idea
19
+
20
+ *.gem
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,13 @@
1
+ class TelegramOnSteroids::InMemoryStore
2
+ def initialize
3
+ @store = {}
4
+ end
5
+
6
+ def read(key)
7
+ @store[key]
8
+ end
9
+
10
+ def write(key, value)
11
+ @store[key] = value
12
+ end
13
+ 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,3 @@
1
+ module TelegramOnSteroids
2
+ VERSION = '0.1.0'
3
+ 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: []