mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-23 03:57:13 +01:00
Ahelp and Mhelp QOL (#24597)
* Bound ahelp/mhelp replies more tightly to their ticket. * Hardened PMs further against missing data. * Support expanding Discord notifications for ahelp/mhelp. Functionality changes: * Informs the user when their ticket is reopened. * Informs other mentors/admins when tickets are converted. * Discord messages now get a "Ticket 3, " or similar prefix. Not useful by itself, but will be with the following PR. Code changes: * When you create a ticket, you get the ticket object back. * checkForTicket now allows you to fetch a ticket by number, and falls back to searching for open tickets if the ticket isn't found. * Corrected and simplified the logic for adding PMs to tickets. * Responses are now added through the ticket system, not the tickets directly.
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
#define MESSAGE_TYPE_DEADCHAT "deadchat"
|
||||
#define MESSAGE_TYPE_OOC "ooc"
|
||||
#define MESSAGE_TYPE_ADMINPM "adminpm"
|
||||
#define MESSAGE_TYPE_MENTORPM "mentorpm"
|
||||
#define MESSAGE_TYPE_COMBAT "combat"
|
||||
#define MESSAGE_TYPE_ADMINCHAT "adminchat"
|
||||
#define MESSAGE_TYPE_MENTORCHAT "mentorchat"
|
||||
|
||||
@@ -110,8 +110,9 @@ SUBSYSTEM_DEF(tickets)
|
||||
to_chat(C.mob, "<span class='[span_class]'>Your [ticket_name] #[ticketNum] remains open! Visit \"My tickets\" under the Admin Tab to view it.</span>")
|
||||
var/url_message = makeUrlMessage(C, text, ticketNum)
|
||||
message_staff(url_message, NONE, TRUE)
|
||||
return T
|
||||
else
|
||||
newTicket(C, text, text)
|
||||
return newTicket(C, text, text)
|
||||
|
||||
/**
|
||||
* Will add the URLs usable by staff to the message and return it
|
||||
@@ -126,13 +127,13 @@ SUBSYSTEM_DEF(tickets)
|
||||
if(istype(target, /datum/ticket))
|
||||
var/datum/ticket/T = target
|
||||
M = get_mob_by_ckey(T.client_ckey)
|
||||
key_and_name = M ? key_name(M, TRUE, ticket_help_type) : "[T.client_ckey] (DC)/(DELETED)"
|
||||
key_and_name = M ? key_name(M, TRUE, ticket_help_type, ticket_id = T.ticketNum) : "[T.client_ckey] (DC)/(DELETED)"
|
||||
msg = T.title
|
||||
ticketNum = T.ticketNum
|
||||
else if(isclient(target))
|
||||
var/client/C = target
|
||||
M = C.mob
|
||||
key_and_name = key_name(M, TRUE, ticket_help_type)
|
||||
key_and_name = key_name(M, TRUE, ticket_help_type, ticket_id = ticketNum)
|
||||
|
||||
var/list/L = list()
|
||||
L += "<span class='[ticket_help_span]'>[ticket_help_type]: </span><span class='boldnotice'>[key_and_name] "
|
||||
@@ -165,12 +166,14 @@ SUBSYSTEM_DEF(tickets)
|
||||
SEND_SOUND(C, sound('sound/effects/adminticketopen.ogg'))
|
||||
|
||||
message_staff(url_title, NONE, TRUE)
|
||||
return T
|
||||
|
||||
//Set ticket state with key N to open
|
||||
/datum/controller/subsystem/tickets/proc/openTicket(N)
|
||||
var/datum/ticket/T = allTickets[N]
|
||||
if(T.ticketState != TICKET_OPEN)
|
||||
message_staff("<span class='[span_class]'>[usr.client] / ([usr]) re-opened [ticket_name] number [N]</span>")
|
||||
to_chat_safe(returnClient(N), "<span class='[span_class]'>Your [ticket_name] has been re-opened.</span>")
|
||||
T.ticketState = TICKET_OPEN
|
||||
return TRUE
|
||||
|
||||
@@ -183,6 +186,12 @@ SUBSYSTEM_DEF(tickets)
|
||||
to_chat_safe(returnClient(N), "<span class='[span_class]'>Your [ticket_name] has now been resolved.</span>")
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/tickets/proc/addResponse(list/tickets, who, message)
|
||||
var/list/ticket_numbers = list()
|
||||
for(var/datum/ticket/T in tickets)
|
||||
ticket_numbers += T.ticketNum
|
||||
T.addResponse(who, message)
|
||||
|
||||
/datum/controller/subsystem/tickets/proc/convert_to_other_ticket(ticketId)
|
||||
if(!check_rights(rights_needed))
|
||||
return
|
||||
@@ -195,6 +204,7 @@ SUBSYSTEM_DEF(tickets)
|
||||
to_chat(usr, "<span class='warning'>This ticket has already been converted!</span>")
|
||||
return
|
||||
convert_ticket(T)
|
||||
message_staff("<span class='[span_class]'>[usr.client] / ([usr]) converted [ticket_name] number [ticketId]</span>")
|
||||
|
||||
/datum/controller/subsystem/tickets/proc/other_ticket_system_staff_check()
|
||||
var/list/staff = staff_countup(other_ticket_permission)
|
||||
@@ -301,7 +311,9 @@ SUBSYSTEM_DEF(tickets)
|
||||
return FALSE
|
||||
|
||||
//Check if the user has ANY ticket not resolved or closed.
|
||||
/datum/controller/subsystem/tickets/proc/checkForTicket(client/C)
|
||||
/datum/controller/subsystem/tickets/proc/checkForTicket(client/C, ticket_id = -1)
|
||||
if(ticket_id > 0 && ticket_id <= length(allTickets))
|
||||
return list(allTickets[ticket_id])
|
||||
var/list/tickets = list()
|
||||
for(var/datum/ticket/T in allTickets)
|
||||
if(T.client_ckey == C.ckey && (T.ticketState == TICKET_OPEN || T.ticketState == TICKET_STALE))
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Always return "Something/(Something)", even if it's an error message.
|
||||
/proc/key_name(whom, include_link = FALSE, type = null)
|
||||
return key_name_helper(whom, TRUE, include_link, type)
|
||||
/proc/key_name(whom, include_link = FALSE, type = null, ticket_id = null)
|
||||
return key_name_helper(whom, TRUE, include_link, type, ticket_id = ticket_id)
|
||||
|
||||
/proc/key_name_hidden(whom, include_link = FALSE, type = null)
|
||||
return key_name_helper(whom, FALSE, include_link, type)
|
||||
/proc/key_name_hidden(whom, include_link = FALSE, type = null, ticket_id = null)
|
||||
return key_name_helper(whom, FALSE, include_link, type, ticket_id = ticket_id)
|
||||
|
||||
/proc/key_name_helper(whom, include_name, include_link = FALSE, type = null)
|
||||
/proc/key_name_helper(whom, include_name, include_link = FALSE, type = null, ticket_id = null)
|
||||
if(include_link != FALSE && include_link != TRUE)
|
||||
stack_trace("Key_name was called with an incorrect include_link [include_link]")
|
||||
|
||||
@@ -42,11 +42,11 @@
|
||||
if(key)
|
||||
if(C && C.holder && C.holder.fakekey && !include_name)
|
||||
if(include_link)
|
||||
. += "<a href='?priv_msg=[C.getStealthKey()];type=[type]'>"
|
||||
. += "<a href='?priv_msg=[C.getStealthKey()];type=[type];ticket_id=[ticket_id]'>"
|
||||
. += "Administrator"
|
||||
else
|
||||
if(include_link && C)
|
||||
. += "<a href='?priv_msg=[C.ckey];type=[type]'>"
|
||||
. += "<a href='?priv_msg=[C.ckey];type=[type];ticket_id=[ticket_id]'>"
|
||||
. += key
|
||||
// See if the player is on the watchlist. Requires admin permissions.
|
||||
if(check_rights(R_ADMIN, FALSE) && C && C.watchlisted)
|
||||
|
||||
@@ -44,7 +44,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
|
||||
for(var/client/C in GLOB.admins)
|
||||
if(check_rights(R_ADMIN | R_MENTOR | R_MOD, 0, C.mob))
|
||||
if(important || (C.prefs && !(C.prefs.toggles & PREFTOGGLE_CHAT_NO_TICKETLOGS)))
|
||||
to_chat(C, msg, MESSAGE_TYPE_MENTORCHAT, confidential = TRUE)
|
||||
to_chat(C, msg, MESSAGE_TYPE_MENTORPM, confidential = TRUE)
|
||||
if(important)
|
||||
if(C.prefs?.sound & SOUND_MENTORHELP)
|
||||
SEND_SOUND(C, sound('sound/effects/adminhelp.ogg'))
|
||||
|
||||
@@ -25,13 +25,20 @@
|
||||
if(!msg) // No message after sanitisation
|
||||
return
|
||||
|
||||
var/span_type
|
||||
var/message_type
|
||||
var/datum/ticket/T
|
||||
if(selected_type == "Mentorhelp")
|
||||
SSmentor_tickets.newHelpRequest(src, msg) // Mhelp
|
||||
T = SSmentor_tickets.newHelpRequest(src, msg) // Mhelp
|
||||
span_type = "mentorhelp"
|
||||
message_type = MESSAGE_TYPE_MENTORPM
|
||||
else
|
||||
SStickets.newHelpRequest(src, msg) // Ahelp
|
||||
T = SStickets.newHelpRequest(src, msg) // Ahelp
|
||||
span_type = "adminhelp"
|
||||
message_type = MESSAGE_TYPE_ADMINPM
|
||||
|
||||
//show it to the person adminhelping too
|
||||
to_chat(src, "<span class='boldnotice'>[selected_type]</b>: [msg]</span>", MESSAGE_TYPE_ADMINPM, confidential = TRUE)
|
||||
to_chat(src, "<span class='[span_type]'>[selected_type]</b>: [msg]</span>", message_type, confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Adminhelp") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
switch(selected_type)
|
||||
@@ -41,11 +48,11 @@
|
||||
var/active_admins = admincount[1]
|
||||
|
||||
log_admin("[selected_type]: [key_name(src)]: [msg] - heard by [active_admins] non-AFK admins.")
|
||||
GLOB.discord_manager.send2discord_simple_noadmins("**\[Adminhelp]** [key_name(src)]: [msg]", check_send_always = TRUE)
|
||||
GLOB.discord_manager.send2discord_simple_noadmins("**\[Adminhelp]** Ticket [T.ticketNum], [key_name(src)]: [msg]", check_send_always = TRUE)
|
||||
|
||||
if("Mentorhelp")
|
||||
var/list/mentorcount = staff_countup(R_MENTOR)
|
||||
var/active_mentors = mentorcount[1]
|
||||
|
||||
log_admin("[selected_type]: [key_name(src)]: [msg] - heard by [active_mentors] non-AFK mentors.")
|
||||
GLOB.discord_manager.send2discord_simple_mentor("[key_name(src)]: [msg]")
|
||||
GLOB.discord_manager.send2discord_simple_mentor("Ticket [T.ticketNum], [key_name(src)]: [msg]")
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
//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, type = "PM")
|
||||
/client/proc/cmd_admin_pm(whom, msg, type = "PM", ticket_id = -1)
|
||||
if(check_mute(ckey, MUTE_ADMINHELP))
|
||||
to_chat(src, "<span class='danger'>Error: Private-Message: You are unable to use PM-s (muted).</span>")
|
||||
return
|
||||
@@ -112,21 +112,30 @@
|
||||
else
|
||||
msg = admin_pencode_to_html(msg)
|
||||
|
||||
var/recieve_span = "playerreply"
|
||||
var/send_span
|
||||
var/recieve_span
|
||||
var/send_pm_type = " "
|
||||
var/recieve_pm_type = "Player"
|
||||
var/message_type
|
||||
var/datum/controller/subsystem/tickets/tickets_system
|
||||
// We treat PMs as mentorhelps if we were explicitly so, or if neither
|
||||
// party is an admin.
|
||||
if(type == "Mentorhelp" || !(check_rights(R_ADMIN|R_MOD, 0, C.mob) || check_rights(R_ADMIN|R_MOD, 0, src.mob)))
|
||||
send_span = "mentorhelp"
|
||||
recieve_span = "mentorhelp"
|
||||
message_type = MESSAGE_TYPE_MENTORPM
|
||||
tickets_system = SSmentor_tickets
|
||||
else
|
||||
send_span = "adminhelp"
|
||||
recieve_span = "adminhelp"
|
||||
message_type = MESSAGE_TYPE_ADMINPM
|
||||
tickets_system = SStickets
|
||||
|
||||
|
||||
if(holder)
|
||||
//mod PMs are maroon
|
||||
//PMs sent from admins and mods display their rank
|
||||
if(holder)
|
||||
if(check_rights(R_MOD|R_MENTOR,0) && !check_rights(R_ADMIN,0))
|
||||
recieve_span = "mentorhelp"
|
||||
else
|
||||
recieve_span = "adminhelp"
|
||||
send_pm_type = holder.rank + " "
|
||||
recieve_pm_type = holder.rank
|
||||
send_pm_type = holder.rank + " "
|
||||
recieve_pm_type = holder.rank
|
||||
|
||||
else if(!C.holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</span>")
|
||||
@@ -159,12 +168,12 @@
|
||||
|
||||
|
||||
var/emoji_msg = "<span class='emoji_enabled'>[msg]</span>"
|
||||
recieve_message = chat_box_red("<span class='[recieve_span]'>[type] from-<b>[recieve_pm_type] [C.holder ? key_name(src, TRUE, type) : key_name_hidden(src, TRUE, type)]</b>:<br><br>[emoji_msg]</span>")
|
||||
recieve_message = chat_box_red("<span class='[recieve_span]'>[type] from-<b>[recieve_pm_type] [C.holder ? key_name(src, TRUE, type, ticket_id = ticket_id) : key_name_hidden(src, TRUE, type, ticket_id = ticket_id)]</b>:<br><br>[emoji_msg]</span>")
|
||||
to_chat(C, recieve_message)
|
||||
var/ping_link = check_rights(R_ADMIN, 0, mob) ? "(<a href='?src=[pm_tracker.UID()];ping=[C.key]'>PING</a>)" : ""
|
||||
var/window_link = "(<a href='?src=[pm_tracker.UID()];newtitle=[C.key]'>WINDOW</a>)"
|
||||
var/alert_link = check_rights(R_ADMIN, FALSE, mob) ? " (<a href='?src=[pm_tracker.UID()];adminalert=[C.mob.UID()]'>ALERT</a>)" : ""
|
||||
to_chat(src, "<span class='pmsend'>[send_pm_type][type] to-<b>[holder ? key_name(C, TRUE, type) : key_name_hidden(C, TRUE, type)]</b>: [emoji_msg]</span> [ping_link] [window_link][alert_link]", MESSAGE_TYPE_ADMINPM)
|
||||
to_chat(src, "<span class='[send_span]'>[send_pm_type][type] to-<b>[holder ? key_name(C, TRUE, type, ticket_id = ticket_id) : key_name_hidden(C, TRUE, type, ticket_id = ticket_id)]</b>: [emoji_msg]</span> [ping_link] [window_link][alert_link]", message_type)
|
||||
|
||||
/*if(holder && !C.holder)
|
||||
C.last_pm_recieved = world.time
|
||||
@@ -182,38 +191,31 @@
|
||||
if(X == C || X == src)
|
||||
continue
|
||||
if(X.key != key && X.key != C.key)
|
||||
switch(type)
|
||||
if("Mentorhelp")
|
||||
if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, X.mob))
|
||||
to_chat(X, "<span class='mentorhelp'>[type]: [key_name(src, TRUE, type)]->[key_name(C, TRUE, type)]: [emoji_msg]</span>", type = MESSAGE_TYPE_MENTORCHAT)
|
||||
if("Adminhelp")
|
||||
if(check_rights(R_ADMIN|R_MOD, 0, X.mob))
|
||||
to_chat(X, "<span class='adminhelp'>[type]: [key_name(src, TRUE, type)]->[key_name(C, TRUE, type)]: [emoji_msg]</span>", type = MESSAGE_TYPE_ADMINCHAT)
|
||||
else
|
||||
if(check_rights(R_ADMIN|R_MOD, 0, X.mob))
|
||||
to_chat(X, "<span class='boldnotice'>[type]: [key_name(src, TRUE, type)]->[key_name(C, TRUE, type)]: [emoji_msg]</span>", type = MESSAGE_TYPE_ADMINCHAT)
|
||||
if(message_type == MESSAGE_TYPE_MENTORPM)
|
||||
if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, X.mob))
|
||||
to_chat(X, "<span class='mentorhelp'>[type]: [key_name(src, TRUE, type, ticket_id = ticket_id)]->[key_name(C, TRUE, type, ticket_id = ticket_id)]: [emoji_msg]</span>", type = message_type)
|
||||
else
|
||||
if(check_rights(R_ADMIN|R_MOD, 0, X.mob))
|
||||
to_chat(X, "<span class='adminhelp'>[type]: [key_name(src, TRUE, type, ticket_id = ticket_id)]->[key_name(C, TRUE, type, ticket_id = ticket_id)]: [emoji_msg]</span>", type = message_type)
|
||||
|
||||
//Check if the mob being PM'd has any open admin tickets.
|
||||
var/tickets = list()
|
||||
if(type == "Mentorhelp")
|
||||
tickets = SSmentor_tickets.checkForTicket(C)
|
||||
else
|
||||
tickets = SStickets.checkForTicket(C)
|
||||
if(tickets)
|
||||
for(var/datum/ticket/i in tickets)
|
||||
i.addResponse(src, msg) // Add this response to their open tickets.
|
||||
//Check if the mob being PM'd has any open tickets.
|
||||
var/list/tickets = tickets_system.checkForTicket(C, ticket_id)
|
||||
|
||||
if(length(tickets))
|
||||
tickets_system.addResponse(tickets, src, msg)
|
||||
return
|
||||
if(type == "Mentorhelp")
|
||||
if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, C.mob)) //Is the person being pm'd an admin? If so we check if the pm'er has open tickets
|
||||
|
||||
// If we didn't find a specific ticket by the target mob, we check for
|
||||
// tickets by the source mob.
|
||||
if(message_type == MESSAGE_TYPE_MENTORPM)
|
||||
if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, C.mob))
|
||||
tickets = SSmentor_tickets.checkForTicket(src)
|
||||
else // Ahelp
|
||||
if(check_rights(R_ADMIN|R_MOD, 0, C.mob)) //Is the person being pm'd an admin? If so we check if the pm'er has open tickets
|
||||
else
|
||||
if(check_rights(R_ADMIN|R_MOD, 0, C.mob))
|
||||
tickets = SStickets.checkForTicket(src)
|
||||
|
||||
if(tickets)
|
||||
for(var/datum/ticket/i in tickets)
|
||||
i.addResponse(src, msg)
|
||||
return
|
||||
if(length(tickets))
|
||||
tickets_system.addResponse(tickets, src, msg)
|
||||
|
||||
/client/proc/cmd_admin_discord_pm()
|
||||
if(check_mute(ckey, MUTE_ADMINHELP))
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
if(href_list["priv_msg"])
|
||||
var/ckey_txt = href_list["priv_msg"]
|
||||
|
||||
cmd_admin_pm(ckey_txt, null, href_list["type"])
|
||||
cmd_admin_pm(ckey_txt, null, href_list["type"], ticket_id = text2num(href_list["ticket_id"]))
|
||||
return
|
||||
|
||||
if(href_list["discord_msg"])
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Circumvents the message queue and sends the message to the recipient (target) as soon as possible.
|
||||
* trailing_newline, confidential, and handle_whitespace currently have no effect, please fix this in the future or remove the arguments to lower cache!
|
||||
*/
|
||||
/proc/to_chat_immediate(target, html, type, text, avoid_highlighting = FALSE, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE)
|
||||
/proc/to_chat_immediate(target, html, type, text, avoid_highlighting = FALSE, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE, ticket_id = -1)
|
||||
// Useful where the integer 0 is the entire message. Use case is enabling to_chat(target, some_boolean) while preventing to_chat(target, "")
|
||||
html = "[html]"
|
||||
text = "[text]"
|
||||
@@ -29,6 +29,8 @@
|
||||
message["html"] = html
|
||||
if(avoid_highlighting)
|
||||
message["avoidHighlighting"] = avoid_highlighting
|
||||
if(ticket_id != -1)
|
||||
message["ticket_id"] = ticket_id
|
||||
|
||||
// send it immediately
|
||||
SSchat.send_immediate(target, message)
|
||||
@@ -48,7 +50,7 @@
|
||||
* AVOID_HIGHLIGHTING: Unused
|
||||
* trailing_newline, confidential, and handle_whitespace currently have no effect, please fix this in the future or remove the arguments to lower cache!
|
||||
*/
|
||||
/proc/to_chat(target, html, type, text, avoid_highlighting, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE)
|
||||
/proc/to_chat(target, html, type, text, avoid_highlighting, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = FALSE, ticket_id = -1)
|
||||
if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized)
|
||||
to_chat_immediate(target, html, type, text)
|
||||
return
|
||||
@@ -74,4 +76,6 @@
|
||||
message["html"] = html
|
||||
if(avoid_highlighting)
|
||||
message["avoidHighlighting"] = avoid_highlighting
|
||||
if(ticket_id != -1)
|
||||
message["ticket_id"] = ticket_id
|
||||
SSchat.queue(target, message)
|
||||
|
||||
Reference in New Issue
Block a user