diff --git a/ByondPOST.dll b/ByondPOST.dll
new file mode 100644
index 00000000000..b33f70b1ec8
Binary files /dev/null and b/ByondPOST.dll differ
diff --git a/baystation12.dme b/baystation12.dme
index e46880cadf2..f1a7f10e973 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -130,6 +130,7 @@
#include "code\datums\crew.dm"
#include "code\datums\datacore.dm"
#include "code\datums\datumvars.dm"
+#include "code\datums\discord_bot.dm"
#include "code\datums\disease.dm"
#include "code\datums\mind.dm"
#include "code\datums\mixed.dm"
@@ -1056,7 +1057,6 @@
#include "code\modules\examine\descriptions\structures.dm"
#include "code\modules\examine\descriptions\turfs.dm"
#include "code\modules\examine\descriptions\weapons.dm"
-#include "code\modules\ext_scripts\discord.dm"
#include "code\modules\ext_scripts\python.dm"
#include "code\modules\flufftext\Dreaming.dm"
#include "code\modules\flufftext\Hallucination.dm"
@@ -1067,6 +1067,7 @@
#include "code\modules\holodeck\HolodeckControl.dm"
#include "code\modules\holodeck\HolodeckObjects.dm"
#include "code\modules\holodeck\HolodeckPrograms.dm"
+#include "code\modules\http\post_request.dm"
#include "code\modules\hydroponics\_hydro_setup.dm"
#include "code\modules\hydroponics\grown.dm"
#include "code\modules\hydroponics\grown_inedible.dm"
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 99cdc085998..944b29fcb3e 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -807,6 +807,22 @@ var/list/gamemode_cache = list()
age_restrictions += name
age_restrictions[name] = text2num(value)
+ else if (type == "discord")
+ // Ideally, this would never happen. But just in case.
+ if (!discord_bot)
+ log_debug("BOREALIS: Attempted to read config/discord.txt before initializing the bot.")
+ return
+
+ switch (name)
+ if ("token")
+ discord_bot.auth_token = value
+ if ("active")
+ discord_bot.active = 1
+ if ("robust_debug")
+ discord_bot.robust_debug = 1
+ else
+ log_misc("Unknown setting in discord configuration: '[name]'")
+
/datum/configuration/proc/pick_mode(mode_name)
// I wish I didn't have to instance the game modes in order to look up
// their information, but it is the only way (at least that I know of).
diff --git a/code/datums/api.dm b/code/datums/api.dm
index 3a73955cadb..c3cf798d94d 100644
--- a/code/datums/api.dm
+++ b/code/datums/api.dm
@@ -632,6 +632,7 @@ proc/api_update_command_database()
s["players"] = 0
s["stationtime"] = worldtime2text()
s["roundduration"] = round_duration()
+ s["gameid"] = game_id
if(queryparams["status"] == "2")
var/list/players = list()
@@ -645,9 +646,9 @@ proc/api_update_command_database()
players += C.key
s["players"] = players.len
- s["playerlist"] = list2params(players)
+ s["playerlist"] = players
s["admins"] = admins.len
- s["adminlist"] = list2params(admins)
+ s["adminlist"] = admins
else
var/n = 0
var/admins = 0
@@ -974,3 +975,29 @@ proc/api_update_command_database()
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
diff --git a/code/datums/discord_bot.dm b/code/datums/discord_bot.dm
new file mode 100644
index 00000000000..1238e3e93e4
--- /dev/null
+++ b/code/datums/discord_bot.dm
@@ -0,0 +1,155 @@
+#define CHAN_ADMIN "channel_admin"
+#define CHAN_CCIAA "channel_cciaa"
+#define CHAN_ANNOUNCE "channel_announce"
+
+var/datum/discord_bot/discord_bot = null
+
+/hook/startup/proc/initialize_discord_bot()
+ if (discord_bot)
+ // This shouldn't be possible, but sure!
+ return 0
+
+ discord_bot = new()
+
+ config.load("config/discord.txt", "discord")
+
+ discord_bot.update_channels()
+
+ return 1
+
+/datum/discord_bot
+ var/list/channels = list()
+
+ var/active = 0
+ var/auth_token = ""
+
+ var/robust_debug = 0
+
+ // Lazy man's rate limiting vars
+ var/rate_limited_since = 0
+ var/queue_being_pushed = 0
+ var/list/queue = list()
+
+/datum/discord_bot/proc/update_channels()
+ if (!active)
+ return 1
+
+ if (!establish_db_connection(dbcon))
+ log_debug("BOREALIS: Failed to update channels due to missing database.")
+ return 2
+
+ channels = list()
+
+ var/DBQuery/channel_query = dbcon.NewQuery("SELECT channel_group, channel_id FROM discord_channels")
+ channel_query.Execute()
+
+ var/list/A
+ while (channel_query.NextRow())
+ if (isnull(channels[channel_query.item[1]]))
+ channels[channel_query.item[1]] = list()
+
+ A = channels[channel_query.item[1]]
+ A += channel_query.item[2]
+
+ log_debug("BOREALIS: Channels updated successfully.")
+ return 0
+
+/datum/discord_bot/proc/send_message(var/channel_group, var/message)
+ if (!active || !auth_token)
+ return
+
+ if (!channel_group || !channels.len || isnull(channels[channel_group]))
+ return
+
+ if (!message)
+ return
+
+ if (length(message) > 2000)
+ message = copytext(message, 1, 2001)
+
+ // Let's run it through the proper JSON encoder, just in case of special characters.
+ message = json_encode(list("content" = message))
+
+ var/list/A = channels[channel_group]
+ var/list/sent = list()
+ for (var/channel in A)
+ if (send_post_request("https://discordapp.com/api/channels/[channel]/messages", message, "Authorization: Bot [auth_token]", "Content-Type: application/json") == 429)
+ // Whoopsies, rate limited.
+ // Set up the queue.
+ rate_limited_since = world.time
+ queue.Add(list(message, A - sent))
+
+ // Schedule a push.
+ spawn (100)
+ push_queue()
+
+ // And exit.
+ return
+ else
+ sent += channel
+
+ if (robust_debug)
+ log_debug("BOEALIS: Message sent to [channel_group]. JSON body: '[message]'")
+
+/datum/discord_bot/proc/send_to_admins(message)
+ send_message(CHAN_ADMIN, message)
+
+/datum/discord_bot/proc/send_to_cciaa(message)
+ send_message(CHAN_CCIAA, message)
+
+/datum/discord_bot/proc/send_to_announce(message)
+ send_message(CHAN_ANNOUNCE, message)
+
+/datum/discord_bot/proc/push_queue()
+ // What facking queue.
+ if (!queue.len)
+ if (robust_debug)
+ log_debug("BOREALIS: Attempted to push a null length queue.")
+ if (queue_being_pushed)
+ queue_being_pushed = 0
+ return
+
+ if (queue_being_pushed)
+ if (robust_debug)
+ log_debug("BOREALIS: Attempted to initialize a second queue driver.")
+ return
+
+ if ((world.time - rate_limited_since) < 100)
+ // Something broke the limit again. Ideally, this wouldn't happen. But sure.
+ // Use a longer timeout, just in case.
+ spawn (200)
+ push_queue()
+
+ queue_being_pushed = 0
+ return
+
+ // Async process lock var. No touchy.
+ queue_being_pushed = 1
+
+ // A[1] - message body.
+ // A[2] - list of channels to send to.
+ var/message
+ var/list/destinations
+ for (var/list/A in queue)
+ message = A[1]
+ destinations = A[2]
+
+ for (var/channel in destinations)
+ if (send_post_request("https://discordapp.com/api/channels/[channel]/messages", message, "Authorization: Bot [auth_token]", "Content-Type: application/json") == 429)
+ // Limited again. Reschedule.
+ rate_limited_since = world.time
+ spawn (100)
+ push_queue()
+
+ queue_being_pushed = 0
+ return
+ else
+ destinations.Remove(channel)
+
+ queue.Remove(A)
+
+ queue_being_pushed = 0
+
+#undef CHAN_ADMIN
+#undef CHAN_CCIAA
+#undef CHAN_ANNOUNCE
diff --git a/code/game/antagonist/antagonist_print.dm b/code/game/antagonist/antagonist_print.dm
index 31a5b96b2ce..756071003be 100644
--- a/code/game/antagonist/antagonist_print.dm
+++ b/code/game/antagonist/antagonist_print.dm
@@ -83,4 +83,29 @@
if(purchases)
text += "
[purchases]"
- return text
\ No newline at end of file
+ return text
+
+/datum/antagonist/proc/print_player_summary_discord()
+ if (current_antagonists.len)
+ return ""
+
+ var/text = "[current_antagonists.len > 1 ? "The [lowertext(role_text_plural)] were:\n" : "The [lowertext(role_text)] was:\n"]"
+ for (var/datum/mind/ply in current_antagonists)
+ var/role = ply.assigned_role ? "\improper[ply.assigned_role]" : "\improper[ply.special_role]: "
+ text += "**[ply.name]** (**[ply.key]**) as \a **[role]** ("
+ if(ply.current)
+ if(ply.current.stat == DEAD)
+ text += "died"
+ else if(isNotStationLevel(ply.current.z))
+ text += "fled the station"
+ else
+ text += "survived"
+ if(ply.current.real_name != ply.name)
+ text += " as **[ply.current.real_name]**"
+ else
+ text += "body destroyed"
+ text += ")\n"
+
+ text += "\n"
+
+ return text
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 523157df59b..c226d64f2cf 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -369,14 +369,22 @@ var/global/list/additional_antag_types = list()
/datum/game_mode/proc/declare_completion()
var/is_antag_mode = (antag_templates && antag_templates.len)
+ var/discord_text = "A round of **[name]** has ended! \[Game ID: [game_id]\]\n\n"
check_victory()
if(is_antag_mode)
sleep(10)
- for(var/datum/antagonist/antag in antag_templates)
+ for (var/datum/antagonist/antag in antag_templates)
sleep(10)
antag.check_victory()
antag.print_player_summary()
+ // Avoid the longest loop if we aren't actively using the bot.
+ if (discord_bot.active)
+ discord_text += antag.print_player_summary_discord()
+
+ discord_bot.send_to_announce(discord_text)
+ discord_text = ""
+
var/clients = 0
var/surviving_humans = 0
var/surviving_total = 0
@@ -423,10 +431,17 @@ var/global/list/additional_antag_types = list()
if(surviving_total > 0)
text += "
There [surviving_total>1 ? "were [surviving_total] survivors" : "was one survivor"]"
text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and [ghosts] ghosts.
"
+
+ discord_text += "There [surviving_total>1 ? "were **[surviving_total] survivors**" : "was **one survivor**"]"
+ discord_text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and **[ghosts] ghosts**."
else
text += "There were no survivors ([ghosts] ghosts)."
+
+ discord_text += "There were **no survivors** ([ghosts] ghosts)."
world << text
+ discord_bot.send_to_announce(discord_text)
+
if(clients > 0)
feedback_set("round_end_clients",clients)
if(ghosts > 0)
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index ce99b333e30..fe16c4251ee 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -147,7 +147,7 @@ var/global/datum/controller/gameticker/ticker
if(C.holder && (C.holder.rights & (R_MOD|R_ADMIN)))
admins_number++
if(admins_number == 0)
- send_to_admin_discord("@everyone Round has started with no admins online.")
+ discord_bot.send_to_admins("@here Round has started with no admins online.")
/* supply_controller.process() //Start the supply shuttle regenerating points -- TLE // handled in scheduler
master_controller.process() //Start master_controller.process()
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 94fc6a7c496..4d85f3b0a75 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -99,10 +99,12 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","
msg = "\blue Request for Help:: [get_options_bar(mob, 3, 1, 1)][ai_cl]: [msg]"
+ var/admin_number_present = 0
var/admin_number_afk = 0
for(var/client/X in admins)
if((R_ADMIN|R_MOD) & X.holder.rights)
+ admin_number_present++
if(X.is_afk())
admin_number_afk++
if(X.prefs.toggles & SOUND_ADMINHELP)
@@ -113,9 +115,9 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","
//show it to the person adminhelping too
src << "PM to-Staff : [original_msg]"
- var/admin_number_present = admins.len - admin_number_afk
+ var/admin_number_active = admin_number_present - admin_number_afk
log_admin("HELP: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.")
- if(admin_number_present <= 0)
- send_to_admin_discord("@everyone Request for Help from [key_name(src)]: [html_decode(original_msg)] - !![admin_number_afk ? "All admins AFK ([admin_number_afk])" : "No admins online"]!!")
+ if(admin_number_active <= 0)
+ discord_bot.send_to_admins("@everyone Request for Help from [key_name(src)]: [html_decode(original_msg)] - !![admin_number_afk ? "All admins AFK ([admin_number_afk])" : "No admins online"]!!")
feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index a75aacd7999..24635707c92 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -129,7 +129,7 @@
sanitize(msg)
- send_to_admin_discord("PlayerPM to [sender] from [key_name(src)]: [html_decode(msg)]")
+ discord_bot.send_to_admins("PlayerPM to [sender] from [key_name(src)]: [html_decode(msg)]")
src << "" + create_text_tag("pm_out_alt", "", src) + " to Discord-[sender]: [msg]"
diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm
index 4ed1684a213..74fe4bc0457 100644
--- a/code/modules/admin/verbs/modifyvariables.dm
+++ b/code/modules/admin/verbs/modifyvariables.dm
@@ -1,7 +1,8 @@
var/list/forbidden_varedit_object_types = list(
/datum/admins, //Admins editing their own admin-power object? Yup, sounds like a good idea.
/obj/machinery/blackbox_recorder, //Prevents people messing with feedback gathering
- /datum/feedback_variable //Prevents people messing with feedback gathering
+ /datum/feedback_variable, //Prevents people messing with feedback gathering
+ /datum/discord_bot //Nope.jpg. Stop it.
)
/*
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index 04c1c02ccd4..05485f11bce 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -29,7 +29,7 @@
//log_admin("HELP: [key_name(src)]: [msg]")
/proc/Centcomm_announce(var/msg, var/mob/Sender, var/iamessage)
- send_to_cciaa_discord("!!! @everyone - Emergency message from the station: `[msg]`, sent by [Sender] !!!")
+ discord_bot.send_to_cciaa("@here - Emergency message from the station: `[msg]`, sent by [Sender]!")
var/msg_cciaa = "\blue CENTCOMM[iamessage ? " IA" : ""]:[key_name(Sender, 1)] (RPLY): [msg]"
msg = "\blue CENTCOMM[iamessage ? " IA" : ""]:[key_name(Sender, 1)] (PP) (VV) (SM) (JMP) (CA) (BSA) (RPLY): [msg]"
diff --git a/code/modules/http/post_request.dm b/code/modules/http/post_request.dm
new file mode 100644
index 00000000000..ffc04a1d872
--- /dev/null
+++ b/code/modules/http/post_request.dm
@@ -0,0 +1,80 @@
+/*
+
+ @===================================@
+ | |
+ | Guide to HTTP Post requests |
+ | |
+ @===================================@
+
+ Making POST requests in byond is SUPER easy with the post request DLL.
+
+ Simply use the call() function to call the post request DLL and enter your details!
+
+ The first bit of code needed will always be the same, You will never have to touch this, Copy-pasta all you like:
+
+ call("ByondPOST.dll", "send_post_request")
+
+ Well thats the first part, the harder part is to input the details into DLL. Lets do that now! The syntax to add arguments to the post request is:
+
+ call("ByondPOST.dll", "send_post_request")(PostURL, PostContent, Header)
+
+ In a lot of cases you might need more then one custom header to make a valid POST request, One such case is when you want to use the Discord API,
+ the discord API requires 2 custom headers, one to tell the server that you are making a JSON post request,
+ the header for this is "Content-Type: application/json" And another to tell the Discord API your login token,
+ Which looks like this "Authorization: YourLoginToken"
+ This can be achieved in Byond with the following code
+
+ call("ByondPOST.dll", "send_post_request")("http://example.com", somebodyhere, "Content-Type: application/json", "Authorization: YourTokenHere")
+
+ As you can see we have added some more arguments onto the proc, You can add as many arguments you like to the proc, Any argument after the PostContent
+ argument is considered a header.
+
+ Some example POST requests:
+
+ <-- Send Discord Message -->
+ call("ByondPOST.dll", "send_post_request")("https://discordapp.com/api/channels/134720091576205312/messages", " { \"content\" : \"Hello World!\" } ", "Content-Type: application/json", "Authorization: DAsDAs4!"�DFdW45%fAsFSa^$!"�$Xfdsfh523ds")
+
+ DLL Written by Oisin100 and modified by Skull132
+*/
+
+/*
+ * A generic proc for sending a post request with the aforementioned .DLL files.
+ * Expected arg structure:
+ * 1st arg - the url
+ * 2nd arg - the request body
+ * 3rd - nth arg - individual headers and their values in format: "headername: value"
+ *
+ * @return int - Error code from one of three possible sources!
+ * -1 indicates proc or library failure.
+ * 0 - 92 are curl errors, and are usually accompanied by a HTTP response code of 0 (request was never made).
+ * 100 - 6xx are HTTP response codes. Curl error code should be 0 in this case, but, in case that it is not,
+ * the HTTP response code is always returned as long as it is not 0.
+ *
+ */
+/proc/send_post_request()
+ if (args.len < 2)
+ return -1
+
+ var/result = call("ByondPOST.dll", "send_post_request")(arglist(args))
+
+ if (!result)
+ log_debug("ByondPOST: No result returned from external library.")
+ return -1
+
+ var/list/A = params2list(result)
+
+ if (!isnull(A["proc"]))
+ // Log the proc error. It should be reviewed by coders ASAP.
+ switch (A["proc"])
+ if ("1")
+ log_debug("ByondPOST: Proc error: Too few arguments sent to function.")
+ if ("2")
+ log_debug("ByondPOST: Proc error: Unable to initialize curl object.")
+ else
+ log_debug("ByondPOST: Proc error: Unknown error.")
+ return -1
+
+ // Curl oriented errors should leave the HTTP response code at 0, as no request was executed.
+ // All HTTP oriented errors will definately return a response code other than 0, so prioritize that.
+ // Fallback is a curl error code (0 - 92).
+ return text2num(A["http"]) != 0 ? text2num(A["http"]) : text2num(A["curl"])
diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm
index 72f484f36c4..71ccf5a52b7 100644
--- a/code/modules/mob/logout.dm
+++ b/code/modules/mob/logout.dm
@@ -4,11 +4,20 @@
log_access("Logout: [key_name(src)]")
if(admin_datums[src.ckey])
if (ticker && ticker.current_state == GAME_STATE_PLAYING) //Only report this stuff if we are currently playing.
- var/admins_number = admins.len
+ var/admins_number = 0
+ var/admins_number_afk = 0
+ for (var/client/C)
+ if (C.holder && (C.holder.rights & (R_MOD|R_ADMIN)))
+ admins_number++
+ if (C.is_afk())
+ admins_number_afk++
message_admins("Admin logout: [key_name(src)]")
- if(admins_number == 0) //Apparently the admin logging out is no longer an admin at this point, so we have to check this towards 0 and not towards 1. Awell.
- send_to_admin_discord("@everyone [key_name(src)] logged out - no more admins online.")
+
+ if (admins_number == 0) //Apparently the admin logging out is no longer an admin at this point, so we have to check this towards 0 and not towards 1. Awell.
+ discord_bot.send_to_admins("@here [key_name(src)] logged out - no more admins online.")
+ else if ((admins_number - admins_number_afk) <= 0)
+ discord_bot.send_to_admins("[key_name(src)] logged out - only AFK admins ([admins_number_afk]) are online.")
..()
return 1
diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm
index 326ae34dac4..75f7dcf85fd 100644
--- a/code/modules/paperwork/faxmachine.dm
+++ b/code/modules/paperwork/faxmachine.dm
@@ -252,7 +252,7 @@ var/list/sent_faxes = list() //cache for faxes that have been sent by the admins
if((R_ADMIN|R_CCIAA) & C.holder.rights)
C << msg
- send_to_cciaa_discord("New fax arrived! [faxname]: \"[sent.name]\" by [sender].")
+ discord_bot.send_to_cciaa("New fax arrived! [faxname]: \"[sent.name]\" by [sender].")
/obj/machinery/photocopier/faxmachine/proc/do_pda_alerts()
if (!alert_pdas || !alert_pdas.len)
diff --git a/config/example/discord.txt b/config/example/discord.txt
new file mode 100644
index 00000000000..c7725c4f755
--- /dev/null
+++ b/config/example/discord.txt
@@ -0,0 +1,9 @@
+## Uncomment this to activate the DM controlled Discord bot.
+# ACTIVE
+
+## Paste the bot's OAuth2 token as a value to this setting. Mind the caps!
+# TOKEN
+
+## Uncomment this to enable robust debugging.
+## This results in more messages being sent via log_debug() during bot operations.
+# ROBUST_DEBUG
diff --git a/html/changelogs/skull132-BOREALISII.yml b/html/changelogs/skull132-BOREALISII.yml
new file mode 100644
index 00000000000..5ce40c0bd82
--- /dev/null
+++ b/html/changelogs/skull132-BOREALISII.yml
@@ -0,0 +1,6 @@
+author: Skull132
+
+delete-after: True
+
+changes:
+ - rscadd: "Implemented BOREALIS II into the game. Updates and other information from the game will now be transmitted to both the public and private Discords, as necessary."
diff --git a/lib/ByondPostDLL/ByondPostDLL.cpp b/lib/ByondPostDLL/ByondPostDLL.cpp
new file mode 100644
index 00000000000..9a79d934529
--- /dev/null
+++ b/lib/ByondPostDLL/ByondPostDLL.cpp
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 2016 Oisin Carr & Skull132
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include
+
+extern "C" __declspec(dllexport) char *send_post_request(int argc, char *argv[])
+{
+ if (argc < 2)
+ {
+ return "proc=1";
+ }
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+ CURL *curl = curl_easy_init();
+
+ if (!curl)
+ {
+ return "proc=2";
+ }
+
+ // Initialize variables.
+ static char return_value[32];
+ long http_code = 0;
+ CURLcode res;
+ struct curl_slist *chunk = NULL;
+
+ for (int i = 2; i < argc; i++)
+ {
+ chunk = curl_slist_append(chunk, argv[i]);
+ }
+
+ // Set curl options.
+ curl_easy_setopt(curl, CURLOPT_URL, argv[0]);
+
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+
+ char *data = argv[1];
+
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
+
+ // Save the response.
+ res = curl_easy_perform(curl);
+
+ // Get the response code and save it to http_code.
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+
+ // Clean up the session info.
+ curl_global_cleanup();
+
+ // Create the feedback message.
+ // Format used is key=value&key=value.
+ snprintf(return_value, 32, "http=%d&curl=%d", http_code, res);
+
+ return return_value;
+}
diff --git a/libcurl.dll b/libcurl.dll
new file mode 100644
index 00000000000..1da2bd26768
Binary files /dev/null and b/libcurl.dll differ