diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 348dc6d551..186ad0a1c5 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -11,5 +11,6 @@ var/list/combatlog = list() var/list/IClog = list() var/list/OOClog = list() var/list/adminlog = list() +var/list/admintickets = list() var/list/active_turfs_startlist = list() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 9205f03f12..cda2c628af 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -22,6 +22,10 @@ var/list/admin_verbs_default = list( /client/proc/stop_sounds ) var/list/admin_verbs_admin = list( + /client/proc/resolvehandlingahelp, + /client/proc/listhandlingahelp, + /datum/adminticket/proc/listunresolvedtickets, + /datum/adminticket/proc/listtickets, /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ /client/proc/invisimin, /*allows our mob to go invisible/visible*/ // /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 5620b4e0f8..cdad814ceb 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -83,25 +83,49 @@ if(src.handle_spam_prevention(msg,MUTE_ADMINHELP)) return + var/ref_mob = "\ref[mob]" + for(var/datum/adminticket/T in admintickets) + if(T.permckey == src.ckey && T.resolved == "No") + if(alert(usr,"You already have an adminhelp open, would you like to bump it?", "Bump Adminhelp", "Yes", "No") == "Yes") + T.logs += "[src.ckey] has bumped this adminhelp!" + if(T.admin == "N/A") + usr << "Due to the fact your Adminhelp had no assigned admin, admins have been pinged." + message_admins("[src.ckey] has bumped their adminhelp #[T.ID], still no assigned admin!") + msg = "ADMINHELP: [key_name(src)] (?) (PP) (VV) (SM) (TP) (FLW) (R): [T.msg]" + for(var/client/X in admins) + if(X.prefs.toggles & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + X << msg + else + usr << "Admins have been notified." + message_admins("[src.ckey] has bumped their adminhelp #[T.ID].") + src.verbs -= /client/verb/adminhelp + adminhelptimerid = addtimer(src,"giveadminhelpverb",1200, FALSE) + return + usr << "Thank you for your patience." + return + + src.verbs -= /client/verb/adminhelp + adminhelptimerid = addtimer(src,"giveadminhelpverb",1200, FALSE) + //clean the input msg - if(!msg) - return + if(!msg) return msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN)) if(!msg) return var/original_msg = msg - //remove our adminhelp verb temporarily to prevent spamming of admins. - src.verbs -= /client/verb/adminhelp - adminhelptimerid = addtimer(src, "giveadminhelpverb", 1200, FALSE) //2 minute cooldown of admin helps - msg = keywords_lookup(msg) - if(!mob) - return //this doesn't happen + if(!mob) return //this doesn't happen - var/ref_mob = "\ref[mob]" - var/ref_client = "\ref[src]" - msg = "HELP: [key_name(src)] (?) (PP) (VV) (SM) (FLW) (TP) (REJT): [msg]" + createticket(src, msg, src.ckey, mob) + var/datum/adminticket/ticket + + for(var/datum/adminticket/T in admintickets) + if(T.permckey == src.ckey) + ticket = T + + msg = "ADMINHELP: [key_name(src)] (?) (PP) (VV) (SM) (TP) (FLW) (R): [msg]" //send this msg to all admins @@ -114,6 +138,7 @@ //show it to the person adminhelping too src << "PM to-Admins: [original_msg]" + //send it to irc if nobody is on and tell us how many were on var/admin_number_present = send2irc_adminless_only(ckey,original_msg) log_admin("HELP: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins who have +BAN.") diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 599a794594..bcef6a83af 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -48,11 +48,46 @@ if(holder) src << "Error: Admin-PM: Client not found." return - message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.") + var/datum/adminticket/ticket + + for(var/datum/adminticket/T in admintickets) + if(T.permckey == C.ckey) + ticket = T + + if(ticket) + if(ticket.active == "No" && ticket.replying == 0) + message_admins("[key_name_admin(src)] has been assigned to [key_name(C, 0, 0)]'s admin help. This is the first reply. ([ticket.uID])") + ticket.replying = 1 + ticket.user << "[src.ckey] has been assigned to your admin help, please await a reply." + else if(ticket.replying == 1) + src << "Error, this ticket is already being replied to!" + return + else if(ticket.admin != "N/A" && ticket.replying == 0) + if(ticket.admin != src.ckey) + if(alert(src, "This adminhelp already has an admin assigned: [ticket.admin]! Are you sure you want to take it over?", "Conflict", "Yes", "No") == "Yes") + message_admins("[key_name_admin(src)] has been assigned to [key_name(C, 0, 0)]'s admin help. Override: [ticket.admin]. ([ticket.uID])") + ticket.user << "[src.ckey] has been assigned to your admin help, please await a reply." + ticket.replying = 1 + else + message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help. They did not have an active ahelp.") + var/msg = input(src,"Message:", "Private message to [key_name(C, 0, 0)]") as text|null + if (!msg) - message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.") + if(ticket) + if(ticket.admin != src.ckey) + message_admins("[key_name_admin(src)] has been unassigned from [key_name(C, 0, 0)]'s admin help. Cancelled reply. ([ticket.uID])") + ticket.user << "[src.ckey] has been unassigned from your admin help. (reply cancelled)" + ticket.replying = 0 + else + message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help. No active ahelp.") return + + if(ticket) + ticket.replying = 0 + ticket.admin = src.ckey + ticket.active = "Yes" + cmd_admin_pm(whom, msg) //takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM. @@ -106,10 +141,16 @@ if(C.holder) if(holder) //both are admins + for(var/datum/adminticket/T in admintickets) + if(T.permckey == C.ckey && T.resolved == "No") + T.logs += "[src] TO [C]: [msg] " C << "Admin PM from-[key_name(src, C, 1)]: [keywordparsedmsg]" src << "Admin PM to-[key_name(C, src, 1)]: [keywordparsedmsg]" else //recipient is an admin but sender is not + for(var/datum/adminticket/T in admintickets) + if(T.permckey == C.ckey && T.resolved == "No") + T.logs += "[src] TO [C]: [msg] " C << "Reply PM from-[key_name(src, C, 1)]: [keywordparsedmsg]" src << "PM to-Admins: [msg]" @@ -119,11 +160,16 @@ else if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT + C << "-- Administrator private message --" C << "Admin PM from-[key_name(src, C, 0)]: [msg]" C << "Click on the administrator's name to reply." src << "Admin PM to-[key_name(C, src, 1)]: [msg]" + for(var/datum/adminticket/T in admintickets) + if(T.permckey == C.ckey && T.resolved == "No") + T.logs += "[src] TO [C]: [msg] " + //always play non-admin recipients the adminhelp sound C << 'sound/effects/adminhelp.ogg' @@ -135,7 +181,10 @@ var/reply = input(C, msg,"Admin PM from-[sendername]", "") as text|null //show message and await a reply if(C && reply) if(sender) - C.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them + C.cmd_admin_pm(sender,reply) + for(var/datum/adminticket/T in admintickets) + if(T.permckey == C.ckey && T.resolved == "No") + T.logs += "[sendername] TO [C]: [msg] " //sender is still about, let's reply to them else adminhelp(reply) //sender has left, adminhelp instead return diff --git a/code/modules/admin/verbs/listahelps.dm b/code/modules/admin/verbs/listahelps.dm new file mode 100644 index 0000000000..b993641f4a --- /dev/null +++ b/code/modules/admin/verbs/listahelps.dm @@ -0,0 +1,312 @@ +/datum/adminticket + var/ID = "" //ID of the ticket, very important as its used to find adminhelps. + var/user = "" //The user of the ahelp. + var/uckey //The saved ckey of the adminheloing user. + var/admin = "N/A" //The handling admin? Like come on. + var/msg = "" //The adminhelp message. + var/resolved = "No" //Is it resolved? Its much easier to have a "Yes" or a "No", as you can directly concat it into strings making life that much easier. + var/permckey = "" //The perm ckey, never removed essentially. + var/permuser = "" //Same as above! + var/uID = "" //The UNIQUE id, made by putting part of the ckey and the ID together. Used internally in code. + var/active = "No" //Is the adminhelp active, eg admin responded? This is the same as above, it makes life easier. + var/logs = list() //The logs of the adminhelp. + var/replying = 0 //Is someone responding to the adminhelp? + var/mob //The mob adminhelping mob. + +/client/proc/list_ahelps(user, resolved) + if(!check_rights(R_BAN)) + src << "Error: Only administrators may use this command." + return + + if(resolved) + user << "Current Ahelps:" + for(var/datum/adminticket/T in admintickets) + var/ref_mob = "\ref[T.mob]" + usr << "#[T.ID] By: [key_name(T.permuser)] Ckey: [T.permckey] Name: [T.permuser] Unique ID: [T.uID]" + usr << " Controls: (?) (PP) (VV) (SM) (TP) (FLW)" + usr << " Message: [T.msg]" + usr << " Handling Admin: [T.admin]" + usr << " Replied To: [T.active]/(LOGS)" + if(T.resolved == "No") + usr << " Resolved: [T.resolved] (Resolve)" + else + usr << " Resolved: [T.resolved] (Unresolve)" + + else + user << "Current Unresolved Ahelps:" + for(var/datum/adminticket/T in admintickets) + if(T.resolved == "No") + var/ref_mob = "\ref[T.mob]" + usr << "#[T.ID] By: [key_name(T.permuser)] Ckey: [T.permckey] Name: [T.permuser] Unique ID: [T.uID]" + usr << " Controls: (?) (PP) (VV) (SM) (TP) (FLW)" + usr << " Message: [T.msg]" + usr << " Handling Admin: [T.admin]" + usr << " Replied To: [T.active]/(LOGS)" + if(T.resolved == "No") + usr << " Resolved: [T.resolved] (Resolve)" + else + usr << " Resolved: [T.resolved] (Unresolve)" + +/client/proc/ahelp_count(modifier) + var/amount + for(var/datum/adminticket/T in admintickets) + switch(modifier) + if(0) + if(T.resolved == "No") + amount++ + if(1) + if(T.resolved == "Yes") + amount++ + if(2) + amount++ + + return amount + + +/datum/adminticket/proc/listtickets() + set category = "Admin Help" + set name = "List Adminhelps" + set desc = "List all current adminhelps" + + if(!check_rights(R_BAN)) + src << "Error: Only administrators may use this command." + return + + var/count = 0 + + for(var/datum/adminticket/T in admintickets) + count++ + + usr << "Current Ahelps:" + + if(count < 1) + usr << " None" + return + + for(var/datum/adminticket/T in admintickets) + var/ref_mob = "\ref[T.mob]" + usr << "#[T.ID] By: [key_name(T.permuser)] Ckey: [T.permckey] Name: [T.permuser] Unique ID: [T.uID]" + usr << " Controls: (?) (PP) (VV) (SM) (TP) (FLW)" + usr << " Message: [T.msg]" + usr << " Handling Admin: [T.admin]" + usr << " Replied To: [T.active]/(LOGS)" + if(T.resolved == "No") + usr << " Resolved: [T.resolved] (Resolve)" + else + usr << " Resolved: [T.resolved] (Unresolve)" + +/datum/adminticket/proc/listunresolvedtickets() + set category = "Admin Help" + set name = "List Unresolved Adminhelps" + set desc = "List all current unresolved adminhelps" + + if(!check_rights(R_BAN)) + src << "Error: Only administrators may use this command." + return + + var/count = 0 + + for(var/datum/adminticket/T in admintickets) + if(T.resolved =="No") + count++ + + usr << "Current Unresolved Ahelps:" + + if(count < 1) + usr << " None" + return + + for(var/datum/adminticket/T in admintickets) + if(T.resolved == "No") + var/ref_mob = "\ref[T.mob]" + usr << "#[T.ID] By: [key_name(T.permuser)] Ckey: [T.permckey] Name: [T.permuser] Unique ID: [T.uID]" + usr << " Controls: (?) (PP) (VV) (SM) (TP) (FLW)" + usr << " Message: [T.msg]" + usr << " Handling Admin: [T.admin]" + usr << " Replied To: [T.active]/(LOGS)" + if(T.resolved == "No") + usr << " Resolved: [T.resolved] (Resolve)" + else + usr << " Resolved: [T.resolved] (Unresolve)" + +/client/proc/listhandlingahelp() + set category = "Admin Help" + set name = "View Handling Ahelps" + set desc = "List all current handling ahelps" + + if(!check_rights(R_BAN)) + src << "Error: Only administrators may use this command." + return + + var/count = 0 + + for(var/datum/adminticket/T in admintickets) + if(T.resolved == "No" && T.admin == ckey) + count++ + + if(count < 1) + usr << "You don't have any ACTIVE ahelps!" + return + + + + for(var/datum/adminticket/T in admintickets) + if(T.resolved == "No" && T.admin == ckey) + var/ref_mob = "\ref[T.mob]" + usr << "#[T.ID] By: [key_name(T.permuser)] Ckey: [T.permckey] Name: [T.permuser] Unique ID: [T.uID]" + usr << " Controls: (?) (PP) (VV) (SM) (TP) (FLW)" + usr << " Message: [T.msg]" + usr << " Handling Admin: [T.admin]" + usr << " Replied To: [T.active]/(LOGS)" + if(T.resolved == "No") + usr << " Resolved: [T.resolved] (Resolve)" + else + usr << " Resolved: [T.resolved] (Unresolve)" + +/client/verb/viewmyahelp() + set category = "Admin" + set name = "View my Ahelps" + set desc = "List your ahelps" + + var/count = 0 + + for(var/datum/adminticket/T in admintickets) + if(T.permckey == ckey) + count++ + + if(count < 1) + usr << "You don't have any ahelps!" + return + + usr << "Resolved Ahelps" + + var/rpass = 0 + + for(var/datum/adminticket/T in admintickets) + if(T.permckey == ckey && T.resolved == "Yes") + rpass = 1 + usr << "Adminhelp ID: #[T.ID] " + usr << " Message: [T.msg]" + usr << " Handling Admin: [T.admin]" + usr << " Replied To: [T.active]/(LOGS)" + usr << " Resolved: [T.resolved]" + + if(rpass == 0) + usr << " None" + + usr << "Unresolved Ahelps" + + var/upass = 0 + + for(var/datum/adminticket/T in admintickets) + if(T.permckey == ckey && T.resolved == "No") + upass = 1 + usr << "Adminhelp ID: #[T.ID] " + usr << " Message: [T.msg]" + usr << " Handling Admin: [T.admin]" + usr << " Replied To: [T.active]/(LOGS)" + usr << " Resolved: [T.resolved]" + + if(upass == 0) + usr << " None" + +/client/proc/createticket(player, message, uckey, mob) + var/datum/adminticket/A = new() + A.user = player + A.msg = message + A.uckey = uckey + A.permckey = uckey + A.permuser = A.user + admintickets += A + A.logs += "ADMINHELP: [A.permckey]([A.permuser]): [A.msg]" + A.mob = mob + + var/index = 0 + for(var/datum/adminticket/T in admintickets) + index++ + T.ID = index + T.uID = "[T.permckey][T.ID]" + +/client/verb/resolveticketself() + set category = "Admin" + set name = "Resolve My Adminhelp" + set desc = "Resolve my own adminhelp" + + var/pass = 0 + var/datum/adminticket/ticket + + for(var/datum/adminticket/T in admintickets) + if(T.permckey == ckey && T.resolved != "Yes") + T.resolved = "Yes" + ticket = T + pass = 1 + + switch(pass) + if(1) + src << "You have resolved your current adminhelp." + message_admins("[src] has resolved his adminhelp (#[ticket.ID])") + if(0) + src << "Error, you do not have any active adminhelps." + +/client/proc/resolvehandlingahelp() + set category = "Admin Help" + set name = "Resolve Handling Ahelp" + set desc = "Resolve my own adminhelp" + + if(!check_rights(R_BAN)) + src << "Error: Only administrators may use this command." + return + + var/count = 0 + + var/datum/adminticket/ticket + + for(var/datum/adminticket/T in admintickets) + if(T.admin == ckey && T.resolved != "Yes") + count++ + ticket = T + + if(count >= 1) + usr << "Adminhelp #[ticket.ID]([ticket.uID]) resolved." + message_admins("Adminhelp ID: #[ticket.ID]([ticket.uID]) was resolved by [usr.ckey]") + ticket.user << "Your adminhelp (#[ticket.ID]) has been resolved by [usr.ckey]" + ticket.user << 'sound/machines/twobeep.ogg' + ticket.resolved = "Yes" + + if(count < 1) + usr << "You are not currently handling any adminhelps!" + +/datum/adminticket/proc/viewlogs(NuID, mob/user) + var/dat = "

View Logs for ahelp [NuID]

" + var/datum/adminticket/ticket + + var/pass = 0 + + for(var/datum/adminticket/T in admintickets) + if(NuID == T.uID) + ticket = T + pass = 1 + + if(pass == 0) + src << "Error, log system not found for [NuID]... " + return + + dat += "" + for(var/text in ticket.logs) + dat += "" + dat += "
[text]
" + + var/datum/browser/popup = new(user, "ahelp logs", ticket.permuser, 500, 500) + popup.set_content(dat) + popup.open() + +/datum/adminticket/Topic(href, href_list) + if(href_list["view_logs"]) + var/datum/adminticket/T = locate(href_list["view_logs"]) + viewlogs(T.uID, usr) + if(href_list["resolve"]) + var/datum/adminticket/T = locate(href_list["resolve"]) + message_admins("Adminhelp ID: #[T.ID]([T.uID]) was [T.resolved == "Yes" ? "unresolved" : "resolved"] by [usr.ckey]") + T.user << "Your adminhelp (#[T.ID]) has been [T.resolved == "Yes" ? "unresolved" : "resolved"] by [usr.ckey]" + T.user << 'sound/machines/twobeep.ogg' + T.resolved = "[T.resolved == "Yes" ? "No" : "Yes"]" \ No newline at end of file diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index d3705b9648..2020fad27c 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -136,6 +136,10 @@ var/next_external_rsc = 0 admins |= src holder.owner = src + if(check_rights(R_ADMIN)) + if(ahelp_count(0) > 0) + list_ahelps(src, 0) + //preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum) prefs = preferences_datums[ckey] if(!prefs) diff --git a/tgstation.dme b/tgstation.dme index 85cf221c3f..e365563baa 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -886,6 +886,7 @@ #include "code\modules\admin\verbs\diagnostics.dm" #include "code\modules\admin\verbs\fps.dm" #include "code\modules\admin\verbs\getlogs.dm" +#include "code\modules\admin\verbs\listahelps.dm" #include "code\modules\admin\verbs\machine_upgrade.dm" #include "code\modules\admin\verbs\manipulate_organs.dm" #include "code\modules\admin\verbs\map_template_loadverb.dm"