tf2_line_parser 0.6.3 → 0.6.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f62b3222dd89877b6ce919788a689fb437bfbc7ca75a981f4d35fade570ec629
4
- data.tar.gz: 4ddd3fcfea754c47b36fd4fd726f210cc9a5b407fa88273029c475e8388f0940
3
+ metadata.gz: f18e216a4247d37720754f3a66281d93c88af477875335fd9a3d3a79228f2a8b
4
+ data.tar.gz: f3ce9a46e96f0b37820c02e5b876f98bf8232fff6e42733ebd79727a4fd9e46a
5
5
  SHA512:
6
- metadata.gz: 99198496cdaf2a68406b0a106b189133c02bf753d816738d99a2f7abd0065d6f0df640f3fce4fb1604ba49898d18b2855015b80e4ae5b7ddb1b03163b2fb05f6
7
- data.tar.gz: c509657ae00cbf328d70b1685aee1d4dd9e0119a328331534b7bad6f47c656d2ce9b58c54caf8903c51c95afba28c9ae9d7d9e2b8a5574b3d763295baa162972
6
+ metadata.gz: d9daf5c5f614d817d09d8de35539f5d7acbc61bbb7b47863e4a12551cdae2a00ba521ede013cfa485eed863520a157007496a34173dfdb46b388dcdb9dd7934e
7
+ data.tar.gz: 985510a7d6108b80db1ae408e32dfa6e0d5da41293b4f50d74abae403a79f3ad4774b750693d85438e86969f48f2d1373fd2dbcc79a533e33e5784de777a9030
@@ -27,13 +27,13 @@ module TF2LineParser
27
27
 
28
28
  class Say < Chat
29
29
  def self.regex
30
- @regex ||= /#{regex_time} #{regex_player} say #{regex_message}/.freeze
30
+ @regex ||= /#{regex_time} #{regex_chat_player} say #{regex_message}/.freeze
31
31
  end
32
32
  end
33
33
 
34
34
  class TeamSay < Chat
35
35
  def self.regex
36
- @regex ||= /#{regex_time} #{regex_player} say_team #{regex_message}/.freeze
36
+ @regex ||= /#{regex_time} #{regex_chat_player} say_team #{regex_message}/.freeze
37
37
  end
38
38
  end
39
39
  end
@@ -18,6 +18,15 @@ module TF2LineParser
18
18
  @regex_player ||= /"(?<player_section>.*?)"/
19
19
  end
20
20
 
21
+ # A chat line's player section ends at the first unescaped " say " or
22
+ # " say_team " that follows a <uid><steamid><team> tag, so tags or events
23
+ # typed into the message can't make the line look like it came from
24
+ # someone else, or like another event. Requiring the tag keeps names
25
+ # like 'x" say "' from turning that player's other lines into chat.
26
+ def self.regex_chat_player
27
+ @regex_chat_player ||= /"(?<player_section>(?:\\.|(?!" say(?:_team)? ").)*?<\d+><[^<>"]*><[^<>"]*>)"/
28
+ end
29
+
21
30
  def self.regex_target
22
31
  @regex_target ||= /"(?<target_section>.*?)"/
23
32
  end
@@ -65,6 +65,10 @@ module TF2LineParser
65
65
  Events::ConsoleSay, Events::Unknown
66
66
  ].freeze
67
67
 
68
+ CHAT_REGEX = /#{Events::Event.regex_time} #{Events::Event.regex_chat_player} (?<chat_type>say|say_team) "/
69
+ CHAT_TYPES = [Events::Say, Events::ConsoleSay].freeze
70
+ TEAM_CHAT_TYPES = [Events::TeamSay].freeze
71
+
68
72
  def initialize(line)
69
73
  @line = line
70
74
  end
@@ -89,6 +93,12 @@ module TF2LineParser
89
93
  private
90
94
 
91
95
  def find_candidate_types(line)
96
+ # Chat first: the message is player-controlled and may contain text
97
+ # that looks like other events (triggered "killed", connected, ...)
98
+ if line.include?('" say') && (chat = match_tidied(line, CHAT_REGEX))
99
+ return chat[:chat_type] == 'say' ? CHAT_TYPES : TEAM_CHAT_TYPES
100
+ end
101
+
92
102
  # Fast path: check for "triggered" which covers most events
93
103
  if line.include?('triggered "')
94
104
  start_idx = line.index('triggered "')
@@ -116,18 +126,19 @@ module TF2LineParser
116
126
 
117
127
  def try_parse_types(line, types)
118
128
  types.each do |type|
119
- begin
120
- match = line.match(type.regex)
121
- rescue ArgumentError
122
- # Handle invalid byte sequences by forcing UTF-8 encoding
123
- tidied_line = line.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: '')
124
- match = tidied_line.match(type.regex)
125
- end
129
+ match = match_tidied(line, type.regex)
126
130
  return type.new(*type.regex_results(match)) if match
127
131
  end
128
132
  nil
129
133
  end
130
134
 
135
+ def match_tidied(line, regex)
136
+ line.match(regex)
137
+ rescue ArgumentError
138
+ # Handle invalid byte sequences by forcing UTF-8 encoding
139
+ line.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: '').match(regex)
140
+ end
141
+
131
142
  def check_damage_subtypes(line)
132
143
  damage_attr_pos = line.index('(damage "')
133
144
  return [Events::Damage] unless damage_attr_pos
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TF2LineParser
4
- VERSION = '0.6.3'
4
+ VERSION = '0.6.5'
5
5
  end
@@ -855,6 +855,63 @@ module TF2LineParser
855
855
  expect(event.player.team).to eq('Red')
856
856
  expect(event.message).to eq('hello world')
857
857
  end
858
+
859
+ context 'with player tags or events typed into chat' do
860
+ let(:prefix) { 'L 10/17/2026 - 12:00:00: ' }
861
+
862
+ it 'credits say lines with a fake say_team tag in the message to the real speaker' do
863
+ event = parse(prefix + '"Troll<4><[U:1:111]><Blue>" say "x" "Owner<3><[U:1:999]><Red>" say_team "!end"')
864
+
865
+ expect(event).to be_a(Events::Say)
866
+ expect(event.player.steam_id).to eq('[U:1:111]')
867
+ expect(event.message).to eq('x" "Owner<3><[U:1:999]><Red>" say_team "!end')
868
+ end
869
+
870
+ it 'credits say_team lines with a fake say tag in the message to the real speaker' do
871
+ event = parse(prefix + '"Troll<4><[U:1:111]><Blue>" say_team "x" "Owner<3><[U:1:999]><Red>" say "!end"')
872
+
873
+ expect(event).to be_a(Events::TeamSay)
874
+ expect(event.player.steam_id).to eq('[U:1:111]')
875
+ end
876
+
877
+ it "doesn't parse chat mentioning a triggered connect as a connect" do
878
+ event = parse(prefix + '"Troll<4><[U:1:111]><Blue>" say "triggered "connected, address" "Victim<5><[U:1:555]><>" connected, address "6.6.6.6:27005"')
879
+
880
+ expect(event).to be_a(Events::Say)
881
+ expect(event.player.steam_id).to eq('[U:1:111]')
882
+ end
883
+
884
+ it "doesn't parse chat mentioning a triggered kill as a kill" do
885
+ event = parse(prefix + '"Troll<4><[U:1:111]><Blue>" say "triggered "killed" "Troll<4><[U:1:111]><Blue>" killed "Vic<6><[U:1:456]><Red>" with "scattergun" (attacker_position "0 0 0")"')
886
+
887
+ expect(event).to be_a(Events::Say)
888
+ end
889
+
890
+ it "doesn't parse chat containing a kill as a kill" do
891
+ event = parse(prefix + '"Troll<4><[U:1:111]><Blue>" say "x" "Troll<4><[U:1:111]><Blue>" killed "Vic<6><[U:1:456]><Red>" with "scattergun""')
892
+
893
+ expect(event).to be_a(Events::Say)
894
+ end
895
+
896
+ it "doesn't parse chat containing damage as damage" do
897
+ event = parse(prefix + '"Troll<4><[U:1:111]><Blue>" say "x" "Troll<4><[U:1:111]><Blue>" triggered "damage" against "Vic<6><[U:1:456]><Red>" (damage "500")"')
898
+
899
+ expect(event).to be_a(Events::Say)
900
+ end
901
+
902
+ it 'still parses kills of players whose name looks like chat' do
903
+ event = parse(prefix + '"x" say "<4><[U:1:111]><Blue>" killed "Vic<6><[U:1:456]><Red>" with "scattergun" (attacker_position "0 0 0") (victim_position "0 0 0")')
904
+
905
+ expect(event).to be_a(Events::Kill)
906
+ expect(event.player.steam_id).to eq('[U:1:111]')
907
+ end
908
+
909
+ it 'still parses kills of players whose name contains say' do
910
+ event = parse(prefix + '"say hi<4><[U:1:111]><Blue>" killed "Vic<6><[U:1:456]><Red>" with "scattergun" (attacker_position "0 0 0") (victim_position "0 0 0")')
911
+
912
+ expect(event).to be_a(Events::Kill)
913
+ end
914
+ end
858
915
  end
859
916
  end
860
917
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tf2_line_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arie
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-03-28 00:00:00.000000000 Z
10
+ date: 2026-10-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: coveralls_reborn