diff --git a/_build_dependencies.sh b/_build_dependencies.sh index 6fb033ad07f..eaeb1716435 100644 --- a/_build_dependencies.sh +++ b/_build_dependencies.sh @@ -10,4 +10,4 @@ export BYOND_MAJOR=513 # Byond Minor export BYOND_MINOR=1526 # For the RUSTG library -export RUSTG_VERSION=2.0 +export RUSTG_VERSION=2.1-P diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index b23380098e6..04008d6779f 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -482,3 +482,12 @@ #define LINDA_SPAWN_N2O 64 #define LINDA_SPAWN_AGENT_B 128 #define LINDA_SPAWN_AIR 256 + +/// Send to the primary Discord webhook +#define DISCORD_WEBHOOK_PRIMARY "PRIMARY" + +/// Send to the admin Discord webhook +#define DISCORD_WEBHOOK_ADMIN "ADMIN" + +/// Send to the mentor Discord webhook +#define DISCORD_WEBHOOK_MENTOR "MENTOR" diff --git a/code/__DEFINES/rust_g.dm b/code/__DEFINES/rust_g.dm index 1a2e8a91c84..ad6f95a8654 100644 --- a/code/__DEFINES/rust_g.dm +++ b/code/__DEFINES/rust_g.dm @@ -20,16 +20,21 @@ #define rustg_log_write(fname, text) call(RUST_G, "log_write")(fname, text) /proc/rustg_log_close_all() return call(RUST_G, "log_close_all")() -// HTTP library stuff // - AA TODO: Add in SShttp +// HTTP library stuff // #define RUSTG_HTTP_METHOD_GET "get" #define RUSTG_HTTP_METHOD_PUT "put" #define RUSTG_HTTP_METHOD_DELETE "delete" #define RUSTG_HTTP_METHOD_PATCH "patch" #define RUSTG_HTTP_METHOD_HEAD "head" #define RUSTG_HTTP_METHOD_POST "post" -#define rustg_http_request_blocking(method, url, body, headers) call(RUST_G, "http_request_blocking")(method, url, body, headers) + +// Commented out because this thing locks up the entire DD process when you use it +// DO NOT USE FOR THE LOVE OF GOD +// #define rustg_http_request_blocking(method, url, body, headers) call(RUST_G, "http_request_blocking")(method, url, body, headers) #define rustg_http_request_async(method, url, body, headers) call(RUST_G, "http_request_async")(method, url, body, headers) #define rustg_http_check_request(req_id) call(RUST_G, "http_check_request")(req_id) +/proc/rustg_create_async_http_client() return call(RUST_G, "start_http_client")() +/proc/rustg_close_async_http_client() return call(RUST_G, "shutdown_http_client")() // SQL stuff // - AA TODO: Async SQL + SSdbcore #define rustg_sql_connect_pool(options) call(RUST_G, "sql_connect_pool")(options) diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 50e70bc62a4..6c6dce80186 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -17,6 +17,8 @@ GLOBAL_VAR(runtime_summary_log) GLOBAL_PROTECT(runtime_summary_log) GLOBAL_VAR(tgui_log) GLOBAL_PROTECT(tgui_log) +GLOBAL_VAR(http_log) +GLOBAL_PROTECT(http_log) GLOBAL_LIST_EMPTY(jobMax) GLOBAL_PROTECT(jobMax) diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 03d34395479..07c5cefbdb3 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -52,7 +52,7 @@ add_attack_logs(src, src, "[key_name_admin(src)] might be running a modified client! (failed can_see on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))", ATKLOG_ALL) var/message = "[key_name(src)] might be running a modified client! (failed can_see on AI click of [A]([COORD(pixel_turf)]))" log_admin(message) - send2irc_adminless_only("NOCHEAT", "[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") + SSdiscord.send2discord_simple_noadmins("[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") var/turf_visible @@ -65,7 +65,7 @@ if(pixel_turf.obscured) log_admin("[key_name_admin(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)])") add_attack_logs(src, src, "[key_name_admin(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))", ATKLOG_ALL) - send2irc_adminless_only("NOCHEAT", "[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") + SSdiscord.send2discord_simple_noadmins("[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") return var/list/modifiers = params2list(params) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 693fb42f5cb..7c54ef79a06 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -164,13 +164,6 @@ var/comms_password = "" - var/use_irc_bot = 0 - var/list/irc_bot_host = list() - var/main_irc = "" - var/admin_irc = "" - var/admin_notify_irc = "" - var/cidrandomizer_irc = "" - var/default_laws = 0 //Controls what laws the AI spawns with. var/list/station_levels = list(1) // Defines which Z-levels the station exists on. @@ -253,6 +246,25 @@ /// BYOND account age limit for notifcations of new accounts (Any accounts older than this value will not send notifications on first join) var/byond_account_age_threshold = 7 + /// Are discord webhooks enabled? + var/discord_webhooks_enabled = FALSE + + /// Role ID to be pinged for administrative events + var/discord_admin_role_id = null // Intentional null usage + + /// Webhook URL for the main public webhook + var/discord_main_webhook_url + + /// Webhook URL for the admin webhook + var/discord_admin_webhook_url + + /// Webhook URL for the mentor webhook + var/discord_mentor_webhook_url + + /// Do we want to forward all adminhelps to the discord or just ahelps when admins are offline. + /// (This does not mean all ahelps are pinged, only ahelps sent when staff are offline get the ping, regardless of this setting) + var/discord_forward_all_ahelps = FALSE + /datum/configuration/New() for(var/T in subtypesof(/datum/game_mode)) var/datum/game_mode/M = T @@ -562,9 +574,6 @@ if("allow_holidays") config.allow_holidays = 1 - if("use_irc_bot") - use_irc_bot = 1 - if("ticklag") Ticklag = text2num(value) @@ -605,21 +614,6 @@ if("comms_password") config.comms_password = value - if("irc_bot_host") - config.irc_bot_host = splittext(value, ";") - - if("main_irc") - config.main_irc = value - - if("admin_irc") - config.admin_irc = value - - if("admin_notify_irc") - config.admin_notify_irc = value - - if("cidrandomizer_irc") - config.cidrandomizer_irc = value - if("python_path") if(value) GLOB.python_path = value @@ -746,6 +740,20 @@ config.enable_gamemode_player_limit = 1 if("byond_account_age_threshold") config.byond_account_age_threshold = text2num(value) + // Discord stuff + if("enable_discord_webhooks") + discord_webhooks_enabled = TRUE + if("discord_webhooks_admin_role_id") + discord_admin_role_id = "[value]" // This MUST be a string because BYOND doesnt like massive integers + if("discord_webhooks_main_url") + discord_main_webhook_url = value + if("discord_webhooks_admin_url") + discord_admin_webhook_url = value + if("discord_webhooks_mentor_url") + discord_mentor_webhook_url = value + if("discord_forward_all_ahelps") + discord_forward_all_ahelps = TRUE + // End discord stuff else log_config("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/subsystem/discord.dm b/code/controllers/subsystem/discord.dm new file mode 100644 index 00000000000..1360b7f9bfb --- /dev/null +++ b/code/controllers/subsystem/discord.dm @@ -0,0 +1,82 @@ +SUBSYSTEM_DEF(discord) + name = "Discord" + flags = SS_NO_FIRE + /// Is the SS enabled + var/enabled = FALSE + /// Last time the administrator ping was dropped. This ensures administrators cannot be mass pinged if a large chunk of ahelps go off at once (IE: tesloose) + var/last_administration_ping = 0 + +/datum/controller/subsystem/discord/Initialize(start_timeofday) + if(config.discord_webhooks_enabled) + enabled = TRUE + return ..() + +// This is designed for ease of simplicity for sending quick messages from parts of the code +/datum/controller/subsystem/discord/proc/send2discord_simple(destination, content) + if(!enabled) + return + var/webhook_url + switch(destination) + if(DISCORD_WEBHOOK_ADMIN) + webhook_url = config.discord_admin_webhook_url + if(DISCORD_WEBHOOK_PRIMARY) + webhook_url = config.discord_main_webhook_url + if(DISCORD_WEBHOOK_MENTOR) + webhook_url = config.discord_mentor_webhook_url + + var/datum/discord_webhook_payload/dwp = new() + dwp.webhook_content = content + SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, webhook_url, dwp.serialize2json(), list("content-type" = "application/json")) + +// This one is designed to take in a [/datum/discord_webhook_payload] which was prepared beforehand +/datum/controller/subsystem/discord/proc/send2discord_complex(destination, datum/discord_webhook_payload/dwp) + if(!enabled) + return + var/webhook_url + switch(destination) + if(DISCORD_WEBHOOK_ADMIN) + webhook_url = config.discord_admin_webhook_url + if(DISCORD_WEBHOOK_PRIMARY) + webhook_url = config.discord_main_webhook_url + SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, webhook_url, dwp.serialize2json(), list("content-type" = "application/json")) + +// This one is for sending messages to the admin channel if no admins are active, complete with a ping to the game admins role +/datum/controller/subsystem/discord/proc/send2discord_simple_noadmins(content, check_send_always = FALSE) + // Setup some stuff + var/alerttext + var/list/admincounter = staff_countup(R_BAN) + var/active_admins = admincounter[1] + var/inactive_admins = admincounter[3] + var/add_ping = TRUE + + if(active_admins <= 0) + if(inactive_admins > 0) + alerttext = " | **ALL ADMINS AFK**" + else + alerttext = " | **NO ADMINS ONLINE**" + else + if(check_send_always && config.discord_forward_all_ahelps) + // If we are here, there are admins online. We want to forward everything, but obviously dont want to add a ping, so we do this + add_ping = FALSE + else + // We have active admins, we dont care about the rest of this proc + return + + var/message = "[content] [alerttext] [add_ping ? handle_administrator_ping() : ""]" + + var/datum/discord_webhook_payload/dwp = new() + dwp.webhook_content = message + + SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, config.discord_admin_webhook_url, dwp.serialize2json(), list("content-type" = "application/json")) + +// Helper to make administrator ping easier +/datum/controller/subsystem/discord/proc/handle_administrator_ping() + // Check if a role is even set + if(config.discord_admin_role_id) + if(last_administration_ping > world.time) + return "*(Role pinged recently)*" + + last_administration_ping = world.time + 60 SECONDS + return "<@&[config.discord_admin_role_id]>" + + return "*(Role not configured)*" diff --git a/code/controllers/subsystem/http.dm b/code/controllers/subsystem/http.dm new file mode 100644 index 00000000000..3a857f76137 --- /dev/null +++ b/code/controllers/subsystem/http.dm @@ -0,0 +1,120 @@ +SUBSYSTEM_DEF(http) + name = "HTTP" + flags = SS_TICKER | SS_BACKGROUND // Measure in ticks, but also only run if we have the spare CPU. We also dont init. + wait = 1 + runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY // All the time + // Assuming for the worst, since only discord is hooked into this for now, but that may change + offline_implications = "The server is no longer capable of making async HTTP requests. Shuttle call recommended." + /// List of all async HTTP requests in the processing chain + var/list/datum/http_request/active_async_requests + /// Variable to define if logging is enabled or not. Disabled by default since we know the requests the server is making. Enable with VV if you need to debug requests + var/logging_enabled = FALSE + +/datum/controller/subsystem/http/Initialize(start_timeofday) + rustg_create_async_http_client() // Open the door + active_async_requests = list() + return ..() + +/datum/controller/subsystem/http/fire(resumed) + for(var/r in active_async_requests) + var/datum/http_request/req = r + // Check if we are complete + if(req.is_complete()) + // If so, take it out the processing list + active_async_requests -= req + var/datum/http_response/res = req.into_response() + + // If the request has a callback, invoke it.Async of course to avoid choking the SS + if(req.cb) + req.cb.InvokeAsync(res) + + // And log the result + if(logging_enabled) + var/list/log_data = list() + log_data += "BEGIN ASYNC RESPONSE (ID: [req.id])" + if(res.errored) + log_data += "\t ----- RESPONSE ERRROR -----" + log_data += "\t [res.error]" + else + log_data += "\tResponse status code: [res.status_code]" + log_data += "\tResponse body: [res.body]" + log_data += "\tResponse headers: [json_encode(res.headers)]" + log_data += "END ASYNC RESPONSE (ID: [req.id])" + rustg_log_write(GLOB.http_log, log_data.Join("\n[GLOB.log_end]")) + +/** + * Async request creator + * + * Generates an async request, and adds it to the subsystem's processing list + * These should be used as they do not lock the entire DD process up as they execute inside their own thread pool inside RUSTG + */ +/datum/controller/subsystem/http/proc/create_async_request(method, url, body = "", list/headers, datum/callback/proc_callback) + var/datum/http_request/req = new() + req.prepare(method, url, body, headers) + if(proc_callback) + req.cb = proc_callback + + // Begin it and add it to the SS active list + req.begin_async() + active_async_requests += req + + if(logging_enabled) + // Create a log holder + var/list/log_data = list() + log_data += "BEGIN ASYNC REQUEST (ID: [req.id])" + log_data += "\t[uppertext(req.method)] [req.url]" + log_data += "\tRequest body: [req.body]" + log_data += "\tRequest headers: [req.headers]" + log_data += "END ASYNC REQUEST (ID: [req.id])" + + // Write the log data + rustg_log_write(GLOB.http_log, log_data.Join("\n[GLOB.log_end]")) + +/** + * Blocking request creator + * + * Generates a blocking request, executes it, logs the info then cleanly returns the response + * Exists as a proof of concept, and should never be used + */ +/datum/controller/subsystem/http/proc/make_blocking_request(method, url, body = "", list/headers) + CRASH("Attempted use of a blocking HTTP request") + /* + var/datum/http_request/req = new() + req.prepare(method, url, body, headers) + req.execute_blocking() + var/datum/http_response/res = req.into_response() + + // Now generate a logfile + var/list/log_data = list() + log_data += "NEW BLOCKING REQUEST" + log_data += "\t[uppertext(req.method)] [req.url]" + log_data += "\tRequest body: [req.body]" + log_data += "\tRequest headers: [req.headers]" + if(res.errored) + log_data += "\t ----- RESPONSE ERRROR -----" + log_data += "\t [res.error]" + else + log_data += "\tResponse status code: [res.status_code]" + log_data += "\tResponse body: [res.body]" + log_data += "\tResponse headers: [json_encode(res.headers)]" + log_data += "END BLOCKING REQUEST" + + // Write the log data + rustg_log_write(GLOB.http_log, log_data.Join("\n[GLOB.log_end]")) + + return res + */ + +/* + + Example of how to use callbacks properly + +/client/verb/testing() + set name = "Testing" + + var/datum/callback/cb = CALLBACK(src, /client/.proc/response, usr) + SShttp.create_async_request(RUSTG_HTTP_METHOD_GET, "http://site.domain/page.html", proc_callback=cb) + +/client/proc/response(mob/user, datum/http_response/response) + to_chat(user, "Code: [response.status_code] | Content: [response.body]") +*/ diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 3a1ba7595c0..d4bbf0fd9e3 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -276,9 +276,7 @@ SUBSYSTEM_DEF(ticker) //start_events() //handles random events and space dust. //new random event system is handled from the MC. - var/list/admins_number = staff_countup(R_BAN) - if(admins_number[1] == 0 && admins_number[3] == 0) - send2irc(config.admin_notify_irc, "Round has started with no admins online.") + SSdiscord.send2discord_simple_noadmins("Round has started") auto_toggle_ooc(0) // Turn it off round_start_time = world.time diff --git a/code/datums/discord.dm b/code/datums/discord.dm new file mode 100644 index 00000000000..48550b6a7b9 --- /dev/null +++ b/code/datums/discord.dm @@ -0,0 +1,130 @@ +/******************************************************************************************************** +* DO NOT EDIT ANYTHING IN HERE WITHOUT CHECKING THE DISCORD API SPEC AND TESTING THE JSON POST FORMAT * +* OTHERWISE, YOU **WILL** BREAK STUFF -aa ************************************************************* +********************************************/ + +/** + * # Discord Webhook Payload + * + * Holder datum for discord webhook POST send data + * + * Holds all information that a webhook would need, + * as well as a method to serialize the entire thing into JSON. + * See https://discord.com/developers/docs/resources/webhook#execute-webhook-jsonform-params + */ +/datum/discord_webhook_payload + /// Name the webhook user should post as + var/webhook_name + /// Content of the webhook message + var/webhook_content + /// List of all embed objects in the message + var/list/datum/discord_embed/embeds + +/datum/discord_webhook_payload/New() + embeds = list() + +/** + * Webhook Serializer + * + * Converts the DM webhook object into JSON for a POST request. + * Not called serialize() because thats a proc at the /datum level already + */ +/datum/discord_webhook_payload/proc/serialize2json() + var/list/json = list() + if(webhook_name) + json["username"] = webhook_name + if(webhook_content) + var/sanitized_content = webhook_content + sanitized_content = replacetext(sanitized_content, "@everyone", "(Attempted atEveryone)") + sanitized_content = replacetext(sanitized_content, "@here", "(Attempted atHere)") + json["content"] = sanitized_content + + // Now serialize the embeds + if(length(embeds)) + json["embeds"] = list() + if(length(embeds) > 10) + embeds.Cut(11) // Cut to 10 embeds because thats the limit of the Discord API + + for(var/e in embeds) + var/datum/discord_embed/embed = e + json["embeds"] += list(embed.serialize2list()) + + return json_encode(json) + +/** + * # Discord Embed + * + * Holder datum for discord embeds + * + * Used in [/datum/discord_webhook_payload] and serves as a code-first means to add an embed. + * See https://discord.com/developers/docs/resources/channel#embed-object + */ +/datum/discord_embed + /// Title of the embed + var/embed_title + /// Content of the embed + var/embed_content + /// Colour of the strip on the side of the embed. Must be in hexadecimal WITHOUT leading hash + var/embed_colour + /// Timestamp the embed was sent at. Must be in 8601 format. Will be autoset on /New() + var/embed_timestamp + /// List of all fields in the embed + var/list/datum/discord_embed_field/fields + +/datum/discord_embed/New() + embed_timestamp = time_stamp() // 8601 is king + fields = list() // Initialize the list + +/** + * Embed Serializer + * + * Converts the DM embed object into JSON for a POST request. + * Not called serialize() because thats a proc at the /datum level already + */ +/datum/discord_embed/proc/serialize2list() + var/list/json = list() + // All these fields can be nulled so presence check them all + if(embed_title) + json["title"] = embed_title + if(embed_content) + json["description"] = embed_content + if(embed_colour) + json["color"] = hex2num(embed_colour) + json["timestamp"] = embed_timestamp + + // Now serialize the fields + if(length(fields)) + json["fields"] = list() + for(var/f in fields) + var/datum/discord_embed_field/field = f + json["fields"] += list(field.serialize2list()) + + return json + +/** + * # Discord Embed Field + * + * Holder datum for discord embed fields + * + * Used in [/datum/discord_embed] and serves as a code-first means to add fields to an embed + * See https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure + */ +/datum/discord_embed_field + /// Name of the field + var/field_name + /// Content of the field + var/field_content + /// Inline flag + var/inline = TRUE + +/datum/discord_embed_field/proc/serialize2list() + // name and content CANNOT be nulled, so assert them + ASSERT(field_name) + ASSERT(field_content) + // Now serialize + var/list/json = list() + json["name"] = field_name + json["value"] = field_content + json["inline"] = inline ? "true" : "false" // Yes this has to be text IN THIS EXACT FORMAT. NO TOUCH. + + return json diff --git a/code/datums/http.dm b/code/datums/http.dm new file mode 100644 index 00000000000..e7ba1620854 --- /dev/null +++ b/code/datums/http.dm @@ -0,0 +1,151 @@ +/** + * # HTTP Request + * + * Holder datum for ingame HTTP requests + * + * Holds information regarding to methods used, URL, and response, + * as well as job IDs and progress tracking for async requests + */ +/datum/http_request + /// The ID of the request (Only set if it is an async request) + var/id + /// Is the request in progress? (Only set if it is an async request) + var/in_progress = FALSE + /// HTTP method used + var/method + /// Body of the request being sent + var/body + /// Request headers being sent + var/headers + /// URL that the request is being sent to + var/url + /// The raw response, which will be decoeded into a [/datum/http_response] + var/_raw_response + /// Callback for executing after async requests. Will be called with an argument of [/datum/http_response] as first argument + var/datum/callback/cb + +/* +########################################################################### +THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB +----------------------- DO NOT MANUALLY INVOKE THEM ----------------------- +########################################################################### +*/ + +/** + * Preparation handler + * + * Call this with relevant parameters to form the request you want to make + * + * Arguments: + * * _method - HTTP Method to use, see code/__DEFINES/rust_g.dm for a full list + * * _url - The URL to send the request to + * * _body - The body of the request, if applicable + * * _headers - Associative list of HTTP headers to send, if applicab;e + */ +/datum/http_request/proc/prepare(_method, _url, _body = "", list/_headers) + if(!length(_headers)) + headers = "" + else + headers = json_encode(_headers) + + method = _method + url = _url + body = _body + +/** + * Blocking executor + * + * Remains as a proof of concept to show it works, but should NEVER be used to do FFI halting the entire DD process up + * Async rqeuests are much preferred, but also require the subsystem to be firing for them to be answered + */ +/datum/http_request/proc/execute_blocking() + CRASH("Attempted to execute a blocking HTTP request") + // _raw_response = rustg_http_request_blocking(method, url, body, headers) + +/** + * Async execution starter + * + * Tells the request to start executing inside its own thread inside RUSTG + * Preferred over blocking, but also requires SShttp to be active + * As such, you cannot use this for events which may happen at roundstart (EG: IPIntel, BYOND account tracking, etc) + */ +/datum/http_request/proc/begin_async() + if(in_progress) + CRASH("Attempted to re-use a request object.") + + id = rustg_http_request_async(method, url, body, headers) + + if(isnull(text2num(id))) + _raw_response = "Proc error: [id]" + CRASH("Proc error: [id]") + else + in_progress = TRUE + +/** + * Async completion checker + * + * Checks if an async request has been complete + * Has safety checks built in to compensate if you call this on blocking requests, + * or async requests which have already finished + */ +/datum/http_request/proc/is_complete() + // If we dont have an ID, were blocking, so assume complete + if(isnull(id)) + return TRUE + + // If we arent in progress, assume complete + if(!in_progress) + return TRUE + + // We got here, so check the status + var/result = rustg_http_check_request(id) + + // If we have no result, were not finished + if(result == RUSTG_JOB_NO_RESULTS_YET) + return FALSE + else + // If we got here, we have a result to parse + _raw_response = result + in_progress = FALSE + return TRUE + +/** + * Response deserializer + * + * Takes a HTTP request object, and converts it into a [/datum/http_response] + * The entire thing is wrapped in try/catch to ensure it doesnt break on invalid requests + * Can be called on async and blocking requests + */ +/datum/http_request/proc/into_response() + var/datum/http_response/R = new() + + try + var/list/L = json_decode(_raw_response) + R.status_code = L["status_code"] + R.headers = L["headers"] + R.body = L["body"] + catch + R.errored = TRUE + R.error = _raw_response + + return R + +/** + * # HTTP Response + * + * Holder datum for HTTP responses + * + * Created from calling [/datum/http_request/proc/into_response()] + * Contains vars about the result of the response + */ +/datum/http_response + /// The HTTP status code of the response + var/status_code + /// The body of the response from the server + var/body + /// Associative list of headers sent from the server + var/list/headers + /// Has the request errored + var/errored = FALSE + /// Raw response if we errored + var/error diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 8757fa00d4f..e52721c29e3 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -209,7 +209,7 @@ if(escaped_on_pod_5 > 0) feedback_set("escaped_on_pod_5",escaped_on_pod_5) - send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") + SSdiscord.send2discord_simple(DISCORD_WEBHOOK_PRIMARY, "A round of [name] has ended - [surviving_total] survivors, [ghosts] ghosts.") return 0 diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm index 95b11353b86..a31ae85a5bd 100644 --- a/code/game/verbs/who.dm +++ b/code/game/verbs/who.dm @@ -126,5 +126,6 @@ modmsg += "\t[C] is a [C.holder.rank]\n" num_mods_online++ - msg = "Current Admins ([num_admins_online]):\n" + msg + "\nCurrent Mentors ([num_mods_online]):\n" + modmsg + var/noadmins_info = "\nIf no admins or mentors are online, make a ticket anyways. Adminhelps and mentorhelps will be relayed to discord, and staff will still be informed." + msg = "Current Admins ([num_admins_online]):\n" + msg + "\nCurrent Mentors ([num_mods_online]):\n" + modmsg + noadmins_info to_chat(src, msg) diff --git a/code/game/world.dm b/code/game/world.dm index 936a66ed18b..a25ba837cc3 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -193,13 +193,14 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday) if(!C) return "No client with that name on server" - var/message = "IRC-Admin PM from [C.holder ? "IRC-" + input["sender"] : "Administrator"]: [input["msg"]]" - var/amessage = "IRC-Admin PM from IRC-[input["sender"]] to [key_name(C)] : [input["msg"]]" + var/message = "Discord PM from [input["sender"]]: [input["msg"]]" + var/amessage = "Discord PM from [input["sender"]] to [key_name(C)]: [input["msg"]]" - C.received_irc_pm = world.time - C.irc_admin = input["sender"] + // THESE TWO VARS DO VERY DIFFERENT THINGS. DO NOT ATTEMPT TO COMBINE THEM + C.received_discord_pm = world.time + C.last_discord_pm_time = 0 - C << 'sound/effects/adminhelp.ogg' + SEND_SOUND(C, 'sound/effects/adminhelp.ogg') to_chat(C, message) for(var/client/A in GLOB.admins) @@ -448,11 +449,13 @@ GLOBAL_VAR_INIT(failed_old_db_connections, 0) GLOB.world_qdel_log = "[GLOB.log_directory]/qdel.log" GLOB.world_asset_log = "[GLOB.log_directory]/asset.log" GLOB.tgui_log = "[GLOB.log_directory]/tgui.log" + GLOB.http_log = "[GLOB.log_directory]/http.log" start_log(GLOB.world_game_log) start_log(GLOB.world_href_log) start_log(GLOB.world_runtime_log) start_log(GLOB.world_qdel_log) start_log(GLOB.tgui_log) + start_log(GLOB.http_log) // This log follows a special format and this path should NOT be used for anything else GLOB.runtime_summary_log = "data/logs/runtime_summary.log" @@ -515,3 +518,8 @@ GLOBAL_VAR_INIT(failed_old_db_connections, 0) var/dll = world.GetConfig("env", "EXTOOLS_DLL") if (dll) call(dll, "debug_initialize")() + + +/world/Del() + rustg_close_async_http_client() // Close the HTTP client. If you dont do this, youll get phantom threads which can crash DD from memory access violations + ..() diff --git a/code/modules/admin/db_ban/functions.dm b/code/modules/admin/db_ban/functions.dm index fd998677d75..87afbcd7b4b 100644 --- a/code/modules/admin/db_ban/functions.dm +++ b/code/modules/admin/db_ban/functions.dm @@ -12,7 +12,7 @@ var/bantype_pass = 0 var/bantype_str var/maxadminbancheck //Used to limit the number of active bans of a certein type that each admin can give. Used to protect against abuse or mutiny. - var/announceinirc //When set, it announces the ban in irc. Intended to be a way to raise an alarm, so to speak. + var/announce_in_discord = FALSE //When set, it announces the ban in irc. Intended to be a way to raise an alarm, so to speak. var/blockselfban //Used to prevent the banning of yourself. var/kickbannedckey //Defines whether this proc should kick the banned person, if they are connected (if banned_mob is defined). //some ban types kick players after this proc passes (tempban, permaban), but some are specific to db_ban, so @@ -46,14 +46,14 @@ duration = -1 bantype_pass = 1 maxadminbancheck = 1 - announceinirc = 1 + announce_in_discord = TRUE blockselfban = 1 kickbannedckey = 1 if(BANTYPE_ADMIN_TEMP) bantype_str = "ADMIN_TEMPBAN" bantype_pass = 1 maxadminbancheck = 1 - announceinirc = 1 + announce_in_discord = TRUE blockselfban = 1 kickbannedckey = 1 @@ -141,8 +141,8 @@ to_chat(usr, "Ban saved to database.") message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) - if(announceinirc) - send2irc("BAN ALERT","[a_ckey] applied a [bantype_str] on [ckey]") + if(announce_in_discord) + SSdiscord.send2discord_simple(DISCORD_WEBHOOK_ADMIN, "**BAN ALERT** [a_ckey] applied a [bantype_str] on [ckey]") if(kickbannedckey) if(banned_mob && banned_mob.client && banned_mob.client.ckey == banckey) diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 8bfce6c05f0..40acc051b7d 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -82,6 +82,7 @@ if(!SSipintel.enabled) return + // Do not refactor this to use SShttp, because that requires the subsystem to be firing for requests to be made, and this will be triggered before the MC has finished loading var/list/http[] = world.Export("http://[config.ipintel_domain]/check.php?ip=[ip]&contact=[config.ipintel_email]&format=json&flags=b") if(http) diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 4289df8ffbc..324899028fc 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -34,62 +34,30 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo else SStickets.newHelpRequest(src, msg) - //See how many staff are on - var/admin_number_afk = 0 - var/list/mentorholders = list() - var/list/modholders = list() - var/list/adminholders = list() - for(var/client/X in GLOB.admins) - if(check_rights(R_ADMIN, 0, X.mob)) - if(X.is_afk()) - admin_number_afk++ - adminholders += X - continue - if(check_rights(R_MOD, 0, X.mob)) - modholders += X - continue - if(check_rights(R_MENTOR, 0, X.mob)) - mentorholders += X - continue - //show it to the person adminhelping too to_chat(src, "[selected_type]: [msg]") - - var/admin_number_present = adminholders.len - admin_number_afk - log_admin("[selected_type]: [key_name(src)]: [msg] - heard by [admin_number_present] non-AFK admins.") - if(admin_number_present <= 0) - if(!admin_number_afk) - send2adminirc("[selected_type] from [key_name(src)]: [msg] - !!No admins online!!") - else - send2adminirc("[selected_type] from [key_name(src)]: [msg] - !!All admins AFK ([admin_number_afk])!!") - else - send2adminirc("[selected_type] from [key_name(src)]: [msg]") feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return -/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) - var/admin_number_total = 0 //Total number of admins - var/admin_number_afk = 0 //Holds the number of admins who are afk - var/admin_number_ignored = 0 //Holds the number of admins without +BAN (so admins who are not really admins) - var/admin_number_decrease = 0 //Holds the number of admins with are afk, ignored or both - for(var/client/X in GLOB.admins) - admin_number_total++ - var/invalid = 0 - if(requiredflags != 0 && !check_rights_for(X, requiredflags)) - admin_number_ignored++ - invalid = 1 - if(X.is_afk()) - admin_number_afk++ - invalid = 1 - if(X.holder.fakekey) - admin_number_ignored++ - invalid = 1 - if(invalid) - admin_number_decrease++ - var/admin_number_present = admin_number_total - admin_number_decrease //Number of admins who are neither afk nor invalid - if(admin_number_present <= 0) - if(!admin_number_afk && !admin_number_ignored) - send2irc(source, "[msg] - No admins online") - else - send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])") - return admin_number_present + switch(selected_type) + if("Adminhelp") + //See how many staff are on + var/list/admincount = staff_countup(R_BAN) + var/active_admins = admincount[1] + + log_admin("[selected_type]: [key_name(src)]: [msg] - heard by [active_admins] non-AFK admins.") + SSdiscord.send2discord_simple_noadmins("[selected_type] from [key_name(src)]: [msg]", check_send_always = TRUE) + + if("Mentorhelp") + var/alerttext + var/list/mentorcount = staff_countup(R_MENTOR) + var/active_mentors = mentorcount[1] + var/inactive_mentors = mentorcount[3] + + if(active_mentors <= 0) + if(inactive_mentors > 0) + alerttext = " | **ALL MENTORS AFK**" + else + alerttext = " | **NO MENTORS ONLINE**" + + log_admin("[selected_type]: [key_name(src)]: [msg] - heard by [active_mentors] non-AFK mentors.") + SSdiscord.send2discord_simple(DISCORD_WEBHOOK_MENTOR, "[key_name(src)]: [msg][alerttext]") diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 48c407e6866..c3b0c9e2ae7 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -211,34 +211,40 @@ i.addResponse(src, msg) return - -/client/proc/cmd_admin_irc_pm() +/client/proc/cmd_admin_discord_pm() if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Private-Message: You are unable to use PM-s (muted).") + to_chat(src, "Error: Private-Message: You are unable to use PMs (muted).") return - var/msg = clean_input("Message:", "Private message to admins on IRC / 400 character limit", , src) + if(last_discord_pm_time > world.time) + to_chat(usr, "Please wait [(last_discord_pm_time - world.time)/10] seconds, or for a reply, before sending another PM to Discord.") + return + + // We only allow PMs once every 10 seconds, othewrise the channel can get spammed very quickly + last_discord_pm_time = world.time + 10 SECONDS + + var/msg = clean_input("Message:", "Private message to admins on Discord / 400 character limit", , src) if(!msg) return sanitize(msg) - if(length(msg) > 400) // TODO: if message length is over 400, divide it up into seperate messages, the message length restriction is based on IRC limitations. Probably easier to do this on the bots ends. + if(length(msg) > 400) // Dont want them super spamming to_chat(src, "Your message was not sent because it was more then 400 characters find your message below for ease of copy/pasting") to_chat(src, "[msg]") return - send2adminirc("PlayerPM from [key_name(src)]: [html_decode(msg)]") + SSdiscord.send2discord_simple(DISCORD_WEBHOOK_ADMIN, "PM from [key_name(src)]: [html_decode(msg)]") - to_chat(src, "IRC PM to-IRC-Admins: [msg]") + to_chat(src, "PM to-Discord Admins: [msg]") - log_admin("PM: [key_name(src)]->IRC: [msg]") + log_admin("PM: [key_name(src)]->Discord: [msg]") for(var/client/X in GLOB.admins) if(X == src) continue - if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, X.mob)) - to_chat(X, "PM: [key_name(src, TRUE, 0)]->IRC-Admins: [msg]") + if(check_rights(R_ADMIN, 0, X.mob)) + to_chat(X, "PM: [key_name_admin(src)]->Discord Admins: [msg]") /client/verb/open_pms_ui() set name = "My PMs" diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index bf15eb66303..70e50610352 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -43,9 +43,6 @@ // comment out the line below when debugging locally to enable the options & messages menu //control_freak = 1 - var/received_irc_pm = -99999 - var/irc_admin //IRC admin that spoke with them last. - var/mute_irc = 0 var/ssd_warning_acknowledged = FALSE //////////////////////////////////// @@ -99,3 +96,11 @@ var/byondacc_date /// Days since the client's BYOND account was created var/byondacc_age = 0 + + + // Do not attempt to merge these vars together. They are for different things + /// Last world.time that a PM was send to discord by a player + var/last_discord_pm_time = 0 + + /// Last world/time that a PM was sent to the player by an admin + var/received_discord_pm = -99999 // Yes this super low number is intentional diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 4009d2fb835..8458ac77595 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -113,14 +113,14 @@ cmd_admin_pm(ckey_txt, null, href_list["type"]) return - if(href_list["irc_msg"]) - if(!holder && received_irc_pm < world.time - 6000) //Worse they can do is spam IRC for 10 minutes - to_chat(usr, "You are no longer able to use this, it's been more then 10 minutes since an admin on IRC has responded to you") + if(href_list["discord_msg"]) + if(!holder && received_discord_pm < world.time - 6000) // Worse they can do is spam discord for 10 minutes + to_chat(usr, "You are no longer able to use this, it's been more then 10 minutes since an admin on Discord has responded to you") return - if(mute_irc) - to_chat(usr, "") + if(prefs.muted & MUTE_ADMINHELP) + to_chat(usr, "You cannot use this as your client has been muted from sending messages to the admins on Discord") return - cmd_admin_irc_pm() + cmd_admin_discord_pm() return @@ -561,7 +561,7 @@ var/watchreason = check_watchlist(ckey) if(watchreason) message_admins("Notice: [key_name_admin(src)] is on the watchlist and has just connected - Reason: [watchreason]") - send2irc(config.admin_notify_irc, "Watchlist - [key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]") + SSdiscord.send2discord_simple_noadmins("**\[Watchlist]** [key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]") //Just the standard check to see if it's actually a number @@ -760,7 +760,7 @@ if(!cidcheck_failedckeys[ckey]) message_admins("[key_name(src)] has been detected as using a CID randomizer. Connection rejected.") - send2irc(config.cidrandomizer_irc, "[key_name(src)] has been detected as using a CID randomizer. Connection rejected.") + SSdiscord.send2discord_simple_noadmins("[key_name(src)] has been detected as using a CID randomizer. Connection rejected.") cidcheck_failedckeys[ckey] = TRUE note_randomizer_user() @@ -773,7 +773,7 @@ if(cidcheck_failedckeys[ckey]) // Atonement message_admins("[key_name_admin(src)] has been allowed to connect after showing they removed their cid randomizer") - send2irc(config.cidrandomizer_irc, "[key_name(src)] has been allowed to connect after showing they removed their cid randomizer.") + SSdiscord.send2discord_simple_noadmins("[key_name(src)] has been allowed to connect after showing they removed their cid randomizer.") cidcheck_failedckeys -= ckey if (cidcheck_spoofckeys[ckey]) message_admins("[key_name_admin(src)] has been allowed to connect after appearing to have attempted to spoof a cid randomizer check because it appears they aren't spoofing one this time") @@ -988,6 +988,7 @@ * Returns the data in a parsed, associative list */ /client/proc/retrieve_byondacc_data() + // Do not refactor this to use SShttp, because that requires the subsystem to be firing for requests to be made, and this will be triggered before the MC has finished loading var/list/http[] = world.Export("http://www.byond.com/members/[ckey]?format=text") if(http) var/status = text2num(http["STATUS"]) diff --git a/code/modules/ext_scripts/irc.dm b/code/modules/ext_scripts/irc.dm deleted file mode 100644 index 7f87413694a..00000000000 --- a/code/modules/ext_scripts/irc.dm +++ /dev/null @@ -1,21 +0,0 @@ -/proc/send2irc(var/channel, var/msg, var/lesser_sanitize = FALSE) - if(config.use_irc_bot && config.irc_bot_host.len) - for(var/IP in config.irc_bot_host) - spawn(0) - if(lesser_sanitize == 7355608) // Super random number because this could be bad if done accidentally - // Runs sanitization but allows <> and // (Needed for discord) - ext_python("ircbot_message.py", "[config.comms_password] [IP] [channel] [not_as_paranoid_sanitize(msg)]") - else - // I have no means of trusting you, cmd - ext_python("ircbot_message.py", "[config.comms_password] [IP] [channel] [paranoid_sanitize(msg)]") - return - -/proc/send2mainirc(var/msg, var/lesser_sanitize = FALSE) - if(config.main_irc) - send2irc(config.main_irc, msg, lesser_sanitize) - return - -/proc/send2adminirc(var/msg) - if(config.admin_irc) - send2irc(config.admin_irc, msg) - return diff --git a/code/modules/ext_scripts/python.dm b/code/modules/ext_scripts/python.dm deleted file mode 100644 index bee1244b94b..00000000000 --- a/code/modules/ext_scripts/python.dm +++ /dev/null @@ -1,9 +0,0 @@ -/proc/ext_python(var/script, var/args, var/scriptsprefix = 1) - if(scriptsprefix) script = "scripts/" + script - - if(world.system_type == MS_WINDOWS) - script = replacetext(script, "/", "\\") - - var/command = GLOB.python_path + " " + script + " " + args - shell("[command]") - return diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 91bf5f0ab9c..041fa87807b 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -15,7 +15,7 @@ message_admins("Admin logout: [key_name_admin(src)]") var/list/admincounter = staff_countup(R_BAN) if(admincounter[1] == 0) // No active admins - send2irc(config.admin_notify_irc, "[key_name(src)] logged out - No active admins, [admincounter[2]] non-admin staff, [admincounter[3]] inactive staff.") + SSdiscord.send2discord_simple(DISCORD_WEBHOOK_ADMIN, "[key_name(src)] logged out - No active admins, [admincounter[2]] non-admin staff, [admincounter[3]] inactive staff.") ..() update_morgue() diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 844615d5be7..84f32308964 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -121,13 +121,12 @@ aw_emerg = status_adminwarn_check(SUPERMATTER_EMERGENCY, aw_emerg, "CRIT: Supermatter integrity is below 50%!(JMP).", FALSE) aw_delam = status_adminwarn_check(SUPERMATTER_DELAMINATING, aw_delam, "CRIT: Supermatter is delaminating!(JMP).", TRUE) -/obj/machinery/power/supermatter_shard/proc/status_adminwarn_check(var/min_status, var/current_state, var/message, var/send_to_irc = FALSE) +/obj/machinery/power/supermatter_shard/proc/status_adminwarn_check(min_status, current_state, message) var/status = get_status() if(status >= min_status) if(!current_state) log_and_message_admins(message) - if(send_to_irc) - send2adminirc(message) + SSdiscord.send2discord_simple_noadmins(message) return TRUE else return FALSE diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index cafe92bcc87..2a50ccff03b 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -231,7 +231,7 @@ return mode = SHUTTLE_DOCKED timer = world.time - send2irc("Server", "The Emergency Shuttle has docked with the station.") + SSdiscord.send2discord_simple(DISCORD_WEBHOOK_PRIMARY, "The Emergency Shuttle has docked with the station.") emergency_shuttle_docked.Announce("The Emergency Shuttle has docked with the station. You have [timeLeft(600)] minutes to board the Emergency Shuttle.") /* diff --git a/config/example/config.txt b/config/example/config.txt index b97b6f701f3..54622806507 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -311,31 +311,9 @@ ALIEN_PLAYER_RATIO 0.2 ##Remove the # to let ghosts spin chairs GHOST_INTERACTION -## Password used for authorizing ircbot and other external tools. +## Password used for authorizing external tools via world/Topic. #COMMS_PASSWORD -## Uncomment to enable sending data to the IRC bot. -#USE_IRC_BOT - -## Host(s) where the IRC bot is hosted. Seperate IP's by ;. Port 45678 needs to be open. -#IRC_BOT_HOST 127.0.0.1;localhost - -## IRC channel to send information to. Leave blank to disable. -#MAIN_IRC #main - -## IRC channel to send adminhelps to. Leave blank to disable adminhelps-to-irc. -#ADMIN_IRC #admin - -## IRC channel to send direct messages to admins to. Leave blank to disable. -#ADMIN_NOTIFY_IRC #paradiseStaff - -## IRC channel to log CID randomizer blocker hits to. Leave blank to silence -#CIDRANDOMIZER_IRC #cidrandomizer - -## Path to the python2 executable on the system. Leave blank for default. -## Default is "python" on Windows, "/usr/bin/env python2" on UNIX. -#PYTHON_PATH pythonw - ## Expected round length in minutes EXPECTED_ROUND_LENGTH 120 @@ -461,3 +439,29 @@ DISABLE_HIGH_POP_MC_MODE_AMOUNT 60 ## BYOND accounts younger than the value below will alert admins when they connect for the first time, ## as well as making the BYOND account age in player panel bold BYOND_ACCOUNT_AGE_THRESHOLD 7 + +##### DISCORD STUFF ##### + +## If you are going to enable discord webhooks, fill out EVERYTHING in this section + +## Uncomment the line below to enable Discord webhooks +#ENABLE_DISCORD_WEBHOOKS + +## Role ID to be pinged with administrative events. If unset, all pings to this role will be disabled +## IF YOU ARE DISABLING THIS COMMENT IT OUT ENTIRELY, DONT LEAVE IT BLANK +#DISCORD_WEBHOOKS_ADMIN_ROLE_ID + +## Webhook URL for the main discord webhook +#DISCORD_WEBHOOKS_MAIN_URL + +## Webhook URL for the admin discord webhook +#DISCORD_WEBHOOKS_ADMIN_URL + +## Webhook URL for the mentor discord webhook +#DISCORD_WEBHOOKS_MENTOR_URL + +## Uncomment to send all ahelps to discord. If the line is commented, only ahelps made when no admins are online will be forwarded +## Ahelps forwarded when staff are online will never have the role ping, regardless of the setting above +#DISCORD_FORWARD_ALL_AHELPS + +##### END DISCORD STUFF ##### diff --git a/paradise.dme b/paradise.dme index f5a81db4362..b4174049832 100644 --- a/paradise.dme +++ b/paradise.dme @@ -222,11 +222,13 @@ #include "code\controllers\subsystem\atoms.dm" #include "code\controllers\subsystem\changelog.dm" #include "code\controllers\subsystem\cleanup.dm" +#include "code\controllers\subsystem\discord.dm" #include "code\controllers\subsystem\events.dm" #include "code\controllers\subsystem\fires.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\ghost_spawns.dm" #include "code\controllers\subsystem\holiday.dm" +#include "code\controllers\subsystem\http.dm" #include "code\controllers\subsystem\icon_smooth.dm" #include "code\controllers\subsystem\idlenpcpool.dm" #include "code\controllers\subsystem\input.dm" @@ -277,9 +279,11 @@ #include "code\datums\datacore.dm" #include "code\datums\datum.dm" #include "code\datums\datumvars.dm" +#include "code\datums\discord.dm" #include "code\datums\dog_fashion.dm" #include "code\datums\gas_mixture.dm" #include "code\datums\holocall.dm" +#include "code\datums\http.dm" #include "code\datums\hud.dm" #include "code\datums\log_record.dm" #include "code\datums\log_viewer.dm" @@ -1502,8 +1506,6 @@ #include "code\modules\examine\descriptions\structures.dm" #include "code\modules\examine\descriptions\turfs.dm" #include "code\modules\examine\descriptions\weapons.dm" -#include "code\modules\ext_scripts\irc.dm" -#include "code\modules\ext_scripts\python.dm" #include "code\modules\fish\fish_eggs.dm" #include "code\modules\fish\fish_items.dm" #include "code\modules\fish\fish_types.dm" diff --git a/rust_g.dll b/rust_g.dll index fb8df35878f..8e80d194a08 100644 Binary files a/rust_g.dll and b/rust_g.dll differ diff --git a/scripts/ircbot_message.py b/scripts/ircbot_message.py deleted file mode 100644 index 4339019e03d..00000000000 --- a/scripts/ircbot_message.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python2 - -# Four arguments, password host channel and message. -# EG: "ircbot_message.py hunter2 example.com #adminchannel ADMINHELP, people are killing me!" - -import sys,cPickle,socket,HTMLParser - -def pack(): - ht = HTMLParser.HTMLParser() - - passwd = sys.argv[1] - ip = sys.argv[3] - try: - data = [] - for in_data in sys.argv[4:]: #The rest of the arguments is data - data += {ht.unescape(in_data)} - except: - data = "NO DATA SPECIFIED" - dictionary = {"ip":ip,"data":[passwd] + data} - pickled = cPickle.dumps(dictionary) - nudge(pickled) -def nudge(data): - HOST = sys.argv[2] - PORT = 45678 - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((HOST,PORT)) - s.send(data) - s.close() - -if __name__ == "__main__" and len(sys.argv) > 1: # If not imported and more than one argument - pack()