ssc.bot 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +3 -0
- data/CHANGELOG.md +11 -1
- data/Gemfile +0 -18
- data/README.md +17 -42
- data/Rakefile +6 -23
- data/lib/ssc.bot.rb +6 -17
- data/lib/ssc.bot/chat_log.rb +102 -126
- data/lib/ssc.bot/chat_log/message.rb +32 -42
- data/lib/ssc.bot/chat_log/message_parsable.rb +14 -38
- data/lib/ssc.bot/chat_log/message_parser.rb +153 -164
- data/lib/ssc.bot/chat_log/messages.rb +43 -55
- data/lib/ssc.bot/chat_log_file.rb +12 -25
- data/lib/ssc.bot/clu.rb +55 -0
- data/lib/ssc.bot/error.rb +11 -33
- data/lib/ssc.bot/jruby.rb +31 -0
- data/lib/ssc.bot/ssc_file.rb +44 -55
- data/lib/ssc.bot/user.rb +21 -0
- data/lib/ssc.bot/user/jrobot_message_sender.rb +51 -62
- data/lib/ssc.bot/user/message_sender.rb +104 -162
- data/lib/ssc.bot/util.rb +37 -31
- data/lib/ssc.bot/version.rb +4 -16
- data/ssc.bot.gemspec +21 -44
- data/test/test_helper.rb +3 -19
- data/test/util_test.rb +93 -0
- metadata +21 -16
@@ -1,64 +1,54 @@
|
|
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.Bot.
|
7
|
-
# Copyright (c) 2020 Jonathan Bradley Whited
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# it under the terms of the GNU Lesser 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.Bot 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 Lesser General Public License for more details.
|
18
|
-
#
|
19
|
-
# You should have received a copy of the GNU Lesser General Public License
|
20
|
-
# along with SSC.Bot. If not, see <https://www.gnu.org/licenses/>.
|
6
|
+
# Copyright (c) 2020-2021 Jonathan Bradley Whited
|
7
|
+
#
|
8
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
21
9
|
#++
|
22
10
|
|
23
11
|
|
12
|
+
require 'attr_bool'
|
24
13
|
require 'set'
|
25
14
|
|
26
|
-
|
27
15
|
module SSCBot
|
28
16
|
class ChatLog
|
29
17
|
###
|
30
18
|
# The base class of all parsed messages from a chat log file.
|
31
|
-
#
|
32
|
-
# @author Jonathan Bradley Whited
|
19
|
+
#
|
20
|
+
# @author Jonathan Bradley Whited
|
33
21
|
# @since 0.1.0
|
34
22
|
###
|
35
23
|
class Message
|
24
|
+
extend AttrBool::Ext
|
25
|
+
|
36
26
|
# Adds +type+ to the list of valid {TYPES}
|
37
27
|
# and creates a boolean method for it ending with a +?+.
|
38
|
-
#
|
28
|
+
#
|
39
29
|
# @param type [Symbol,String] the new type to add
|
40
30
|
def self.add_type(type)
|
41
|
-
type = type.to_sym
|
42
|
-
|
31
|
+
type = type.to_sym
|
32
|
+
|
43
33
|
return if TYPES.include?(type)
|
44
|
-
|
34
|
+
|
45
35
|
TYPES.add(type)
|
46
|
-
|
47
|
-
name = type.to_s
|
48
|
-
|
36
|
+
|
37
|
+
name = type.to_s.sub('?','q_')
|
38
|
+
|
49
39
|
define_method(:"type_#{name}?") do
|
50
40
|
return @type == type
|
51
41
|
end
|
52
42
|
end
|
53
|
-
|
43
|
+
|
54
44
|
# Valid types of messages.
|
55
|
-
#
|
45
|
+
#
|
56
46
|
# You can add your own custom type(s) that you parse manually:
|
57
47
|
# SSCBot::ChatLog::Message.add_type(:custom)
|
58
|
-
TYPES = Set.new
|
59
|
-
|
48
|
+
TYPES = Set.new
|
49
|
+
|
60
50
|
# In order of F1 Help box.
|
61
|
-
%i
|
51
|
+
%i[
|
62
52
|
pub team private remote freq chat
|
63
53
|
?lines ?namelen ?ignore ?nopubchat ?obscene ?away ?log ?logbuffer
|
64
54
|
?kill kill ?enter enter ?leave leave ?message ?messages ?chat
|
@@ -68,38 +58,38 @@ class ChatLog
|
|
68
58
|
?sound ?alarm ?sheep ?getnews
|
69
59
|
?squadowner ?squad ?squadlist ?loadmacro ?savemacro
|
70
60
|
unknown
|
71
|
-
|
61
|
+
].each do |type|
|
72
62
|
add_type(type)
|
73
63
|
end
|
74
|
-
|
64
|
+
|
75
65
|
# @param type [Symbol] the type to check if valid
|
76
66
|
# @return [Boolean] +true+ if +type+ is one of {TYPES}, else +false+
|
77
67
|
def self.valid_type?(type)
|
78
68
|
return TYPES.include?(type)
|
79
69
|
end
|
80
|
-
|
70
|
+
|
81
71
|
attr_reader :line # @return [String] the raw (unparsed) line from the file
|
82
72
|
attr_reader :type # @return [Symbol] what type of message this is; one of {TYPES}
|
83
|
-
|
73
|
+
|
84
74
|
# @param line [String] the raw (unparsed) line from the file
|
85
75
|
# @param type [Symbol] what type of message this is; must be one of {TYPES}
|
86
76
|
def initialize(line,type:)
|
87
|
-
type = type.to_sym
|
88
|
-
|
89
|
-
raise ArgumentError,"invalid line{#{line.inspect
|
90
|
-
raise ArgumentError,"invalid type{#{type.inspect
|
91
|
-
|
77
|
+
type = type.to_sym
|
78
|
+
|
79
|
+
raise ArgumentError,"invalid line{#{line.inspect}}" if line.nil?
|
80
|
+
raise ArgumentError,"invalid type{#{type.inspect}}" if !self.class.valid_type?(type)
|
81
|
+
|
92
82
|
@line = line
|
93
83
|
@type = type
|
94
84
|
end
|
95
|
-
|
85
|
+
|
96
86
|
# A convenience method for comparing anything that responds to
|
97
87
|
# +:to_sym():+, like +String+.
|
98
|
-
#
|
88
|
+
#
|
99
89
|
# @param type [String,Symbol] the type to convert & compare against
|
100
90
|
# @return [Boolean] +true+ if this message is of type +type+, else +false+
|
101
91
|
def type?(type)
|
102
|
-
return @type == type.to_sym
|
92
|
+
return @type == type.to_sym
|
103
93
|
end
|
104
94
|
end
|
105
95
|
end
|
@@ -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.Bot.
|
7
|
-
# Copyright (c) 2020 Jonathan Bradley Whited
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# it under the terms of the GNU Lesser 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.Bot 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 Lesser General Public License for more details.
|
18
|
-
#
|
19
|
-
# You should have received a copy of the GNU Lesser General Public License
|
20
|
-
# along with SSC.Bot. If not, see <https://www.gnu.org/licenses/>.
|
6
|
+
# Copyright (c) 2020-2021 Jonathan Bradley Whited
|
7
|
+
#
|
8
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
21
9
|
#++
|
22
10
|
|
23
11
|
|
@@ -25,38 +13,26 @@ require 'forwardable'
|
|
25
13
|
|
26
14
|
require 'ssc.bot/chat_log/message_parser'
|
27
15
|
|
28
|
-
|
29
16
|
module SSCBot
|
30
17
|
class ChatLog
|
31
18
|
###
|
32
|
-
# @author Jonathan Bradley Whited
|
19
|
+
# @author Jonathan Bradley Whited
|
33
20
|
# @since 0.1.0
|
34
21
|
###
|
35
22
|
module MessageParsable
|
36
23
|
extend Forwardable
|
37
|
-
|
24
|
+
|
38
25
|
MAX_NAMELEN = MessageParser::MAX_NAMELEN
|
39
|
-
|
40
|
-
def_delegators(:@parser,
|
41
|
-
:autoset_namelen?,:autoset_namelen=,
|
42
|
-
:check_history_count,:check_history_count=,
|
43
|
-
:commands,
|
44
|
-
:messages,
|
45
|
-
:namelen,:namelen=,
|
46
|
-
:regex_cache,
|
47
|
-
:store_history?,:store_history=,
|
48
|
-
:strict?,:strict=,
|
49
|
-
|
50
|
-
:parse,
|
51
|
-
|
52
|
-
:clear_history,
|
53
|
-
:reset_namelen,
|
54
|
-
:store_command,
|
55
|
-
|
56
|
-
:command?,
|
57
|
-
)
|
58
|
-
|
26
|
+
|
59
27
|
attr_reader :parser
|
28
|
+
|
29
|
+
(MessageParser.public_instance_methods - Class.public_instance_methods).each do |method|
|
30
|
+
name = method.to_s
|
31
|
+
|
32
|
+
next if name.start_with?('match_') || name.start_with?('parse_')
|
33
|
+
|
34
|
+
def_delegator(:@parser,method)
|
35
|
+
end
|
60
36
|
end
|
61
37
|
end
|
62
38
|
end
|
@@ -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.Bot.
|
7
|
-
# Copyright (c) 2020 Jonathan Bradley Whited
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# it under the terms of the GNU Lesser 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.Bot 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 Lesser General Public License for more details.
|
18
|
-
#
|
19
|
-
# You should have received a copy of the GNU Lesser General Public License
|
20
|
-
# along with SSC.Bot. If not, see <https://www.gnu.org/licenses/>.
|
6
|
+
# Copyright (c) 2020-2021 Jonathan Bradley Whited
|
7
|
+
#
|
8
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
21
9
|
#++
|
22
10
|
|
23
11
|
|
@@ -30,16 +18,17 @@ require 'ssc.bot/util'
|
|
30
18
|
require 'ssc.bot/chat_log/message'
|
31
19
|
require 'ssc.bot/chat_log/messages'
|
32
20
|
|
33
|
-
|
34
21
|
module SSCBot
|
35
22
|
class ChatLog
|
36
23
|
###
|
37
|
-
# @author Jonathan Bradley Whited
|
24
|
+
# @author Jonathan Bradley Whited
|
38
25
|
# @since 0.1.0
|
39
26
|
###
|
40
27
|
class MessageParser
|
28
|
+
extend AttrBool::Ext
|
29
|
+
|
41
30
|
MAX_NAMELEN = 24
|
42
|
-
|
31
|
+
|
43
32
|
attr_accessor? :autoset_namelen
|
44
33
|
attr_accessor :check_history_count
|
45
34
|
attr_reader :commands
|
@@ -48,10 +37,10 @@ class ChatLog
|
|
48
37
|
attr_reader :regex_cache
|
49
38
|
attr_accessor? :store_history
|
50
39
|
attr_accessor? :strict
|
51
|
-
|
40
|
+
|
52
41
|
def initialize(autoset_namelen: true,check_history_count: 5,namelen: nil,store_history: true,strict: true)
|
53
42
|
super()
|
54
|
-
|
43
|
+
|
55
44
|
@autoset_namelen = autoset_namelen
|
56
45
|
@check_history_count = check_history_count
|
57
46
|
@commands = {}
|
@@ -61,37 +50,37 @@ class ChatLog
|
|
61
50
|
@store_history = store_history
|
62
51
|
@strict = strict
|
63
52
|
end
|
64
|
-
|
53
|
+
|
65
54
|
# The Ruby interpreter should cache the args' default values,
|
66
55
|
# so no reason to manually cache them unless a variable is involved inside.
|
67
|
-
#
|
56
|
+
#
|
68
57
|
# @example Default Format
|
69
58
|
# 'X Name> Message'
|
70
|
-
def match_player(line,type_name:,name_prefix: '',name_suffix: '> ',type_prefix:
|
59
|
+
def match_player(line,type_name:,name_prefix: '',name_suffix: '> ',type_prefix: /../,use_namelen: true)
|
71
60
|
cached_regex = @regex_cache[type_name]
|
72
|
-
|
73
|
-
if cached_regex.nil?
|
61
|
+
|
62
|
+
if cached_regex.nil?
|
74
63
|
cached_regex = {}
|
75
64
|
@regex_cache[type_name] = cached_regex
|
76
65
|
end
|
77
|
-
|
78
|
-
use_namelen &&= !@namelen.nil?
|
66
|
+
|
67
|
+
use_namelen &&= !@namelen.nil?
|
79
68
|
key = use_namelen ? @namelen : :no_namelen
|
80
69
|
regex = cached_regex[key]
|
81
|
-
|
82
|
-
if regex.nil?
|
70
|
+
|
71
|
+
if regex.nil?
|
83
72
|
name_prefix = Util.quote_str_or_regex(name_prefix)
|
84
73
|
name_suffix = Util.quote_str_or_regex(name_suffix)
|
85
74
|
type_prefix = Util.quote_str_or_regex(type_prefix)
|
86
|
-
|
75
|
+
|
87
76
|
if use_namelen
|
88
77
|
name = /.{#{@namelen}}/
|
89
78
|
else
|
90
79
|
name = /.*?\S/
|
91
80
|
end
|
92
|
-
|
81
|
+
|
93
82
|
name = Util.quote_str_or_regex(name)
|
94
|
-
|
83
|
+
|
95
84
|
# Be careful to not use spaces ' ', but to use '\\ ' (or '\s') instead
|
96
85
|
# because of the '/x' option.
|
97
86
|
regex = /
|
@@ -99,25 +88,25 @@ class ChatLog
|
|
99
88
|
#{name_prefix}(?<name>#{name})#{name_suffix}
|
100
89
|
(?<message>.*)\z
|
101
90
|
/x
|
102
|
-
|
91
|
+
|
103
92
|
cached_regex[key] = regex
|
104
93
|
end
|
105
|
-
|
94
|
+
|
106
95
|
return regex.match(line)
|
107
96
|
end
|
108
|
-
|
97
|
+
|
109
98
|
def parse(line)
|
110
|
-
if line.nil?
|
99
|
+
if line.nil?
|
111
100
|
if @strict
|
112
|
-
raise ArgumentError,"invalid line{#{line.inspect
|
101
|
+
raise ArgumentError,"invalid line{#{line.inspect}}"
|
113
102
|
else
|
114
103
|
line = ''
|
115
104
|
end
|
116
105
|
end
|
117
|
-
|
106
|
+
|
118
107
|
message = nil
|
119
|
-
|
120
|
-
if !line.empty?
|
108
|
+
|
109
|
+
if !line.empty?
|
121
110
|
case line[0]
|
122
111
|
when 'C'
|
123
112
|
message = parse_chat(line)
|
@@ -151,64 +140,64 @@ class ChatLog
|
|
151
140
|
end
|
152
141
|
end
|
153
142
|
end
|
154
|
-
|
155
|
-
if message.nil?
|
143
|
+
|
144
|
+
if message.nil?
|
156
145
|
message = Message.new(line,type: :unknown)
|
157
146
|
end
|
158
|
-
|
147
|
+
|
159
148
|
if @store_history
|
160
149
|
@messages << message
|
161
150
|
end
|
162
|
-
|
151
|
+
|
163
152
|
return message
|
164
153
|
end
|
165
|
-
|
154
|
+
|
166
155
|
# @example Format
|
167
156
|
# # NOT affected by namelen.
|
168
157
|
# 'C 1:Name> Message'
|
169
158
|
def parse_chat(line)
|
170
|
-
match = match_player(line,type_name: :chat,name_prefix:
|
159
|
+
match = match_player(line,type_name: :chat,name_prefix: /(?<channel>\d+)\:/,use_namelen: false)
|
171
160
|
player = parse_player(line,type_name: :chat,match: match)
|
172
|
-
|
173
|
-
return nil if player.nil?
|
174
|
-
|
175
|
-
channel = match[:channel].to_i
|
176
|
-
|
161
|
+
|
162
|
+
return nil if player.nil?
|
163
|
+
|
164
|
+
channel = match[:channel].to_i
|
165
|
+
|
177
166
|
return ChatMessage.new(line,channel: channel,name: player.name,message: player.message)
|
178
167
|
end
|
179
|
-
|
168
|
+
|
180
169
|
# @example Format
|
181
170
|
# 'E Name> Message'
|
182
171
|
def parse_freq(line)
|
183
172
|
player = parse_player(line,type_name: :freq)
|
184
|
-
|
185
|
-
return nil if player.nil?
|
186
|
-
|
173
|
+
|
174
|
+
return nil if player.nil?
|
175
|
+
|
187
176
|
return FreqMessage.new(line,name: player.name,message: player.message)
|
188
177
|
end
|
189
|
-
|
178
|
+
|
190
179
|
# @example Format
|
191
180
|
# ' Killed.Name(100) killed by: Killer.Name'
|
192
181
|
def parse_kill(line,match:)
|
193
|
-
if match.nil?
|
182
|
+
if match.nil?
|
194
183
|
if @strict
|
195
184
|
raise ParseError,"invalid kill message{#{line}}"
|
196
185
|
else
|
197
186
|
return nil
|
198
187
|
end
|
199
188
|
end
|
200
|
-
|
189
|
+
|
201
190
|
killed = match[:killed]
|
202
|
-
bounty = match[:bounty].to_i
|
191
|
+
bounty = match[:bounty].to_i
|
203
192
|
killer = match[:killer]
|
204
|
-
|
193
|
+
|
205
194
|
return KillMessage.new(line,killed: killed,bounty: bounty,killer: killer)
|
206
195
|
end
|
207
|
-
|
196
|
+
|
208
197
|
# @example Default Format
|
209
198
|
# 'X Name> Message'
|
210
199
|
def parse_player(line,type_name:,match: :default)
|
211
|
-
if match.nil?
|
200
|
+
if match.nil?
|
212
201
|
if @strict
|
213
202
|
raise ParseError,"invalid #{type_name} message{#{line}}"
|
214
203
|
else
|
@@ -218,47 +207,47 @@ class ChatLog
|
|
218
207
|
# Use type_name of :player (not passed in param) for regex_cache.
|
219
208
|
match = match_player(line,type_name: :player)
|
220
209
|
end
|
221
|
-
|
210
|
+
|
222
211
|
name = Util.u_lstrip(match[:name])
|
223
212
|
message = match[:message]
|
224
|
-
|
225
|
-
if name.empty?
|
213
|
+
|
214
|
+
if name.empty? || name.length > MAX_NAMELEN
|
226
215
|
if @strict
|
227
216
|
raise ParseError,"invalid player name for #{type_name} message{#{line}}"
|
228
217
|
else
|
229
218
|
return nil
|
230
219
|
end
|
231
220
|
end
|
232
|
-
|
221
|
+
|
233
222
|
return PlayerMessage.new(line,type: :unknown,name: name,message: message)
|
234
223
|
end
|
235
|
-
|
224
|
+
|
236
225
|
# @example Format
|
237
226
|
# 'P Name> Message'
|
238
227
|
def parse_private(line)
|
239
228
|
player = parse_player(line,type_name: :private)
|
240
|
-
|
241
|
-
return nil if player.nil?
|
242
|
-
|
229
|
+
|
230
|
+
return nil if player.nil?
|
231
|
+
|
243
232
|
return PrivateMessage.new(line,name: player.name,message: player.message)
|
244
233
|
end
|
245
|
-
|
234
|
+
|
246
235
|
# @example Format
|
247
236
|
# ' Name> Message'
|
248
237
|
def parse_pub(line,match:)
|
249
238
|
player = parse_player(line,type_name: :pub,match: match)
|
250
|
-
|
251
|
-
return nil if player.nil?
|
252
|
-
|
253
|
-
cmd = Util.u_strip(player.message).downcase
|
254
|
-
|
239
|
+
|
240
|
+
return nil if player.nil?
|
241
|
+
|
242
|
+
cmd = Util.u_strip(player.message).downcase
|
243
|
+
|
255
244
|
if cmd.start_with?('?find')
|
256
|
-
store_command(:pub,%s
|
245
|
+
store_command(:pub,%s(?find)) # See: match_q_find?()
|
257
246
|
end
|
258
|
-
|
247
|
+
|
259
248
|
return PubMessage.new(line,name: player.name,message: player.message)
|
260
249
|
end
|
261
|
-
|
250
|
+
|
262
251
|
# @example Format
|
263
252
|
# ' Not online, last seen more than 10 days ago'
|
264
253
|
# ' Not online, last seen 9 days ago'
|
@@ -269,74 +258,74 @@ class ChatLog
|
|
269
258
|
# ' Name is in SSCJ Devastation'
|
270
259
|
# ' Name is in SSCC Metal Gear CTF'
|
271
260
|
def parse_q_find(line,match:)
|
272
|
-
if match.nil?
|
261
|
+
if match.nil?
|
273
262
|
if @strict
|
274
263
|
raise ParseError,"invalid ?find message{#{line}}"
|
275
264
|
else
|
276
265
|
return nil
|
277
266
|
end
|
278
267
|
end
|
279
|
-
|
268
|
+
|
280
269
|
caps = match.named_captures
|
281
270
|
q_find = nil
|
282
|
-
|
271
|
+
|
283
272
|
if (days = caps['days'])
|
284
273
|
more = caps.key?('more')
|
285
|
-
days = days.to_i
|
286
|
-
|
274
|
+
days = days.to_i
|
275
|
+
|
287
276
|
q_find = QFindMessage.new(line,find_type: :days,more: more,days: days)
|
288
277
|
elsif (hours = caps['hours'])
|
289
|
-
hours = hours.to_i
|
290
|
-
|
278
|
+
hours = hours.to_i
|
279
|
+
|
291
280
|
q_find = QFindMessage.new(line,find_type: :hours,hours: hours)
|
292
281
|
elsif (player = caps['player'])
|
293
282
|
if (arena = caps['arena'])
|
294
283
|
private = (arena == '(Private arena)')
|
295
|
-
|
284
|
+
|
296
285
|
q_find = QFindMessage.new(line,find_type: :arena,player: player,arena: arena,private: private)
|
297
286
|
elsif (zone = caps['zone'])
|
298
287
|
q_find = QFindMessage.new(line,find_type: :zone,player: player,zone: zone)
|
299
288
|
end
|
300
289
|
end
|
301
|
-
|
302
|
-
if q_find.nil?
|
290
|
+
|
291
|
+
if q_find.nil? && @strict
|
303
292
|
raise ParseError,"invalid ?find message{#{line}}"
|
304
293
|
end
|
305
|
-
|
294
|
+
|
306
295
|
return q_find
|
307
296
|
end
|
308
|
-
|
297
|
+
|
309
298
|
# @example Format
|
310
299
|
# ' Log file open: session.log'
|
311
300
|
# ' Log file closed'
|
312
301
|
def parse_q_log(line,match:)
|
313
|
-
if match.nil?
|
302
|
+
if match.nil?
|
314
303
|
if @strict
|
315
304
|
raise ParseError,"invalid ?log message{#{line}}"
|
316
305
|
else
|
317
306
|
return nil
|
318
307
|
end
|
319
308
|
end
|
320
|
-
|
309
|
+
|
321
310
|
filename = match.named_captures['filename']
|
322
|
-
log_type = filename.nil?
|
323
|
-
|
311
|
+
log_type = filename.nil? ? :close : :open
|
312
|
+
|
324
313
|
return QLogMessage.new(line,log_type: log_type,filename: filename)
|
325
314
|
end
|
326
|
-
|
315
|
+
|
327
316
|
# @example Format
|
328
317
|
# ' Message Name Length: 24'
|
329
318
|
def parse_q_namelen(line,match:)
|
330
|
-
if match.nil?
|
319
|
+
if match.nil?
|
331
320
|
if @strict
|
332
321
|
raise ParseError,"invalid ?namelen message{#{line}}"
|
333
322
|
else
|
334
323
|
return nil
|
335
324
|
end
|
336
325
|
end
|
337
|
-
|
338
|
-
namelen = match[:namelen].to_i
|
339
|
-
|
326
|
+
|
327
|
+
namelen = match[:namelen].to_i
|
328
|
+
|
340
329
|
if namelen < 1
|
341
330
|
if @strict
|
342
331
|
raise ParseError,"invalid namelen for ?namelen message{#{line}}"
|
@@ -346,105 +335,105 @@ class ChatLog
|
|
346
335
|
elsif namelen > MAX_NAMELEN
|
347
336
|
warn("namelen{#{namelen}} > max{#{MAX_NAMELEN}} for ?namelen message{#{line}}",uplevel: 0)
|
348
337
|
end
|
349
|
-
|
338
|
+
|
350
339
|
if @autoset_namelen
|
351
340
|
@namelen = namelen
|
352
341
|
end
|
353
|
-
|
342
|
+
|
354
343
|
return QNamelenMessage.new(line,namelen: namelen)
|
355
344
|
end
|
356
|
-
|
345
|
+
|
357
346
|
# @example Format
|
358
347
|
# # NOT affected by namelen.
|
359
348
|
# 'P :Self.Name:Message'
|
360
349
|
# 'P (Name)>Message'
|
361
350
|
def parse_remote(line,match:)
|
362
|
-
player = parse_player(line,type_name: %s
|
363
|
-
|
364
|
-
return nil if player.nil?
|
365
|
-
|
351
|
+
player = parse_player(line,type_name: %s(remote.private),match: match)
|
352
|
+
|
353
|
+
return nil if player.nil?
|
354
|
+
|
366
355
|
own = (line[2] == ':')
|
367
356
|
squad = (player.name[0] == '#')
|
368
|
-
|
357
|
+
|
369
358
|
return RemoteMessage.new(line,
|
370
359
|
own: own,squad: squad,
|
371
360
|
name: player.name,message: player.message,
|
372
361
|
)
|
373
362
|
end
|
374
|
-
|
363
|
+
|
375
364
|
# @example Format
|
376
365
|
# 'T Name> Message'
|
377
366
|
def parse_team(line)
|
378
367
|
player = parse_player(line,type_name: :team)
|
379
|
-
|
380
|
-
return nil if player.nil?
|
381
|
-
|
368
|
+
|
369
|
+
return nil if player.nil?
|
370
|
+
|
382
371
|
return TeamMessage.new(line,name: player.name,message: player.message)
|
383
372
|
end
|
384
|
-
|
385
|
-
def clear_history
|
386
|
-
@messages.clear
|
373
|
+
|
374
|
+
def clear_history
|
375
|
+
@messages.clear
|
387
376
|
end
|
388
|
-
|
389
|
-
def reset_namelen
|
377
|
+
|
378
|
+
def reset_namelen
|
390
379
|
@namelen = nil
|
391
380
|
end
|
392
|
-
|
381
|
+
|
393
382
|
def store_command(type,name)
|
394
383
|
type_hash = @commands[type]
|
395
|
-
|
396
|
-
if type_hash.nil?
|
384
|
+
|
385
|
+
if type_hash.nil?
|
397
386
|
type_hash = {}
|
398
387
|
@commands[type] = type_hash
|
399
388
|
end
|
400
|
-
|
389
|
+
|
401
390
|
type_hash[name] = @messages.length # Index of when command was found/stored
|
402
391
|
end
|
403
|
-
|
392
|
+
|
404
393
|
def command?(type,name,delete: true)
|
405
394
|
return true if @check_history_count < 1
|
406
|
-
|
395
|
+
|
407
396
|
type_hash = @commands[type]
|
408
|
-
|
409
|
-
if !type_hash.nil?
|
397
|
+
|
398
|
+
if !type_hash.nil?
|
410
399
|
index = type_hash[name]
|
411
|
-
|
412
|
-
if !index.nil?
|
400
|
+
|
401
|
+
if !index.nil? && (@messages.length - index) <= @check_history_count
|
413
402
|
type_hash.delete(name) if delete
|
414
|
-
|
403
|
+
|
415
404
|
return true
|
416
405
|
end
|
417
406
|
end
|
418
|
-
|
407
|
+
|
419
408
|
return false
|
420
409
|
end
|
421
|
-
|
410
|
+
|
422
411
|
# @example Format
|
423
412
|
# ' Killed.Name(100) killed by: Killer.Name'
|
424
413
|
def match_kill?(line)
|
425
414
|
return false if line.length < 19 # ' N(0) killed by: N'
|
426
|
-
|
415
|
+
|
427
416
|
return /\A (?<killed>.*?\S)\((?<bounty>\d+)\) killed by: (?<killer>.*?\S)\z/.match(line)
|
428
417
|
end
|
429
|
-
|
418
|
+
|
430
419
|
# @example Format
|
431
420
|
# ' Name> Message'
|
432
421
|
def match_pub?(line)
|
433
422
|
return false if line.length < 5 # ' N> '
|
434
|
-
|
423
|
+
|
435
424
|
match = match_player(line,type_name: :pub,type_prefix: ' ')
|
436
|
-
|
437
|
-
if !match.nil?
|
425
|
+
|
426
|
+
if !match.nil?
|
438
427
|
name = Util.u_lstrip(match[:name])
|
439
|
-
|
440
|
-
if name.empty?
|
428
|
+
|
429
|
+
if name.empty? || name.length > MAX_NAMELEN
|
441
430
|
return false
|
442
431
|
end
|
443
432
|
end
|
444
|
-
|
433
|
+
|
445
434
|
return match
|
446
435
|
end
|
447
|
-
|
436
|
+
|
448
437
|
# @example Format
|
449
438
|
# ' Not online, last seen more than 10 days ago'
|
450
439
|
# ' Not online, last seen 9 days ago'
|
@@ -456,25 +445,25 @@ class ChatLog
|
|
456
445
|
# ' Name is in SSCC Metal Gear CTF'
|
457
446
|
def match_q_find?(line)
|
458
447
|
return false if line.length < 7 # ' N - A'
|
459
|
-
return false unless command?(:pub,%s
|
460
|
-
|
448
|
+
return false unless command?(:pub,%s(?find))
|
449
|
+
|
461
450
|
if line.start_with?(' Not online, last seen ')
|
462
451
|
match = line.match(/(?<more>more) than (?<days>\d+) days ago\z/)
|
463
|
-
match = line.match(/(?<days>\d+) days? ago\z/) if match.nil?
|
464
|
-
match = line.match(/(?<hours>\d+) hours? ago\z/) if match.nil?
|
465
|
-
|
452
|
+
match = line.match(/(?<days>\d+) days? ago\z/) if match.nil?
|
453
|
+
match = line.match(/(?<hours>\d+) hours? ago\z/) if match.nil?
|
454
|
+
|
466
455
|
return match
|
467
456
|
else
|
468
457
|
match = line.match(/\A (?<player>.+) is in (?<zone>.+)\z/)
|
469
|
-
match = line.match(/\A (?<player>.+) - (?<arena>.+)\z/) if match.nil?
|
470
|
-
|
458
|
+
match = line.match(/\A (?<player>.+) - (?<arena>.+)\z/) if match.nil?
|
459
|
+
|
471
460
|
if match
|
472
461
|
caps = match.named_captures
|
473
|
-
|
462
|
+
|
474
463
|
player = caps['player']
|
475
|
-
|
464
|
+
|
476
465
|
return false if player.length > MAX_NAMELEN
|
477
|
-
|
466
|
+
|
478
467
|
if caps.key?('arena')
|
479
468
|
area = caps['arena']
|
480
469
|
elsif caps.key?('zone')
|
@@ -482,59 +471,59 @@ class ChatLog
|
|
482
471
|
else
|
483
472
|
return false
|
484
473
|
end
|
485
|
-
|
474
|
+
|
486
475
|
# If do /\A (?<player>[^[[:space:]]].+[^[[:space:]])/, then it won't
|
487
476
|
# capture names/zones/arenas that are only 1 char long, so do this.
|
488
|
-
[player[0],player[-1],area[0],area[-1]].each
|
477
|
+
[player[0],player[-1],area[0],area[-1]].each do |c|
|
489
478
|
if c =~ /[[:space:]]/
|
490
479
|
return false
|
491
480
|
end
|
492
481
|
end
|
493
|
-
|
482
|
+
|
494
483
|
return match
|
495
484
|
end
|
496
485
|
end
|
497
|
-
|
486
|
+
|
498
487
|
return false
|
499
488
|
end
|
500
|
-
|
489
|
+
|
501
490
|
# @example Format
|
502
491
|
# ' Log file open: session.log'
|
503
492
|
# ' Log file closed'
|
504
493
|
def match_q_log?(line)
|
505
494
|
return false if line.length < 17
|
506
|
-
|
495
|
+
|
507
496
|
match = /\A Log file open: (?<filename>.+)\z/.match(line)
|
508
|
-
match = /\A Log file closed\z/.match(line) if match.nil?
|
509
|
-
|
497
|
+
match = /\A Log file closed\z/.match(line) if match.nil?
|
498
|
+
|
510
499
|
return match
|
511
500
|
end
|
512
|
-
|
501
|
+
|
513
502
|
# @example Format
|
514
503
|
# ' Message Name Length: 24'
|
515
504
|
def match_q_namelen?(line)
|
516
505
|
return false if line.length < 24 # '...: 0'
|
517
506
|
return false if line[21] != ':'
|
518
|
-
|
507
|
+
|
519
508
|
return /\A Message Name Length: (?<namelen>\d+)\z/.match(line)
|
520
509
|
end
|
521
|
-
|
510
|
+
|
522
511
|
# @example Format
|
523
512
|
# # NOT affected by namelen.
|
524
513
|
# 'P :Self.Name:Message'
|
525
514
|
# 'P (Name)>Message'
|
526
515
|
def match_remote?(line)
|
527
516
|
return false if line.length < 5 # 'P :N:'
|
528
|
-
|
517
|
+
|
529
518
|
case line[2]
|
530
519
|
when ':'
|
531
|
-
return match_player(line,type_name: %s
|
520
|
+
return match_player(line,type_name: %s(remote.out),
|
532
521
|
name_prefix: ':',name_suffix: ':',use_namelen: false)
|
533
522
|
when '('
|
534
|
-
return match_player(line,type_name: %s
|
523
|
+
return match_player(line,type_name: %s(remote.in),
|
535
524
|
name_prefix: '(',name_suffix: ')>',use_namelen: false)
|
536
525
|
end
|
537
|
-
|
526
|
+
|
538
527
|
return false
|
539
528
|
end
|
540
529
|
end
|