mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-29 23:17:02 +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:
@@ -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