Merge pull request #2056 from CHOMPStationBot/upstream-merge-10316

[MIRROR] [MIRROR] Ports Nebula's Discord Webhook Integration
This commit is contained in:
Nadyr
2021-05-27 21:36:38 -04:00
committed by GitHub
27 changed files with 532 additions and 14 deletions

View File

@@ -166,3 +166,9 @@
#define COLOR_ASTEROID_ROCK "#735555"
#define COLOR_GOLD "#ffcc33"
// Discord requires colors to be in decimal instead of hexadecimal.
#define COLOR_WEBHOOK_DEFAULT 0x8bbbd5 // "#8bbbd5"
#define COLOR_WEBHOOK_GOOD 0x2ECC71 // "#2ECC71"
#define COLOR_WEBHOOK_POOR 0xE67E22 // "#E67E22"
#define COLOR_WEBHOOK_BAD 0xE74C3C // "#E74C3C"

View File

@@ -449,3 +449,16 @@ GLOBAL_LIST_INIT(all_volume_channels, list(
#define LOADOUT_WHITELIST_OFF 0
#define LOADOUT_WHITELIST_LAX 1
#define LOADOUT_WHITELIST_STRICT 2
#ifndef WINDOWS_HTTP_POST_DLL_LOCATION
#define WINDOWS_HTTP_POST_DLL_LOCATION "lib/byhttp.dll"
#endif
#ifndef UNIX_HTTP_POST_DLL_LOCATION
#define UNIX_HTTP_POST_DLL_LOCATION "lib/libbyhttp.so"
#endif
#ifndef HTTP_POST_DLL_LOCATION
#define HTTP_POST_DLL_LOCATION (world.system_type == MS_WINDOWS ? WINDOWS_HTTP_POST_DLL_LOCATION : UNIX_HTTP_POST_DLL_LOCATION)
#endif

View File

@@ -52,6 +52,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
// Subsystem init_order, from highest priority to lowest priority
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_WEBHOOKS 50
#define INIT_ORDER_DBCORE 41 //CHOMPEdit
#define INIT_ORDER_SQLITE 40
#define INIT_ORDER_CHEMISTRY 35

View File

@@ -0,0 +1,10 @@
// Please don't forget to update the webhooks page on the GitHub Wiki page with your new webhook ID.
#define WEBHOOK_ROUNDEND "webhook_roundend"
#define WEBHOOK_ROUNDPREP "webhook_roundprep"
#define WEBHOOK_ROUNDSTART "webhook_roundstart"
#define WEBHOOK_SUBMAP_LOADED "webhook_submap_loaded"
#define WEBHOOK_CUSTOM_EVENT "webhook_custom_event"
#define WEBHOOK_ELEVATOR_FALL "webhook_elevator_fall"
#define WEBHOOK_AHELP_SENT "webhook_ahelp_sent"
#define WEBHOOK_FAX_SENT "webhook_fax_sent"

View File

@@ -856,3 +856,14 @@ proc/dd_sortedTextList(list/incoming)
return result
var/global/list/json_cache = list()
/proc/cached_json_decode(var/json_to_decode)
if(!json_to_decode || !length(json_to_decode))
return list()
try
if(isnull(global.json_cache[json_to_decode]))
global.json_cache[json_to_decode] = json_decode(json_to_decode)
. = global.json_cache[json_to_decode]
catch(var/exception/e)
log_error("Exception during JSON decoding ([json_to_decode]): [e]")
return list()

View File

@@ -509,3 +509,23 @@ proc/TextPreview(var/string,var/len=40)
var/charcount = count - length_char(text)
var/list/chars_to_add[max(charcount + 1, 0)]
return text + jointext(chars_to_add, char)
//Readds quotes and apostrophes to HTML-encoded strings
/proc/readd_quotes(var/t)
var/list/repl_chars = list(""" = "\"","'" = "'")
for(var/char in repl_chars)
var/index = findtext(t, char)
while(index)
t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5)
index = findtext(t, char)
return t
// Rips out paper HTML but tries to keep it semi-readable.
/proc/paper_html_to_plaintext(paper_text)
paper_text = replacetext(paper_text, "<hr>", "-----")
paper_text = replacetext(paper_text, "<li>", "- ") // This makes ordered lists turn into unordered but fixing that is too much effort.
paper_text = replacetext(paper_text, "</li>", "\n")
paper_text = replacetext(paper_text, "<p>", "\n")
paper_text = replacetext(paper_text, "<br>", "\n")
paper_text = strip_html_properly(paper_text) // Get rid of everything else entirely.
return paper_text

View File

@@ -1,9 +0,0 @@
//Readds quotes and apostrophes to HTML-encoded strings
/proc/readd_quotes(var/t)
var/list/repl_chars = list("&#34;" = "\"","&#39;" = "'")
for(var/char in repl_chars)
var/index = findtext(t, char)
while(index)
t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5)
index = findtext(t, char)
return t

View File

@@ -397,3 +397,17 @@
return /datum
return text2path(copytext(string_type, 1, last_slash))
//checks if a file exists and contains text
//returns text as a string if these conditions are met
/proc/safe_file2text(filename, error_on_invalid_return = TRUE)
try
if(fexists(filename))
. = file2text(filename)
if(!. && error_on_invalid_return)
error("File empty ([filename])")
else if(error_on_invalid_return)
error("File not found ([filename])")
catch(var/exception/E)
if(error_on_invalid_return)
error("Exception when loading file as string: [E]")

View File

@@ -293,6 +293,8 @@ var/list/gamemode_cache = list()
var/static/vgs_access_identifier = null // VOREStation Edit - VGS
var/static/vgs_server_port = null // VOREStation Edit - VGS
var/disable_webhook_embeds = FALSE
/datum/configuration/New()
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
for (var/T in L)
@@ -1018,6 +1020,9 @@ var/list/gamemode_cache = list()
if("use_loyalty_implants")
config.use_loyalty_implants = 1
if("loadout_whitelist")
config.loadout_whitelist = text2num(value)
else
log_misc("Unknown setting in configuration: '[name]'")

View File

@@ -49,6 +49,13 @@ var/global/datum/controller/subsystem/ticker/ticker
/datum/controller/subsystem/ticker/Initialize()
pregame_timeleft = config.pregame_time
send2mainirc("Server lobby is loaded and open at byond://[config.serverurl ? config.serverurl : (config.server ? config.server : "[world.address]:[world.port]")]")
SSwebhooks.send(
WEBHOOK_ROUNDPREP,
list(
"map" = station_name(),
"url" = get_world_url()
)
)
GLOB.autospeaker = new (null, null, null, 1) //Set up Global Announcer
return ..()

View File

@@ -0,0 +1,94 @@
SUBSYSTEM_DEF(webhooks)
name = "Webhooks"
init_order = INIT_ORDER_WEBHOOKS
flags = SS_NO_FIRE
var/list/webhook_decls = list()
/datum/controller/subsystem/webhooks/Initialize()
load_webhooks()
. = ..()
/datum/controller/subsystem/webhooks/proc/load_webhooks()
if(!fexists(HTTP_POST_DLL_LOCATION))
to_world_log("Unable to locate HTTP POST lib at [HTTP_POST_DLL_LOCATION], webhooks will not function on this run.")
return
var/list/all_webhooks_by_id = list()
var/list/all_webhooks = decls_repository.get_decls_of_subtype(/decl/webhook)
for(var/wid in all_webhooks)
var/decl/webhook/webhook = all_webhooks[wid]
if(webhook.id)
all_webhooks_by_id[webhook.id] = webhook
webhook_decls.Cut()
var/webhook_config = safe_file2text("config/webhooks.json")
if(webhook_config)
for(var/webhook_data in cached_json_decode(webhook_config))
var/wid = webhook_data["id"]
var/wurl = webhook_data["url"]
var/list/wmention = webhook_data["mentions"]
if(wmention && !islist(wmention))
wmention = list(wmention)
to_world_log("Setting up webhook [wid].")
if(wid && wurl && all_webhooks_by_id[wid])
var/decl/webhook/webhook = all_webhooks_by_id[wid]
webhook.urls = islist(wurl) ? wurl : list(wurl)
for(var/url in webhook.urls)
if(!webhook.urls[url])
webhook.urls[url] = list()
else if(!islist(webhook.urls[url]))
webhook.urls[url] = list(webhook.urls[url])
if(wmention)
webhook.mentions = wmention?.Copy()
webhook_decls[wid] = webhook
to_world_log("Webhook [wid] ready.")
else
to_world_log("Failed to set up webhook [wid].")
/datum/controller/subsystem/webhooks/proc/send(var/wid, var/wdata)
var/decl/webhook/webhook = webhook_decls[wid]
if(webhook)
if(webhook.send(wdata))
to_world_log("Sent webhook [webhook.id].")
log_debug("Webhook sent: [webhook.id].")
else
to_world_log("Failed to send webhook [webhook.id].")
log_debug("Webhook failed to send: [webhook.id].")
/client/proc/reload_webhooks()
set name = "Reload Webhooks"
set category = "Debug"
if(!holder)
return
if(!SSwebhooks.subsystem_initialized)
to_chat(usr, SPAN_WARNING("Let the webhook subsystem initialize before trying to reload it."))
return
to_world_log("[usr.key] has reloaded webhooks.")
log_and_message_admins("has reloaded webhooks.")
SSwebhooks.load_webhooks()
/client/proc/ping_webhook()
set name = "Ping Webhook"
set category = "Debug"
if(!holder)
return
if(!length(SSwebhooks.webhook_decls))
to_chat(usr, SPAN_WARNING("Webhook list is empty; either webhooks are disabled, webhooks aren't configured, or the subsystem hasn't initialized."))
return
var/choice = input(usr, "Select a webhook to ping.", "Ping Webhook") as null|anything in SSwebhooks.webhook_decls
if(choice && SSwebhooks.webhook_decls[choice])
var/decl/webhook/webhook = SSwebhooks.webhook_decls[choice]
log_and_message_admins("has pinged webhook [choice].", usr)
to_world_log("[usr.key] has pinged webhook [choice].")
webhook.send()
/hook/roundstart/proc/run_webhook()
SSwebhooks.send(WEBHOOK_ROUNDSTART, list("url" = get_world_url()))
return 1

View File

@@ -396,6 +396,15 @@ var/global/list/additional_antag_types = list()
feedback_set("escaped_on_cryopod",escaped_on_cryopod)
send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.")
SSwebhooks.send(
WEBHOOK_ROUNDEND,
list(
"survivors" = surviving_total,
"escaped" = escaped_total,
"ghosts" = ghosts,
"clients" = clients
)
)
return 0

View File

@@ -712,3 +712,39 @@ proc/establish_old_db_connection()
SStimer?.reset_buckets()
#undef FAILED_DB_CONNECTION_CUTOFF
/proc/get_world_url()
. = "byond://"
if(config.serverurl)
. += config.serverurl
else if(config.server)
. += config.server
else
. += "[world.address]:[world.port]"
var/global/game_id = null
/hook/startup/proc/generate_gameid()
if(game_id != null)
return
game_id = ""
var/list/c = list(
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"
)
var/l = c.len
var/t = world.timeofday
for(var/_ = 1 to 4)
game_id = "[c[(t % l) + 1]][game_id]"
t = round(t / l)
game_id = "-[game_id]"
t = round(world.realtime / (10 * 60 * 60 * 24))
for(var/_ = 1 to 3)
game_id = "[c[(t % l) + 1]][game_id]"
t = round(t / l)
return 1

View File

@@ -1633,6 +1633,19 @@ datum/admins/var/obj/item/weapon/paper/admin/faxreply // var to hold fax replies
if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights)
to_chat(C, "<span class='log_message'><span class='prefix'>FAX LOG:</span>[key_name_admin(src.owner)] has sent a fax message to [destination.department] (<a href='?_src_=holder;AdminFaxView=\ref[rcvdcopy]'>VIEW</a>)</span>")
var/plaintext_title = P.sender ? "replied to [key_name(P.sender)]'s fax" : "sent a fax message to [destination.department]"
var/fax_text = paper_html_to_plaintext(P.info)
log_game(plaintext_title)
log_game(fax_text)
SSwebhooks.send(
WEBHOOK_FAX_SENT,
list(
"name" = "[key_name(owner)] [plaintext_title].",
"body" = fax_text
)
)
else
to_chat(src.owner, "<span class='warning'>Message reply failed.</span>")

View File

@@ -239,7 +239,9 @@ var/list/admin_verbs_debug = list(
/client/proc/admin_give_modifier,
/client/proc/simple_DPS,
/datum/admins/proc/view_feedback,
/client/proc/debug_global_variables
/client/proc/debug_global_variables,
/client/proc/ping_webhook,
/client/proc/reload_webhooks
)
var/list/admin_verbs_paranoid_debug = list(

View File

@@ -213,6 +213,17 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
else
ahelp_discord_message("ADMINHELP: FROM: [initiator_ckey]/[initiator_key_name] - MSG: **[msg]** - Heard by [activeMins] NON-AFK staff members.") //CHOMPEdit
//YW EDIT END
// Also send it to discord since that's the hip cool thing now.
SSwebhooks.send(
WEBHOOK_AHELP_SENT,
list(
"name" = "Ticket ([id]) (Game ID: [game_id]) ticket opened.",
"body" = "[key_name(initiator)] has opened a ticket. \n[msg]",
"color" = COLOR_WEBHOOK_POOR
)
)
GLOB.ahelp_tickets.active_tickets += src
/datum/admin_help/Destroy()
@@ -305,6 +316,14 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
feedback_inc("ahelp_reopen")
TicketPanel() //can only be done from here, so refresh it
SSwebhooks.send(
WEBHOOK_AHELP_SENT,
list(
"name" = "Ticket ([id]) (Game ID: [game_id]) reopened.",
"body" = "Reopened by [key_name(usr)]."
)
)
//private
/datum/admin_help/proc/RemoveActive()
if(state != AHELP_ACTIVE)
@@ -330,6 +349,14 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/msg = "Ticket [TicketHref("#[id]")] closed by [key_name_admin(usr)]."
message_admins(msg)
log_admin(msg)
SSwebhooks.send(
WEBHOOK_AHELP_SENT,
list(
"name" = "Ticket ([id]) (Game ID: [game_id]) closed.",
"body" = "Closed by [key_name(usr)].",
"color" = COLOR_WEBHOOK_BAD
)
)
//Mark open ticket as resolved/legitimate, returns ahelp verb
/datum/admin_help/proc/Resolve(silent = FALSE)
@@ -347,6 +374,14 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/msg = "Ticket [TicketHref("#[id]")] resolved by [key_name_admin(usr)]"
message_admins(msg)
log_admin(msg)
SSwebhooks.send(
WEBHOOK_AHELP_SENT,
list(
"name" = "Ticket ([id]) (Game ID: [game_id]) resolved.",
"body" = "Marked as Resolved by [key_name(usr)].",
"color" = COLOR_WEBHOOK_GOOD
)
)
//Close and return ahelp verb, use if ticket is incoherent
/datum/admin_help/proc/Reject(key_name = key_name_admin(usr))
@@ -367,6 +402,14 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
log_admin(msg)
AddInteraction("Rejected by [key_name_admin(usr)].")
Close(silent = TRUE)
SSwebhooks.send(
WEBHOOK_AHELP_SENT,
list(
"name" = "Ticket ([id]) (Game ID: [game_id]) rejected.",
"body" = "Rejected by [key_name(usr)].",
"color" = COLOR_WEBHOOK_BAD
)
)
//Resolve ticket with IC Issue message
/datum/admin_help/proc/ICIssue(key_name = key_name_admin(usr))
@@ -386,6 +429,14 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
log_admin(msg)
AddInteraction("Marked as IC issue by [key_name_admin(usr)]")
Resolve(silent = TRUE)
SSwebhooks.send(
WEBHOOK_AHELP_SENT,
list(
"name" = "Ticket ([id]) (Game ID: [game_id]) marked as IC issue.",
"body" = "Marked as IC Issue by [key_name(usr)].",
"color" = COLOR_WEBHOOK_BAD
)
)
//Resolve ticket with IC Issue message
/datum/admin_help/proc/HandleIssue()
@@ -397,11 +448,18 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(initiator)
to_chat(initiator, msg)
feedback_inc("ahelp_icissue")
feedback_inc("ahelp_handling")
msg = "Ticket [TicketHref("#[id]")] being handled by [key_name(usr,FALSE,FALSE)]"
message_admins(msg)
log_admin(msg)
AddInteraction("[key_name_admin(usr)] is now handling this ticket.")
SSwebhooks.send(
WEBHOOK_AHELP_SENT,
list(
"name" = "Ticket ([id]) (Game ID: [game_id]) being handled.",
"body" = "[key_name(usr)] is now handling the ticket."
)
)
//Show the ticket panel
/datum/admin_help/proc/TicketPanel()

View File

@@ -24,6 +24,13 @@
to_world("<span class='alert'>[custom_event_msg]</span>")
to_world("<br>")
SSwebhooks.send(
WEBHOOK_CUSTOM_EVENT,
list(
"text" = custom_event_msg,
)
)
// normal verb for players to view info
/client/verb/cmd_view_custom_event()
set category = "OOC"

View File

@@ -227,6 +227,21 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
sleep(50)
visible_message("[src] beeps, \"Message transmitted successfully.\"")
// Turns objects into just text.
/obj/machinery/photocopier/faxmachine/proc/make_summary(obj/item/sent)
if(istype(sent, /obj/item/weapon/paper))
var/obj/item/weapon/paper/P = sent
return P.info
if(istype(sent, /obj/item/weapon/paper_bundle))
. = ""
var/obj/item/weapon/paper_bundle/B = sent
for(var/i in 1 to B.pages.len)
var/obj/item/weapon/paper/P = B.pages[i]
if(istype(P)) // Photos can show up here too.
if(.) // Space out different pages.
. += "<br>"
. += "PAGE [i] - [P.name]<br>"
. += P.info
/obj/machinery/photocopier/faxmachine/proc/message_admins(var/mob/sender, var/faxname, var/obj/item/sent, var/reply_type, font_colour="#006100")
var/msg = "<span class='notice'><b><font color='[font_colour]'>[faxname]: </font>[get_options_bar(sender, 2,1,1)]"
@@ -242,3 +257,24 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
var/faxid = export_fax(sent)
message_chat_admins(sender, faxname, sent, faxid, font_colour)
// VoreStation Edit End
// Webhooks don't parse the HTML on the paper, so we gotta strip them out so it's still readable.
var/summary = make_summary(sent)
summary = paper_html_to_plaintext(summary)
log_game("Fax to [lowertext(faxname)] was sent by [key_name(sender)].")
log_game(summary)
var/webhook_length_limit = 1900 // The actual limit is a little higher.
if(length(summary) > webhook_length_limit)
summary = copytext(summary, 1, webhook_length_limit + 1)
summary += "\n\[Truncated\]"
SSwebhooks.send(
WEBHOOK_FAX_SENT,
list(
"name" = "[faxname] '[sent.name]' sent from [key_name(sender)]",
"body" = summary
)
)

View File

@@ -0,0 +1,72 @@
/decl/webhook
var/id
var/list/urls
var/list/mentions
/decl/webhook/proc/get_message(var/list/data)
. = list()
/decl/webhook/proc/http_post(var/target_url, var/payload)
if (!target_url)
return -1
var/result = call(HTTP_POST_DLL_LOCATION, "send_post_request")(target_url, payload, json_encode(list("Content-Type" = "application/json")))
result = cached_json_decode(result)
if (result["error_code"])
log_debug("byhttp error: [result["error"]] ([result["error_code"]])")
return result["error_code"]
return list(
"status_code" = result["status_code"],
"body" = result["body"]
)
/decl/webhook/proc/send(var/list/data)
var/list/message = get_message(data)
if(!length(message))
return FALSE
if(config.disable_webhook_embeds)
var/list/embed_content
for(var/list/embed in message["embeds"])
if(embed["title"])
LAZYADD(embed_content, "**[embed["title"]]**")
if(embed["description"])
LAZYADD(embed_content, embed["description"])
if(length(embed_content))
if(message["content"])
message["content"] = "[message["content"]]\n[jointext(embed_content, "\n")]"
else
message["content"] = jointext(embed_content, "\n")
message -= "embeds"
. = TRUE
for(var/target_url in urls)
var/url_message = message.Copy()
var/list/url_mentions = get_mentions(target_url)
if(islist(url_mentions) && length(url_mentions))
if(url_message["content"])
url_message["content"] = "[jointext(url_mentions, ", ")]: [url_message["content"]]"
else
url_message["content"] = "[jointext(url_mentions, ", ")]"
var/list/httpresponse = http_post(target_url, json_encode(url_message))
if(!islist(httpresponse))
. = FALSE
continue
switch(httpresponse["status_code"])
if (200 to 299)
continue
if (400 to 599)
log_debug("Webhooks: HTTP error code while sending to '[target_url]': [httpresponse["status_code"]]. Data: [httpresponse["body"]].")
else
log_debug("Webhooks: unknown HTTP code while sending to '[target_url]': [httpresponse["status_code"]]. Data: [httpresponse["body"]].")
. = FALSE
/decl/webhook/proc/get_mentions(var/mentioning_url)
. = mentions?.Copy()
var/url_mentions = LAZYACCESS(urls, mentioning_url)
if(length(url_mentions))
LAZYDISTINCTADD(., url_mentions)

View File

@@ -0,0 +1,13 @@
/decl/webhook/ahelp_sent
id = WEBHOOK_AHELP_SENT
/decl/webhook/ahelp_sent/get_message(var/list/data)
.= ..()
.["embeds"] = list(list(
"title" = "[data["name"]]",
"description" = data["body"],
"color" = data["color"] || COLOR_WEBHOOK_DEFAULT
))
/decl/webhook/ahelp_sent/get_mentions()
. = !length(GLOB.admins) && ..() // VOREStation Edit - GLOB admins

View File

@@ -0,0 +1,11 @@
/decl/webhook/custom_event
id = WEBHOOK_CUSTOM_EVENT
// Data expects a "text" field containing the new custom event text.
/decl/webhook/custom_event/get_message(var/list/data)
. = ..()
.["embeds"] = list(list(
"title" = "A custom event is beginning.",
"description" = (data && data["text"]) || "undefined",
"color" = COLOR_WEBHOOK_DEFAULT
))

View File

@@ -0,0 +1,10 @@
/decl/webhook/fax_sent
id = WEBHOOK_FAX_SENT
/decl/webhook/fax_sent/get_message(var/list/data)
.= ..()
.["embeds"] = list(list(
"title" = "[data["name"]]",
"description" = data["body"],
"color" = COLOR_WEBHOOK_DEFAULT
))

View File

@@ -0,0 +1,26 @@
/decl/webhook/roundend
id = WEBHOOK_ROUNDEND
// Data expects three numerical fields: "survivors", "escaped", "ghosts", "clients"
/decl/webhook/roundend/get_message(var/list/data)
. = ..()
var/desc = "A round of **[SSticker.mode ? SSticker.mode.name : "Unknown"]** ([game_id]) has ended.\n\n"
if(data)
var/s_escaped = "Escaped"
if(!emergency_shuttle.evac)
s_escaped = "Transferred"
if(data["survivors"] > 0)
desc += "Survivors: **[data["survivors"]]**\n"
desc += "[s_escaped]: **[data["escaped"]]**\n"
else
desc += "There were **no survivors**.\n\n"
desc += "Ghosts: **[data["ghosts"]]**\n"
desc += "Players: **[data["clients"]]**\n"
desc += "Round duration: **[roundduration2text()]**"
.["embeds"] = list(list(
// "title" = global.end_credits_title,
"title" = "Round Has Ended",
"description" = desc,
"color" = COLOR_WEBHOOK_DEFAULT
))

View File

@@ -0,0 +1,17 @@
/decl/webhook/roundprep
id = WEBHOOK_ROUNDPREP
// Data expects "url" and field pointing to the current hosted server and port to connect on.
/decl/webhook/roundprep/get_message(var/list/data)
. = ..()
var/desc = "The server has been started!\n"
if(data && data["map"])
desc += "Map: **[data["map"]]**\n"
if(data && data["url"])
desc += "Address: <[data["url"]]>"
.["embeds"] = list(list(
"title" = "New round is being set up.",
"description" = desc,
"color" = COLOR_WEBHOOK_DEFAULT
))

View File

@@ -0,0 +1,16 @@
/decl/webhook/roundstart
id = WEBHOOK_ROUNDSTART
// Data expects a "url" field pointing to the current hosted server and port to connect on.
/decl/webhook/roundstart/get_message(var/list/data)
. = ..()
var/desc = "Gamemode: **[SSticker.mode.name]**\n"
desc += "Players: **[global.player_list.len]**"
if(data && data["url"])
desc += "\nAddress: <[data["url"]]>"
.["embeds"] = list(list(
"title" = "Round has started.",
"description" = desc,
"color" = COLOR_WEBHOOK_DEFAULT
))

View File

@@ -0,0 +1,12 @@
[
{
"id" : "webhook_roundend",
"url" : {
"someurl0" : [],
"someurl1" : [],
"someurl2" : "somemention0",
"someurl3" : [ "somemention1", "somemention2" ]
},
"mentions" : [ "somemention3", "somemention4" ]
}
]

View File

@@ -104,6 +104,7 @@
#include "code\__defines\unit_tests.dm"
#include "code\__defines\vote.dm"
#include "code\__defines\vv.dm"
#include "code\__defines\webhooks.dm"
#include "code\__defines\wires.dm"
#include "code\__defines\xenoarcheaology.dm"
#include "code\__defines\ZAS.dm"
@@ -145,7 +146,6 @@
#include "code\_helpers\storage.dm"
#include "code\_helpers\string_lists.dm"
#include "code\_helpers\text.dm"
#include "code\_helpers\text_vr.dm"
#include "code\_helpers\time.dm"
#include "code\_helpers\turfs.dm"
#include "code\_helpers\type2type.dm"
@@ -309,6 +309,7 @@
#include "code\controllers\subsystems\timer.dm"
#include "code\controllers\subsystems\transcore_vr.dm"
#include "code\controllers\subsystems\vote.dm"
#include "code\controllers\subsystems\webhooks.dm"
#include "code\controllers\subsystems\xenoarch.dm"
#include "code\controllers\subsystems\processing\bellies_vr.dm"
#include "code\controllers\subsystems\processing\fastprocess.dm"
@@ -4162,6 +4163,13 @@
#include "code\modules\vore\resizing\sizegun_vr.dm"
#include "code\modules\vore\smoleworld\smoleworld_vr.dm"
#include "code\modules\vore\weight\fitness_machines_vr.dm"
#include "code\modules\webhooks\_webhook.dm"
#include "code\modules\webhooks\webhook_ahelp2discord.dm"
#include "code\modules\webhooks\webhook_custom_event.dm"
#include "code\modules\webhooks\webhook_fax2discord.dm"
#include "code\modules\webhooks\webhook_roundend.dm"
#include "code\modules\webhooks\webhook_roundprep.dm"
#include "code\modules\webhooks\webhook_roundstart.dm"
#include "code\modules\xenoarcheaology\anomaly_container.dm"
#include "code\modules\xenoarcheaology\boulder.dm"
#include "code\modules\xenoarcheaology\effect.dm"