diff --git a/SQL/database_changelog.txt b/SQL/database_changelog.txt index fcf347d99d32..e068891ad60a 100644 --- a/SQL/database_changelog.txt +++ b/SQL/database_changelog.txt @@ -1,15 +1,23 @@ Any time you make a change to the schema files, remember to increment the database schema version. Generally increment the minor number, major should be reserved for significant changes to the schema. Both values go up to 255. -The latest database version is 5.1; The query to update the schema revision table is: +The latest database version is 5.2; The query to update the schema revision table is: -INSERT INTO `schema_revision` (`major`, `minor`) VALUES (5, 1); +INSERT INTO `schema_revision` (`major`, `minor`) VALUES (5, 2); or -INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (5, 1); +INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (5, 2); In any query remember to add a prefix to the table names if you use one. ---------------------------------------------------- +Version 5.2, 28 Aug 2019, by AffectedArc07/TheGamer01 + +Added a field to the `player` table to track ckey and discord ID relationships + + ALTER TABLE `player` + ADD COLUMN `discord_id` BIGINT NULL DEFAULT NULL AFTER `flags`; +---------------------------------------------------- + Version 5.1, 25 Feb 2018, by MrStonedOne Added four tables to enable storing of stickybans in the database since byond can lose them, and to enable disabling stickybans for a round without depending on a crash free round. Existing stickybans are automagically imported to the tables. diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql index 911264ae05e6..c4d13ba5c5a3 100644 --- a/SQL/tgstation_schema.sql +++ b/SQL/tgstation_schema.sql @@ -320,6 +320,7 @@ CREATE TABLE `player` ( `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', `accountjoindate` DATE DEFAULT NULL, `flags` smallint(5) unsigned DEFAULT '0' NOT NULL, + `discord_id` BIGINT(20) NULL DEFAULT NULL, PRIMARY KEY (`ckey`), KEY `idx_player_cid_ckey` (`computerid`,`ckey`), KEY `idx_player_ip_ckey` (`ip`,`ckey`) diff --git a/SQL/tgstation_schema_prefixed.sql b/SQL/tgstation_schema_prefixed.sql index 0c7a1883c914..2e504785ec0d 100644 --- a/SQL/tgstation_schema_prefixed.sql +++ b/SQL/tgstation_schema_prefixed.sql @@ -320,6 +320,7 @@ CREATE TABLE `SS13_player` ( `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', `accountjoindate` DATE DEFAULT NULL, `flags` smallint(5) unsigned DEFAULT '0' NOT NULL, + `discord_id` BIGINT(20) NULL DEFAULT NULL, PRIMARY KEY (`ckey`), KEY `idx_player_cid_ckey` (`computerid`,`ckey`), KEY `idx_player_ip_ckey` (`ip`,`ckey`) diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index f1a1127648ef..60e6507da68d 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -1,7 +1,7 @@ //Update this whenever the db schema changes //make sure you add an update to the schema_version stable in the db changelog #define DB_MAJOR_VERSION 5 -#define DB_MINOR_VERSION 1 +#define DB_MINOR_VERSION 2 //Timing subsystem @@ -78,6 +78,7 @@ #define INIT_ORDER_SHUTTLE -21 #define INIT_ORDER_MINOR_MAPPING -40 #define INIT_ORDER_PATH -50 +#define INIT_ORDER_DISCORD -60 #define INIT_ORDER_PERSISTENCE -95 #define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init. diff --git a/code/controllers/subsystem/discord.dm b/code/controllers/subsystem/discord.dm new file mode 100644 index 000000000000..cd4cdc08dced --- /dev/null +++ b/code/controllers/subsystem/discord.dm @@ -0,0 +1,56 @@ +/* +NOTES: +There is a DB table to track ckeys and associated discord IDs. +The system is kept per-server (EG: Terry will not notify people who pressed notify on Sybil), but the accounts are between servers so you dont have to relink on each server. + ################## +# HOW THIS WORKS # +################## +User inputs their Discord ID on the server. +They then type !verify ckey in a channel on their discord +Then their Discord ID is linked to their Ckey in the databse +*/ + +SUBSYSTEM_DEF(discord) + name = "Discord" + flags = SS_NO_FIRE + + var/list/account_link_cache = list() // List that holds accounts to link, used in conjunction with TGS + var/notify_file = file("data/notify.json") + + // Returns ID from ckey +/datum/controller/subsystem/discord/proc/lookup_id(lookup_ckey) + var/datum/DBQuery/query_get_discord_id = SSdbcore.NewQuery("SELECT discord_id FROM [format_table_name("player")] WHERE ckey = '[sanitizeSQL(lookup_ckey)]'") + if(!query_get_discord_id.Execute()) + qdel(query_get_discord_id) + return + if(query_get_discord_id.NextRow()) + . = query_get_discord_id.item[1] + qdel(query_get_discord_id) + + // Returns ckey from ID +/datum/controller/subsystem/discord/proc/lookup_ckey(lookup_id) + var/datum/DBQuery/query_get_discord_ckey = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE discord_id = '[sanitizeSQL(lookup_id)]'") + if(!query_get_discord_ckey.Execute()) + qdel(query_get_discord_ckey) + return + if(query_get_discord_ckey.NextRow()) + . = query_get_discord_ckey.item[1] + qdel(query_get_discord_ckey) + + // Finalises link +/datum/controller/subsystem/discord/proc/link_account(ckey) + var/datum/DBQuery/link_account = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET discord_id = '[sanitizeSQL(account_link_cache[ckey])]' WHERE ckey = '[sanitizeSQL(ckey)]'") + link_account.Execute() + qdel(link_account) + account_link_cache -= ckey + + // Unlink account (Admin verb used) +/datum/controller/subsystem/discord/proc/unlink_account(ckey) + var/datum/DBQuery/unlink_account = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET discord_id = NULL WHERE ckey = '[sanitizeSQL(ckey)]'") + unlink_account.Execute() + qdel(unlink_account) + + // Clean up a discord account mention +/datum/controller/subsystem/discord/proc/id_clean(input) + var/regex/num_only = regex("\[^0-9\]", "g") + return num_only.Replace(input, "") \ No newline at end of file diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 6cc61e013077..a9aa82067c31 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -76,6 +76,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/resetasaycolor, /client/proc/toggleadminhelpsound, /client/proc/respawn_character, + /datum/proc/discord_id_manipulation, /datum/admins/proc/open_borgopanel, /datum/admins/proc/restart, //yogs - moved from +server /client/proc/admin_pick_random_player, //yogs diff --git a/code/modules/discord/accountlink.dm b/code/modules/discord/accountlink.dm new file mode 100644 index 000000000000..e85f00f23ec2 --- /dev/null +++ b/code/modules/discord/accountlink.dm @@ -0,0 +1,26 @@ +// Verb to link discord accounts to BYOND accounts +/client/verb/linkdiscord() + set category = "Special Verbs" + set name = "Link Discord Account" + set desc = "Link your discord account to your BYOND account." + + // Safety checks + if(!CONFIG_GET(flag/sql_enabled)) + to_chat(src, "This feature requires the SQL backend to be running.") + return + + if(!SSdiscord) // SS is still starting + to_chat(src, "The server is still starting up. Please wait before attempting to link your account ") + return + + var/stored_id = SSdiscord.lookup_id(usr.ckey) + if(!stored_id) // Account is not linked + var/know_how = alert("Get your Discord ID ready, you can get this by typing !myid in any channel!","Question","OK","Cancel Linking") + if(know_how == "Cancel Linking") + return + var/entered_id = input("Please enter your Discord ID (18-ish digits)", "Enter Discord ID", null, null) as text|null + SSdiscord.account_link_cache[replacetext(lowertext(usr.ckey), " ", "")] = "[entered_id]" // Prepares for TGS-side verification, also fuck spaces + alert(usr, "Account link started. Please type the following in any channel '!verify yourckey' (Example: !verify nichlas0010)") + else // Account is already linked + alert("You already have the Discord Account [stored_id] linked to [usr.ckey]. If you need to have this reset, please contact an admin!","Already Linked","OK") + return \ No newline at end of file diff --git a/code/modules/discord/manipulation.dm b/code/modules/discord/manipulation.dm new file mode 100644 index 000000000000..df962bfc1de7 --- /dev/null +++ b/code/modules/discord/manipulation.dm @@ -0,0 +1,37 @@ +// Verb to manipulate IDs and ckeys +/client/proc/discord_id_manipulation() + set name = "Discord Manipulation" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + holder.discord_manipulation() + + +/datum/admins/proc/discord_manipulation() + if(!usr.client.holder) + return + + var/lookup_choice = alert(usr, "Do you wish to lookup account by ID or ckey?", "Lookup Type", "ID", "Ckey", "Cancel") + switch(lookup_choice) + if("ID") + var/lookup_id = input(usr,"Enter Discord ID to lookup ckey") as text + var/returned_ckey = SSdiscord.lookup_id(lookup_id) + if(returned_ckey) + var/unlink_choice = alert(usr, "Discord ID [lookup_id] is linked to Ckey [returned_ckey]. Do you wish to unlink or cancel?", "Account Found", "Unlink", "Cancel") + if(unlink_choice == "Unlink") + SSdiscord.unlink_account(returned_ckey) + else + to_chat(usr, "Discord ID [lookup_id] has no associated ckey") + if("Ckey") + var/lookup_ckey = input(usr,"Enter Ckey to lookup ID") as text + var/returned_id = SSdiscord.lookup_id(lookup_ckey) + if(returned_id) + to_chat(usr, "Ckey [lookup_ckey] is assigned to Discord ID [returned_id]") + to_chat(usr, "Discord mention format: <@[returned_id]>") // < and > print < > in HTML without using them as tags + var/unlink_choice = alert(usr, "Ckey [lookup_ckey] is linked to Discord ID [returned_id]. Do you wish to unlink or cancel?", "Account Found", "Unlink", "Cancel") + if(unlink_choice == "Unlink") + SSdiscord.unlink_account(lookup_ckey) + else + to_chat(usr, "Ckey [lookup_ckey] has no associated Discord ID!") \ No newline at end of file diff --git a/yogstation.dme b/yogstation.dme index a0d058ffef21..93459903c051 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -253,6 +253,7 @@ #include "code\controllers\subsystem\communications.dm" #include "code\controllers\subsystem\dbcore.dm" #include "code\controllers\subsystem\dcs.dm" +#include "code\controllers\subsystem\discord.dm" #include "code\controllers\subsystem\disease.dm" #include "code\controllers\subsystem\economy.dm" #include "code\controllers\subsystem\events.dm" @@ -1643,6 +1644,8 @@ #include "code\modules\detectivework\evidence.dm" #include "code\modules\detectivework\footprints_and_rag.dm" #include "code\modules\detectivework\scanner.dm" +#include "code\modules\discord\accountlink.dm" +#include "code\modules\discord\manipulation.dm" #include "code\modules\economy\_economy.dm" #include "code\modules\economy\account.dm" #include "code\modules\economy\pay_stand.dm" diff --git a/yogstation/code/datums/world_topic.dm b/yogstation/code/datums/world_topic.dm index 82326437802c..a19b9505dedc 100644 --- a/yogstation/code/datums/world_topic.dm +++ b/yogstation/code/datums/world_topic.dm @@ -83,3 +83,33 @@ if(X != C) to_chat(X, "Mentor PM: [discord_mentor_link(from, from_id)]->[key_name_mentor(C, X, 0, 0, show_char_recip)]: [msg]") return 1 + +/datum/world_topic/verify + keyword = "verify" + require_comms_key = TRUE + +/datum/world_topic/verify/Run(list/input) + var/id = input["verify"] + var/ckey = input["ckey"] + var/lowerparams = replacetext(lowertext(ckey), " ", "") // Fuck spaces + if(SSdiscord.account_link_cache[lowerparams]) // First if they are in the list, then if the ckey matches + if(SSdiscord.account_link_cache[lowerparams] == "[SSdiscord.id_clean(id)]") // If the associated ID is the correct one + SSdiscord.link_account(lowerparams) + return "Successfully linked accounts" + else + return "That ckey is not associated to this discord account. If someone has used your ID, please inform an administrator" + else + return "Failure (Double check your ckey)" + +/datum/world_topic/unlink + keyword = "unlink" + require_comms_key = TRUE + +/datum/world_topic/unlink/Run(list/input) + var/ckey = input["unlink"] + var/returned_id = SSdiscord.lookup_id(ckey) + if(returned_id) + SSdiscord.unlink_account(ckey) + return "[ckey] has been unlinked!" + else + return "Account not unlinked! Please contact a coder." \ No newline at end of file