33 SongAPIMap songAPIMap{};
34 VoiceConnectionsMap voiceConnectionMap{};
35 SoundCloudAPIMap soundCloudAPIMap{};
36 YouTubeAPIMap youtubeAPIMap{};
37 std::atomic_bool doWeQuit{};
40 DiscordCoreInternal::SoundCloudAPI* DiscordCoreClient::getSoundCloudAPI(Snowflake guildId) {
42 if (!Globals::soundCloudAPIMap.contains(guildId.operator uint64_t())) {
43 Globals::soundCloudAPIMap[guildId.operator uint64_t()] =
44 std::make_unique<DiscordCoreInternal::SoundCloudAPI>(guild.discordCoreClient, guild.discordCoreClient->httpsClient.get(), guildId);
46 return Globals::soundCloudAPIMap[guildId.operator uint64_t()].get();
49 DiscordCoreInternal::YouTubeAPI* DiscordCoreClient::getYouTubeAPI(Snowflake guildId) {
51 if (!Globals::youtubeAPIMap.contains(guildId.operator uint64_t())) {
52 Globals::youtubeAPIMap[guildId.operator uint64_t()] =
53 std::make_unique<DiscordCoreInternal::YouTubeAPI>(guild.discordCoreClient, guild.discordCoreClient->httpsClient.get(), guildId);
55 return Globals::youtubeAPIMap[guildId.operator uint64_t()].get();
58 VoiceConnection* DiscordCoreClient::getVoiceConnection(Snowflake guildId) {
60 if (!Globals::voiceConnectionMap.contains(guildId.operator uint64_t())) {
61 uint64_t theShardId{ (guildId.operator uint64_t() >> 22) % guild.
discordCoreClient->configManager.getTotalShardCount() };
62 uint64_t baseSocketIndex{ theShardId % guild.discordCoreClient->baseSocketAgentsMap.size() };
63 auto baseSocketAgent = guild.discordCoreClient->baseSocketAgentsMap[baseSocketIndex].get();
64 Globals::voiceConnectionMap[guildId.operator uint64_t()] = std::make_unique<VoiceConnection>(guild.discordCoreClient,
65 static_cast<DiscordCoreInternal::WebSocketClient*
>(baseSocketAgent->shardMap[theShardId].get()), &Globals::doWeQuit);
67 guild.voiceConnectionPtr = Globals::voiceConnectionMap[guildId.operator uint64_t()].get();
68 return guild.voiceConnectionPtr;
71 SongAPI* DiscordCoreClient::getSongAPI(Snowflake guildId) {
72 if (!Globals::songAPIMap.contains(guildId.operator uint64_t())) {
73 Globals::songAPIMap[guildId.operator uint64_t()] = std::make_unique<SongAPI>(guildId);
75 return Globals::songAPIMap[guildId.operator uint64_t()].get();
78 void atexitHandler() {
79 Globals::doWeQuit.store(
true);
82 SIGTERMError::SIGTERMError(
const std::string&
string) : DCAException(string){};
84 SIGSEGVError::SIGSEGVError(
const std::string&
string) : DCAException(string){};
86 SIGINTError::SIGINTError(
const std::string&
string) : DCAException(string){};
88 SIGILLError::SIGILLError(
const std::string&
string) : DCAException(string){};
90 SIGABRTError::SIGABRTError(
const std::string&
string) : DCAException(string){};
92 SIGFPEError::SIGFPEError(
const std::string&
string) : DCAException(string){};
94 void signalHandler(int32_t value) {
98 throw SIGTERMError{
"Exiting for: SIGTERM." };
101 throw SIGSEGVError{
"Exiting for: SIGSEGV." };
104 throw SIGINTError{
"Exiting for: SIGINT." };
107 throw SIGILLError{
"Exiting for: SIGILL." };
110 throw SIGABRTError{
"Exiting for: SIGABRT." };
113 throw SIGFPEError{
"Exiting for: SIGFPE." };
116 }
catch (SIGINTError&) {
118 Globals::doWeQuit.store(
true);
121 std::exit(EXIT_FAILURE);
126 std::atexit(&atexitHandler);
127 std::signal(SIGTERM, &signalHandler);
128 std::signal(SIGSEGV, &signalHandler);
129 std::signal(SIGINT, &signalHandler);
130 std::signal(SIGILL, &signalHandler);
131 std::signal(SIGABRT, &signalHandler);
132 std::signal(SIGFPE, &signalHandler);
133 configManager = ConfigManager{ configData };
134 if (!DiscordCoreInternal::SSLConnectionInterface::initialize()) {
135 if (configManager.doWePrintGeneralErrorMessages()) {
136 cout << shiftToBrightRed() <<
"Failed to initialize the SSL_CTX structure!" << reset() << endl << endl;
140 if (configManager.doWePrintFFMPEGSuccessMessages()) {
141 av_log_set_level(AV_LOG_INFO);
142 }
else if (configManager.doWePrintFFMPEGErrorMessages()) {
143 av_log_set_level(AV_LOG_ERROR);
144 }
else if (!configManager.doWePrintFFMPEGErrorMessages() && !configManager.doWePrintFFMPEGSuccessMessages()) {
145 av_log_set_level(AV_LOG_QUIET);
147 if (sodium_init() == -1) {
148 if (configManager.doWePrintGeneralErrorMessages()) {
149 cout << shiftToBrightRed() <<
"LibSodium failed to initialize!" << reset() << endl << endl;
153 httpsClient = std::make_unique<DiscordCoreInternal::HttpsClient>(&configManager);
154 ApplicationCommands::initialize(httpsClient.get());
155 AutoModerationRules::initialize(httpsClient.get());
156 Channels::initialize(httpsClient.get(), &configManager);
157 Guilds::initialize(httpsClient.get(),
this, &configManager);
158 GuildMembers::initialize(httpsClient.get(), &configManager);
159 GuildScheduledEvents::initialize(httpsClient.get());
160 Interactions::initialize(httpsClient.get());
161 Messages::initialize(httpsClient.get());
162 Reactions::initialize(httpsClient.get());
163 Roles::initialize(httpsClient.get(), &configManager);
164 Stickers::initialize(httpsClient.get());
165 StageInstances::initialize(httpsClient.get());
166 Threads::initialize(httpsClient.get());
167 WebHooks::initialize(httpsClient.get());
168 Users::initialize(httpsClient.get(), &configManager);
169 didWeStartCorrectly =
true;
172 ConfigManager& DiscordCoreClient::getConfigManager() {
173 return configManager;
177 return DiscordCoreClient::currentUser;
180 void DiscordCoreClient::runBot() {
181 if (!didWeStartCorrectly) {
184 if (!instantiateWebSockets()) {
185 Globals::doWeQuit.store(
true);
188 while (!Globals::doWeQuit.load()) {
189 std::this_thread::sleep_for(1ms);
194 void DiscordCoreClient::registerFunction(
const std::vector<std::string>& functionNames, std::unique_ptr<BaseFunction> baseFunction,
196 commandData.alwaysRegister = alwaysRegister;
197 commandController.registerFunction(functionNames, std::move(baseFunction));
198 commandsToRegister.emplace_back(commandData);
202 return commandController;
209 Milliseconds DiscordCoreClient::getTotalUpTime() {
210 return std::chrono::duration_cast<Milliseconds>(SysClock::now().time_since_epoch()) - startupTimeSinceEpoch;
213 void DiscordCoreClient::registerFunctionsInternal() {
214 std::vector<ApplicationCommand> theCommands{
215 ApplicationCommands::getGlobalApplicationCommandsAsync({ .applicationId = getBotUser().id, .withLocalizations =
false }).get()
217 while (commandsToRegister.size() > 0) {
218 CreateApplicationCommandData data = commandsToRegister.front();
219 commandsToRegister.pop_front();
220 data.applicationId = getBotUser().id;
221 if (data.alwaysRegister) {
222 if (data.guildId != 0) {
223 ApplicationCommands::createGuildApplicationCommandAsync(*
static_cast<CreateGuildApplicationCommandData*
>(&data)).get();
225 ApplicationCommands::createGlobalApplicationCommandAsync(*
static_cast<CreateGlobalApplicationCommandData*
>(&data)).get();
228 std::vector<ApplicationCommand> guildCommands{};
229 if (data.guildId != 0) {
230 guildCommands = ApplicationCommands::getGuildApplicationCommandsAsync(
231 { .applicationId = getBotUser().id, .withLocalizations =
false, .guildId = data.guildId })
235 for (
auto& value: theCommands) {
236 if (*
static_cast<ApplicationCommandData*
>(&value) == *
static_cast<ApplicationCommandData*
>(&data)) {
240 for (
auto& value: guildCommands) {
241 if (*
static_cast<ApplicationCommandData*
>(&value) == *
static_cast<ApplicationCommandData*
>(&data)) {
246 if (data.guildId != 0) {
247 ApplicationCommands::createGuildApplicationCommandAsync(*
static_cast<CreateGuildApplicationCommandData*
>(&data)).get();
250 ApplicationCommands::createGlobalApplicationCommandAsync(*
static_cast<CreateGlobalApplicationCommandData*
>(&data)).get();
257 GatewayBotData DiscordCoreClient::getGateWayBot() {
259 DiscordCoreInternal::HttpsWorkloadData workload{ DiscordCoreInternal::HttpsWorkloadType::Get_Gateway_Bot };
260 workload.workloadClass = DiscordCoreInternal::HttpsWorkloadClass::Get;
261 workload.relativePath =
"/gateway/bot";
262 workload.callStack =
"DiscordCoreClient::getGateWayBot()";
263 GatewayBotData data{};
264 httpsClient->submitWorkloadAndGetResult<GatewayBotData>(workload, data);
267 if (configManager.doWePrintGeneralErrorMessages()) {
274 bool DiscordCoreClient::instantiateWebSockets() {
275 GatewayBotData gatewayData{ getGateWayBot() };
277 if (gatewayData.url ==
"") {
278 if (configManager.doWePrintGeneralErrorMessages()) {
279 cout << shiftToBrightRed()
280 <<
"Failed to collect the connection URL! Closing! Did you remember to "
281 "properly set your bot token?"
285 std::this_thread::sleep_for(5s);
288 if (configManager.getStartingShard() + configManager.getShardCountForThisProcess() > configManager.getTotalShardCount()) {
289 if (configManager.doWePrintGeneralErrorMessages()) {
290 cout << shiftToBrightRed() <<
"Your sharding options are incorrect! Please fix it!" << reset() << endl << endl;
292 std::this_thread::sleep_for(5s);
295 uint32_t theWorkerCount = configManager.getTotalShardCount() <= std::thread::hardware_concurrency() ? configManager.getTotalShardCount()
296 : std::thread::hardware_concurrency();
297 if (configManager.getConnectionAddress() ==
"") {
298 configManager.setConnectionAddress(gatewayData.url.substr(gatewayData.url.find(
"wss://") + std::string(
"wss://").size()));
300 if (configManager.getConnectionPort() == 0) {
301 configManager.setConnectionPort(443);
303 for (uint32_t x = 0; x < configManager.getTotalShardCount(); ++x) {
304 if (!baseSocketAgentsMap.contains(x % theWorkerCount)) {
305 while (!connectionStopWatch01.hasTimePassed()) {
306 std::this_thread::sleep_for(1ms);
308 connectionStopWatch01.resetTimer();
309 baseSocketAgentsMap[x % theWorkerCount] =
310 std::make_unique<DiscordCoreInternal::BaseSocketAgent>(
this, &Globals::doWeQuit, x % theWorkerCount);
312 ConnectionPackage data{};
313 data.currentShard = x;
314 data.currentReconnectTries = 0;
315 baseSocketAgentsMap[x % theWorkerCount]->shardMap[x] =
316 std::make_unique<DiscordCoreInternal::WebSocketClient>(
this, data.currentShard, &Globals::doWeQuit,
false);
317 baseSocketAgentsMap[x % theWorkerCount]->getClient(x).connections = std::make_unique<ConnectionPackage>(data);
320 for (
auto& value: configManager.getFunctionsToExecute()) {
321 if (value.repeated) {
322 TimeElapsedHandlerNoArgs onSend = [=,
this]() ->
void {
323 value.function(
this);
325 ThreadPool::storeThread(onSend, value.intervalInMs);
327 ThreadPool::executeFunctionAfterTimePeriod(value.function, value.intervalInMs,
false,
this);
330 startupTimeSinceEpoch = std::chrono::duration_cast<Milliseconds>(SysClock::now().time_since_epoch());
334 DiscordCoreClient::~DiscordCoreClient() noexcept {
335 for (
auto& [key, value]: NewThreadAwaiterBase::threadPool.workerThreads) {
336 if (value.thread.joinable()) {
337 value.thread.request_stop();
338 value.thread.detach();
343 BotUser DiscordCoreClient::currentUser{};
DiscordCoreAPI_Dll void reportException(const std::string ¤tFunctionName, std::source_location location=std::source_location::current(), std::exception_ptr ptr=nullptr)
Prints the current file, line, and column from which the function is being called - typically from wi...
DiscordCoreClient * discordCoreClient
A pointer to the DiscordCoreClient.
The main namespace for this library.
For creating an application command.
A class for handling commands from user input.
Class for handling the assignment of event-handling functions.int32_t.
A type of User, to represent the Bot and some of its associated endpoints.
static GuildData getCachedGuild(GetGuildData dataPackage)
Collects a Guild from the library's cache.
Configuration data for the library's main class, DiscordCoreClient.