tgchat: Hotfixes, Message Annotations (#52947)

This PR changes the message pipeline a little bit to support list-based messages, which can be annotated with custom data. Function signature of to_chat was slightly changed as well:

// Plain text message
to_chat(client,
  type = MESSAGE_TYPE_INFO,
  text = "foo")

// HTML message
to_chat(client,
  type = MESSAGE_TYPE_INFO,
  html = "<span class='notice'>foo</span>")

Old to_chat format is still supported, but handle_whitespace, trailing_newline and confidential flags have no effect. confidential flag could still be revived though, if there is enough merit in it, for example to filter out confidential messages when saving a chat log.

The reason for using /list and not /datum, is because lists are plain faster, and there are minimal data transformations - these lists are fed directly to json_encode and sent to tgchat.

Plain text messages do not need to be HTML-escaped, which makes them safer and more performant than HTML messages. Plain text messages can be made interactive (or formatted with CSS) by custom-handling them in javscript based on message type and annotations.

It would be impossible to annotate every single message in the game (at the moment of writing, there are 9447 to_chat calls in the code), but it could be done selectively, for only those messages that are hard to classify by span classes (and there are still A LOT of them).
Please annotate more messages. Thank you.

    Fixes #52943
    Fixes #52908
    Fixes #52816

Changelog

cl
add: tgchat: Unread message count is now smarter and won't increase on other tabs if you have already read the same message in the active tab.
add: tgchat: Admin PMs are now properly annotated and can be filtered into separate tabs.
fix: tgchat: Fix: Highlighted message overlay no longer blocks clicks. Clicking a highlighted (F) link should work as it should.
fix: tgui: Fixed NTOS bluescreen due to calling .includes() on a stylesheet href which could be null on certain browsers.
code: tgchat: Chat schema bumped to version 5. All chat-related settings were reset to avoid breakage.
/cl
This commit is contained in:
Aleksej Komarov
2020-08-17 10:38:38 +12:00
committed by GitHub
parent da18bb4dd8
commit 64dcf44cc6
33 changed files with 440 additions and 228 deletions
+10 -4
View File
@@ -1,12 +1,18 @@
////////////////////////////////
/proc/message_admins(msg)
msg = "<span class=\"admin filter_adminlog\"><span class=\"prefix\">ADMIN LOG:</span> <span class=\"message linkify\">[msg]</span></span>"
to_chat(GLOB.admins, msg, confidential = TRUE)
msg = "<span class=\"admin\"><span class=\"prefix\">ADMIN LOG:</span> <span class=\"message linkify\">[msg]</span></span>"
to_chat(GLOB.admins,
type = MESSAGE_TYPE_ADMINLOG,
html = msg,
confidential = TRUE)
/proc/relay_msg_admins(msg)
msg = "<span class=\"admin filter_adminlog\"><span class=\"prefix\">RELAY:</span> <span class=\"message linkify\">[msg]</span></span>"
to_chat(GLOB.admins, msg, confidential = TRUE)
msg = "<span class=\"admin\"><span class=\"prefix\">RELAY:</span> <span class=\"message linkify\">[msg]</span></span>"
to_chat(GLOB.admins,
type = MESSAGE_TYPE_ADMINLOG,
html = msg,
confidential = TRUE)
///////////////////////////////////////////////////////////////////////////////////////////////Panels
-5
View File
@@ -134,8 +134,3 @@
/proc/log_ipintel(text)
log_game("IPINTEL: [text]")
debug_admins("IPINTEL: [text]")
+9 -3
View File
@@ -267,10 +267,16 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(X.prefs.toggles & SOUND_ADMINHELP)
SEND_SOUND(X, sound('sound/effects/adminhelp.ogg'))
window_flash(X, ignorepref = TRUE)
to_chat(X, admin_msg, confidential = TRUE)
to_chat(X,
type = MESSAGE_TYPE_ADMINPM,
html = admin_msg,
confidential = TRUE)
//show it to the person adminhelping too
to_chat(initiator, "<span class='adminnotice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
to_chat(initiator,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='adminnotice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>",
confidential = TRUE)
SSblackbox.LogAhelp(id, "Ticket Opened", msg, null, initiator.ckey)
//Reopen a closed ticket
@@ -608,7 +614,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if (!server_url)
CRASH("Invalid cross comms config: [server_name]")
world.Export("[server_url]?[list2params(message)]")
/proc/tgsadminwho()
var/list/message = list("Admins: ")
+117 -31
View File
@@ -5,9 +5,12 @@
set category = null
set name = "Admin PM Mob"
if(!holder)
to_chat(src, "<span class='danger'>Error: Admin-PM-Context: Only administrators may use this command.</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM-Context: Only administrators may use this command.</span>",
confidential = TRUE)
return
if( !ismob(M) || !M.client )
if(!ismob(M) || !M.client)
return
cmd_admin_pm(M.client,null)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin PM Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -17,7 +20,10 @@
set category = "Admin"
set name = "Admin PM"
if(!holder)
to_chat(src, "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>",
confidential = TRUE)
return
var/list/client/targets[0]
for(var/client/T)
@@ -36,7 +42,10 @@
/client/proc/cmd_ahelp_reply(whom)
if(prefs.muted & MUTE_ADMINHELP)
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>",
confidential = TRUE)
return
var/client/C
if(istext(whom))
@@ -47,7 +56,10 @@
C = whom
if(!C)
if(holder)
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM: Client not found.</span>",
confidential = TRUE)
return
var/datum/admin_help/AH = C.current_ticket
@@ -62,8 +74,14 @@
if(GLOB.directory[AH.initiator_ckey]) // Client has reconnected, lets try to recover
whom = GLOB.directory[AH.initiator_ckey]
else
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
to_chat(src, "<span class='danger'><b>Message not sent:</b></span><br>[msg]", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM: Client not found.</span>",
confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'><b>Message not sent:</b></span><br>[msg]",
confidential = TRUE)
AH.AddInteraction("<b>No client found, message not sent:</b><br>[msg]")
return
cmd_admin_pm(whom, msg)
@@ -72,12 +90,21 @@
//Fetching a message if needed. src is the sender and C is the target client
/client/proc/cmd_admin_pm(whom, msg)
if(prefs.muted & MUTE_ADMINHELP)
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>",
confidential = TRUE)
return
if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo
to_chat(src, "<span class='danger'>You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.</span>", confidential = TRUE)
to_chat(src, "<span class='notice'>Message: [msg]</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.</span>",
confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='notice'>Message: [msg]</span>",
confidential = TRUE)
return
var/client/recipient
@@ -95,7 +122,10 @@
recipient = whom
if(!recipient)
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM: Client not found.</span>",
confidential = TRUE)
return
recipient_ckey = recipient.ckey
@@ -110,7 +140,10 @@
if(!msg)
return
if(holder)
to_chat(src, "<span class='danger'>Error: Use the admin IRC/Discord channel, nerd.</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Use the admin IRC/Discord channel, nerd.</span>",
confidential = TRUE)
return
@@ -127,8 +160,14 @@
recipient = GLOB.directory[recipient_ckey]
else
if(holder)
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
to_chat(src, "<span class='danger'><b>Message not sent:</b></span><br>[msg]", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM: Client not found.</span>",
confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'><b>Message not sent:</b></span><br>[msg]",
confidential = TRUE)
if(recipient_ticket)
recipient_ticket.AddInteraction("<b>No client found, message not sent:</b><br>[msg]")
return
@@ -138,7 +177,10 @@
if(prefs.muted & MUTE_ADMINHELP)
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>",
confidential = TRUE)
return
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
@@ -158,7 +200,10 @@
var/keywordparsedmsg = keywords_lookup(msg)
if(external)
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>",
confidential = TRUE)
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)]</b> to <i>External</i>: [keywordparsedmsg]</font>")
externalreplyamount--
send2adminchat("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
@@ -168,9 +213,14 @@
badmin = TRUE
if(recipient.holder && !badmin)
if(holder)
to_chat(recipient, "<span class='danger'>Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>", confidential = TRUE)
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>", confidential = TRUE)
to_chat(recipient,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>",
confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>",
confidential = TRUE)
//omg this is dumb, just fill in both their tickets
var/interaction_message = "<font color='purple'>PM from-<b>[key_name(src, recipient, 1)]</b> to-<b>[key_name(recipient, src, 1)]</b>: [keywordparsedmsg]</font>"
admin_ticket_log(src, interaction_message)
@@ -180,8 +230,14 @@
else //recipient is an admin but sender is not
var/replymsg = "Reply PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span>"
admin_ticket_log(src, "<font color='red'>[replymsg]</font>")
to_chat(recipient, "<span class='danger'>[replymsg]</span>", confidential = TRUE)
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
to_chat(recipient,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>[replymsg]</span>",
confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>",
confidential = TRUE)
SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
//play the receiving admin the adminhelp sound (if they have them enabled)
@@ -196,10 +252,22 @@
already_logged = TRUE
SSblackbox.LogAhelp(recipient.current_ticket.id, "Ticket Opened", msg, recipient.ckey, src.ckey)
to_chat(recipient, "<font color='red' size='4'><b>-- Administrator private message --</b></font>", confidential = TRUE)
to_chat(recipient, "<span class='adminsay'>Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
to_chat(recipient, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>", confidential = TRUE)
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
to_chat(recipient,
type = MESSAGE_TYPE_ADMINPM,
html = "<font color='red' size='4'><b>-- Administrator private message --</b></font>",
confidential = TRUE)
to_chat(recipient,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='adminsay'>Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span></span>",
confidential = TRUE)
to_chat(recipient,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>",
confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span></span>",
confidential = TRUE)
admin_ticket_log(recipient, "<font color='purple'>PM From [key_name_admin(src)]: [keywordparsedmsg]</font>")
@@ -215,20 +283,29 @@
INVOKE_ASYNC(src, .proc/popup_admin_pm, recipient, msg)
else //neither are admins
to_chat(src, "<span class='danger'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</span>", confidential = TRUE)
to_chat(src,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='danger'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</span>",
confidential = TRUE)
return
if(external)
log_admin_private("PM: [key_name(src)]->External: [rawmsg]")
for(var/client/X in GLOB.admins)
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]-&gt;External:</B> [keywordparsedmsg]</span>", confidential = TRUE)
to_chat(X,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='notice'><B>PM: [key_name(src, X, 0)]-&gt;External:</B> [keywordparsedmsg]</span>",
confidential = TRUE)
else
window_flash(recipient, ignorepref = TRUE)
log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
//we don't use message_admins here because the sender/receiver might get it too
for(var/client/X in GLOB.admins)
if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]-&gt;[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]</span>" , confidential = TRUE)
to_chat(X,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='notice'><B>PM: [key_name(src, X, 0)]-&gt;[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]</span>" ,
confidential = TRUE)
/client/proc/popup_admin_pm(client/recipient, msg)
var/sender = src
@@ -318,9 +395,18 @@
log_admin_private("External PM: [sender] -> [key_name(C)] : [msg]")
msg = emoji_parse(msg)
to_chat(C, "<font color='red' size='4'><b>-- Administrator private message --</b></font>", confidential = TRUE)
to_chat(C, "<span class='adminsay'>Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]</span>", confidential = TRUE)
to_chat(C, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>", confidential = TRUE)
to_chat(C,
type = MESSAGE_TYPE_ADMINPM,
html = "<font color='red' size='4'><b>-- Administrator private message --</b></font>",
confidential = TRUE)
to_chat(C,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='adminsay'>Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]</span>",
confidential = TRUE)
to_chat(C,
type = MESSAGE_TYPE_ADMINPM,
html = "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>",
confidential = TRUE)
admin_ticket_log(C, "<font color='purple'>PM From [tgs_tagged]: [msg]</font>")
+5 -1
View File
@@ -13,7 +13,11 @@
msg = keywords_lookup(msg)
var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "<font color=[prefs.asaycolor]>" : "<font color='#FF4500'>"
msg = "<span class='adminsay'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> [ADMIN_FLW(mob)]: [custom_asay_color]<span class='message linkify'>[msg]</span></span>[custom_asay_color ? "</font>":null]"
to_chat(GLOB.admins, msg, confidential = TRUE)
to_chat(GLOB.admins, list(
"type" = MESSAGE_TYPE_ADMINCHAT,
"html" = msg,
"confidential" = TRUE,
))
SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!