mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-14 03:32:00 +00:00
* Give admins the ability to put a popup notice for players in tickets (#61010) Admins can now give players a popup if they are not responding to tickets. Popup is cleared when player replies or ticket is resolved/closed. As more and more of the chat screen is made irrelevant, new players read it less and less. This means that a lot of new players are ignoring ahelps, which is something I've encountered myself. * Give admins the ability to put a popup notice for players in tickets Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
444 lines
16 KiB
Plaintext
444 lines
16 KiB
Plaintext
#define EXTERNALREPLYCOUNT 2
|
|
|
|
//allows right clicking mobs to send an admin PM to their client, forwards the selected mob's client to cmd_admin_pm
|
|
/client/proc/cmd_admin_pm_context(mob/M in GLOB.mob_list)
|
|
set category = null
|
|
set name = "Admin PM Mob"
|
|
if(!holder)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM-Context: Only administrators may use this command."),
|
|
confidential = TRUE)
|
|
return
|
|
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!
|
|
|
|
//shows a list of clients we could send PMs to, then forwards our choice to cmd_admin_pm
|
|
/client/proc/cmd_admin_pm_panel()
|
|
set category = "Admin"
|
|
set name = "Admin PM"
|
|
if(!holder)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM-Panel: Only administrators may use this command."),
|
|
confidential = TRUE)
|
|
return
|
|
var/list/targets = list()
|
|
for(var/client/client as anything in GLOB.clients)
|
|
if(client.mob)
|
|
if(isnewplayer(client.mob))
|
|
targets["(New Player) - [client]"] = client
|
|
else if(isobserver(client.mob))
|
|
targets["[client.mob.name](Ghost) - [client]"] = client
|
|
else
|
|
targets["[client.mob.real_name](as [client.mob.name]) - [client]"] = client
|
|
else
|
|
targets["(No Mob) - [client]"] = client
|
|
var/target = input(src,"To whom shall we send a message?","Admin PM",null) as null|anything in sortList(targets)
|
|
cmd_admin_pm(targets[target],null)
|
|
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin PM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
|
|
|
/client/proc/cmd_ahelp_reply(whom)
|
|
if(prefs.muted & MUTE_ADMINHELP)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM: You are unable to use admin PM-s (muted)."),
|
|
confidential = TRUE)
|
|
return
|
|
var/client/C
|
|
if(istext(whom))
|
|
if(whom[1] == "@")
|
|
whom = findStealthKey(whom)
|
|
C = GLOB.directory[whom]
|
|
else if(istype(whom, /client))
|
|
C = whom
|
|
if(!C)
|
|
if(holder)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM: Client not found."),
|
|
confidential = TRUE)
|
|
return
|
|
|
|
var/datum/admin_help/AH = C.current_ticket
|
|
|
|
if(AH)
|
|
message_admins("[key_name_admin(src)] has started replying to [key_name_admin(C, 0, 0)]'s admin help.")
|
|
var/msg = input(src,"Message:", "Private message to [C.holder?.fakekey ? "an Administrator" : key_name(C, 0, 0)].") as message|null
|
|
if (!msg)
|
|
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name_admin(C, 0, 0)]'s admin help.")
|
|
return
|
|
if(!C) //We lost the client during input, disconnected or relogged.
|
|
if(GLOB.directory[AH.initiator_ckey]) // Client has reconnected, lets try to recover
|
|
whom = GLOB.directory[AH.initiator_ckey]
|
|
else
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM: Client not found."),
|
|
confidential = TRUE)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = "[span_danger("<b>Message not sent:</b>")]<br>[msg]",
|
|
confidential = TRUE)
|
|
AH.AddInteraction("<b>No client found, message not sent:</b><br>[msg]")
|
|
return
|
|
cmd_admin_pm(whom, msg)
|
|
|
|
//takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM.
|
|
//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,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM: You are unable to use admin PM-s (muted)."),
|
|
confidential = TRUE)
|
|
return
|
|
|
|
if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be."),
|
|
confidential = TRUE)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_notice("Message: [msg]"),
|
|
confidential = TRUE)
|
|
return
|
|
|
|
var/client/recipient
|
|
var/recipient_ckey // Stored in case client is deleted between this and after the message is input
|
|
var/datum/admin_help/recipient_ticket // Stored in case client is deleted between this and after the message is input
|
|
var/external = 0
|
|
if(istext(whom))
|
|
if(whom[1] == "@")
|
|
whom = findStealthKey(whom)
|
|
if(whom == "IRCKEY")
|
|
external = 1
|
|
else
|
|
recipient = GLOB.directory[whom]
|
|
else if(istype(whom, /client))
|
|
recipient = whom
|
|
|
|
if(!recipient)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM: Client not found."),
|
|
confidential = TRUE)
|
|
return
|
|
|
|
recipient_ckey = recipient.ckey
|
|
recipient_ticket = recipient.current_ticket
|
|
|
|
if(external)
|
|
if(!externalreplyamount) //to prevent people from spamming irc/discord
|
|
return
|
|
if(!msg)
|
|
msg = input(src,"Message:", "Private message to Administrator") as message|null
|
|
|
|
if(!msg)
|
|
return
|
|
if(holder)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Use the admin IRC/Discord channel, nerd."),
|
|
confidential = TRUE)
|
|
return
|
|
|
|
|
|
else
|
|
//get message text, limit it's length.and clean/escape html
|
|
if(!msg)
|
|
msg = input(src,"Message:", "Private message to [recipient.holder?.fakekey ? "an Administrator" : key_name(recipient, 0, 0)].") as message|null
|
|
msg = trim(msg)
|
|
if(!msg)
|
|
return
|
|
|
|
if(!recipient)
|
|
if(GLOB.directory[recipient_ckey]) // Client has reconnected, lets try to recover
|
|
recipient = GLOB.directory[recipient_ckey]
|
|
else
|
|
if(holder)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM: Client not found."),
|
|
confidential = TRUE)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = "[span_danger("<b>Message not sent:</b>")]<br>[msg]",
|
|
confidential = TRUE)
|
|
if(recipient_ticket)
|
|
recipient_ticket.AddInteraction("<b>No client found, message not sent:</b><br>[msg]")
|
|
return
|
|
else
|
|
current_ticket.MessageNoRecipient(msg)
|
|
return
|
|
|
|
|
|
if(prefs.muted & MUTE_ADMINHELP)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM: You are unable to use admin PM-s (muted)."),
|
|
confidential = TRUE)
|
|
return
|
|
|
|
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
|
|
return
|
|
|
|
//clean the message if it's not sent by a high-rank admin
|
|
if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots
|
|
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
|
|
if(!msg)
|
|
return
|
|
|
|
var/rawmsg = msg
|
|
|
|
if(holder)
|
|
msg = emoji_parse(msg)
|
|
|
|
var/keywordparsedmsg = keywords_lookup(msg)
|
|
|
|
if(external)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_notice("PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</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>")
|
|
AH.AddInteractionPlayer("<font color='red'>Reply PM from-<b>[key_name(src, TRUE, FALSE)]</b> to <i>External</i>: [keywordparsedmsg]</font>") // SKYRAT EDIT ADDITION -- Player ticket viewing
|
|
externalreplyamount--
|
|
send2adminchat("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
|
|
else
|
|
var/badmin = FALSE //Lets figure out if an admin is getting bwoinked.
|
|
if(holder && recipient.holder && !current_ticket) //Both are admins, and this is not a reply to our own ticket.
|
|
badmin = TRUE
|
|
if(recipient.holder && !badmin)
|
|
SEND_SIGNAL(current_ticket, COMSIG_ADMIN_HELP_REPLIED)
|
|
|
|
if(holder)
|
|
to_chat(recipient,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span>"),
|
|
confidential = TRUE)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_notice("Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</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) // SKYRAT EDIT ORIGINAL
|
|
admin_ticket_log(src, interaction_message, FALSE) // SKYRAT EDIT CHANGE -- Player ticket viewing
|
|
if(recipient != src)//reeee
|
|
// admin_ticket_log(recipient, interaction_message) // SKYRAT EDIT ORIGINAL
|
|
admin_ticket_log(recipient, interaction_message, FALSE) // SKYRAT EDIT CHANGE -- Player ticket viewing
|
|
SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
|
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>") // SKYRAT EDIT ORIGINAL
|
|
admin_ticket_log(src, "<font color='red'>[replymsg]</font>", FALSE) // SKYRAT EDIT CHANGE -- Player ticket viewing
|
|
to_chat(recipient,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("[replymsg]"),
|
|
confidential = TRUE)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_notice("PM to-<b>Admins</b>: <span class='linkify'>[msg]</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)
|
|
if(recipient.prefs.toggles & SOUND_ADMINHELP)
|
|
SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg'))
|
|
else
|
|
if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT
|
|
var/already_logged = FALSE
|
|
if(!recipient.current_ticket)
|
|
//new /datum/admin_help(msg, recipient, TRUE) //ORIGINAL
|
|
new /datum/admin_help(msg, recipient, TRUE, src) //SKYRAT EDIT CHANGE - ADMIN
|
|
already_logged = TRUE
|
|
SSblackbox.LogAhelp(recipient.current_ticket.id, "Ticket Opened", msg, recipient.ckey, src.ckey)
|
|
|
|
//SKYRAT EDIT ADDITION BEGIN - ADMIN
|
|
if(recipient.current_ticket.handler)
|
|
if(recipient.current_ticket.handler != usr.ckey)
|
|
var/response = tgui_alert(usr, "This ticket is already being handled by [recipient.current_ticket.handler]. Do you want to continue?", "Ticket already assigned", list("Yes", "No"))
|
|
|
|
if(response == "No")
|
|
return
|
|
else
|
|
recipient.current_ticket.HandleIssue()
|
|
//SKYRAT EDIT ADDITION END
|
|
|
|
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_adminsay("Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span>"),
|
|
confidential = TRUE)
|
|
to_chat(recipient,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_adminsay("<i>Click on the administrator's name to reply.</i>"),
|
|
confidential = TRUE)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_notice("Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span>"),
|
|
confidential = TRUE)
|
|
|
|
// admin_ticket_log(recipient, "<font color='purple'>PM From [key_name_admin(src)]: [keywordparsedmsg]</font>") // SKYRAT EDIT ORIGINAL
|
|
admin_ticket_log(recipient, "<font color='purple'>PM From [key_name_admin(src, FALSE)]: [keywordparsedmsg]</font>", FALSE) // SKYRAT EDIT CHANGE -- Player ticket viewing
|
|
|
|
if(!already_logged) //Reply to an existing ticket
|
|
SSblackbox.LogAhelp(recipient.current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
|
|
|
//always play non-admin recipients the adminhelp sound
|
|
SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg'))
|
|
|
|
else //neither are admins
|
|
if(!current_ticket)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_danger("Error: Admin-PM: Non-admin to non-admin PM communication is forbidden."),
|
|
confidential = TRUE)
|
|
to_chat(src,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = "[span_danger("<b>Message not sent:</b>")]<br>[msg]",
|
|
confidential = TRUE)
|
|
return
|
|
current_ticket.MessageNoRecipient(msg)
|
|
|
|
if(external)
|
|
log_admin_private("PM: [key_name(src)]->External: [rawmsg]")
|
|
for(var/client/X in GLOB.admins)
|
|
to_chat(X,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_notice("<B>PM: [key_name(src, X, 0)]->External:</B> [keywordparsedmsg]"),
|
|
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,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_notice("<B>PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]") ,
|
|
confidential = TRUE)
|
|
|
|
#define TGS_AHELP_USAGE "Usage: ticket <close|resolve|icissue|reject|reopen \[ticket #\]|list>"
|
|
/proc/TgsPm(target,msg,sender)
|
|
target = ckey(target)
|
|
var/client/C = GLOB.directory[target]
|
|
|
|
var/datum/admin_help/ticket = C ? C.current_ticket : GLOB.ahelp_tickets.CKey2ActiveTicket(target)
|
|
var/compliant_msg = trim(lowertext(msg))
|
|
var/tgs_tagged = "[sender](TGS/External)"
|
|
var/list/splits = splittext(compliant_msg, " ")
|
|
if(splits.len && splits[1] == "ticket")
|
|
if(splits.len < 2)
|
|
return TGS_AHELP_USAGE
|
|
switch(splits[2])
|
|
if("close")
|
|
if(ticket)
|
|
ticket.Close(tgs_tagged)
|
|
return "Ticket #[ticket.id] successfully closed"
|
|
if("resolve")
|
|
if(ticket)
|
|
ticket.Resolve(tgs_tagged)
|
|
return "Ticket #[ticket.id] successfully resolved"
|
|
if("icissue")
|
|
if(ticket)
|
|
ticket.ICIssue(tgs_tagged)
|
|
return "Ticket #[ticket.id] successfully marked as IC issue"
|
|
if("reject")
|
|
if(ticket)
|
|
ticket.Reject(tgs_tagged)
|
|
return "Ticket #[ticket.id] successfully rejected"
|
|
if("reopen")
|
|
if(ticket)
|
|
return "Error: [target] already has ticket #[ticket.id] open"
|
|
var/fail = splits.len < 3 ? null : -1
|
|
if(!isnull(fail))
|
|
fail = text2num(splits[3])
|
|
if(isnull(fail))
|
|
return "Error: No/Invalid ticket id specified. [TGS_AHELP_USAGE]"
|
|
var/datum/admin_help/AH = GLOB.ahelp_tickets.TicketByID(fail)
|
|
if(!AH)
|
|
return "Error: Ticket #[fail] not found"
|
|
if(AH.initiator_ckey != target)
|
|
return "Error: Ticket #[fail] belongs to [AH.initiator_ckey]"
|
|
AH.Reopen()
|
|
return "Ticket #[ticket.id] successfully reopened"
|
|
if("list")
|
|
var/list/tickets = GLOB.ahelp_tickets.TicketsByCKey(target)
|
|
if(!tickets.len)
|
|
return "None"
|
|
. = ""
|
|
for(var/I in tickets)
|
|
var/datum/admin_help/AH = I
|
|
if(.)
|
|
. += ", "
|
|
if(AH == ticket)
|
|
. += "Active: "
|
|
. += "#[AH.id]"
|
|
return
|
|
else
|
|
return TGS_AHELP_USAGE
|
|
return "Error: Ticket could not be found"
|
|
|
|
var/static/stealthkey
|
|
var/adminname = CONFIG_GET(flag/show_irc_name) ? tgs_tagged : "Administrator"
|
|
|
|
if(!C)
|
|
return "Error: No client"
|
|
|
|
if(!stealthkey)
|
|
stealthkey = GenTgsStealthKey()
|
|
|
|
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
|
|
if(!msg)
|
|
return "Error: No message"
|
|
|
|
message_admins("External message from [sender] to [key_name_admin(C)] : [msg]")
|
|
log_admin_private("External PM: [sender] -> [key_name(C)] : [msg]")
|
|
msg = emoji_parse(msg)
|
|
|
|
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_adminsay("Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]"),
|
|
confidential = TRUE)
|
|
to_chat(C,
|
|
type = MESSAGE_TYPE_ADMINPM,
|
|
html = span_adminsay("<i>Click on the administrator's name to reply.</i>"),
|
|
confidential = TRUE)
|
|
|
|
admin_ticket_log(C, "<font color='purple'>PM From [tgs_tagged]: [msg]</font>")
|
|
|
|
window_flash(C, ignorepref = TRUE)
|
|
//always play non-admin recipients the adminhelp sound
|
|
SEND_SOUND(C, 'sound/effects/adminhelp.ogg')
|
|
|
|
C.externalreplyamount = EXTERNALREPLYCOUNT
|
|
|
|
return "Message Successful"
|
|
|
|
/proc/GenTgsStealthKey()
|
|
var/num = (rand(0,1000))
|
|
var/i = 0
|
|
while(i == 0)
|
|
i = 1
|
|
for(var/P in GLOB.stealthminID)
|
|
if(num == GLOB.stealthminID[P])
|
|
num++
|
|
i = 0
|
|
var/stealth = "@[num2text(num)]"
|
|
GLOB.stealthminID["IRCKEY"] = stealth
|
|
return stealth
|
|
|
|
#undef EXTERNALREPLYCOUNT
|