diff --git a/code/__DEFINES/chat.dm b/code/__DEFINES/chat.dm
index 149f5d85698..44bcdcd2a19 100644
--- a/code/__DEFINES/chat.dm
+++ b/code/__DEFINES/chat.dm
@@ -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"
diff --git a/code/controllers/subsystem/tickets/tickets.dm b/code/controllers/subsystem/tickets/tickets.dm
index 55881a681de..655f6eda086 100644
--- a/code/controllers/subsystem/tickets/tickets.dm
+++ b/code/controllers/subsystem/tickets/tickets.dm
@@ -110,8 +110,9 @@ SUBSYSTEM_DEF(tickets)
to_chat(C.mob, "Your [ticket_name] #[ticketNum] remains open! Visit \"My tickets\" under the Admin Tab to view it.")
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 += "[ticket_help_type]: [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("[usr.client] / ([usr]) re-opened [ticket_name] number [N]")
+ to_chat_safe(returnClient(N), "Your [ticket_name] has been re-opened.")
T.ticketState = TICKET_OPEN
return TRUE
@@ -183,6 +186,12 @@ SUBSYSTEM_DEF(tickets)
to_chat_safe(returnClient(N), "Your [ticket_name] has now been resolved.")
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, "This ticket has already been converted!")
return
convert_ticket(T)
+ message_staff("[usr.client] / ([usr]) converted [ticket_name] number [ticketId]")
/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))
diff --git a/code/defines/procs/admin_keyname_procs.dm b/code/defines/procs/admin_keyname_procs.dm
index 6bebe06b5f2..efd26bab4c1 100644
--- a/code/defines/procs/admin_keyname_procs.dm
+++ b/code/defines/procs/admin_keyname_procs.dm
@@ -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)
- . += ""
+ . += ""
. += "Administrator"
else
if(include_link && C)
- . += ""
+ . += ""
. += key
// See if the player is on the watchlist. Requires admin permissions.
if(check_rights(R_ADMIN, FALSE) && C && C.watchlisted)
diff --git a/code/modules/admin/misc_admin_procs.dm b/code/modules/admin/misc_admin_procs.dm
index 35b1a6c8121..0609b8f91fd 100644
--- a/code/modules/admin/misc_admin_procs.dm
+++ b/code/modules/admin/misc_admin_procs.dm
@@ -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'))
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 475877cb210..78febb17ac3 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -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, "[selected_type]: [msg]", MESSAGE_TYPE_ADMINPM, confidential = TRUE)
+ to_chat(src, "[selected_type]: [msg]", 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]")
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index a6abcf6e0d1..98f8570eb40 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -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, "Error: Private-Message: You are unable to use PM-s (muted).")
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, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.")
@@ -159,12 +168,12 @@
var/emoji_msg = "[msg]"
- recieve_message = chat_box_red("[type] from-[recieve_pm_type] [C.holder ? key_name(src, TRUE, type) : key_name_hidden(src, TRUE, type)]:
[emoji_msg]")
+ recieve_message = chat_box_red("[type] from-[recieve_pm_type] [C.holder ? key_name(src, TRUE, type, ticket_id = ticket_id) : key_name_hidden(src, TRUE, type, ticket_id = ticket_id)]:
[emoji_msg]")
to_chat(C, recieve_message)
var/ping_link = check_rights(R_ADMIN, 0, mob) ? "(PING)" : ""
var/window_link = "(WINDOW)"
var/alert_link = check_rights(R_ADMIN, FALSE, mob) ? " (ALERT)" : ""
- to_chat(src, "[send_pm_type][type] to-[holder ? key_name(C, TRUE, type) : key_name_hidden(C, TRUE, type)]: [emoji_msg] [ping_link] [window_link][alert_link]", MESSAGE_TYPE_ADMINPM)
+ to_chat(src, "[send_pm_type][type] to-[holder ? key_name(C, TRUE, type, ticket_id = ticket_id) : key_name_hidden(C, TRUE, type, ticket_id = ticket_id)]: [emoji_msg] [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, "[type]: [key_name(src, TRUE, type)]->[key_name(C, TRUE, type)]: [emoji_msg]", type = MESSAGE_TYPE_MENTORCHAT)
- if("Adminhelp")
- if(check_rights(R_ADMIN|R_MOD, 0, X.mob))
- to_chat(X, "[type]: [key_name(src, TRUE, type)]->[key_name(C, TRUE, type)]: [emoji_msg]", type = MESSAGE_TYPE_ADMINCHAT)
- else
- if(check_rights(R_ADMIN|R_MOD, 0, X.mob))
- to_chat(X, "[type]: [key_name(src, TRUE, type)]->[key_name(C, TRUE, type)]: [emoji_msg]", 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, "[type]: [key_name(src, TRUE, type, ticket_id = ticket_id)]->[key_name(C, TRUE, type, ticket_id = ticket_id)]: [emoji_msg]", type = message_type)
+ else
+ if(check_rights(R_ADMIN|R_MOD, 0, X.mob))
+ to_chat(X, "[type]: [key_name(src, TRUE, type, ticket_id = ticket_id)]->[key_name(C, TRUE, type, ticket_id = ticket_id)]: [emoji_msg]", 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))
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index d8031cc4f49..bc0de582ba8 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -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"])
diff --git a/code/modules/tgui/tgui_panel/to_chat.dm b/code/modules/tgui/tgui_panel/to_chat.dm
index beb0f70be6d..7489b162606 100644
--- a/code/modules/tgui/tgui_panel/to_chat.dm
+++ b/code/modules/tgui/tgui_panel/to_chat.dm
@@ -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)
diff --git a/tgui/packages/tgui-panel/chat/constants.js b/tgui/packages/tgui-panel/chat/constants.js
index bdef964f5e7..91fc756802c 100644
--- a/tgui/packages/tgui-panel/chat/constants.js
+++ b/tgui/packages/tgui-panel/chat/constants.js
@@ -29,6 +29,7 @@ export const MESSAGE_TYPE_WARNING = 'warning';
export const MESSAGE_TYPE_DEADCHAT = 'deadchat';
export const MESSAGE_TYPE_OOC = 'ooc';
export const MESSAGE_TYPE_ADMINPM = 'adminpm';
+export const MESSAGE_TYPE_MENTORPM = 'mentorpm';
export const MESSAGE_TYPE_COMBAT = 'combat';
export const MESSAGE_TYPE_ADMINCHAT = 'adminchat';
export const MESSAGE_TYPE_MENTORCHAT = 'mentorchat';
@@ -90,7 +91,13 @@ export const MESSAGE_TYPES = [
type: MESSAGE_TYPE_ADMINPM,
name: 'Admin PMs',
description: 'Messages to/from admins (adminhelp)',
- selector: '.pm, .adminhelp',
+ selector: '.adminpm, .adminhelp, .adminticket, .adminticketalt',
+ },
+ {
+ type: MESSAGE_TYPE_MENTORPM,
+ name: 'Mentor PMs',
+ description: 'Messages to/from mentors (mentorhelp)',
+ selector: '.mentorpm, .mentorhelp',
},
{
type: MESSAGE_TYPE_COMBAT,
diff --git a/tgui/public/tgui-panel.bundle.js b/tgui/public/tgui-panel.bundle.js
index c1eb6b12aaf..0785f57e7ad 100644
--- a/tgui/public/tgui-panel.bundle.js
+++ b/tgui/public/tgui-panel.bundle.js
@@ -114,11 +114,11 @@
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT
- */var o=e.loadChat=(0,n.createAction)("chat/load"),a=e.rebuildChat=(0,n.createAction)("chat/rebuild"),u=e.clearChat=(0,n.createAction)("chat/clear"),l=e.updateMessageCount=(0,n.createAction)("chat/updateMessageCount"),c=e.addChatPage=(0,n.createAction)("chat/addPage",function(){return{payload:(0,r.createPage)()}}),h=e.changeChatPage=(0,n.createAction)("chat/changePage"),v=e.updateChatPage=(0,n.createAction)("chat/updatePage"),i=e.toggleAcceptedType=(0,n.createAction)("chat/toggleAcceptedType"),d=e.removeChatPage=(0,n.createAction)("chat/removePage"),s=e.changeScrollTracking=(0,n.createAction)("chat/changeScrollTracking"),p=e.saveChatToDisk=(0,n.createAction)("chat/saveToDisk"),f=e.moveChatPageLeft=(0,n.createAction)("chat/movePageLeft"),g=e.moveChatPageRight=(0,n.createAction)("chat/movePageRight")},67392:function(E,e){"use strict";e.__esModule=!0,e.MESSAGE_TYPE_WARNING=e.MESSAGE_TYPE_UNKNOWN=e.MESSAGE_TYPE_SYSTEM=e.MESSAGE_TYPE_RADIO=e.MESSAGE_TYPE_OOC=e.MESSAGE_TYPE_MENTORCHAT=e.MESSAGE_TYPE_LOCALCHAT=e.MESSAGE_TYPE_INTERNAL=e.MESSAGE_TYPE_INFO=e.MESSAGE_TYPE_EVENTCHAT=e.MESSAGE_TYPE_DEBUG=e.MESSAGE_TYPE_DEADCHAT=e.MESSAGE_TYPE_COMBAT=e.MESSAGE_TYPE_ATTACKLOG=e.MESSAGE_TYPE_ADMINPM=e.MESSAGE_TYPE_ADMINLOG=e.MESSAGE_TYPE_ADMINCHAT=e.MESSAGE_TYPES=e.MESSAGE_SAVE_INTERVAL=e.MESSAGE_PRUNE_INTERVAL=e.MAX_VISIBLE_MESSAGES=e.MAX_PERSISTED_MESSAGES=e.IMAGE_RETRY_MESSAGE_AGE=e.IMAGE_RETRY_LIMIT=e.IMAGE_RETRY_DELAY=e.COMBINE_MAX_TIME_WINDOW=e.COMBINE_MAX_MESSAGES=void 0;/**
+ */var o=e.loadChat=(0,n.createAction)("chat/load"),a=e.rebuildChat=(0,n.createAction)("chat/rebuild"),u=e.clearChat=(0,n.createAction)("chat/clear"),l=e.updateMessageCount=(0,n.createAction)("chat/updateMessageCount"),c=e.addChatPage=(0,n.createAction)("chat/addPage",function(){return{payload:(0,r.createPage)()}}),h=e.changeChatPage=(0,n.createAction)("chat/changePage"),v=e.updateChatPage=(0,n.createAction)("chat/updatePage"),i=e.toggleAcceptedType=(0,n.createAction)("chat/toggleAcceptedType"),d=e.removeChatPage=(0,n.createAction)("chat/removePage"),s=e.changeScrollTracking=(0,n.createAction)("chat/changeScrollTracking"),p=e.saveChatToDisk=(0,n.createAction)("chat/saveToDisk"),f=e.moveChatPageLeft=(0,n.createAction)("chat/movePageLeft"),g=e.moveChatPageRight=(0,n.createAction)("chat/movePageRight")},67392:function(E,e){"use strict";e.__esModule=!0,e.MESSAGE_TYPE_WARNING=e.MESSAGE_TYPE_UNKNOWN=e.MESSAGE_TYPE_SYSTEM=e.MESSAGE_TYPE_RADIO=e.MESSAGE_TYPE_OOC=e.MESSAGE_TYPE_MENTORPM=e.MESSAGE_TYPE_MENTORCHAT=e.MESSAGE_TYPE_LOCALCHAT=e.MESSAGE_TYPE_INTERNAL=e.MESSAGE_TYPE_INFO=e.MESSAGE_TYPE_EVENTCHAT=e.MESSAGE_TYPE_DEBUG=e.MESSAGE_TYPE_DEADCHAT=e.MESSAGE_TYPE_COMBAT=e.MESSAGE_TYPE_ATTACKLOG=e.MESSAGE_TYPE_ADMINPM=e.MESSAGE_TYPE_ADMINLOG=e.MESSAGE_TYPE_ADMINCHAT=e.MESSAGE_TYPES=e.MESSAGE_SAVE_INTERVAL=e.MESSAGE_PRUNE_INTERVAL=e.MAX_VISIBLE_MESSAGES=e.MAX_PERSISTED_MESSAGES=e.IMAGE_RETRY_MESSAGE_AGE=e.IMAGE_RETRY_LIMIT=e.IMAGE_RETRY_DELAY=e.COMBINE_MAX_TIME_WINDOW=e.COMBINE_MAX_MESSAGES=void 0;/**
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT
- */var t=e.MAX_VISIBLE_MESSAGES=2500,n=e.MAX_PERSISTED_MESSAGES=1e3,r=e.MESSAGE_SAVE_INTERVAL=1e4,o=e.MESSAGE_PRUNE_INTERVAL=6e4,a=e.COMBINE_MAX_TIME_WINDOW=5e3,u=e.COMBINE_MAX_MESSAGES=5,l=e.IMAGE_RETRY_DELAY=250,c=e.IMAGE_RETRY_LIMIT=10,h=e.IMAGE_RETRY_MESSAGE_AGE=6e4,v=e.MESSAGE_TYPE_UNKNOWN="unknown",i=e.MESSAGE_TYPE_INTERNAL="internal",d=e.MESSAGE_TYPE_SYSTEM="system",s=e.MESSAGE_TYPE_LOCALCHAT="localchat",p=e.MESSAGE_TYPE_RADIO="radio",f=e.MESSAGE_TYPE_INFO="info",g=e.MESSAGE_TYPE_WARNING="warning",m=e.MESSAGE_TYPE_DEADCHAT="deadchat",b=e.MESSAGE_TYPE_OOC="ooc",I=e.MESSAGE_TYPE_ADMINPM="adminpm",O=e.MESSAGE_TYPE_COMBAT="combat",C=e.MESSAGE_TYPE_ADMINCHAT="adminchat",S=e.MESSAGE_TYPE_MENTORCHAT="mentorchat",y=e.MESSAGE_TYPE_EVENTCHAT="eventchat",T=e.MESSAGE_TYPE_ADMINLOG="adminlog",P=e.MESSAGE_TYPE_ATTACKLOG="attacklog",N=e.MESSAGE_TYPE_DEBUG="debug",M=e.MESSAGE_TYPES=[{type:d,name:"System Messages",description:"Messages from your client, always enabled",selector:".boldannounceooc",important:!0},{type:s,name:"Local",description:"In-character local messages (say, emote, etc)",selector:".say, .emote"},{type:p,name:"Radio",description:"All departments of radio messages",selector:".alert, .syndradio, .centradio, .airadio, .entradio, .comradio, .secradio, .engradio, .medradio, .sciradio, .supradio, .srvradio, .expradio, .radio, .deptradio, .newscaster"},{type:f,name:"Info",description:"Non-urgent messages from the game and items",selector:".notice:not(.pm), .adminnotice, .info, .sinister, .cult"},{type:g,name:"Warnings",description:"Urgent messages from the game and items",selector:".warning:not(.pm), .critical, .userdanger, .italics, .boldannounceic, .boldwarning"},{type:m,name:"Deadchat",description:"All of deadchat",selector:".deadsay"},{type:b,name:"OOC",description:"The bluewall of global OOC messages",selector:".ooc, .adminooc"},{type:I,name:"Admin PMs",description:"Messages to/from admins (adminhelp)",selector:".pm, .adminhelp"},{type:O,name:"Combat Log",description:"Urist McTraitor has stabbed you with a knife!",selector:".danger"},{type:v,name:"Unsorted",description:"Everything we could not sort, always enabled"},{type:C,name:"Admin Chat",description:"ASAY messages",selector:".admin_channel, .adminsay",admin:!0},{type:S,name:"Mentor Chat",description:"MSAY messages",selector:".mentor_channel",admin:!0},{type:T,name:"Admin Log",description:"ADMIN LOG: Urist McAdmin has jumped to coordinates X, Y, Z",selector:".log_message",admin:!0},{type:P,name:"Attack Log",description:"Urist McTraitor has shot John Doe",admin:!0},{type:N,name:"Debug Log",description:"DEBUG: SSPlanets subsystem Recover().",selector:".pr_announce, .debug",admin:!0}]},23360:function(E,e,t){"use strict";e.__esModule=!0,e.chatReducer=e.chatMiddleware=e.ChatTabs=e.ChatPanel=e.ChatPageSettings=void 0;var n=t(59216);e.ChatPageSettings=n.ChatPageSettings;var r=t(49084);e.ChatPanel=r.ChatPanel;var o=t(54213);e.ChatTabs=o.ChatTabs;var a=t(13172);e.chatMiddleware=a.chatMiddleware;var u=t(7912);e.chatReducer=u.chatReducer},13172:function(E,e,t){"use strict";e.__esModule=!0,e.chatMiddleware=void 0;var n=i(t(85966)),r=t(96417),o=t(91577),a=t(65910),u=t(3578),l=t(67392),c=t(92863),h=t(57218),v=t(87919);function i(S){return S&&S.__esModule?S:{default:S}}function d(S,y){var T=typeof Symbol!="undefined"&&S[Symbol.iterator]||S["@@iterator"];if(T)return(T=T.call(S)).next.bind(T);if(Array.isArray(S)||(T=s(S))||y&&S&&typeof S.length=="number"){T&&(S=T);var P=0;return function(){return P>=S.length?{done:!0}:{done:!1,value:S[P++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function s(S,y){if(S){if(typeof S=="string")return p(S,y);var T=Object.prototype.toString.call(S).slice(8,-1);if(T==="Object"&&S.constructor&&(T=S.constructor.name),T==="Map"||T==="Set")return Array.from(S);if(T==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(T))return p(S,y)}}function p(S,y){(y==null||y>S.length)&&(y=S.length);for(var T=0,P=new Array(y);T=0;--Et){var it=this.tryEntries[Et],pt=it.completion;if(it.tryLoc==="root")return gt("end");if(it.tryLoc<=this.prev){var rt=P.call(it,"catchLoc"),_=P.call(it,"finallyLoc");if(rt&&_){if(this.prev=0;--gt){var Et=this.tryEntries[gt];if(Et.tryLoc<=this.prev&&P.call(Et,"finallyLoc")&&this.prev=0;--ot){var gt=this.tryEntries[ot];if(gt.finallyLoc===Z)return this.complete(gt.completion,gt.afterLoc),Nt(gt),Y}}return J}(),catch:function(){function J(Z){for(var ot=this.tryEntries.length-1;ot>=0;--ot){var gt=this.tryEntries[ot];if(gt.tryLoc===Z){var Et=gt.completion;if(Et.type==="throw"){var it=Et.arg;Nt(gt)}return it}}throw new Error("illegal catch attempt")}return J}(),delegateYield:function(){function J(Z,ot,gt){return this.delegate={iterator:dt(Z),resultName:ot,nextLoc:gt},this.method==="next"&&(this.arg=S),Y}return J}()},y}function g(S,y,T,P,N,M,R){try{var L=S[M](R),K=L.value}catch(U){T(U);return}L.done?y(K):Promise.resolve(K).then(P,N)}function m(S){return function(){var y=this,T=arguments;return new Promise(function(P,N){var M=S.apply(y,T);function R(K){g(M,P,N,R,L,"next",K)}function L(K){g(M,P,N,R,L,"throw",K)}R(void 0)})}}/**
+ */var t=e.MAX_VISIBLE_MESSAGES=2500,n=e.MAX_PERSISTED_MESSAGES=1e3,r=e.MESSAGE_SAVE_INTERVAL=1e4,o=e.MESSAGE_PRUNE_INTERVAL=6e4,a=e.COMBINE_MAX_TIME_WINDOW=5e3,u=e.COMBINE_MAX_MESSAGES=5,l=e.IMAGE_RETRY_DELAY=250,c=e.IMAGE_RETRY_LIMIT=10,h=e.IMAGE_RETRY_MESSAGE_AGE=6e4,v=e.MESSAGE_TYPE_UNKNOWN="unknown",i=e.MESSAGE_TYPE_INTERNAL="internal",d=e.MESSAGE_TYPE_SYSTEM="system",s=e.MESSAGE_TYPE_LOCALCHAT="localchat",p=e.MESSAGE_TYPE_RADIO="radio",f=e.MESSAGE_TYPE_INFO="info",g=e.MESSAGE_TYPE_WARNING="warning",m=e.MESSAGE_TYPE_DEADCHAT="deadchat",b=e.MESSAGE_TYPE_OOC="ooc",I=e.MESSAGE_TYPE_ADMINPM="adminpm",O=e.MESSAGE_TYPE_MENTORPM="mentorpm",C=e.MESSAGE_TYPE_COMBAT="combat",S=e.MESSAGE_TYPE_ADMINCHAT="adminchat",y=e.MESSAGE_TYPE_MENTORCHAT="mentorchat",T=e.MESSAGE_TYPE_EVENTCHAT="eventchat",P=e.MESSAGE_TYPE_ADMINLOG="adminlog",N=e.MESSAGE_TYPE_ATTACKLOG="attacklog",M=e.MESSAGE_TYPE_DEBUG="debug",R=e.MESSAGE_TYPES=[{type:d,name:"System Messages",description:"Messages from your client, always enabled",selector:".boldannounceooc",important:!0},{type:s,name:"Local",description:"In-character local messages (say, emote, etc)",selector:".say, .emote"},{type:p,name:"Radio",description:"All departments of radio messages",selector:".alert, .syndradio, .centradio, .airadio, .entradio, .comradio, .secradio, .engradio, .medradio, .sciradio, .supradio, .srvradio, .expradio, .radio, .deptradio, .newscaster"},{type:f,name:"Info",description:"Non-urgent messages from the game and items",selector:".notice:not(.pm), .adminnotice, .info, .sinister, .cult"},{type:g,name:"Warnings",description:"Urgent messages from the game and items",selector:".warning:not(.pm), .critical, .userdanger, .italics, .boldannounceic, .boldwarning"},{type:m,name:"Deadchat",description:"All of deadchat",selector:".deadsay"},{type:b,name:"OOC",description:"The bluewall of global OOC messages",selector:".ooc, .adminooc"},{type:I,name:"Admin PMs",description:"Messages to/from admins (adminhelp)",selector:".adminpm, .adminhelp, .adminticket, .adminticketalt"},{type:O,name:"Mentor PMs",description:"Messages to/from mentors (mentorhelp)",selector:".mentorpm, .mentorhelp"},{type:C,name:"Combat Log",description:"Urist McTraitor has stabbed you with a knife!",selector:".danger"},{type:v,name:"Unsorted",description:"Everything we could not sort, always enabled"},{type:S,name:"Admin Chat",description:"ASAY messages",selector:".admin_channel, .adminsay",admin:!0},{type:y,name:"Mentor Chat",description:"MSAY messages",selector:".mentor_channel",admin:!0},{type:P,name:"Admin Log",description:"ADMIN LOG: Urist McAdmin has jumped to coordinates X, Y, Z",selector:".log_message",admin:!0},{type:N,name:"Attack Log",description:"Urist McTraitor has shot John Doe",admin:!0},{type:M,name:"Debug Log",description:"DEBUG: SSPlanets subsystem Recover().",selector:".pr_announce, .debug",admin:!0}]},23360:function(E,e,t){"use strict";e.__esModule=!0,e.chatReducer=e.chatMiddleware=e.ChatTabs=e.ChatPanel=e.ChatPageSettings=void 0;var n=t(59216);e.ChatPageSettings=n.ChatPageSettings;var r=t(49084);e.ChatPanel=r.ChatPanel;var o=t(54213);e.ChatTabs=o.ChatTabs;var a=t(13172);e.chatMiddleware=a.chatMiddleware;var u=t(7912);e.chatReducer=u.chatReducer},13172:function(E,e,t){"use strict";e.__esModule=!0,e.chatMiddleware=void 0;var n=i(t(85966)),r=t(96417),o=t(91577),a=t(65910),u=t(3578),l=t(67392),c=t(92863),h=t(57218),v=t(87919);function i(S){return S&&S.__esModule?S:{default:S}}function d(S,y){var T=typeof Symbol!="undefined"&&S[Symbol.iterator]||S["@@iterator"];if(T)return(T=T.call(S)).next.bind(T);if(Array.isArray(S)||(T=s(S))||y&&S&&typeof S.length=="number"){T&&(S=T);var P=0;return function(){return P>=S.length?{done:!0}:{done:!1,value:S[P++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function s(S,y){if(S){if(typeof S=="string")return p(S,y);var T=Object.prototype.toString.call(S).slice(8,-1);if(T==="Object"&&S.constructor&&(T=S.constructor.name),T==="Map"||T==="Set")return Array.from(S);if(T==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(T))return p(S,y)}}function p(S,y){(y==null||y>S.length)&&(y=S.length);for(var T=0,P=new Array(y);T=0;--Et){var it=this.tryEntries[Et],pt=it.completion;if(it.tryLoc==="root")return gt("end");if(it.tryLoc<=this.prev){var rt=P.call(it,"catchLoc"),_=P.call(it,"finallyLoc");if(rt&&_){if(this.prev=0;--gt){var Et=this.tryEntries[gt];if(Et.tryLoc<=this.prev&&P.call(Et,"finallyLoc")&&this.prev=0;--ot){var gt=this.tryEntries[ot];if(gt.finallyLoc===Z)return this.complete(gt.completion,gt.afterLoc),Nt(gt),Y}}return J}(),catch:function(){function J(Z){for(var ot=this.tryEntries.length-1;ot>=0;--ot){var gt=this.tryEntries[ot];if(gt.tryLoc===Z){var Et=gt.completion;if(Et.type==="throw"){var it=Et.arg;Nt(gt)}return it}}throw new Error("illegal catch attempt")}return J}(),delegateYield:function(){function J(Z,ot,gt){return this.delegate={iterator:dt(Z),resultName:ot,nextLoc:gt},this.method==="next"&&(this.arg=S),Y}return J}()},y}function g(S,y,T,P,N,M,R){try{var L=S[M](R),K=L.value}catch(U){T(U);return}L.done?y(K):Promise.resolve(K).then(P,N)}function m(S){return function(){var y=this,T=arguments;return new Promise(function(P,N){var M=S.apply(y,T);function R(K){g(M,P,N,R,L,"next",K)}function L(K){g(M,P,N,R,L,"throw",K)}R(void 0)})}}/**
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT