mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-27 05:57:24 +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,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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user