From 441c79dac5805ef46db8dfa87d314d3b79b681c9 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Thu, 28 Dec 2023 10:10:20 -0600 Subject: [PATCH] TGS Support Updates (#2552) Updates our TGS chat commands to use the new framework, and adds a few more commands similar to our redbot's. Also adds a feature where the round will automatically be delayed when a TGS deployment is incoming. Better access to commands for those who don't want to set up our external redbot. Plus, it's nice to support the new features. Also, delaying the round before a deploy certainly saves me a lot of headaches from trying to squeeze in a deployment before round restart. :cl: admin: The server will now automatically delay round-end when a TGS deployment is incoming server: Adds more TGS chat commands as well as support for the newer chat message system. /:cl: --- code/__DEFINES/misc.dm | 4 + code/__HELPERS/chat.dm | 11 +- code/controllers/subsystem/ping.dm | 4 +- code/datums/tgs_event_handler.dm | 25 ++- code/modules/admin/chat_commands.dm | 200 ++++++++++++++++++++++-- code/modules/admin/verbs/adminhelp.dm | 16 +- code/modules/admin/verbs/panicbunker.dm | 4 +- code/modules/discord/tgs_commands.dm | 11 +- 8 files changed, 233 insertions(+), 42 deletions(-) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index e8975a1a6653..9dbe4fae64e9 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -474,3 +474,7 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE #define CUSTOM_HOLODECK_ONE (1<<1) #define CUSTOM_HOLODECK_TWO (1<<2) #define HOLODECK_DEBUG (1<<3)//you should never see this + +#define ROUND_END_NOT_DELAYED 0 +#define ROUND_END_DELAYED 1 +#define ROUND_END_TGS 2 diff --git a/code/__HELPERS/chat.dm b/code/__HELPERS/chat.dm index 2a546370ec0e..c265623b1643 100644 --- a/code/__HELPERS/chat.dm +++ b/code/__HELPERS/chat.dm @@ -47,18 +47,17 @@ In TGS3 it will always be sent to all connected designated game chats. var/datum/tgs_version/version = world.TgsVersion() if(channel_tag == "" || version.suite == 3) - world.TgsTargetedChatBroadcast(message, FALSE) + world.TgsTargetedChatBroadcast(new /datum/tgs_message_content(message), FALSE) return var/list/channels_to_use = list() - for(var/I in world.TgsChatChannelInfo()) - var/datum/tgs_chat_channel/channel = I + for(var/datum/tgs_chat_channel/channel as anything in world.TgsChatChannelInfo()) var/list/applicable_tags = splittext(channel.custom_tag, ",") if(channel_tag in applicable_tags) channels_to_use += channel - if(channels_to_use.len) - world.TgsChatBroadcast(message, channels_to_use) + if(length(channels_to_use)) + world.TgsChatBroadcast(new /datum/tgs_message_content(message), channels_to_use) /** * Sends a message to TGS admin chat channels. @@ -69,4 +68,4 @@ In TGS3 it will always be sent to all connected designated game chats. /proc/send2adminchat(category, message) category = replacetext(replacetext(category, "\proper", ""), "\improper", "") message = replacetext(replacetext(message, "\proper", ""), "\improper", "") - world.TgsTargetedChatBroadcast("[category] | [message]", TRUE) + world.TgsTargetedChatBroadcast(new /datum/tgs_message_content("[category] | [message]"), TRUE) diff --git a/code/controllers/subsystem/ping.dm b/code/controllers/subsystem/ping.dm index 8886a4b61caa..cf99a4fe49c5 100644 --- a/code/controllers/subsystem/ping.dm +++ b/code/controllers/subsystem/ping.dm @@ -13,8 +13,8 @@ SUBSYSTEM_DEF(ping) var/list/currentrun = list() -/datum/controller/subsystem/ping/stat_entry() - ..("P:[GLOB.clients.len]") +/datum/controller/subsystem/ping/stat_entry(msg) + return ..("P:[GLOB.clients.len]") /datum/controller/subsystem/ping/fire(resumed = FALSE) // Prepare the new batch of clients diff --git a/code/datums/tgs_event_handler.dm b/code/datums/tgs_event_handler.dm index 55c7c6427749..dda39c21df55 100644 --- a/code/datums/tgs_event_handler.dm +++ b/code/datums/tgs_event_handler.dm @@ -1,5 +1,6 @@ /datum/tgs_event_handler/impl var/datum/timedevent/reattach_timer + var/datum/timedevent/delay_end_timer /datum/tgs_event_handler/impl/HandleEvent(event_code, ...) switch(event_code) @@ -14,28 +15,44 @@ message_admins("TGS: Instance renamed to from [world.TgsInstanceName()] to [args[2]]") if(TGS_EVENT_COMPILE_START) message_admins("TGS: Deployment started, new game version incoming...") + SSticker.delay_end = ROUND_END_TGS + SSticker.admin_delay_notice = "TGS Deployment in progress, please wait..." + delay_end_timer = addtimer(CALLBACK(src, PROC_REF(end_delay)), 5 MINUTES, TIMER_STOPPABLE) if(TGS_EVENT_COMPILE_CANCELLED) message_admins("TGS: Deployment cancelled!") + end_delay() if(TGS_EVENT_COMPILE_FAILURE) message_admins("TGS: Deployment failed!") + end_delay() if(TGS_EVENT_DEPLOYMENT_COMPLETE) message_admins("TGS: Deployment complete!") - to_chat(world, "Server updated, changes will be applied on the next round...") + to_chat(world, span_boldannounce("Server updated, changes will be applied on the next round...")) + end_delay() if(TGS_EVENT_WATCHDOG_DETACH) message_admins("TGS restarting...") - reattach_timer = addtimer(CALLBACK(src, PROC_REF(LateOnReattach)), 1 MINUTES) + reattach_timer = addtimer(CALLBACK(src, PROC_REF(LateOnReattach)), 1 MINUTES, TIMER_STOPPABLE) if(TGS_EVENT_WATCHDOG_REATTACH) var/datum/tgs_version/old_version = world.TgsVersion() var/datum/tgs_version/new_version = args[2] if(!old_version.Equals(new_version)) - to_chat(world, "TGS updated to v[new_version.deprefixed_parameter]") + to_chat(world, span_boldannounce("TGS updated to v[new_version.deprefixed_parameter]")) else message_admins("TGS: Back online") if(reattach_timer) deltimer(reattach_timer) reattach_timer = null if(TGS_EVENT_WATCHDOG_SHUTDOWN) - to_chat_immediate(world, "Server is shutting down!") + to_chat_immediate(world, span_boldannounce("Server is shutting down!")) /datum/tgs_event_handler/impl/proc/LateOnReattach() message_admins("Warning: TGS hasn't notified us of it coming back for a full minute! Is there a problem?") + +/datum/tgs_event_handler/impl/proc/end_delay() + if(SSticker.delay_end == ROUND_END_TGS) + SSticker.delay_end = ROUND_END_NOT_DELAYED + SSticker.admin_delay_notice = null + if(delay_end_timer) + deltimer(delay_end_timer) + delay_end_timer = null + if(SSticker.ready_for_reboot && !SSticker.delay_end) //we undelayed after standard reboot would occur + SSticker.standard_reboot() diff --git a/code/modules/admin/chat_commands.dm b/code/modules/admin/chat_commands.dm index 53c28d27974e..4a3e95bdf230 100644 --- a/code/modules/admin/chat_commands.dm +++ b/code/modules/admin/chat_commands.dm @@ -13,16 +13,30 @@ break if(!active_admins) SSticker.Reboot("Restart requested from the discord.", "discord") +<<<<<<< HEAD return "Запущен перезапуск..." else return "На данный момент имеются активные администраторы на сервере! Перезапуск через Discord невозможен!" +======= + return new /datum/tgs_message_content("Запущен перезапуск...") + else + return new /datum/tgs_message_content("На данный момент имеются активные администраторы на сервере! Перезапуск через Discord невозможен!") +>>>>>>> 35406f91c0 (TGS Support Updates (#2552)) /datum/tgs_chat_command/join name = "join" help_text = "Покажет ссылку для подключения к игре." /datum/tgs_chat_command/join/Run(datum/tgs_chat_user/sender, params) - return "<[world.internet_address]:[world.port]>" + var/datum/tgs_chat_embed/structure/embed = new() + embed.title = "Join Server" + embed.colour = COLOR_DARK_CYAN + embed.description = "Enter this URL into the BYOND pager to join the server: byond://[world.internet_address]:[world.port]" + + var/datum/tgs_message_content/join = new() + join.embed = embed + + return join /datum/tgs_chat_command/tgsstatus name = "status" @@ -33,8 +47,9 @@ /datum/tgs_chat_command/tgsstatus/Run(datum/tgs_chat_user/sender, params) var/rtod = REALTIMEOFDAY if(rtod - last_tgs_status < TGS_STATUS_THROTTLE) - return + return new /datum/tgs_message_content("Please wait a few seconds before using this command again.") last_tgs_status = rtod +<<<<<<< HEAD var/list/adm = get_admin_counts() var/list/allmins = adm["total"] var/status = "Администраторы: [allmins.len] (Активные: [english_list(adm["present"])] AFK: [english_list(adm["afk"])] Скрытые: [english_list(adm["stealth"])] Пропущенные: [english_list(adm["noflags"])]). " @@ -42,8 +57,64 @@ for(var/c in GLOB.clients) var/client/C = c status += "\n[C.key]" +======= + + var/datum/tgs_chat_embed/structure/embed = new() + embed.title = "Server Admin Status" + embed.colour = COLOR_DARK_CYAN + + embed.fields = list() + embed.fields += new /datum/tgs_chat_embed/field("Раунд", "[GLOB.round_id ? "Раунд #[GLOB.round_id]" : "Не запущен"]\n[station_name()]\n[length(SSovermap.controlled_ships)] кораблей") + embed.fields += new /datum/tgs_chat_embed/field("Администраторы", tgsadminwho()) + embed.fields += new /datum/tgs_chat_embed/field("Игроки", "Всего: [length(GLOB.clients)]\nАктивных: [get_active_player_count(FALSE, TRUE, FALSE)]\nЖивых: [get_active_player_count(TRUE, TRUE, TRUE)]") + + embed.fields += new /datum/tgs_chat_embed/field("Тикеты", "Активных: [length(GLOB.ahelp_tickets.active_tickets)]\nВыполненых: [length(GLOB.ahelp_tickets.resolved_tickets)]\nЗакрытых: [length(GLOB.ahelp_tickets.closed_tickets)]") + embed.fields += new /datum/tgs_chat_embed/field("Interviews", "Open: [length(GLOB.interviews.open_interviews) - length(GLOB.interviews.interview_queue)]\nSubmitted: [length(GLOB.interviews.interview_queue)]\nClosed: [length(GLOB.interviews.closed_interviews)]") + + embed.fields += new /datum/tgs_chat_embed/field("Режим", "[SSticker.mode ? SSticker.mode.name : "Не запущен"]") + embed.fields += new /datum/tgs_chat_embed/field("Время раунда", ROUND_TIME) + embed.fields += new /datum/tgs_chat_embed/field("Time Dilation", "[round(SStime_track.time_dilation_current, 0.1)]% ([round(SStime_track.time_dilation_avg, 0.1)]% avg)") + + for(var/datum/tgs_chat_embed/field/field as anything in embed.fields) + field.is_inline = TRUE + + var/datum/tgs_message_content/status = new() + status.embed = embed + +>>>>>>> 35406f91c0 (TGS Support Updates (#2552)) return status +/datum/tgs_chat_command/subsystems + name = "subsystems" + help_text = "Gets the status of the server subsystems" + admin_only = TRUE + var/last_tgs_subsystems = 0 + +/datum/tgs_chat_command/subsystems/Run(datum/tgs_chat_user/sender, params) + var/rtod = REALTIMEOFDAY + if(rtod - last_tgs_subsystems < TGS_STATUS_THROTTLE) + return new /datum/tgs_message_content("Please wait a few seconds before using this command again.") + last_tgs_subsystems = rtod + + var/datum/tgs_chat_embed/structure/embed = new() + embed.title = "Server Subsystems" + embed.colour = COLOR_DARK_CYAN + + embed.description = Master.stat_entry() + + embed.fields = list() + for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems) + if(params && !findtext(sub_system.name, params)) + continue + var/datum/tgs_chat_embed/field/sub_system_entry = new ("\[[sub_system.state_letter()]] [sub_system.name]", sub_system.stat_entry()) + sub_system_entry.is_inline = TRUE + embed.fields += sub_system_entry + + var/datum/tgs_message_content/subsystems = new() + subsystems.embed = embed + + return subsystems + /datum/tgs_chat_command/tgscheck name = "check" help_text = "Показывает количество игроков, игровой режим и ссылку для доступа к игре." @@ -52,10 +123,32 @@ /datum/tgs_chat_command/tgscheck/Run(datum/tgs_chat_user/sender, params) var/rtod = REALTIMEOFDAY if(rtod - last_tgs_check < TGS_STATUS_THROTTLE) - return + return new /datum/tgs_message_content("Please wait a few seconds before using this command again.") last_tgs_check = rtod +<<<<<<< HEAD var/server = CONFIG_GET(string/server) return "[GLOB.round_id ? "Раунд #[GLOB.round_id]: " : ""][GLOB.clients.len] игроков, Игровой режим: [GLOB.master_mode]; Раунд [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "byond://celadon.pro:1337"]" +======= + + var/datum/tgs_chat_embed/structure/embed = new() + embed.title = "Состояние сервера" + embed.colour = COLOR_DARK_CYAN + + embed.fields = list() + embed.fields += new /datum/tgs_chat_embed/field("Раунд", "[GLOB.round_id ? "Раунд #[GLOB.round_id]" : "Не запущен"]") + embed.fields += new /datum/tgs_chat_embed/field("Игроки", "[length(GLOB.player_list) || "отсутствуют"]") + embed.fields += new /datum/tgs_chat_embed/field("Администраторы", "[length(GLOB.admins) || "отсутствуют"]") + embed.fields += new /datum/tgs_chat_embed/field("Время раунда", ROUND_TIME) + embed.fields += new /datum/tgs_chat_embed/field("Time Dilation", "[round(SStime_track.time_dilation_current, 0.1)]% ([round(SStime_track.time_dilation_avg, 0.1)]% avg)") + + for(var/datum/tgs_chat_embed/field/field as anything in embed.fields) + field.is_inline = TRUE + + var/datum/tgs_message_content/status = new() + status.embed = embed + + return status +>>>>>>> 35406f91c0 (TGS Support Updates (#2552)) /datum/tgs_chat_command/ahelp name = "ahelp" @@ -65,7 +158,11 @@ /datum/tgs_chat_command/ahelp/Run(datum/tgs_chat_user/sender, params) var/list/all_params = splittext(params, " ") if(all_params.len < 2) +<<<<<<< HEAD return "Неверные параметры." +======= + return new /datum/tgs_message_content("Неверные параметры.") +>>>>>>> 35406f91c0 (TGS Support Updates (#2552)) var/target = all_params[1] all_params.Cut(1, 2) var/id = text2num(target) @@ -74,10 +171,14 @@ if(AH) target = AH.initiator_ckey else +<<<<<<< HEAD return "Тикет #[id] не найден!" +======= + return new /datum/tgs_message_content("Тикет #[id] не найден!") +>>>>>>> 35406f91c0 (TGS Support Updates (#2552)) var/res = TgsPm(target, all_params.Join(" "), sender.friendly_name) if(res != "Message Successful") - return res + return new /datum/tgs_message_content(res) /datum/tgs_chat_command/namecheck name = "namecheck" @@ -87,10 +188,10 @@ /datum/tgs_chat_command/namecheck/Run(datum/tgs_chat_user/sender, params) params = trim(params) if(!params) - return "Insufficient parameters" + return new /datum/tgs_message_content("Please specify a target.") log_admin("Chat Name Check: [sender.friendly_name] on [params]") message_admins("Name checking [params] from [sender.friendly_name]") - return keywords_lookup(params, 1) + return new /datum/tgs_message_content(keywords_lookup(params, TRUE)) /datum/tgs_chat_command/adminwho name = "adminwho" @@ -98,7 +199,15 @@ admin_only = TRUE /datum/tgs_chat_command/adminwho/Run(datum/tgs_chat_user/sender, params) - return tgsadminwho() + var/datum/tgs_chat_embed/structure/embed = new() + embed.title = "Admins" + embed.colour = COLOR_DARK_CYAN + embed.description = tgsadminwho() || "No admins online." + + var/datum/tgs_message_content/adminwho = new() + adminwho.embed = embed + + return adminwho GLOBAL_LIST(round_end_notifiees) @@ -111,7 +220,11 @@ GLOBAL_LIST(round_end_notifiees) if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted()) return "[sender.mention], раунд уже закончился!" LAZYSET(GLOB.round_end_notifiees, sender.mention, TRUE) +<<<<<<< HEAD return "Я дам знать, [sender.mention], когда закончится раунд." +======= + return new /datum/tgs_message_content( "Я оповещу, [sender.mention], когда закончится раунд.") +>>>>>>> 35406f91c0 (TGS Support Updates (#2552)) /datum/tgs_chat_command/sdql name = "sdql" @@ -120,15 +233,26 @@ GLOBAL_LIST(round_end_notifiees) /datum/tgs_chat_command/sdql/Run(datum/tgs_chat_user/sender, params) if(GLOB.AdminProcCaller) - return "Unable to run query, another admin proc call is in progress. Try again later." + return new /datum/tgs_message_content("Unable to run query, another admin proc call is in progress. Try again later.") GLOB.AdminProcCaller = "CHAT_[sender.friendly_name]" //_ won't show up in ckeys so it'll never match with a real admin var/list/results = world.SDQL2_query(params, GLOB.AdminProcCaller, GLOB.AdminProcCaller) GLOB.AdminProcCaller = null if(!results) - return "Query produced no output" + return new /datum/tgs_message_content("Query produced no output.") var/list/text_res = results.Copy(1, 3) var/list/refs = results.len > 3 ? results.Copy(4) : null - . = "[text_res.Join("\n")][refs ? "\nRefs: [refs.Join(" ")]" : ""]" + + var/datum/tgs_chat_embed/structure/embed = new() + embed.title = "SDQL Query Results" + embed.colour = COLOR_DARK_CYAN + embed.description = text_res.Join("\n") || "No results." + embed.fields = list() + embed.fields += new /datum/tgs_chat_embed/field("Refs", refs ? refs.Join("\n") : "None") + + var/datum/tgs_message_content/sdql = new() + sdql.embed = embed + + return sdql /datum/tgs_chat_command/reload_admins name = "reload_admins" @@ -138,8 +262,64 @@ GLOBAL_LIST(round_end_notifiees) /datum/tgs_chat_command/reload_admins/Run(datum/tgs_chat_user/sender, params) ReloadAsync() log_admin("[sender.friendly_name] reloaded admins via chat command.") +<<<<<<< HEAD return "Администраторы перезагружены." +======= + return new /datum/tgs_message_content("Администраторы перезагружены.") +>>>>>>> 35406f91c0 (TGS Support Updates (#2552)) /datum/tgs_chat_command/reload_admins/proc/ReloadAsync() set waitfor = FALSE load_admins() + +/datum/tgs_chat_command/manifest + name = "manifest" + help_text = "Displays the current crew manifest" + +/datum/tgs_chat_command/manifest/Run(datum/tgs_chat_user/sender, params) + var/list/manifest = SSovermap.get_manifest() + + var/datum/tgs_chat_embed/structure/embed = new() + embed.title = "__Crew Manifest:__" + embed.colour = COLOR_DARK_CYAN + + if(!length(manifest)) + embed.description = "No crew manifest available." + else + embed.fields = list() + for(var/ship in manifest) + var/list/entries = manifest[ship] + var/list/ship_entries = list() + for(var/entry in entries) + var/list/entry_list = entry + ship_entries += "[entry_list["name"]]: [entry_list["rank"]]" + + var/datum/tgs_chat_embed/field/ship_field = new(ship, ship_entries.Join("\n")) + ship_field.is_inline = TRUE + embed.fields += ship_field + + var/datum/tgs_message_content/manifest_content = new() + manifest_content.embed = embed + + return manifest_content + +/datum/tgs_chat_command/who + name = "who" + help_text = "Displays the current player list" + +/datum/tgs_chat_command/who/Run(datum/tgs_chat_user/sender, params) + var/datum/tgs_chat_embed/structure/embed = new() + embed.colour = COLOR_DARK_CYAN + + if(!length(GLOB.clients)) + embed.title = "__Players:__" + embed.description = "No players online." + else + embed.title = "__Players ([length(GLOB.clients)]):__" + for(var/client/player as anything in GLOB.clients) + embed.description += "[player.ckey]\n" + + var/datum/tgs_message_content/who = new() + who.embed = embed + + return who diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 222879de4169..7019a148baa3 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -687,7 +687,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) /proc/send2tgs(msg,msg2) msg = format_text(msg) msg2 = format_text(msg2) - world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE) + world.TgsTargetedChatBroadcast(new /datum/tgs_message_content("[msg] | [msg2]"), TRUE) /** * Sends a message to a set of cross-communications-enabled servers using world topic calls @@ -735,19 +735,11 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) /proc/tgsadminwho() - var/list/message = list("Admins: ") var/list/admin_keys = list() - for(var/adm in GLOB.admins) - var/client/C = adm - admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]" + for(var/client/admin as anything in GLOB.admins) + admin_keys += "[admin][admin.holder.fakekey ? "(Stealth)" : ""][admin.is_afk() ? "(AFK)" : ""]" - for(var/admin in admin_keys) - if(LAZYLEN(message) > 1) - message += ", [admin]" - else - message += "[admin]" - - return jointext(message, "") + return jointext(admin_keys, "\n") /proc/keywords_lookup(msg,external) diff --git a/code/modules/admin/verbs/panicbunker.dm b/code/modules/admin/verbs/panicbunker.dm index b7c6ad3b256c..24834e5af5d4 100644 --- a/code/modules/admin/verbs/panicbunker.dm +++ b/code/modules/admin/verbs/panicbunker.dm @@ -74,7 +74,7 @@ GLOBAL_LIST_EMPTY(bunker_passthrough) /datum/tgs_chat_command/addbunkerbypass/Run(datum/tgs_chat_user/sender, params) if(!CONFIG_GET(flag/sql_enabled)) - return "The Database is not enabled!" + return new /datum/tgs_message_content("The Database is not enabled!") GLOB.bunker_passthrough |= ckey(params) @@ -82,7 +82,7 @@ GLOBAL_LIST_EMPTY(bunker_passthrough) SSpersistence.SavePanicBunker() //we can do this every time, it's okay log_admin("[sender.friendly_name] has added [params] to the current round's bunker bypass list.") message_admins("[sender.friendly_name] has added [params] to the current round's bunker bypass list.") - return "[params] has been added to the current round's bunker bypass list." + return new /datum/tgs_message_content("[params] has been added to the current round's bunker bypass list.") /datum/controller/subsystem/persistence/proc/LoadPanicBunker() var/bunker_path = file("data/bunker_passthrough.json") diff --git a/code/modules/discord/tgs_commands.dm b/code/modules/discord/tgs_commands.dm index e2c92e345459..d79bf07b87e4 100644 --- a/code/modules/discord/tgs_commands.dm +++ b/code/modules/discord/tgs_commands.dm @@ -5,13 +5,12 @@ /datum/tgs_chat_command/notify/Run(datum/tgs_chat_user/sender, params) if(!CONFIG_GET(string/chat_announce_new_game)) - return "Notifcations are currently disabled" + return new /datum/tgs_message_content("New round notifications are currently disabled.") - for(var/member in SSdiscord.notify_members) // If they are in the list, take them out - if(member == sender.mention) - SSdiscord.notify_members -= sender.mention - return "You will no longer be notified when the server restarts" + if(sender.mention in SSdiscord.notify_members) // If they are in the list, take them out + SSdiscord.notify_members -= sender.mention + return new /datum/tgs_message_content("You will no longer be notified when the round ends") // If we got here, they arent in the list. Chuck 'em in! SSdiscord.notify_members += sender.mention - return "You will now be notified when the server restarts" + return new /datum/tgs_message_content("You will now be notified when the server restarts")