mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-02-07 13:27:13 +01:00
4f70f89b78
Foundation for the discord library bindings. To be gradually exposed to the script.
326 lines
12 KiB
C++
326 lines
12 KiB
C++
/************************************************************************************
|
|
*
|
|
* D++, A Lightweight C++ library for Discord
|
|
*
|
|
* Copyright 2021 Craig Edwards and D++ contributors
|
|
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
************************************************************************************/
|
|
#define _XOPEN_SOURCE
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <dpp/discordclient.h>
|
|
#include <dpp/discord.h>
|
|
#include <dpp/event.h>
|
|
#include <dpp/cache.h>
|
|
#include <dpp/stringops.h>
|
|
#include <dpp/nlohmann/json.hpp>
|
|
#include <fmt/format.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <time.h>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
|
|
char* strptime(const char* s, const char* f, struct tm* tm) {
|
|
std::istringstream input(s);
|
|
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
|
|
input >> std::get_time(tm, f);
|
|
if (input.fail()) {
|
|
return const_cast< char* >("");
|
|
}
|
|
return (char*)(s + input.tellg());
|
|
}
|
|
#endif
|
|
|
|
namespace dpp {
|
|
|
|
uint64_t SnowflakeNotNull(const json* j, const char *keyname) {
|
|
/* Snowflakes are a special case. Pun intended.
|
|
* Because discord drinks the javascript kool-aid, they have to send 64 bit integers as strings as js can't deal with them
|
|
* even though we can. So, all snowflakes are sent and received wrapped as string values and must be read by nlohmann::json
|
|
* as string types, then converted from string to uint64_t. Checks for existence of the value, and that it is a string containing
|
|
* a number. If not, then this function returns 0.
|
|
*/
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
return !k->is_null() && k->is_string() ? strtoull(k->get<std::string>().c_str(), nullptr, 10) : 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SetSnowflakeNotNull(const json* j, const char *keyname, uint64_t &v) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
v = !k->is_null() && k->is_string() ? strtoull(k->get<std::string>().c_str(), nullptr, 10) : 0;
|
|
}
|
|
}
|
|
|
|
|
|
std::string StringNotNull(const json* j, const char *keyname) {
|
|
/* Returns empty string if the value is not a string, or is null or not defined */
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
return !k->is_null() && k->is_string() ? k->get<std::string>() : "";
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
void SetStringNotNull(const json* j, const char *keyname, std::string &v) {
|
|
/* Returns empty string if the value is not a string, or is null or not defined */
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
v = !k->is_null() && k->is_string() ? k->get<std::string>() : "";
|
|
}
|
|
}
|
|
|
|
uint64_t Int64NotNull(const json* j, const char *keyname) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
return !k->is_null() && !k->is_string() ? k->get<uint64_t>() : 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SetInt64NotNull(const json* j, const char *keyname, uint64_t &v) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
v = !k->is_null() && !k->is_string() ? k->get<uint64_t>() : 0;
|
|
}
|
|
}
|
|
|
|
|
|
uint32_t Int32NotNull(const json* j, const char *keyname) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
return !k->is_null() && !k->is_string() ? k->get<uint32_t>() : 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SetInt32NotNull(const json* j, const char *keyname, uint32_t &v) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
v = !k->is_null() && !k->is_string() ? k->get<uint32_t>() : 0;
|
|
}
|
|
}
|
|
|
|
uint16_t Int16NotNull(const json* j, const char *keyname) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
return !k->is_null() && !k->is_string() ? k->get<uint16_t>() : 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SetInt16NotNull(const json* j, const char *keyname, uint16_t &v) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
v = !k->is_null() && !k->is_string() ? k->get<uint16_t>() : 0;
|
|
}
|
|
}
|
|
|
|
uint8_t Int8NotNull(const json* j, const char *keyname) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
return !k->is_null() && !k->is_string() ? k->get<uint8_t>() : 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SetInt8NotNull(const json* j, const char *keyname, uint8_t &v) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
v = !k->is_null() && !k->is_string() ? k->get<uint8_t>() : 0;
|
|
}
|
|
}
|
|
|
|
bool BoolNotNull(const json* j, const char *keyname) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
return !k->is_null() ? (k->get<bool>() == true) : false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SetBoolNotNull(const json* j, const char *keyname, bool &v) {
|
|
auto k = j->find(keyname);
|
|
if (k != j->end()) {
|
|
v = !k->is_null() ? (k->get<bool>() == true) : false;
|
|
}
|
|
}
|
|
|
|
std::string base64_encode(unsigned char const* buf, unsigned int buffer_length) {
|
|
/* Quick and dirty base64 encode */
|
|
static const char to_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
size_t ret_size = buffer_length + 2;
|
|
|
|
ret_size = 4 * ret_size / 3;
|
|
|
|
std::string ret;
|
|
ret.reserve(ret_size);
|
|
|
|
for (unsigned int i=0; i<ret_size/4; ++i)
|
|
{
|
|
size_t index = i*3;
|
|
unsigned char b3[3];
|
|
b3[0] = buf[index+0];
|
|
b3[1] = buf[index+1];
|
|
b3[2] = buf[index+2];
|
|
|
|
ret.push_back(to_base64[ ((b3[0] & 0xfc) >> 2) ]);
|
|
ret.push_back(to_base64[ ((b3[0] & 0x03) << 4) + ((b3[1] & 0xf0) >> 4) ]);
|
|
ret.push_back(to_base64[ ((b3[1] & 0x0f) << 2) + ((b3[2] & 0xc0) >> 6) ]);
|
|
ret.push_back(to_base64[ ((b3[2] & 0x3f)) ]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
time_t TimestampNotNull(const json* j, const char* keyname)
|
|
{
|
|
/* Parses discord ISO 8061 timestamps to time_t, accounting for local time adjustment.
|
|
* Note that discord timestamps contain a decimal seconds part, which time_t and struct tm
|
|
* can't handle. We strip these out.
|
|
*/
|
|
time_t retval = 0;
|
|
if (j->find(keyname) != j->end() && !(*j)[keyname].is_null() && (*j)[keyname].is_string()) {
|
|
tm timestamp = {};
|
|
std::string timedate = (*j)[keyname].get<std::string>();
|
|
if (timedate.find('+') != std::string::npos && timedate.find('.') != std::string::npos) {
|
|
std::string tzpart = timedate.substr(timedate.find('+'), timedate.length());
|
|
timedate = timedate.substr(0, timedate.find('.')) + tzpart ;
|
|
strptime(timedate.substr(0, 19).c_str(), "%FT%TZ%z", ×tamp);
|
|
timestamp.tm_isdst = 0;
|
|
retval = mktime(×tamp);
|
|
} else {
|
|
strptime(timedate.substr(0, 19).c_str(), "%F %T", ×tamp);
|
|
retval = mktime(×tamp);
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
void SetTimestampNotNull(const json* j, const char* keyname, time_t &v)
|
|
{
|
|
/* Parses discord ISO 8061 timestamps to time_t, accounting for local time adjustment.
|
|
* Note that discord timestamps contain a decimal seconds part, which time_t and struct tm
|
|
* can't handle. We strip these out.
|
|
*/
|
|
time_t retval = 0;
|
|
if (j->find(keyname) != j->end() && !(*j)[keyname].is_null() && (*j)[keyname].is_string()) {
|
|
tm timestamp = {};
|
|
std::string timedate = (*j)[keyname].get<std::string>();
|
|
if (timedate.find('+') != std::string::npos && timedate.find('.') != std::string::npos) {
|
|
std::string tzpart = timedate.substr(timedate.find('+'), timedate.length());
|
|
timedate = timedate.substr(0, timedate.find('.')) + tzpart ;
|
|
strptime(timedate.substr(0, 19).c_str(), "%FT%TZ%z", ×tamp);
|
|
timestamp.tm_isdst = 0;
|
|
retval = mktime(×tamp);
|
|
} else {
|
|
strptime(timedate.substr(0, 19).c_str(), "%F %T", ×tamp);
|
|
retval = mktime(×tamp);
|
|
}
|
|
v = retval;
|
|
}
|
|
}
|
|
|
|
std::map<std::string, dpp::events::event*> eventmap = {
|
|
{ "__LOG__", new dpp::events::logger() },
|
|
{ "GUILD_CREATE", new dpp::events::guild_create() },
|
|
{ "GUILD_UPDATE", new dpp::events::guild_update() },
|
|
{ "GUILD_DELETE", new dpp::events::guild_delete() },
|
|
{ "GUILD_MEMBER_UPDATE", new dpp::events::guild_member_update() },
|
|
{ "RESUMED", new dpp::events::resumed() },
|
|
{ "READY", new dpp::events::ready() },
|
|
{ "CHANNEL_CREATE", new dpp::events::channel_create() },
|
|
{ "CHANNEL_UPDATE", new dpp::events::channel_update() },
|
|
{ "CHANNEL_DELETE", new dpp::events::channel_delete() },
|
|
{ "PRESENCE_UPDATE", new dpp::events::presence_update() },
|
|
{ "TYPING_START", new dpp::events::typing_start() },
|
|
{ "MESSAGE_CREATE", new dpp::events::message_create() },
|
|
{ "MESSAGE_UPDATE", new dpp::events::message_update() },
|
|
{ "MESSAGE_DELETE", new dpp::events::message_delete() },
|
|
{ "MESSAGE_DELETE_BULK", new dpp::events::message_delete_bulk() },
|
|
{ "MESSAGE_REACTION_ADD", new dpp::events::message_reaction_add() },
|
|
{ "MESSAGE_REACTION_REMOVE", new dpp::events::message_reaction_remove() },
|
|
{ "MESSAGE_REACTION_REMOVE_ALL", new dpp::events::message_reaction_remove_all() },
|
|
{ "MESSAGE_REACTION_REMOVE_EMOJI", new dpp::events::message_reaction_remove_emoji() },
|
|
{ "CHANNEL_PINS_UPDATE", new dpp::events::channel_pins_update() },
|
|
{ "GUILD_BAN_ADD", new dpp::events::guild_ban_add() },
|
|
{ "GUILD_BAN_REMOVE", new dpp::events::guild_ban_remove() },
|
|
{ "GUILD_EMOJIS_UPDATE", new dpp::events::guild_emojis_update() },
|
|
{ "GUILD_INTEGRATIONS_UPDATE", new dpp::events::guild_integrations_update() },
|
|
{ "INTEGRATION_CREATE", new dpp::events::integration_create() },
|
|
{ "INTEGRATION_UPDATE", new dpp::events::integration_update() },
|
|
{ "INTEGRATION_DELETE", new dpp::events::integration_delete() },
|
|
{ "GUILD_MEMBER_ADD", new dpp::events::guild_member_add() },
|
|
{ "GUILD_MEMBER_REMOVE", new dpp::events::guild_member_remove() },
|
|
{ "GUILD_MEMBERS_CHUNK", new dpp::events::guild_members_chunk() },
|
|
{ "GUILD_ROLE_CREATE", new dpp::events::guild_role_create() },
|
|
{ "GUILD_ROLE_UPDATE", new dpp::events::guild_role_update() },
|
|
{ "GUILD_ROLE_DELETE", new dpp::events::guild_role_delete() },
|
|
{ "VOICE_STATE_UPDATE", new dpp::events::voice_state_update() },
|
|
{ "VOICE_SERVER_UPDATE", new dpp::events::voice_server_update() },
|
|
{ "WEBHOOKS_UPDATE", new dpp::events::webhooks_update() },
|
|
{ "INVITE_CREATE", new dpp::events::invite_create() },
|
|
{ "INVITE_DELETE", new dpp::events::invite_delete() },
|
|
{ "APPLICATION_COMMAND_CREATE", new dpp::events::application_command_create() },
|
|
{ "APPLICATION_COMMAND_UPDATE", new dpp::events::application_command_update() },
|
|
{ "APPLICATION_COMMAND_DELETE", new dpp::events::application_command_delete() },
|
|
{ "INTERACTION_CREATE", new dpp::events::interaction_create() },
|
|
{ "USER_UPDATE", new dpp::events::user_update() },
|
|
{ "GUILD_JOIN_REQUEST_DELETE", new dpp::events::guild_join_request_delete() },
|
|
{ "STAGE_INSTANCE_CREATE", new dpp::events::stage_instance_create() },
|
|
{ "STAGE_INSTANCE_DELETE", new dpp::events::stage_instance_delete() },
|
|
{ "THREAD_CREATE", new dpp::events::thread_create() },
|
|
{ "THREAD_UPDATE", new dpp::events::thread_update() },
|
|
{ "THREAD_DELETE", new dpp::events::thread_delete() },
|
|
{ "THREAD_LIST_SYNC", new dpp::events::thread_list_sync() },
|
|
{ "THREAD_MEMBER_UPDATE", new dpp::events::thread_member_update() },
|
|
{ "THREAD_MEMBERS_UPDATE", new dpp::events::thread_members_update() },
|
|
{ "GUILD_APPLICATION_COMMAND_COUNTS_UPDATE", nullptr },
|
|
{ "GUILD_STICKERS_UPDATE", new dpp::events::guild_stickers_update() },
|
|
{ "APPLICATION_COMMAND_PERMISSIONS_UPDATE", nullptr },
|
|
};
|
|
|
|
void discord_client::HandleEvent(const std::string &event, json &j, const std::string &raw)
|
|
{
|
|
auto ev_iter = eventmap.find(event);
|
|
if (ev_iter != eventmap.end()) {
|
|
/* A handler with nullptr is silently ignored. We don't plan to make a handler for it
|
|
* so this usually some user-only thing thats crept into the API and shown to bots
|
|
* that we dont care about.
|
|
*/
|
|
if (ev_iter->second != nullptr) {
|
|
ev_iter->second->handle(this, j, raw);
|
|
}
|
|
} else {
|
|
log(dpp::ll_debug, fmt::format("Unhandled event: {}, {}", event, j.dump()));
|
|
}
|
|
}
|
|
|
|
};
|