Files
Aurora.3/code/datums/api.dm
Lohikar 61b5203d24 Runtime Map Loading (#3597)
changes:

Maps are no longer compiled in, instead loaded directly from the DMMs at runtime.
Z level defines have been moved from the config to map datums.
Unit tests now use typecaches.
DMMS now actually works.
DMMS has been updated slightly.
DMMS is now capable of loading simple lists of non-text types.
DMMS is now faster when loading many types without mapped in attributes and when loading area instances.
Asteroid generation is now defined on the map datum instead of being hard-coded in SSasteroid.
Holodeck presets are now defined on the map datum.
Atmos machinery now uses Initialize().
2017-10-18 23:07:34 +03:00

1008 lines
33 KiB
Plaintext

//
// This file contains the API commands for the serverside API
//
// IMPORTANT:
// When changing api commands always update the version number of the API
// The version number is defined in /datum/topic_command/api_get_version
//Init the API at startup
/hook/startup/proc/setup_api()
for (var/path in typesof(/datum/topic_command) - /datum/topic_command)
var/datum/topic_command/A = new path()
if(A != null)
topic_commands[A.name] = A
topic_commands_names.Add(A.name)
listclearnulls(topic_commands)
listclearnulls(topic_commands_names)
if (config.api_rate_limit_whitelist.len)
// To make the api_rate_limit_whitelist[addr] grabs actually work.
for (var/addr in config.api_rate_limit_whitelist)
config.api_rate_limit_whitelist[addr] = 1
return 1
/world/proc/api_do_auth_check(var/addr, var/auth, var/datum/topic_command/command)
//Check if command is on nothrottle list
if(command.no_throttle == 1)
log_debug("API: Throttling bypassed - Command [command.name] set to no_throttle")
else
if(world_api_rate_limit[addr] != null && config.api_rate_limit_whitelist[addr] == null) //Check if the ip is in the rate limiting list and not in the whitelist
if(abs(world_api_rate_limit[addr] - world.time) < config.api_rate_limit) //Check the last request time of the ip
world_api_rate_limit[addr] = world.time // Set the time of the last request
return 2 //Throttled
world_api_rate_limit[addr] = world.time // Set the time of the last request
//Check if the command is on the auth whitelist
if(command.no_auth == 1)
log_debug("API: Auth bypassed - Command [command.name] set to no_auth")
return 0 // Authed (bypassed)
var/DBQuery/authquery = dbcon.NewQuery({"SELECT api_f.command
FROM ss13_api_token_command as api_t_f, ss13_api_tokens as api_t, ss13_api_commands as api_f
WHERE api_t.id = api_t_f.token_id AND api_f.id = api_t_f.command_id
AND api_t.deleted_at IS NULL
AND (
(token = :token: AND ip = :ip: AND command = :command:)
OR
(token = :token: AND ip IS NULL AND command = :command:)
OR
(token = :token: AND ip = :ip: AND command = \"_ANY\")
OR
(token = :token: AND ip IS NULL AND command = \"_ANY\")
OR
(token IS NULL AND ip IS NULL AND command = :command:)
)"})
//Check if the token is not deleted
//Check if one of the following is true:
// Full Match - Token IP and Command Matches
// Any IP - Token and Command Matches, IP is set to NULL (not required)
// Any Command - Token and IP Matches, Command is set to _ANY
// Any Command, Any IP - Token Matches, IP is set to NULL (not required), Command is set to _ANY
// Public - Token is set to NULL, IP is set to NULL and command matches
authquery.Execute(list("token" = auth, "ip" = addr, "command" = command.name))
log_debug("API: Auth Check - Query Executed - Returned Rows: [authquery.RowCount()]")
if (authquery.RowCount())
return 0 // Authed
return 1 // Bad Key
proc/api_update_command_database()
log_debug("API: DB Command Update Called")
//Check if DB Connection is established
if (!establish_db_connection(dbcon))
return 0 //Error
var/DBQuery/commandinsertquery = dbcon.NewQuery({"INSERT INTO ss13_api_commands (command,description)
VALUES (:command_name:,:command_description:)
ON DUPLICATE KEY UPDATE description = :command_description:;"})
for(var/com in topic_commands)
var/datum/topic_command/command = topic_commands[com]
commandinsertquery.Execute(list("command_name" = command.name, "command_description" = command.description))
log_debug("API: DB Command Update Executed")
return 1 //OK
//API Boilerplate
/datum/topic_command
var/name = null //Name for the command
var/no_auth = 0 //If the user does NOT need to be authed to use the command
var/no_throttle = 0 //If this command should NOT be limited by the throtteling
var/description = null //Description for the command
var/list/params = list() //Required Parameters for the command
//Explanation of the parameter options:
//Required - name -> Name of the parameter - should be the same as the index in the list
//Required - desc -> Description of the parameter
//Required - req -> Is this a required parameter: 1 -> Yes, 0 -> No
//Required - type -> What type is this:
// str->String,
// int->Integer,
// lst->List/array,
// senderkey->unique identifier of the person sending the request
// slct -> Select one of multiple specified options
//Required* - options -> The possible options that can be selected (slct)
var/statuscode = null
var/response = null
var/data = null
/datum/topic_command/proc/run_command(queryparams)
// Always returns 1 --> Details status in statuscode, response and data
return 1
/datum/topic_command/proc/check_params_missing(queryparams)
//Check if some of the required params are missing
// 0 -> if all params are supplied
// >=1 -> if a param is missing
var/list/missing_params = list()
var/errorcount = 0
for(var/key in params)
var/list/param = params[key]
if(queryparams[key] == null)
if(param["req"] == 0)
log_debug("API: The following parameter is OPTIONAL and missing: [param["name"]] - [param["desc"]]")
else
log_debug("API: The following parameter is REQUIRED but missing: [param["name"]] - [param["desc"]]")
errorcount ++
missing_params += param["name"]
if(errorcount)
log_debug("API: Request aborted. Required parameters missing")
statuscode = 400
response = "Required params missing"
data = missing_params
return errorcount
return 0
//
// API for the API
//
/datum/topic_command/api_get_version
name = "api_get_version"
description = "Gets the version of the API"
no_auth = 1
no_throttle = 1
/datum/topic_command/api_get_version/run_command(queryparams)
var/list/version = list()
var/versionstring = null
//The Version Number follows SemVer http://semver.org/
version["major"] = 2 //Major Version Number --> Increment when implementing breaking changes
version["minor"] = 0 //Minor Version Number --> Increment when adding features
version["patch"] = 0 //Patchlevel --> Increment when fixing bugs
versionstring = "[version["major"]].[version["minor"]].[version["patch"]]"
statuscode = 200
response = versionstring
data = version
return 1
//Get all the commands a specific token / ip combo is authorized to use
/datum/topic_command/api_get_authed_commands
name = "api_get_authed_commands"
description = "Returns the commands that can be accessed by the requesting ip and token"
/datum/topic_command/api_get_authed_commands/run_command(queryparams)
var/list/commands = list()
//Check if DB Connection is established
if (!establish_db_connection(dbcon))
statuscode = 500
response = "DB Connection Unavailable"
return 1
var/DBQuery/commandsquery = dbcon.NewQuery({"SELECT api_f.command
FROM ss13_api_token_command as api_t_f, ss13_api_tokens as api_t, ss13_api_commands as api_f
WHERE api_t.id = api_t_f.token_id AND api_f.id = api_t_f.command_id
AND (
(token = :token: AND ip = :ip:)
OR
(token = :token: AND ip IS NULL)
OR
(token IS NULL AND ip = :ip:)
)
ORDER BY command DESC"})
commandsquery.Execute(list("token" = queryparams["auth"], "ip" = queryparams["addr"]))
while (commandsquery.NextRow())
commands[commandsquery.item[1]] = commandsquery.item[1]
if(commandsquery.item[1] == "_ANY")
statuscode = 200
response = "Authorized commands retrieved - ALL"
data = topic_commands_names
return 1
statuscode = 200
response = "Authorized commands retrieved"
data = commands
return 1
//Get details for a specific api command
/datum/topic_command/api_explain_command
name = "api_explain_command"
description = "Explains a specific API command"
no_throttle = 1
params = list(
"command" = list("name"="command","desc"="The name of the API command that should be explained","req"=1,"type"="str")
)
/datum/topic_command/api_explain_command/run_command(queryparams)
var/datum/topic_command/apicommand = topic_commands[queryparams["command"]]
var/list/commanddata = list()
if (isnull(apicommand))
statuscode = 501
response = "Not Implemented - The requested command does not exist"
return 1
//Then query for auth
if (!establish_db_connection(dbcon))
statuscode = 500
response = "DB Connection Unavailable"
return 1
var/DBQuery/permquery = dbcon.NewQuery({"SELECT api_f.command
FROM ss13_api_token_command as api_t_f, ss13_api_tokens as api_t, ss13_api_commands as api_f
WHERE api_t.id = api_t_f.token_id AND api_f.id = api_t_f.command_id
AND api_t.deleted_at IS NULL
AND (
(token = :token: AND ip = :ip: AND command = :command:)
OR
(token = :token: AND ip IS NULL AND command = :command:)
OR
(token = :token: AND ip = :ip: AND command = \"_ANY\")
OR
(token = :token: AND ip IS NULL AND command = \"_ANY\")
OR
(token IS NULL AND ip IS NULL AND command = :command:)
)"})
//Get the tokens and the associated commands
//Check if the token, the ip and the command matches OR
// the token + command matches and the ip is NULL (commands that can be used by any ip, but require a token)
// the token + ip matches and the command is NULL (Allow a specific ip with a specific token to use all commands)
// the token + ip is NULL and the command matches (Allow a specific command to be used without auth)
permquery.Execute(list("token" = queryparams["auth"], "ip" = queryparams["addr"], "command" = queryparams["command"]))
if (!permquery.RowCount())
statuscode = 401
response = "Unauthorized - To access this command"
return 1
commanddata["name"] = apicommand.name
commanddata["description"] = apicommand.description
commanddata["params"] = apicommand.params
statuscode = 200
response = "Command data retrieved"
data = commanddata
return 1
/datum/topic_command/update_command_database
name = "update_command_database"
description = "Updates the available topic commands in the database"
/datum/topic_command/update_command_database/run_command(queryparams)
api_update_command_database()
statuscode = 200
response = "Database Updated"
return 1
//
// API for the other stuff
//
//Char Names
/datum/topic_command/get_char_list
name = "get_char_list"
description = "Provides a list of all characters ingame"
/datum/topic_command/get_char_list/run_command(queryparams)
var/list/chars = list()
var/list/mobs = sortmobs()
for(var/mob/M in mobs)
if(!M.ckey) continue
chars[M.name] += M.key ? (M.client ? M.key : "[M.key] (DC)") : "No key"
statuscode = 200
response = "Char list fetched"
data = chars
return 1
//Admin Count
/datum/topic_command/get_count_admin
name = "get_count_admin"
description = "Gets the number of admins connected"
/datum/topic_command/get_count_admin/run_command(queryparams)
var/n = 0
for (var/client/client in clients)
if (client.holder && client.holder.rights & (R_ADMIN))
n++
statuscode = 200
response = "Admin count fetched"
data = n
return 1
//CCIA Count
/datum/topic_command/get_count_cciaa
name = "get_count_cciaa"
description = "Gets the number of ccia connected"
/datum/topic_command/get_count_ccia/run_command(queryparams)
var/n = 0
for (var/client/client in clients)
if (client.holder && (client.holder.rights & R_CCIAA) && !(client.holder.rights & R_ADMIN))
n++
statuscode = 200
response = "CCIA count fetched"
data = n
return 1
//Mod Count
/datum/topic_command/get_count_mod
name = "get_count_mod"
description = "Gets the number of mods connected"
/datum/topic_command/get_count_mod/run_command(queryparams)
var/n = 0
for (var/client/client in clients)
if (client.holder && (client.holder.rights & R_MOD) && !(client.holder.rights & R_ADMIN))
n++
statuscode = 200
response = "Mod count fetched"
data = n
return 1
//Player Count
/datum/topic_command/get_count_player
name = "get_count_player"
description = "Gets the number of players connected"
/datum/topic_command/get_count_player/run_command(queryparams)
var/n = 0
for(var/mob/M in player_list)
if(M.client)
n++
statuscode = 200
response = "Player count fetched"
data = n
return 1
//Get available Fax Machines
/datum/topic_command/get_faxmachines
name = "get_faxmachines"
description = "Gets all available fax machines"
/datum/topic_command/get_faxmachines/run_command(queryparams)
var/list/faxlocations = list()
for (var/obj/machinery/photocopier/faxmachine/F in allfaxes)
faxlocations.Add(F.department)
statuscode = 200
response = "Fax machines fetched"
data = faxlocations
return 1
//Get Fax List
/datum/topic_command/get_faxlist
name = "get_faxlist"
description = "Gets the list of faxes sent / received"
params = list(
"faxtype" = list("name"="faxtype","desc"="Type of the faxes that should be retrieved","req"=1,"type"="slct","options"=list("sent","received"))
)
/datum/topic_command/get_faxlist/run_command(queryparams)
var/list/faxes = list()
switch (queryparams["faxtype"])
if ("received")
faxes = arrived_faxes
if ("sent")
faxes = sent_faxes
if (!faxes || !faxes.len)
statuscode = 404
response = "No faxes found"
data = null
return 1
var/list/output = list()
for (var/i = 1, i <= faxes.len, i++)
var/obj/item/a = faxes[i]
output += "[i]"
output[i] = a.name ? a.name : "Untitled Fax"
statuscode = 200
response = "Fetched Fax List"
data = output
return 1
//Get Specific Fax
/datum/topic_command/get_fax
name = "get_fax"
description = "Gets a specific fax that has been sent or received"
params = list(
"faxtype" = list("name"="faxtype","desc"="Type of the faxes that should be retrieved","req"=1,"type"="slct","options"=list("sent","received")),
"faxid" = list("name"="faxid","desc"="ID of the fax that should be retrieved","req"=1,"type"="int")
)
/datum/topic_command/get_fax/run_command(queryparams)
var/list/faxes = list()
switch (queryparams["faxtype"])
if ("received")
faxes = arrived_faxes
if ("sent")
faxes = sent_faxes
if (!faxes || !faxes.len)
statuscode = 500
response = "No faxes found!"
data = null
return 1
var/fax_id = text2num(queryparams["faxid"])
if (fax_id > faxes.len || fax_id < 1)
statuscode = 404
response = "Invalid Fax ID"
data = null
return 1
var/output = list()
if (istype(faxes[fax_id], /obj/item/weapon/paper))
var/obj/item/weapon/paper/a = faxes[fax_id]
output["title"] = a.name ? a.name : "Untitled Fax"
var/content = replacetext(a.info, "<br>", "\n")
content = strip_html_properly(content, 0)
output["content"] = content
statuscode = 200
response = "Fax (Paper) with id [fax_id] retrieved"
data = output
return 1
else if (istype(faxes[fax_id], /obj/item/weapon/photo))
statuscode = 501
response = "Fax is a Photo - Unable to send"
data = null
return 1
else if (istype(faxes[fax_id], /obj/item/weapon/paper_bundle))
var/obj/item/weapon/paper_bundle/b = faxes[fax_id]
output["title"] = b.name ? b.name : "Untitled Paper Bundle"
if (!b.pages || !b.pages.len)
statuscode = 500
response = "Fax Paper Bundle is empty - This should not happen"
data = null
return 1
var/i = 0
for (var/obj/item/weapon/paper/c in b.pages)
i++
var/content = replacetext(c.info, "<br>", "\n")
content = strip_html_properly(content, 0)
output["content"] += "Page [i]:\n[content]\n\n"
statuscode = 200
response = "Fax (PaperBundle) retrieved"
data = output
return 1
statuscode = 500
response = "Unable to recognize the fax type. Cannot send contents!"
data = null
return 1
//Get Ghosts
/datum/topic_command/get_ghosts
name = "get_ghosts"
description = "Gets the ghosts"
/datum/topic_command/get_ghosts/run_command(queryparams)
var/list/ghosts[] = list()
ghosts = get_ghosts(1,1)
statuscode = 200
response = "Fetched Ghost list"
data = ghosts
return 1
// Crew Manifest
/datum/topic_command/get_manifest
name = "get_manifest"
description = "Gets the crew manifest"
/datum/topic_command/get_manifest/run_command(queryparams)
var/list/positions = list()
var/list/set_names = list(
"heads" = command_positions,
"sec" = security_positions,
"eng" = engineering_positions,
"med" = medical_positions,
"sci" = science_positions,
"civ" = civilian_positions,
"bot" = nonhuman_positions
)
for(var/datum/data/record/t in data_core.general)
var/name = t.fields["name"]
var/rank = t.fields["rank"]
var/real_rank = make_list_rank(t.fields["real_rank"])
var/department = 0
for(var/k in set_names)
if(real_rank in set_names[k])
if(!positions[k])
positions[k] = list()
positions[k][name] = rank
department = 1
if(!department)
if(!positions["misc"])
positions["misc"] = list()
positions["misc"][name] = rank
// for(var/k in positions)
// positions[k] = list2params(positions[k]) // converts positions["heads"] = list("Bob"="Captain", "Bill"="CMO") into positions["heads"] = "Bob=Captain&Bill=CMO"
statuscode = 200
response = "Manifest fetched"
data = positions
return 1
//Player Ckeys
/datum/topic_command/get_player_list
name = "get_player_list"
description = "Gets a list of connected players"
params = list(
"showadmins" = list("name"="show admins","desc"="A boolean to toggle whether or not hidden admins should be shown with proper or improper ckeys.","req"=0,"type"="int")
)
/datum/topic_command/get_player_list/run_command(queryparams)
var/show_hidden_admins = 0
if (!isnull(queryparams["showadmins"]))
show_hidden_admins = text2num(queryparams["showadmins"])
var/list/players = list()
for (var/client/C in clients)
if (show_hidden_admins && C.holder && C.holder.fakekey)
players += ckey(C.holder.fakekey)
else
players += C.ckey
statuscode = 200
response = "Player list fetched"
data = players
return 1
//Get info about a specific player
/datum/topic_command/get_player_info
name = "get_player_info"
description = "Gets information about a specific player"
params = list(
"search" = list("name"="search","desc"="List with strings that should be searched for","req"=1,"type"="lst")
)
/datum/topic_command/get_player_info/run_command(queryparams)
var/list/search = queryparams["search"]
var/list/ckeysearch = list()
for(var/text in search)
ckeysearch += ckey(text)
var/list/match = list()
for(var/mob/M in mob_list)
var/strings = list(M.name, M.ckey)
if(M.mind)
strings += M.mind.assigned_role
strings += M.mind.special_role
for(var/text in strings)
if(ckey(text) in ckeysearch)
match[M] += 10 // an exact match is far better than a partial one
else
for(var/searchstr in search)
if(findtext(text, searchstr))
match[M] += 1
var/maxstrength = 0
for(var/mob/M in match)
maxstrength = max(match[M], maxstrength)
for(var/mob/M in match)
if(match[M] < maxstrength)
match -= M
if(!match.len)
statuscode = 449
response = "No match found"
data = null
return 1
else if(match.len == 1)
var/mob/M = match[1]
var/info = list()
info["key"] = M.key
if (M.client)
var/client/C = M.client
info["discordmuted"] = C.mute_discord ? "Yes" : "No"
info["name"] = M.name == M.real_name ? M.name : "[M.name] ([M.real_name])"
info["role"] = M.mind ? (M.mind.assigned_role ? M.mind.assigned_role : "No role") : "No mind"
var/turf/MT = get_turf(M)
info["loc"] = M.loc ? "[M.loc]" : "null"
info["turf"] = MT ? "[MT] @ [MT.x], [MT.y], [MT.z]" : "null"
info["area"] = MT ? "[MT.loc]" : "null"
info["antag"] = M.mind ? (M.mind.special_role ? M.mind.special_role : "Not antag") : "No mind"
info["hasbeenrev"] = M.mind ? M.mind.has_been_rev : "No mind"
info["stat"] = M.stat
info["type"] = M.type
if(isliving(M))
var/mob/living/L = M
info["damage"] = list2params(list(
oxy = L.getOxyLoss(),
tox = L.getToxLoss(),
fire = L.getFireLoss(),
brute = L.getBruteLoss(),
clone = L.getCloneLoss(),
brain = L.getBrainLoss()
))
else
info["damage"] = "non-living"
info["gender"] = M.gender
statuscode = 200
response = "Client data fetched"
data = info
return 1
else
statuscode = 449
response = "Multiple Matches found"
data = null
return 1
//Get Server Status
/datum/topic_command/get_serverstatus
name = "get_serverstatus"
description = "Gets the serverstatus"
/datum/topic_command/get_serverstatus/run_command(queryparams)
var/list/s[] = list()
s["version"] = game_version
s["mode"] = master_mode
s["respawn"] = config.abandon_allowed
s["enter"] = config.enter_allowed
s["vote"] = config.allow_vote_mode
s["ai"] = config.allow_ai
s["host"] = host ? host : null
s["players"] = 0
s["stationtime"] = worldtime2text()
s["roundduration"] = round_duration()
s["gameid"] = game_id
if(queryparams["status"] == "2")
var/list/players = list()
var/list/admins = list()
for(var/client/C in clients)
if(C.holder)
if(C.holder.fakekey)
continue
admins[C.key] = C.holder.rank
players += C.key
s["players"] = players.len
s["playerlist"] = players
s["admins"] = admins.len
s["adminlist"] = admins
else
var/n = 0
var/admins = 0
for(var/client/C in clients)
if(C.holder)
if(C.holder.fakekey)
continue //so stealthmins aren't revealed by the hub
admins++
s["player[n]"] = C.key
n++
s["players"] = n
s["admins"] = admins
statuscode = 200
response = "Server Status fetched"
data = s
return 1
//Get a Staff List
/datum/topic_command/get_stafflist
name = "get_stafflist"
description = "Gets a list of connected staffmembers"
/datum/topic_command/get_stafflist/run_command(queryparams)
var/list/staff = list()
for (var/client/C in admins)
staff[C] = C.holder.rank
statuscode = 200
response = "Staff list fetched"
data = staff
return 1
//Grant Respawn
/datum/topic_command/grant_respawn
name = "grant_respawn"
description = "Grants a respawn to a specific target"
params = list(
"senderkey" = list("name"="senderkey","desc"="Unique id of the person that authorized the respawn","req"=1,"type"="senderkey"),
"target" = list("name"="target","desc"="Ckey of the target that should be granted a respawn","req"=1,"type"="str")
)
/datum/topic_command/grant_respawn/run_command(queryparams)
var/list/ghosts = get_ghosts(1,1)
var/target = queryparams["target"]
var/allow_antaghud = queryparams["allow_antaghud"]
var/senderkey = queryparams["senderkey"] //Identifier of the sender (Ckey / Userid / ...)
var/mob/dead/observer/G = ghosts[target]
if(!G in ghosts)
statuscode = 404
response = "Target not in ghosts list"
data = null
return 1
if(G.has_enabled_antagHUD && config.antag_hud_restricted && allow_antaghud == 0)
statuscode = 409
response = "Ghost has used Antag Hud - Respawn Aborted"
data = null
return 1
G.timeofdeath=-19999 /* time of death is checked in /mob/verb/abandon_mob() which is the Respawn verb.
timeofdeath is used for bodies on autopsy but since we're messing with a ghost I'm pretty sure
there won't be an autopsy.
*/
var/datum/preferences/P
if (G.client)
P = G.client.prefs
else if (G.ckey)
P = preferences_datums[G.ckey]
else
statuscode = 500
response = "Something went wrong, couldn't find the target's preferences datum"
data = null
return 1
for (var/entry in P.time_of_death)//Set all the prefs' times of death to a huge negative value so any respawn timers will be fine
P.time_of_death[entry] = -99999
G.has_enabled_antagHUD = 2
G.can_reenter_corpse = 1
G:show_message(text("<span class='notice'><B>You may now respawn. You should roleplay as if you learned nothing about the round during your time with the dead.</B></span>"), 1)
log_admin("[senderkey] allowed [key_name(G)] to bypass the 30 minute respawn limit via the API",ckey=key_name(G),admin_key=senderkey)
message_admins("Admin [senderkey] allowed [key_name_admin(G)] to bypass the 30 minute respawn limit via the API", 1)
statuscode = 200
response = "Respawn Granted"
data = null
return 1
//Ping Test
/datum/topic_command/ping
name = "ping"
description = "API test command"
/datum/topic_command/ping/run_command(queryparams)
var/x = 1
for (var/client/C)
x++
statuscode = 200
response = "Pong"
data = x
return 1
//Restart Round
/datum/topic_command/restart_round
name = "restart_round"
description = "Restarts the round"
params = list(
"senderkey" = list("name"="senderkey","desc"="Unique id of the person that authorized the restart","req"=1,"type"="senderkey")
)
/datum/topic_command/restart_round/run_command(queryparams)
var/senderkey = sanitize(queryparams["senderkey"]) //Identifier of the sender (Ckey / Userid / ...)
world << "<font size=4 color='#ff2222'>Server restarting by remote command.</font>"
log_and_message_admins("World restart initiated remotely by [senderkey].")
feedback_set_details("end_error","remote restart")
spawn(50)
log_game("Rebooting due to remote command.")
world.Reboot(10)
statuscode = 200
response = "Restart Command accepted"
data = null
return 1
//Get available Fax Machines
/datum/topic_command/send_adminmsg
name = "send_adminmsg"
description = "Sends a adminmessage to a player"
params = list(
"ckey" = list("name"="ckey","desc"="The target of the adminmessage","req"=1,"type"="str"),
"msg" = list("name"="msg","desc"="The message that should be sent","req"=1,"type"="str"),
"senderkey" = list("name"="senderkey","desc"="Unique id of the person that sent the adminmessage","req"=1,"type"="senderkey"),
"rank" = list("name"="rank","desc"="The rank that should be displayed - Defaults to admin if none specified","req"=0,"type"="str")
)
/datum/topic_command/send_adminmsg/run_command(queryparams)
/*
We got an adminmsg from IRC bot lets split the API
expected output:
1. ckey = ckey of person the message is to
2. msg = contents of message, parems2list requires
3. rank = Rank that should be displayed
4. senderkey = the ircnick that send the message.
*/
var/client/C
var/req_ckey = ckey(queryparams["ckey"])
for(var/client/K in clients)
if(K.ckey == req_ckey)
C = K
break
if(!C)
statuscode = 404
response = "No client with that name on server"
data = null
return 1
var/rank = queryparams["rank"]
if(!rank)
rank = "Admin"
var/message = "<font color='red'>[rank] PM from <b><a href='?discord_msg=[queryparams["senderkey"]]'>[queryparams["senderkey"]]</a></b>: [queryparams["msg"]]</font>"
var/amessage = "<font color='blue'>[rank] PM from <a href='?discord_msg=[queryparams["senderkey"]]'>[queryparams["senderkey"]]</a> to <b>[key_name(C, highlight_special = 1)]</b> : [queryparams["msg"]]</font>"
C.received_discord_pm = world.time
C.discord_admin = queryparams["senderkey"]
C << 'sound/effects/adminhelp.ogg'
C << message
for(var/client/A in admins)
if(A != C)
A << amessage
statuscode = 200
response = "Admin Message sent"
data = null
return 1
//Send a Command Report
/datum/topic_command/send_commandreport
name = "send_commandreport"
description = "Sends a command report"
params = list(
"senderkey" = list("name"="senderkey","desc"="Unique id of the person that sent the commandreport","req"=1,"type"="senderkey"),
"title" = list("name"="title","desc"="The message title that should be sent, Defaults to NanoTrasen Update if not specified","req"=0,"type"="str"),
"body" = list("name"="body","desc"="The message body that should be sent","req"=1,"type"="str"),
"type" = list("name"="type","desc"="The type of the message that should be sent, Defaults to freeform","req"=0,"type"="slct","options"=list("freeform","ccia")),
"sendername" = list("name"="sendername","desc"="IC Name of the sender for the CCIA Report, Defaults to CCIAAMS, \[Command-StationName\]","req"=0,"type"="string"),
"announce" = list("name"="announce","desc"="If the report should be announce 1 -> Yes, 0 -> No, Defaults to 1","req"=0,"type"="int")
)
/datum/topic_command/send_commandreport/run_command(queryparams)
var/senderkey = sanitize(queryparams["senderkey"]) //Identifier of the sender (Ckey / Userid / ...)
var/reporttitle = sanitizeSafe(queryparams["title"]) //Title of the report
var/reportbody = nl2br(sanitize(queryparams["body"],encode=0,extra=0,max_length=0)) //Body of the report
var/reporttype = queryparams["type"] //Type of the report: freeform / ccia / admin
var/reportsender = sanitizeSafe(queryparams["sendername"]) //Name of the sender
var/reportannounce = text2num(queryparams["announce"]) //Announce the contents report to the public: 1 / 0
if(!reporttitle)
reporttitle = "NanoTrasen Update"
if(!reporttype)
reporttype = "freeform"
if(!reportannounce)
reportannounce = 1
//Set the report footer for CCIA Announcements
if (reporttype == "ccia")
if (reportsender)
reportbody += "<br><br>- [reportsender], Central Command Internal Affairs Agent, [commstation_name()]"
else
reportbody += "<br><br>- CCIAAMS, [commstation_name()]"
//Send the message to the communications consoles
post_comm_message(reporttitle, reportbody)
if(reportannounce == 1)
command_announcement.Announce(reportbody, reporttitle, new_sound = 'sound/AI/commandreport.ogg', do_newscast = 1, msg_sanitized = 1);
if(reportannounce == 0)
world << "<span class='alert'>New NanoTrasen Update available at all communication consoles.</span>"
world << sound('sound/AI/commandreport.ogg')
log_admin("[senderkey] has created a command report via the api: [reportbody]",admin_key=senderkey)
message_admins("[senderkey] has created a command report via the api", 1)
statuscode = 200
response = "Command Report sent"
data = null
return 1
//Send Fax
/datum/topic_command/send_fax
name = "send_fax"
description = "Sends a fax"
params = list(
"senderkey" = list("name"="senderkey","desc"="Unique id of the person that sent the fax","req"=1,"type"="senderkey"),
"title" = list("name"="title","desc"="The message title that should be sent","req"=1,"type"="str"),
"body" = list("name"="body","desc"="The message body that should be sent","req"=1,"type"="str"),
"target" = list("name"="target","desc"="The target faxmachines the fax should be sent to","req"=1,"type"="lst")
)
/datum/topic_command/send_fax/run_command(queryparams)
var/list/responselist = list()
var/list/sendsuccess = list()
var/list/targetlist = queryparams["target"] //Target locations where the fax should be sent to
var/senderkey = sanitize(queryparams["senderkey"]) //Identifier of the sender (Ckey / Userid / ...)
var/faxtitle = sanitizeSafe(queryparams["title"]) //Title of the report
var/faxbody = sanitize(queryparams["body"],max_length=0) //Body of the report
var/faxannounce = text2num(queryparams["announce"]) //Announce the contents report to the public: 1 / 0
if(!targetlist || targetlist.len < 1)
statuscode = 400
response = "Parameter target not set"
data = null
return 1
var/sendresult = 0
//Send the fax
for (var/obj/machinery/photocopier/faxmachine/F in allfaxes)
if (F.department in targetlist)
sendresult = send_fax(F, faxtitle, faxbody, senderkey)
if (sendresult == 1)
sendsuccess.Add(F.department)
responselist[F.department] = "success"
else
responselist[F.department] = "failed"
//Announce that the fax has been sent
if(faxannounce == 1)
if(sendsuccess.len < 1)
command_announcement.Announce("A fax message from Central Command could not be delivered because all of the following fax machines are inoperational: <br>"+list2text(targetlist, ", "), "Fax Received", new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1);
else
command_announcement.Announce("A fax message from Central Command has been sent to the following fax machines: <br>"+list2text(sendsuccess, ", "), "Fax Received", new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1);
log_admin("[senderkey] sent a fax via the API: : [faxbody]",admin_key=senderkey)
message_admins("[senderkey] sent a fax via the API", 1)
statuscode = 200
response = "Fax sent"
data = responselist
return 1
/datum/topic_command/send_fax/proc/send_fax(var/obj/machinery/photocopier/faxmachine/F, title, body, senderkey)
// Create the reply message
var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( null ) //hopefully the null loc won't cause trouble for us
P.name = "[current_map.boss_name] - [title]"
P.info = body
P.update_icon()
// Stamps
var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
stampoverlay.icon_state = "paper_stamp-cent"
if(!P.stamped)
P.stamped = new
P.stamped += /obj/item/weapon/stamp
P.overlays += stampoverlay
P.stamps += "<HR><i>This paper has been stamped by the Central Command Quantum Relay.</i>"
if(F.recievefax(P))
log_and_message_admins("[senderkey] sent a fax message to the [F.department] fax machine via the api. (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[F.x];Y=[F.y];Z=[F.z]'>JMP</a>)")
sent_faxes += P
return 1
else
qdel(P)
return 2
// Update discord_bot's channels.
/datum/topic_command/update_bot_channels
name = "update_bot_channels"
description = "Tells the ingame instance of the Discord bot to update its cached channels list."
/datum/topic_command/update_bot_channels/run_command()
data = null
if (!discord_bot)
statuscode = 404
response = "Ingame Discord bot not initialized."
return 1
switch (discord_bot.update_channels())
if (1)
statuscode = 404
response = "Ingame Discord bot is not active."
if (2)
statuscode = 500
response = "Ingame Discord bot encountered error attempting to access database."
else
statuscode = 200
response = "Ingame Discord bot's channels were successfully updated."
return 1