ssc.nob 0.1.0-java → 0.1.1-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -2
- data/Gemfile +1 -19
- data/Gemfile.lock +49 -0
- data/README.md +56 -8
- data/Rakefile +52 -25
- data/bin/ssc.nob +3 -14
- data/lib/ssc.nob.rb +743 -156
- data/lib/ssc.nob/config.rb +94 -59
- data/lib/ssc.nob/error.rb +5 -17
- data/lib/ssc.nob/ssc_bot.rb +67 -88
- data/lib/ssc.nob/ssc_chat_log.rb +34 -46
- data/lib/ssc.nob/ssc_chat_log/message.rb +15 -27
- data/lib/ssc.nob/ssc_chat_log/message_parser.rb +39 -51
- data/lib/ssc.nob/userface.rb +33 -45
- data/lib/ssc.nob/util.rb +14 -26
- data/lib/ssc.nob/version.rb +4 -16
- data/ssc.nob.gemspec +24 -43
- metadata +45 -15
data/lib/ssc.nob/config.rb
CHANGED
@@ -1,87 +1,107 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
# encoding: UTF-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
4
|
#--
|
6
5
|
# This file is part of SSC.Nob.
|
7
|
-
# Copyright (c) 2020 Jonathan Bradley Whited
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# it under the terms of the GNU General Public License as published by
|
11
|
-
# the Free Software Foundation, either version 3 of the License, or
|
12
|
-
# (at your option) any later version.
|
13
|
-
#
|
14
|
-
# SSC.Nob is distributed in the hope that it will be useful,
|
15
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
-
# GNU General Public License for more details.
|
18
|
-
#
|
19
|
-
# You should have received a copy of the GNU General Public License
|
20
|
-
# along with SSC.Nob. If not, see <https://www.gnu.org/licenses/>.
|
6
|
+
# Copyright (c) 2020-2021 Jonathan Bradley Whited
|
7
|
+
#
|
8
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
21
9
|
#++
|
22
10
|
|
23
11
|
|
12
|
+
require 'java'
|
24
13
|
require 'psych'
|
25
14
|
|
26
15
|
require 'ssc.nob/error'
|
27
16
|
require 'ssc.nob/userface'
|
28
17
|
require 'ssc.nob/util'
|
29
18
|
|
19
|
+
java_import 'java.awt.event.KeyEvent'
|
20
|
+
|
30
21
|
|
31
22
|
module SSCNob
|
32
23
|
###
|
33
|
-
# @author Jonathan Bradley Whited
|
24
|
+
# @author Jonathan Bradley Whited
|
34
25
|
# @since 0.1.0
|
35
26
|
###
|
36
27
|
class Config
|
37
28
|
include Uface
|
38
|
-
|
29
|
+
|
39
30
|
attr_reader :file
|
31
|
+
attr_accessor :msg_key
|
40
32
|
attr_accessor :ssc_dir
|
41
33
|
attr_accessor :username
|
42
|
-
|
34
|
+
|
43
35
|
def initialize(file='ssc.nob.yml')
|
44
36
|
super()
|
45
|
-
|
37
|
+
|
46
38
|
@file = File.expand_path(file)
|
39
|
+
@msg_key = nil
|
47
40
|
@ssc_dir = nil
|
48
41
|
@username = nil
|
49
42
|
end
|
50
|
-
|
51
|
-
def
|
43
|
+
|
44
|
+
def build_msg_key
|
45
|
+
# Can be a single space ' '.
|
46
|
+
return @msg_key if @msg_key.nil? || @msg_key.length == 1
|
47
|
+
|
48
|
+
fuzzy_key = Util.strip(@msg_key).downcase
|
49
|
+
|
50
|
+
if fuzzy_key.empty?
|
51
|
+
@msg_key = nil
|
52
|
+
|
53
|
+
return @msg_key
|
54
|
+
end
|
55
|
+
|
56
|
+
KeyEvent.constants.each do |c|
|
57
|
+
name = c.to_s.downcase
|
58
|
+
|
59
|
+
if name.start_with?('vk_') && name.include?(fuzzy_key)
|
60
|
+
return KeyEvent.const_get(c)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
raise UserError,"that's an invalid msg key{#{@msg_key}}, user."
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_ssc_log_dir
|
52
68
|
return File.join(@ssc_dir,'logs')
|
53
69
|
end
|
54
|
-
|
55
|
-
def
|
70
|
+
|
71
|
+
def check_msg_key
|
72
|
+
build_msg_key
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_ssc_dir
|
56
76
|
@ssc_dir = Util.strip(@ssc_dir)
|
57
|
-
|
58
|
-
if @ssc_dir.nil?
|
77
|
+
|
78
|
+
if @ssc_dir.nil? || @ssc_dir.empty?
|
59
79
|
raise UserError,"that's a blank folder name, user."
|
60
80
|
end
|
61
|
-
|
81
|
+
|
62
82
|
@ssc_dir = File.expand_path(@ssc_dir)
|
63
|
-
|
83
|
+
|
64
84
|
if !Dir.exist?(@ssc_dir)
|
65
85
|
raise UserError,"that folder{#{@ssc_dir}} doesn't exist, user."
|
66
86
|
end
|
67
87
|
if !File.directory?(@ssc_dir)
|
68
88
|
raise UserError,"that's a file{#{@ssc_dir}}, not a folder, user."
|
69
89
|
end
|
70
|
-
|
71
|
-
ssc_log_dir = build_ssc_log_dir
|
72
|
-
|
90
|
+
|
91
|
+
ssc_log_dir = build_ssc_log_dir
|
92
|
+
|
73
93
|
if !Dir.exist?(ssc_log_dir) || !File.directory?(ssc_log_dir)
|
74
94
|
raise UserError,"why's there no 'logs' folder{#{ssc_log_dir}}, user?"
|
75
95
|
end
|
76
96
|
end
|
77
|
-
|
78
|
-
def check_username
|
97
|
+
|
98
|
+
def check_username
|
79
99
|
raise UserError,"that's a blank username, user." if Util.blank?(@username)
|
80
100
|
end
|
81
|
-
|
101
|
+
|
82
102
|
def load_file!(mode: 'rt:BOM|UTF-8',**kargs)
|
83
103
|
data = File.read(@file,mode: mode,**kargs)
|
84
|
-
|
104
|
+
|
85
105
|
yaml = Psych.safe_load(data,
|
86
106
|
aliases: false,
|
87
107
|
filename: @file,
|
@@ -89,63 +109,78 @@ module SSCNob
|
|
89
109
|
symbolize_names: true,
|
90
110
|
**kargs,
|
91
111
|
)
|
92
|
-
|
112
|
+
|
113
|
+
@msg_key = yaml[:msg_key]
|
93
114
|
@ssc_dir = Util.strip(yaml[:ssc_dir])
|
94
|
-
@ssc_dir = File.expand_path(@ssc_dir) if !@ssc_dir.nil?
|
115
|
+
@ssc_dir = File.expand_path(@ssc_dir) if !@ssc_dir.nil? && !@ssc_dir.empty?
|
95
116
|
@username = yaml[:username]
|
96
117
|
end
|
97
|
-
|
118
|
+
|
98
119
|
def save_file(mode: 'wt',**kargs)
|
99
120
|
File.open(@file,mode: mode,**kargs) do |fout|
|
100
|
-
fout.write(to_s
|
121
|
+
fout.write(to_s)
|
101
122
|
end
|
102
123
|
end
|
103
|
-
|
104
|
-
def user_init!
|
124
|
+
|
125
|
+
def user_init!
|
105
126
|
if File.exist?(@file)
|
106
|
-
load_file!
|
127
|
+
load_file!
|
107
128
|
end
|
108
|
-
|
109
|
-
if valid?
|
110
|
-
uface.type(
|
129
|
+
|
130
|
+
if valid?
|
131
|
+
uface.type('Welcome back, ')
|
111
132
|
puts "#{uface.user(@username)}."
|
112
133
|
uface.type("Here's a hot cup of coffee: ")
|
113
134
|
puts uface.coffee
|
114
135
|
else
|
115
136
|
uface.types('Welcome, new user.')
|
116
137
|
puts
|
117
|
-
|
138
|
+
|
118
139
|
@username = uface.ask("What's your #{uface.user('username')}? ")
|
119
|
-
check_username
|
120
|
-
|
140
|
+
check_username
|
141
|
+
|
121
142
|
@ssc_dir = uface.ask("Where's your #{uface.ssc} folder? ")
|
122
|
-
check_ssc_dir
|
123
|
-
|
143
|
+
check_ssc_dir
|
144
|
+
|
145
|
+
puts(<<~MSGKEY)
|
146
|
+
What's your #{uface.color('Message Key').aqua.bold}?
|
147
|
+
- Can input a key code, like 'VK_TAB' or 'TAB'
|
148
|
+
- Can input a letter, like 'm'
|
149
|
+
- Can input nothing
|
150
|
+
MSGKEY
|
151
|
+
@msg_key = uface.ask('> ')
|
152
|
+
check_msg_key
|
153
|
+
|
124
154
|
puts
|
125
155
|
puts uface.gt(@file)
|
126
156
|
if uface.agree('Save this configuration (y/n)? ')
|
127
|
-
save_file
|
157
|
+
save_file
|
128
158
|
end
|
129
159
|
end
|
130
160
|
end
|
131
|
-
|
132
|
-
def valid?
|
161
|
+
|
162
|
+
def valid?
|
133
163
|
begin
|
134
|
-
|
135
|
-
|
136
|
-
|
164
|
+
check_msg_key
|
165
|
+
check_ssc_dir
|
166
|
+
check_username
|
167
|
+
rescue UserError => e
|
168
|
+
puts e.to_s.capitalize
|
169
|
+
puts
|
170
|
+
|
137
171
|
return false
|
138
172
|
end
|
139
|
-
|
173
|
+
|
140
174
|
return true
|
141
175
|
end
|
142
|
-
|
143
|
-
def to_s
|
176
|
+
|
177
|
+
def to_s
|
144
178
|
yaml = {
|
179
|
+
'msg_key' => @msg_key,
|
145
180
|
'username' => @username,
|
146
181
|
'ssc_dir' => @ssc_dir,
|
147
182
|
}
|
148
|
-
|
183
|
+
|
149
184
|
return Psych.dump(yaml,header: true)
|
150
185
|
end
|
151
186
|
end
|
data/lib/ssc.nob/error.rb
CHANGED
@@ -1,32 +1,20 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
# encoding: UTF-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
4
|
#--
|
6
5
|
# This file is part of SSC.Nob.
|
7
|
-
# Copyright (c) 2020 Jonathan Bradley Whited
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# it under the terms of the GNU General Public License as published by
|
11
|
-
# the Free Software Foundation, either version 3 of the License, or
|
12
|
-
# (at your option) any later version.
|
13
|
-
#
|
14
|
-
# SSC.Nob is distributed in the hope that it will be useful,
|
15
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
-
# GNU General Public License for more details.
|
18
|
-
#
|
19
|
-
# You should have received a copy of the GNU General Public License
|
20
|
-
# along with SSC.Nob. If not, see <https://www.gnu.org/licenses/>.
|
6
|
+
# Copyright (c) 2020-2021 Jonathan Bradley Whited
|
7
|
+
#
|
8
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
21
9
|
#++
|
22
10
|
|
23
11
|
|
24
12
|
module SSCNob
|
25
13
|
###
|
26
|
-
# @author Jonathan Bradley Whited
|
14
|
+
# @author Jonathan Bradley Whited
|
27
15
|
# @since 0.1.0
|
28
16
|
###
|
29
17
|
class Error < ::StandardError; end
|
30
|
-
|
18
|
+
|
31
19
|
class UserError < Error; end
|
32
20
|
end
|
data/lib/ssc.nob/ssc_bot.rb
CHANGED
@@ -1,23 +1,11 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
# encoding: UTF-8
|
3
2
|
# frozen_string_literal: true
|
4
3
|
|
5
4
|
#--
|
6
5
|
# This file is part of SSC.Nob.
|
7
|
-
# Copyright (c) 2020 Jonathan Bradley Whited
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# it under the terms of the GNU General Public License as published by
|
11
|
-
# the Free Software Foundation, either version 3 of the License, or
|
12
|
-
# (at your option) any later version.
|
13
|
-
#
|
14
|
-
# SSC.Nob is distributed in the hope that it will be useful,
|
15
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
-
# GNU General Public License for more details.
|
18
|
-
#
|
19
|
-
# You should have received a copy of the GNU General Public License
|
20
|
-
# along with SSC.Nob. If not, see <https://www.gnu.org/licenses/>.
|
6
|
+
# Copyright (c) 2020-2021 Jonathan Bradley Whited
|
7
|
+
#
|
8
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
21
9
|
#++
|
22
10
|
|
23
11
|
|
@@ -36,129 +24,118 @@ java_import 'java.awt.event.KeyEvent'
|
|
36
24
|
|
37
25
|
module SSCNob
|
38
26
|
###
|
39
|
-
# @author Jonathan Bradley Whited
|
27
|
+
# @author Jonathan Bradley Whited
|
40
28
|
# @since 0.1.0
|
41
29
|
###
|
42
30
|
class SSCBot
|
43
31
|
MESSAGE_FLOOD_COUNT = 8
|
44
|
-
MESSAGE_FLOOD_TIME =
|
45
|
-
|
32
|
+
MESSAGE_FLOOD_TIME = 6
|
33
|
+
|
46
34
|
attr_accessor :auto_delay_time
|
47
35
|
attr_reader :char_codes
|
48
36
|
attr_reader :clipboard
|
49
37
|
attr_reader :last_message_time
|
50
38
|
attr_reader :message_count
|
51
|
-
attr_accessor :message_key
|
52
39
|
attr_reader :robot
|
53
|
-
|
54
|
-
|
55
|
-
def initialize(auto_delay_time: 0.2,message_key: KeyEvent::VK_TAB)
|
40
|
+
|
41
|
+
def initialize(auto_delay_time: 0.1)
|
56
42
|
super()
|
57
|
-
|
43
|
+
|
58
44
|
@auto_delay_time = auto_delay_time
|
59
45
|
@char_codes = {}
|
60
|
-
@clipboard = Toolkit.getDefaultToolkit
|
61
|
-
@last_message_time = Time.now
|
46
|
+
@clipboard = Toolkit.getDefaultToolkit.getSystemClipboard
|
47
|
+
@last_message_time = Time.now
|
62
48
|
@message_count = 0
|
63
|
-
@
|
64
|
-
|
65
|
-
|
49
|
+
@robot = Robot.new
|
50
|
+
|
66
51
|
@robot.setAutoDelay(0) # Don't use Java's, too slow
|
67
|
-
|
68
|
-
build_char_codes
|
52
|
+
|
53
|
+
build_char_codes
|
69
54
|
end
|
70
|
-
|
71
|
-
def auto_delay
|
55
|
+
|
56
|
+
def auto_delay
|
72
57
|
sleep(@auto_delay_time)
|
73
|
-
|
58
|
+
|
74
59
|
return self
|
75
60
|
end
|
76
|
-
|
61
|
+
|
77
62
|
def chat_message(channel,msg)
|
78
63
|
return pub_message(";#{channel};#{msg}")
|
79
64
|
end
|
80
|
-
|
65
|
+
|
81
66
|
def copy(str)
|
82
|
-
@clipboard.setContents(StringSelection.new(str),nil)
|
83
|
-
|
67
|
+
@clipboard.setContents(StringSelection.new(str),nil)
|
68
|
+
|
84
69
|
return self
|
85
70
|
end
|
86
|
-
|
87
|
-
def enter
|
71
|
+
|
72
|
+
def enter
|
88
73
|
return type_key(KeyEvent::VK_ENTER)
|
89
74
|
end
|
90
|
-
|
75
|
+
|
91
76
|
def paste(str=nil)
|
92
|
-
copy(str) unless str.nil?
|
93
|
-
|
77
|
+
copy(str) unless str.nil?
|
78
|
+
|
94
79
|
# FIXME: change to VK_META for macOS
|
95
80
|
roll_keys(KeyEvent::VK_CONTROL,KeyEvent::VK_V)
|
96
|
-
|
81
|
+
|
97
82
|
return self
|
98
83
|
end
|
99
|
-
|
100
|
-
def prevent_flooding
|
84
|
+
|
85
|
+
def prevent_flooding
|
101
86
|
@message_count += 1
|
102
|
-
|
87
|
+
|
103
88
|
if @message_count >= MESSAGE_FLOOD_COUNT
|
104
|
-
time_diff = Time.now
|
105
|
-
|
106
|
-
if time_diff
|
107
|
-
|
108
|
-
|
109
|
-
|
89
|
+
time_diff = Time.now - @last_message_time
|
90
|
+
time_diff = (MESSAGE_FLOOD_TIME - time_diff).round
|
91
|
+
time_diff = 1 if time_diff < 1 # Always sleep for at least 1 sec
|
92
|
+
|
93
|
+
sleep(time_diff)
|
94
|
+
|
110
95
|
@message_count = 0
|
111
96
|
end
|
112
|
-
|
113
|
-
@last_message_time = Time.now
|
114
|
-
|
115
|
-
return self
|
116
|
-
end
|
117
|
-
|
118
|
-
def pub_message(msg)
|
119
|
-
type_key(@message_key).paste(msg).enter()
|
120
|
-
|
121
|
-
sleep(1.2)
|
122
|
-
|
97
|
+
|
98
|
+
@last_message_time = Time.now
|
99
|
+
|
123
100
|
return self
|
124
101
|
end
|
125
|
-
|
102
|
+
|
126
103
|
def roll_keys(*key_codes)
|
127
|
-
key_codes.each
|
104
|
+
key_codes.each do |key_code|
|
128
105
|
@robot.keyPress(key_code)
|
129
|
-
auto_delay
|
106
|
+
auto_delay
|
130
107
|
end
|
131
|
-
|
108
|
+
|
132
109
|
(key_codes.length - 1).downto(0) do |i|
|
133
110
|
@robot.keyRelease(key_codes[i])
|
134
|
-
auto_delay
|
111
|
+
auto_delay
|
135
112
|
end
|
136
|
-
|
113
|
+
|
137
114
|
return self
|
138
115
|
end
|
139
|
-
|
116
|
+
|
140
117
|
def type(str)
|
141
|
-
str.each_char
|
118
|
+
str.each_char do |c|
|
142
119
|
key_codes = @char_codes[c]
|
143
|
-
|
144
|
-
next if key_codes.nil?
|
145
|
-
|
120
|
+
|
121
|
+
next if key_codes.nil?
|
122
|
+
|
146
123
|
roll_keys(*key_codes)
|
147
124
|
end
|
148
|
-
|
125
|
+
|
149
126
|
return self
|
150
127
|
end
|
151
|
-
|
128
|
+
|
152
129
|
def type_key(key_code)
|
153
130
|
@robot.keyPress(key_code)
|
154
|
-
auto_delay
|
131
|
+
auto_delay
|
155
132
|
@robot.keyRelease(key_code)
|
156
|
-
auto_delay
|
157
|
-
|
133
|
+
auto_delay
|
134
|
+
|
158
135
|
return self
|
159
136
|
end
|
160
|
-
|
161
|
-
def build_char_codes
|
137
|
+
|
138
|
+
def build_char_codes
|
162
139
|
@char_codes.store("\b",[KeyEvent::VK_BACK_SPACE])
|
163
140
|
@char_codes.store("\f",[KeyEvent::VK_PAGE_DOWN])
|
164
141
|
@char_codes.store("\n",[KeyEvent::VK_ENTER])
|
@@ -167,15 +144,15 @@ module SSCNob
|
|
167
144
|
@char_codes.store(' ',[KeyEvent::VK_SPACE])
|
168
145
|
@char_codes.store('!',[KeyEvent::VK_SHIFT,KeyEvent::VK_EXCLAMATION_MARK])
|
169
146
|
@char_codes.store('"',[KeyEvent::VK_SHIFT,KeyEvent::VK_QUOTEDBL])
|
170
|
-
@char_codes.store('#',[KeyEvent::VK_SHIFT,KeyEvent::
|
171
|
-
@char_codes.store('$',[KeyEvent::VK_SHIFT,KeyEvent::
|
147
|
+
@char_codes.store('#',[KeyEvent::VK_SHIFT,KeyEvent::VK_NUMBER_SIGN])
|
148
|
+
@char_codes.store('$',[KeyEvent::VK_SHIFT,KeyEvent::VK_DOLLAR])
|
172
149
|
@char_codes.store('%',[KeyEvent::VK_SHIFT,KeyEvent::VK_5])
|
173
|
-
@char_codes.store('&',[KeyEvent::VK_SHIFT,KeyEvent::
|
150
|
+
@char_codes.store('&',[KeyEvent::VK_SHIFT,KeyEvent::VK_AMPERSAND])
|
174
151
|
@char_codes.store('\'',[KeyEvent::VK_QUOTE])
|
175
|
-
@char_codes.store('(',[KeyEvent::VK_SHIFT,KeyEvent::
|
176
|
-
@char_codes.store(')',[KeyEvent::VK_SHIFT,KeyEvent::
|
177
|
-
@char_codes.store('*',[KeyEvent::VK_SHIFT,KeyEvent::
|
178
|
-
@char_codes.store('+',[KeyEvent::VK_SHIFT,KeyEvent::
|
152
|
+
@char_codes.store('(',[KeyEvent::VK_SHIFT,KeyEvent::VK_LEFT_PARENTHESIS])
|
153
|
+
@char_codes.store(')',[KeyEvent::VK_SHIFT,KeyEvent::VK_RIGHT_PARENTHESIS])
|
154
|
+
@char_codes.store('*',[KeyEvent::VK_SHIFT,KeyEvent::VK_ASTERISK])
|
155
|
+
@char_codes.store('+',[KeyEvent::VK_SHIFT,KeyEvent::VK_PLUS])
|
179
156
|
@char_codes.store(',',[KeyEvent::VK_COMMA])
|
180
157
|
@char_codes.store('-',[KeyEvent::VK_MINUS])
|
181
158
|
@char_codes.store('.',[KeyEvent::VK_PERIOD])
|
@@ -259,6 +236,8 @@ module SSCNob
|
|
259
236
|
@char_codes.store('|',[KeyEvent::VK_SHIFT,KeyEvent::VK_BACK_SLASH])
|
260
237
|
@char_codes.store('}',[KeyEvent::VK_SHIFT,KeyEvent::VK_BRACERIGHT])
|
261
238
|
@char_codes.store('~',[KeyEvent::VK_SHIFT,KeyEvent::VK_BACK_QUOTE])
|
239
|
+
@char_codes.store('¡',[KeyEvent::VK_SHIFT,KeyEvent::VK_INVERTED_EXCLAMATION_MARK])
|
240
|
+
@char_codes.store('€',[KeyEvent::VK_SHIFT,KeyEvent::VK_EURO_SIGN])
|
262
241
|
end
|
263
242
|
end
|
264
243
|
end
|