mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-25 04:57:12 +01:00
SShttp + SSdiscord | ASYNCHRONOUS STUFF IN BYOND! (#14762)
* SShttp + SSdiscord | ASYNCHRONOUS STUFF IN BYOND! * Cleanup * HTTP Callback example * Fixes rust instability * More refactors * This works * The sanitizer (Now worth £3000) * New configs + other stuff * Lets give this a shot * Farie changes * Mentor support * Farie fixes
This commit is contained in:
@@ -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]'")
|
||||
|
||||
|
||||
@@ -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)*"
|
||||
@@ -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, "<span class='notice'>Code: [response.status_code] | Content: [response.body]")
|
||||
*/
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user