ssc.bot 0.1.1 → 0.2.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 +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
|