rbender 0.3.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/bin/rbender +15 -0
- data/lib/rbender/base.rb +169 -0
- data/lib/rbender/keyboard.rb +143 -0
- data/lib/rbender/keyboard_inline.rb +72 -0
- data/lib/rbender/mongo_client.rb +25 -0
- data/lib/rbender/sessionmanager.rb +52 -0
- data/lib/rbender/state.rb +288 -0
- data/lib/rbender.rb +39 -0
- metadata +52 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: b890be6a91b37ef81fbf86ff6dd52a3ba3d1edbb
|
|
4
|
+
data.tar.gz: cdf21a6de8c3cf294d185b8a5afeb7136354d530
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8f4323aac3697a92d4946b40cabdbe34a307c8cf5701c0bffa98da76b771a1c974dc35548a9a4ba4a1b6de31c06e58840a8e01463f1805f7ac9c85a588c92387
|
|
7
|
+
data.tar.gz: 33391ad4a580ff4b19a8d0b744f7084c60b6a7a56ba30b834814d299fc50f40f89ac2bed5fdbe55d2bfd97e3c619ccc0a5be0de8534eb123daef23adc091a5c8
|
data/bin/rbender
ADDED
data/lib/rbender/base.rb
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# require_relative '../rbender'
|
|
2
|
+
# require_relative 'sessionmanager'
|
|
3
|
+
|
|
4
|
+
require 'rbender/sessionmanager'
|
|
5
|
+
require 'rbender/keyboard_inline'
|
|
6
|
+
require 'rbender/mongo_client'
|
|
7
|
+
require 'rbender/state'
|
|
8
|
+
|
|
9
|
+
require 'telegram/bot'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RBender::Base
|
|
13
|
+
attr_accessor :api
|
|
14
|
+
include RBender::SessionManager
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@states = {}
|
|
18
|
+
@global_state = nil
|
|
19
|
+
@modules_block
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def modules(&modules_block)
|
|
23
|
+
@modules_block = modules_block
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Runs the bot with server
|
|
27
|
+
public
|
|
28
|
+
def run!
|
|
29
|
+
puts "Bot is loading...".green
|
|
30
|
+
puts "Bot is running...".green
|
|
31
|
+
Telegram::Bot::Client.run(@token) do |bot|
|
|
32
|
+
@api = bot.api
|
|
33
|
+
|
|
34
|
+
unless @modules_block.nil?
|
|
35
|
+
instance_exec(@api, @mongo_client, &@modules_block)
|
|
36
|
+
end
|
|
37
|
+
bot.listen do |message| # When message has gotten
|
|
38
|
+
# begin
|
|
39
|
+
_process_message(message)
|
|
40
|
+
# rescue => ex
|
|
41
|
+
# puts ex.message
|
|
42
|
+
# end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
public
|
|
48
|
+
def _process_message(message)
|
|
49
|
+
chat_id = message.from.id # Unique chat ID
|
|
50
|
+
|
|
51
|
+
if has_session?(chat_id)
|
|
52
|
+
session = load_session(chat_id)
|
|
53
|
+
|
|
54
|
+
else # If user is new
|
|
55
|
+
session = {_state: :start,
|
|
56
|
+
_state_stack: [],
|
|
57
|
+
_keyboard_switchers: {},
|
|
58
|
+
_keyboard_switch_groups: {},
|
|
59
|
+
_lang: :default
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
session[:user] = {chat_id: chat_id,
|
|
64
|
+
first_name: message.from.first_name,
|
|
65
|
+
last_name: message.from.last_name,
|
|
66
|
+
username: message.from.username
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
session[:user].freeze # User's data must be immutable!
|
|
70
|
+
|
|
71
|
+
if is_start_message? message
|
|
72
|
+
session[:_state] = :start
|
|
73
|
+
session[:_state_stack] = []
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
state = session[:_state] # User's state
|
|
77
|
+
state_block = @states[state] # Block of the state
|
|
78
|
+
state_object = RBender::State.new message, # Object to invoke
|
|
79
|
+
@api,
|
|
80
|
+
session,
|
|
81
|
+
&state_block
|
|
82
|
+
state_object.instance_eval(&@global_state) unless @global_state.nil?
|
|
83
|
+
state_object._build
|
|
84
|
+
state_object._invoke
|
|
85
|
+
save_session session
|
|
86
|
+
|
|
87
|
+
state_new = session[:_state] # New user's state
|
|
88
|
+
|
|
89
|
+
unless state_new == state # If state has changed
|
|
90
|
+
state_object._invoke_after if state_object.has_after? #invoke after hook for an old state
|
|
91
|
+
|
|
92
|
+
state_new_block = @states[state_new]
|
|
93
|
+
state_object = RBender::State.new message,
|
|
94
|
+
@api,
|
|
95
|
+
session,
|
|
96
|
+
&state_new_block
|
|
97
|
+
|
|
98
|
+
state_object.instance_eval(&@global_state) unless @global_state.nil?
|
|
99
|
+
state_object._build
|
|
100
|
+
|
|
101
|
+
if state_object.has_keyboard? # Invoke a keyboard if it's exists
|
|
102
|
+
state_object._build_keyboard
|
|
103
|
+
state_object._invoke_keyboard
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
state_object._invoke_before if state_object.has_before? #invoke before hook for a new state
|
|
107
|
+
else
|
|
108
|
+
if state_object.has_keyboard? # Invoke a keyboard if it's exists
|
|
109
|
+
unless state_object._keyboard.one_time? or not state_object._keyboard.force_invoked?
|
|
110
|
+
state_object._build_keyboard
|
|
111
|
+
state_object._invoke_keyboard
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def is_start_message?(message)
|
|
118
|
+
if message.instance_of? Telegram::Bot::Types::Message
|
|
119
|
+
if message.text == '/start'
|
|
120
|
+
true
|
|
121
|
+
else
|
|
122
|
+
false
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @param [Hash] params - hash with params
|
|
128
|
+
#
|
|
129
|
+
# Available parameters:
|
|
130
|
+
# * mongo_server_ip (required)
|
|
131
|
+
# * mongo server_port (required)
|
|
132
|
+
# * bot_name (required)
|
|
133
|
+
# * token (required)
|
|
134
|
+
# * localizations(optional) default = :default
|
|
135
|
+
def set_params(params)
|
|
136
|
+
RBender::MongoClient.setup(params[:bot_name],
|
|
137
|
+
params[:mongo_server_ip],
|
|
138
|
+
params[:mongo_server_port])
|
|
139
|
+
|
|
140
|
+
session_setup(RBender::MongoClient.client)
|
|
141
|
+
|
|
142
|
+
@token = params[:token]
|
|
143
|
+
# if params.has_key? localizations
|
|
144
|
+
#
|
|
145
|
+
# end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# Adds new state to the bot.
|
|
150
|
+
# @param [String] state_name - name of the state
|
|
151
|
+
# @param [Block] block - actions while state has invoked
|
|
152
|
+
#
|
|
153
|
+
public
|
|
154
|
+
def state(state_name, &block)
|
|
155
|
+
unless @states.has_key? state_name
|
|
156
|
+
@states[state_name] = block
|
|
157
|
+
else
|
|
158
|
+
raise "State name is duplicated!"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def global(&block)
|
|
164
|
+
@global_state = block
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
class RBender::Keyboard
|
|
2
|
+
|
|
3
|
+
attr_accessor :markup,
|
|
4
|
+
:localizations,
|
|
5
|
+
:actions,
|
|
6
|
+
:response,
|
|
7
|
+
:switchers,
|
|
8
|
+
:markup_tg,
|
|
9
|
+
:markup_final,
|
|
10
|
+
:switch_groups,
|
|
11
|
+
:session
|
|
12
|
+
|
|
13
|
+
def initialize(response)
|
|
14
|
+
@response = response
|
|
15
|
+
@actions = {}
|
|
16
|
+
@markup = []
|
|
17
|
+
@localizations = {}
|
|
18
|
+
@switchers = {}
|
|
19
|
+
@markup_tg = nil
|
|
20
|
+
@markup_final = {}
|
|
21
|
+
@switch_groups = {}
|
|
22
|
+
@button_switch_group = {}
|
|
23
|
+
@one_time = false
|
|
24
|
+
@session = nil
|
|
25
|
+
@hide_on_action = false
|
|
26
|
+
@resize = false
|
|
27
|
+
@force_invoke = false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def hide_on_action
|
|
31
|
+
@hide_on_action = true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def force_invoked?
|
|
35
|
+
@force_invoke
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Adds button to the keyboard
|
|
39
|
+
#
|
|
40
|
+
# Example:
|
|
41
|
+
# button :btn_test, "Test button" do
|
|
42
|
+
# some_actions
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# button :btn_with_loc, {ru: "Кнопка", en: "Button"} do
|
|
46
|
+
# some_actions
|
|
47
|
+
# end
|
|
48
|
+
|
|
49
|
+
def button(id, localizations, *icons, &action)
|
|
50
|
+
@actions[id] = action
|
|
51
|
+
@localizations[id] = localizations
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Checks keyboard one time or not
|
|
55
|
+
def one_time?
|
|
56
|
+
@one_time
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# makes a keyboard one time
|
|
60
|
+
def one_time
|
|
61
|
+
@one_time = true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Add a line to markup
|
|
65
|
+
def add_line (*buttons)
|
|
66
|
+
@markup += [buttons]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Set response when keyboard is invoked
|
|
70
|
+
def set_response(new_response)
|
|
71
|
+
@response = new_response
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Adds switcher to buttons
|
|
75
|
+
def switcher(id, *switches)
|
|
76
|
+
@switchers[id] = switches
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Adds switch group
|
|
80
|
+
def switch_group(group_name, *buttons)
|
|
81
|
+
@switch_groups[group_name] = buttons
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Resize telegram buttons
|
|
85
|
+
def resize
|
|
86
|
+
@resize = true
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Adds button switch group
|
|
90
|
+
def button_switch_group(button_id)
|
|
91
|
+
@switch_groups.each do |group, buttons|
|
|
92
|
+
if buttons.include? button_id
|
|
93
|
+
return group
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Method to initialize a keyboard
|
|
100
|
+
#
|
|
101
|
+
def _build(session)
|
|
102
|
+
#TODO: add localization support
|
|
103
|
+
markup = []
|
|
104
|
+
@markup.each do |line|
|
|
105
|
+
buf_line = []
|
|
106
|
+
line.each do |button_id|
|
|
107
|
+
btn_id = button_id # used for replacing group mask
|
|
108
|
+
if @switch_groups.member? button_id # if it's switch group mask
|
|
109
|
+
button_num = 0
|
|
110
|
+
if session[:_keyboard_switch_groups].member? btn_id
|
|
111
|
+
button_num = session[:_keyboard_switch_groups][btn_id]
|
|
112
|
+
else
|
|
113
|
+
session[:_keyboard_switch_groups][btn_id] = 0
|
|
114
|
+
end
|
|
115
|
+
button = @localizations[@switch_groups[button_id][button_num]].dup
|
|
116
|
+
btn_id = @switch_groups[btn_id][button_num]
|
|
117
|
+
else # if it's normal button
|
|
118
|
+
button = @localizations[btn_id].dup
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
if @switchers.member? btn_id # Code for switcher
|
|
122
|
+
switch_num = 0
|
|
123
|
+
if session[:_keyboard_switchers].member? btn_id # If switcher inside session
|
|
124
|
+
switch_num = session[:_keyboard_switchers][btn_id]
|
|
125
|
+
else
|
|
126
|
+
session[:_keyboard_switchers][btn_id] = 0 # Switcher init
|
|
127
|
+
end
|
|
128
|
+
switcher = @switchers[btn_id][switch_num]
|
|
129
|
+
button << " " << switcher unless switcher.empty?
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
buf_line << button
|
|
133
|
+
@markup_final[btn_id] = button
|
|
134
|
+
end
|
|
135
|
+
markup << buf_line
|
|
136
|
+
end
|
|
137
|
+
# puts @keyboard.markup_final
|
|
138
|
+
@markup_tg = Telegram::Bot::Types::ReplyKeyboardMarkup
|
|
139
|
+
.new(keyboard: markup,
|
|
140
|
+
one_time_keyboard: one_time?,
|
|
141
|
+
resize_keyboard: @resize)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
CALLBACK_SPLITTER = '|'
|
|
2
|
+
|
|
3
|
+
class RBender::KeyboardInline
|
|
4
|
+
attr_accessor :markup_tg,
|
|
5
|
+
:buttons_actions
|
|
6
|
+
|
|
7
|
+
def initialize(name, session, block)
|
|
8
|
+
@name = name
|
|
9
|
+
@buttons_callback = {}
|
|
10
|
+
@buttons_inline = {}
|
|
11
|
+
@buttons_links = {}
|
|
12
|
+
@buttons_actions = {}
|
|
13
|
+
@markup = []
|
|
14
|
+
@markup_tg = []
|
|
15
|
+
@session = session
|
|
16
|
+
@keyboard_block = block
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def session
|
|
20
|
+
@session
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Adds button with callback
|
|
24
|
+
def button(name, description, &action)
|
|
25
|
+
callback = @name.to_s + RBender::CALLBACK_SPLITTER + name.to_s
|
|
26
|
+
@buttons_callback[name] = Telegram::Bot::Types::InlineKeyboardButton.new(text: description,
|
|
27
|
+
callback_data: callback)
|
|
28
|
+
@buttons_actions[name.to_s] = action
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Adds link button
|
|
32
|
+
def button_link(name, description, link)
|
|
33
|
+
@buttons_links[name] = Telegram::Bot::Types::InlineKeyboardButton.new(text: description,
|
|
34
|
+
url: link)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Adds inline switch mode button
|
|
38
|
+
def button_inline(name, description, inline_query)
|
|
39
|
+
@buttons_inline[name] = Telegram::Bot::Types::InlineKeyboardButton.new(text: description,
|
|
40
|
+
switch_inline_query: inline_query)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Adds a line to markup
|
|
44
|
+
def add_line(*buttons)
|
|
45
|
+
@markup += [buttons]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def _invoke
|
|
49
|
+
instance_eval(&@keyboard_block)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def _build
|
|
53
|
+
@markup = []
|
|
54
|
+
_invoke
|
|
55
|
+
|
|
56
|
+
buttons = {}
|
|
57
|
+
buttons.merge! @buttons_callback
|
|
58
|
+
buttons.merge! @buttons_links
|
|
59
|
+
buttons.merge! @buttons_inline
|
|
60
|
+
|
|
61
|
+
markup = []
|
|
62
|
+
@markup.each do |btn_row|
|
|
63
|
+
line = []
|
|
64
|
+
btn_row.each do |btn|
|
|
65
|
+
raise "The #{buttons[btn]} doesn't exists" if btn.nil?
|
|
66
|
+
line << buttons[btn]
|
|
67
|
+
end
|
|
68
|
+
markup << line
|
|
69
|
+
end
|
|
70
|
+
@markup_tg = Telegram::Bot::Types::InlineKeyboardMarkup.new(inline_keyboard: markup)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'mongo'
|
|
2
|
+
require 'singleton'
|
|
3
|
+
|
|
4
|
+
class RBender::MongoClient
|
|
5
|
+
include Singleton
|
|
6
|
+
attr_reader :mongo_client
|
|
7
|
+
|
|
8
|
+
public
|
|
9
|
+
def self.client
|
|
10
|
+
instance.mongo_client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.setup(bot_name, mongo_server_ip, mongo_server_port)
|
|
14
|
+
instance.setup(bot_name, mongo_server_ip, mongo_server_port)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def setup(bot_name, mongo_server_ip, mongo_server_port)
|
|
18
|
+
@bot_name = bot_name
|
|
19
|
+
@mongo_server_ip = mongo_server_ip
|
|
20
|
+
@mongo_server_port = mongo_server_port
|
|
21
|
+
@mongo_client = Mongo::Client.new(["#{mongo_server_ip}:#{mongo_server_port}"], :database => bot_name)
|
|
22
|
+
|
|
23
|
+
Mongo::Logger.logger.level = ::Logger::FATAL
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module RBender
|
|
2
|
+
module SessionManager
|
|
3
|
+
|
|
4
|
+
private
|
|
5
|
+
def session_setup(mongo_client)
|
|
6
|
+
@mongo_client = mongo_client
|
|
7
|
+
@sessions_mongo = @mongo_client[:sessions]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def load_session(chat_id)
|
|
11
|
+
result = nil
|
|
12
|
+
@sessions_mongo.find(chat_id: chat_id).each do |document|
|
|
13
|
+
result = document[:session]
|
|
14
|
+
end
|
|
15
|
+
result
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def save_session(session)
|
|
19
|
+
@sessions_mongo.update_one({chat_id: session[:user][:chat_id]},
|
|
20
|
+
{'$set' => {session: session}},
|
|
21
|
+
{upsert: true})
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def has_session?(chat_id)
|
|
25
|
+
count = @sessions_mongo.find(chat_id: chat_id).count
|
|
26
|
+
count > 0 ? true : false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
public
|
|
30
|
+
def self.chatid_list
|
|
31
|
+
sessions = RBender::MongoClient.client[:sessions]
|
|
32
|
+
result = sessions.distinct('chat_id')
|
|
33
|
+
chatid_list = []
|
|
34
|
+
result.each do |doc|
|
|
35
|
+
chatid_list << doc
|
|
36
|
+
end
|
|
37
|
+
chatid_list
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
# Mongo scheme:
|
|
44
|
+
# Session
|
|
45
|
+
# {chat_id: {session_key: session_value}}
|
|
46
|
+
#
|
|
47
|
+
# States
|
|
48
|
+
# {chat_id: state}
|
|
49
|
+
#
|
|
50
|
+
#
|
|
51
|
+
#
|
|
52
|
+
#
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
require_relative '../r_bender'
|
|
2
|
+
|
|
3
|
+
class RBender::State
|
|
4
|
+
def initialize(message, api, session, &state_block)
|
|
5
|
+
@message = message
|
|
6
|
+
@api = api
|
|
7
|
+
@session = session
|
|
8
|
+
@state_block = state_block
|
|
9
|
+
@keyboard = nil
|
|
10
|
+
@inline_keyboards = {}
|
|
11
|
+
@action_after = nil
|
|
12
|
+
@action_before = nil
|
|
13
|
+
@text_action = nil
|
|
14
|
+
@helpers_block = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def _keyboard
|
|
18
|
+
@keyboard
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Invokes states and processes user's input
|
|
22
|
+
def _invoke
|
|
23
|
+
case message
|
|
24
|
+
when Telegram::Bot::Types::CallbackQuery
|
|
25
|
+
_process_callback
|
|
26
|
+
when Telegram::Bot::Types::Message
|
|
27
|
+
_process_text_message
|
|
28
|
+
else
|
|
29
|
+
puts "This type isn't available: #{message.class}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Process if message is just text
|
|
34
|
+
def _process_text_message
|
|
35
|
+
|
|
36
|
+
unless @keyboard.nil? # if state has keyboard
|
|
37
|
+
@keyboard.instance_eval(&@helpers_block) unless @helpers_block.nil?
|
|
38
|
+
_build_keyboard
|
|
39
|
+
|
|
40
|
+
@keyboard.markup_final.each do |btn, final_btn|
|
|
41
|
+
if message.text == final_btn
|
|
42
|
+
instance_exec(&@keyboard.actions[btn])
|
|
43
|
+
|
|
44
|
+
unless (group_id = @keyboard.button_switch_group(btn)).nil? # Switch group's logic
|
|
45
|
+
group_size = @keyboard.switch_groups[group_id].size
|
|
46
|
+
@session[:_keyboard_switch_groups][group_id] += 1
|
|
47
|
+
@session[:_keyboard_switch_groups][group_id] %= group_size
|
|
48
|
+
end
|
|
49
|
+
if @keyboard.switchers.member? btn # Switcher's Logic
|
|
50
|
+
switcher_size = @keyboard.switchers[btn].size
|
|
51
|
+
@session[:_keyboard_switchers][btn] += 1
|
|
52
|
+
@session[:_keyboard_switchers][btn] %= switcher_size
|
|
53
|
+
end
|
|
54
|
+
return
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
unless @text_action.nil?
|
|
60
|
+
|
|
61
|
+
instance_exec(@message.text, &@text_action)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Process if message is inline keyboard callback
|
|
67
|
+
def _process_callback
|
|
68
|
+
keyboard_name, action = @message.data.split(RBender::CALLBACK_SPLITTER)
|
|
69
|
+
keyboard = @inline_keyboards[keyboard_name.to_sym]
|
|
70
|
+
keyboard.instance_eval(&@helpers_block) unless @helpers_block.nil?
|
|
71
|
+
keyboard._invoke unless keyboard.nil?
|
|
72
|
+
|
|
73
|
+
unless keyboard.nil?
|
|
74
|
+
unless keyboard.buttons_actions[action].nil?
|
|
75
|
+
instance_eval(&keyboard.buttons_actions[action])
|
|
76
|
+
else
|
|
77
|
+
raise "There is no action called '#{action}'"
|
|
78
|
+
end
|
|
79
|
+
else
|
|
80
|
+
edit_message_text text: "deleted"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def _build
|
|
85
|
+
instance_exec(&@state_block)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def _build_keyboard
|
|
89
|
+
@keyboard._build(@session)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def _invoke_keyboard
|
|
93
|
+
|
|
94
|
+
@api.send_message(chat_id: message.from.id,
|
|
95
|
+
text: @keyboard.response,
|
|
96
|
+
reply_markup: @keyboard.markup_tg)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def _invoke_before
|
|
100
|
+
instance_eval(&@action_before)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def has_after?
|
|
104
|
+
@action_after.nil? ? false : true
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def has_before?
|
|
108
|
+
@action_before.nil? ? false : true
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def _invoke_after
|
|
112
|
+
instance_eval(&@action_after)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def has_keyboard?
|
|
116
|
+
@keyboard.nil? ? false : true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
#--------------
|
|
120
|
+
# User methods
|
|
121
|
+
#--------------
|
|
122
|
+
|
|
123
|
+
def keyboard(response_message, &keyboard_block)
|
|
124
|
+
@keyboard = RBender::Keyboard.new response_message
|
|
125
|
+
@keyboard.session = @session
|
|
126
|
+
@keyboard.instance_eval(&keyboard_block)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
#initialize helper methods
|
|
130
|
+
def helpers(&helpers_block)
|
|
131
|
+
@helpers_block = helpers_block
|
|
132
|
+
instance_eval(&helpers_block)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Set message user gets while keyboard has invoked
|
|
136
|
+
def set_response(new_response)
|
|
137
|
+
@keyboard.set_response(new_response)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Returns session hash
|
|
141
|
+
def session
|
|
142
|
+
@session
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Returns message object
|
|
146
|
+
def message
|
|
147
|
+
@message
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# adds inline keyboard
|
|
151
|
+
def keyboard_inline(inline_keyboard_name, &inline_keyboard_block)
|
|
152
|
+
keyboard = @inline_keyboards[inline_keyboard_name] = RBender::KeyboardInline.new(inline_keyboard_name,
|
|
153
|
+
@session,
|
|
154
|
+
inline_keyboard_block)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def inline_markup(name)
|
|
158
|
+
|
|
159
|
+
raise "Keyboard #{name} doesn't exists!" unless @inline_keyboards.member? name
|
|
160
|
+
keyboard = @inline_keyboards[name]
|
|
161
|
+
keyboard.instance_eval(&@helpers_block) unless @helpers_block.nil?
|
|
162
|
+
keyboard._build
|
|
163
|
+
keyboard.markup_tg
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def switch(state_to)
|
|
167
|
+
@session[:_state_stack].push(@session[:_state])
|
|
168
|
+
@session[:_state] = state_to
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def switch_prev
|
|
172
|
+
@session[:_state] = @session[:_state_stack].pop
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def switcher_state(id)
|
|
176
|
+
session[:_keyboard_switchers][id]
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
#before hook
|
|
180
|
+
def before(&action)
|
|
181
|
+
if @action_before.nil?
|
|
182
|
+
@action_before = action
|
|
183
|
+
else
|
|
184
|
+
raise 'Too many before hooks!'
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
#after hook
|
|
189
|
+
def after(&action)
|
|
190
|
+
if @action_after.nil?
|
|
191
|
+
@action_after = action
|
|
192
|
+
else
|
|
193
|
+
raise 'Too many after hooks!'
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Text callbacks
|
|
198
|
+
def text(&action)
|
|
199
|
+
if @text_action.nil?
|
|
200
|
+
@text_action = action
|
|
201
|
+
else
|
|
202
|
+
raise 'Too many text processors!'
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
#--------------
|
|
208
|
+
# API METHODS
|
|
209
|
+
#--------------
|
|
210
|
+
# Hides inline keyboard
|
|
211
|
+
# Must be called from any inline keyboard state
|
|
212
|
+
def hide_inline
|
|
213
|
+
edit_message_reply_markup
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Hides keyboard's markup.
|
|
217
|
+
def hide_keyboard
|
|
218
|
+
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
#
|
|
222
|
+
# @param text [String] string
|
|
223
|
+
#
|
|
224
|
+
def answer_callback_query(text: nil,
|
|
225
|
+
show_alert: nil)
|
|
226
|
+
begin
|
|
227
|
+
@api.answer_callback_query callback_query_id: @message.id,
|
|
228
|
+
text: text,
|
|
229
|
+
show_alert: show_alert
|
|
230
|
+
rescue
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def send_message(text:,
|
|
235
|
+
chat_id: @message.from.id,
|
|
236
|
+
parse_mode: nil,
|
|
237
|
+
disable_web_page_preview: nil,
|
|
238
|
+
disable_notification: nil,
|
|
239
|
+
reply_to_message_id: nil,
|
|
240
|
+
reply_markup: nil)
|
|
241
|
+
|
|
242
|
+
if text.strip.empty?
|
|
243
|
+
raise "A text can't be empty or consists of space symbols only"
|
|
244
|
+
end
|
|
245
|
+
@api.send_message chat_id: chat_id,
|
|
246
|
+
text: text,
|
|
247
|
+
disable_web_page_preview: disable_web_page_preview,
|
|
248
|
+
disable_notification: disable_notification,
|
|
249
|
+
reply_to_message_id: reply_to_message_id,
|
|
250
|
+
parse_mode: parse_mode,
|
|
251
|
+
reply_markup: reply_markup
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def edit_message_text(inline_message_id: nil,
|
|
256
|
+
text:,
|
|
257
|
+
message_id: @message.message.message_id,
|
|
258
|
+
parse_mode: nil,
|
|
259
|
+
disable_web_page_preview: nil,
|
|
260
|
+
reply_markup: nil)
|
|
261
|
+
begin
|
|
262
|
+
@api.edit_message_text chat_id: @message.from.id,
|
|
263
|
+
message_id: message_id,
|
|
264
|
+
text: text,
|
|
265
|
+
inline_message_id: inline_message_id,
|
|
266
|
+
parse_mode: parse_mode,
|
|
267
|
+
disable_web_page_preview: disable_web_page_preview,
|
|
268
|
+
reply_markup: reply_markup
|
|
269
|
+
rescue
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def edit_message_reply_markup(chat_id: @message.from.id,
|
|
274
|
+
message_id: @message.message.message_id,
|
|
275
|
+
inline_message_id: nil,
|
|
276
|
+
reply_markup: nil)
|
|
277
|
+
begin
|
|
278
|
+
@api.edit_message_reply_markup chat_id: chat_id,
|
|
279
|
+
message_id: message_id,
|
|
280
|
+
inline_message_id: inline_message_id,
|
|
281
|
+
reply_markup: reply_markup
|
|
282
|
+
rescue
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
data/lib/rbender.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'rbender/base'
|
|
2
|
+
require 'colorize'
|
|
3
|
+
|
|
4
|
+
module RBender
|
|
5
|
+
VERSION = '0.2.30'
|
|
6
|
+
@@bot = RBender::Base.new
|
|
7
|
+
|
|
8
|
+
def self.instance
|
|
9
|
+
@@bot
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.reload
|
|
13
|
+
@@bot = RBender::Base.new
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def method_missing(m, *args, &block)
|
|
19
|
+
if RBender::Base.method_defined? m
|
|
20
|
+
if block_given?
|
|
21
|
+
if args.empty?
|
|
22
|
+
RBender.instance.send(m, &block)
|
|
23
|
+
else
|
|
24
|
+
args = args[0] if args.count == 1
|
|
25
|
+
RBender.instance.send(m, args, &block)
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
if args.empty?
|
|
29
|
+
RBender.instance.send(m)
|
|
30
|
+
else
|
|
31
|
+
args = args[0] if args.count == 1
|
|
32
|
+
RBender.instance.send(m, args)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
raise NoMethodError, 'Method is missing'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rbender
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Arthur
|
|
8
|
+
- Korochansky
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2017-05-18 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: Gem provides domain specific language for messengers bot creation.
|
|
15
|
+
email: art2rik.desperado@gmail.com
|
|
16
|
+
executables:
|
|
17
|
+
- rbender
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- bin/rbender
|
|
22
|
+
- lib/rbender.rb
|
|
23
|
+
- lib/rbender/base.rb
|
|
24
|
+
- lib/rbender/keyboard.rb
|
|
25
|
+
- lib/rbender/keyboard_inline.rb
|
|
26
|
+
- lib/rbender/mongo_client.rb
|
|
27
|
+
- lib/rbender/sessionmanager.rb
|
|
28
|
+
- lib/rbender/state.rb
|
|
29
|
+
homepage: https://github.com/art2rik/RBender
|
|
30
|
+
licenses: []
|
|
31
|
+
metadata: {}
|
|
32
|
+
post_install_message:
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements: []
|
|
47
|
+
rubyforge_project:
|
|
48
|
+
rubygems_version: 2.6.10
|
|
49
|
+
signing_key:
|
|
50
|
+
specification_version: 4
|
|
51
|
+
summary: BenderBot is a tool for fast develop and test bots for messengers.
|
|
52
|
+
test_files: []
|