This commit is contained in:
xbl
2025-12-16 16:42:06 +01:00
parent d60627e2d2
commit 324d36746a

View File

@ -15,6 +15,10 @@ import logging
logger = logging.getLogger('logger') logger = logging.getLogger('logger')
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
# Separate logger for all JSON events when --json flag is used
all_json_logger = logging.getLogger('all_json')
all_json_logger.setLevel(logging.DEBUG)
# Separate logger for unknown JSON events # Separate logger for unknown JSON events
unknown_json_logger = logging.getLogger('unknown_json') unknown_json_logger = logging.getLogger('unknown_json')
unknown_json_logger.setLevel(logging.DEBUG) unknown_json_logger.setLevel(logging.DEBUG)
@ -246,12 +250,80 @@ def get_team_color(player_name):
'SPECTATOR': '^3(SPEC)^7' 'SPECTATOR': '^3(SPEC)^7'
}.get(team, '') }.get(team, '')
def format_powerup_message(msg_str):
"""Format powerup pickup and kill messages with colors"""
if msg_str.startswith("broadcast:"):
msg_str = msg_str[11:].strip()
# Check for powerup pickup: "PlayerName got the PowerupName!"
pickup_match = re.match(r'^(.+?)\s+got the\s+(.+?)!', msg_str)
if pickup_match:
player_name = pickup_match.group(1).strip()
powerup_name = pickup_match.group(2).strip()
# Get team color for player
player_clean = re.sub(r'\^\d', '', player_name)
team_prefix = get_team_color(player_clean)
# Color code powerups
powerup_colors = {
'Quad Damage': '^5Quad Damage^7',
'Battle Suit': '^3Battle Suit^7',
'Regeneration': '^1Regeneration^7',
'Haste': '^3Haste^7',
'Invisibility': '^5Invisibility^7',
'Flight': '^5Flight^7',
'Medkit': '^1Medkit^7',
'MegaHealth': '^4MegaHealth^7'
}
colored_powerup = powerup_colors.get(powerup_name, f'^6{powerup_name}^7')
return f"{team_prefix}{player_name} ^7got the {colored_powerup}!\n"
# Check for powerup carrier kill: "PlayerName killed the PowerupName carrier!"
carrier_match = re.match(r'^(.+?)\s+killed the\s+(.+?)\s+carrier!', msg_str)
if carrier_match:
player_name = carrier_match.group(1).strip()
powerup_name = carrier_match.group(2).strip()
# Get team color for player
player_clean = re.sub(r'\^\d', '', player_name)
team_prefix = get_team_color(player_clean)
# Color code powerups (same as above)
powerup_colors = {
'Quad Damage': '^5Quad Damage^7',
'Battle Suit': '^3Battle Suit^7',
'Regeneration': '^1Regeneration^7',
'Haste': '^3Haste^7',
'Invisibility': '^5Invisibility^7',
'Flight': '^5Flight^7',
'Medkit': '^1Medkit^7',
'MegaHealth': '^4MegaHealth^7'
}
colored_powerup = powerup_colors.get(powerup_name, f'^6{powerup_name}^7')
return f"{team_prefix}{player_name} ^7killed the {colored_powerup} ^7carrier!\n"
# Not a powerup message
return None
def ParseGameEvent(message): def ParseGameEvent(message):
"""Parse JSON game events and return formatted message""" """Parse JSON game events and return formatted message"""
global current_gametype, recent_events global current_gametype, recent_events
try: try:
jObject = json.loads(message) jObject = json.loads(message)
# Log ALL JSON events to file if --json flag was provided
if all_json_logger.handlers:
all_json_logger.info('JSON Event received:')
all_json_logger.info(json.dumps(jObject, indent=2))
all_json_logger.info('---')
if 'TYPE' not in jObject or 'DATA' not in jObject: if 'TYPE' not in jObject or 'DATA' not in jObject:
logger.debug('JSON missing TYPE or DATA fields') logger.debug('JSON missing TYPE or DATA fields')
return None return None
@ -279,7 +351,7 @@ def ParseGameEvent(message):
recent_events.pop(0) recent_events.pop(0)
warmup = data.get('WARMUP', False) warmup = data.get('WARMUP', False)
warmup_suffix = " ^7(^3warmup^7)" if warmup else "" warmup_suffix = " ^3(warmup)" if warmup else ""
if event_type == 'PLAYER_SWITCHTEAM': if event_type == 'PLAYER_SWITCHTEAM':
if 'KILLER' not in data: if 'KILLER' not in data:
@ -366,7 +438,7 @@ def ParseGameEvent(message):
} }
weapon_name = weapon_names.get(weapon, 'the %s' % weapon) weapon_name = weapon_names.get(weapon, 'the %s' % weapon)
return "%s%s ^8fragged^7 %s%s ^7with %s%s\n" % ( return "%s%s ^7fragged^7 %s%s ^7with %s%s\n" % (
killer_team_prefix, killer_name, killer_team_prefix, killer_name,
victim_team_prefix, victim_name, victim_team_prefix, victim_name,
weapon_name, warmup_suffix weapon_name, warmup_suffix
@ -459,6 +531,7 @@ def InitWindows(screen, args):
curses.initscr() curses.initscr()
screen.nodelay(1) screen.nodelay(1)
curses.start_color() curses.start_color()
curses.use_default_colors()
curses.cbreak() curses.cbreak()
curses.setsyx(-1, -1) curses.setsyx(-1, -1)
screen.addstr("Quake Live rcon: %s" % args.host) screen.addstr("Quake Live rcon: %s" % args.host)
@ -514,6 +587,7 @@ def main(screen):
parser.add_argument( '--identity', default = uuid.uuid1().hex, help = 'Specify the socket identity. Random UUID used by default' ) parser.add_argument( '--identity', default = uuid.uuid1().hex, help = 'Specify the socket identity. Random UUID used by default' )
parser.add_argument( '-v', '--verbose', action='count', default=0, help = 'Increase verbosity (use -v for INFO, -vv for DEBUG)' ) parser.add_argument( '-v', '--verbose', action='count', default=0, help = 'Increase verbosity (use -v for INFO, -vv for DEBUG)' )
parser.add_argument( '--unknown-log', default='unknown_events.log', help = 'File to log unknown JSON events. Defaults to unknown_events.log' ) parser.add_argument( '--unknown-log', default='unknown_events.log', help = 'File to log unknown JSON events. Defaults to unknown_events.log' )
parser.add_argument('--json', '-j', dest='json_log', default=None, help='File to log all JSON events. If specified, all JSON messages from server will be captured')
args = parser.parse_args() args = parser.parse_args()
# Set logging level based on verbosity # Set logging level based on verbosity
@ -689,6 +763,15 @@ def main(screen):
stats_connected = True stats_connected = True
PrintMessageFormatted(output_window, "Stats stream connected - ready for game events\n") PrintMessageFormatted(output_window, "Stats stream connected - ready for game events\n")
# Set up file handler for all JSON events if --json flag is provided
if args.json_log:
json_file_handler = logging.FileHandler(args.json_log, mode='a')
json_file_formatter = logging.Formatter('%(asctime)s - %(message)s', '%Y-%m-%d %H:%M:%S')
json_file_handler.setFormatter(json_file_formatter)
all_json_logger.addHandler(json_file_handler)
all_json_logger.propagate = False
PrintMessageFormatted(output_window, f"*** JSON capture enabled: {args.json_log} ***\n")
except Exception as e: except Exception as e:
timestamp = time.strftime('%H:%M:%S') timestamp = time.strftime('%H:%M:%S')
PrintMessageFormatted(output_window, f"^1[^7{timestamp}^1] Error: Stats connection failed: {e}^7\n", add_timestamp=False) PrintMessageFormatted(output_window, f"^1[^7{timestamp}^1] Error: Stats connection failed: {e}^7\n", add_timestamp=False)
@ -728,6 +811,13 @@ def main(screen):
# It's JSON but we didn't parse it - already logged to file # It's JSON but we didn't parse it - already logged to file
logger.debug('Unparsed JSON event') logger.debug('Unparsed JSON event')
else: else:
# Check for powerup messages
formatted_powerup = format_powerup_message(msg_str)
if formatted_powerup:
PrintMessageFormatted(output_window, formatted_powerup)
curses.setsyx(y,x)
curses.doupdate()
continue
# Not JSON - check if it's a bot debug message (filter unless verbose) # Not JSON - check if it's a bot debug message (filter unless verbose)
is_bot_debug = ' entered ' in msg_str and (' seek ' in msg_str or ' battle ' in msg_str or ' chase' in msg_str or ' fight' in msg_str) is_bot_debug = ' entered ' in msg_str and (' seek ' in msg_str or ' battle ' in msg_str or ' chase' in msg_str or ' fight' in msg_str)