4141)
4242_FULL_COMMAND_TEXT = f"CASE WHEN { _SKILL } THEN substr({ _CAST } , 1, 80) ELSE substr(message, 1, { MESSAGE_MAX } ) END"
4343
44+ # RPCharacters' chat channels, as sent by command (`/looc hi`): alias → label,
45+ # from Main's plugins/RPCharacters/chat.yml. Plain chat goes to the player's
46+ # current channel, which CoreProtect does not record.
47+ CHANNELS = {
48+ "rp" : "RP" , "shout" : "Shout" , "yell" : "Yell" , "y" : "Yell" , "whisper" : "Whisper" , "wh" : "Whisper" ,
49+ "me" : "Emote" , "dm" : "DM" , "narrate" : "DM" , "scene" : "Scene" ,
50+ "ooc" : "OOC" , "looc" : "LOOC" , "gooc" : "GOOC" , "fooc" : "FOOC" , "pooc" : "POOC" , "rooc" : "ROOC" ,
51+ "admin" : "Admin" , "a" : "Admin" , "helper" : "Helper" , "h" : "Helper" ,
52+ }
53+ _FIRST_WORD = "lower(CASE WHEN instr(message, ' ') > 0 THEN substr(message, 1, instr(message, ' ') - 1) ELSE message END)"
54+ _CHANNEL = "{} IN ({})" .format (_FIRST_WORD , ", " .join (
55+ f"'/{ prefix } { alias } '" for alias in CHANNELS for prefix in ("" , "rpcharacters:" )))
56+
57+
58+ def channel_of (command : str | None ) -> str | None :
59+ """The chat channel a command speaks in (`/looc hi` → LOOC), or None."""
60+ word = (command or "" ).split (" " , 1 )[0 ].lower ().removeprefix ("/" ).removeprefix ("rpcharacters:" )
61+ return CHANNELS .get (word )
62+
4463
4564@dataclass (frozen = True )
4665class Source :
@@ -53,6 +72,8 @@ class Source:
5372 full_columns : str | None = None
5473 # Read only when the caller may read messages.
5574 sensitive : bool = False
75+ # Kinds used instead when the caller may read messages.
76+ full_kinds : dict [str , str ] | None = None
5677
5778
5879SOURCES = (
@@ -71,10 +92,15 @@ class Source:
7192 f"{ _SKILL } AS is_skill, { _TELEPORT } > 0 AS teleport, { _COMMAND_TEXT } AS text, 0 AS truncated" ,
7293 {"skill" : _SKILL , "command" : f"NOT { _SKILL } " },
7394 full_columns = (f"{ _SKILL } AS is_skill, { _TELEPORT } > 0 AS teleport, { _FULL_COMMAND_TEXT } AS text, "
74- f"NOT { _SKILL } AND length(message) > { MESSAGE_MAX } AS truncated" )),
95+ f"NOT { _SKILL } AND length(message) > { MESSAGE_MAX } AS truncated" ),
96+ # Those who may read chat see channel commands as chat (source 10), not here.
97+ full_kinds = {"skill" : _SKILL , "command" : f"NOT { _SKILL } AND NOT { _CHANNEL } " }),
7598 Source (8 , "session" , "co_session" , "action" , {"session" : "action IN (0, 1)" }),
7699 Source (9 , "chat" , "co_chat" , f"substr(message, 1, { MESSAGE_MAX } ) AS text, length(message) > { MESSAGE_MAX } AS truncated" ,
77100 {"chat" : "1" }, sensitive = True ),
101+ Source (10 , "channel" , "co_command" ,
102+ f"substr(message, 1, { MESSAGE_MAX } ) AS text, length(message) > { MESSAGE_MAX } AS truncated" ,
103+ {"chat" : _CHANNEL }, sensitive = True ),
78104)
79105# Kinds anyone with view_players may ask for, and the extra ones for view_player_messages.
80106KINDS = tuple (dict .fromkeys (kind for source in SOURCES if not source .sensitive for kind in source .kinds ))
@@ -190,7 +216,8 @@ def fetch(reader: Reader, maps: Maps, ids: list[int], kinds: tuple[str, ...], cu
190216 for source in SOURCES :
191217 if source .table not in maps .tables or (source .sensitive and not full_text ):
192218 continue
193- conditions = [cond for kind , cond in source .kinds .items () if kind in kinds ]
219+ source_kinds = (source .full_kinds if full_text else None ) or source .kinds
220+ conditions = [cond for kind , cond in source_kinds .items () if kind in kinds ]
194221 if not conditions :
195222 continue
196223 upper = _upper (source , cursor )
@@ -332,6 +359,7 @@ def _entry(source: Source, row: dict, maps: Maps, victims: dict) -> dict:
332359 "truncated" : False ,
333360 "rolled_back" : _rollback (row .get ("rolled_back" )),
334361 "target_info" : None ,
362+ "channel" : None ,
335363 }
336364 if source .name == "block" :
337365 if action == 3 :
@@ -372,10 +400,15 @@ def _entry(source: Source, row: dict, maps: Maps, victims: dict) -> dict:
372400 entry .update (kind = "skill" , verb = "teleported with" if row ["teleport" ] else "cast" ,
373401 target = (row ["text" ] or "" ).strip () or "a skill" )
374402 else :
375- entry .update (kind = "command" , verb = "ran" , target = row ["text" ] or "/" , truncated = bool (row ["truncated" ]))
403+ # Without full text this is just `/looc`: which channel, never what was said.
404+ entry .update (kind = "command" , verb = "ran" , target = row ["text" ] or "/" , truncated = bool (row ["truncated" ]),
405+ channel = channel_of (row ["text" ]))
376406 elif source .name == "chat" :
377407 entry .update (kind = "chat" , verb = "said" , target = None , message = row ["text" ] or "" ,
378408 truncated = bool (row ["truncated" ]))
409+ elif source .name == "channel" :
410+ entry .update (kind = "chat" , verb = "said" , target = None , channel = channel_of (row ["text" ]),
411+ message = (row ["text" ] or "" ).partition (" " )[2 ].strip (), truncated = bool (row ["truncated" ]))
379412 else :
380413 entry .update (kind = "session" , verb = "logged in" if action == 1 else "logged out" , target = None )
381414 return entry
0 commit comments