rbender 0.3.0 → 0.4.92
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 +4 -4
- data/bin/rbender +117 -8
- data/lib/rbender.rb +2 -2
- data/lib/rbender/base.rb +178 -154
- data/lib/rbender/config_handler.rb +35 -0
- data/lib/rbender/keyboard.rb +112 -140
- data/lib/rbender/keyboard_inline.rb +3 -3
- data/lib/rbender/methods.rb +129 -0
- data/lib/rbender/mongo_client.rb +4 -6
- data/lib/rbender/state.rb +202 -285
- data/templates/Gemfile +3 -0
- data/templates/config.yaml +9 -0
- data/templates/locales/en.yaml +3 -0
- data/templates/sample.rb +61 -0
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 08ad5e18cf3f89a0c2e77f4bb029986396cda53c
|
|
4
|
+
data.tar.gz: 02c2241374a6e8c45a4df2f78c288814bfaee467
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d310e3e1f769f80c6b6ccea0d4c3a20341e18580938296c10acad617d3a5c3f0eb784f19adddff5520543177f736299d8eb49a3f4bd71db1f70f3b546aa67821
|
|
7
|
+
data.tar.gz: d1d18a8e0981e7bca3a47ba227c83db61ed52a148a9d4b5f7b73b0868883ca8ea70eb7d2b2b6957da2593251cb80072a663b38fce9eb62862ccb2c44f3b2df45
|
data/bin/rbender
CHANGED
|
@@ -1,15 +1,124 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
|
|
2
|
+
require 'rbender'
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
require 'optparse'
|
|
5
|
+
require 'fileutils'
|
|
3
6
|
|
|
7
|
+
class OptParser
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
MODE_DEV = "dev"
|
|
10
|
+
MODE_PROD = "prod"
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
def self.parse(args)
|
|
13
|
+
options = OpenStruct.new
|
|
14
|
+
options.mode = "dev"
|
|
15
|
+
options.name = "prod"
|
|
16
|
+
options.start = false
|
|
17
|
+
|
|
18
|
+
global = OptionParser.new do |opts|
|
|
19
|
+
opts.banner = "Usage: rbender [options]"
|
|
20
|
+
|
|
21
|
+
opts.separator ""
|
|
22
|
+
opts.separator "Common options:"
|
|
23
|
+
opts.on_tail("-v", "--version", "Prints current RBender version") do
|
|
24
|
+
puts "RBender version: #{RBender::VERSION}"
|
|
25
|
+
puts "by art2rik (https://github.com/art2rik)"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
opts.on("-m", "--mode [MODE]", [MODE_DEV, MODE_PROD],
|
|
29
|
+
"Sets running mode (default 'dev')") do |mode|
|
|
30
|
+
options.mode = mode
|
|
31
|
+
unless mode != MODE_DEV or mode != MODE_PROD
|
|
32
|
+
raise ArgumentError, "Available modes are dev/prod"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
opts.on_tail("-h", "--help", "Shows help message") do
|
|
37
|
+
puts opts
|
|
38
|
+
exit
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
global.order!
|
|
44
|
+
# subcommands[args.shift].order!
|
|
45
|
+
|
|
46
|
+
global.parse! args
|
|
47
|
+
|
|
48
|
+
options
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def create_project(options, args)
|
|
54
|
+
unless args[0] =~ /\w{3,16}/ and args[1] == nil
|
|
55
|
+
raise ArgumentError, "Name must be from 3 to 16 english letters without spaces"
|
|
56
|
+
end
|
|
57
|
+
name = args[0]
|
|
58
|
+
name_undrscr = RBender::ConfigHandler.underscore(name)
|
|
59
|
+
begin
|
|
60
|
+
FileUtils.mkdir name
|
|
61
|
+
rescue
|
|
62
|
+
puts "Folder #{name} already exists"
|
|
63
|
+
exit
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
project_dir = "#{Dir.getwd}/#{name}"
|
|
67
|
+
FileUtils.chdir name
|
|
68
|
+
FileUtils.mkdir "locales"
|
|
69
|
+
FileUtils.mkdir "lib"
|
|
70
|
+
|
|
71
|
+
FileUtils.chdir __dir__
|
|
72
|
+
FileUtils.chdir "../templates"
|
|
73
|
+
FileUtils.cp_r "locales", project_dir
|
|
74
|
+
FileUtils.cp_r "config.yaml", project_dir
|
|
75
|
+
FileUtils.cp_r "Gemfile", project_dir
|
|
76
|
+
FileUtils.cp_r "sample.rb", "#{project_dir}/#{name_undrscr}.rb"
|
|
77
|
+
|
|
78
|
+
#TODO: change name in config
|
|
79
|
+
|
|
80
|
+
RBender::ConfigHandler.config_path = project_dir
|
|
81
|
+
config = RBender::ConfigHandler.settings
|
|
82
|
+
config['title'] = name
|
|
83
|
+
config.save
|
|
84
|
+
|
|
85
|
+
puts "Project #{name} successfully created"
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def run(options, args)
|
|
90
|
+
begin
|
|
91
|
+
settings = RBender::ConfigHandler.settings
|
|
92
|
+
rescue
|
|
93
|
+
puts 'Main file or config are missing!'
|
|
12
94
|
end
|
|
13
|
-
|
|
95
|
+
title = RBender::ConfigHandler.underscore(settings['title'])
|
|
96
|
+
load "#{Dir.pwd}/#{title}.rb"
|
|
97
|
+
bot_frame = RBender.instance
|
|
98
|
+
#TODO: add mode setting
|
|
99
|
+
RBender::ConfigHandler.config_path = __dir__
|
|
100
|
+
bot_frame.set_params(settings)
|
|
101
|
+
bot_frame.run!
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# --- EXECUTE CODE ______
|
|
105
|
+
begin
|
|
106
|
+
options = OptParser.parse(ARGV.dup)
|
|
107
|
+
command = ARGV.shift
|
|
108
|
+
|
|
109
|
+
case command
|
|
110
|
+
when 'new'
|
|
111
|
+
create_project(options, ARGV.dup)
|
|
112
|
+
when "start"
|
|
113
|
+
run(options, ARGV.dup)
|
|
114
|
+
end
|
|
115
|
+
rescue ArgumentError => e
|
|
116
|
+
puts "Wrong argument: #{e}"
|
|
117
|
+
puts "Try 'help' command"
|
|
14
118
|
end
|
|
15
119
|
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
data/lib/rbender.rb
CHANGED
|
@@ -2,7 +2,7 @@ require 'rbender/base'
|
|
|
2
2
|
require 'colorize'
|
|
3
3
|
|
|
4
4
|
module RBender
|
|
5
|
-
VERSION = '0.
|
|
5
|
+
VERSION = '0.4.82'
|
|
6
6
|
@@bot = RBender::Base.new
|
|
7
7
|
|
|
8
8
|
def self.instance
|
|
@@ -33,7 +33,7 @@ def method_missing(m, *args, &block)
|
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
else
|
|
36
|
-
|
|
36
|
+
raise NoMethodError, "Method #{m} is missing!"
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
data/lib/rbender/base.rb
CHANGED
|
@@ -5,165 +5,189 @@ require 'rbender/sessionmanager'
|
|
|
5
5
|
require 'rbender/keyboard_inline'
|
|
6
6
|
require 'rbender/mongo_client'
|
|
7
7
|
require 'rbender/state'
|
|
8
|
+
require 'rbender/config_handler'
|
|
9
|
+
require 'rbender/methods'
|
|
10
|
+
require 'rbender/keyboard'
|
|
11
|
+
|
|
8
12
|
|
|
9
13
|
require 'telegram/bot'
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
class RBender::Base
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
17
|
+
attr_accessor :api
|
|
18
|
+
include RBender::SessionManager
|
|
19
|
+
|
|
20
|
+
public
|
|
21
|
+
|
|
22
|
+
def initialize
|
|
23
|
+
@states = {}
|
|
24
|
+
@global_state = nil
|
|
25
|
+
@modules_block
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def modules(&modules_block)
|
|
29
|
+
@modules_block = modules_block
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Runs the bot with server
|
|
33
|
+
def run!
|
|
34
|
+
puts "Bot is loading...".green
|
|
35
|
+
puts "Bot is running...".green
|
|
36
|
+
Telegram::Bot::Client.run(@token) do |bot|
|
|
37
|
+
@api = bot.api
|
|
38
|
+
|
|
39
|
+
unless @modules_block.nil?
|
|
40
|
+
instance_exec(@api, @mongo_client, &@modules_block)
|
|
41
|
+
end
|
|
42
|
+
bot.listen do |message| # When message has gotten
|
|
43
|
+
begin
|
|
44
|
+
process_message(message)
|
|
45
|
+
rescue => ex
|
|
46
|
+
puts ex.message.red
|
|
47
|
+
puts ex.backtrace
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# @param [Hash] params - hash with params
|
|
55
|
+
#
|
|
56
|
+
# Available parameters:
|
|
57
|
+
# * mongo (required) connection string
|
|
58
|
+
# * bot_name (required) name of bot
|
|
59
|
+
# * token (required) token
|
|
60
|
+
def set_params(params)
|
|
61
|
+
RBender::MongoClient.setup(params['title'], params['mongo'])
|
|
62
|
+
|
|
63
|
+
session_setup(RBender::MongoClient.client)
|
|
64
|
+
|
|
65
|
+
@token = params['development']['token']
|
|
66
|
+
# if params.has_key? localizations
|
|
67
|
+
#
|
|
68
|
+
# end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Adds new state to the bot.
|
|
72
|
+
# @param [String] state_name - name of the state
|
|
73
|
+
# @param [Block] block - actions while state has invoked
|
|
74
|
+
#
|
|
75
|
+
def state(state_name, &block)
|
|
76
|
+
unless @states.has_key? state_name
|
|
77
|
+
@states[state_name] = block
|
|
78
|
+
else
|
|
79
|
+
raise "State name is duplicated!"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def global(&block)
|
|
85
|
+
@global_state = block
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def process_message(message)
|
|
92
|
+
chat_id = message.from.id # Unique chat ID
|
|
93
|
+
|
|
94
|
+
session = get_session(chat_id, message)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
state = session[:state] # User's state
|
|
98
|
+
state_block = @states[state] # Block of the state
|
|
99
|
+
state_object = RBender::State.new message, # Object to invoke
|
|
100
|
+
@api,
|
|
101
|
+
session,
|
|
102
|
+
&state_block
|
|
103
|
+
state_object.instance_eval(&@global_state) unless @global_state.nil?
|
|
104
|
+
state_object.build
|
|
105
|
+
state_object.invoke
|
|
106
|
+
|
|
107
|
+
if is_start_message? message
|
|
108
|
+
state_object.invoke_before if state_object.has_before?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
save_session session
|
|
112
|
+
|
|
113
|
+
state_new = session[:state] # New user's state
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
on_state_changed(state_object,
|
|
117
|
+
state,
|
|
118
|
+
state_new,
|
|
119
|
+
message,
|
|
120
|
+
session)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def on_state_changed(state_object, state_old, state_new, message, session)
|
|
124
|
+
if state_new != state_old # If state has changed
|
|
125
|
+
state_object.invoke_after if state_object.has_after? #invoke after hook for an old state
|
|
126
|
+
|
|
127
|
+
state_new_block = @states[state_new]
|
|
128
|
+
state_object = RBender::State.new message,
|
|
129
|
+
@api,
|
|
130
|
+
session,
|
|
131
|
+
&state_new_block
|
|
132
|
+
|
|
133
|
+
state_object.instance_eval(&@global_state) unless @global_state.nil?
|
|
134
|
+
state_object.build
|
|
135
|
+
|
|
136
|
+
if state_object.has_keyboard? # Invoke a keyboard if it's exists
|
|
137
|
+
state_object.build_keyboard
|
|
138
|
+
state_object.invoke_keyboard
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
state_object.invoke_before if state_object.has_before? #invoke before hook for a new state
|
|
142
|
+
else
|
|
143
|
+
if state_object.has_keyboard? # Invoke a keyboard if it's exists
|
|
144
|
+
unless state_object.get_keyboard.one_time? or not state_object.get_keyboard.force_invoked?
|
|
145
|
+
state_object.build_keyboard
|
|
146
|
+
state_object.invoke_keyboard
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def get_session(chat_id, message)
|
|
153
|
+
if has_session?(chat_id)
|
|
154
|
+
session = load_session(chat_id)
|
|
155
|
+
|
|
156
|
+
else # If user is new
|
|
157
|
+
session = {state: :start,
|
|
158
|
+
state_stack: [],
|
|
159
|
+
keyboard_switchers: {},
|
|
160
|
+
keyboard_switch_groups: {},
|
|
161
|
+
lang: :default
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
session[:user] = {chat_id: chat_id,
|
|
167
|
+
first_name: message.from.first_name,
|
|
168
|
+
last_name: message.from.last_name,
|
|
169
|
+
user_name: message.from.username
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
session[:user].freeze # User's data must be immutable!
|
|
173
|
+
|
|
174
|
+
if is_start_message? message
|
|
175
|
+
session[:state] = :start
|
|
176
|
+
session[:state_stack] = []
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
session
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def is_start_message?(message)
|
|
183
|
+
if message.instance_of? Telegram::Bot::Types::Message
|
|
184
|
+
if message.text == '/start'
|
|
185
|
+
true
|
|
186
|
+
else
|
|
187
|
+
false
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
167
191
|
end
|
|
168
192
|
|
|
169
193
|
|