diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index b6c1c00339c..984e8418a60 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -27,9 +27,13 @@ else if (!dooc_allowed && !src.client.holder && (src.client.deadchat != 0)) usr << "\red OOC for dead mobs has been turned off." return - else if (src.client && (src.client.muted || src.client.muted_complete)) - src << "You are muted." - return + else if (src.client) + if(src.client.muted_ooc) + src << "\red You cannot use OOC (muted by admins)." + return + + if (src.client.handle_spam_prevention(msg,MUTE_OOC)) + return else if (findtext(msg, "byond://") && !src.client.holder) src << "Advertising other servers is not allowed." log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]") diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index f81c58ffb7e..7c86e0ac061 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -752,34 +752,23 @@ var/global/BSACooldown = 0 message_admins("\blue [key_name_admin(usr)] removed [t] from the goonlist.") remove_goon(t) */ - if (href_list["mute2"]) - if ((src.rank in list( "Moderator", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" ))) - var/mob/M = locate(href_list["mute2"]) + if (href_list["mute"]) + if ((src.rank in list( "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" ))) + var/mob/M = locate(href_list["mute"]) + var/mute_type = href_list["mute_type"] + if(istext(mute_type)) + mute_type = text2num(mute_type) + if(!isnum(mute_type)) + return if (ismob(M)) - if ((M.client && M.client.holder && (M.client.holder.level >= src.level))) - alert("You cannot perform this action. You must be of a higher administrative rank!", null, null, null, null, null) - return if(!M.client) src << "This mob doesn't have a client tied to it." return - M.client.muted = !M.client.muted - log_admin("[key_name(usr)] has [(M.client.muted ? "muted" : "voiced")] [key_name(M)].") - message_admins("\blue [key_name_admin(usr)] has [(M.client.muted ? "muted" : "voiced")] [key_name_admin(M)].", 1) - M << "You have been [(M.client.muted ? "muted" : "voiced")]. Please resolve this in adminhelp." - if (href_list["mute_complete"]) - if ((src.rank in list( "Moderator", "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" ))) - var/mob/M = locate(href_list["mute_complete"]) - if (ismob(M)) if ((M.client && M.client.holder && (M.client.holder.level >= src.level))) alert("You cannot perform this action. You must be of a higher administrative rank!", null, null, null, null, null) return - if(!M.client) - src << "This mob doesn't have a client tied to it." - return - M.client.muted_complete = !M.client.muted_complete - log_admin("[key_name(usr)] has [(M.client.muted_complete ? "completely muted" : "voiced (complete)")] [key_name(M)].") - message_admins("\blue [key_name_admin(usr)] has [(M.client.muted_complete ? "completely muted" : "voiced (complete)")] [key_name_admin(M)].", 1) - M << "You have been [(M.client.muted_complete ? "completely muted" : "voiced (complete)")]. You are unable to speak or even adminhelp" + + cmd_admin_mute(M, mute_type) if (href_list["c_mode"]) if ((src.rank in list( "Temporary Admin", "Admin Candidate", "Trial Admin", "Badmin", "Game Admin", "Game Master" ))) @@ -1234,6 +1223,10 @@ var/global/BSACooldown = 0 if (href_list["adminmoreinfo"]) var/mob/M = locate(href_list["adminmoreinfo"]) + if(!M) + usr << "\blue The mob no longer exists." + return + if(src && src.owner) var/location_description = "" var/special_role_description = "" @@ -2279,14 +2272,13 @@ var/global/BSACooldown = 0 if(M.client) body += "| Prison | " - body += "Mute: " - if(M.client.muted_complete) - body += "Completely Muted: (Allow adminhelp)" - else - if(M.client.muted) - body += "Soft Mute: (Unmute) (Mute adminhelps)" - else - body += "Voiced: (Mute)" + body += "
Mute: " + body += "\[IC | " + body += "OOC | " + body += "PRAY | " + body += "ADMINHELP | " + body += "DEADCHAT\]" + body += "(toggle all)" body += "

" body += "Jump to | " diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 0fbe6e98981..ef915f3698a 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -1,14 +1,17 @@ //This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE! -var/list/adminhelp_ignored_words = list("unknown","the","a","an", "monkey", "alien") +var/list/adminhelp_ignored_words = list("unknown","the","a","an", "monkey", "alien", "as") /client/verb/adminhelp(msg as text) set category = "Admin" set name = "Adminhelp" - if (muted_complete) - src << "Error: Admin-PM: You are completely muted." + if (muted_adminhelp) + src << "Error: Admin-PM: You cannot send adminhelps (Muted by admins)." + return + + if (src.handle_spam_prevention(msg,MUTE_ADMINHELP)) return if(!msg) return @@ -80,7 +83,7 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an", "monkey", "ali if (X.holder) if(X.sound_adminhelp) X << 'adminhelp.ogg' - var/msg_to_send = "\blue HELP: [key_name(src, X)] (PP) (VV) (SM) (JMP) (CA): [msg]" + var/msg_to_send = "\blue HELP: [key_name(src, X)] (?) (PP) (VV) (SM) (JMP) (CA): [msg]" msg_to_send = dd_replaceText(msg_to_send, "HOLDERREF", "\ref[X.holder]") msg_to_send = dd_replaceText(msg_to_send, "ADMINREF", "\ref[X]") X << msg_to_send diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 8411ad31577..3894a31bed2 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -36,8 +36,11 @@ //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(var/client/C, var/msg) - if(src.muted_complete) - src << "Error: Admin-PM: You are completely muted." + if(src.muted_adminhelp) + src << "Error: Admin-PM: You are unable to use admin PM-s (muted by admins)." + return + + if (src.handle_spam_prevention(msg,MUTE_ADMINHELP)) return if( !C || !istype(C,/client) ) diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 40f4ee1a588..f82d6ef0456 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -7,8 +7,11 @@ src << "Only administrators may use this command." return - if (src.muted || src.muted_complete) - src << "You are muted." + if (src.muted_adminhelp) + src << "You cannot send ASAY messages (muted by admins)." + return + + if (src.handle_spam_prevention(msg,MUTE_ADMINHELP)) return msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 4ef4d3e72c7..9e4d5dc876a 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -7,7 +7,11 @@ return if(!src.mob) return - if(src.muted || src.muted_complete) + if(src.muted_deadchat) + src << "\red You cannot send DSAY messages (muted by admins)." + return + + if (src.handle_spam_prevention(msg,MUTE_DEADCHAT)) return msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN) diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index f6f9cfa87dd..68d02a9afd6 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -7,14 +7,18 @@ if (!msg) return - if (usr.client && (usr.client.muted || usr.client.muted_complete)) - return + if (usr.client) + if(usr.client.muted_pray) + return + + if (src.client.handle_spam_prevention(msg,MUTE_PRAY)) + return var/icon/cross = icon('storage.dmi',"bible") for (var/mob/M in world) if (M.client && M.client.holder && M.client.seeprayers) - M << "\blue \icon[cross] PRAY: [key_name(src, M)] (PP) (VV) (SM) (JMP) (CA) (SC): [msg]" + M << "\blue \icon[cross] PRAY: [key_name(src, M)] (?) (PP) (VV) (SM) (JMP) (CA) (SC): [msg]" usr << "Your prayers have been received by the gods." feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 7aa60025c2d..dff13da6bc8 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -123,25 +123,73 @@ message_admins("[key_name_admin(usr)] has toggled [key_name_admin(M)]'s nodamage to [(M.nodamage ? "On" : "Off")]", 1) feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/cmd_admin_mute(mob/M as mob in world) - set category = "Special Verbs" - set name = "Admin Mute" - if(!holder) - src << "Only administrators may use this command." - return - if (M.client && M.client.holder && (M.client.holder.level >= holder.level)) - alert("You cannot perform this action. You must be of a higher administrative rank!", null, null, null, null, null) - return +proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) + if(!automute) + if(usr && usr.client) + if(!usr.client.holder) + src << "Only administrators may use this command." + return + if (M.client && M.client.holder && (M.client.holder.level >= usr.client.holder.level)) + alert("You cannot perform this action. You must be of a higher administrative rank!", null, null, null, null, null) + return if(!M.client) src << "This mob doesn't have a client tied to it." return - M.client.muted = !M.client.muted - log_admin("[key_name(src)] has [(M.client.muted ? "muted" : "voiced")] [key_name(M)].") - message_admins("[key_name_admin(src)] has [(M.client.muted ? "muted" : "voiced")] [key_name_admin(M)].", 1) + var/muteunmute = 0 //0 = unmuted; 1 = muted + var/mute_string = "unknown" - M << "You have been [(M.client.muted ? "muted" : "voiced")]." - feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + //The '| automute' thing ensures that if an automute is being applied by code, it always mutes to prevent any potential for automute to unmute someone who was muted. + switch(mute_type) + if(MUTE_IC) + M.client.muted_ic = !M.client.muted_ic | automute + muteunmute = M.client.muted_ic + mute_string = "IC (say and emote)" + if(MUTE_OOC) + M.client.muted_ooc = !M.client.muted_ooc | automute + muteunmute = M.client.muted_ooc + mute_string = "OOC" + if(MUTE_PRAY) + M.client.muted_pray = !M.client.muted_pray | automute + muteunmute = M.client.muted_pray + mute_string = "pray" + if(MUTE_ADMINHELP) + M.client.muted_adminhelp = !M.client.muted_adminhelp | automute + muteunmute = M.client.muted_adminhelp + mute_string = "adminhelp, admin PM and ASAY" + if(MUTE_DEADCHAT) + M.client.muted_deadchat = !M.client.muted_deadchat | automute + muteunmute = M.client.muted_deadchat + mute_string = "deadchat and DSAY" + if(MUTE_ALL) + mute_string = "everything" + if( M.client.muted_ic ) + M.client.muted_ic = 1 + M.client.muted_ooc = 1 + M.client.muted_pray = 1 + M.client.muted_adminhelp = 1 + M.client.muted_deadchat = 1 + muteunmute = 1 + else + M.client.muted_ic = 0 + M.client.muted_ooc = 0 + M.client.muted_pray = 0 + M.client.muted_adminhelp = 0 + M.client.muted_deadchat = 0 + muteunmute = 0 + + if(!automute) + log_admin("[key_name(usr)] has [(muteunmute ? "muted" : "voiced")] [key_name(M)] from [mute_string]") + message_admins("[key_name_admin(usr)] has [(muteunmute ? "muted" : "voiced")] [key_name_admin(M)] from [mute_string].", 1) + + M << "You have been [(muteunmute ? "muted" : "voiced")] from [mute_string] by [(usr.client.stealth)?"an admin":"[usr.client]"]." + feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + else + log_admin("SPAM AUTOMUTE: [(muteunmute ? "muted" : "voiced")] [key_name(M)] from [mute_string]") + message_admins("SPAM AUTOMUTE: [(muteunmute ? "muted" : "voiced")] [key_name_admin(M)] from [mute_string].", 1) + + M << "You have been [(muteunmute ? "muted" : "voiced")] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin." + feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/cmd_admin_add_random_ai_law() diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 07f851d6580..f267a6bd766 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -9,8 +9,16 @@ var/fakekey = null var/seeprayers = 0 var/ooccolor = "#b82e00" - var/muted = null //Can't talk in OOC, say, whisper, emote... anything except for adminhelp and admin-pm. An admin punishment - var/muted_complete = null //Can't talk in any way shape or form (muted + can't adminhelp or respond to admin pm-s). An admin punishment + + var/muted_ic //can't use 'say' while alive or emotes. + var/muted_ooc //can't speak in ooc + var/muted_deadchat //can't use 'say' while dead or DSAY + var/muted_pray //can't send prayers + var/muted_adminhelp //can't send adminhelps, PM-s or use ASAY + + var/last_message = "" //Contains the last message sent by this client - used to protect against copy-paste spamming. + var/last_message_count = 0 //contins a number of how many times a message identical to last_message was sent. + var/warned = 0 var/sound_adminhelp = 0 //If set to 1 this will play a sound when adminhelps are received. diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index ec367a706fc..90d1c2b6bd4 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -52,6 +52,20 @@ ..() //redirect to [locate(hsrc)]/Topic() +/client/proc/handle_spam_prevention(var/message, var/mute_type) + if(src.last_message == message) + src.last_message_count++ + if(src.last_message_count >= SPAM_TRIGGER_AUTOMUTE) + src << "\red You have exceeded the spam filter limit for identical messages. An auto-mute was applied." + cmd_admin_mute(src.mob, mute_type, 1) + return 1 + if(src.last_message_count >= SPAM_TRIGGER_WARNING) + src << "\red You are nearing the spam filter limit for identical messages." + return 0 + else + last_message = message + src.last_message_count = 0 + return 0 //This stops files larger than UPLOAD_LIMIT being sent from client to server via input(), client.Import() etc. /client/AllowUpload(filename, filelength) diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 9c015e7e880..4a39d8f0fb5 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -9,9 +9,13 @@ log_say("Ghost/[src.key] : [message]") - if (src.client && (src.client.muted || src.client.muted_complete)) - src << "You are muted." - return + if (src.client) + if(src.client.muted_deadchat) + src << "\red You cannot talk in deadchat (muted by admins)." + return + + if (src.client.handle_spam_prevention(message,MUTE_DEADCHAT)) + return . = src.say_dead(message) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 1da2e94e0f0..194f609a780 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -65,9 +65,12 @@ if ("me") if(silent) return - if (src.client && (client.muted || client.muted_complete)) - src << "You are muted." - return + if (src.client) + if (client.muted_ic) + src << "\red You cannot send IC messages (muted by admins)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return if (stat) return if(!(message)) diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index 3de25582982..0c79f8e1af3 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -10,9 +10,14 @@ log_whisper("[src.name]/[src.key] : [message]") - if (src.client && (src.client.muted || src.client.muted_complete)) - src << "You are muted." - return + if (src.client) + if (src.client.muted_ic) + src << "\red You cannot whisper (muted by admins)." + return + + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return + if (src.stat == 2) return src.say_dead(message) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 42dbffffd4c..fac157c90df 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -92,9 +92,12 @@ var/list/department_radio_keys = list( if (silent) return - if (src.client && (client.muted || src.client.muted_complete)) - src << "You are muted." - return + if (src.client) + if(client.muted_ic) + src << "\red You cannot speak in IC (muted by admins)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return // stat == 2 is handled above, so this stops transmission of uncontious messages if (stat) diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index 89571eff031..db694473ff9 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -70,9 +70,12 @@ if ("me") if(silent) return - if (src.client && (client.muted || client.muted_complete)) - src << "You are muted." - return + if (src.client) + if(client.muted_ic) + src << "You cannot send IC messages (muted by admins)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return if (stat) return if(!(message)) diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 069cb12d227..96369dc814d 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -2,9 +2,12 @@ if (!message) return - if (src.client && (client.muted || src.client.muted_complete)) - src << "You are muted." - return + if (src.client) + if(client.muted_ic) + src << "You cannot send IC messages (muted by admins)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return if (stat == 2) message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) diff --git a/code/setup.dm b/code/setup.dm index 889878a2ad0..9dfa29e1947 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -361,3 +361,14 @@ var/list/liftable_structures = list(\ /obj/structure/filingcabinet, \ /obj/structure/reagent_dispensers, \ /obj/machinery/portable_atmospherics/canister) + +//A set of constants used to determine which type of mute an admin wishes to apply: +#define MUTE_IC 1 +#define MUTE_OOC 2 +#define MUTE_PRAY 3 +#define MUTE_ADMINHELP 4 +#define MUTE_DEADCHAT 5 +#define MUTE_ALL 6 + +#define SPAM_TRIGGER_WARNING 5 +#define SPAM_TRIGGER_AUTOMUTE 10 \ No newline at end of file