From a53b81a8922ded3a12720ee3739bcdc3cd77b599 Mon Sep 17 00:00:00 2001
From: BongaTheProto <93835010+BongaTheProto@users.noreply.github.com>
Date: Thu, 4 Jul 2024 20:56:12 -0500
Subject: [PATCH] discord verification
the system that makes players need to verify in our discord for everyone's safety
---
code/controllers/subsystem/discord.dm | 8 ++--
code/modules/discord/accountlink.dm | 18 ++++++--
config/config.txt | 3 ++
config/splurt/discord.txt | 20 ++++++++
modular_zzplurt/code/_globalvars/mobs.dm | 1 +
.../configuration/entries/discord.dm | 9 ++++
.../code/controllers/subsystem/discord.dm | 33 +++++++++++++
.../code/modules/client/verbs/looc.dm | 10 ++++
.../code/modules/client/verbs/ooc.dm | 10 ++++
.../code/modules/discord/tgs_commands.dm | 46 +++++++++++++++++++
modular_zzplurt/code/modules/discord/verbs.dm | 45 ++++++++++++++++++
.../modules/mob/dead/new_player/new_player.dm | 10 ++++
tgstation.dme | 8 ++++
13 files changed, 213 insertions(+), 8 deletions(-)
create mode 100644 config/splurt/discord.txt
create mode 100644 modular_zzplurt/code/_globalvars/mobs.dm
create mode 100644 modular_zzplurt/code/controllers/configuration/entries/discord.dm
create mode 100644 modular_zzplurt/code/controllers/subsystem/discord.dm
create mode 100644 modular_zzplurt/code/modules/client/verbs/looc.dm
create mode 100644 modular_zzplurt/code/modules/client/verbs/ooc.dm
create mode 100644 modular_zzplurt/code/modules/discord/tgs_commands.dm
create mode 100644 modular_zzplurt/code/modules/discord/verbs.dm
create mode 100644 modular_zzplurt/code/modules/mob/dead/new_player/new_player.dm
diff --git a/code/controllers/subsystem/discord.dm b/code/controllers/subsystem/discord.dm
index 7efdbfcda6a55..822b7857301eb 100644
--- a/code/controllers/subsystem/discord.dm
+++ b/code/controllers/subsystem/discord.dm
@@ -119,7 +119,7 @@ SUBSYSTEM_DEF(discord)
/datum/controller/subsystem/discord/proc/get_or_generate_one_time_token_for_ckey(ckey)
// Is there an existing valid one time token
- var/datum/discord_link_record/link = find_discord_link_by_ckey(ckey, timebound = TRUE)
+ var/datum/discord_link_record/link = find_discord_link_by_ckey(ckey, timebound = TRUE, only_valid = TRUE) //SPLURT EDIT - Lookup only valid links
if(link)
return link.one_time_token
@@ -191,7 +191,7 @@ SUBSYSTEM_DEF(discord)
var/timeboundsql = ""
if(timebound)
timeboundsql = "AND timestamp >= Now() - INTERVAL 4 HOUR"
- var/query = "SELECT CAST(discord_id AS CHAR(25)), ckey, MAX(timestamp), one_time_token FROM [format_table_name("discord_links")] WHERE one_time_token = :one_time_token [timeboundsql] GROUP BY ckey, discord_id, one_time_token LIMIT 1"
+ var/query = "SELECT CAST(discord_id AS CHAR(25)), ckey, MAX(timestamp), one_time_token FROM [format_table_name("discord_links")] WHERE one_time_token = :one_time_token [timeboundsql] GROUP BY ckey, discord_id, one_time_token ORDER BY timestamp DESC LIMIT 1" //SPLURT EDIT - Order by timestamp
var/datum/db_query/query_get_discord_link_record = SSdbcore.NewQuery(
query,
list("one_time_token" = one_time_token)
@@ -227,7 +227,7 @@ SUBSYSTEM_DEF(discord)
if(only_valid)
validsql = "AND valid = 1"
- var/query = "SELECT CAST(discord_id AS CHAR(25)), ckey, MAX(timestamp), one_time_token FROM [format_table_name("discord_links")] WHERE ckey = :ckey [timeboundsql] [validsql] GROUP BY ckey, discord_id, one_time_token LIMIT 1"
+ var/query = "SELECT CAST(discord_id AS CHAR(25)), ckey, MAX(timestamp), one_time_token FROM [format_table_name("discord_links")] WHERE ckey = :ckey [timeboundsql] [validsql] GROUP BY ckey, discord_id, one_time_token ORDER BY timestamp DESC LIMIT 1" //SPLURT EDIT - Order by timestamp
var/datum/db_query/query_get_discord_link_record = SSdbcore.NewQuery(
query,
list("ckey" = ckey)
@@ -265,7 +265,7 @@ SUBSYSTEM_DEF(discord)
if(only_valid)
validsql = "AND valid = 1"
- var/query = "SELECT CAST(discord_id AS CHAR(25)), ckey, MAX(timestamp), one_time_token FROM [format_table_name("discord_links")] WHERE discord_id = :discord_id [timeboundsql] [validsql] GROUP BY ckey, discord_id, one_time_token LIMIT 1"
+ var/query = "SELECT CAST(discord_id AS CHAR(25)), ckey, MAX(timestamp), one_time_token FROM [format_table_name("discord_links")] WHERE discord_id = :discord_id [timeboundsql] [validsql] GROUP BY ckey, discord_id, one_time_token ORDER BY timestamp DESC LIMIT 1" //SPLURT EDIT - Order by timestamp
var/datum/db_query/query_get_discord_link_record = SSdbcore.NewQuery(
query,
list("discord_id" = discord_id)
diff --git a/code/modules/discord/accountlink.dm b/code/modules/discord/accountlink.dm
index 5ff80d34e471e..e0db1bdb607d6 100644
--- a/code/modules/discord/accountlink.dm
+++ b/code/modules/discord/accountlink.dm
@@ -25,12 +25,22 @@
else
- // Will generate one if an expired one doesn't exist already, otherwise will grab existing token
- var/one_time_token = SSdiscord.get_or_generate_one_time_token_for_ckey(ckey)
- SSdiscord.reverify_cache[usr.ckey] = one_time_token
- message = "Your one time token is: [one_time_token]. Assuming you have the required living minutes in game, you can now verify yourself on Discord by using the command: [prefix]verify [one_time_token]"
+ //SPLURT EDIT - Only one linked account
+ var/datum/discord_link_record/existing_link = SSdiscord.find_discord_link_by_ckey(usr?.ckey, only_valid = TRUE)
+ //Do not create a new entry if they already have a linked account
+ if(existing_link?.discord_id)
+ message = "You already have a linked account with discord ID ([existing_link.discord_id]) linked on [existing_link.timestamp]. If you desire to change your account please contact staff."
+ else
+ // Will generate one if an expired one doesn't exist already, otherwise will grab existing token
+ var/one_time_token = SSdiscord.get_or_generate_one_time_token_for_ckey(ckey)
+ SSdiscord.reverify_cache[usr.ckey] = one_time_token
+ message = "Your one time token is: [one_time_token]. Assuming you have the required living minutes in game, you can now verify yourself on Discord by using the command: [prefix]verify [one_time_token]"
+ //SPLURT EDIT END
//Now give them a browse window so they can't miss whatever we told them
+ //SPLURT EDIT - Notify if they must stay in our discord server
+ if(CONFIG_GET(flag/forced_discord_stay))
+ message += span_warning("Remember that to mantain verification you MUST stay in the discord server")
var/datum/browser/window = new/datum/browser(usr, "discordverification", "Discord Verification")
window.set_content("
[message]
")
window.open()
diff --git a/config/config.txt b/config/config.txt
index 2bafd3d37e776..28370d5825557 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -11,6 +11,9 @@ $include interviews.txt
$include lua.txt
$include auxtools.txt
+# SPLURT configs
+$include splurt/discord.txt
+
# You can use the @ character at the beginning of a config option to lock it from being edited in-game
# Example usage:
# @SERVERNAME tgstation
diff --git a/config/splurt/discord.txt b/config/splurt/discord.txt
new file mode 100644
index 0000000000000..2caf282a4078a
--- /dev/null
+++ b/config/splurt/discord.txt
@@ -0,0 +1,20 @@
+# Discord verification channel name
+## This config has no actual function, it's only aesthetic lol ##
+## Use the actual name of the channel ##
+#VERIFICATION_CHANNEL verification
+
+# Discord verification command name
+## The name of the command you use for verification, only in case you've set up an alias for it ##
+## Defaults to "verify" ##
+#VERIFICATION_COMMAND verify
+
+# Need discord account to join
+## Uncomment to make players need to link their discord accounts to ckeys in order to join the game_mode ##
+## The discord verification system makes use of https://github.com/optimumtact/orangescogs ##
+## Needs a working database with the station's current schema ##
+#DISCORD_BUNKER
+
+# Discord forcing mode
+## Uncomment this if your players need to remain in the discord to keep verification ##
+## Note, this only produces aesthetic changes, the real magic happens with the redbot modules
+#FORCED_DISCORD_STAY
diff --git a/modular_zzplurt/code/_globalvars/mobs.dm b/modular_zzplurt/code/_globalvars/mobs.dm
new file mode 100644
index 0000000000000..92a3c83791f14
--- /dev/null
+++ b/modular_zzplurt/code/_globalvars/mobs.dm
@@ -0,0 +1 @@
+GLOBAL_LIST_EMPTY(discord_passthrough)
diff --git a/modular_zzplurt/code/controllers/configuration/entries/discord.dm b/modular_zzplurt/code/controllers/configuration/entries/discord.dm
new file mode 100644
index 0000000000000..29b302fe8aa96
--- /dev/null
+++ b/modular_zzplurt/code/controllers/configuration/entries/discord.dm
@@ -0,0 +1,9 @@
+/datum/config_entry/string/verification_channel
+ default = "verification"
+
+/datum/config_entry/string/verification_command
+ default = "verify"
+
+/datum/config_entry/flag/discord_bunker
+
+/datum/config_entry/flag/forced_discord_stay
diff --git a/modular_zzplurt/code/controllers/subsystem/discord.dm b/modular_zzplurt/code/controllers/subsystem/discord.dm
new file mode 100644
index 0000000000000..14ede844bea1c
--- /dev/null
+++ b/modular_zzplurt/code/controllers/subsystem/discord.dm
@@ -0,0 +1,33 @@
+/datum/controller/subsystem/discord/Initialize()
+ . = ..()
+ delete_nulls()
+
+
+/datum/controller/subsystem/discord/proc/check_login(mob/dead/new_player/player)
+ . = TRUE
+ if(!(SSdbcore.IsConnected() && CONFIG_GET(flag/discord_bunker) && CONFIG_GET(string/discordbotcommandprefix))) //If not configured/using DB
+ return TRUE
+ if(!player.client) //Safety check
+ return FALSE
+ if(player.client?.ckey in GLOB.discord_passthrough) //If they have bypass
+ return TRUE
+
+ var/datum/discord_link_record/player_link = find_discord_link_by_ckey(player.client?.ckey, only_valid = TRUE)
+
+ if(!(player_link && player_link?.discord_id))
+ return FALSE
+
+/datum/controller/subsystem/discord/proc/delete_nulls()
+ var/query = "DELETE FROM [format_table_name("discord_links")] WHERE discord_id IS NULL"
+ var/datum/db_query/query_delete_nulls = SSdbcore.NewQuery(
+ query
+ )
+ if(!query_delete_nulls.Execute())
+ log_runtime("DATABASE: There was an error while deleting NULL discord IDs") // This codebase doesn't have a subsystem_log()
+ message_admins(span_warning("There was an error while deleting NULL IDs, please delete them manually using the Delete Null Discords verb"))
+ send2adminchat("Discord Subsystem", "There was an error while deleting NULL IDs, please delete them manually using `!tgs discordnulls`")
+ qdel(query_delete_nulls)
+ return FALSE
+
+ qdel(query_delete_nulls)
+ return TRUE
diff --git a/modular_zzplurt/code/modules/client/verbs/looc.dm b/modular_zzplurt/code/modules/client/verbs/looc.dm
new file mode 100644
index 0000000000000..6a01d74a0dd28
--- /dev/null
+++ b/modular_zzplurt/code/modules/client/verbs/looc.dm
@@ -0,0 +1,10 @@
+/client/looc(msg as text)
+ var/vibe_check = SSdiscord?.check_login(usr)
+ if(isnull(vibe_check))
+ to_chat(usr, span_notice("The server is still starting up. Please wait... "))
+ return
+ else if(!vibe_check) //Dirty but I guess we gotta tell when the subsystem hasn't started
+ to_chat(usr, span_warning("You must link your discord account to your ckey in order to join the game. Join our discord and use the [CONFIG_GET(string/discordbotcommandprefix)][CONFIG_GET(string/verification_command)] command [CONFIG_GET(string/verification_channel) ? "as indicated in #[CONFIG_GET(string/verification_channel)] " : ""]. It won't take you more than two minutes :)
Ahelp or ask staff in the discord if this is an error."))
+ return
+
+ . = ..()
diff --git a/modular_zzplurt/code/modules/client/verbs/ooc.dm b/modular_zzplurt/code/modules/client/verbs/ooc.dm
new file mode 100644
index 0000000000000..55aeb17848052
--- /dev/null
+++ b/modular_zzplurt/code/modules/client/verbs/ooc.dm
@@ -0,0 +1,10 @@
+/client/ooc(msg as text)
+ var/vibe_check = SSdiscord?.check_login(usr)
+ if(isnull(vibe_check))
+ to_chat(usr, span_notice("The server is still starting up. Please wait... "))
+ return
+ else if(!vibe_check) //Dirty but I guess we gotta tell when the subsystem hasn't started
+ to_chat(usr, span_warning("You must link your discord account to your ckey in order to join the game. Join our discord and use the [CONFIG_GET(string/discordbotcommandprefix)][CONFIG_GET(string/verification_command)] command [CONFIG_GET(string/verification_channel) ? "as indicated in #[CONFIG_GET(string/verification_channel)] " : ""]. It won't take you more than two minutes :)
Ahelp or ask staff in the discord if this is an error."))
+ return
+
+ . = ..()
diff --git a/modular_zzplurt/code/modules/discord/tgs_commands.dm b/modular_zzplurt/code/modules/discord/tgs_commands.dm
new file mode 100644
index 0000000000000..1bd9c583910c3
--- /dev/null
+++ b/modular_zzplurt/code/modules/discord/tgs_commands.dm
@@ -0,0 +1,46 @@
+/datum/tgs_chat_command/adddiscordpass
+ name = "adddiscordpass"
+ help_text = "adddiscordpass | Add someone to the discord bunker bypass list"
+ admin_only = TRUE
+
+/datum/tgs_chat_command/adddiscordpass/Run(datum/tgs_chat_user/sender, params)
+ if(!SSdbcore.IsConnected())
+ return "The Database is not connected!"
+ if(!SSdiscord)
+ return "The discord subsystem hasn't initialized yet!"
+ if(!CONFIG_GET(flag/discord_bunker))
+ return "The Discord Bunker is deactivated!"
+
+ GLOB.discord_passthrough |= ckey(params)
+ GLOB.discord_passthrough[ckey(params)] = world.realtime
+ log_admin("[sender.friendly_name] has added [params] to the current round's discord bypass list.")
+ message_admins("[sender.friendly_name] has added [params] to the current round's discord bypass list.")
+ return "[params] has been added to the current round's bunker bypass list."
+
+/datum/tgs_chat_command/revdiscordpass
+ name = "revdiscordpass"
+ help_text = "revdiscordpass | Remove someone from the discord bypass list"
+ admin_only = TRUE
+
+/datum/tgs_chat_command/revdiscordpass/Run(datum/tgs_chat_user/sender, params)
+ if(!SSdbcore.IsConnected())
+ return "The Database is not connected!"
+ if(!SSdiscord)
+ return "The discord subsystem hasn't initialized yet!"
+ if(!CONFIG_GET(flag/discord_bunker))
+ return "The Discord Bunker is deactivated!"
+
+ GLOB.discord_passthrough -= ckey(params)
+ log_admin("[sender.friendly_name] has removed [params] from the current round's discord bypass list.")
+ message_admins("[sender.friendly_name] has removed [params] from the current round's discord bypass list.")
+ return "[params] has been removed from the current round's discord bypass list."
+
+/datum/tgs_chat_command/discordnulls
+ name = "discordnulls"
+ help_text = "Deletes all rows in the database where discord_id is NULL."
+ admin_only = TRUE
+
+/datum/tgs_chat_command/discordnulls/Run(datum/tgs_chat_user/sender, params)
+ log_admin("[sender.friendly_name] has attempted to delete the NULLs from the discord database.")
+ message_admins("[sender.friendly_name] has attempted to delete the NULLs from the discord database.")
+ return "[SSdiscord.delete_nulls() ? "NULL rows deleted successfully" : "There was a problem while deleting NULLs"]"
diff --git a/modular_zzplurt/code/modules/discord/verbs.dm b/modular_zzplurt/code/modules/discord/verbs.dm
new file mode 100644
index 0000000000000..da04e3c428bd8
--- /dev/null
+++ b/modular_zzplurt/code/modules/discord/verbs.dm
@@ -0,0 +1,45 @@
+ADMIN_VERB(discordbunker, R_SERVER, "Toggle Discord Bunker", "Toggles the Discord Bunker on or off.", ADMIN_CATEGORY_SERVER)
+ if(!SSdbcore.IsConnected())
+ to_chat(user, span_adminnotice("The Database is not connected/enabled!"))
+ return
+
+ var/new_dbun = !CONFIG_GET(flag/discord_bunker)
+ CONFIG_SET(flag/discord_bunker, new_dbun)
+
+ log_admin("[key_name(user)] has toggled the Discord Bunker, it is now [new_dbun ? "on" : "off"]")
+ message_admins("[key_name_admin(user)] has toggled the Discord Bunker, it is now [new_dbun ? "enabled" : "disabled"].")
+ SSblackbox.record_feedback("nested tally", "discord_toggle", 1, list("Toggle Discord Bunker", "[new_dbun ? "Enabled" : "Disabled"]"))
+ send2adminchat("Discord Bunker", "[key_name(user)] has toggled the Discord Bunker, it is now [new_dbun ? "enabled" : "disabled"].")
+
+ADMIN_VERB(adddiscordbypass, R_SERVER, "Add Discord Bypass", "Allows a given ckey to connect through the discord bunker for the round even if they haven't verified yet.", ADMIN_CATEGORY_SERVER, ckeytobypass as text)
+ if(!SSdbcore.IsConnected())
+ to_chat(user, span_adminnotice("The Database is not connected!"))
+ return
+ if(!SSdiscord)
+ to_chat(user, span_adminnotice("The discord subsystem hasn't initialized yet!"))
+ return
+ if(!CONFIG_GET(flag/discord_bunker))
+ to_chat(user, span_adminnotice("The Discord Bunker is deactivated!"))
+ return
+
+ GLOB.discord_passthrough |= ckey(ckeytobypass)
+ GLOB.discord_passthrough[ckey(ckeytobypass)] = world.realtime
+ log_admin("[key_name(user)] has added [ckeytobypass] to the current round's discord bypass list.")
+ message_admins("[key_name_admin(user)] has added [ckeytobypass] to the current round's discord bypass list.")
+ send2adminchat("Discord Bunker", "[key_name(user)] has added [ckeytobypass] to the current round's discord bypass list.")
+
+ADMIN_VERB(revokediscordbypass, R_SERVER, "Revoke Discord Bypass", "Revoke's a ckey's permission to bypass the discord bunker for a given round.", ADMIN_CATEGORY_SERVER, ckeytobypass as text)
+ if(!SSdbcore.IsConnected())
+ to_chat(user, span_adminnotice("The Database is not connected!"))
+ return
+ if(!SSdiscord)
+ to_chat(user, span_adminnotice("The discord subsystem hasn't initialized yet!"))
+ return
+ if(!CONFIG_GET(flag/discord_bunker))
+ to_chat(user, span_adminnotice("The Discord Bunker is deactivated!"))
+ return
+
+ GLOB.discord_passthrough -= ckey(ckeytobypass)
+ log_admin("[key_name(user)] has removed [ckeytobypass] from the current round's discord bypass list.")
+ message_admins("[key_name_admin(user)] has removed [ckeytobypass] from the current round's discord bypass list.")
+ send2adminchat("Discord Bunker", "[key_name(user)] has removed [ckeytobypass] from the current round's discord bypass list.")
diff --git a/modular_zzplurt/code/modules/mob/dead/new_player/new_player.dm b/modular_zzplurt/code/modules/mob/dead/new_player/new_player.dm
new file mode 100644
index 0000000000000..44db66ca92d58
--- /dev/null
+++ b/modular_zzplurt/code/modules/mob/dead/new_player/new_player.dm
@@ -0,0 +1,10 @@
+/mob/dead/new_player/Topic(href, list/href_list)
+ var/vibe_check = SSdiscord?.check_login(src)
+ if(isnull(vibe_check))
+ to_chat(usr, span_notice("The server is still starting up. Please wait... "))
+ return
+ if(href_list["observe"] || href_list["toggle_ready"] || href_list["late_join"])
+ if(!vibe_check)
+ to_chat(src, span_warning("You must link your discord account to your ckey in order to join the game. Join our discord and use the [CONFIG_GET(string/discordbotcommandprefix)][CONFIG_GET(string/verification_command)] command [CONFIG_GET(string/verification_channel) ? "as indicated in #[CONFIG_GET(string/verification_channel)] " : ""]. It won't take you more than two minutes :)
Ahelp or ask staff in the discord if this is an error."))
+ return
+ . = ..()
diff --git a/tgstation.dme b/tgstation.dme
index 351aaf97f8e61..32dc0c5266c2a 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -9056,4 +9056,12 @@
#include "modular_zubbers\master_files\skyrat\modules\cortical_borer\code\cortical_borer_antag.dm"
#include "modular_zubbers\master_files\skyrat\modules\opposing_force\code\opposing_force_subsystem.dm"
#include "modular_zubbers\master_files\skyrat\modules\verbs\code\subtle.dm"
+#include "modular_zzplurt\code\_globalvars\mobs.dm"
+#include "modular_zzplurt\code\controllers\configuration\entries\discord.dm"
+#include "modular_zzplurt\code\controllers\subsystem\discord.dm"
+#include "modular_zzplurt\code\modules\client\verbs\looc.dm"
+#include "modular_zzplurt\code\modules\client\verbs\ooc.dm"
+#include "modular_zzplurt\code\modules\discord\tgs_commands.dm"
+#include "modular_zzplurt\code\modules\discord\verbs.dm"
+#include "modular_zzplurt\code\modules\mob\dead\new_player\new_player.dm"
// END_INCLUDE