Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TGS] DMAPI update to 7.3.0 + discord config #5880

Merged
merged 4 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions code/controllers/configuration.dm
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ GLOBAL_LIST_EMPTY(storyteller_cache)
var/webhook_url
var/webhook_key

var/message_announce_new_game = "A new round has begun!" // SOJOURN: discord bot configuration
var/message_announce_round_end = "The round is almost over! Get ready for the next one." // SOJOURN: discord bot configuration

var/profiler_permission = R_DEBUG | R_SERVER

var/allow_ic_printing = TRUE
Expand Down Expand Up @@ -805,6 +808,17 @@ GLOBAL_LIST_EMPTY(storyteller_cache)

else
log_misc("Unknown setting in configuration: '[name]'")

// SOJOURN: discord bot configuration: START
else if(type == "discord")
switch(name)
if("message_announce_new_game")
config.message_announce_new_game = value
if("message_announce_round_end")
config.message_announce_round_end = value
else
log_misc("Unknown setting in configuration: '[name]'")
// SOJOURN: discord bot configuration: END
fps = round(fps)
if(fps <= 0)
fps = initial(fps)
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/master.dm
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new

world.TgsInitializationComplete()

world.TgsTargetedChatBroadcast(new /datum/tgs_message_content(text = "A new round has begun!"))
world.TgsTargetedChatBroadcast(new /datum/tgs_message_content(text = config.message_announce_new_game))

// Sort subsystems by display setting for easy access.
sortTim(subsystems, GLOBAL_PROC_REF(cmp_subsystem_display))
Expand Down
2 changes: 2 additions & 0 deletions code/game/world.dm
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ var/world_topic_spam_protect_time = world.timeofday
if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[config.server]")

world.TgsTargetedChatBroadcast(new /datum/tgs_message_content(text = config.message_announce_round_end))
TgsReboot()

#ifdef UNIT_TESTS
Expand Down Expand Up @@ -262,6 +263,7 @@ var/world_topic_spam_protect_time = world.timeofday
config.load("config/config.txt")
config.load("config/game_options.txt", "game_options")
config.loadsql("config/dbconfig.txt")
config.load("config/discord.txt", "discord") // SOJOURN: discord bot configuration

/hook/startup/proc/loadMods()
world.load_mods()
Expand Down
11 changes: 11 additions & 0 deletions config/example/discord.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Chat Announce Options
## Various messages to be sent to game chats.

## Welcome message sent at the start of a new round.
## Example of a notification message: "A new round has begun!"
MESSAGE_ANNOUNCE_NEW_GAME A new round has begun!

## Message sent at the end of a round.
## Example of a role notification message: "<@&859887924223672360> The round is almost over! Get ready for the next one."
## Replace 859887924223672360 with the appropriate role ID if needed.
MESSAGE_ANNOUNCE_ROUND_END The round is almost over! Get ready for the next one.
123 changes: 86 additions & 37 deletions tgs_dmapi/tgs.dm
Original file line number Diff line number Diff line change
@@ -1,58 +1,59 @@
// tgstation-server DMAPI
// The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF RFC 2119.

#define TGS_DMAPI_VERSION "7.1.2"
#define TGS_DMAPI_VERSION "7.3.0"

// All functions and datums outside this document are subject to change with any version and should not be relied on.

// CONFIGURATION

/// Create this define if you want to do TGS configuration outside of this file.
/// Consumers SHOULD create this define if you want to do TGS configuration outside of this file.
#ifndef TGS_EXTERNAL_CONFIGURATION

// Comment this out once you've filled in the below.
// #error TGS API unconfigured
// Consumers MUST comment this out once you've filled in the below and are not using [TGS_EXTERNAL_CONFIGURATION].
// #error TGS API unconfigured

// Uncomment this if you wish to allow the game to interact with TGS 3..
// Consumers MUST uncomment this if you wish to allow the game to interact with TGS version 3.
// This will raise the minimum required security level of your game to TGS_SECURITY_TRUSTED due to it utilizing call()().
//#define TGS_V3_API

// Required interfaces (fill in with your codebase equivalent):

/// Create a global variable named `Name` and set it to `Value`.
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(Name, Value)
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(Name, Value)

/// Read the value in the global variable `Name`.
#define TGS_READ_GLOBAL(Name) GLOB.Name
#define TGS_READ_GLOBAL(Name) GLOB.Name

/// Set the value in the global variable `Name` to `Value`.
#define TGS_WRITE_GLOBAL(Name, Value) (GLOB.Name = Value)
#define TGS_WRITE_GLOBAL(Name, Value) (GLOB.Name = Value)

/// Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game use of DD -> TGS capabilities.
#define TGS_PROTECT_DATUM(Path)
#define TGS_PROTECT_DATUM(Path)

/// Display an announcement `message` from the server to all players.
#define TGS_WORLD_ANNOUNCE(message) (to_world(message))
#define TGS_WORLD_ANNOUNCE(message) (to_world(message))

/// Notify current in-game administrators of a string `event`.
#define TGS_NOTIFY_ADMINS(event) (to_chat(admins, span_adminnotice(event)))
#define TGS_NOTIFY_ADMINS(event) (to_chat(admins, span_adminnotice(event)))

/// Write an info `message` to a server log.
#define TGS_INFO_LOG(message) (log_debug(message))
#define TGS_INFO_LOG(message) (log_debug(message))

/// Write an warning `message` to a server log.
#define TGS_WARNING_LOG(message) (log_debug(message))
#define TGS_WARNING_LOG(message) (log_debug(message))

/// Write an error `message` to a server log.
#define TGS_ERROR_LOG(message) (log_debug(message))
#define TGS_ERROR_LOG(message) (log_debug(message))

/// Get the number of connected /clients.
#define TGS_CLIENT_COUNT (length(clients))
#define TGS_CLIENT_COUNT (length(clients))

#endif

#ifndef TGS_FILE2TEXT_NATIVE
#ifdef file2text
#error Your codebase is re-defining the BYOND proc file2text. The DMAPI requires the native version to read the result of world.Export(). You can fix this by adding "#define TGS_FILE2TEXT_NATIVE file2text" before your override of file2text to allow the DMAPI to use the native version. This will only be used for world.Export(), not regular file accesses
#error Your codebase is re-defining the BYOND proc file2text. The DMAPI requires the native version to read the result of world.Export(). You SHOULD fix this by adding "#define TGS_FILE2TEXT_NATIVE file2text" before your override of file2text to allow the DMAPI to use the native version. This will only be used for world.Export(), not regular file accesses
#endif
#define TGS_FILE2TEXT_NATIVE file2text
#endif
Expand Down Expand Up @@ -152,16 +153,17 @@
//REQUIRED HOOKS

/**
* Call this somewhere in [/world/proc/New] that is always run. This function may sleep!
* Consumers MUST call this somewhere in [/world/proc/New] that is always run. This function may sleep!
*
* * event_handler - Optional user defined [/datum/tgs_event_handler].
* * minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated. Can be one of [TGS_SECURITY_ULTRASAFE], [TGS_SECURITY_SAFE], or [TGS_SECURITY_TRUSTED].
* * http_handler - Optional user defined [/datum/tgs_http_handler].
*/
/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE, datum/tgs_http_handler/http_handler)
return

/**
* Call this when your initializations are complete and your game is ready to play before any player interactions happen.
* Consumers MUST call this when world initializations are complete and the game is ready to play before any player interactions happen.
*
* This may use [/world/var/sleep_offline] to make this happen so ensure no changes are made to it while this call is running.
* Afterwards, consider explicitly setting it to what you want to avoid this BYOND bug: http://www.byond.com/forum/post/2575184
Expand All @@ -170,9 +172,10 @@
/world/proc/TgsInitializationComplete()
return

/**
* Call this as late as possible in [world/proc/Reboot] (BEFORE ..()).
*/
/// Consumers MUST run this macro at the start of [/world/proc/Topic].
// #define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return

/// Consumers MUST call this as late as possible in [world/proc/Reboot] (BEFORE ..()).
/world/proc/TgsReboot()
return

Expand Down Expand Up @@ -266,7 +269,7 @@
/// The [/datum/tgs_chat_channel] the user was from.
var/datum/tgs_chat_channel/channel

/// User definable handler for TGS events.
/// User definable handler for TGS events This abstract version SHOULD be overridden to be used.
/datum/tgs_event_handler
/// If the handler receieves [TGS_EVENT_HEALTH_CHECK] events.
var/receive_health_checks = FALSE
Expand All @@ -280,7 +283,41 @@
set waitfor = FALSE
return

/// User definable chat command.
/// User definable handler for HTTP calls. This abstract version MUST be overridden to be used.
/datum/tgs_http_handler

/**
* User definable callback for executing HTTP GET requests.
* MUST perform BYOND sleeps while the request is in flight.
* MUST return a [/datum/tgs_http_result].
* SHOULD log its own errors
*
* url - The full URL to execute the GET request for including query parameters.
*/
/datum/tgs_http_handler/proc/PerformGet(url)
CRASH("[type]/PerformGet not implemented!")

/// Result of a [/datum/tgs_http_handler] call. MUST NOT be overridden.
/datum/tgs_http_result
/// HTTP response as text
var/response_text
/// Boolean request success flag. Set for any 2XX response code.
var/success

/**
* Create a [/datum/tgs_http_result].
*
* * response_text - HTTP response as text. Must be provided in New().
* * success - Boolean request success flag. Set for any 2XX response code. Must be provided in New().
*/
/datum/tgs_http_result/New(response_text, success)
if(response_text && !istext(response_text))
CRASH("response_text was not text!")

src.response_text = response_text
src.success = success

/// User definable chat command. This abstract version MUST be overridden to be used.
/datum/tgs_chat_command
/// The string to trigger this command on a chat bot. e.g `@bot name ...` or `!tgs name ...`.
var/name = ""
Expand All @@ -293,21 +330,27 @@

/**
* Process command activation. Should return a [/datum/tgs_message_content] to respond to the issuer with.
* MUST be implemented
*
* sender - The [/datum/tgs_chat_user] who issued the command.
* params - The trimmed string following the command `/datum/tgs_chat_command/var/name].
* * sender - The [/datum/tgs_chat_user] who issued the command.
* * params - The trimmed string following the command `/datum/tgs_chat_command/var/name].
*/
/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params)
CRASH("[type] has no implementation for Run()")

/// User definable chat message.
/// User definable chat message. MUST NOT be overridden.
/datum/tgs_message_content
/// The tring content of the message. Must be provided in New().
/// The string content of the message. Must be provided in New().
var/text

/// The [/datum/tgs_chat_embed] to embed in the message. Not supported on all chat providers.
var/datum/tgs_chat_embed/structure/embed

/**
* Create a [/datum/tgs_message_content].
*
* * text - The string content of the message.
*/
/datum/tgs_message_content/New(text)
..()
if(!istext(text))
Expand All @@ -316,7 +359,7 @@

src.text = text

/// User definable chat embed. Currently mirrors Discord chat embeds. See https://discord.com/developers/docs/resources/channel#embed-object-embed-structure for details.
/// User definable chat embed. Currently mirrors Discord chat embeds. See https://discord.com/developers/docs/resources/message#embed-object for details.
/datum/tgs_chat_embed/structure
var/title
var/description
Expand All @@ -328,13 +371,13 @@
/// Colour must be #AARRGGBB or #RRGGBB hex string.
var/colour

/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-image-structure for details.
var/datum/tgs_chat_embed/media/image

/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-thumbnail-structure for details.
var/datum/tgs_chat_embed/media/thumbnail

/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-video-structure for details.
var/datum/tgs_chat_embed/media/video

var/datum/tgs_chat_embed/footer/footer
Expand All @@ -343,58 +386,64 @@

var/list/datum/tgs_chat_embed/field/fields

/// Common datum for similar discord embed medias.
/// Common datum for similar Discord embed medias.
/datum/tgs_chat_embed/media
/// Must be set in New().
var/url
var/width
var/height
var/proxy_url

/// Create a [/datum/tgs_chat_embed].
/datum/tgs_chat_embed/media/New(url)
..()
if(!istext(url))
CRASH("[/datum/tgs_chat_embed/media] created with no url!")

src.url = url

/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-footer-structure for details.
/datum/tgs_chat_embed/footer
/// Must be set in New().
var/text
var/icon_url
var/proxy_icon_url

/// Create a [/datum/tgs_chat_embed/footer].
/datum/tgs_chat_embed/footer/New(text)
..()
if(!istext(text))
CRASH("[/datum/tgs_chat_embed/footer] created with no text!")

src.text = text

/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-provider-structure for details.
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-provider-structure for details.
/datum/tgs_chat_embed/provider
var/name
var/url

/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure for details. Must have name set in New().
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-author-structure for details. Must have name set in New().
/datum/tgs_chat_embed/provider/author
var/icon_url
var/proxy_icon_url

/// Create a [/datum/tgs_chat_embed/footer].
/datum/tgs_chat_embed/provider/author/New(name)
..()
if(!istext(name))
CRASH("[/datum/tgs_chat_embed/provider/author] created with no name!")

src.name = name

/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure for details. Must have name and value set in New().
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-field-structure for details.
/datum/tgs_chat_embed/field
/// Must be set in New().
var/name
/// Must be set in New().
var/value
var/is_inline

/// Create a [/datum/tgs_chat_embed/field].
/datum/tgs_chat_embed/field/New(name, value)
..()
if(!istext(name))
Expand Down
2 changes: 1 addition & 1 deletion tgs_dmapi/tgs/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# DMAPI Internals

This folder should be placed on it's own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.
This folder should be placed on its own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.

- [includes.dm](./includes.dm) is the file that should be included by DM code, it handles including the rest.
- The [core](./core) folder includes all code not directly part of any API version.
Expand Down
2 changes: 1 addition & 1 deletion tgs_dmapi/tgs/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This folder contains all DMAPI code not directly involved in an API.

- [_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
- [byond_world_export.dm](./byond_world_export.dm) contains the default `/datum/tgs_http_handler` implementation which uses `world.Export()`.
- [core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code.
- [datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement.
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
-
22 changes: 22 additions & 0 deletions tgs_dmapi/tgs/core/byond_world_export.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/datum/tgs_http_handler/byond_world_export

/datum/tgs_http_handler/byond_world_export/PerformGet(url)
// This is an infinite sleep until we get a response
var/export_response = world.Export(url)
TGS_DEBUG_LOG("byond_world_export: Export complete")

if(!export_response)
TGS_ERROR_LOG("byond_world_export: Failed request: [url]")
return new /datum/tgs_http_result(null, FALSE)

var/content = export_response["CONTENT"]
if(!content)
TGS_ERROR_LOG("byond_world_export: Failed request, missing content!")
return new /datum/tgs_http_result(null, FALSE)

var/response_json = TGS_FILE2TEXT_NATIVE(content)
if(!response_json)
TGS_ERROR_LOG("byond_world_export: Failed request, failed to load content!")
return new /datum/tgs_http_result(null, FALSE)

return new /datum/tgs_http_result(response_json, TRUE)
Loading
Loading