From 80a413efdcdb2bf089a53e97dd2c2d336398d3f0 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Thu, 27 Aug 2026 20:14:05 +0200 Subject: [PATCH] test build --- CMakeLists.txt | 8 + include/concord/all.PRE.h | 26 + include/concord/anomap.h | 67 + include/concord/application_command.h | 256 + include/concord/attributes.h | 12 + include/concord/audit_log.h | 37 + include/concord/auto_moderation.h | 99 + include/concord/carray.h | 232 + include/concord/channel.h | 670 ++ include/concord/chash.h | 510 ++ include/concord/clock.h | 464 + include/concord/cog-utils.h | 137 + include/concord/concord-error.h | 69 + include/concord/concord-notifier.h | 50 + include/concord/concord-once.h | 49 + include/concord/discord-cache.h | 51 + include/concord/discord-events.h | 989 ++ include/concord/discord-internal.h | 1260 +++ include/concord/discord-request.h | 101 + include/concord/discord-response.h | 192 + include/concord/discord-worker.h | 54 + include/concord/discord.h | 603 ++ include/concord/discord_codecs.PRE.h | 74 + include/concord/discord_codecs.h | 10530 ++++++++++++++++++++++ include/concord/emoji.h | 95 + include/concord/gateway.h | 76 + include/concord/gencodecs-process.PRE.h | 27 + include/concord/gencodecs.h | 74 + include/concord/guild.h | 683 ++ include/concord/guild_scheduled_event.h | 118 + include/concord/guild_template.h | 126 + include/concord/interaction.h | 152 + include/concord/invite.h | 51 + include/concord/io_poller.h | 118 + include/concord/jsmn-find.h | 683 ++ include/concord/jsmn.h | 471 + include/concord/json-build.h | 957 ++ include/concord/log.h | 46 + include/concord/logmod.h | 1094 +++ include/concord/oa_hash.h | 345 + include/concord/oauth2.h | 38 + include/concord/osname.h | 319 + include/concord/priority_queue.h | 59 + include/concord/queriec.h | 30 + include/concord/queue.h | 111 + include/concord/stage_instance.h | 75 + include/concord/sticker.h | 104 + include/concord/threadpool.h | 101 + include/concord/types.h | 66 + include/concord/user-agent.h | 400 + include/concord/user.h | 115 + include/concord/voice.h | 31 + include/concord/webhook.h | 211 + include/concord/websockets.h | 326 + include/dlibc/vector.h | 872 ++ src/main.c | 6 + 56 files changed, 24520 insertions(+) create mode 100644 include/concord/all.PRE.h create mode 100644 include/concord/anomap.h create mode 100644 include/concord/application_command.h create mode 100644 include/concord/attributes.h create mode 100644 include/concord/audit_log.h create mode 100644 include/concord/auto_moderation.h create mode 100644 include/concord/carray.h create mode 100644 include/concord/channel.h create mode 100644 include/concord/chash.h create mode 100644 include/concord/clock.h create mode 100644 include/concord/cog-utils.h create mode 100644 include/concord/concord-error.h create mode 100644 include/concord/concord-notifier.h create mode 100644 include/concord/concord-once.h create mode 100644 include/concord/discord-cache.h create mode 100644 include/concord/discord-events.h create mode 100644 include/concord/discord-internal.h create mode 100644 include/concord/discord-request.h create mode 100644 include/concord/discord-response.h create mode 100644 include/concord/discord-worker.h create mode 100644 include/concord/discord.h create mode 100644 include/concord/discord_codecs.PRE.h create mode 100644 include/concord/discord_codecs.h create mode 100644 include/concord/emoji.h create mode 100644 include/concord/gateway.h create mode 100644 include/concord/gencodecs-process.PRE.h create mode 100644 include/concord/gencodecs.h create mode 100644 include/concord/guild.h create mode 100644 include/concord/guild_scheduled_event.h create mode 100644 include/concord/guild_template.h create mode 100644 include/concord/interaction.h create mode 100644 include/concord/invite.h create mode 100644 include/concord/io_poller.h create mode 100644 include/concord/jsmn-find.h create mode 100644 include/concord/jsmn.h create mode 100644 include/concord/json-build.h create mode 100644 include/concord/log.h create mode 100644 include/concord/logmod.h create mode 100644 include/concord/oa_hash.h create mode 100644 include/concord/oauth2.h create mode 100644 include/concord/osname.h create mode 100644 include/concord/priority_queue.h create mode 100644 include/concord/queriec.h create mode 100644 include/concord/queue.h create mode 100644 include/concord/stage_instance.h create mode 100644 include/concord/sticker.h create mode 100644 include/concord/threadpool.h create mode 100644 include/concord/types.h create mode 100644 include/concord/user-agent.h create mode 100644 include/concord/user.h create mode 100644 include/concord/voice.h create mode 100644 include/concord/webhook.h create mode 100644 include/concord/websockets.h create mode 100644 include/dlibc/vector.h create mode 100644 src/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f22675..f136367 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -411,6 +411,14 @@ else() LOG_OUTPUT_ON_FAILURE TRUE ) + ExternalProject_Add_Step(concord_external mirror_headers + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CONCORD_INCLUDE_DIR}/concord" + "${PROJECT_SOURCE_DIR}/include/concord" + DEPENDEES install + COMMENT "Mirroring Concord headers into include/concord" + ) + add_library(concord::concord STATIC IMPORTED GLOBAL) set_target_properties(concord::concord PROPERTIES IMPORTED_LOCATION "${CONCORD_LIBRARY}" diff --git a/include/concord/all.PRE.h b/include/concord/all.PRE.h new file mode 100644 index 0000000..8c5c804 --- /dev/null +++ b/include/concord/all.PRE.h @@ -0,0 +1,26 @@ +#include "custom.PRE.h" + +#include "application.PRE.h" +#include "audit_log.PRE.h" +#include "auto_moderation.PRE.h" +#include "invite.PRE.h" +#include "channel.PRE.h" +#include "emoji.PRE.h" +#include "guild.PRE.h" +#include "guild_scheduled_event.PRE.h" +#include "guild_template.PRE.h" +#include "stage_instance.PRE.h" +#include "sticker.PRE.h" +#include "user.PRE.h" +#include "voice.PRE.h" +#include "webhook.PRE.h" + +#include "gateway.PRE.h" +#include "oauth2.PRE.h" +#include "permissions.PRE.h" +#include "teams.PRE.h" +#include "voice_connections.PRE.h" + +#include "application_commands.PRE.h" +#include "message_components.PRE.h" +#include "interactions.PRE.h" diff --git a/include/concord/anomap.h b/include/concord/anomap.h new file mode 100644 index 0000000..93cb7f4 --- /dev/null +++ b/include/concord/anomap.h @@ -0,0 +1,67 @@ +// MIT License +// Copyright (c) 2022 Anotra +// https://github.com/Anotra/anomap + +#pragma once + +#ifndef ANOMAP_H +#define ANOMAP_H + +#include +#include + +#define ANOMAP_DECLARE_COMPARE_FUNCTION(function_name, data_type) \ + static int \ + function_name(const void *a, const void *b) { \ + if (*(data_type *)a == *(data_type *)b) return 0; \ + return *(data_type *)a > *(data_type *)b ? 1 : -1; \ + } + +enum anomap_operation { + anomap_insert = 1 << 0, + anomap_update = 1 << 1, + anomap_upsert = anomap_insert | anomap_update, + anomap_delete = 1 << 2, + anomap_getval = 1 << 3, +}; + +struct anomap; + +struct anomap *anomap_create(size_t key_size, size_t val_size, + int (*cmp)(const void *, const void *)); +void anomap_destroy(struct anomap *map); + +struct anomap_item_changed { + void *data; + enum anomap_operation op; + void *key; + struct { + void *prev; + void *now; + } val; +}; + +typedef void anomap_on_item_changed( + struct anomap *map, struct anomap_item_changed *item_changed); + +void anomap_set_on_item_changed( + struct anomap *map, anomap_on_item_changed *on_changed, void *data); + +size_t anomap_length(struct anomap *map); +void anomap_clear(struct anomap *map); + +bool anomap_index_of(struct anomap *map, void *key, size_t *index); +bool anomap_at_index(struct anomap *map, size_t index, void *key, void *val); + +enum anomap_operation anomap_do(struct anomap *map, + enum anomap_operation operation, + void *key, void *val); + +size_t anomap_copy_range(struct anomap *map, + size_t from_index, size_t to_index, + void *keys, void *vals); +size_t anomap_delete_range(struct anomap *map, + size_t from_index, size_t to_index, + void *keys, void *vals); + +#endif // !ANOMAP_H diff --git a/include/concord/application_command.h b/include/concord/application_command.h new file mode 100644 index 0000000..206ea19 --- /dev/null +++ b/include/concord/application_command.h @@ -0,0 +1,256 @@ +/** + * @file application_command.h + * @author Cogmasters + * @brief Application Command public functions and datatypes + * @todo application_id should be cached and used when its input value is `0` + */ + +#ifndef DISCORD_APPLICATION_COMMAND_H +#define DISCORD_APPLICATION_COMMAND_H + +/** @defgroup DiscordAPIInteractionsApplicationCommand Slash commands + * @ingroup DiscordAPIInteractions + * @brief Receiving and registering slash commands + * @{ */ + +/** + * @brief Fetch all of the global commands for your application + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @CCORD_ret_obj{ret,application_commands} + * @CCORD_return + */ +CCORDcode discord_get_global_application_commands( + struct discord *client, + u64snowflake application_id, + struct discord_ret_application_commands *ret); + +/** + * @brief Create a new global command + * @note New global commands will be available in all guilds after 1 hour + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param params request parameters + * @CCORD_ret_obj{ret,application_command} + * @CCORD_return + */ +CCORDcode discord_create_global_application_command( + struct discord *client, + u64snowflake application_id, + struct discord_create_global_application_command *params, + struct discord_ret_application_command *ret); + +/** + * @brief Fetch a global command for your application + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param command_id the registered command id + * @CCORD_ret_obj{ret,application_command} + * @CCORD_return + */ +CCORDcode discord_get_global_application_command( + struct discord *client, + u64snowflake application_id, + u64snowflake command_id, + struct discord_ret_application_command *ret); + +/** + * @brief Edit a global command + * @note Updates will be available in all guilds after 1 hour + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param command_id the registered command id + * @param params request parameters + * @CCORD_ret_obj{ret,application_command} + * @CCORD_return + */ +CCORDcode discord_edit_global_application_command( + struct discord *client, + u64snowflake application_id, + u64snowflake command_id, + struct discord_edit_global_application_command *params, + struct discord_ret_application_command *ret); + +/** + * @brief Deletes a global command + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param command_id the registered command id + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_global_application_command( + struct discord *client, + u64snowflake application_id, + u64snowflake command_id, + struct discord_ret *ret); + +/** + * @brief Overwrite existing global application commands + * @note Updates will be available in all guilds after 1 hour + * @warning Will overwrite all types of application commands: slash + * commands, user commands, and message commands + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param params the request parameters, a list of application commands + * @CCORD_ret_obj{ret,application_commands} + * @CCORD_return + */ +CCORDcode discord_bulk_overwrite_global_application_commands( + struct discord *client, + u64snowflake application_id, + struct discord_application_commands *params, + struct discord_ret_application_commands *ret); + +/** + * @brief Fetch all of the guild commands of a given guild + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param guild_id the guild where the commands are located + * @CCORD_ret_obj{ret,application_commands} + * @CCORD_return + */ +CCORDcode discord_get_guild_application_commands( + struct discord *client, + u64snowflake application_id, + u64snowflake guild_id, + struct discord_ret_application_commands *ret); + +/** + * @brief Create a new guild command + * @note Commands will be available in the guild immediately + * @note Will overwrite any existing guild command with the same name + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param guild_id the guild where the command is located + * @param params request parameters + * @CCORD_ret_obj{ret,application_command} + * @CCORD_return + */ +CCORDcode discord_create_guild_application_command( + struct discord *client, + u64snowflake application_id, + u64snowflake guild_id, + struct discord_create_guild_application_command *params, + struct discord_ret_application_command *ret); + +/** + * @brief Fetch a guild command for your application + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param guild_id the guild where the command is located + * @param command_id the registered command id + * @CCORD_ret_obj{ret,application_command} + * @CCORD_return + */ +CCORDcode discord_get_guild_application_command( + struct discord *client, + u64snowflake application_id, + u64snowflake guild_id, + u64snowflake command_id, + struct discord_ret_application_command *ret); + +/** + * @brief Edit a guild command + * @note Updates for guild commands will be available immediately + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param guild_id the guild where the command is located + * @param command_id the registered command id + * @param params request parameters + * @CCORD_ret_obj{ret,application_command} + * @CCORD_return + */ +CCORDcode discord_edit_guild_application_command( + struct discord *client, + u64snowflake application_id, + u64snowflake guild_id, + u64snowflake command_id, + struct discord_edit_guild_application_command *params, + struct discord_ret_application_command *ret); + +/** + * @brief Deletes a guild command + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param guild_id the guild where the command is located + * @param command_id the registered command id + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_guild_application_command(struct discord *client, + u64snowflake application_id, + u64snowflake guild_id, + u64snowflake command_id, + struct discord_ret *ret); + +/** + * @brief Overwrite existing guild application commands + * @warning This will overwrite all types of application commands: slash + * commands, user commands, and message commands + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param guild_id the guild where the commands are located + * @param params the request parameters, a list of application commands + * @CCORD_ret_obj{ret,application_commands} + * @CCORD_return + */ +CCORDcode discord_bulk_overwrite_guild_application_commands( + struct discord *client, + u64snowflake application_id, + u64snowflake guild_id, + struct discord_bulk_overwrite_guild_application_commands *params, + struct discord_ret_application_commands *ret); + +/** + * @brief Fetches command permissions for all commands in a given guild + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param guild_id the guild where the commands are located + * @CCORD_ret_obj{ret,guild_application_command_permissions} + * @CCORD_return + */ +CCORDcode discord_get_guild_application_command_permissions( + struct discord *client, + u64snowflake application_id, + u64snowflake guild_id, + struct discord_ret_guild_application_command_permissions *ret); + +/** + * @brief Fetches command permissions for a specific command in a given guild + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the parent application + * @param guild_id the guild where the command is located + * @param command_id the registered command id + * @CCORD_ret_obj{ret,application_command_permissions} + * @CCORD_return + */ +CCORDcode discord_get_application_command_permissions( + struct discord *client, + u64snowflake application_id, + u64snowflake guild_id, + u64snowflake command_id, + struct discord_ret_application_command_permission *ret); + +/** @example slash-commands.c + * Demonstrates registering and reacting to slash commands */ +/** @example slash-commands2.c + * Demonstrates registering and reacting to slash commands from the console */ + +/** @} DiscordAPIInteractionsApplicationCommand */ + +#endif /* DISCORD_APPLICATION_COMMAND_H */ diff --git a/include/concord/attributes.h b/include/concord/attributes.h new file mode 100644 index 0000000..0c94c8d --- /dev/null +++ b/include/concord/attributes.h @@ -0,0 +1,12 @@ +#ifndef ATTRIBUTES_H +#define ATTRIBUTES_H + +#if defined(__MINGW32__) \ + || (defined(__GNUC__) && __GNUC__ > 4 ? true : __GNUC_PATCHLEVEL__ >= 4) \ + || defined(__USE_MINGW_ANSI_STDIO) +#define PRINTF_LIKE(a, b) __attribute__((format(gnu_printf, a, b))) +#else +#define PRINTF_LIKE(a, b) +#endif + +#endif /* ATTRIBUTES_H */ diff --git a/include/concord/audit_log.h b/include/concord/audit_log.h new file mode 100644 index 0000000..c4ed61c --- /dev/null +++ b/include/concord/audit_log.h @@ -0,0 +1,37 @@ +/** + * @file audit_log.h + * @author Cogmasters + * @brief Audit Log public functions and datatypes + */ + +#ifndef DISCORD_AUDIT_LOG +#define DISCORD_AUDIT_LOG + +/** @defgroup DiscordAPIAuditLog Audit Log + * @ingroup DiscordAPI + * @brief Audit Log's public API supported by Concord + * @{ */ + +/** + * @brief Get audit log for a given guild + * + * @note Requires the 'VIEW_AUDIT_LOG' permission + * @param client the client created with discord_from_token() + * @param guild_id the guild to retrieve the audit log from + * @param params request parameters + * @CCORD_ret_obj{ret,audit_log} + * @CCORD_return + */ +CCORDcode discord_get_guild_audit_log( + struct discord *client, + u64snowflake guild_id, + struct discord_get_guild_audit_log *params, + struct discord_ret_audit_log *ret); + +/** @example audit-log.c + * Demonstrates listening to audit-log events and fetching a specific audit-log + */ + +/** @} DiscordAPIAuditLog */ + +#endif /* DISCORD_AUDIT_LOG */ diff --git a/include/concord/auto_moderation.h b/include/concord/auto_moderation.h new file mode 100644 index 0000000..36824d0 --- /dev/null +++ b/include/concord/auto_moderation.h @@ -0,0 +1,99 @@ +/** + * @file auto_moderation.h + * @author Cogmasters + * @brief Auto Moderation public functions and datatypes + */ + +#ifndef DISCORD_AUTO_MODERATION_H +#define DISCORD_AUTO_MODERATION_H + +/** @defgroup DiscordAPIAutoModeration Auto Moderation + * @ingroup DiscordAPI + * @brief Auto Moderation public API supported by Concord + * @{ */ + +/** + * @brief Get a list of all rules currently configured for the guild + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to fetch the rules from + * @CCORD_ret_obj{ret,auto_moderation_rules} + * @CCORD_return + */ +CCORDcode discord_list_auto_moderation_rules_for_guild( + struct discord *client, + u64snowflake guild_id, + struct discord_ret_auto_moderation_rules *ret); + +/** + * @brief Get a single rule + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to fetch the rule from + * @param auto_moderation_rule_id the rule to be fetched + * @CCORD_ret_obj{ret,auto_moderation_rule} + * @CCORD_return + */ +CCORDcode discord_get_auto_moderation_rule( + struct discord *client, + u64snowflake guild_id, + u64snowflake auto_moderation_rule_id, + struct discord_ret_auto_moderation_rule *ret); + +/** + * @brief Create a new rule + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to create the rule in + * @param params request parameters + * @CCORD_ret_obj{ret,auto_moderation_rule} + * @CCORD_return + */ +CCORDcode discord_create_auto_moderation_rule( + struct discord *client, + u64snowflake guild_id, + struct discord_create_auto_moderation_rule *params, + struct discord_ret_auto_moderation_rule *ret); + +/** + * @brief Modify an existing rule + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild where the rule to be modified is at + * @param auto_moderation_rule_id the rule to be modified + * @param params request parameters + * @CCORD_ret_obj{ret,auto_moderation_rule} + * @CCORD_return + */ +CCORDcode discord_modify_auto_moderation_rule( + struct discord *client, + u64snowflake guild_id, + u64snowflake auto_moderation_rule_id, + struct discord_modify_auto_moderation_rule *params, + struct discord_ret_auto_moderation_rule *ret); + +/** + * @brief Delete a rule + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild where the rule to be deleted is at + * @param auto_moderation_rule_id the rule to be deleted + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_auto_moderation_rule( + struct discord *client, + u64snowflake guild_id, + u64snowflake auto_moderation_rule_id, + struct discord_delete_auto_moderation_rule *params, + struct discord_ret *ret); + +/** @} DiscordAPIAutoModeration */ + +#endif /* DISCORD_AUTO_MODERATION_H */ diff --git a/include/concord/carray.h b/include/concord/carray.h new file mode 100644 index 0000000..efa9490 --- /dev/null +++ b/include/concord/carray.h @@ -0,0 +1,232 @@ +/* Copyright 2022 Cogmasters */ +/* + * C-Ware License + * + * Copyright (c) 2022, C-Ware + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Redistributions of modified source code must append a copyright notice in + * the form of 'Copyright ' to each modified source file's + * copyright notice, and the standalone license file if one exists. + * + * A 'redistribution' can be constituted as any version of the original source + * code material that is intended to comprise some other derivative work of + * this code. A fork created for the purpose of contributing to any version of + * the source does not constitute a truly 'derivative work' and does not require + * listing. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Modified by Lucas Müller , 19 Sept 2022 + * - __carray_init() should initialize its `size` value + * + * Modified by Lucas Müller , 15 Feb 2022 + * - CARRAY_RESIZE() has a fallback value (+1) + * + * Modified by Lucas Müller , 06 Feb 2022 + * - __carray_init() accept initial length + * + * Modified by Lucas Müller , 02 Feb 2022 + * - remove free(carray) at __carrray_free() + * + * Modified by Lucas Müller , 01 Feb 2022 + * - change CARRAY_INITIAL_SIZE from 5 to 4 + * - change CARRAY_RESIZE to doubling arrays to reduce realloc calls + * - remove calloc() from __carray_init(), expect user to allocate it + * - remove pseudo-return from __carray_init() + * + * Modified by Lucas Müller , 27 Jan 2022 + * - rename contents -> array + * - rename logical_size -> size + * - rename physical_size -> realsize + */ + +#ifndef CWARE_ARRAY_H +#define CWARE_ARRAY_H + +#ifndef CARRAY_INITIAL_SIZE +#define CARRAY_INITIAL_SIZE 4 +#endif + +#ifndef CARRAY_RESIZE +#define CARRAY_RESIZE(current_size) \ + 1 + current_size * 2 +#endif + +/* carray_init */ +#ifndef CARRAY_STACKFUL +#define __carray_init(carray, length, _type, _compare, _free) \ +do { \ + (carray)->realsize = length; \ + (carray)->size = 0; \ + (carray)->array = calloc(length, sizeof(_type)); \ +} while (0) + +#define carray_init(carray, settings) \ + __carray_init(carray, CARRAY_INITIAL_SIZE, settings) +#else +#define carray_init(carray, length, block) \ +do { \ + carray.realsize = length; \ + carray.size = 0; \ + carray.array = block; \ +} while (0) +#endif + +/* carray_insert */ +#ifndef CARRAY_STACKFUL +#define __carray_insert_handle_full(carray, index, value) \ + (carray)->realsize = CARRAY_RESIZE((carray)->realsize); \ + (carray)->array = realloc((carray)->array, sizeof(*(carray)->array) * (size_t) (carray)->realsize) +#else +#define __carray_insert_handle_full(carray, index, value) \ + fprintf(stderr, "carray_insert: attempt to insert value '%s' into full array '%s'. (%s:%i)\n", #value, #carray, __FILE__, __LINE__); \ + exit(EXIT_FAILURE) +#endif + +#define carray_insert(carray, index, value) \ + if((carray)->size == (carray)->realsize) { \ + __carray_insert_handle_full(carray, index, value); \ + } \ + \ + if(index < 0 || index > (carray)->size) { \ + fprintf(stderr, "carray_insert: attempt to insert at index %i, out of bounds of array '%s'. (%s:%i)\n", index, #carray, __FILE__, __LINE__); \ + exit(EXIT_FAILURE); \ + } \ + \ + memmove((carray)->array + index + 1, (carray)->array + index, sizeof(*(carray)->array) * (size_t) ((carray)->size - index)); \ + (carray)->array[index] = value; \ + (carray)->size++ + +/* carray_pop */ +#define carray_pop(carray, index, location) \ + location; \ + \ + if(index < 0 || index >= (carray)->size) { \ + fprintf(stderr, "carray_pop: attempt to pop index %i, out of bounds of array '%s'. (%s:%i)\n", index, #carray, __FILE__, __LINE__); \ + exit(EXIT_FAILURE); \ + } \ + \ + (carray)->size--; \ + (location) = (carray)->array[(index)]; \ + memmove((carray)->array + index, (carray)->array + index + 1, sizeof(*(carray)->array) * (size_t) ((carray)->size - index)) + +/* carray_remove */ +#define __carray_remove(carray, value, _type, _compare, _free) \ +do { \ + int __CARRAY_ITER_INDEX = 0; \ + \ + for(__CARRAY_ITER_INDEX = 0; __CARRAY_ITER_INDEX < (carray)->size; __CARRAY_ITER_INDEX++) { \ + _type __CARRAY_OPERAND_A = (carray)->array[__CARRAY_ITER_INDEX]; \ + _type __CARRAY_OPERAND_B = value; \ + \ + if((_compare) == 0) \ + continue; \ + \ + _free; \ + (carray)->size--; \ + memmove((carray)->array + __CARRAY_ITER_INDEX, \ + (carray)->array + __CARRAY_ITER_INDEX + 1, \ + sizeof(*(carray)->array) * (size_t) ((carray)->size - __CARRAY_ITER_INDEX)); \ + \ + __CARRAY_ITER_INDEX = -1; \ + break; \ + } \ + \ + if(__CARRAY_ITER_INDEX != -1) { \ + fprintf(stderr, "carray_remove: attempt to remove value '%s' that is not in array '%s'. (%s:%i)\n", #value, #carray, __FILE__, __LINE__); \ + exit(EXIT_FAILURE); \ + } \ +} while(0) + +#define carray_remove(carray, value, settings) \ + __carray_remove(carray, value, settings) + +/* carray_find */ +#define __carray_find(carray, value, location, _type, _compare, _free) \ + -1; \ + \ + do { \ + int __CARRAY_ITER_INDEX = 0; \ + location = -1; \ + \ + for(__CARRAY_ITER_INDEX = 0; __CARRAY_ITER_INDEX < (carray)->size; __CARRAY_ITER_INDEX++) { \ + _type __CARRAY_OPERAND_A = (carray)->array[__CARRAY_ITER_INDEX]; \ + _type __CARRAY_OPERAND_B = value; \ + \ + if((_compare) == 0) \ + continue; \ + \ + location = __CARRAY_ITER_INDEX; \ + \ + break; \ + } \ + } while(0) + +#define carray_find(carray, value, location, settings) \ + __carray_find(carray, value, location, settings) + +#ifndef CARRAY_STACKFUL +#define __carray_free_array(carray) free((carray)->array); +#else +#define __carray_free_array(carray) +#endif + +/* carray_free */ +#define __carray_free(carray, _type, _compare, _free) \ +do { \ + int __CARRAY_ITER_INDEX = 0; \ + \ + for(__CARRAY_ITER_INDEX = 0; __CARRAY_ITER_INDEX < (carray)->size; __CARRAY_ITER_INDEX++) { \ + _type __CARRAY_OPERAND_A = (carray)->array[__CARRAY_ITER_INDEX]; \ + (void) __CARRAY_OPERAND_A; \ + \ + _free; \ + } \ + \ + __carray_free_array(carray); \ +} while(0) + +#define carray_free(carray, settings) \ + __carray_free(carray, settings) + +/* carray_append */ +#ifndef CARRAY_STACKFUL +#define __carray_append_handle_full(carray, value) \ + (carray)->realsize = CARRAY_RESIZE((carray)->realsize); \ + (carray)->array = realloc((carray)->array, sizeof(*(carray)->array) * (size_t) (carray)->realsize) +#else +#define __carray_append_handle_full(carray, value) \ + fprintf(stderr, "carray_append: attempt to append value '%s' into full array '%s'. (%s:%i)\n", #value, #carray, __FILE__, __LINE__); \ + exit(EXIT_FAILURE) +#endif + +#define carray_append(carray, value) \ + if((carray)->size == (carray)->realsize) { \ + __carray_append_handle_full(carray, value); \ + } \ + \ + (carray)->array[(carray)->size] = value; \ + (carray)->size++; + +#endif diff --git a/include/concord/channel.h b/include/concord/channel.h new file mode 100644 index 0000000..c42c817 --- /dev/null +++ b/include/concord/channel.h @@ -0,0 +1,670 @@ +/** + * @file channel.h + * @author Cogmasters + * @brief Channel public functions and datatypes + */ + +#ifndef DISCORD_CHANNEL_H +#define DISCORD_CHANNEL_H + +/* forward declaration */ +struct discord_ret_users; +/**/ + +/** @defgroup DiscordAPIChannel Channel + * @ingroup DiscordAPI + * @brief Channel's public API supported by Concord + * @{ */ + +/** + * @brief Get channel from given id + * @note If the channel is a thread, a thread member object is included in the + * returned result + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to be retrieved + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_get_channel(struct discord *client, + u64snowflake channel_id, + struct discord_ret_channel *ret); + +/** + * @brief Update a channel's settings + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to be modified + * @param params request parameters + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_modify_channel(struct discord *client, + u64snowflake channel_id, + struct discord_modify_channel *params, + struct discord_ret_channel *ret); + +/** + * @brief Delete a channel, or close a private message + * @note Requires the MANAGE_CHANNELS permission for the guild, or + * MANAGE_THREADS if the channel is a thread + * @note Deleting a category does not delete its child channels; they will have + * their parent_id removed and a `Channel Update Gateway` event will + * fire for each of them + * @note Fires a `Channel Delete` event (or `Thread Delete` if the channel + * was a thread) + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to be deleted + * @param params request parameters + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_delete_channel(struct discord *client, + u64snowflake channel_id, + struct discord_delete_channel *params, + struct discord_ret_channel *ret); + +/** + * @brief Get messages for a given channel + * @note If operating on a guild channel, this endpoint requires the + * VIEW_CHANNEL permission to be present on the current user + * @note If the current user is missing the READ_MESSAGE_HISTORY permission + * in the channel then this will return no messages (since they cannot + * read the message history) + * @note The before, after, and around keys are mutually exclusive, only one + * may be passed at a time + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to get messages from + * @param params request parameters + * @CCORD_ret_obj{ret,messages} + * @CCORD_return + */ +CCORDcode discord_get_channel_messages( + struct discord *client, + u64snowflake channel_id, + struct discord_get_channel_messages *params, + struct discord_ret_messages *ret); + +/** + * @brief Get a specific message in the channel + * @note If operating on a guild channel, this endpoint requires the + * 'READ_MESSAGE_HISTORY' permission to be present on the current user + * @param client the client created with discord_from_token() + * @param channel_id the channel where the message resides + * @param message_id the message itself + * @CCORD_ret_obj{ret,message} + * @CCORD_return + */ +CCORDcode discord_get_channel_message(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + struct discord_ret_message *ret); + +/** + * @brief Post a message to a guild text or DM channel + * @note Fires a `Message Create` event + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to send the message at + * @param params request parameters + * @CCORD_ret_obj{ret,message} + * @CCORD_return + */ +CCORDcode discord_create_message(struct discord *client, + u64snowflake channel_id, + struct discord_create_message *params, + struct discord_ret_message *ret); + +/** + * @brief Crosspost a message in a News Channel to following channels + * @note This endpoint requires the 'SEND_MESSAGES' permission, if the current + * user sent the message, or additionally the 'MANAGE_MESSAGES' + * permission, for all other messages, to be present for the current + * user + * + * @param client the client created with discord_from_token() + * @param channel_id the news channel that will crosspost + * @param message_id the message that will crospost + * @CCORD_ret_obj{ret,message} + * @CCORD_return + */ +CCORDcode discord_crosspost_message(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + struct discord_ret_message *ret); + +/** + * @brief Create a reaction for the message + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param message_id the message to receive a reaction + * @param emoji_id the emoji id (leave as 0 if not a custom emoji) + * @param emoji_name the emoji name + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_create_reaction(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + u64snowflake emoji_id, + const char emoji_name[], + struct discord_ret *ret); + +/** + * @brief Delete a reaction the current user has made for the message + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param message_id the message to have a reaction deleted + * @param emoji_id the emoji id (leave as 0 if not a custom emoji) + * @param emoji_name the emoji name + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_own_reaction(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + u64snowflake emoji_id, + const char emoji_name[], + struct discord_ret *ret); + +/** + * @brief Deletes another user's reaction + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param message_id the message to have a reaction deleted + * @param user_id the user the reaction belongs to + * @param emoji_id the emoji id (leave as 0 if not a custom emoji) + * @param emoji_name the emoji name + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_user_reaction(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + u64snowflake user_id, + u64snowflake emoji_id, + const char emoji_name[], + struct discord_ret *ret); + +/** + * @brief Get a list of users that reacted with given emoji + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param message_id the message reacted to + * @param emoji_id the emoji id (leave as 0 if not a custom emoji) + * @param emoji_name the emoji name + * @param params request parameters + * @CCORD_ret_obj{ret,users} + * @CCORD_return + */ +CCORDcode discord_get_reactions(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + u64snowflake emoji_id, + const char emoji_name[], + struct discord_get_reactions *params, + struct discord_ret_users *ret); + +/** + * @brief Deletes all reactions from message + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param message_id the message that will be purged of reactions + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_all_reactions(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + struct discord_ret *ret); + +/** + * @brief Deletes all the reactions for a given emoji on message + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param message_id the message that will be purged of reactions from + * particular emoji + * @param emoji_id the emoji id (leave as 0 if not a custom emoji) + * @param emoji_name the emoji name + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_all_reactions_for_emoji(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + u64snowflake emoji_id, + const char emoji_name[], + struct discord_ret *ret); + +/** + * @brief Edit a previously sent message + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param message_id the message that will be purged of reactions from + * particular emoji + * @param params request parameters + * @CCORD_ret_obj{ret,message} + * @CCORD_return + */ +CCORDcode discord_edit_message(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + struct discord_edit_message *params, + struct discord_ret_message *ret); + +/** + * @brief Delete a message + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param message_id the message that will be purged of reactions from + * particular emoji + * @param params request parameters + * @CCORD_return + */ +CCORDcode discord_delete_message(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + struct discord_delete_message *params, + struct discord_ret *ret); + +/** + * @brief Delete multiple messages in a single request + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_bulk_delete_messages( + struct discord *client, + u64snowflake channel_id, + struct discord_bulk_delete_messages *params, + struct discord_ret *ret); + +/** + * @brief Edit the channel permission overwrites for a user or role in a + * channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param overwrite_id + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_edit_channel_permissions( + struct discord *client, + u64snowflake channel_id, + u64snowflake overwrite_id, + struct discord_edit_channel_permissions *params, + struct discord_ret *ret); + +/** + * @brief Get invites (with invite metadata) for the channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @CCORD_ret_obj{ret,invites} + * @CCORD_return + */ +CCORDcode discord_get_channel_invites(struct discord *client, + u64snowflake channel_id, + struct discord_ret_invites *ret); + +/** + * @brief Create a new invite for the channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the message belongs to + * @param params request parameters + * @CCORD_ret_obj{ret,invite} + * @CCORD_return + */ +CCORDcode discord_create_channel_invite( + struct discord *client, + u64snowflake channel_id, + struct discord_create_channel_invite *params, + struct discord_ret_invite *ret); + +/** + * @brief Delete a channel permission overwrite for a user or role in a + * channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to the permission deleted + * @param overwrite_id the id of the overwritten permission + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_channel_permission( + struct discord *client, + u64snowflake channel_id, + u64snowflake overwrite_id, + struct discord_delete_channel_permission *params, + struct discord_ret *ret); + +/** + * @brief Post a typing indicator for the specified channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to post the typing indicator to + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_trigger_typing_indicator(struct discord *client, + u64snowflake channel_id, + struct discord_ret *ret); + +/** + * @brief Follow a News Channel to send messages to a target channel + * @note Requires MANAGE_WEBHOOKS permission in the target channel + * MANAGE_WEBHOOKS permission in the target channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to be followed + * @CCORD_ret_obj{ret,followed_channel} + * @CCORD_return + */ +CCORDcode discord_follow_news_channel( + struct discord *client, + u64snowflake channel_id, + struct discord_follow_news_channel *params, + struct discord_ret_followed_channel *ret); + +/** + * @brief Get all pinned messages in the channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel where the get pinned messages from + * @CCORD_ret_obj{ret,messages} + * @CCORD_return + */ +CCORDcode discord_get_pinned_messages(struct discord *client, + u64snowflake channel_id, + struct discord_ret_messages *ret); + +/** + * @brief Pin a message to a channel + * + * @param client the client created with discord_from_token() + * @param channel_id channel to pin the message on + * @param message_id message to be pinned + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_pin_message(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + struct discord_pin_message *params, + struct discord_ret *ret); + +/** + * @brief Unpin a message from a channel + * + * @param client the client created with discord_from_token() + * @param channel_id channel for the message to be unpinned + * @param message_id message to be unpinned + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_unpin_message(struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + struct discord_unpin_message *params, + struct discord_ret *ret); + +/** + * @brief Adds a recipient to a Group DM using their access token + * + * @param client the client created with discord_from_token() + * @param channel_id group to add the user in + * @param user_id user to be added + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_group_dm_add_recipient( + struct discord *client, + u64snowflake channel_id, + u64snowflake user_id, + struct discord_group_dm_add_recipient *params, + struct discord_ret *ret); + +/** + * @brief Removes a recipient from a Group DM + * + * @param client the client created with discord_from_token() + * @param channel_id channel for the user to be removed from + * @param user_id user to be removed + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_group_dm_remove_recipient(struct discord *client, + u64snowflake channel_id, + u64snowflake user_id, + struct discord_ret *ret); + +/** + * @brief Creates a new thread from an existing message + * @note Fires a `Thread Create` event + * + * @param client the client created with discord_from_token() + * @param channel_id channel to start a thread on + * @param message_id message to start a thread from + * @param params request parameters + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_start_thread_with_message( + struct discord *client, + u64snowflake channel_id, + u64snowflake message_id, + struct discord_start_thread_with_message *params, + struct discord_ret_channel *ret); + +/** + * @brief Creates a new thread that is not connected to an existing message + * @note Fires a `Thread Create` event + * + * @param client the client created with discord_from_token() + * @param channel_id channel to start a thread on + * @param params request parameters + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_start_thread_without_message( + struct discord *client, + u64snowflake channel_id, + struct discord_start_thread_without_message *params, + struct discord_ret_channel *ret); + +/** + * @brief Adds the current user to an un-archived thread + * @note Fires a `Thread Members Update` event + * + * @param client the client created with discord_from_token() + * @param channel_id the thread to be joined + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_join_thread(struct discord *client, + u64snowflake channel_id, + struct discord_ret *ret); + +/** + * @brief Adds another member to an un-archived thread + * @note Fires a `Thread Members Update` event + * + * @param client the client created with discord_from_token() + * @param channel_id the thread to be joined + * @param user_id user to be added to thread + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_add_thread_member(struct discord *client, + u64snowflake channel_id, + u64snowflake user_id, + struct discord_ret *ret); + +/** + * @brief Removes the current user from a un-archived thread + * @note Fires a `Thread Members Update` event + * + * @param client the client created with discord_from_token() + * @param channel_id the thread to be removed from + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_leave_thread(struct discord *client, + u64snowflake channel_id, + struct discord_ret *ret); + +/** + * @brief Removes another member from a un-archived thread + * @note Fires a `Thread Members Update` event + * @note Requires `MANAGE_THREADS` permission + * + * @param client the client created with discord_from_token() + * @param channel_id the thread to be removed from + * @param user_id user to be removed + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_remove_thread_member(struct discord *client, + u64snowflake channel_id, + u64snowflake user_id, + struct discord_ret *ret); + +/** + * @brief Get members from a given thread channel + * @note Fires a `Thread Members Update` event + * @note Requires `MANAGE_THREADS` permission + * + * @param client the client created with discord_from_token() + * @param channel_id the thread to be joined + * @CCORD_ret_obj{ret,thread_members} + * @CCORD_return + */ +CCORDcode discord_list_thread_members(struct discord *client, + u64snowflake channel_id, + struct discord_ret_thread_members *ret); + +/** + * @brief Get public archived threads in a given channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to be searched for threads + * @param before return threads before this timestamp + * @param limit maximum number of threads to return + * @CCORD_ret_obj{ret,thread_response_body} + * @CCORD_return + */ +CCORDcode discord_list_public_archived_threads( + struct discord *client, + u64snowflake channel_id, + u64unix_ms before, + int limit, + struct discord_ret_thread_response_body *ret); + +/** + * @brief Get private archived threads in a given channel + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to be searched for threads + * @param before return threads before this timestamp + * @param limit maximum number of threads to return + * @CCORD_ret_obj{ret,thread_response_body} + * @CCORD_return + */ +CCORDcode discord_list_private_archived_threads( + struct discord *client, + u64snowflake channel_id, + u64unix_ms before, + int limit, + struct discord_ret_thread_response_body *ret); + +/** + * @brief Get private archived threads that current user has joined + * + * @param client the client created with discord_from_token() + * @param channel_id the channel to be searched for threads + * @param before return threads before this timestamp + * @param limit maximum number of threads to return + * @CCORD_ret_obj{ret,thread_response_body} + * @CCORD_return + */ +CCORDcode discord_list_joined_private_archived_threads( + struct discord *client, + u64snowflake channel_id, + u64unix_ms before, + int limit, + struct discord_ret_thread_response_body *ret); + +/** @defgroup DiscordAPIChannelHelper Helper functions + * @brief Custom helper functions + * @{ */ + +/** + * @brief Get a guild's channel from its given numerical position + * + * @param client the client created with discord_from_token() + * @param guild_id guild the channel belongs to + * @param type the channel type where to take position reference from + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_get_channel_at_pos(struct discord *client, + u64snowflake guild_id, + enum discord_channel_types type, + int position, + struct discord_ret_channel *ret); + +/** + * @brief Append to an overwrite list + * @note the list should be freed with `discord_overwrite_list_free()` after + * its no longer being used + * + * @param permission_overwrites list to be appended to + * @param id role or user id + * @param type either 0 (role) or 1 (member) + * @param allow permission bit set + * @param deny permission bit set + */ +void discord_overwrite_append(struct discord_overwrites *permission_overwrites, + u64snowflake id, + int type, + u64bitmask allow, + u64bitmask deny); + +/** @} DiscordAPIChannelHelper */ + +/** @example channel.c + * Demonstrates a couple use cases of the Channel API */ +/** @example embed.c + * Demonstrates embed manipulation */ +/** @example fetch-messages.c + * Demonstrates fetching user messages */ +/** @example manual-dm.c + * Demonstrates sending DMs with your client */ +/** @example pin.c + * Demonstrates pinning messages */ +/** @example reaction.c + * Demonstrates a couple use cases of the Channel reactions API */ + +/** @} DiscordAPIChannel */ + +#endif /* DISCORD_CHANNEL_H */ diff --git a/include/concord/chash.h b/include/concord/chash.h new file mode 100644 index 0000000..af09888 --- /dev/null +++ b/include/concord/chash.h @@ -0,0 +1,510 @@ +/* Copyright 2022 Cogmasters */ +/* + * C-Ware License + * + * Copyright (c) 2022, C-Ware + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Redistributions of modified source code must append a copyright notice in + * the form of 'Copyright ' to each modified source file's + * copyright notice, and the standalone license file if one exists. + * + * A 'redistribution' can be constituted as any version of the original source + * code material that is intended to comprise some other derivative work of + * this code. A fork created for the purpose of contributing to any version of + * the source does not constitute a truly 'derivative work' and does not require + * listing. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Modified by Lucas Müller (muller.lucas@hotmail.com), 16 May 2022 + * - add __chash_init() and __chash_free() as a non-malloc option */ + +#ifndef CWARE_LIBCHASH_H +#define CWARE_LIBCHASH_H + +#define CWARE_LIBCHASH_VERSION "x.0.0" + +/* How big heap-allocated hashtables are by default */ +#ifndef CHASH_INITIAL_SIZE +#define CHASH_INITIAL_SIZE 10 +#elif CHASH_INITIAL_SIZE <= 0 + "chash_init: default length must be greater than 0" +#endif + +/* Calculates the next size of the hashtable. */ +#ifndef CHASH_RESIZE +#define CHASH_RESIZE(size) \ + ((size) * 1.3) +#endif + +/* The threshold that, when passed, will cause a resize */ +#ifndef CHASH_LOAD_THRESHOLD +#define CHASH_LOAD_THRESHOLD 0.8 +#endif + +/* The type that is used for counters; useful for aligning hashtable + * length and capacity fields so type casting warnings do not appear */ +#ifndef CHASH_COUNTER_TYPE +#define CHASH_COUNTER_TYPE int +#endif + +/* The name of the key field */ +#ifndef CHASH_KEY_FIELD +#define CHASH_KEY_FIELD key +#endif + +/* The name of the value field */ +#ifndef CHASH_VALUE_FIELD +#define CHASH_VALUE_FIELD value +#endif + +/* The name of the state field */ +#ifndef CHASH_STATE_FIELD +#define CHASH_STATE_FIELD state +#endif + +/* The name of the buckets field */ +#ifndef CHASH_BUCKETS_FIELD +#define CHASH_BUCKETS_FIELD buckets +#endif + +/* The name of the length field */ +#ifndef CHASH_LENGTH_FIELD +#define CHASH_LENGTH_FIELD length +#endif + +/* The name of the capacity field */ +#ifndef CHASH_CAPACITY_FIELD +#define CHASH_CAPACITY_FIELD capacity +#endif + +/* State enums */ +#define CHASH_UNFILLED 0 +#define CHASH_FILLED 1 +#define CHASH_TOMBSTONE 2 + +/* Built-ins */ + +#define chash_string_hash(key, hash) \ + 5031; \ + do { \ + int __CHASH_HINDEX = 0; \ + \ + for(__CHASH_HINDEX = 0; (key)[__CHASH_HINDEX] != '\0'; \ + __CHASH_HINDEX++) { \ + (hash) = (((hash) << 1) + (hash)) + (key)[__CHASH_HINDEX]; \ + } \ + } while(0) + +#define chash_string_compare(cmp_a, cmp_b) \ + (strcmp((cmp_a), (cmp_b)) == 0) + +#define chash_default_init(bucket, _key, _value) \ + (bucket).CHASH_KEY_FIELD = (_key); \ + (bucket).CHASH_VALUE_FIELD = _value + + + + + + + + + + + + + + + + + + + + +/* utility macros */ + +#define __chash_abs(x) \ + ((x) < 0 ? (x) * - 1 : (x)) + +#define __chash_hash(mod, _key, namespace) \ + __CHASH_HASH = namespace ## _HASH((_key), __CHASH_HASH); \ + __CHASH_HASH = __CHASH_HASH % (mod); \ + __CHASH_HASH = __chash_abs(__CHASH_HASH); + +#define __chash_probe(hashtable, _key, namespace) \ + while(__CHASH_INDEX < (hashtable)->CHASH_CAPACITY_FIELD) { \ + if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD == \ + CHASH_UNFILLED) \ + break; \ + \ + if((namespace ## _COMPARE((_key), \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_KEY_FIELD)) == 1) { \ + \ + __CHASH_INDEX = -1; \ + break; \ + } \ + \ + __CHASH_HASH = (__CHASH_HASH + 1) % (hashtable)->CHASH_CAPACITY_FIELD; \ + __CHASH_INDEX++; \ + } \ + +#define __chash_probe_to_unfilled(mod, _key, buffer, namespace) \ + while(1) { \ + if(buffer[__CHASH_HASH].CHASH_STATE_FIELD != CHASH_FILLED) \ + break; \ + \ + if((namespace ## _COMPARE((_key), buffer[__CHASH_HASH].CHASH_KEY_FIELD)) \ + == 1) \ + break; \ + \ + __CHASH_HASH = (__CHASH_HASH + 1) % mod; \ + } \ + +#define __chash_resize(hashtable, namespace) \ +do { \ + CHASH_COUNTER_TYPE __CHASH_INDEX = 0; \ + namespace ## _BUCKET *__CHASH_BUCKETS = NULL; \ + CHASH_COUNTER_TYPE __CHASH_NEXT_SIZE = (CHASH_COUNTER_TYPE) \ + CHASH_RESIZE((hashtable)->CHASH_CAPACITY_FIELD); \ + \ + if((namespace ## _HEAP) == 0) { \ + if((hashtable)->CHASH_LENGTH_FIELD != \ + (hashtable)->CHASH_CAPACITY_FIELD) { \ + break; \ + } \ + \ + fprintf(stderr, "__chash_resize: hashtable is full. could not resize" \ + " (%s:%i)\n", __FILE__, __LINE__); \ + abort(); \ + } \ + \ + if((double) (hashtable)->CHASH_LENGTH_FIELD / \ + (double) (hashtable)->CHASH_CAPACITY_FIELD < CHASH_LOAD_THRESHOLD) \ + break; \ + \ + __CHASH_BUCKETS = malloc((size_t) (__CHASH_NEXT_SIZE \ + * ((CHASH_COUNTER_TYPE) \ + sizeof(namespace ## _BUCKET)))); \ + memset(__CHASH_BUCKETS, 0, ((size_t) (__CHASH_NEXT_SIZE \ + * ((CHASH_COUNTER_TYPE) \ + sizeof(namespace ## _BUCKET))))); \ + \ + for(__CHASH_INDEX = 0; __CHASH_INDEX < (hashtable)->CHASH_CAPACITY_FIELD; \ + __CHASH_INDEX++) { \ + namespace ## _BUCKET __CHASH_NEW_KEY_BUCKET; \ + memset(&__CHASH_NEW_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \ + namespace ## _INIT(__CHASH_NEW_KEY_BUCKET, \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_KEY_FIELD, \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_VALUE_FIELD); \ + \ + if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_STATE_FIELD \ + != CHASH_FILLED) \ + continue; \ + \ + __chash_hash(__CHASH_NEXT_SIZE, __CHASH_NEW_KEY_BUCKET.CHASH_KEY_FIELD, \ + namespace); \ + __chash_probe_to_unfilled(__CHASH_NEXT_SIZE, \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_KEY_FIELD, \ + __CHASH_BUCKETS, namespace) \ + \ + __CHASH_BUCKETS[__CHASH_HASH] = __CHASH_NEW_KEY_BUCKET; \ + __CHASH_BUCKETS[__CHASH_HASH].CHASH_STATE_FIELD = CHASH_FILLED; \ + __CHASH_HASH = 0; \ + } \ + \ + free((hashtable)->CHASH_BUCKETS_FIELD); \ + (hashtable)->CHASH_BUCKETS_FIELD = __CHASH_BUCKETS; \ + (hashtable)->CHASH_CAPACITY_FIELD = __CHASH_NEXT_SIZE; \ + __CHASH_HASH = 0; \ +} while(0) + +#define __chash_assert_nonnull(func, ptr) \ +do { \ + if((ptr) == NULL) { \ + fprintf(stderr, #func ": " #ptr " cannot be null (%s:%i)\n", \ + __FILE__, __LINE__); \ + abort(); \ + } \ +} while(0) + + + + + + + + + + + + + + + + +/* operations */ +#define __chash_init(hashtable, namespace) \ + (hashtable)->CHASH_LENGTH_FIELD = 0; \ + (hashtable)->CHASH_CAPACITY_FIELD = CHASH_INITIAL_SIZE; \ + (hashtable)->CHASH_BUCKETS_FIELD = malloc(CHASH_INITIAL_SIZE \ + * sizeof(*((hashtable)->CHASH_BUCKETS_FIELD))); \ + memset((hashtable)->CHASH_BUCKETS_FIELD, 0, \ + sizeof(*((hashtable)->CHASH_BUCKETS_FIELD)) * CHASH_INITIAL_SIZE) + +#define chash_init(hashtable, namespace) \ + NULL; \ + \ + (hashtable) = malloc(sizeof((*(hashtable)))); \ + __chash_init(hashtable, namespace) + +#define chash_init_stack(hashtable, buffer, _length, namespace) \ + (*(hashtable)); \ + \ + if((_length) <= 0) { \ + fprintf(stderr, "chash_init_stack: hashtable cannot have a maximum " \ + "length of 0 or less (%s:%i)\n", __FILE__, __LINE__); \ + abort(); \ + } \ + \ + __chash_assert_nonnull(chash_init_stack, buffer); \ + \ + (hashtable)->CHASH_LENGTH_FIELD = 0; \ + (hashtable)->CHASH_CAPACITY_FIELD = _length; \ + (hashtable)->CHASH_BUCKETS_FIELD = buffer + +#define chash_assign(hashtable, _key, _value, namespace) \ +do { \ + long __CHASH_HASH = 0; \ + namespace ## _BUCKET __CHASH_KEY_BUCKET; \ + memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \ + namespace ## _INIT(__CHASH_KEY_BUCKET, _key, _value); \ + \ + __chash_assert_nonnull(chash_assign, hashtable); \ + __chash_assert_nonnull(chash_assign, (hashtable)->CHASH_BUCKETS_FIELD); \ + __chash_resize(hashtable, namespace); \ + __chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \ + __chash_probe_to_unfilled((hashtable)->CHASH_CAPACITY_FIELD, \ + (_key), (hashtable)->CHASH_BUCKETS_FIELD, namespace) \ + \ + if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD == \ + CHASH_FILLED) { \ + namespace ## _FREE_VALUE( \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD); \ + } else { \ + (hashtable)->CHASH_LENGTH_FIELD++; \ + } \ + \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH] = __CHASH_KEY_BUCKET; \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD = \ + CHASH_FILLED; \ +} while(0) + +#define chash_lookup(hashtable, _key, storage, namespace) \ +storage; \ + \ +do { \ + int __CHASH_INDEX = 0; \ + long __CHASH_HASH = 0; \ + namespace ## _BUCKET __CHASH_KEY_BUCKET; \ + memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \ + namespace ## _INIT(__CHASH_KEY_BUCKET, _key, \ + __CHASH_KEY_BUCKET.CHASH_VALUE_FIELD); \ + \ + (void) __CHASH_KEY_BUCKET; \ + \ + __chash_assert_nonnull(chash_lookup, hashtable); \ + __chash_assert_nonnull(chash_lookup, (hashtable)->CHASH_BUCKETS_FIELD); \ + __chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \ + __chash_probe(hashtable, _key, namespace) \ + \ + if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \ + CHASH_FILLED) || __CHASH_INDEX != -1) { \ + fprintf(stderr, "chash_lookup: failed to find key in hashtable (%s:%i)" \ + "\n", __FILE__, __LINE__); \ + abort(); \ + } \ + \ + storage = (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD; \ +} while(0) + +#define chash_delete(hashtable, _key, namespace) \ +do { \ + int __CHASH_INDEX = 0; \ + long __CHASH_HASH = 0; \ + \ + __chash_assert_nonnull(chash_delete, hashtable); \ + __chash_assert_nonnull(chash_delete, (hashtable)->CHASH_BUCKETS_FIELD); \ + __chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \ + __chash_probe(hashtable, _key, namespace) \ + \ + if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \ + CHASH_FILLED) || __CHASH_INDEX != -1) { \ + fprintf(stderr, "chash_delete: failed to find key in hashtable (%s:%i)" \ + "\n", __FILE__, __LINE__); \ + abort(); \ + } \ + \ + namespace ## _FREE_KEY((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH] \ + .CHASH_KEY_FIELD); \ + namespace ## _FREE_VALUE( \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD); \ + (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD = \ + CHASH_TOMBSTONE; \ + (hashtable)->CHASH_LENGTH_FIELD--; \ +} while(0) + +#define chash_contains(hashtable, _key, storage, namespace) \ +1; \ + \ +do { \ + int __CHASH_INDEX = 0; \ + long __CHASH_HASH = 0; \ + \ + __chash_assert_nonnull(chash_contents, hashtable); \ + __chash_assert_nonnull(chash_contents, (hashtable)->CHASH_BUCKETS_FIELD); \ + __chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \ + __chash_probe(hashtable, _key, namespace) \ + \ + if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \ + CHASH_FILLED) || __CHASH_INDEX != -1) { \ + storage = 0; \ + } \ +} while(0) + +#define chash_lookup_bucket(hashtable, _key, storage, namespace) \ +storage; \ + \ +do { \ + CHASH_COUNTER_TYPE __CHASH_INDEX = 0; \ + long __CHASH_HASH = 0; \ + namespace ## _BUCKET __CHASH_KEY_BUCKET; \ + memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \ + namespace ## _INIT(__CHASH_KEY_BUCKET, _key, \ + __CHASH_KEY_BUCKET.CHASH_VALUE_FIELD); \ + \ + (void) __CHASH_KEY_BUCKET; \ + \ + __chash_assert_nonnull(chash_lookup_bucket, hashtable); \ + __chash_assert_nonnull(chash_lookup_bucket, \ + (hashtable)->CHASH_BUCKETS_FIELD); \ + __chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \ + __chash_probe(hashtable, _key, namespace) \ + \ + if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \ + CHASH_FILLED) || __CHASH_INDEX != -1) { \ + fprintf(stderr, "chash_lookup_bucket: failed to find key in hashtable" \ + "(%s:%i) \n", __FILE__, __LINE__); \ + abort(); \ + } \ + \ + storage = ((hashtable)->CHASH_BUCKETS_FIELD + __CHASH_HASH); \ +} while(0) + +#define __chash_free(hashtable, namespace) \ +do { \ + __chash_assert_nonnull(__chash_free, hashtable); \ + __chash_assert_nonnull(__chash_free, (hashtable)->CHASH_BUCKETS_FIELD); \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + \ + while((hashtable)->CHASH_CAPACITY_FIELD != -1) { \ + if((hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_STATE_FIELD != CHASH_FILLED) { \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + continue; \ + } \ + \ + namespace ##_FREE_KEY( \ + (hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_KEY_FIELD); \ + namespace ##_FREE_VALUE( \ + (hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_VALUE_FIELD); \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + (hashtable)->CHASH_LENGTH_FIELD--; \ + } \ + \ + if((namespace ## _HEAP) == 1) { \ + free((hashtable)->CHASH_BUCKETS_FIELD); \ + } \ +} while(0) + +#define chash_free(hashtable, namespace) \ +do { \ + __chash_assert_nonnull(chash_free, hashtable); \ + __chash_assert_nonnull(chash_free, (hashtable)->CHASH_BUCKETS_FIELD); \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + \ + while((hashtable)->CHASH_CAPACITY_FIELD != -1) { \ + if((hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_STATE_FIELD != CHASH_FILLED) { \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + continue; \ + } \ + \ + namespace ##_FREE_KEY( \ + (hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_KEY_FIELD); \ + namespace ##_FREE_VALUE( \ + (hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_VALUE_FIELD); \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + (hashtable)->CHASH_LENGTH_FIELD--; \ + } \ + \ + if((namespace ## _HEAP) == 1) { \ + free((hashtable)->CHASH_BUCKETS_FIELD); \ + free((hashtable)); \ + } \ +} while(0) + +#define chash_is_full(hashtable, namespace) \ + (((hashtable)->CHASH_LENGTH_FIELD) == ((hashtable)->CHASH_CAPACITY_FIELD)) + + + + + + + + + +/* Iterator logic */ +#define chash_iter(hashtable, index, _key, _value) \ + for((index) = 0, (_key) = (hashtable)->CHASH_BUCKETS_FIELD[index]. \ + CHASH_KEY_FIELD, \ + (_value) = (hashtable)->CHASH_BUCKETS_FIELD[index].CHASH_VALUE_FIELD; \ + (index) < (hashtable)->CHASH_CAPACITY_FIELD; \ + (index) = ((index) < (hashtable)->CHASH_CAPACITY_FIELD) \ + ? ((index) + 1) : index, \ + (_key) = (hashtable)->CHASH_BUCKETS_FIELD[index].CHASH_KEY_FIELD, \ + (_value) = (hashtable)->CHASH_BUCKETS_FIELD[index].CHASH_VALUE_FIELD, \ + (index) = (hashtable)->CHASH_CAPACITY_FIELD) + +#define chash_skip(hashtable, index) \ + if((hashtable)->CHASH_BUCKETS_FIELD[index]. \ + CHASH_STATE_FIELD != CHASH_FILLED) \ + continue; + +#endif diff --git a/include/concord/clock.h b/include/concord/clock.h new file mode 100644 index 0000000..5e6bb3c --- /dev/null +++ b/include/concord/clock.h @@ -0,0 +1,464 @@ +/* Clocks (v1) + * Portable Snippets - https://github.com/nemequ/portable-snippets + * Created by Evan Nemerson + * + * To the extent possible under law, the authors have waived all + * copyright and related or neighboring rights to this code. For + * details, see the Creative Commons Zero 1.0 Universal license at + * https://creativecommons.org/publicdomain/zero/1.0/ + */ + +#if !defined(PSNIP_CLOCK_H) +#define PSNIP_CLOCK_H + +/* For maximum portability include the exact-int module from + portable snippets. */ +#if !defined(psnip_uint64_t) || !defined(psnip_int32_t) || \ + !defined(psnip_uint32_t) || !defined(psnip_int32_t) +# include +# if !defined(psnip_int64_t) +# define psnip_int64_t int64_t +# endif +# if !defined(psnip_uint64_t) +# define psnip_uint64_t uint64_t +# endif +# if !defined(psnip_int32_t) +# define psnip_int32_t int32_t +# endif +# if !defined(psnip_uint32_t) +# define psnip_uint32_t uint32_t +# endif +#endif + +#if !defined(PSNIP_CLOCK_STATIC_INLINE) +# if defined(__GNUC__) +# define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__)) +# else +# define PSNIP_CLOCK__COMPILER_ATTRIBUTES +# endif + +# define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static +#endif + +enum PsnipClockType { + /* This clock provides the current time, in units since 1970-01-01 + * 00:00:00 UTC not including leap seconds. In other words, UNIX + * time. Keep in mind that this clock doesn't account for leap + * seconds, and can go backwards (think NTP adjustments). */ + PSNIP_CLOCK_TYPE_WALL = 1, + /* The CPU time is a clock which increases only when the current + * process is active (i.e., it doesn't increment while blocking on + * I/O). */ + PSNIP_CLOCK_TYPE_CPU = 2, + /* Monotonic time is always running (unlike CPU time), but it only + ever moves forward unless you reboot the system. Things like NTP + adjustments have no effect on this clock. */ + PSNIP_CLOCK_TYPE_MONOTONIC = 3 +}; + +struct PsnipClockTimespec { + psnip_uint64_t seconds; + psnip_uint64_t nanoseconds; +}; + +/* Methods we support: */ + +#define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1 +#define PSNIP_CLOCK_METHOD_TIME 2 +#define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3 +#define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4 +#define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5 +#define PSNIP_CLOCK_METHOD_CLOCK 6 +#define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7 +#define PSNIP_CLOCK_METHOD_GETRUSAGE 8 +#define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9 +#define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10 + +#include + +#if defined(HEDLEY_UNREACHABLE) +# define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE() +#else +# define PSNIP_CLOCK_UNREACHABLE() assert(0) +#endif + +/* Choose an implementation */ + +/* #undef PSNIP_CLOCK_WALL_METHOD */ +/* #undef PSNIP_CLOCK_CPU_METHOD */ +/* #undef PSNIP_CLOCK_MONOTONIC_METHOD */ + +/* We want to be able to detect the libc implementation, so we include + ( isn't available everywhere). */ +#if defined(__unix__) || defined(__unix) || defined(__linux__) +# include +# include +#endif + +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) +/* glibc 2.17+ and FreeBSD are known to work without librt. If you + * know of others please let us know so we can add them. */ +# if \ + (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \ + (defined(__FreeBSD__)) || \ + !defined(PSNIP_CLOCK_NO_LIBRT) +/* Even though glibc unconditionally sets _POSIX_TIMERS, it doesn't + actually declare the relevant APIs unless _POSIX_C_SOURCE >= + 199309L, and if you compile in standard C mode (e.g., c11 instead + of gnu11) _POSIX_C_SOURCE will be unset by default. */ +# if _POSIX_C_SOURCE >= 199309L +# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME +# endif +# endif +#endif + +#if defined(_WIN32) +# if !defined(PSNIP_CLOCK_CPU_METHOD) +# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES +# endif +# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) +# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER +# endif +#endif + +#if defined(__MACH__) && !defined(__gnu_hurd__) +# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) +# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME +# endif +#endif + +#if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME) +# include +# if !defined(PSNIP_CLOCK_WALL_METHOD) +# if defined(CLOCK_REALTIME_PRECISE) +# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE +# elif !defined(__sun) +# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME +# endif +# endif +# if !defined(PSNIP_CLOCK_CPU_METHOD) +# if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID) +# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID +# elif defined(CLOCK_VIRTUAL) +# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL +# endif +# endif +# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) +# if defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC) +# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC +# endif +# endif +#endif + +#if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L) +# if !defined(PSNIP_CLOCK_WALL_METHOD) +# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY +# endif +#endif + +#if !defined(PSNIP_CLOCK_WALL_METHOD) +# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME +#endif + +#if !defined(PSNIP_CLOCK_CPU_METHOD) +# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK +#endif + +/* Primarily here for testing. */ +#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && defined(PSNIP_CLOCK_REQUIRE_MONOTONIC) +# error No monotonic clock found. +#endif + +/* Implementations */ + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME)) +# include +#endif + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) +# include +#endif + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) +# include +#endif + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) +# include +# include +#endif + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) +# include +# include +# include +#endif + +/*** Implementations ***/ + +#define PSNIP_CLOCK_NSEC_PER_SEC ((psnip_uint32_t) (1000000000ULL)) + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock__clock_getres (clockid_t clk_id) { + struct timespec res; + int r; + + r = clock_getres(clk_id, &res); + if (r != 0) + return 0; + + return (psnip_uint32_t) (PSNIP_CLOCK_NSEC_PER_SEC / res.tv_nsec); +} + +PSNIP_CLOCK__FUNCTION int +psnip_clock__clock_gettime (clockid_t clk_id, struct PsnipClockTimespec* res) { + struct timespec ts; + + if (clock_gettime(clk_id, &ts) != 0) + return -10; + + res->seconds = (psnip_uint64_t) (ts.tv_sec); + res->nanoseconds = (psnip_uint64_t) (ts.tv_nsec); + + return 0; +} +#endif + +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock_wall_get_precision (void) { +#if !defined(PSNIP_CLOCK_WALL_METHOD) + return 0; +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL); +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY + return 1000000; +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME + return 1; +#else + return 0; +#endif +} + +PSNIP_CLOCK__FUNCTION int +psnip_clock_wall_get_time (struct PsnipClockTimespec* res) { + (void) res; + +#if !defined(PSNIP_CLOCK_WALL_METHOD) + return -2; +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res); +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME + res->seconds = (uint64_t) time(NULL); + res->nanoseconds = 0; +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) + return -6; + + res->seconds = tv.tv_sec; + res->nanoseconds = tv.tv_usec * 1000; +#else + return -2; +#endif + + return 0; +} + +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock_cpu_get_precision (void) { +#if !defined(PSNIP_CLOCK_CPU_METHOD) + return 0; +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU); +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK + return CLOCKS_PER_SEC; +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES + return PSNIP_CLOCK_NSEC_PER_SEC / 100; +#else + return 0; +#endif +} + +PSNIP_CLOCK__FUNCTION int +psnip_clock_cpu_get_time (struct PsnipClockTimespec* res) { +#if !defined(PSNIP_CLOCK_CPU_METHOD) + (void) res; + return -2; +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res); +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK + clock_t t = clock(); + if (t == ((clock_t) -1)) + return -5; + res->seconds = t / CLOCKS_PER_SEC; + res->nanoseconds = (t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC); +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES + FILETIME CreationTime, ExitTime, KernelTime, UserTime; + LARGE_INTEGER date, adjust; + + if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime)) + return -7; + + /* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */ + date.HighPart = UserTime.dwHighDateTime; + date.LowPart = UserTime.dwLowDateTime; + adjust.QuadPart = 11644473600000 * 10000; + date.QuadPart -= adjust.QuadPart; + + res->seconds = date.QuadPart / 10000000; + res->nanoseconds = (date.QuadPart % 10000000) * (PSNIP_CLOCK_NSEC_PER_SEC / 100); +#elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) != 0) + return -8; + + res->seconds = usage.ru_utime.tv_sec; + res->nanoseconds = tv.tv_usec * 1000; +#else + (void) res; + return -2; +#endif + + return 0; +} + +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock_monotonic_get_precision (void) { +#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) + return 0; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC); +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME + static mach_timebase_info_data_t tbi = { 0, }; + if (tbi.denom == 0) + mach_timebase_info(&tbi); + return (psnip_uint32_t) (tbi.numer / tbi.denom); +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 + return 1000; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER + LARGE_INTEGER Frequency; + QueryPerformanceFrequency(&Frequency); + return (psnip_uint32_t) ((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) ? PSNIP_CLOCK_NSEC_PER_SEC : Frequency.QuadPart); +#else + return 0; +#endif +} + +PSNIP_CLOCK__FUNCTION int +psnip_clock_monotonic_get_time (struct PsnipClockTimespec* res) { +#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) + (void) res; + return -2; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res); +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME + psnip_uint64_t nsec = mach_absolute_time(); + static mach_timebase_info_data_t tbi = { 0, }; + if (tbi.denom == 0) + mach_timebase_info(&tbi); + nsec *= ((psnip_uint64_t) tbi.numer) / ((psnip_uint64_t) tbi.denom); + res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC; + res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER + LARGE_INTEGER t, f; + if (QueryPerformanceCounter(&t) == 0) + return -12; + + QueryPerformanceFrequency(&f); + res->seconds = t.QuadPart / f.QuadPart; + res->nanoseconds = t.QuadPart % f.QuadPart; + if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) + res->nanoseconds /= f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC; + else + res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / f.QuadPart; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 + const ULONGLONG msec = GetTickCount64(); + res->seconds = msec / 1000; + res->nanoseconds = sec % 1000; +#else + return -2; +#endif + + return 0; +} + +/* Returns the number of ticks per second for the specified clock. + * For example, a clock with millisecond precision would return 1000, + * and a clock with 1 second (such as the time() function) would + * return 1. + * + * If the requested clock isn't available, it will return 0. + * Hopefully this will be rare, but if it happens to you please let us + * know so we can work on finding a way to support your system. + * + * Note that different clocks on the same system often have a + * different precisions. + */ +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock_get_precision (enum PsnipClockType clock_type) { + switch (clock_type) { + case PSNIP_CLOCK_TYPE_MONOTONIC: + return psnip_clock_monotonic_get_precision (); + case PSNIP_CLOCK_TYPE_CPU: + return psnip_clock_cpu_get_precision (); + case PSNIP_CLOCK_TYPE_WALL: + return psnip_clock_wall_get_precision (); + } + + PSNIP_CLOCK_UNREACHABLE(); + return 0; +} + +/* Set the provided timespec to the requested time. Returns 0 on + * success, or a negative value on failure. */ +PSNIP_CLOCK__FUNCTION int +psnip_clock_get_time (enum PsnipClockType clock_type, struct PsnipClockTimespec* res) { + assert(res != NULL); + + switch (clock_type) { + case PSNIP_CLOCK_TYPE_MONOTONIC: + return psnip_clock_monotonic_get_time (res); + case PSNIP_CLOCK_TYPE_CPU: + return psnip_clock_cpu_get_time (res); + case PSNIP_CLOCK_TYPE_WALL: + return psnip_clock_wall_get_time (res); + } + + return -1; +} + +#endif /* !defined(PSNIP_CLOCK_H) */ diff --git a/include/concord/cog-utils.h b/include/concord/cog-utils.h new file mode 100644 index 0000000..f5bac87 --- /dev/null +++ b/include/concord/cog-utils.h @@ -0,0 +1,137 @@ +#ifndef COG_UTILS_H +#define COG_UTILS_H + +#include +#include +#include "attributes.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Load file contents into a string + * + * @param fp the file to be read + * @param len optional pointer to store the amount of bytes read + * @return the file contents + */ +char *cog_load_whole_file_fp(FILE *fp, size_t *len); +/** + * @brief Load file contents into a string + * + * Wrapper over cog_load_whole_file_fp(), get the file by its + * relative-path. + * @param filename the name of the file to be read + * @param len optional pointer to store the amount of bytes read + * @return the file contents + */ +char *cog_load_whole_file(const char filename[], size_t *len); + +/** + * @brief Get the difference between UTC and the latest local standard time, in + * seconds. + * @return difference between UTC and local time in seconds + */ +long cog_timezone(void); + +/** + * @brief Convert a iso8601 string to a unix timestamp (milliseconds) + * + * Can be matched to the json_extract() and json_inject() %F specifier + * @param str the iso8601 string timestamp + * @param len the string length + * @param p_value pointer to the `uint64_t` variable to receive the converted + * timestamp + * @return 1 on success, 0 on failure + */ +int cog_iso8601_to_unix_ms(const char str[], size_t len, uint64_t *p_value); + +/** + * @brief Convert a unix timestamp (milliseconds) to a iso8601 string + * + * @param timestamp the buffer to receive the converted timestamp + * @param len the size of the buffer + * @param value the unix timestamp to be converted to iso8601 + * @return the amount of characters (in bytes) written to the buffer + */ +int cog_unix_ms_to_iso8601(char str[], size_t len, const uint64_t value); + +/** + * @brief Convert a numerical string to `uint64_t` + * + * @param str the numerical string + * @param len the string length + * @param p_value pointer to the `uint64_t` variable to receive the converted + * value + * @return 1 on success, 0 on failure + */ +int cog_strtou64(char *str, size_t len, uint64_t *p_value); + +/** + * @brief Convert `uint64_t` to a numerical string + * + * @param str the buffer to store the numerical string + * @param len the size of the buffer + * @param p_value the `unsigned long long` value + * @return the amount of characters (in bytes) written to the buffer + */ +int cog_u64tostr(char *str, size_t len, uint64_t *p_value); + +/** + * @brief Copies at most `len` bytes of `src` to `*p_dest`. + * + * Analogous to `strndup()` + * @param src the buffer to be copied + * @param len the maximum amount of characters to be copied + * @param p_dest a pointer to the new `src` copy + * @return length of copied string on success, 0 on failure + */ +size_t cog_strndup(const char src[], size_t len, char **p_dest); + +/** + * @brief Copies at most `len` bytes of `src` to `*p_dest`. + * + * Analogous to `asprintf()` + * @param strp source to write resulting string to + * @param fmt printf format string + * @param ... variadic arguments to be matched to `fmt` specifiers + * @return length of copied string on success, -1 on failure + */ +size_t cog_asprintf(char **strp, const char fmt[], ...) PRINTF_LIKE(2, 3); + +/** + * @brief Sleep for amount of milliseconds + * + * @param tms amount of milliseconds to sleep for + * @return 0 on success, -1 on error with an `errno` set to indicate the error + */ +int cog_sleep_ms(const long tms); + +/** + * @brief Sleep for amount of microseconds + * + * @param tms amount of microseconds to sleep for + * @return 0 on success, -1 on error with an `errno` set to indicate the error + */ +int cog_sleep_us(const long tms); + +/** + * @brief Get the current timestamp in milliseconds + * + * @return the timestamp on success, 0 on failure + */ +uint64_t cog_timestamp_ms(void); + +/** + * @brief Get the current timestamp in microseconds + * + * @return the timestamp on success, 0 on failure + */ +uint64_t cog_timestamp_us(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* COG_UTILS_H */ diff --git a/include/concord/concord-error.h b/include/concord/concord-error.h new file mode 100644 index 0000000..f0b7758 --- /dev/null +++ b/include/concord/concord-error.h @@ -0,0 +1,69 @@ +/** @file error.h */ + +#ifndef CONCORD_ERROR_H +#define CONCORD_ERROR_H + +/** @defgroup ConcordError Error handling + * @brief Concord error codes and meaning + * @{ */ + +/** @brief Concord error codes */ +typedef int CCORDcode; + +/* XXX: As new values are added, ccord_strerror() and ccord_code_as_string() + * should be updated accordingly! */ +/** @defgroup CoreError Core error codes + * @brief These codes are used by the core library and should be used by all + * modules + * @ + * @{ */ + +/** most likely a bug in the library, please report it */ +#define CCORD_INTERNAL_ERROR -200 +/** couldn't encode format */ +#define CCORD_BAD_ENCODE -101 +/** couldn't decode format */ +#define CCORD_BAD_DECODE -100 +/** out of memory, something really bad happened! */ +#define CCORD_OUT_OF_MEMORY -60 +/** check strerror() for more information */ +#define CCORD_ERRNO -50 +/** curl has been compiled without the --enable-websockets flag */ +#define CCORD_CURL_WEBSOCKETS_MISSING -14 +/** curl need to be updated to 8.7.1 or greater */ +#define CCORD_CURL_OUTDATED_VERSION -13 +/** failure when creating request's payload */ +#define CCORD_MALFORMED_PAYLOAD -12 +/** couldn't enqueue worker thread (queue is full) */ +#define CCORD_FULL_WORKER -11 +/** couldn't perform action because resource is unavailable */ +#define CCORD_RESOURCE_UNAVAILABLE -10 +/** couldn't cleanup resource automatically due to being claimed */ +#define CCORD_RESOURCE_OWNERSHIP -9 +/** attempt to initialize globals more than once */ +#define CCORD_GLOBAL_INIT -8 +/** curl's multi handle internal error */ +#define CCORD_CURLM_INTERNAL -7 +/** curl's easy handle internal error */ +#define CCORD_CURLE_INTERNAL -6 +/** internal failure when encoding or decoding JSON */ +#define CCORD_BAD_JSON -5 +/** bad value for parameter */ +#define CCORD_BAD_PARAMETER -4 +/** received a non-standard http code */ +#define CCORD_UNUSUAL_HTTP_CODE -3 +/** no response came through from curl */ +#define CCORD_CURL_NO_RESPONSE -2 +/** request wasn't succesful */ +#define CCORD_HTTP_CODE -1 +/** action was a success */ +#define CCORD_OK 0 + +const char *ccord_code_as_string(CCORDcode code); +const char *ccord_strerror(CCORDcode code); + +/** @} CoreError */ + +/** @} ConcordError */ + +#endif /* CONCORD_ERROR_H */ diff --git a/include/concord/concord-notifier.h b/include/concord/concord-notifier.h new file mode 100644 index 0000000..ea31d67 --- /dev/null +++ b/include/concord/concord-notifier.h @@ -0,0 +1,50 @@ +/** + * @file concord-notifier.h + * @author Cogmasters + * @brief Notifier fds listening to pipe, this can be used to propagate events + */ + +#ifndef CONCORD_NOTIFIER_H +#define CONCORD_NOTIFIER_H + +#include "concord-error.h" + +/** + * @brief Open notifier pipe + * + * @param pipe The pipe to open for emitting notifications + * @return CCORDcode on success, CCORD_ERRNO on error + */ +CCORDcode ccord_notifier_open(int pipe[2]); + +/** + * @brief Close notifier pipe + * + * @param pipe The pipe to close + */ +void ccord_notifier_close(int pipe[2]); + +/** + * @brief Notify fds listening to pipe + * + * @param pipe The pipe to notify + */ +void ccord_notifier_notify(int pipe[2]); + +/** + * @brief Whether or not pipe is currently notifying fds + * + * @param pipe The pipe to check + * @return 1 if notifying, 0 if not + */ +_Bool ccord_notifier_is_notifying(int pipe[2]); + +/** + * @brief Receive a listener for pipe notifications + * + * @param pipe The pipe to listen to + * @return fd on success, -1 on error + */ +int ccord_notifier_listen(const int pipe[2]); + +#endif /* CONCORD_NOTIFIER_H */ diff --git a/include/concord/concord-once.h b/include/concord/concord-once.h new file mode 100644 index 0000000..9d52348 --- /dev/null +++ b/include/concord/concord-once.h @@ -0,0 +1,49 @@ +/** + * @file concord-once.h + * @author Cogmasters + * @brief Initialized once + */ + +#ifndef CONCORD_ONCE_H +#define CONCORD_ONCE_H + +#include "concord-error.h" + +/** Callback function type for user initialization */ +typedef CCORDcode (*ccord_once_cb)(long flags); + +/** + * @brief Register a user callback for initialization + * + * This callback will be executed exactly once during initialization + * after all internal initializations have been performed. + * Multiple callbacks can be registered from different modules, and they will + * be executed in the order they were registered. + * + * @param callback The function to call during initialization + * @param flags Flag to pass to the callback, that will be passed to the + * callback function + * @CCORD_return + */ +CCORDcode ccord_once_set_callback(ccord_once_cb callback, long flags); + +/** + * @brief Initialize context once + * + * @param once Pointer to a static boolean flag that will be set to true if the + * initialization was successful. This flag should be used to ensure that + * the initialization is only performed once. + * @CCORD_return + */ +CCORDcode ccord_once(_Bool *once); + +/** + * @brief Cleanup once context + * + * This function will be called to cleanup the once context. + * It will be called when the program is exiting, and it will + * cleanup all registered callbacks. + */ +void ccord_once_cleanup(void); + +#endif /* CONCORD_ONCE_H */ diff --git a/include/concord/discord-cache.h b/include/concord/discord-cache.h new file mode 100644 index 0000000..46102b6 --- /dev/null +++ b/include/concord/discord-cache.h @@ -0,0 +1,51 @@ +/** + * @file discord-cache.h + * @author Cogmasters + * @brief Caching of Discord resources + */ + +#ifndef DISCORD_CACHE_H +#define DISCORD_CACHE_H + +/** @defgroup DiscordClientCache Caching + * @ingroup DiscordClient + * @brief Caching API supported by Concord + * @{ */ + +enum discord_cache_options { + DISCORD_CACHE_MESSAGES = 1 << 0, + DISCORD_CACHE_GUILDS = 1 << 1, +}; + +void discord_cache_enable(struct discord *client, + enum discord_cache_options options); + +/** + * @brief Get a message from cache, only if locally available in RAM + * @note When done, discord_unclaim() must be called on the message resource + * + * @param client the client initialized with discord_from_token() + * @param channel_id the channel id the message is in + * @param message_id the id of the message + * @return `NULL` if not found, or a cache'd message + */ +const struct discord_message *discord_cache_get_channel_message( + struct discord *client, u64snowflake channel_id, u64snowflake message_id); + +/** + * @brief Get a guild from cache, only if locally available in RAM + * @note When done, discord_unclaim() must be called on the guild resource + * + * @param client the client initialized with discord_from_token() + * @param guild_id the id of the guild + * @return `NULL` if not found, or a cache'd guild + */ +const struct discord_guild *discord_cache_get_guild(struct discord *client, + u64snowflake guild_id); + +/** @example cache.c + * Demonstrates cache usage */ + +/** @} DiscordClientCache */ + +#endif /* DISCORD_CACHE_H */ diff --git a/include/concord/discord-events.h b/include/concord/discord-events.h new file mode 100644 index 0000000..ab468ce --- /dev/null +++ b/include/concord/discord-events.h @@ -0,0 +1,989 @@ +/** + * @file discord-events.h + * @author Cogmasters + * @brief Listen, react and trigger Discord Gateway events + */ + +#ifndef DISCORD_EVENTS_H +#define DISCORD_EVENTS_H + +/** @defgroup DiscordCommands Commands + * @ingroup DiscordClient + * @brief Requests made by the client to the Gateway socket + * @{ */ + +/** + * @brief Request all members for a guild or a list of guilds + * @see + * https://discord.com/developers/docs/topics/gateway#request-guild-members + * + * @param client the client created with discord_from_token() + * @param request request guild members information + */ +void discord_request_guild_members( + struct discord *client, struct discord_request_guild_members *request); + +/** + * @brief Sent when a client wants to join, move or disconnect from a voice + * channel + * + * @param client the client created with discord_from_token() + * @param update request guild members information + */ +void discord_update_voice_state(struct discord *client, + struct discord_update_voice_state *update); + +/** + * @brief Update the client presence status + * @see discord_presence_add_activity() + * + * @param client the client created with discord_from_token() + * @param presence status to update the client's to + */ +void discord_update_presence(struct discord *client, + struct discord_presence_update *presence); + +/** @} DiscordCommands */ + +/** @defgroup DiscordEvents Events + * @ingroup DiscordClient + * @brief Events sent over the Gateway socket to the client + * @{ */ + +/** @brief Discord Gateway's events */ +enum discord_gateway_events { + DISCORD_EV_NONE = 0, /**< missing event */ + DISCORD_EV_READY, + DISCORD_EV_RESUMED, + DISCORD_EV_RECONNECT, + DISCORD_EV_INVALID_SESSION, + DISCORD_EV_APPLICATION_COMMAND_PERMISSIONS_UPDATE, + DISCORD_EV_AUTO_MODERATION_RULE_CREATE, + DISCORD_EV_AUTO_MODERATION_RULE_UPDATE, + DISCORD_EV_AUTO_MODERATION_RULE_DELETE, + DISCORD_EV_AUTO_MODERATION_ACTION_EXECUTION, + DISCORD_EV_CHANNEL_CREATE, + DISCORD_EV_CHANNEL_UPDATE, + DISCORD_EV_CHANNEL_DELETE, + DISCORD_EV_CHANNEL_PINS_UPDATE, + DISCORD_EV_THREAD_CREATE, + DISCORD_EV_THREAD_UPDATE, + DISCORD_EV_THREAD_DELETE, + DISCORD_EV_THREAD_LIST_SYNC, + DISCORD_EV_THREAD_MEMBER_UPDATE, + DISCORD_EV_THREAD_MEMBERS_UPDATE, + DISCORD_EV_GUILD_CREATE, + DISCORD_EV_GUILD_UPDATE, + DISCORD_EV_GUILD_DELETE, + DISCORD_EV_GUILD_BAN_ADD, + DISCORD_EV_GUILD_BAN_REMOVE, + DISCORD_EV_GUILD_EMOJIS_UPDATE, + DISCORD_EV_GUILD_STICKERS_UPDATE, + DISCORD_EV_GUILD_INTEGRATIONS_UPDATE, + DISCORD_EV_GUILD_MEMBER_ADD, + DISCORD_EV_GUILD_MEMBER_REMOVE, + DISCORD_EV_GUILD_MEMBER_UPDATE, + DISCORD_EV_GUILD_MEMBERS_CHUNK, + DISCORD_EV_GUILD_ROLE_CREATE, + DISCORD_EV_GUILD_ROLE_UPDATE, + DISCORD_EV_GUILD_ROLE_DELETE, + DISCORD_EV_GUILD_SCHEDULED_EVENT_CREATE, + DISCORD_EV_GUILD_SCHEDULED_EVENT_UPDATE, + DISCORD_EV_GUILD_SCHEDULED_EVENT_DELETE, + DISCORD_EV_GUILD_SCHEDULED_EVENT_USER_ADD, + DISCORD_EV_GUILD_SCHEDULED_EVENT_USER_REMOVE, + DISCORD_EV_INTEGRATION_CREATE, + DISCORD_EV_INTEGRATION_UPDATE, + DISCORD_EV_INTEGRATION_DELETE, + DISCORD_EV_INTERACTION_CREATE, + DISCORD_EV_INVITE_CREATE, + DISCORD_EV_INVITE_DELETE, + DISCORD_EV_MESSAGE_CREATE, + DISCORD_EV_MESSAGE_UPDATE, + DISCORD_EV_MESSAGE_DELETE, + DISCORD_EV_MESSAGE_DELETE_BULK, + DISCORD_EV_MESSAGE_REACTION_ADD, + DISCORD_EV_MESSAGE_REACTION_REMOVE, + DISCORD_EV_MESSAGE_REACTION_REMOVE_ALL, + DISCORD_EV_MESSAGE_REACTION_REMOVE_EMOJI, + DISCORD_EV_PRESENCE_UPDATE, + DISCORD_EV_STAGE_INSTANCE_CREATE, + DISCORD_EV_STAGE_INSTANCE_DELETE, + DISCORD_EV_STAGE_INSTANCE_UPDATE, + DISCORD_EV_TYPING_START, + DISCORD_EV_USER_UPDATE, + DISCORD_EV_VOICE_STATE_UPDATE, + DISCORD_EV_VOICE_SERVER_UPDATE, + DISCORD_EV_WEBHOOKS_UPDATE, + DISCORD_EV_MAX /**< total amount of enumerators */ +}; + +/** + * @brief return value of discord_set_event_scheduler() callback + * @see discord_set_event_scheduler() + */ +typedef enum discord_event_scheduler { + /** this event has been handled */ + DISCORD_EVENT_IGNORE, + /** handle this event in main thread */ + DISCORD_EVENT_MAIN_THREAD, + /** handle this event in a worker thread */ + DISCORD_EVENT_WORKER_THREAD +} discord_event_scheduler_t; + +/** + * @brief Event Handling Mode callback + * + * A very important callback that enables the user with a fine-grained control + * of how each event is handled: blocking, non-blocking or ignored + * @see discord_set_event_scheduler(), @ref discord_gateway_events + */ +typedef enum discord_event_scheduler (*discord_ev_scheduler)( + struct discord *client, + const char data[], + size_t size, + enum discord_gateway_events event); + +/** + * @brief Provides control over Discord event's callback scheduler + * @see @ref discord_event_scheduler, @ref discord_gateway_events + * + * Allows the user to scan the preliminary raw JSON event payload, and control + * whether it should trigger callbacks + * @param client the client created_with discord_from_token() + * @param fn the function that will be executed + * @warning The user is responsible for providing their own locking mechanism + * to avoid race-condition on sensitive data + */ +void discord_set_event_scheduler(struct discord *client, + discord_ev_scheduler callback); + +/** + * @brief Subscribe to Discord Events + * + * @param client the client created with discord_from_token() + * @param code the intents opcode, can be set as a bitmask operation + */ +void discord_add_intents(struct discord *client, uint64_t code); + +/** + * @brief Unsubscribe from Discord Events + * + * @param client the client created with discord_from_token() + * @param code the intents opcode, can be set as bitmask operation + * Ex: 1 << 0 | 1 << 1 | 1 << 4 + */ +void discord_remove_intents(struct discord *client, uint64_t code); + +/** + * @brief Set a mandatory prefix before commands + * @see discord_set_on_command() + * + * Example: If @a 'help' is a command and @a '!' prefix is set, the command + * will only be validated if @a '!help' is sent + * @param client the client created with discord_from_token() + * @param prefix the mandatory command prefix + */ +void discord_set_prefix(struct discord *client, const char prefix[]); + +/** + * @brief Set command/callback pair + * + * The callback is triggered when a user types the assigned command in a + * chat visible to the client + * @param client the client created with discord_from_token() + * @param command the command to trigger the callback + * @param callback the callback to be triggered on event + * @note The command and any subjacent empty space is left out of + * the message content + */ +void discord_set_on_command( + struct discord *client, + const char *command, + void (*callback)(struct discord *client, + const struct discord_message *event)); + +/** + * @brief Set a variadic series of NULL terminated commands to a callback + * + * The callback is triggered when a user types one of the assigned commands in + * a chat visble to the client + * @param client the client created with discord_from_token() + * @param commands array of commands to trigger the callback + * @param amount amount of commands provided + * @param callback the callback to be triggered on event + * @note The command and any subjacent empty space is left out of + * the message content + */ +void discord_set_on_commands( + struct discord *client, + const char *commands[], + int amount, + void (*callback)(struct discord *client, + const struct discord_message *event)); + +/** + * @brief Triggers when idle + * @note This is a Concord custom event + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_idle(struct discord *client, + void (*callback)(struct discord *client)); + +/** + * @brief Triggers once per event-loop cycle + * @note This is a Concord custom event + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_cycle(struct discord *client, + void (*callback)(struct discord *client)); + +/** + * @brief Triggers when the client session is ready + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_ready(struct discord *client, + void (*callback)(struct discord *client, + const struct discord_ready *event)); + +/** + * @brief Triggers when an application command permission is updated + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_application_command_permissions_update( + struct discord *client, + void (*callback)( + struct discord *client, + const struct discord_application_command_permissions *event)); + +/** + * @brief Triggers when an auto moderation rule is created + * @note This implicitly sets + * @ref DISCORD_GATEWAY_AUTO_MODERATION_CONFIGURATION intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_auto_moderation_rule_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_auto_moderation_rule *event)); + +/** + * @brief Triggers when an auto moderation rule is updated + * @note This implicitly sets + * @ref DISCORD_GATEWAY_AUTO_MODERATION_CONFIGURATION intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_auto_moderation_rule_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_auto_moderation_rule *event)); + +/** + * @brief Triggers when an auto moderation rule is deleted + * @note This implicitly sets + * @ref DISCORD_GATEWAY_AUTO_MODERATION_CONFIGURATION intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_auto_moderation_rule_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_auto_moderation_rule *event)); + +/** + * @brief Triggers when an auto moderation rule is triggered and an execution + * is executed (e.g a message was blocked) + * @note This implicitly sets @ref DISCORD_GATEWAY_AUTO_MODERATION_EXECUTION + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_auto_moderation_action_execution( + struct discord *client, + void (*callback)( + struct discord *client, + const struct discord_auto_moderation_action_execution *event)); + +/** + * @brief Triggers when a channel is created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_channel_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_channel *event)); + +/** + * @brief Triggers when a channel is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_channel_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_channel *event)); + +/** + * @brief Triggers when a channel is deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_channel_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_channel *event)); + +/** + * @brief Triggers when a channel pin is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGES intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_channel_pins_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_channel_pins_update *event)); + +/** + * @brief Triggers when a thread is created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_thread_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_channel *event)); + +/** + * @brief Triggers when a thread is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_thread_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_channel *event)); + +/** + * @brief Triggers when a thread is deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_thread_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_channel *event)); + +/** + * @brief Triggers when the current user gains access to a channel + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_thread_list_sync( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_thread_list_sync *event)); + +/** + * @brief Triggers when a thread the bot is in gets updated + * @note For bots, this event largely is just a signal that you are a member of + * the thread + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_thread_member_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_thread_member *event)); + +/** + * @brief Triggers when someone is added or removed from a thread + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS and + * @ref DISCORD_GATEWAY_GUILD_MEMBERS intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_thread_members_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_thread_members_update *event)); + +/** + * @brief Triggers when a guild is created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild *event)); + +/** + * @brief Triggers when a guild is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild *event)); + +/** + * @brief Triggers when a guild is deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild *event)); + +/** + * @brief Triggers when a user is banned from a guild + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_BANS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_ban_add( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_ban_add *event)); + +/** + * @brief Triggers when a user is unbanned from a guild + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_BANS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_ban_remove( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_ban_remove *event)); + +/** + * @brief Triggers when a guild emojis are updated + * @note This implicitly sets + * @ref DISCORD_GATEWAY_GUILD_EMOJIS_AND_STICKERS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_emojis_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_emojis_update *event)); + +/** + * @brief Triggers when a guild stickers are updated + * @note This implicitly sets + * @ref DISCORD_GATEWAY_GUILD_EMOJIS_AND_STICKERS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_stickers_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_stickers_update *event)); + +/** + * @brief Triggers when a guild integrations are updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INTEGRATIONS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_integrations_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_integrations_update *event)); + +/** + * @brief Triggers when a guild member is added + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MEMBERS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_member_add( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_member *event)); + +/** + * @brief Triggers when a guild member is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MEMBERS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_member_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_member_update *event)); + +/** + * @brief Triggers when a guild member is removed + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MEMBERS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_member_remove( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_member_remove *event)); + +/** + * @brief Triggers in response to discord_request_guild_members() + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_members_chunk( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_members_chunk *event)); + +/** + * @brief Triggers when a guild role is created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_role_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_role_create *event)); + +/** + * @brief Triggers when a guild role is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_role_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_role_update *event)); + +/** + * @brief Triggers when a guild role is deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_role_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_role_delete *event)); + +/** + * @brief Triggers when a guild scheduled event is created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_scheduled_event_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_scheduled_event *event)); + +/** + * @brief Triggers when a guild scheduled event is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_scheduled_event_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_scheduled_event *event)); + +/** + * @brief Triggers when a guild scheduled event is deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_scheduled_event_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_guild_scheduled_event *event)); + +/** + * @brief Triggers when a user subscribes to a guild scheduled event + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_scheduled_event_user_add( + struct discord *client, + void (*callback)( + struct discord *client, + const struct discord_guild_scheduled_event_user_add *event)); + +/** + * @brief Triggers when a user unsubscribes from a guild scheduled event + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_guild_scheduled_event_user_remove( + struct discord *client, + void (*callback)( + struct discord *client, + const struct discord_guild_scheduled_event_user_remove *event)); + +/** + * @brief Triggers when a guild integration is created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INTEGRATIONS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_integration_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_integration *event)); + +/** + * @brief Triggers when a guild integration is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INTEGRATIONS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_integration_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_integration *event)); + +/** + * @brief Triggers when a guild integration is deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INTEGRATIONS + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_integration_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_integration_delete *event)); + +/** + * @brief Triggers when user has used an interaction, such as an application + * command + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_interaction_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_interaction *event)); + +/** + * @brief Triggers when an invite to a channel has been created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INVITES intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_invite_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_invite_create *event)); + +/** + * @brief Triggers when an invite to a channel has been deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_INVITES intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_invite_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_invite_delete *event)); + +/** + * @brief Triggers when a message is created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGES and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGES intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_message_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_message *event)); + +/** + * @brief Triggers when a message is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGES and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGES intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_message_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_message *event)); + +/** + * @brief Triggers when a message is deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGES and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGES intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_message_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_message_delete *event)); + +/** + * @brief Triggers when messages are deleted in bulk + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGES + * intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_message_delete_bulk( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_message_delete_bulk *event)); + +/** + * @brief Triggers when a message reaction is added + * @note This implicitly sets + * @ref DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_message_reaction_add( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_message_reaction_add *event)); + +/** + * @brief Triggers when a message reaction is removed + * @note This implicitly sets + * @ref DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_message_reaction_remove( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_message_reaction_remove *event)); + +/** + * @brief Triggers when all message reactions are removed + * @note This implicitly sets + * @ref DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_message_reaction_remove_all( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_message_reaction_remove_all *event)); +/** @brief Triggers when all instances of a particular reaction from some + * message is removed */ + +/** + * @brief Triggers when all instances of a particular reaction is removed from + * a message + * @note This implicitly sets + * @ref DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_message_reaction_remove_emoji( + struct discord *client, + void (*callback)( + struct discord *client, + const struct discord_message_reaction_remove_emoji *event)); + +/** + * @brief Triggers when user presence is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_PRESENCES intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_presence_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_presence_update *event)); + +/** + * @brief Triggers when a stage instance is created + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_stage_instance_create( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_stage_instance *event)); + +/** + * @brief Triggers when a stage instance is updated + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_stage_instance_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_stage_instance *event)); + +/** + * @brief Triggers when a stage instance is deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILDS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_stage_instance_delete( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_stage_instance *event)); + +/** + * @brief Triggers when user starts typing in a channel + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_MESSAGE_TYPING and + * @ref DISCORD_GATEWAY_DIRECT_MESSAGE_TYPING intents + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_typing_start( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_typing_start *event)); + +/** + * @brief Triggers when properties about a user changed + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_user_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_user *event)); + +/** + * @brief Triggers when a voice state is updated + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_voice_state_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_voice_state *event)); + +/** + * @brief Triggers when voice server is updated + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_voice_server_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_voice_server_update *event)); + +/** + * @brief Triggers when guild channel has been created, updated or deleted + * @note This implicitly sets @ref DISCORD_GATEWAY_GUILD_WEBHOOKS intent + * + * @param client the client created with discord_from_token() + * @param callback the callback to be triggered on event + */ +void discord_set_on_webhooks_update( + struct discord *client, + void (*callback)(struct discord *client, + const struct discord_webhooks_update *event)); + +/** @} DiscordEvents */ + +#endif /* DISCORD_EVENTS_H */ diff --git a/include/concord/discord-internal.h b/include/concord/discord-internal.h new file mode 100644 index 0000000..10921a2 --- /dev/null +++ b/include/concord/discord-internal.h @@ -0,0 +1,1260 @@ +/** + * @file discord-internal.h + * @ingroup DiscordInternal + * @author Cogmasters + * @brief Internal functions and datatypes + */ + +#ifndef DISCORD_INTERNAL_H +#define DISCORD_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +#define JSONB_HEADER +#include "json-build.h" +#define JSMN_STRICT +#define JSMN_HEADER +#include "jsmn.h" +#include "jsmn-find.h" +#define LOGMOD_HEADER +#include "logmod.h" + +#include "user-agent.h" +#include "websockets.h" +#include "cog-utils.h" +#include "io_poller.h" +#include "queue.h" +#include "priority_queue.h" +#include "attributes.h" + +/** @brief Return 1 if string isn't considered empty */ +#define NOT_EMPTY_STR(str) ((str) && *(str)) +/** + * @brief Get container `type` from a field `ptr` + * + * @param[in] ptr the field contained in `type` + * @param[in] type the container datatype + * @param[in] path the path to the field from the container POV + */ +#define CONTAINEROF(ptr, type, path) \ + ((type *)((char *)(ptr) - offsetof(type, path))) + +/** @defgroup DiscordInternal Internal implementation details + * @brief Documentation useful when developing or debugging Concord itself + * @{ */ + +/** @brief cet client from its nested field */ +#define CLIENT(ptr, path) CONTAINEROF(ptr, struct discord, path) + +/** + * @brief log and return `code` if `expect` condition is false + * + * @param[in] client the Discord client + * @param[in] expect the expected outcome + * @param[in] code return CCORDcode error code + * @param[in] reason for return + * @return the provided @ref CCORDcode `code` parameter + */ +#define CCORD_EXPECT(client, expect, code, reason) \ + do { \ + if (!(expect)) { \ + logmod_log(ERROR, (client)->logger, \ + "Expected: " #expect " | %s (%s)", \ + discord_strerror(code, client), \ + discord_code_as_string(code)); \ + return code; \ + } \ + } while (0) + +/** + * @brief log and return `code` if function call doesn't returns @ref CCORD_OK + * + * @param[in] client the Discord client + * @param[in] fn the function call that returns CCORDcode + * @return the returned @ref CCORDcode `code` parameter + */ +#define CCORD_EXPECT_OK(client, fn) \ + do { \ + const CCORDcode code = (fn); \ + if (code != CCORD_OK) { \ + logmod_log(ERROR, (client)->logger, \ + "Expected: CCORD_OK == " #fn " | %s (%s)", \ + discord_strerror(code, client), \ + discord_code_as_string(code)); \ + return code; \ + } \ + } while (0) + +/** URL endpoint threshold length */ +#define DISCORD_ENDPT_LEN 512 +/** Route's unique key threshold length */ +#define DISCORD_ROUTE_LEN 256 + +/** @defgroup DiscordInternalTimer Timer API + * @brief Callback scheduling API + * @{ */ + +struct discord_timers { + priority_queue *q; + struct io_poller *io; + struct { + bool is_active; + pthread_t thread; + struct discord_timer *timer; + bool skip_update_phase; + } active; + pthread_mutex_t lock; + pthread_cond_t cond; +}; + +/** + * @brief Prepare timers for usage + * + * @param timers the 'struct discord_timers' to init + * @CCORD_return + */ +CCORDcode discord_timers_init(struct discord_timers *timers, + struct io_poller *io); + +/** + * @brief Cleanup timers and call cancel any running ones + * + * @param client the client created with discord_init() + * @param timers the 'struct discord_timers' to cleanup + */ +void discord_timers_cleanup(struct discord *client, + struct discord_timers *timers); + +/** + * @brief Get earliest trigger time from a group of timers + * + * @param timers array of timers + * @param n number of timers in array + * @param now current time + * @param max_time max time to allowed + * @return time in microseconds until next timer, or max + */ +int64_t discord_timers_get_next_trigger(struct discord_timers *const timers[], + size_t n, + int64_t now, + int64_t max_time); + +/** + * @brief Run all timers that are due + * + * @param client the client created with discord_init() + * @param timers the timers to run + */ +void discord_timers_run(struct discord *client, struct discord_timers *timers); + +/** + * @brief Modifies or creates a timer + * + * @param client the client created with discord_init() + * @param timers the timer group to perform this operation on + * @param timer the timer that should be modified + * @return the id of the timer + */ +unsigned _discord_timer_ctl(struct discord *client, + struct discord_timers *timers, + struct discord_timer *timer); + +/** + * @brief Modifies or creates a timer + * + * @param client the client created with discord_init() + * @param timer the timer that should be modified + * @return the id of the timer + */ +unsigned discord_internal_timer_ctl(struct discord *client, + struct discord_timer *timer); + +/** + * @brief Creates a one shot timer that automatically deletes itself upon + * completion + * + * @param client the client created with discord_init() + * @param on_tick_cb (nullable) the callback that should be called when timer + * triggers + * @param on_status_changed_cb (nullable) the callback for status updates + * timer->flags will have: DISCORD_TIMER_CANCELED, and DISCORD_TIMER_DELETE + * @param data user data + * @param delay delay before timer should start in milliseconds + * @return the id of the timer + */ +unsigned discord_internal_timer(struct discord *client, + discord_ev_timer on_tick_cb, + discord_ev_timer on_status_changed_cb, + void *data, + int64_t delay); + +/** @} DiscordInternalTimer */ + +/** @defgroup DiscordInternalREST REST API + * @brief Wrapper to the Discord REST API + * @{ */ + +/** @defgroup DiscordInternalRESTRequest Request's handling + * @brief Store, manage and dispatch individual requests + * @{ */ + +/** @defgroup DiscordInternalRESTRequestRatelimit Ratelimiting + * @brief Enforce ratelimiting per the official Discord Documentation + * @{ */ + +/** + * @brief Value assigned to @ref discord_bucket `busy_req` field in case + * it's being timed-out + */ +#define DISCORD_BUCKET_TIMEOUT (void *)(0xf) + +/** + * @brief The ratelimiter struct for handling ratelimiting + * @note this struct **SHOULD** only be handled from the `REST` manager thread + */ +struct discord_ratelimiter { + /** `DISCORD_RATELIMIT` logging module */ + struct logmod_logger *logger; + /** amount of bucket's routes discovered */ + int length; + /** route's cap before increase */ + int capacity; + /** + * routes matched to individual buckets + * @note datatype declared at discord-rest_ratelimit.c + */ + struct _discord_route *routes; + /** singleton bucket for requests that haven't been matched to a + * known or new bucket (i.e first time running the request) */ + struct discord_bucket *null; + /** singleton bucket for requests that are not part of any known + * ratelimiting group */ + struct discord_bucket *miss; + + /* client-wide global ratelimiting */ + u64unix_ms *global_wait_tstamp; + + /** bucket queues */ + struct { + /** buckets that are currently pending (have pending requests) */ + QUEUE(struct discord_bucket) pending; + } queues; +}; + +/** + * @brief Initialize ratelimiter handle + * + * A hashtable shall be used for storage and retrieval of discovered buckets + * @param rl the ratelimiter handle to be initialized + * @CCORD_return + */ +CCORDcode discord_ratelimiter_init(struct discord_ratelimiter *rl); + +/** + * @brief Cleanup all buckets that have been discovered + * + * @note pending requests will be moved to `rest.queues->recycling` + * @param rl the handle initialized with discord_ratelimiter_init() + */ +void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); + +/** + * @brief Build unique key formed from the HTTP method and endpoint + * @see https://discord.com/developers/docs/topics/rate-limits + * + * @param[in] method the request method + * @param[out] key unique key for matching to buckets + * @param[in] endpoint_fmt the printf-like endpoint formatting string + * @param[in] args variadic arguments matched to `endpoint_fmt` + */ +void discord_ratelimiter_build_key(enum http_method method, + char key[DISCORD_ROUTE_LEN], + const char endpoint_fmt[], + va_list args); + +/** + * @brief Update the bucket with response header data + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param bucket NULL when bucket is first discovered + * @param key obtained from discord_ratelimiter_build_key() + * @param info informational struct containing details on the current transfer + * @note If the bucket was just discovered it will be created here. + */ +void discord_ratelimiter_build(struct discord_ratelimiter *rl, + struct discord_bucket *bucket, + const char key[], + struct ua_info *info); + +/** + * @brief Update global ratelimiting value + * @todo check if all pending buckets must be unset + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param bucket bucket that received the global ratelimiting notice + * @param wait_ms the amount of time that all buckets should wait for + */ +void discord_ratelimiter_set_global_timeout(struct discord_ratelimiter *rl, + struct discord_bucket *bucket, + u64unix_ms wait_ms); + +/** @brief The Discord bucket for handling per-group ratelimits */ +struct discord_bucket { + /** the hash associated with the bucket's ratelimiting group */ + char hash[64]; + /** maximum connections this bucket can handle before ratelimit */ + long limit; + /** connections this bucket can do before pending for cooldown */ + long remaining; + /** timestamp of when cooldown timer resets */ + u64unix_ms reset_tstamp; + + /** + * pointer to this bucket's currently busy request + * @note @ref DISCORD_BUCKET_TIMEOUT if bucket is being ratelimited + */ + struct discord_request *busy_req; + + /** request queues */ + struct { + /** next requests queue */ + QUEUE(struct discord_request) next; + } queues; + /** entry for @ref discord_ratelimiter pending buckets queue */ + QUEUE entry; +}; + +/** + * @brief Set bucket timeout + * + * @param bucket the bucket to be checked for time out + * @param wait_ms how long the bucket should wait for + */ +void discord_bucket_set_timeout(struct discord_bucket *bucket, + u64unix_ms wait_ms); + +/** + * @brief Get a `struct discord_bucket` assigned to `key` + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param key obtained from discord_ratelimiter_build_key() + * @return bucket matched to `key` + */ +struct discord_bucket *discord_bucket_get(struct discord_ratelimiter *rl, + const char key[]); + +/** + * @brief Insert into bucket's next requests queue + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param b the bucket to insert the request to + * @param req the request to be inserted to bucket + * @param high_priority if high priority then request shall be prioritized over + * already enqueued requests + */ +void discord_bucket_insert(struct discord_ratelimiter *rl, + struct discord_bucket *b, + struct discord_request *req, + bool high_priority); + +/** + * @brief Iterate and select next requests + * @note discord_bucket_unselect() must be called once bucket's current request + * is done and its next one should be selected + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param data user arbitrary data + * @param iter the user callback to be called per bucket + */ +void discord_bucket_request_selector( + struct discord_ratelimiter *rl, + void *data, + void (*iter)(void *data, struct discord_request *req)); + +/** + * @brief Unselect a request provided at discord_ratelimiter_request_selector() + * @note counterpart to discord_ratelimiter_request_selector() + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param b the request's bucket + * @param req the request to unslect + */ +void discord_bucket_request_unselect(struct discord_ratelimiter *rl, + struct discord_bucket *b, + struct discord_request *req); + +/** @} DiscordInternalRESTRequestRatelimit */ + +/** @brief Generic request dispatcher */ +struct discord_ret_dispatch { + DISCORD_RET_DEFAULT_FIELDS; + /** `true` if may receive a datatype from response */ + bool has_type; + + /** + * optional callback to be executed on a successful request + * @todo should be cast to the original callback signature before calling, + * otherwise its UB + */ + union { + void (*typed)(struct discord *client, + struct discord_response *resp, + const void *ret); + void (*typeless)(struct discord *client, + struct discord_response *resp); + } done; + + /** if an address is provided, then request will block the thread and + * perform on-spot. On success the response object will be written to + * the address. */ + void *sync; +}; + +/** @brief Attributes of response datatype */ +struct discord_ret_response { + /** pointer to datatype */ + void *data; + /** size of datatype in bytes */ + size_t size; + /** initializer function for datatype fields */ + void (*init)(void *data); + /** populate datatype with JSON values */ + size_t (*from_json)(const char *json, size_t len, void *data); + /** cleanup function for datatype */ + void (*cleanup)(void *data); +}; + +/** + * @brief Macro containing @ref discord_attributes fields + * @note this exists for @ref discord_request alignment purposes + */ +#define DISCORD_ATTRIBUTES_FIELDS \ + /** attributes set by client for request dispatch behavior */ \ + struct discord_ret_dispatch dispatch; \ + /** information for parsing response into a datatype (if possible) */ \ + struct discord_ret_response response; \ + /** if @ref HTTP_MIMEPOST provide attachments for file transfer */ \ + struct discord_attachments attachments; \ + /** indicated reason to why the action was taken @note when used at \ + * @ref discord_request buffer is kept and reused */ \ + char *reason + +/** @brief Request to be performed */ +struct discord_attributes { + DISCORD_ATTRIBUTES_FIELDS; +}; + +/** + * @brief Individual requests that are scheduled to run asynchronously + * @note this struct **SHOULD NOT** be handled from the `REST` manager thread + * @note its fields are aligned with @ref discord_attributes + * (see @ref DISCORD_ATTRIBUTES_FIELDS) + */ +struct discord_request { + DISCORD_ATTRIBUTES_FIELDS; + + /** the request's bucket */ + struct discord_bucket *b; + /** request body handle */ + struct ccord_szbuf body; + /** the request's http method */ + enum http_method method; + /** the request's endpoint */ + char endpoint[DISCORD_ENDPT_LEN]; + /** the request bucket's key */ + char key[DISCORD_ROUTE_LEN]; + /** the connection handler assigned */ + struct ua_conn *conn; + /** request's status code */ + CCORDcode code; + /** response payload */ + struct ccord_szbuf_readonly json; + /** current retry attempt (stop at rest->retry_limit) */ + int retry_attempt; + /** synchronize synchronous requests */ + pthread_cond_t *cond; + /** entry for @ref discord_ratelimiter and @ref discord_bucket queues */ + QUEUE entry; +}; + +/** @brief The handle used for handling asynchronous requests */ +struct discord_requestor { + /** `DISCORD_REQUEST` logging module */ + struct logmod_logger *logger; + /** the user agent handle for performing requests */ + struct user_agent *ua; + /** curl_multi handle for performing asynchronous requests */ + CURLM *mhandle; + /** enforce Discord's ratelimiting for requests */ + struct discord_ratelimiter ratelimiter; + + /** max amount of retries before a failed request gives up */ + int retry_limit; + + /** request queues */ + struct { + /** requests for recycling */ + QUEUE(struct discord_request) recycling; + /** pending requests waiting to be assigned to a bucket */ + QUEUE(struct discord_request) pending; + /** + * finished requests that are done performing and waiting for + * their callbacks to be called from the main thread + */ + QUEUE(struct discord_request) finished; + } *queues; + + /** queue locks */ + struct { + /** recycling queue lock */ + pthread_mutex_t recycling; + /** pending queue lock */ + pthread_mutex_t pending; + /** finished queue lock */ + pthread_mutex_t finished; + } *qlocks; +}; + +/** + * @brief Initialize the request handler + * + * This shall initialize a `CURLM` multi handle for performing requests + * asynchronously, and a queue for storing individual requests + * @param rqtor the requestor handle to be initialized + * @param token the bot token + * @CCORD_return + */ +CCORDcode discord_requestor_init(struct discord_requestor *rqtor, + const char token[]); + +/** + * @brief Free the request handler + * + * @param rqtor the handle initialized with discord_requestor_init() + */ +void discord_requestor_cleanup(struct discord_requestor *rqtor); + +/** + * @brief Check for and start pending bucket's requests + * + * @param rqtor the handle initialized with discord_requestor_init() + * @CCORD_return + */ +CCORDcode discord_requestor_start_pending(struct discord_requestor *rqtor); + +/** + * @brief Poll for request's completion + * + * @param rqtor the handle initialized with discord_requestor_init() + * @CCORD_return + */ +CCORDcode discord_requestor_info_read(struct discord_requestor *rqtor); + +/** + * @brief Run pending callbacks from completed requests + * + * @param req the request containing preliminary information for its dispatch + */ +void discord_requestor_dispatch_responses(struct discord_requestor *rqtor); + +/** + * @brief Mark request as canceled and move it to the recycling queue + * + * @param rqtor the requestor handle initialized with discord_requestor_init() + * @param req the on-going request to be canceled + */ +void discord_request_cancel(struct discord_requestor *rqtor, + struct discord_request *req); + +/** + * @brief Begin a new request + * + * The returned request automatically be performed from the `REST` thread + * @param rqtor the requestor handle initialized with discord_requestor_init() + * @param req the request containing preliminary information for its dispatch + * and response's parsing + * @param body the request's body + * @param method the request's HTTP method + * @param endpoint the request's endpoint + * @param key the request bucket's group for ratelimiting + * @CCORD_return + */ +CCORDcode discord_request_begin(struct discord_requestor *rqtor, + const struct discord_attributes *req, + const struct ccord_szbuf *body, + const enum http_method method, + const char endpoint[DISCORD_ENDPT_LEN], + const char key[DISCORD_ROUTE_LEN]); + +/** @} DiscordInternalRESTRequest */ + +/** + * @brief The handle used for interfacing with Discord's REST API + * + * This handle will manage the special REST thread where requests are performed + * in + */ +struct discord_rest { + /** `DISCORD_HTTP` or `DISCORD_WEBHOOK` logging module */ + struct logmod_logger *logger; + /** the requests handler */ + struct discord_requestor requestor; + /** the timer queue for the rest thread */ + struct discord_timers timers; + /** poller for REST requests */ + struct io_poller *io_poller; + /** threadpool for managing the REST thread */ + struct threadpool_t *tpool; +}; + +/** + * @brief Initialize an REST handle + * + * Structure used for interfacing with the Discord's REST API + * @param rest the REST handle to be initialized + * @param token the bot token + * @CCORD_return + */ +CCORDcode discord_rest_init(struct discord_rest *rest, const char token[]); + +/** + * @brief Free an REST handle + * + * @param rest the handle initialized with discord_rest_init() + */ +void discord_rest_cleanup(struct discord_rest *rest); + +/** + * @brief Perform a request to Discord + * + * This functions is a selector over discord_rest_run() or + * discord_rest_run_requestor() + * @param rest the handle initialized with discord_rest_init() + * @param req return object of request + * @param body the body sent for methods that require (ex: post), leave as + * null if unecessary + * @param method the method in opcode format of the request being sent + * @param endpoint_fmt the printf-like endpoint formatting string + * @CCORD_return + * @note if sync is set then this function will block the thread and perform it + * immediately + */ +CCORDcode discord_rest_run(struct discord_rest *rest, + const struct discord_attributes *req, + const struct ccord_szbuf *body, + const enum http_method method, + const char endpoint_fmt[], + ...) PRINTF_LIKE(5, 6); + +/** + * @brief Stop all bucket's on-going, pending and timed-out requests + * + * The requests will be moved over to client's 'queues->recycling' queue + * @param rest the handle initialized with discord_rest_init() + */ +void discord_rest_stop_buckets(struct discord_rest *rest); + +/** @} DiscordInternalREST */ + +/** @defgroup DiscordInternalGateway WebSockets API + * @brief Wrapper to the Discord Gateway API + * @{ */ + +/** @defgroup DiscordInternalGatewaySessionStatus Client's session status + * @brief Client's session status + * @{ */ +/** client is currently offline */ +#define DISCORD_SESSION_OFFLINE 0u +/** client will attempt to resume session after reconnect */ +#define DISCORD_SESSION_RESUMABLE 1u << 0 +/** client in the process of being shutdown */ +#define DISCORD_SESSION_SHUTDOWN 1u << 1 +/** @} DiscordInternalGatewaySessionStatus */ + +/** @brief The handle for storing the Discord Gateway session */ +struct discord_gateway_session { + /** whether client is ready to start sending/receiving events */ + bool is_ready; + /** session id for resuming lost connections */ + char id[64]; + /** amount of shards being used by this session */ + int shards; + /** the session base url */ + char base_url[256]; + /** the base url for resuming */ + char resume_url[256]; + /** session limits */ + struct discord_session_start_limit start_limit; + /** active concurrent sessions */ + int concurrent; + /** event counter to avoid reaching limit of 120 events per 60 sec */ + int event_count; + /** @ref DiscordInternalGatewaySessionStatus */ + unsigned status; + + /** retry connection structure */ + struct { + /** will attempt reconnecting if true */ + bool enable; + /** current retry attempt (resets to 0 when succesful) */ + int attempt; + /** max amount of retries before giving up */ + int limit; + } retry; +}; + +/** @brief The handle for storing the Discord response payload */ +struct discord_gateway_payload { + /** current iteration JSON */ + struct { + /** the JSON text */ + char *start; + /** the text length */ + size_t size; + /** jsmn-find lookup table */ + jsmnf_table *table; + /** lookup table size */ + size_t ntable; + } json; + + /** field 'op' */ + enum discord_gateway_opcodes opcode; + /** field 's' */ + int seq; + /** field 't' */ + char name[32]; + /** field 't' enumerator value */ + enum discord_gateway_events event; + /** field 'd' */ + const jsmnf_pair *data; +}; + +/** A generic event callback for casting */ +typedef void (*discord_ev_event)(struct discord *client, const void *event); +/** An event callback for @ref DISCORD_EV_MESSAGE_CREATE */ +typedef void (*discord_ev_message)(struct discord *client, + const struct discord_message *event); + +/** @brief The handle used for interfacing with Discord's Gateway API */ +struct discord_gateway { + /** `DISCORD_GATEWAY` logging module */ + struct logmod_logger *logger; + /** the websockets handle that connects to Discord */ + struct websockets *ws; + /** curl_multi handle for non-blocking transfer over websockets */ + CURLM *mhandle; + + /** timers kept for synchronization */ + struct { + /** + * fixed milliseconds interval between heartbeats + * @note obtained at `HELLO` + */ + int64_t hbeat_interval; + /** + * boolean that indicates if the last heartbeat was answered + * @note used to detect zombie connections + */ + bool hbeat_acknowledged; + /** + * Gateway's concept of "now" + * @note updated at discord_gateway_perform() + */ + u64unix_ms now; + /** + * last heartbeat pulse timestamp + * @note first sent at `READY` and `RESUME`, then updated every + * `hbeat_interval` + */ + u64unix_ms hbeat_last; + /** + * timestamp of last succesful identify request + * @note updated at discord_gateway_send_identify() + */ + u64unix_ms identify_last; + /** + * timestamp of last succesful event + * @note resets every 60s + */ + u64unix_ms event; + /** timer id for heartbeat timer */ + unsigned hbeat_timer; + + /** + * latency obtained from `HEARTBEAT` and `HEARTBEAT_ACK` response + * interval + */ + int ping_ms; + /** ping rwlock */ + pthread_rwlock_t rwlock; + } *timer; + + /** the identify structure for client authentication */ + struct discord_identify id; + + /** on-going session structure */ + struct discord_gateway_session *session; + + /** response-payload structure */ + struct discord_gateway_payload payload; + /** + * the user's callbacks for Discord events + * @note index 0 for cache callbacks, index 1 for user callbacks + * @todo should be cast to the original callback signature before calling, + * otherwise its UB + */ + discord_ev_event cbs[2][DISCORD_EV_MAX]; + /** the event scheduler callback */ + discord_ev_scheduler scheduler; +}; + +/** + * @brief Initialize a Gateway handle + * + * Structure used for interfacing with the Discord's Gateway API + * @param gw the gateway handle to be initialized + * @param token the bot token + * @CCORD_return + */ +CCORDcode discord_gateway_init(struct discord_gateway *gw, const char token[]); + +/** + * @brief Free a Gateway handle + * + * @param gw the handle initialized with discord_gateway_init() + */ +void discord_gateway_cleanup(struct discord_gateway *gw); + +/** + * @brief Initialize handle with the new session primitives + * + * @param gw the handle initialized with discord_gateway_init() + * @CCORD_return + */ +CCORDcode discord_gateway_start(struct discord_gateway *gw); + +/** + * @brief Cleanup and reset `gw` session primitives + * + * @param ws the WebSockets handle created with ws_init() + * @return `true` if session is over, `false` if session can be retried for + * reconnection + */ +bool discord_gateway_end(struct discord_gateway *gw); + +/** + * @brief Check and manage on-going Gateway session + * + * @param req the request handler + * @CCORD_return + */ +CCORDcode discord_gateway_perform(struct discord_gateway *gw); + +/** + * @brief Gracefully shutdown a ongoing Discord connection over WebSockets + * + * @param gw the handle initialized with discord_gateway_init() + */ +void discord_gateway_shutdown(struct discord_gateway *gw); + +/** + * @brief Gracefully reconnect a ongoing Discord connection over WebSockets + * + * @param gw the handle initialized with discord_gateway_init() + * @param resume true to attempt to resume to previous session, + * false restart a fresh session + */ +void discord_gateway_reconnect(struct discord_gateway *gw, bool resume); + +/** + * @brief Trigger the initial handshake with the gateway + * + * @param gw the handle initialized with discord_gateway_init() + * @param event provide client identification information + */ +void discord_gateway_send_identify(struct discord_gateway *gw, + struct discord_identify *event); + +/** + * @brief Replay missed events when a disconnected client resumes + * + * @param gw the handle initialized with discord_gateway_init() + * @param event session resume information + */ +void discord_gateway_send_resume(struct discord_gateway *gw, + struct discord_resume *event); + +/** + * @brief Maintain an active gateway connection + * + * @param gw the handle initialized with discord_gateway_init() + * @param seq the last session sequence number + */ +void discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq); + +/** + * @brief Request all members for a guild or a list of guilds. + * + * @param gw the handle initialized with discord_gateway_init() + * @param event request guild members information + */ +void discord_gateway_send_request_guild_members( + struct discord_gateway *gw, struct discord_request_guild_members *event); + +/** + * @brief Sent when a client wants to join, move or disconnect from a voice + * channel + * + * @param gw the handle initialized with discord_gateway_init() + * @param event request guild members information + */ +void discord_gateway_send_update_voice_state( + struct discord_gateway *gw, struct discord_update_voice_state *event); + +/** + * @brief Send client's presence status update payload + * + * @param gw the handle initialized with discord_gateway_init() + * @param event the presence to be set + */ +void discord_gateway_send_presence_update( + struct discord_gateway *gw, struct discord_presence_update *event); + +/** + * @brief Dispatch user callback matched to event + * + * @param gw the handle initialized with discord_gateway_init() + */ +void discord_gateway_dispatch(struct discord_gateway *gw); + +/** @} DiscordInternalGateway */ + +/** @defgroup DiscordInternalRefcount Reference counter + * @brief Handle automatic cleanup of user's data + * @{ */ + +/** + * @brief Automatically cleanup user data + * + * Automatically cleanup user data that is passed around Discord event's + * callbacks once its reference counter reaches 0, meaning there are no + * more callbacks expecting the data + */ +struct discord_refcounter { + /** `DISCORD_REFCOUNT` logging module */ + struct logmod_logger *logger; + /** amount of individual user's data held for automatic cleanup */ + int length; + /** cap before increase */ + int capacity; + /** + * individual user's data held for automatic cleanup + * @note datatype declared at discord-refcount.c + */ + struct _discord_ref *refs; + /** global lock */ + pthread_mutex_t *g_lock; +}; + +/** + * @brief Initialize reference counter handle + * + * A hashtable shall be used for storage and retrieval of user data + * @param rc the reference counter handle to be initialized + * @CCORD_return + */ +CCORDcode discord_refcounter_init(struct discord_refcounter *rc); + +/** + * @brief Add a new internal reference to the reference counter + * + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data address to be referenced + * @param cleanup function for cleaning `data` resources once its + * no longer referenced + * @param should_free whether `data` cleanup should be followed by a free() + */ +void discord_refcounter_add_internal(struct discord_refcounter *rc, + void *data, + void (*cleanup)(void *data), + bool should_free); + +/** + * @brief Add a new client reference to the reference counter + * + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data address to be referenced + * @param cleanup function for cleaning `data` resources once its + * no longer referenced + * @param should_free whether `data` cleanup should be followed by a free() + */ +void discord_refcounter_add_client(struct discord_refcounter *rc, + void *data, + void (*cleanup)(struct discord *client, + void *data), + bool should_free); + +/** + * @brief Cleanup refcounter and all user data currently held + * + * @param rc the handle initialized with discord_refcounter_init() + */ +void discord_refcounter_cleanup(struct discord_refcounter *rc); + +/** + * @brief Claim ownership of `data` + * @see discord_refcounter_unclaim() + * + * After ownership is claimed `data` will no longer be cleaned automatically, + * instead shall be cleaned only when the same amount of + * discord_refcounter_unclaim() have been called + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data to have its ownership claimed + * @retval CCORD_OK counter for `data` has been incremented + * @retval CCORD_RESOURCE_UNAVAILABLE couldn't find a match to `data` + */ +CCORDcode discord_refcounter_claim(struct discord_refcounter *rc, + const void *data); + +/** + * @brief Unclaim ownership of `data` + * @see discord_refcounter_claim() + * + * This will make the resource eligible for cleanup, so this should only be + * called when you no longer plan to use it + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data to have its ownership unclaimed + * @retval CCORD_OK counter for `data` has been decremented + * @retval CCORD_RESOURCE_UNAVAILABLE couldn't find a match to `data` + * @retval CCORD_RESOURCE_OWNERSHIP `data` has never been discord_claim() 'd + */ +CCORDcode discord_refcounter_unclaim(struct discord_refcounter *rc, + void *data); + +/** + * @brief Increment the reference counter for `ret->data` + * @see discord_refcounter_decr() + * + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data to have its reference counter incremented + * @retval CCORD_OK counter for `data` has been incremented + * @retval CCORD_RESOURCE_UNAVAILABLE couldn't find a match to `data` + */ +CCORDcode discord_refcounter_incr(struct discord_refcounter *rc, void *data); + +/** + * @brief Decrement the reference counter for `data` + * @see discord_refcounter_incr() + * + * If the count reaches zero then `data` shall be cleanup up with its + * user-defined cleanup function + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data to have its reference counter decremented + * @retval CCORD_OK counter for `data` has been decremented + * @retval CCORD_RESOURCE_UNAVAILABLE couldn't find a match to `data` + * @retval CCORD_RESOURCE_OWNERSHIP caught attempt to cleanup a claimed + * resource + */ +CCORDcode discord_refcounter_decr(struct discord_refcounter *rc, void *data); + +/** @} DiscordInternalRefcount */ + +/** @defgroup DiscordInternalMessageCommands Message Commands API + * @brief The Message Commands API for registering and parsing user commands + * @{ */ + +/** + * @brief The handle for storing user's message commands + * @see discord_set_on_command() + */ +struct discord_message_commands { + /** `DISCORD_MESSAGE_COMMANDS` logging module */ + const struct logmod_logger *logger; + /** the prefix expected for every command */ + struct ccord_szbuf prefix; + /** fallback message command @see discord_set_on_command() */ + discord_ev_message fallback; + /** amount of message commands created */ + int length; + /** message commands cap before increase */ + int capacity; + /** + * message command entries + * @note datatype declared at discord-messagecommands.c + */ + struct _discord_message_commands_entry *entries; +}; + +/** + * @brief Initialize a Message Commands handle + * + * @param cmds the message commands handle to be initialized + * @CCORD_return + */ +CCORDcode discord_message_commands_init(struct discord_message_commands *cmds); + +/** + * @brief Free a Message Commands handle + * + * @param cmds the handle initialized with discord_message_commands_init() + */ +void discord_message_commands_cleanup(struct discord_message_commands *cmds); + +/** + * @brief Search for a callback matching the command + * + * @param cmds the handle initialized with discord_message_commands_init() + * @param command the command to be searched for + * @param length the command length + * @return the callback match, `NULL` in case there wasn't a match + */ +discord_ev_message discord_message_commands_find( + struct discord_message_commands *cmds, + const char command[], + size_t length); + +/** + * @brief Add a new command/callback pair, or update an existing command + * + * @param cmds the handle initialized with discord_message_commands_init() + * @param command the message command to be matched with callback + * @param length the command length + * @param callback the callback to be triggered when the command is sent + */ +void discord_message_commands_append(struct discord_message_commands *cmds, + const char command[], + size_t length, + discord_ev_message callback); + +/** + * @brief Set a mandatory prefix before commands + * @see discord_set_on_command() + * + * Example: If @a 'help' is a command and @a '!' prefix is set, the command + * will only be validated if @a '!help' is sent + * @param cmds the handle initialized with discord_message_commands_init() + * @param prefix the mandatory command prefix + * @param length the prefix length + */ +void discord_message_commands_set_prefix(struct discord_message_commands *cmds, + const char prefix[], + size_t length); + +/** + * @brief Read the current @ref DISCORD_EV_MESSAGE_CREATE payload and attempt + * to perform its matching callback + * + * @param cmds the handle initialized with discord_message_commands_init() + * @param payload the event payload to read from + * (assumes its from `MESSAGE_CREATE`) + * @return `true` if the callback has been performed + */ +bool discord_message_commands_try_perform( + struct discord_message_commands *cmds, + struct discord_gateway_payload *payload); + +/** @} DiscordInternalMessageCommands */ + +/** @defgroup DiscordInternalCache Cache API + * @brief The Cache API for storage and retrieval of Discord data + * @{ */ + +/** + * @brief The Discord Cache control handler + */ +struct discord_cache { + struct _discord_cache_data *data; + void (*cleanup)(struct discord *client); + + /** gateway should call this when a shard has lost connection */ + void (*on_shard_disconnected)(struct discord *client, + const struct discord_identify *ident, + bool resumable); + /** gateway should call this when a shard has resumed */ + void (*on_shard_resumed)(struct discord *client, + const struct discord_identify *ident); + /** gateway should call this when a shard has reconnected */ + void (*on_shard_reconnected)(struct discord *client, + const struct discord_identify *ident); +}; + +/** @} DiscordInternalCache */ + +/** + * @brief The Discord client handler + * + * Used to access/perform public functions from discord.h + * @see discord_init(), discord_config_init(), discord_cleanup() + */ +struct discord { + /** `DISCORD` logging module */ + struct logmod_logger *logger; + /** LogMod loggers table */ + struct logmod_logger table[64]; + /** LogMod handler */ + struct logmod logmod; + /** the configuration handler */ + struct discord_config config; + /** the id of the process where this modules was created */ + unsigned pid; + /** whether this is the original client or a clone */ + bool is_original; + /** the io poller for listening to file descriptors */ + struct io_poller *io_poller; + + /** the user's message commands @see discord_set_on_command() */ + struct discord_message_commands commands; + /** user's data reference counter for automatic cleanup */ + struct discord_refcounter refcounter; + + /** the handle for interfacing with Discord's REST API */ + struct discord_rest rest; + /** the handle for interfacing with Discord's Gateway API */ + struct discord_gateway gw; + /** the client's user structure */ + struct discord_user self; + /** the handle for registering and retrieving Discord data */ + struct discord_cache cache; + + /** fd that gets triggered when discord_shutdown_all() is called */ + int shutdown_fd; + + struct { + struct discord_timers internal; + struct discord_timers user; + } timers; + + /** triggers when idle */ + void (*on_idle)(struct discord *client); + /** triggers once per loop cycle */ + void (*on_cycle)(struct discord *client); + + /** user arbitrary data @see discord_set_data() */ + void *data; + + /** keep tab of amount of worker threads being used by client */ + struct { + /** amount of worker-threads currently being used by client */ + int count; + /** synchronize `count` between workers */ + pthread_mutex_t lock; + /** notify of `count` decrement */ + pthread_cond_t cond; + } *workers; + + /** config.json file contents if client was initialized with + * discord_from_json() */ + struct ccord_szbuf file; +}; + +/** @} DiscordInternal */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* DISCORD_INTERNAL_H */ diff --git a/include/concord/discord-request.h b/include/concord/discord-request.h new file mode 100644 index 0000000..9d17f5d --- /dev/null +++ b/include/concord/discord-request.h @@ -0,0 +1,101 @@ +/** + * @file discord-request.h + * @ingroup DiscordInternalREST + * @author Cogmasters + * @brief Generic macros for initializing a @ref discord_attributes + */ + +#ifndef DISCORD_REQUEST_H +#define DISCORD_REQUEST_H + +/* helper typedefs for casting */ +typedef void (*cast_done_typed)(struct discord *, + struct discord_response *, + const void *); +typedef void (*cast_init)(void *); +typedef void (*cast_cleanup)(void *); +typedef size_t (*cast_from_json)(const char *, size_t, void *); + +/* helper typedef for getting sizeof of `struct discord_ret` common fields */ +typedef struct { + DISCORD_RET_DEFAULT_FIELDS; +} discord_ret_default_fields; + +#define _RET_COPY_TYPED(dest, src) \ + do { \ + memcpy(&(dest), &(src), sizeof(discord_ret_default_fields)); \ + (dest).has_type = true; \ + (dest).done.typed = (cast_done_typed)(src).done; \ + (dest).sync = (src).sync; \ + } while (0) + +#define _RET_COPY_TYPELESS(dest, src) \ + do { \ + memcpy(&(dest), &(src), sizeof(discord_ret_default_fields)); \ + (dest).has_type = false; \ + (dest).done.typeless = (src).done; \ + (dest).sync = (void *)(src).sync; \ + } while (0) + +/** + * @brief Helper for setting attributes for a specs-generated return struct + * + * @param[out] attr @ref discord_attributes handler to be initialized + * @param[in] type datatype of the struct + * @param[in] ret dispatch attributes + * @param[in] _reason reason for request (if available) + */ +#define DISCORD_ATTR_INIT(attr, type, ret, _reason) \ + do { \ + (attr).response.size = sizeof(struct type); \ + (attr).response.init = (cast_init)type##_init; \ + (attr).response.from_json = (cast_from_json)type##_from_json; \ + (attr).response.cleanup = (cast_cleanup)type##_cleanup; \ + (attr).reason = _reason; \ + if (ret) _RET_COPY_TYPED(attr.dispatch, *ret); \ + } while (0) + +/** + * @brief Helper for setting attributes for a specs-generated list + * + * @param[out] attr @ref discord_attributes handler to be initialized + * @param[in] type datatype of the list + * @param[in] ret dispatch attributes + * @param[in] _reason reason for request (if available) + */ +#define DISCORD_ATTR_LIST_INIT(attr, type, ret, _reason) \ + do { \ + (attr).response.size = sizeof(struct type); \ + (attr).response.from_json = (cast_from_json)type##_from_json; \ + (attr).response.cleanup = (cast_cleanup)type##_cleanup; \ + (attr).reason = _reason; \ + if (ret) _RET_COPY_TYPED(attr.dispatch, *ret); \ + } while (0) + +/** + * @brief Helper for setting attributes for attruests that doensn't expect a + * response object + * + * @param[out] attr @ref discord_attributes handler to be initialized + * @param[in] ret dispatch attributes + * @param[in] _reason reason for request (if available) + */ +#define DISCORD_ATTR_BLANK_INIT(attr, ret, _reason) \ + do { \ + (attr).reason = _reason; \ + if (ret) _RET_COPY_TYPELESS(attr.dispatch, *ret); \ + } while (0) + +/** + * @brief Helper for initializing attachments ids + * + * @param[in,out] attchs a @ref discord_attachments to have its IDs initialized + */ +#define DISCORD_ATTACHMENTS_IDS_INIT(attchs) \ + do { \ + for (int i = 0; i < attchs->size; ++i) { \ + attchs->array[i].id = (u64snowflake)i; \ + } \ + } while (0) + +#endif /* DISCORD_REQUEST_H */ diff --git a/include/concord/discord-response.h b/include/concord/discord-response.h new file mode 100644 index 0000000..8f643a7 --- /dev/null +++ b/include/concord/discord-response.h @@ -0,0 +1,192 @@ +/** + * @file discord-response.h + * @author Cogmasters + * @brief Generic macros for initializing a @ref discord_response and return + * handles + */ + +#ifndef DISCORD_RESPONSE_H +#define DISCORD_RESPONSE_H + +/** @brief The response for the completed request */ +struct discord_response { + /** user arbitrary data provided at @ref discord_ret */ + void *data; + /** kept concord's parameter provided at @ref discord_ret */ + const void *keep; + /** request completion status @see @ref ConcordError */ + CCORDcode code; + /** the JSON object in case of a @ref CCORD_OK, or the JSON error + * object in case of a @ref CCORD_DISCORD_JSON_CODE + * + * @see https://discord.com/developers/docs/reference#error-messages */ + struct ccord_szbuf_readonly json; +}; + +/****************************************************************************** + * Templates for generating type-safe return handles for async requests + ******************************************************************************/ + +/** + * @brief Macro containing common fields for `struct discord_ret*` datatypes + * @note this exists for alignment purposes + */ +#define DISCORD_RET_DEFAULT_FIELDS \ + /** user arbitrary data to be passed to `done` or `fail` callbacks */ \ + void *data; \ + /** cleanup method to be called for `data`, once its no longer \ + being referenced */ \ + void (*cleanup)(struct discord * client, void *data); \ + /** Concord callback parameter the client wish to keep reference */ \ + const void *keep; \ + /** if `true` then request will be prioritized over already enqueued \ + requests */ \ + bool high_priority; \ + /** optional callback to be executed on a failed request */ \ + void (*fail)(struct discord * client, struct discord_response * resp) + +#define DISCORD_RETURN(_type) \ + /** @brief Request's return context */ \ + struct discord_ret_##_type { \ + DISCORD_RET_DEFAULT_FIELDS; \ + /** optional callback to be executed on a successful request */ \ + void (*done)(struct discord * client, \ + struct discord_response *resp, \ + const struct discord_##_type *ret); \ + /** if an address is provided, then request will block the thread and \ + perform on-spot. \ + On success the response object will be written to the address, \ + unless enabled with @ref DISCORD_SYNC_FLAG */ \ + struct discord_##_type *sync; \ + } + +/** @brief Request's return context */ +struct discord_ret { + DISCORD_RET_DEFAULT_FIELDS; + /** optional callback to be executed on a successful request */ + void (*done)(struct discord *client, struct discord_response *resp); + /** if `true`, request will block the thread and perform on-spot */ + bool sync; +}; + +/** @brief flag for enabling `sync` mode without expecting a datatype return */ +#define DISCORD_SYNC_FLAG ((void *)-1) + +/** @addtogroup DiscordAPIOAuth2 + * @{ */ +DISCORD_RETURN(application); +DISCORD_RETURN(auth_response); +/** @} DiscordAPIOAuth2 */ + +/** @addtogroup DiscordAPIAuditLog + * @{ */ +DISCORD_RETURN(audit_log); +/** @} DiscordAPIAuditLog */ + +/** @addtogroup DiscordAPIAutoModeration + * @{ */ +DISCORD_RETURN(auto_moderation_rule); +DISCORD_RETURN(auto_moderation_rules); +/** @} DiscordAPIAutoModeration */ + +/** @addtogroup DiscordAPIChannel + * @{ */ +DISCORD_RETURN(channel); +DISCORD_RETURN(channels); +DISCORD_RETURN(message); +DISCORD_RETURN(messages); +DISCORD_RETURN(followed_channel); +DISCORD_RETURN(thread_members); +DISCORD_RETURN(thread_response_body); +/** @} DiscordAPIChannel */ + +/** @addtogroup DiscordAPIEmoji + * @{ */ +DISCORD_RETURN(emoji); +DISCORD_RETURN(emojis); +/** @} DiscordAPIEmoji */ + +/** @addtogroup DiscordAPIGuild + * @{ */ +DISCORD_RETURN(guild); +DISCORD_RETURN(guilds); +DISCORD_RETURN(guild_preview); +DISCORD_RETURN(guild_member); +DISCORD_RETURN(guild_members); +DISCORD_RETURN(guild_widget); +DISCORD_RETURN(guild_widget_settings); +DISCORD_RETURN(ban); +DISCORD_RETURN(bans); +DISCORD_RETURN(role); +DISCORD_RETURN(roles); +DISCORD_RETURN(welcome_screen); +DISCORD_RETURN(integrations); +DISCORD_RETURN(prune_count); +/** @} DiscordAPIGuild */ + +/** @addtogroup DiscordAPIGuildScheduledEvent + * @{ */ +DISCORD_RETURN(guild_scheduled_event); +DISCORD_RETURN(guild_scheduled_events); +DISCORD_RETURN(guild_scheduled_event_users); +/** @} DiscordAPIGuildScheduledEvent */ + +/** @addtogroup DiscordAPIGuildTemplate + * @{ */ +DISCORD_RETURN(guild_template); +DISCORD_RETURN(guild_templates); +/** @} DiscordAPIGuildTemplate */ + +/** @addtogroup DiscordAPIInvite + * @{ */ +DISCORD_RETURN(invite); +DISCORD_RETURN(invites); +/** @} DiscordAPIInvite */ + +/** @addtogroup DiscordAPIStageInstance + * @{ */ +DISCORD_RETURN(stage_instance); +/** @} DiscordAPIStageInstance */ + +/** @addtogroup DiscordAPISticker + * @{ */ +DISCORD_RETURN(sticker); +DISCORD_RETURN(stickers); +DISCORD_RETURN(list_nitro_sticker_packs); +/** @} DiscordAPISticker */ + +/** @addtogroup DiscordAPIUser + * @{ */ +DISCORD_RETURN(user); +DISCORD_RETURN(users); +DISCORD_RETURN(connections); +/** @} DiscordAPIUser */ + +/** @addtogroup DiscordAPIVoice + * @{ */ +DISCORD_RETURN(voice_regions); +/** @} DiscordAPIVoice */ + +/** @addtogroup DiscordAPIWebhook + * @{ */ +DISCORD_RETURN(webhook); +DISCORD_RETURN(webhooks); +/** @} DiscordAPIWebhook */ + +/** @addtogroup DiscordAPIInteractionsApplicationCommand + * @ingroup DiscordAPIInteractions + * @{ */ +DISCORD_RETURN(application_command); +DISCORD_RETURN(application_commands); +DISCORD_RETURN(application_command_permission); +DISCORD_RETURN(application_command_permissions); +DISCORD_RETURN(guild_application_command_permissions); +/** @} DiscordAPIInteractionsApplicationCommand */ + +/** @addtogroup DiscordAPIInteractionsReact + * @ingroup DiscordAPIInteractions + * @{ */ +DISCORD_RETURN(interaction_response); +/** @} DiscordAPIInteractionsReact */ + +#endif /* DISCORD_RESPONSE_H */ diff --git a/include/concord/discord-worker.h b/include/concord/discord-worker.h new file mode 100644 index 0000000..ad1e084 --- /dev/null +++ b/include/concord/discord-worker.h @@ -0,0 +1,54 @@ +/** + * @file discord-worker.h + * @author Cogmasters + * @brief Global threadpool + */ + +#ifndef DISCORD_WORKER_H +#define DISCORD_WORKER_H + +#include "concord-error.h" + +/* forward declaration */ +struct discord; +/**/ + +/** @defgroup DiscordInternalWorker Global threadpool + * @ingroup DiscordInternal + * @brief A global threadpool for worker-threads handling + * @{ */ + +/** + * @brief Initialize global threadpool and priority queue + * + * @param flags unused for now, but reserved for future use + * @CCORD_return + */ +CCORDcode discord_worker_global_init(long flags); + +/** @brief Cleanup global threadpool and priority queue */ +void discord_worker_global_cleanup(void); + +/** + * @brief Run a callback from a worker thread + * + * @param client the client that will be using the worker thread + * @param callback user callback to be executed + * @param data user data to be passed to callback + * @CCORD_return + */ +CCORDcode discord_worker_add(struct discord *client, + void (*callback)(void *data), + void *data); + +/** + * @brief Wait until worker-threads being used by `client` have been joined + * + * @param client the client currently using a worker thread + * @CCORD_return + */ +CCORDcode discord_worker_join(struct discord *client); + +/** @} DiscordInternalWorker */ + +#endif /* DISCORD_WORKER_H */ diff --git a/include/concord/discord.h b/include/concord/discord.h new file mode 100644 index 0000000..6b5ef03 --- /dev/null +++ b/include/concord/discord.h @@ -0,0 +1,603 @@ +/** + * @file discord.h + * @author Cogmasters + * @brief Public functions and datatypes + * + * These symbols are organized in a intuitive fashion to be easily + * matched to the official Discord API docs + * @see https://discord.com/developers/docs/intro + */ + +#ifndef DISCORD_H +#define DISCORD_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include + +#include "concord-error.h" +#include "types.h" +#include "io_poller.h" +#define LOGMOD_HEADER +#include "logmod.h" + +#ifndef DISCORD_VERSION +/** + * @brief The Discord API version to use + * @warning only change this if you know what you are doing! + */ +#define DISCORD_VERSION "10" +#endif + +#define DISCORD_API_BASE_URL "https://discord.com/api/v" DISCORD_VERSION +#define DISCORD_GATEWAY_URL_SUFFIX "?v=" DISCORD_VERSION "&encoding=json" + +/* forward declaration */ +struct discord; +/**/ + +#include "discord_codecs.h" +#include "discord-response.h" + +/** @defgroup DiscordClient Client */ + +/** @defgroup DiscordConstants Constants + * @brief Macros for constants defined by Discord + * @note macros assume the worst-case scenario for strings, where each + * character is 4 bytes long (UTF8) + * @{ */ + +/** @defgroup DiscordConstantsGeneral General lengths + * @brief Max length for general fields + * @{ */ +#define DISCORD_MAX_NAME_LEN 4 * 100 + 1 +#define DISCORD_MAX_TOPIC_LEN 4 * 1024 + 1 +#define DISCORD_MAX_DESCRIPTION_LEN 4 * 2048 + 1 +#define DISCORD_MAX_USERNAME_LEN 4 * 32 + 1 +#define DISCORD_MAX_DISCRIMINATOR_LEN 4 + 1 +#define DISCORD_MAX_REASON_LEN 4 * 512 + 1 +#define DISCORD_MAX_MESSAGE_LEN 4 * 2000 + 1 +#define DISCORD_MAX_PAYLOAD_LEN 4 * 4096 + 1 +/** @} DiscordConstantsGeneral */ + +/** @defgroup DiscordConstantsEmbed Embed lengths + * @brief Max length for embed fields + * @{ */ +#define DISCORD_EMBED_TITLE_LEN 4 * 256 + 1 +#define DISCORD_EMBED_DESCRIPTION_LEN 4 * 4096 + 1 +#define DISCORD_EMBED_MAX_FIELDS 25 +#define DISCORD_EMBED_FIELD_NAME_LEN 4 * 256 + 1 +#define DISCORD_EMBED_FIELD_VALUE_LEN 4 * 1024 + 1 +#define DISCORD_EMBED_FOOTER_TEXT_LEN 4 * 2048 + 1 +#define DISCORD_EMBED_AUTHOR_NAME_LEN 4 * 256 + 1 +/** @} DiscordConstantsEmbed */ + +/** @defgroup DiscordConstantsWebhook Webhook lengths + * @brief Max length for embed fields + * @{ */ +#define DISCORD_WEBHOOK_NAME_LEN 4 * 80 + 1 +/** @} DiscordConstantsWebhook */ + +/** @} DiscordConstants */ + +/** @addtogroup ConcordError + * @{ */ + +/* XXX: As new values are added, discord_strerror() and + * discord_code_as_string() should be updated accordingly! */ +/** @defgroup DiscordError Discord error codes + * @brief Error codes triggered from Discord + * @{ */ + +/** Alias for @ref CCORD_OK */ +#define CCORD_DISCORD_OK CCORD_OK +/** action is pending (ex: request has been enqueued and will be performed + * later) */ +#define CCORD_PENDING 1 +/** received a JSON error message */ +#define CCORD_DISCORD_JSON_CODE 100 +/** bad authentication token */ +#define CCORD_DISCORD_BAD_AUTH 101 +/** being ratelimited */ +#define CCORD_DISCORD_RATELIMIT 102 +/** couldn't establish connection to Discord */ +#define CCORD_DISCORD_CONNECTION 103 + +/** + * @brief Return the value of CCORDcode as a string + * + * @param code the CCORDcode value + * @return the enum value as a string + */ +const char *discord_code_as_string(CCORDcode code); + +/** + * @brief Return the meaning of CCORDcode + * + * @param code the CCORDcode value + * @param client @note unused parameter + * @return a string containing the code meaning + */ +const char *discord_strerror(CCORDcode code, struct discord *client); + +/** @} DiscordError */ + +/** @} ConcordError */ + +/** @defgroup DiscordAPI API + * @brief The Discord public API supported by Concord + * @{ */ + +#include "audit_log.h" +#include "auto_moderation.h" +#include "invite.h" +#include "channel.h" +#include "emoji.h" +#include "guild.h" +#include "guild_scheduled_event.h" +#include "guild_template.h" +#include "stage_instance.h" +#include "sticker.h" +#include "user.h" +#include "voice.h" +#include "webhook.h" +#include "gateway.h" +#include "oauth2.h" +/** @defgroup DiscordAPIInteractions Interactions + * @brief Interactions public API supported by Concord + * @{ */ +#include "application_command.h" +#include "interaction.h" +/** @} DiscordAPIInteractions */ + +/** @} DiscordAPI */ + +/** @addtogroup DiscordClient + * @brief Client functions and datatypes + * @{ */ + +/** @struct discord */ + +#include "discord-cache.h" +#include "discord-events.h" + +/** + * @brief Claim ownership of a resource provided by Concord + * @see discord_unclaim() + * + * @param client the client initialized with discord_from_token() + * @param data a resource provided by Concord + * @return pointer to `data` (for one-liners) + */ +#define discord_claim(client, data) (__discord_claim(client, data), data) +void __discord_claim(struct discord *client, const void *data); + +/** + * @brief Unclaim ownership of a resource provided by Concord + * @note this will make the resource eligible for cleanup, so this should + * only be called when you no longer plan to use it + * @see discord_claim() + * + * @param client the client initialized with discord_from_token() + * @param data a resource provided by Concord, that has been + * previously claimed with discord_claim() + */ +void discord_unclaim(struct discord *client, const void *data); + +/** @deprecated since v3.0.0, keep backwards compatibility */ +#define ccord_global_init() + +/** @deprecated since v3.0.0, keep backwards compatibility */ +#define ccord_global_cleanup() + +/** + * @brief Gracefully notify all Discord connections for shutting down + * + * @note this function will not wait before returning, and will + * return immediately. The shutdown process will be handled + * in the background. + */ +void discord_shutdown_all(void); + +/** + * @brief Check if all Discord connections shutting down is in progress + * + * @return true if all shutdown is in progress, false otherwise + */ +bool discord_shutdown_all_ongoing(void); + +/** + * @brief Creates a Discord Client handle from a token + * @see discord_get_logmod() to configure logging behavior + * + * @param token the bot token + * @return the newly created Discord Client handle + */ +struct discord *discord_from_token(const char token[]); + +/** + * @brief Creates a Discord Client handle from a `config.json` file + * @see discord_get_logmod() to configure logging behavior + * + * @param config_file the `config.json` file name + * @return the newly created Discord Client handle + */ +struct discord *discord_from_json(const char config_file[]); + +/** + * @brief The Discord configuration handler + * + * This struct is used to store the Discord client configuration + */ +struct discord_config { + /** the bot token */ + char *token; + struct { + /** minimum logging level */ + enum logmod_levels level; + /** silence terminal logging */ + bool quiet; + /** enable color to terminal logging */ + bool color; + /** overwrite existing files */ + bool overwrite; + /* the trace log file */ + FILE *trace; + /* the http log file */ + FILE *http; + /* the ws log file */ + FILE *ws; + struct { + size_t size; + char **ids; + } disable; /**< list of 'id' that should be ignored */ + } log; /**< logging directives */ +}; + +/** + * @brief Creates a Discord Client handle from a + * @ref discord_config structure + * @see discord_get_logmod() to configure logging behavior + * + * @param config the @ref discord_config structure + * @return the newly created Discord Client handle + */ +struct discord *discord_from_config(const struct discord_config *config); + +/** + * @brief Backwards compatible alias for discord_from_token() + * @deprecated since v3.0.0 + */ +#define discord_init discord_from_token + +/** + * @brief Backwards compatible alias for discord_from_json() + * @deprecated since v3.0.0 + */ +#define discord_config_init discord_from_json + +/** + * @brief Get the contents from the config file field + * @note your bot **MUST** have been initialized with discord_from_json() + * + * @code{.c} + * // Assume the following custom config.json field to be extracted + * // "field": { "foo": "a string", "bar": 1234 } + * + * ... + * struct ccord_szbuf_readonly value; + * char foo[128]; + * long bar; + * + * // field.foo + * value = discord_config_get_field(client, (char *[2]){ "field", "foo" }, 2); + * snprintf(foo, sizeof(foo), "%.*s", (int)value.size, value.start); + * // field.bar + * value = discord_config_get_field(client, (char *[2]){ "field", "bar" }, 2); + * bar = strtol(value.start, NULL, 10); + * + * printf("%s %ld", foo, bar); // "a string" 1234 + * @endcode + * + * @param client the client created with discord_from_json() + * @param path the JSON key path + * @param depth the path depth + * @return a read-only sized buffer containing the field's contents + */ +struct ccord_szbuf_readonly discord_config_get_field(struct discord *client, + char *const path[], + unsigned depth); + +/** + * @brief Clone a discord client + * + * Should be called before entering a thread, to ensure each thread + * has its own client instance with unique buffers, url and headers + * @param orig the original client created with discord_from_token() + * @return the client clone + */ + +struct discord *discord_clone(const struct discord *orig); + +/** + * @brief Free a Discord Client handle + * + * @param client the client created with discord_from_token() + */ + +void discord_cleanup(struct discord *client); + +/** + * @brief Get the client's cached user + * + * @param client the client created with discord_from_token() + * @warning the returned structure should NOT be modified + */ + +const struct discord_user *discord_get_self(struct discord *client); + +/** + * @brief Start a connection to the Discord Gateway + * + * @param client the client created with discord_from_token() + * @CCORD_return + */ +CCORDcode discord_run(struct discord *client); + +/** + * @brief Gracefully shutdown an ongoing Discord connection + * + * @param client the client created with discord_from_token() + */ +void discord_shutdown(struct discord *client); + +/** + * @brief Gracefully reconnects an ongoing Discord connection + * + * @param client the client created with discord_from_token() + * @param resume true to attempt to resume to previous session, + * false restart a fresh session + */ +void discord_reconnect(struct discord *client, bool resume); + +/** + * @brief Store user arbitrary data that can be retrieved by discord_get_data() + * + * @param client the client created with discord_from_token() + * @param data user arbitrary data + * @return pointer to user data + * @warning the user should provide their own locking mechanism to protect + * its data from race conditions + */ +void *discord_set_data(struct discord *client, void *data); + +/** + * @brief Receive user arbitrary data stored with discord_set_data() + * + * @param client the client created with discord_from_token() + * @return pointer to user data + * @warning the user should provide their own locking mechanism to protect + * its data from race conditions + */ +void *discord_get_data(struct discord *client); + +/** + * @brief Get the client WebSockets ping + * @note Only works after a connection has been established via + * discord_run() + * + * @param client the client created with discord_from_token() + * @return the ping in milliseconds + */ +int discord_get_ping(struct discord *client); + +/** + * @brief Get the current timestamp (in milliseconds) + * + * @param client the client created with discord_from_token() + * @return the timestamp in milliseconds + */ +uint64_t discord_timestamp(struct discord *client); + +/** + * @brief Get the current timestamp (in microseconds) + * + * @param client the client created with discord_from_token() + * @return the timestamp in microseconds + */ +uint64_t discord_timestamp_us(struct discord *client); + +/** + * @brief Retrieve client's logging module for configuration purposes + * @see logmod.h + * + * @param client the client created with discord_from_token() + * @return the client's logging manager + */ +struct logmod *discord_get_logmod(struct discord *client); + +/** + * @brief get the io_poller used by the discord client + * + * @param client the client created with discord_from_token() + * @return struct io_poller* + */ +struct io_poller *discord_get_io_poller(struct discord *client); + +/** @addtogroup DiscordTimer Timer + * @brief Schedule callbacks to be called in the future + * @{ */ + +/* forward declaration */ +struct discord_timer; +/**/ + +/** @brief callback to be used with struct discord_timer */ +typedef void (*discord_ev_timer)(struct discord *client, + struct discord_timer *ev); + +/** @brief flags used to change behaviour of timer */ +enum discord_timer_flags { + /** use milliseconds for interval and start_time */ + DISCORD_TIMER_MILLISECONDS = 0, + /** use microseconds for interval and start_time */ + DISCORD_TIMER_MICROSECONDS = 1 << 0, + /** whether or not timer is marked for deletion */ + DISCORD_TIMER_DELETE = 1 << 1, + /** automatically delete a timer once its repeat counter runs out */ + DISCORD_TIMER_DELETE_AUTO = 1 << 2, + /** timer has been canceled. user should cleanup only */ + DISCORD_TIMER_CANCELED = 1 << 3, + /** flag is set when on_tick callback has been called */ + DISCORD_TIMER_TICK = 1 << 4, + /** used in discord_timer_ctl to get the timer's data */ + DISCORD_TIMER_GET = 1 << 5, + /** timer should run using a fixed interval based on start time */ + DISCORD_TIMER_INTERVAL_FIXED = 1 << 6, +}; + +/** @brief struct used for modifying, and getting info about a timer */ +struct discord_timer { + /** the identifier used for the timer. 0 creates a new timer */ + unsigned id; + /** the flags used to manipulate the timer */ + enum discord_timer_flags flags; + /** (nullable) the callback that should be called when timer triggers */ + discord_ev_timer on_tick; + /** (nullable) the callback for status updates timer->flags + * will have: DISCORD_TIMER_CANCELED, and DISCORD_TIMER_DELETE */ + discord_ev_timer on_status_changed; + /** user data */ + void *data; + /** delay before timer should start */ + int64_t delay; + /** interval that the timer should repeat at. must be >= 0 */ + int64_t interval; + /** how many times a timer should repeat (-1 == infinity) */ + int64_t repeat; +}; + +/** + * @brief modifies or creates a timer + * + * @param client the client created with discord_from_token() + * @param timer the timer that should be modified + * @return the id of the timer + */ +unsigned discord_timer_ctl(struct discord *client, + struct discord_timer *timer); + +/** + * @brief creates a one shot timer that automatically + * deletes itself upon completion + * + * @param client the client created with discord_from_token() + * @param on_tick_cb (nullable) the callback that should be called when timer + * triggers + * @param on_status_changed_cb (nullable) the callback for status updates + * timer->flags will have: DISCORD_TIMER_CANCELED, and DISCORD_TIMER_DELETE + * @param data user data + * @param delay delay before timer should start in milliseconds + * @return the id of the timer + */ +unsigned discord_timer(struct discord *client, + discord_ev_timer on_tick_cb, + discord_ev_timer on_status_changed_cb, + void *data, + int64_t delay); + +/** + * @brief creates a repeating timer that automatically + * deletes itself upon completion + * + * @param client the client created with discord_from_token() + * @param on_tick_cb (nullable) the callback that should be called when timer + * triggers + * @param on_status_changed_cb (nullable) the callback for status updates + * timer->flags will have: DISCORD_TIMER_CANCELED, and DISCORD_TIMER_DELETE + * @param data user data + * @param delay delay before timer should start in milliseconds + * @param interval interval between runs. (-1 == disable repeat) + * @param repeat repetitions (-1 == infinity) + * @return the id of the timer + */ +unsigned discord_timer_interval(struct discord *client, + discord_ev_timer on_tick_cb, + discord_ev_timer on_status_changed_cb, + void *data, + int64_t delay, + int64_t interval, + int64_t repeat); + +/** + * @brief get the data associated with the timer + * + * @param client the client created with discord_from_token() + * @param id id of the timer + * @param timer where to copy the timer data to + * @return true on success + */ +bool discord_timer_get(struct discord *client, + unsigned id, + struct discord_timer *timer); + +/** + * @brief starts a timer + * + * @param client the client created with discord_from_token() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_start(struct discord *client, unsigned id); + +/** + * @brief stops a timer + * + * @param client the client created with discord_from_token() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_stop(struct discord *client, unsigned id); + +/** + * @brief cancels a timer, + * this will delete the timer if DISCORD_TIMER_DELETE_AUTO is enabled + * + * @param client the client created with discord_from_token() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_cancel(struct discord *client, unsigned id); + +/** + * @brief deletes a timer + * + * @param client the client created with discord_from_token() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_delete(struct discord *client, unsigned id); + +/** + * @brief cancels, and deletes a timer + * + * @param client the client created with discord_from_token() + * @param id id of the timer + * @return true on success + */ +bool discord_timer_cancel_and_delete(struct discord *client, unsigned id); + +/** @example timers.c + * Demonstrates the Timer API for callback scheduling */ + +/** @} DiscordTimer */ +/** @} DiscordClient */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* DISCORD_H */ diff --git a/include/concord/discord_codecs.PRE.h b/include/concord/discord_codecs.PRE.h new file mode 100644 index 0000000..a93b9f4 --- /dev/null +++ b/include/concord/discord_codecs.PRE.h @@ -0,0 +1,74 @@ +#include "gencodecs.h" + +#ifdef GENCODECS_HEADER +PP_INCLUDE() +PP_INCLUDE("carray.h") +PP_INCLUDE("cog-utils.h") +PP_INCLUDE("types.h") +PP_INCLUDE("concord-error.h") +#endif + +/* Custom JSON encoding macros */ +#define GENCODECS_JSON_ENCODER_PTR_json_char(b, buf, size, _var, _type) \ + if (0 > (code = jsonb_token_auto(b, buf, size, _var, \ + _var ? strlen(_var) : 0))) \ + return code +#define GENCODECS_JSON_ENCODER_size_t(b, buf, size, _var, _type) \ + { \ + char tok[64]; \ + int toklen; \ + toklen = sprintf(tok, "%zu", _var); \ + if (0 > (code = jsonb_token_auto(b, buf, size, tok, toklen))) \ + return code; \ + } +#define GENCODECS_JSON_ENCODER_uint64_t(b, buf, size, _var, _type) \ + { \ + char tok[64]; \ + int toklen; \ + toklen = sprintf(tok, "%" PRIu64, _var); \ + if (0 > (code = jsonb_string_auto(b, buf, size, tok, toklen))) \ + return code; \ + } +#define GENCODECS_JSON_ENCODER_u64snowflake GENCODECS_JSON_ENCODER_uint64_t +#define GENCODECS_JSON_ENCODER_u64bitmask GENCODECS_JSON_ENCODER_uint64_t +#define GENCODECS_JSON_ENCODER_u64unix_ms(b, buf, size, _var, _type) \ + { \ + char tok[64]; \ + int toklen = cog_unix_ms_to_iso8601(tok, sizeof(tok), _var); \ + if (0 > (code = jsonb_string_auto(b, buf, size, tok, toklen))) \ + return code; \ + } + +/* Custom JSON decoding macros */ +#define GENCODECS_JSON_DECODER_PTR_json_char(_f, _js, _var, _type) \ + if (_f) { \ + _var = _gc_strndup(js + _f->v->start, _f->v->end - _f->v->start); \ + ret += _f->v->end - _f->v->start; \ + } +#define GENCODECS_JSON_DECODER_size_t(_f, _js, _var, _type) \ + if (_f && _f->v->type == JSMN_PRIMITIVE) \ + _var = (size_t)strtoull(_js + _f->v->start, NULL, 10) +#define GENCODECS_JSON_DECODER_uint64_t(_f, _js, _var, _type) \ + if (_f) sscanf(_js + _f->v->start, "%" SCNu64, &_var) +#define GENCODECS_JSON_DECODER_u64snowflake GENCODECS_JSON_DECODER_uint64_t +#define GENCODECS_JSON_DECODER_u64bitmask GENCODECS_JSON_DECODER_uint64_t +#define GENCODECS_JSON_DECODER_u64unix_ms(_f, _js, _var, _type) \ + if (_f && _f->v->type == JSMN_STRING) \ + cog_iso8601_to_unix_ms(_js + _f->v->start, _f->v->end - _f->v->start, &_var) + +/* Custom field macros */ +#define FIELD_SNOWFLAKE(_name) \ + FIELD_PRINTF(_name, u64snowflake, "\"%" PRIu64 "\"", "%" SCNu64) +#define FIELD_BITMASK(_name) \ + FIELD_PRINTF(_name, u64bitmask, "\"%" PRIu64 "\"", "%" SCNu64) +#define FIELD_TIMESTAMP(_name) \ + FIELD_CUSTOM(_name, #_name, u64unix_ms, DECOR_BLANK, INIT_BLANK, \ + CLEANUP_BLANK, GENCODECS_JSON_ENCODER_u64unix_ms, \ + GENCODECS_JSON_DECODER_u64unix_ms, (u64unix_ms)0) + +/* if GENCODECS_READ is not specified then generate for all files */ +#ifndef GENCODECS_READ +#define GENCODECS_READ "all.PRE.h" +#endif + +#include "gencodecs-process.PRE.h" diff --git a/include/concord/discord_codecs.h b/include/concord/discord_codecs.h new file mode 100644 index 0000000..58b8e4d --- /dev/null +++ b/include/concord/discord_codecs.h @@ -0,0 +1,10530 @@ +#ifndef DISCORD_CODECS_H +#define DISCORD_CODECS_H +#include +#include +#include +#include "carray.h" +#include "cog-utils.h" +#include "types.h" +#include "concord-error.h" +struct strings { int size; + char * * array; +int realsize; }; +struct json_values { int size; + json_char * * array; +int realsize; }; +struct snowflakes { int size; + u64snowflake *array; +int realsize; }; +struct bitmasks { int size; + u64bitmask *array; +int realsize; }; +struct integers { int size; + int *array; +int realsize; }; +#define DISCORD_APPLICATION_GATEWAY_PRESENCE 1 << 12 +#define DISCORD_APPLICATION_GATEWAY_PRESENCE_LIMITED 1 << 13 +#define DISCORD_APPLICATION_GATEWAY_GUILD_MEMBERS 1 << 14 +#define DISCORD_APPLICATION_GATEWAY_GUILD_MEMBERS_LIMITED 1 << 15 +#define DISCORD_APPLICATION_VERIFICATION_PENDING_GUILD_LIMIT 1 << 16 +#define DISCORD_APPLICATION_EMBEDDED 1 << 17 +#define DISCORD_APPLICATION_GATEWAY_MESSAGE_CONTENT 1 << 18 +#define DISCORD_APPLICATION_GATEWAY_MESSAGE_CONTENT_LIMITED 1 << 19 +struct discord_application { + u64snowflake id; + char * name; + char * icon; + char * description; + + struct strings * rpc_origins; + + bool bot_public; + bool bot_require_code_grant; + char * terms_of_service_url; + char * privacy_policy_url; + + struct discord_user * owner; + + char * summary; + char * verify_key; + + struct discord_team * team; + + u64snowflake guild_id; + u64snowflake primary_sku_id; + char * slug; + char * cover_image; + u64bitmask flags; +}; +struct discord_install_params { + struct strings * scopes; + u64bitmask permissions; +}; +enum discord_audit_log_events { + DISCORD_AUDIT_LOG_GUILD_UPDATE = 1, + DISCORD_AUDIT_LOG_CHANNEL_CREATE = 10, + DISCORD_AUDIT_LOG_CHANNEL_UPDATE = 11, + DISCORD_AUDIT_LOG_CHANNEL_DELETE = 12, + DISCORD_AUDIT_LOG_CHANNEL_OVERWRITE_CREATE = 13, + DISCORD_AUDIT_LOG_CHANNEL_OVERWRITE_UPDATE = 14, + DISCORD_AUDIT_LOG_CHANNEL_OVERWRITE_DELETE = 15, + DISCORD_AUDIT_LOG_MEMBER_KICK = 20, + DISCORD_AUDIT_LOG_MEMBER_PRUNE = 21, + DISCORD_AUDIT_LOG_MEMBER_BAN_ADD = 22, + DISCORD_AUDIT_LOG_MEMBER_BAN_REMOVE = 23, + DISCORD_AUDIT_LOG_MEMBER_UPDATE = 24, + DISCORD_AUDIT_LOG_MEMBER_ROLE_UPDATE = 25, + DISCORD_AUDIT_LOG_MEMBER_MOVE = 26, + DISCORD_AUDIT_LOG_MEMBER_DISCONNECT = 27, + DISCORD_AUDIT_LOG_BOT_ADD = 28, + DISCORD_AUDIT_LOG_ROLE_CREATE = 30, + DISCORD_AUDIT_LOG_ROLE_UPDATE = 31, + DISCORD_AUDIT_LOG_ROLE_DELETE = 32, + DISCORD_AUDIT_LOG_INVITE_CREATE = 40, + DISCORD_AUDIT_LOG_INVITE_UPDATE = 41, + DISCORD_AUDIT_LOG_INVITE_DELETE = 42, + DISCORD_AUDIT_LOG_WEBHOOK_CREATE = 50, + DISCORD_AUDIT_LOG_WEBHOOK_UPDATE = 51, + DISCORD_AUDIT_LOG_WEBHOOK_DELETE = 52, + DISCORD_AUDIT_LOG_EMOJI_CREATE = 60, + DISCORD_AUDIT_LOG_EMOJI_UPDATE = 61, + DISCORD_AUDIT_LOG_EMOJI_DELETE = 62, + DISCORD_AUDIT_LOG_MESSAGE_DELETE = 72, + DISCORD_AUDIT_LOG_MESSAGE_BULK_DELETE = 73, + DISCORD_AUDIT_LOG_MESSAGE_PIN = 74, + DISCORD_AUDIT_LOG_MESSAGE_UNPIN = 75, + DISCORD_AUDIT_LOG_INTEGRATION_CREATE = 80, + DISCORD_AUDIT_LOG_INTEGRATION_UPDATE = 81, + DISCORD_AUDIT_LOG_INTEGRATION_DELETE = 82, + DISCORD_AUDIT_LOG_STAGE_INSTANCE_CREATE = 83, + DISCORD_AUDIT_LOG_STAGE_INSTANCE_UPDATE = 84, + DISCORD_AUDIT_LOG_STAGE_INSTANCE_DELETE = 85, + DISCORD_AUDIT_LOG_STICKER_CREATE = 90, + DISCORD_AUDIT_LOG_STICKER_UPDATE = 91, + DISCORD_AUDIT_LOG_STICKER_DELETE = 92, + DISCORD_AUDIT_LOG_GUILD_SCHEDULED_EVENT_CREATE = 100, + DISCORD_AUDIT_LOG_GUILD_SCHEDULED_EVENT_UPDATE = 101, + DISCORD_AUDIT_LOG_GUILD_SCHEDULED_EVENT_DELETE = 102, + DISCORD_AUDIT_LOG_THREAD_CREATE = 110, + DISCORD_AUDIT_LOG_THREAD_UPDATE = 111, + DISCORD_AUDIT_LOG_THREAD_DELETE = 112, + DISCORD_AUDIT_LOG_APPLICATION_COMMAND_PERMISSION_UPDATE = 121, + DISCORD_AUDIT_LOG_AUTO_MODERATION_RULE_CREATE = 140, + DISCORD_AUDIT_LOG_AUTO_MODERATION_RULE_UPDATE = 141, + DISCORD_AUDIT_LOG_AUTO_MODERATION_RULE_DELETE = 142, + DISCORD_AUDIT_LOG_AUTO_MODERATION_BLOCK_MESSAGE = 143 +}; +struct discord_audit_log { + + struct discord_audit_log_entries * audit_log_entries; + + + struct discord_guild_scheduled_events * guild_scheduled_events; + + + struct discord_integrations * integrations; + + + struct discord_channels * threads; + + + struct discord_users * users; + + + struct discord_webhooks * webhooks; + +}; +struct discord_audit_log_entry { + u64snowflake target_id; + + struct discord_audit_log_changes * changes; + + u64snowflake user_id; + u64snowflake id; + + enum discord_audit_log_events action_type; + + + struct discord_optional_audit_entry_infos * options; + + char * reason; +}; +struct discord_audit_log_entries { int size; + struct discord_audit_log_entry *array; +int realsize; }; +struct discord_optional_audit_entry_info { + u64snowflake channel_id; + char * count; + char * delete_member_days; + u64snowflake id; + char * members_removed; + u64snowflake message_id; + char * role_name; + char * type; +}; +struct discord_optional_audit_entry_infos { int size; + struct discord_optional_audit_entry_info *array; +int realsize; }; +struct discord_audit_log_change { + json_char * new_value; + json_char * old_value; + char * key; +}; +struct discord_audit_log_changes { int size; + struct discord_audit_log_change *array; +int realsize; }; +struct discord_get_guild_audit_log { + u64snowflake user_id; + int action_type; + u64snowflake before; + + int limit; + +}; +enum discord_auto_moderation_trigger_types { + DISCORD_AUTO_MODERATION_KEYWORD = 1, + DISCORD_AUTO_MODERATION_HARMFUL_LINK = 2, + DISCORD_AUTO_MODERATION_SPAM = 3, + DISCORD_AUTO_MODERATION_KEYWORD_PRESET = 4 +}; +enum discord_auto_moderation_keyword_preset_types { + DISCORD_AUTO_MODERATION_PROFANITY = 1, + DISCORD_AUTO_MODERATION_SEXUAL_CONTENT = 2, + DISCORD_AUTO_MODERATION_SLURS = 3 +}; +enum discord_auto_moderation_event_types { + DISCORD_AUTO_MODERATION_MESSAGE_SEND = 1 +}; +enum discord_auto_moderation_action_types { + DISCORD_AUTO_MODERATION_ACTION_BLOCK_MESSAGE = 1, + DISCORD_AUTO_MODERATION_ACTION_SEND_ALERT_MESSAGE = 2, + DISCORD_AUTO_MODERATION_ACTION_TIMEOUT = 3 +}; +struct discord_auto_moderation_trigger_metadata { + struct strings * keyword_filter; + struct integers * presets; +}; +struct discord_auto_moderation_action { + + enum discord_auto_moderation_action_types type; + + + struct discord_auto_moderation_action_metadata * metadata; + +}; +struct discord_auto_moderation_actions { int size; + struct discord_auto_moderation_action *array; +int realsize; }; +struct discord_auto_moderation_action_metadata { + + u64snowflake channel_id; + + + int duration_seconds; + +}; +struct discord_auto_moderation_rule { + u64snowflake id; + u64snowflake guild_id; + char * name; + u64snowflake creator_id; + + enum discord_auto_moderation_event_types event_type; + + + enum discord_auto_moderation_trigger_types trigger_type; + + struct discord_auto_moderation_actions * actions; + struct discord_auto_moderation_trigger_metadata * trigger_metadata; + bool enabled; + struct snowflakes * exempt_roles; + struct snowflakes * exempt_channels; +}; +struct discord_auto_moderation_rules { int size; + struct discord_auto_moderation_rule *array; +int realsize; }; +struct discord_create_auto_moderation_rule { + char * reason; + char * name; + + enum discord_auto_moderation_event_types event_type; + + + enum discord_auto_moderation_trigger_types trigger_type; + + + struct discord_auto_moderation_actions * actions; + + + struct discord_auto_moderation_trigger_metadata * trigger_metadata; + + bool enabled; + + struct snowflakes * exempt_roles; + + + struct snowflakes * exempt_channels; + +}; +struct discord_modify_auto_moderation_rule { + char * reason; + + char * name; + + + enum discord_auto_moderation_event_types event_type; + + + struct discord_auto_moderation_trigger_metadata * trigger_metadata; + + + struct discord_auto_moderation_actions * actions; + + bool enabled; + + struct snowflakes * exempt_roles; + + + struct snowflakes * exempt_channels; + +}; +struct discord_delete_auto_moderation_rule { + char * reason; +}; +enum discord_invite_target_types { + DISCORD_INVITE_TARGET_STREAM = 1, + DISCORD_INVITE_TARGET_EMBEDDED_APPLICATION = 2 +}; +struct discord_invite { + char * code; + + struct discord_guild * guild; + + struct discord_channel * channel; + + struct discord_user * inviter; + + + enum discord_invite_target_types target_type; + + + struct discord_user * target_user; + + + struct discord_application * target_application; + + int approximate_presence_count; + int approximate_member_count; + + u64unix_ms expires_at; + + + struct discord_invite_stage_instance * stage_instance; + + + struct discord_guild_scheduled_event * guild_scheduled_event; + +}; +struct discord_invites { int size; + struct discord_invite *array; +int realsize; }; +struct discord_invite_metadata { + int uses; + int max_uses; + int max_age; + bool temporary; + + u64unix_ms created_at; + +}; +struct discord_invite_stage_instance { + + struct discord_guild_members * members; + + int participant_count; + int speaker_count; + char * topic; +}; +struct discord_get_invite { + bool with_counts; + bool with_expiration; + + u64snowflake guild_scheduled_event_id; + +}; +struct discord_delete_invite { + char * reason; +}; +enum discord_channel_types { + DISCORD_CHANNEL_GUILD_TEXT = 0, + DISCORD_CHANNEL_DM = 1, + DISCORD_CHANNEL_GUILD_VOICE = 2, + DISCORD_CHANNEL_GROUP_DM = 3, + DISCORD_CHANNEL_GUILD_CATEGORY = 4, + DISCORD_CHANNEL_GUILD_ANNOUNCEMENT = 5, + DISCORD_CHANNEL_GUILD_NEWS = DISCORD_CHANNEL_GUILD_ANNOUNCEMENT, + DISCORD_CHANNEL_GUILD_STORE = 6, + DISCORD_CHANNEL_ANNOUNCEMENT_THREAD = 10, + DISCORD_CHANNEL_GUILD_NEWS_THREAD = DISCORD_CHANNEL_ANNOUNCEMENT_THREAD, + DISCORD_CHANNEL_GUILD_PUBLIC_THREAD = 11, + DISCORD_CHANNEL_GUILD_PRIVATE_THREAD = 12, + DISCORD_CHANNEL_GUILD_STAGE_VOICE = 13, + DISCORD_CHANNEL_GUILD_DIRECTORY = 14, + DISCORD_CHANNEL_GUILD_FORUM = 15, + DISCORD_CHANNEL_GUILD_MEDIA = 16 +}; +enum discord_video_quality_modes { + DISCORD_VIDEO_QUALITY_AUTO = 1, + DISCORD_VIDEO_QUALITY_FULL = 2 +}; +#define DISCORD_CHANNEL_PINNED 1 << 1 +#define DISCORD_CHANNEL_REQUIRE_TAG 1 << 4 +#define DISCORD_CHANNEL_HIDE_MEDIA_DOWNLOAD_OPTIONS 1 << 5 +enum discord_sort_order_types { + DISCORD_SORT_ORDER_LATEST_ACTIVITY = 0, + DISCORD_SORT_ORDER_CREATION_DATE = 1 +}; +enum discord_forum_layout_types { + DISCORD_FORUM_LAYOUT_NOT_SET = 0, + DISCORD_FORUM_LAYOUT_LIST_VIEW = 1, + DISCORD_FORUM_LAYOUT_GALLERY_VIEW = 1 +}; +struct discord_channel { + u64snowflake id; + enum discord_channel_types type; + + u64snowflake guild_id; + + int position; + struct discord_overwrites * permission_overwrites; + char * name; + char * topic; + bool nsfw; + u64snowflake last_message_id; + int bitrate; + int user_limit; + int rate_limit_per_user; + struct discord_users * recipients; + char * icon; + u64snowflake owner_id; + u64snowflake application_id; + bool managed; + u64snowflake parent_id; + u64unix_ms last_pin_timestamp; + char * rtc_region; + int voice_quality_mode; + int message_count; + int member_count; + struct discord_thread_metadata * thread_metadata; + struct discord_thread_member * member; + int default_auto_archive_duration; + u64bitmask permissions; + u64bitmask flags; + int total_message_sent; + struct discord_thread_tags * available_tags; + struct snowflakes * applied_tags; + struct discord_thread_default_reaction * default_reaction_emoji; + int default_thread_rate_limit_per_user; + enum discord_sort_order_types default_sort_order; + enum discord_forum_layout_types default_forum_layout; +}; +struct discord_channels { int size; + struct discord_channel *array; +int realsize; }; +enum discord_message_types { + DISCORD_MESSAGE_DEFAULT = 0, + DISCORD_MESSAGE_RECIPIENT_ADD = 1, + DISCORD_MESSAGE_RECIPIENT_REMOVE = 2, + DISCORD_MESSAGE_CALL = 3, + DISCORD_MESSAGE_CHANNEL_NAME_CHANGE = 4, + DISCORD_MESSAGE_CHANNEL_ICON_CHANGE = 5, + DISCORD_MESSAGE_CHANNEL_PINNED_MESSAGE = 6, + DISCORD_MESSAGE_GUILD_MEMBER_JOIN = 7, + DISCORD_MESSAGE_USER_PREMIUM_GUILD_SUBSCRIPTION = 8, + DISCORD_MESSAGE_USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9, + DISCORD_MESSAGE_USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10, + DISCORD_MESSAGE_USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11, + DISCORD_MESSAGE_CHANNEL_FOLLOW_ADD = 12, + DISCORD_MESSAGE_GUILD_DISCOVERY_DISQUALIFIED = 14, + DISCORD_MESSAGE_GUILD_DISCOVERY_REQUALIFIED = 15, + DISCORD_MESSAGE_GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16, + DISCORD_MESSAGE_GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17, + DISCORD_MESSAGE_THREAD_CREATED = 18, + DISCORD_MESSAGE_REPLY = 19, + DISCORD_MESSAGE_CHAT_INPUT_COMMAND = 20, + DISCORD_MESSAGE_THREAD_STARTER_MESSAGE = 21, + DISCORD_MESSAGE_GUILD_INVITE_REMINDER = 22, + DISCORD_MESSAGE_CONTEXT_MENU_COMMAND = 23, + DISCORD_MESSAGE_AUTO_MODERATION_ACTION = 24, + DISCORD_MESSAGE_ROLE_SUBSCRIPTION_PURCHASE = 25, + DISCORD_MESSAGE_INTERACTION_PREMIUM_UPSELL = 26, + DISCORD_MESSAGE_STAGE_START = 27, + DISCORD_MESSAGE_STAGE_END = 28, + DISCORD_MESSAGE_STAGE_SPEAKER = 29, + DISCORD_MESSAGE_STAGE_TOPIC = 31, + DISCORD_MESSAGE_GUILD_APPLICATION_PREMIUM_SUBSCRIPTION = 32 +}; +enum discord_message_activity_types { + DISCORD_MESSAGE_ACTIVITY_JOIN = 1, + DISCORD_MESSAGE_ACTIVITY_SPECTATE = 2, + DISCORD_MESSAGE_ACTIVITY_LISTEN = 3, + DISCORD_MESSAGE_ACTIVITY_JOIN_REQUEST = 5 +}; +struct discord_message_activity { + enum discord_message_activity_types type; + char * party_id; +}; +#define DISCORD_MESSAGE_CROSSPOSTED 1 << 0 +#define DISCORD_MESSAGE_IS_CROSSPOST 1 << 1 +#define DISCORD_MESSAGE_SUPPRESS_EMBEDS 1 << 2 +#define DISCORD_MESSAGE_SOURCE_MESSAGE_DELETED 1 << 3 +#define DISCORD_MESSAGE_URGENT 1 << 4 +#define DISCORD_MESSAGE_HAS_THREAD 1 << 5 +#define DISCORD_MESSAGE_EPHEMERAL 1 << 6 +#define DISCORD_MESSAGE_LOADING 1 << 7 +#define DISCORD_MESSAGE_FAILED_TO_MENTION_SOME_ROLES_IN_THREAD 1 << 8 +#define DISCORD_MESSAGE_SUPPRESS_NOTIFICATIONS 1 << 12 +#define DISCORD_MESSAGE_IS_VOICE_MESSAGE 1 << 13 +#define DISCORD_MESSAGE_HAS_COMPONENTS_V2 1 << 15 +struct discord_message_reference { + u64snowflake message_id; + u64snowflake channel_id; + u64snowflake guild_id; + bool fail_if_not_exists; +}; +struct discord_message { + u64snowflake id; + u64snowflake channel_id; + u64snowflake guild_id; + struct discord_user * author; + struct discord_guild_member * member; + char * content; + u64unix_ms timestamp; + + u64unix_ms edited_timestamp; + + bool tts; + bool mention_everyone; + struct discord_users * mentions; + struct snowflakes * mention_roles; + struct discord_channels * mention_channels; + struct discord_attachments * attachments; + struct discord_embeds * embeds; + struct discord_reactions * reactions; + json_char * nonce; + bool pinned; + u64snowflake webhook_id; + enum discord_message_types type; + struct discord_message_activity * activity; + struct discord_application * application; + u64snowflake application_id; + struct discord_message_reference * message_reference; + u64bitmask flags; + struct discord_message * referenced_message; + struct discord_message_interaction * interaction; + struct discord_channel * thread; + struct discord_components * components; + struct discord_sticker_items * sticker_items; + struct discord_stickers * stickers; + int position; + struct discord_role_subscription_data * role_subscription_data; + struct discord_resolved_data * resolved; +}; +struct discord_messages { int size; + struct discord_message *array; +int realsize; }; +struct discord_followed_channel { + u64snowflake channel_id; + u64snowflake webhook_id; +}; +struct discord_reaction_count_details { + int burst; + int normal; +}; +struct discord_reaction { + int count; + struct discord_reaction_count_details * count_details; + bool me; + bool me_burst; + struct discord_emoji * emoji; + char * burst_colors; +}; +struct discord_reactions { int size; + struct discord_reaction *array; +int realsize; }; +struct discord_overwrite { + u64snowflake id; + int type; + u64bitmask allow; + u64bitmask deny; +}; +struct discord_overwrites { int size; + struct discord_overwrite *array; +int realsize; }; +struct discord_thread_metadata { + bool archived; + int auto_archive_duration; + u64unix_ms archive_timestamp; + bool locked; + bool invitable; + u64unix_ms create_timestamp; +}; +struct discord_thread_member { + u64snowflake id; + u64snowflake user_id; + u64unix_ms join_timestamp; + u64bitmask flags; + struct discord_guild_member * member; + u64snowflake guild_id; +}; +struct discord_thread_members { int size; + struct discord_thread_member *array; +int realsize; }; +struct discord_thread_default_reaction { + u64snowflake emoji_id; + char * emoji_name; +}; +struct discord_thread_tag { + u64snowflake id; + char * name; + bool moderated; + u64snowflake emoji_id; + char * emoji_name; +}; +struct discord_thread_tags { int size; + struct discord_thread_tag *array; +int realsize; }; +struct discord_embed_thumbnail { + char * url; + + char * proxy_url; + + + int height; + + + int width; + +}; +struct discord_embed_video { + + char * url; + + + char * proxy_url; + + + int height; + + + int width; + +}; +struct discord_embed_image { + char * url; + + char * proxy_url; + + + int height; + + + int width; + +}; +struct discord_embed_provider { + + char * name; + + + char * url; + +}; +struct discord_embed_author { + char * name; + + char * url; + + + char * icon_url; + + + char * proxy_icon_url; + +}; +struct discord_embed_footer { + char * text; + + char * icon_url; + + + char * proxy_icon_url; + +}; +struct discord_embed_field { + char * name; + char * value; + bool Inline; +}; +struct discord_embed_fields { int size; + struct discord_embed_field *array; +int realsize; }; +struct discord_embed { + char * title; + char * type; + char * description; + char * url; + + u64unix_ms timestamp; + + int color; + + struct discord_embed_footer * footer; + + + struct discord_embed_image * image; + + + struct discord_embed_thumbnail * thumbnail; + + + struct discord_embed_video * video; + + + struct discord_embed_provider * provider; + + + struct discord_embed_author * author; + + + struct discord_embed_fields * fields; + +}; +struct discord_embeds { int size; + struct discord_embed *array; +int realsize; }; +#define DISCORD_ATTACHMENT_IS_REMIX 1 << 2 +struct discord_attachment { + char * content; + u64snowflake id; + char * filename; + char * description; + + char * content_type; + + size_t size; + char * url; + char * proxy_url; + + int height; + + + int width; + + bool ephemeral; + + int duration_secs; + + + char * waveform; + + + u64bitmask flags; + +}; +struct discord_attachments { int size; + struct discord_attachment *array; +int realsize; }; +struct discord_channel_mention { + u64snowflake id; + u64snowflake guild_id; + enum discord_channel_types type; + char * name; +}; +struct discord_allowed_mention { + struct strings * parse; + struct snowflakes * roles; + struct snowflakes * users; + bool replied_user; +}; +struct discord_role_subscription_data { + u64snowflake role_subscription_listing_id; + char * tier_name; + int total_months_subscribed; + bool is_renewal; +}; +struct discord_thread_response_body { + struct discord_channels * threads; + struct discord_thread_members * members; + bool has_more; +}; +struct discord_modify_channel { + char * reason; + char * name; + + enum discord_channel_types type; + + + int position; + + + char * topic; + + + bool nsfw; + + + int rate_limit_per_user; + + + int user_limit; + + + struct discord_overwrites * permission_overwrites; + + + u64snowflake parent_id; + + + char * rtc_region; + + + int video_quality_mode; + + + int default_auto_archive_duration; + + bool archived; + + int auto_archive_duration; + + bool locked; + bool invitable; +}; +struct discord_delete_channel { + char * reason; +}; +struct discord_get_channel_messages { + + u64snowflake around; + + + u64snowflake before; + + + u64snowflake after; + + + int limit; + +}; +struct discord_create_message { + char * content; + + bool tts; + + struct discord_embeds * embeds; + + struct discord_allowed_mention * allowed_mentions; + + + struct discord_message_reference * message_reference; + + + struct discord_components * components; + + + struct snowflakes * sticker_ids; + + + struct discord_attachments * attachments; + + + u64bitmask flags; + + + bool enforce_nonce; + +}; +struct discord_get_reactions { + + u64snowflake after; + + + int limit; + +}; +struct discord_edit_message { + char * content; + struct discord_embeds * embeds; + + u64bitmask flags; + + + struct discord_allowed_mention * allowed_mentions; + + + struct discord_components * components; + + + struct discord_attachments * attachments; + +}; +struct discord_delete_message { + char * reason; +}; +struct discord_bulk_delete_messages { + char * reason; + struct snowflakes * messages; +}; +struct discord_edit_channel_permissions { + char * reason; + + u64bitmask allow; + + + u64bitmask deny; + + int type; +}; +struct discord_create_channel_invite { + char * reason; + + int max_age; + + + int max_uses; + + + bool temporary; + + + bool unique; + + + enum discord_invite_target_types target_type; + + + u64snowflake target_user_id; + + + u64snowflake target_application_id; + +}; +struct discord_delete_channel_permission { + char * reason; +}; +struct discord_follow_news_channel { + + u64snowflake webhook_channel_id; + +}; +struct discord_pin_message { + char * reason; +}; +struct discord_unpin_message { + char * reason; +}; +struct discord_group_dm_add_recipient { + char * access_token; + char * nick; +}; +struct discord_start_thread_with_message { + char * reason; + char * name; + + int auto_archive_duration; + + + int rate_limit_per_user; + +}; +struct discord_start_thread_without_message { + char * reason; + char * name; + + int auto_archive_duration; + + enum discord_channel_types type; + bool invitable; + + int rate_limit_per_user; + +}; +struct discord_list_active_threads { + + struct discord_channels * threads; + + + struct discord_thread_members * members; + + bool has_more; +}; +struct discord_emoji { + u64snowflake id; + char * name; + + struct snowflakes * roles; + + + struct discord_user * user; + + bool require_colons; + bool managed; + bool animated; + bool available; +}; +struct discord_emojis { int size; + struct discord_emoji *array; +int realsize; }; +struct discord_create_guild_emoji { + char * reason; + char * name; + char * image; + + struct snowflakes * roles; + +}; +struct discord_modify_guild_emoji { + char * reason; + char * name; + char * image; + + struct snowflakes * roles; + +}; +struct discord_delete_guild_emoji { + char * reason; +}; +#define DISCORD_SYSTEM_SUPPRESS_JOIN_NOTIFICATIONS 1 << 0 +#define DISCORD_SYSTEM_SUPPRESS_PREMIUM_NOTIFICATIONS 1 << 1 +#define DISCORD_SYSTEM_SUPPRESS_GUILD_REMINDER_NOTIFICATIONS 1 << 2 +#define DISCORD_SYSTEM_SUPPRESS_JOIN_NOTIFICATION_REPLIES 1 << 3 +enum discord_message_notification_level { + DISCORD_MESSAGE_NOTIFICATION_ALL_MESSAGES = 0, + DISCORD_MESSAGE_NOTIFICATION_ONLY_MESSAGES = 1 +}; +enum discord_explicit_content_filter_level { + DISCORD_EXPLICIT_CONTENT_DISABLED = 0, + DISCORD_EXPLICIT_CONTENT_MEMBERS_WITHOUT_ROLES = 1, + DISCORD_MESSAGE_NOTIFICATION_ALL_MEMBERS = 2 +}; +enum discord_mfa_level { + DISCORD_MFA_NONE = 0, + DISCORD_MFA_ELEVATED = 1 +}; +enum discord_verification_level { + DISCORD_VERIFICATION_NONE = 0, + DISCORD_VERIFICATION_LOW = 1, + DISCORD_VERIFICATION_MEDIUM = 2, + DISCORD_VERIFICATION_HIGH = 3, + DISCORD_VERIFICATION_VERY_HIGH = 4 +}; +enum discord_guild_nsfw_level { + DISCORD_GUILD_NSFW_DEFAULT = 0, + DISCORD_GUILD_NSFW_EXPLICIT = 1, + DISCORD_GUILD_NSFW_SAFE = 2, + DISCORD_GUILD_NSFW_AGE_RESTRICTED = 3 +}; +enum discord_premium_tier { + DISCORD_PREMIUM_TIER_NONE = 0, + DISCORD_PREMIUM_TIER_1 = 1, + DISCORD_PREMIUM_TIER_2 = 2, + DISCORD_PREMIUM_TIER_3 = 3 +}; +enum discord_integration_expire_behaviors { + DISCORD_INTEGRATION_REMOVE_ROLE = 0, + DISCORD_INTEGRATION_KICK = 1 +}; +struct discord_guild { + u64snowflake id; + char * name; + char * icon; + + char * icon_hash; + + char * splash; + char * discovery_splash; + bool owner; + u64snowflake owner_id; + + u64bitmask permissions; + + u64snowflake afk_channel_id; + int afk_timeout; + bool widget_enabled; + u64snowflake widget_channel_id; + enum discord_verification_level verification_level; + enum discord_message_notification_level default_message_notifications; + enum discord_explicit_content_filter_level explicit_content_filter; + struct discord_roles * roles; + struct discord_emojis * emojis; + struct strings * features; + enum discord_mfa_level mfa_level; + u64snowflake application_id; + u64snowflake system_channel_id; + u64bitmask system_channel_flags; + u64snowflake rules_channel_id; + + u64unix_ms joined_at; + + bool large; + bool unavailable; + int member_count; + + struct discord_voice_states * voice_states; + + + struct discord_guild_members * members; + + + struct discord_channels * channels; + + + struct discord_channels * threads; + + + struct discord_presence_updates * presences; + + + int max_presences; + + + int max_members; + + char * vanity_url_code; + char * description; + char * banner; + enum discord_premium_tier premium_tier; + int premium_subscription_count; + char * preferred_locale; + u64snowflake public_updates_channel_id; + + int max_video_channel_users; + + + int approximate_member_count; + + + int approximate_presence_count; + + + struct discord_welcome_screen * welcome_screen; + + enum discord_guild_nsfw_level nsfw_level; + + struct discord_stage_instances * stage_instances; + + + struct discord_stickers * stickers; + + + struct discord_guild_scheduled_events * guild_scheduled_events; + + bool premium_progress_bar_enabled; +}; +struct discord_guilds { int size; + struct discord_guild *array; +int realsize; }; +struct discord_guild_preview { + u64snowflake id; + char * name; + char * icon; + char * splash; + char * discovery_splash; + struct discord_emojis * emojis; + struct strings * features; + int approximate_member_count; + int approximate_presence_count; + char * description; + struct discord_stickers * stickers; +}; +struct discord_guild_widget_settings { + char * reason; + bool enabled; + u64snowflake channel_id; +}; +struct discord_guild_widget { + u64snowflake id; + char * name; + char * instant_invite; + struct discord_channels * channels; + struct discord_users * members; + int presence_count; +}; +struct discord_guild_member { + + struct discord_user * user; + + + char * nick; + + + char * avatar; + + struct snowflakes * roles; + u64unix_ms joined_at; + + u64unix_ms premium_since; + + bool deaf; + bool muted; + bool pending; + + u64bitmask permissions; + + u64unix_ms communication_disabled_until; + u64snowflake guild_id; +}; +struct discord_guild_members { int size; + struct discord_guild_member *array; +int realsize; }; +struct discord_integration { + u64snowflake id; + char * name; + char * type; + bool enabled; + bool syncing; + u64snowflake role_id; + bool enable_emoticons; + enum discord_integration_expire_behaviors expire_behavior; + int expire_grace_period; + struct discord_user * user; + struct discord_integration_account * account; + u64unix_ms synced_at; + int subscriber_count; + bool revoked; + struct discord_integration_application * application; + u64snowflake guild_id; +}; +struct discord_integrations { int size; + struct discord_integration *array; +int realsize; }; +struct discord_integration_account { + char * id; + char * name; +}; +struct discord_integration_application { + u64snowflake id; + char * name; + char * icon; + char * description; + char * summary; + + struct discord_user * bot; + +}; +struct discord_ban { + char * reason; + struct discord_user * user; +}; +struct discord_bans { int size; + struct discord_ban *array; +int realsize; }; +struct discord_welcome_screen { + char * description; + + struct discord_welcome_screen_channels * welcome_channels; + +}; +struct discord_welcome_screen_channel { + u64snowflake channel_id; + char * description; + u64snowflake emoji_id; + char * emoji_name; +}; +struct discord_welcome_screen_channels { int size; + struct discord_welcome_screen_channel *array; +int realsize; }; +struct discord_prune_count { + int pruned; +}; +struct discord_create_guild { + char * reason; + char * name; + + char * region; + + + char * icon; + + enum discord_verification_level verification_level; + enum discord_message_notification_level default_message_notifications; + enum discord_explicit_content_filter_level explicit_content_filter; + + struct discord_roles * roles; + + + struct discord_channels * channels; + + + u64snowflake afk_channel_id; + + int afk_timeout; + u64snowflake system_channel_id; + u64bitmask system_channel_flags; +}; +struct discord_modify_guild { + char * reason; + char * name; + enum discord_verification_level verification_level; + enum discord_message_notification_level default_message_notifications; + enum discord_explicit_content_filter_level explicit_content_filter; + u64snowflake afk_channel_id; + int afk_timeout; + char * icon; + u64snowflake owner_id; + char * splash; + char * discovery_splash; + char * banner; + u64snowflake system_channel_id; + u64bitmask system_channel_flags; + u64snowflake rules_channel_id; + u64snowflake public_updates_channel_id; + char * preferred_locale; + struct strings * features; + char * description; + bool premium_progress_bar_enabled; +}; +struct discord_create_guild_channel { + char * reason; + char * name; + enum discord_channel_types type; + char * topic; + + int bitrate; + + + int user_limit; + + int rate_limit_per_user; + int position; + struct discord_overwrites * permission_overwrites; + + u64snowflake parent_id; + + bool nsfw; +}; +struct discord_modify_guild_channel_position { + u64snowflake id; + + int position; + + bool lock_category; + + u64snowflake parent_id; + +}; +struct discord_modify_guild_channel_positions { int size; + struct discord_modify_guild_channel_position *array; +int realsize; }; +struct discord_list_active_guild_threads { + struct discord_channels * threads; + struct discord_thread_members * members; +}; +struct discord_list_guild_members { + int limit; + u64snowflake after; +}; +struct discord_search_guild_members { + char * query; + int limit; +}; +struct discord_add_guild_member { + char * access_token; + char * nick; + struct snowflakes * roles; + bool mute; + bool deaf; +}; +struct discord_modify_guild_member { + char * reason; + + char * nick; + + + struct snowflakes * roles; + + + bool mute; + + + bool deaf; + + + u64snowflake channel_id; + + + u64unix_ms communication_disabled_until; + +}; +struct discord_modify_current_member { + char * reason; + + char * nick; + +}; +struct discord_modify_current_user_nick { + char * reason; + + char * nick; + +}; +struct discord_add_guild_member_role { + char * reason; +}; +struct discord_remove_guild_member_role { + char * reason; +}; +struct discord_remove_guild_member { + char * reason; +}; +struct discord_create_guild_ban { + char * reason; + + int delete_message_days; + +}; +struct discord_remove_guild_ban { + char * reason; +}; +struct discord_create_guild_role { + char * reason; + char * name; + u64bitmask permissions; + int color; + bool hoist; + char * icon; + char * unicode_emoji; + bool mentionable; +}; +struct discord_modify_guild_role_position { + u64snowflake id; + + int position; + +}; +struct discord_modify_guild_role_positions { int size; + struct discord_modify_guild_role_position *array; +int realsize; }; +struct discord_modify_guild_role { + char * reason; + char * name; + u64bitmask permissions; + int color; + bool hoist; + char * icon; + char * unicode_emoji; + bool mentionable; +}; +struct discord_delete_guild_role { + char * reason; +}; +struct discord_get_guild_prune_count { + + int days; + + struct snowflakes * include_roles; +}; +struct discord_begin_guild_prune { + char * reason; + + int days; + + bool compute_prune_count; + struct snowflakes * include_roles; +}; +struct discord_delete_guild_integrations { + char * reason; + + int days; + + struct snowflakes * include_roles; +}; +struct discord_get_guild_widget_image { + + char * style; + +}; +struct discord_modify_guild_welcome_screen { + char * reason; + bool enabled; + struct discord_welcome_screen_channels * welcome_channels; + + char * description; + +}; +struct discord_modify_current_user_voice_state { + u64snowflake channel_id; + bool suppress; + + u64unix_ms request_to_speak_timestamp; + +}; +struct discord_modify_user_voice_state { + u64snowflake channel_id; + bool suppress; +}; +enum discord_guild_scheduled_event_privacy_level { + DISCORD_GUILD_SCHEDULED_EVENT_GUILD_ONLY = 2 +}; +enum discord_guild_scheduled_event_entity_types { + DISCORD_GUILD_SCHEDULED_EVENT_ENTITY_STAGE_INSTANCE = 1, + DISCORD_GUILD_SCHEDULED_EVENT_ENTITY_VOICE = 2, + DISCORD_GUILD_SCHEDULED_EVENT_ENTITY_EXTERNAL = 3 +}; +enum discord_guild_scheduled_event_status { + DISCORD_GUILD_SCHEDULED_EVENT_SCHEDULED = 1, + DISCORD_GUILD_SCHEDULED_EVENT_ACTIVE = 2, + DISCORD_GUILD_SCHEDULED_EVENT_COMPLETED = 3, + DISCORD_GUILD_SCHEDULED_EVENT_CANCELED = 4 +}; +struct discord_guild_scheduled_event { + u64snowflake id; + u64snowflake guild_id; + u64snowflake channel_id; + u64snowflake creator_id; + char * name; + char * description; + u64unix_ms scheduled_start_time; + u64unix_ms scheduled_end_time; + + enum discord_guild_scheduled_event_privacy_level privacy_level; + + + enum discord_guild_scheduled_event_status status; + + + enum discord_guild_scheduled_event_entity_types entity_type; + + u64snowflake entity_id; + + struct discord_guild_scheduled_event_entity_metadata * entity_metadata; + + + struct discord_user * creator; + + int user_count; + char * image; +}; +struct discord_guild_scheduled_event_entity_metadata { + + char * location; + +}; +struct discord_guild_scheduled_events { int size; + struct discord_guild_scheduled_event *array; +int realsize; }; +struct discord_guild_scheduled_event_user { + u64snowflake guild_scheduled_event_id; + + struct discord_user * user; + + + struct discord_guild_member * member; + +}; +struct discord_guild_scheduled_event_users { int size; + struct discord_guild_scheduled_event_user *array; +int realsize; }; +struct discord_list_guild_scheduled_events { + bool with_user_count; +}; +struct discord_create_guild_scheduled_event { + char * reason; + + u64snowflake channel_id; + + + struct discord_guild_scheduled_event_entity_metadata * entity_metadata; + + char * name; + + enum discord_guild_scheduled_event_privacy_level privacy_level; + + + u64unix_ms scheduled_start_time; + + + u64unix_ms scheduled_end_time; + + + char * description; + + + enum discord_guild_scheduled_event_entity_types entity_type; + + + char * image; + +}; +struct discord_get_guild_scheduled_event { + bool with_user_count; +}; +struct discord_modify_guild_scheduled_event { + char * reason; + + u64snowflake channel_id; + + + struct discord_guild_scheduled_event_entity_metadata * entity_metadata; + + char * name; + + u64unix_ms scheduled_start_time; + + + u64unix_ms scheduled_end_time; + + + char * description; + + + enum discord_guild_scheduled_event_entity_types entity_type; + + + enum discord_guild_scheduled_event_status status; + + + char * image; + +}; +struct discord_get_guild_scheduled_event_users { + int limit; + bool with_member; + + u64snowflake before; + + + u64snowflake after; + +}; +struct discord_guild_template { + char * code; + char * name; + char * description; + int usage_count; + u64snowflake creator_id; + struct discord_user * creator; + u64unix_ms created_at; + u64unix_ms updated_at; + u64snowflake source_guild_id; + struct discord_guild * serialized_source_guild; + bool is_dirty; +}; +struct discord_guild_templates { int size; + struct discord_guild_template *array; +int realsize; }; +struct discord_create_guild_from_guild_template { + char * name; + + char * icon; + +}; +struct discord_create_guild_template { + char * name; + + char * description; + +}; +struct discord_modify_guild_template { + + char * name; + + + char * description; + +}; +enum discord_privacy_level { + DISCORD_PRIVACY_PUBLIC = 1, + DISCORD_PRIVACY_GUILD_ONLY = 2 +}; +struct discord_stage_instance { + u64snowflake id; + u64snowflake guild_id; + u64snowflake channel_id; + char * topic; + + enum discord_privacy_level privacy_level; + + bool discoverable_disabled; +}; +struct discord_stage_instances { int size; + struct discord_stage_instance *array; +int realsize; }; +struct discord_create_stage_instance { + char * reason; + u64snowflake channel_id; + char * topic; + + enum discord_privacy_level privacy_level; + +}; +struct discord_modify_stage_instance { + char * reason; + char * topic; + + enum discord_privacy_level privacy_level; + +}; +struct discord_delete_stage_instance { + char * reason; +}; +enum discord_sticker_types { + DISCORD_STICKER_STANDARD = 1, + DISCORD_STICKER_GUILD = 2 +}; +enum discord_sticker_format_types { + DISCORD_STICKER_FORMAT_PNG = 1, + DISCORD_STICKER_FORMAT_APNG = 2, + DISCORD_STICKER_FORMAT_LOTTIE = 3 +}; +struct discord_sticker { + u64snowflake id; + + u64snowflake pack_id; + + char * name; + char * description; + char * tags; + + enum discord_sticker_types type; + + + enum discord_sticker_format_types format_type; + + bool available; + + u64snowflake guild_id; + + + struct discord_user * user; + + int sort_value; +}; +struct discord_stickers { int size; + struct discord_sticker *array; +int realsize; }; +struct discord_sticker_item { + u64snowflake id; + char * name; + + enum discord_sticker_format_types format_type; + +}; +struct discord_sticker_items { int size; + struct discord_sticker_item *array; +int realsize; }; +struct discord_sticker_pack { + u64snowflake id; + + struct discord_stickers * stickers; + + char * name; + u64snowflake sku_id; + + u64snowflake cover_sticker_id; + + char * description; + + u64snowflake banner_asset_id; + +}; +struct discord_sticker_packs { int size; + struct discord_sticker_pack *array; +int realsize; }; +struct discord_list_nitro_sticker_packs { + struct discord_sticker_packs * sticker_packs; +}; +struct discord_create_guild_sticker { + char * reason; + char * name; + char * description; + char * tags; + struct discord_attachment * file; +}; +struct discord_modify_guild_sticker { + char * reason; + char * name; + char * description; + char * tags; +}; +struct discord_delete_guild_sticker { + char * reason; +}; +#define DISCORD_USER_NONE 0 +#define DISCORD_USER_STAFF 1 << 0 +#define DISCORD_USER_PARTNER 1 << 1 +#define DISCORD_USER_HYPESQUAD 1 << 2 +#define DISCORD_USER_BUG_HUNTER_LEVEL_1 1 << 3 +#define DISCORD_USER_HYPESQUAD_ONLINE_HOUSE_1 1 << 6 +#define DISCORD_USER_HYPESQUAD_ONLINE_HOUSE_2 1 << 7 +#define DISCORD_USER_HYPESQUAD_ONLINE_HOUSE_3 1 << 8 +#define DISCORD_USER_PREMIUM_EARLY_SUPPORTER 1 << 9 +#define DISCORD_USER_TEAM_PSEUDO_USER 1 << 10 +#define DISCORD_USER_BUG_HUNTER_LEVEL_2 1 << 14 +#define DISCORD_USER_VERIFIED_BOT 1 << 16 +#define DISCORD_USER_VERIFIED_DEVELOPER 1 << 17 +#define DISCORD_USER_CERTIFIED_MODERATOR 1 << 18 +#define DISCORD_USER_BOT_HTTP_INTERACTIONS 1 << 19 +enum discord_premium_types { + DISCORD_PREMIUM_NONE = 0, + DISCORD_PREMIUM_NITRO_CLASSIC = 1, + DISCORD_PREMIUM_NITRO = 2 +}; +enum discord_visibility_types { + DISCORD_VISIBILITY_NONE = 0, + DISCORD_VISIBILITY_EVERYONE = 1 +}; +struct discord_user { + u64snowflake id; + char * username; + char * discriminator; + char * avatar; + bool bot; + bool System; + bool mfa_enabled; + char * banner; + int accent_color; + char * locale; + bool verified; + char * email; + u64bitmask flags; + enum discord_premium_types premium_type; + u64bitmask public_flags; +}; +struct discord_users { int size; + struct discord_user *array; +int realsize; }; +struct discord_connection { + u64snowflake id; + char * name; + char * type; + bool revoked; + + struct discord_integrations * integrations; + + bool verified; + bool friend_sync; + bool show_activity; + enum discord_visibility_types visibility; +}; +struct discord_connections { int size; + struct discord_connection *array; +int realsize; }; +struct discord_modify_current_user { + + char * username; + + + char * avatar; + +}; +struct discord_get_current_user_guilds { + + u64snowflake before; + + + u64snowflake after; + + + int limit; + +}; +struct discord_create_dm { + + u64snowflake recipient_id; + +}; +struct discord_create_group_dm { + + struct snowflakes * access_tokens; + + + struct strings * nicks; + +}; +struct discord_voice_state { + u64snowflake guild_id; + u64snowflake channel_id; + u64snowflake user_id; + struct discord_guild_member * member; + char * session_id; + bool deaf; + bool mute; + bool self_deaf; + bool self_mute; + bool self_stream; + bool self_video; + bool suppress; + + u64unix_ms request_to_speak_timestamp; + +}; +struct discord_voice_states { int size; + struct discord_voice_state *array; +int realsize; }; +struct discord_voice_region { + char * id; + char * name; + bool optimal; + bool deprecated; + bool custom; +}; +struct discord_voice_regions { int size; + struct discord_voice_region *array; +int realsize; }; +enum discord_webhook_types { + DISCORD_WEBHOOK_INCOMING = 1, + DISCORD_WEBHOOK_CHANNEL_FOLLOWER = 2, + DISCORD_WEBHOOK_APPLICATION = 3 +}; +struct discord_webhook { + u64snowflake id; + + enum discord_webhook_types type; + + u64snowflake guild_id; + u64snowflake channel_id; + + struct discord_user * user; + + char * name; + char * token; + u64snowflake application_id; + + struct discord_channel * source_channel; + + char * url; +}; +struct discord_webhooks { int size; + struct discord_webhook *array; +int realsize; }; +struct discord_create_webhook { + char * reason; + char * name; + + char * avatar; + +}; +struct discord_modify_webhook { + char * reason; + char * name; + + char * avatar; + + u64snowflake channel_id; +}; +struct discord_delete_webhook { + char * reason; +}; +struct discord_modify_webhook_with_token { + char * name; + + char * avatar; + +}; +struct discord_execute_webhook { + bool wait; + u64snowflake thread_id; + char * content; + char * username; + char * avatar_url; + bool tts; + + struct discord_embeds * embeds; + + + struct discord_allowed_mention * allowed_mentions; + + + struct discord_components * components; + + + struct discord_attachments * attachments; + + + u64bitmask flags; + +}; +struct discord_get_webhook_message { + + u64snowflake thread_id; + +}; +struct discord_edit_webhook_message { + u64snowflake thread_id; + char * content; + + struct discord_embeds * embeds; + + + struct discord_allowed_mention * allowed_mentions; + + + struct discord_components * components; + + + struct discord_attachments * attachments; + +}; +struct discord_delete_webhook_message { + + u64snowflake thread_id; + +}; +#define DISCORD_GATEWAY_GUILDS 1 << 0 +#define DISCORD_GATEWAY_GUILD_MEMBERS 1 << 1 +#define DISCORD_GATEWAY_GUILD_BANS 1 << 2 +#define DISCORD_GATEWAY_GUILD_EMOJIS_AND_STICKERS 1 << 3 +#define DISCORD_GATEWAY_GUILD_INTEGRATIONS 1 << 4 +#define DISCORD_GATEWAY_GUILD_WEBHOOKS 1 << 5 +#define DISCORD_GATEWAY_GUILD_INVITES 1 << 6 +#define DISCORD_GATEWAY_GUILD_VOICE_STATES 1 << 7 +#define DISCORD_GATEWAY_GUILD_PRESENCES 1 << 8 +#define DISCORD_GATEWAY_GUILD_MESSAGES 1 << 9 +#define DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS 1 << 10 +#define DISCORD_GATEWAY_GUILD_MESSAGE_TYPING 1 << 11 +#define DISCORD_GATEWAY_DIRECT_MESSAGES 1 << 12 +#define DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS 1 << 13 +#define DISCORD_GATEWAY_DIRECT_MESSAGE_TYPING 1 << 14 +#define DISCORD_GATEWAY_MESSAGE_CONTENT 1 << 15 +#define DISCORD_GATEWAY_GUILD_SCHEDULED_EVENTS 1 << 16 +#define DISCORD_GATEWAY_AUTO_MODERATION_CONFIGURATION 1 << 20 +#define DISCORD_GATEWAY_AUTO_MODERATION_EXECUTION 1 << 21 +#define DISCORD_ACTIVITY_INSTANCE 1 << 0 +#define DISCORD_ACTIVITY_JOIN 1 << 1 +#define DISCORD_ACTIVITY_SPECTATE 1 << 2 +#define DISCORD_ACTIVITY_JOIN_REQUEST 1 << 3 +#define DISCORD_ACTIVITY_SYNC 1 << 4 +#define DISCORD_ACTIVITY_PLAY 1 << 5 +#define DISCORD_ACTIVITY_PARTY_PRIVACY_FRIENDS 1 << 6 +#define DISCORD_ACTIVITY_PARTY_PRIVACY_VOICE_CHANNEL 1 << 7 +#define DISCORD_ACTIVITY_EMBEDDED 1 << 8 +enum discord_gateway_close_opcodes { + DISCORD_GATEWAY_CLOSE_REASON_UNKNOWN_ERROR = 4000, + DISCORD_GATEWAY_CLOSE_REASON_UNKNOWN_OPCODE = 4001, + DISCORD_GATEWAY_CLOSE_REASON_DECODE_ERROR = 4002, + DISCORD_GATEWAY_CLOSE_REASON_NOT_AUTHENTICATED = 4003, + DISCORD_GATEWAY_CLOSE_REASON_AUTHENTICATION_FAILED = 4004, + DISCORD_GATEWAY_CLOSE_REASON_ALREADY_AUTHENTICATED = 4005, + DISCORD_GATEWAY_CLOSE_REASON_INVALID_SEQUENCE = 4007, + DISCORD_GATEWAY_CLOSE_REASON_RATE_LIMITED = 4008, + DISCORD_GATEWAY_CLOSE_REASON_SESSION_TIMED_OUT = 4009, + DISCORD_GATEWAY_CLOSE_REASON_INVALID_SHARD = 4010, + DISCORD_GATEWAY_CLOSE_REASON_SHARDING_REQUIRED = 4011, + DISCORD_GATEWAY_CLOSE_REASON_INVALID_API_VERSION = 4012, + DISCORD_GATEWAY_CLOSE_REASON_INVALID_INTENTS = 4013, + DISCORD_GATEWAY_CLOSE_REASON_DISALLOWED_INTENTS = 4014, + DISCORD_GATEWAY_CLOSE_REASON_RECONNECT = 4900 +}; +enum discord_gateway_opcodes { + DISCORD_GATEWAY_DISPATCH = 0, + DISCORD_GATEWAY_HEARTBEAT = 1, + DISCORD_GATEWAY_IDENTIFY = 2, + DISCORD_GATEWAY_PRESENCE_UPDATE = 3, + DISCORD_GATEWAY_VOICE_STATE_UPDATE = 4, + DISCORD_GATEWAY_RESUME = 6, + DISCORD_GATEWAY_RECONNECT = 7, + DISCORD_GATEWAY_REQUEST_GUILD_MEMBERS = 8, + DISCORD_GATEWAY_INVALID_SESSION = 9, + DISCORD_GATEWAY_HELLO = 10, + DISCORD_GATEWAY_HEARTBEAT_ACK = 11 +}; +enum discord_activity_types { + DISCORD_ACTIVITY_GAME = 0, + DISCORD_ACTIVITY_STREAMING = 1, + DISCORD_ACTIVITY_LISTENING = 2, + DISCORD_ACTIVITY_WATCHING = 3, + DISCORD_ACTIVITY_CUSTOM = 4, + DISCORD_ACTIVITY_COMPETING = 5 +}; +struct discord_activity { + + char * name; + + enum discord_activity_types type; + + char * url; + + + u64unix_ms created_at; + + + struct discord_activity_timestamps * timestamps; + + + u64snowflake application_id; + + + char * details; + + + char * state; + + + struct discord_activity_emoji * emoji; + + + struct discord_activity_party * party; + + + struct discord_activity_assets * assets; + + + struct discord_activity_secrets * secrets; + + bool instance; + u64bitmask flags; + + struct discord_activity_buttons * buttons; + +}; +struct discord_activities { int size; + struct discord_activity *array; +int realsize; }; +struct discord_activity_timestamps { + u64unix_ms start; + u64unix_ms end; +}; +struct discord_activity_emoji { + char * name; + u64snowflake id; + bool animated; +}; +struct discord_activity_party { + char * id; + struct integers * size; +}; +struct discord_activity_assets { + char * large_image; + char * large_text; + char * small_image; + char * small_text; +}; +struct discord_activity_secrets { + char * join; + char * spectate; + char * match; +}; +struct discord_activity_button { + char * label; + char * url; +}; +struct discord_activity_buttons { int size; + struct discord_activity_button *array; +int realsize; }; +struct discord_presence_update { + + struct discord_user * user; + + + u64snowflake guild_id; + + + char * status; + + + struct discord_client_status * client_status; + + + struct discord_activities * activities; + + + u64unix_ms since; + + bool afk; +}; +struct discord_client_status { + char * desktop; + char * mobile; + char * web; +}; +struct discord_presence_updates { int size; + struct discord_presence_update *array; +int realsize; }; +struct discord_identify { + char * token; + struct discord_identify_connection * properties; + bool compress; + int large_threshold; + + struct integers * shard; + + + struct discord_presence_update * presence; + + u64bitmask intents; +}; +struct discord_identify_connection { + char * os; + char * browser; + char * device; +}; +struct discord_resume { + char * token; + char * session_id; + int seq; +}; +struct discord_request_guild_members { + u64snowflake guild_id; + char * query; + int limit; + bool presences; + + struct snowflakes * user_ids; + + + char * nonce; + +}; +struct discord_update_voice_state { + u64snowflake guild_id; + u64snowflake channel_id; + bool self_mute; + bool self_deaf; +}; +struct discord_ready { + int v; + struct discord_user * user; + struct discord_guilds * guilds; + char * session_id; + struct integers * shard; + struct discord_application * application; +}; +struct discord_auto_moderation_action_execution { + u64snowflake guild_id; + struct discord_auto_moderation_action * action; + enum discord_auto_moderation_trigger_types rule_trigger_type; + u64snowflake user_id; + u64snowflake channel_id; + u64snowflake message_id; + u64snowflake alert_system_message_id; + char * content; + char * matched_keyword; + char * matched_content; +}; +struct discord_thread_list_sync { + u64snowflake guild_id; + struct snowflakes * channel_ids; + struct discord_channels * threads; + struct discord_thread_members * members; +}; +struct discord_thread_members_update { + u64snowflake id; + u64snowflake guild_id; + int member_count; + struct discord_thread_members * added_members; + struct snowflakes * removed_member_ids; +}; +struct discord_channel_pins_update { + u64snowflake guild_id; + u64snowflake channel_id; + u64unix_ms last_pin_timestamp; +}; +struct discord_guild_ban_add { + u64snowflake guild_id; + struct discord_user * user; +}; +struct discord_guild_ban_remove { + u64snowflake guild_id; + struct discord_user * user; +}; +struct discord_guild_emojis_update { + u64snowflake guild_id; + struct discord_emojis * emojis; +}; +struct discord_guild_stickers_update { + u64snowflake guild_id; + struct discord_stickers * stickers; +}; +struct discord_guild_integrations_update { + u64snowflake guild_id; +}; +struct discord_guild_member_remove { + u64snowflake guild_id; + struct discord_user * user; +}; +struct discord_guild_member_update { + u64snowflake guild_id; + struct snowflakes * roles; + struct discord_user * user; + char * nick; + char * avatar; + u64unix_ms joined_at; + u64unix_ms premium_since; + bool deaf; + bool mute; + bool pending; + u64unix_ms communication_disabled_until; +}; +struct discord_guild_members_chunk { + u64snowflake guild_id; + struct discord_guild_members * members; + int chunk_index; + int chunk_count; + struct snowflakes * not_found; + struct discord_presence_updates * presences; + char * nonce; +}; +struct discord_guild_role_create { + u64snowflake guild_id; + struct discord_role * role; +}; +struct discord_guild_role_update { + u64snowflake guild_id; + struct discord_role * role; +}; +struct discord_guild_role_delete { + u64snowflake guild_id; + u64snowflake role_id; +}; +struct discord_guild_scheduled_event_user_add { + u64snowflake guild_scheduled_event_id; + u64snowflake user_id; + u64snowflake guild_id; +}; +struct discord_guild_scheduled_event_user_remove { + u64snowflake guild_scheduled_event_id; + u64snowflake user_id; + u64snowflake guild_id; +}; +struct discord_integration_delete { + u64snowflake id; + u64snowflake guild_id; + u64snowflake application_id; +}; +struct discord_invite_create { + u64snowflake channel_id; + char * code; + u64unix_ms created_at; + u64snowflake guild_id; + struct discord_user * inviter; + int max_age; + int max_uses; + enum discord_invite_target_types target_type; + struct discord_user * target_user; + struct discord_application * target_application; + bool temporary; + int uses; +}; +struct discord_invite_delete { + u64snowflake channel_id; + u64snowflake guild_id; + char * code; +}; +struct discord_message_delete { + u64snowflake id; + u64snowflake channel_id; + u64snowflake guild_id; +}; +struct discord_message_delete_bulk { + struct snowflakes * ids; + u64snowflake channel_id; + u64snowflake guild_id; +}; +struct discord_message_reaction_add { + u64snowflake user_id; + u64snowflake channel_id; + u64snowflake message_id; + u64snowflake guild_id; + struct discord_guild_member * member; + struct discord_emoji * emoji; +}; +struct discord_message_reaction_remove { + u64snowflake user_id; + u64snowflake channel_id; + u64snowflake message_id; + u64snowflake guild_id; + struct discord_emoji * emoji; +}; +struct discord_message_reaction_remove_all { + u64snowflake channel_id; + u64snowflake message_id; + u64snowflake guild_id; +}; +struct discord_message_reaction_remove_emoji { + u64snowflake channel_id; + u64snowflake guild_id; + u64snowflake message_id; + struct discord_emoji * emoji; +}; +struct discord_typing_start { + u64snowflake channel_id; + u64snowflake guild_id; + u64snowflake user_id; + u64unix_ms timestamp; + struct discord_guild_member * member; +}; +struct discord_voice_server_update { + char * token; + u64snowflake guild_id; + char * endpoint; +}; +struct discord_webhooks_update { + u64snowflake guild_id; + u64snowflake channel_id; +}; +struct discord_session_start_limit { + int total; + int remaining; + int reset_after; + int max_concurrency; +}; +struct discord_auth_response { + struct discord_application * application; + struct strings * scopes; + u64unix_ms expires; + struct discord_user * user; +}; +#define DISCORD_PERM_CREATE_INSTANT_INVITE 1 << 0 +#define DISCORD_PERM_KICK_MEMBERS 1 << 1 +#define DISCORD_PERM_BAN_MEMBERS 1 << 2 +#define DISCORD_PERM_ADMINISTRATOR 1 << 3 +#define DISCORD_PERM_MANAGE_CHANNELS 1 << 4 +#define DISCORD_PERM_MANAGE_GUILD 1 << 5 +#define DISCORD_PERM_ADD_REACTIONS 1 << 6 +#define DISCORD_PERM_VIEW_AUDIT_LOG 1 << 7 +#define DISCORD_PERM_PRIORITY_SPEAKER 1 << 8 +#define DISCORD_PERM_STREAM 1 << 9 +#define DISCORD_PERM_VIEW_CHANNEL 1 << 10 +#define DISCORD_PERM_SEND_MESSAGES 1 << 11 +#define DISCORD_PERM_SEND_TTS_MESSAGES 1 << 12 +#define DISCORD_PERM_MANAGE_MESSAGES 1 << 13 +#define DISCORD_PERM_EMBED_LINKS 1 << 14 +#define DISCORD_PERM_ATTACH_FILES 1 << 15 +#define DISCORD_PERM_READ_MESSAGE_HISTORY 1 << 16 +#define DISCORD_PERM_MENTION_EVERYONE 1 << 17 +#define DISCORD_PERM_USE_EXTERNAL_EMOJIS 1 << 18 +#define DISCORD_PERM_VIEW_GUILD_INSIGHTS 1 << 19 +#define DISCORD_PERM_CONNECT 1 << 20 +#define DISCORD_PERM_SPEAK 1 << 21 +#define DISCORD_PERM_MUTE_MEMBERS 1 << 22 +#define DISCORD_PERM_DEAFEN_MEMBERS 1 << 23 +#define DISCORD_PERM_MOVE_MEMBERS 1 << 24 +#define DISCORD_PERM_USE_VAD 1 << 25 +#define DISCORD_PERM_CHANGE_NICKNAME 1 << 26 +#define DISCORD_PERM_MANAGE_NICKNAMES 1 << 27 +#define DISCORD_PERM_MANAGE_ROLES 1 << 28 +#define DISCORD_PERM_MANAGE_WEBHOOKS 1 << 29 +#define DISCORD_PERM_MANAGE_EMOJIS_AND_STICKERS 1 << 30 +#define DISCORD_PERM_USE_APPLICATION_COMMANDS 1 << 31 +#define DISCORD_PERM_REQUEST_TO_SPEAK 1 << 32 +#define DISCORD_PERM_MANAGE_EVENTS 1 << 33 +#define DISCORD_PERM_MANAGE_THREADS 1 << 34 +#define DISCORD_PERM_CREATE_PUBLIC_THREADS 1 << 35 +#define DISCORD_PERM_CREATE_PRIVATE_THREADS 1 << 36 +#define DISCORD_PERM_USE_EXTERNAL_STICKERS 1 << 37 +#define DISCORD_PERM_SEND_MESSAGES_IN_THREADS 1 << 38 +#define DISCORD_PERM_START_EMBEDDED_ACTIVITIES 1 << 39 +#define DISCORD_PERM_MODERATE_MEMBERS 1 << 40 +struct discord_role { + u64snowflake id; + char * name; + int color; + bool hoist; + + char * icon; + + + char * unicode_emoji; + + int position; + u64bitmask permissions; + bool managed; + bool mentionable; + + struct discord_role_tag * tags; + +}; +struct discord_roles { int size; + struct discord_role *array; +int realsize; }; +struct discord_role_tag { + + u64snowflake bot_id; + + + u64snowflake integration_id; + + bool premium_subscribe; +}; +enum discord_membership_state { + DISCORD_MEMBERSHIP_INVITED = 1, + DISCORD_MEMBERSHIP_ACCEPTED = 2 +}; +struct discord_team { + char * icon; + u64snowflake id; + + struct discord_team_members * members; + + char * name; + u64snowflake owner_user_id; +}; +struct discord_team_member { + enum discord_membership_state membership_state; + + struct bitmasks * permissions; + + u64snowflake team_id; + + struct discord_user * user; + +}; +struct discord_team_members { int size; + struct discord_team_member *array; +int realsize; }; +#define DISCORD_VOICE_MICROPHONE 1 << 0 +#define DISCORD_VOICE_SOUNDSHARE 1 << 1 +#define DISCORD_VOICE_PRIORITY 1 << 2 +enum discord_voice_close_opcodes { + DISCORD_VOICE_CLOSE_REASON_UNKNOWN_OPCODE = 4001, + DISCORD_VOICE_CLOSE_REASON_DECODE_ERROR = 4002, + DISCORD_VOICE_CLOSE_REASON_NOT_AUTHENTICATED = 4003, + DISCORD_VOICE_CLOSE_REASON_AUTHENTICATION_FAILED = 4004, + DISCORD_VOICE_CLOSE_REASON_ALREADY_AUTHENTICATED = 4005, + DISCORD_VOICE_CLOSE_REASON_INVALID_SESSION = 4006, + DISCORD_VOICE_CLOSE_REASON_SESSION_TIMED_OUT = 4009, + DISCORD_VOICE_CLOSE_REASON_SERVER_NOT_FOUND = 4011, + DISCORD_VOICE_CLOSE_REASON_UNKNOWN_PROTOCOL = 4012, + DISCORD_VOICE_CLOSE_REASON_DISCONNECTED = 4014, + DISCORD_VOICE_CLOSE_REASON_SERVER_CRASH = 4015, + DISCORD_VOICE_CLOSE_REASON_UNKNOWN_ENCRYPTION_MODE = 4016 +}; +enum discord_voice_opcodes { + DISCORD_VOICE_IDENTIFY = 0, + DISCORD_VOICE_SELECT_PROTOCOL = 1, + DISCORD_VOICE_READY = 2, + DISCORD_VOICE_HEARTBEAT = 3, + DISCORD_VOICE_SESSION_DESCRIPTION = 4, + DISCORD_VOICE_SPEAKING = 5, + DISCORD_VOICE_HEARTBEAT_ACK = 6, + DISCORD_VOICE_RESUME = 7, + DISCORD_VOICE_HELLO = 8, + DISCORD_VOICE_RESUMED = 9, + DISCORD_VOICE_CLIENT_DISCONNECT = 13, + DISCORD_VOICE_CODEC = 14 +}; +enum discord_application_command_types { + DISCORD_APPLICATION_CHAT_INPUT = 1, + DISCORD_APPLICATION_USER = 2, + DISCORD_APPLICATION_MESSAGE = 3 +}; +enum discord_application_command_option_types { + DISCORD_APPLICATION_OPTION_SUB_COMMAND = 1, + DISCORD_APPLICATION_OPTION_SUB_COMMAND_GROUP = 2, + DISCORD_APPLICATION_OPTION_STRING = 3, + DISCORD_APPLICATION_OPTION_INTEGER = 4, + DISCORD_APPLICATION_OPTION_BOOLEAN = 5, + DISCORD_APPLICATION_OPTION_USER = 6, + DISCORD_APPLICATION_OPTION_CHANNEL = 7, + DISCORD_APPLICATION_OPTION_ROLE = 8, + DISCORD_APPLICATION_OPTION_MENTIONABLE = 9, + DISCORD_APPLICATION_OPTION_NUMBER = 10, + DISCORD_APPLICATION_OPTION_ATTACHMENT = 11 +}; +enum discord_application_command_permission_types { + DISCORD_APPLICATION_PERMISSION_ROLE = 1, + DISCORD_APPLICATION_PERMISSION_USER = 2, + DISCORD_APPLICATION_PERMISSION_CHANNEL = 3 +}; +struct discord_application_command { + + u64snowflake id; + + + enum discord_application_command_types type; + + + u64snowflake application_id; + + + u64snowflake guild_id; + + + char * name; + + + char * description; + + + struct discord_application_command_options * options; + + + u64bitmask default_member_permissions; + + + bool dm_permission; + + + bool default_permission; + + + u64snowflake version; + +}; +struct discord_application_commands { int size; + struct discord_application_command *array; +int realsize; }; +struct discord_application_command_option { + enum discord_application_command_option_types type; + char * name; + char * description; + + bool required; + + + struct discord_application_command_option_choices * choices; + + + struct discord_application_command_options * options; + + + struct integers * channel_types; + + + char * min_value; + + + char * max_value; + + + bool autocomplete; + +}; +struct discord_application_command_options { int size; + struct discord_application_command_option *array; +int realsize; }; +struct discord_application_command_option_choice { + char * name; + json_char * value; +}; +struct discord_application_command_option_choices { int size; + struct discord_application_command_option_choice *array; +int realsize; }; +struct discord_application_command_interaction_data_option { + char * name; + enum discord_application_command_option_types type; + + json_char * value; + + + struct discord_application_command_interaction_data_options * options; + + bool focused; +}; +struct discord_application_command_interaction_data_options { int size; + struct discord_application_command_interaction_data_option *array; +int realsize; }; +struct discord_guild_application_command_permission { + u64snowflake id; + u64snowflake application_id; + u64snowflake guild_id; + struct discord_application_command_permissions * permissions; +}; +struct discord_guild_application_command_permissions { int size; + struct discord_guild_application_command_permission *array; +int realsize; }; +struct discord_application_command_permission { + u64snowflake id; + enum discord_application_command_permission_types type; + bool permission; +}; +struct discord_application_command_permissions { int size; + struct discord_application_command_permission *array; +int realsize; }; +struct discord_create_global_application_command { + char * name; + char * description; + + struct discord_application_command_options * options; + + + u64bitmask default_member_permissions; + + bool dm_permission; + bool default_permission; + + enum discord_application_command_types type; + +}; +struct discord_edit_global_application_command { + char * name; + char * description; + + struct discord_application_command_options * options; + + + u64bitmask default_member_permissions; + + bool dm_permission; + bool default_permission; +}; +struct discord_create_guild_application_command { + char * name; + char * description; + + struct discord_application_command_options * options; + + + u64bitmask default_member_permissions; + + bool dm_permission; + bool default_permission; + + enum discord_application_command_types type; + +}; +struct discord_edit_guild_application_command { + char * name; + char * description; + + struct discord_application_command_options * options; + + + u64bitmask default_member_permissions; + + bool default_permission; +}; +struct discord_bulk_overwrite_guild_application_commands { + u64snowflake id; + char * name; + struct strings * name_localizations; + char * description; + struct strings * description_localizations; + + struct discord_application_command_options * options; + + + u64bitmask default_member_permissions; + + bool dm_permission; + + enum discord_application_command_types type; + +}; +enum discord_component_types { + DISCORD_COMPONENT_ACTION_ROW = 1, + DISCORD_COMPONENT_BUTTON = 2, + DISCORD_COMPONENT_SELECT_MENU = 3, + DISCORD_COMPONENT_TEXT_INPUT = 4, + DISCORD_COMPONENT_USER_SELECT = 5, + DISCORD_COMPONENT_ROLE_SELECT = 6, + DISCORD_COMPONENT_MENTION_SELECT = 7, + DISCORD_COMPONENT_CHANNEL_SELECT = 8, + DISCORD_COMPONENT_SECTION = 9, + DISCORD_COMPONENT_TEXT_DISPLAY = 10, + DISCORD_COMPONENT_THUMBNAIL = 11, + DISCORD_COMPONENT_MEDIA_GALLERY = 12, + DISCORD_COMPONENT_FILE = 13, + DISCORD_COMPONENT_SEPARATOR = 14, + DISCORD_COMPONENT_CONTENT_INVENTORY_ENTRY = 16, + DISCORD_COMPONENT_CONTAINER = 17 +}; +enum discord_component_styles { + DISCORD_BUTTON_PRIMARY = 1, + DISCORD_BUTTON_SECONDARY = 2, + DISCORD_BUTTON_SUCCESS = 3, + DISCORD_BUTTON_DANGER = 4, + DISCORD_BUTTON_LINK = 5, + DISCORD_TEXT_SHORT = 1, + DISCORD_TEXT_PARAGRAPH = 2 +}; +enum discord_component_spacing { + DISCORD_COMPONENT_SPACING_SMALL = 1, + DISCORD_COMPONENT_SPACING_LARGE = 2 +}; +struct discord_component { + + u64snowflake id; + + + enum discord_component_types type; + + char * custom_id; + + u64snowflake sku_id; + + + enum discord_component_styles style; + + char * label; + + struct discord_emoji * emoji; + + char * url; + + struct discord_select_options * options; + + char * placeholder; + + int min_values; + + + int max_values; + + + struct discord_components * components; + + int min_length; + int max_length; + bool required; + char * value; + + bool disabled; + + + struct discord_component * accessory; + + + struct discord_component_media * media; + + + char * content; + + + char * description; + + bool spoiler; + + struct discord_component_items * items; + + + struct discord_component_media * file; + + + bool divider; + + enum discord_component_spacing spacing; + + int color; + +}; +struct discord_components { int size; + struct discord_component *array; +int realsize; }; +struct discord_select_option { + char * label; + char * value; + char * description; + + struct discord_emoji * emoji; + + bool Default; +}; +struct discord_select_options { int size; + struct discord_select_option *array; +int realsize; }; +struct discord_component_media { + char * url; +}; +struct discord_component_item { + + struct discord_component_media * media; + + + char * description; + + bool spoiler; +}; +struct discord_component_items { int size; + struct discord_component_item *array; +int realsize; }; +enum discord_interaction_types { + DISCORD_INTERACTION_PING = 1, + DISCORD_INTERACTION_APPLICATION_COMMAND = 2, + DISCORD_INTERACTION_MESSAGE_COMPONENT = 3, + DISCORD_INTERACTION_APPLICATION_COMMAND_AUTOCOMPLETE = 4, + DISCORD_INTERACTION_MODAL_SUBMIT = 5 +}; +enum discord_interaction_callback_types { + DISCORD_INTERACTION_PONG = 1, + DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE = 4, + DISCORD_INTERACTION_DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE = 5, + DISCORD_INTERACTION_DEFERRED_UPDATE_MESSAGE = 6, + DISCORD_INTERACTION_UPDATE_MESSAGE = 7, + DISCORD_INTERACTION_APPLICATION_COMMAND_AUTOCOMPLETE_RESULT = 8, + DISCORD_INTERACTION_MODAL = 9 +}; +struct discord_interaction { + u64snowflake id; + u64snowflake application_id; + enum discord_interaction_types type; + struct discord_interaction_data * data; + u64snowflake guild_id; + u64snowflake channel_id; + struct discord_guild_member * member; + struct discord_user * user; + char * token; + int version; + struct discord_message * message; + char * locale; + char * guild_locale; +}; +struct discord_interaction_data { + u64snowflake id; + char * name; + enum discord_application_command_types type; + struct discord_resolved_data * resolved; + struct discord_application_command_interaction_data_options * options; + char * custom_id; + enum discord_component_types component_type; + struct strings * values; + u64snowflake target_id; + struct discord_components * components; +}; +struct discord_resolved_data { + json_char * users; + json_char * members; + json_char * roles; + json_char * channels; + json_char * messages; + json_char * attachments; +}; +struct discord_message_interaction { + u64snowflake id; + enum discord_interaction_types type; + char * name; + struct discord_user * user; + struct discord_guild_member * member; +}; +struct discord_interaction_response { + enum discord_interaction_callback_types type; + + struct discord_interaction_callback_data * data; + +}; +struct discord_interaction_callback_data { + + struct discord_components * components; + + + bool tts; + + + char * content; + + + struct discord_embeds * embeds; + + + u64bitmask flags; + + + struct discord_attachments * attachments; + + struct discord_application_command_option_choices * choices; + char * custom_id; + char * title; +}; +struct discord_edit_original_interaction_response { + u64snowflake thread_id; + char * content; + + struct discord_embeds * embeds; + + + struct discord_allowed_mention * allowed_mentions; + + + struct discord_components * components; + + + struct discord_attachments * attachments; + +}; +struct discord_create_followup_message { + bool wait; + u64snowflake thread_id; + char * avatar_url; + bool tts; + + struct discord_embeds * embeds; + + + struct discord_allowed_mention * allowed_mentions; + + + struct discord_components * components; + + + struct discord_attachments * attachments; + + + u64bitmask flags; + +}; +struct discord_edit_followup_message { + u64snowflake thread_id; + char * content; + + struct discord_embeds * embeds; + + + struct discord_allowed_mention * allowed_mentions; + + + struct discord_components * components; + + + struct discord_attachments * attachments; + +}; +#include +#include +#define JSMN_STRICT +#define JSMN_HEADER +#include "jsmn.h" +#include "jsmn-find.h" +#define JSONB_HEADER +#include "json-build.h" +#include +#include "carray.h" +#include "cog-utils.h" +#include "types.h" +#include "concord-error.h" +void strings_cleanup(struct strings *self); + + +void json_values_cleanup(struct json_values *self); + + +void snowflakes_cleanup(struct snowflakes *self); + + +void bitmasks_cleanup(struct bitmasks *self); + + +void integers_cleanup(struct integers *self); + + + + + + + + + + +void discord_application_init(struct discord_application *self); void discord_application_cleanup(struct discord_application *self); + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_install_params_init(struct discord_install_params *self); void discord_install_params_cleanup(struct discord_install_params *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_audit_log_init(struct discord_audit_log *self); void discord_audit_log_cleanup(struct discord_audit_log *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_auto_moderation_rule_init(struct discord_auto_moderation_rule *self); void discord_auto_moderation_rule_cleanup(struct discord_auto_moderation_rule *self); + + + + + + + + + + + + + + + + +void discord_auto_moderation_rules_cleanup(struct discord_auto_moderation_rules *self); + + +void discord_create_auto_moderation_rule_init(struct discord_create_auto_moderation_rule *self); void discord_create_auto_moderation_rule_cleanup(struct discord_create_auto_moderation_rule *self); + + + + + + + + + + + + + + + + + + + + + + +void discord_modify_auto_moderation_rule_init(struct discord_modify_auto_moderation_rule *self); void discord_modify_auto_moderation_rule_cleanup(struct discord_modify_auto_moderation_rule *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_invite_init(struct discord_invite *self); void discord_invite_cleanup(struct discord_invite *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_invites_cleanup(struct discord_invites *self); + + + + + + + + + + + + + + + + + + + +void discord_get_invite_init(struct discord_get_invite *self); void discord_get_invite_cleanup(struct discord_get_invite *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_channel_init(struct discord_channel *self); void discord_channel_cleanup(struct discord_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_channels_cleanup(struct discord_channels *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_message_init(struct discord_message *self); void discord_message_cleanup(struct discord_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_messages_cleanup(struct discord_messages *self); + + +void discord_followed_channel_init(struct discord_followed_channel *self); void discord_followed_channel_cleanup(struct discord_followed_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_thread_member_init(struct discord_thread_member *self); void discord_thread_member_cleanup(struct discord_thread_member *self); + + + + + + + +void discord_thread_members_cleanup(struct discord_thread_members *self); + + + + + + + + + + + + + +void discord_thread_tags_cleanup(struct discord_thread_tags *self); + + +void discord_embed_thumbnail_init(struct discord_embed_thumbnail *self); void discord_embed_thumbnail_cleanup(struct discord_embed_thumbnail *self); + + + + + + + + + + + +void discord_embed_video_init(struct discord_embed_video *self); void discord_embed_video_cleanup(struct discord_embed_video *self); + + + + + + + + + + + + + +void discord_embed_image_init(struct discord_embed_image *self); void discord_embed_image_cleanup(struct discord_embed_image *self); + + + + + + + + + + + +void discord_embed_provider_init(struct discord_embed_provider *self); void discord_embed_provider_cleanup(struct discord_embed_provider *self); + + + + + + + +void discord_embed_author_init(struct discord_embed_author *self); void discord_embed_author_cleanup(struct discord_embed_author *self); + + + + + + + + + + + +void discord_embed_footer_init(struct discord_embed_footer *self); void discord_embed_footer_cleanup(struct discord_embed_footer *self); + + + + + + + + +void discord_embed_field_init(struct discord_embed_field *self); void discord_embed_field_cleanup(struct discord_embed_field *self); + + + + +void discord_embed_fields_cleanup(struct discord_embed_fields *self); + + +void discord_embed_init(struct discord_embed *self); void discord_embed_cleanup(struct discord_embed *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_embeds_cleanup(struct discord_embeds *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_attachments_cleanup(struct discord_attachments *self); + + + + + + + + + + + + + + + + + + + + +void discord_thread_response_body_init(struct discord_thread_response_body *self); void discord_thread_response_body_cleanup(struct discord_thread_response_body *self); + + + + +void discord_modify_channel_init(struct discord_modify_channel *self); void discord_modify_channel_cleanup(struct discord_modify_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_create_message_init(struct discord_create_message *self); void discord_create_message_cleanup(struct discord_create_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_edit_message_init(struct discord_edit_message *self); void discord_edit_message_cleanup(struct discord_edit_message *self); + + + + + + + + + + + + + + + + + + +void discord_bulk_delete_messages_init(struct discord_bulk_delete_messages *self); void discord_bulk_delete_messages_cleanup(struct discord_bulk_delete_messages *self); + + + +void discord_edit_channel_permissions_init(struct discord_edit_channel_permissions *self); void discord_edit_channel_permissions_cleanup(struct discord_edit_channel_permissions *self); + + + + + + + + + +void discord_create_channel_invite_init(struct discord_create_channel_invite *self); void discord_create_channel_invite_cleanup(struct discord_create_channel_invite *self); + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_follow_news_channel_init(struct discord_follow_news_channel *self); void discord_follow_news_channel_cleanup(struct discord_follow_news_channel *self); + + + + + + + + + + +void discord_group_dm_add_recipient_init(struct discord_group_dm_add_recipient *self); void discord_group_dm_add_recipient_cleanup(struct discord_group_dm_add_recipient *self); + + + +void discord_start_thread_with_message_init(struct discord_start_thread_with_message *self); void discord_start_thread_with_message_cleanup(struct discord_start_thread_with_message *self); + + + + + + + + + +void discord_start_thread_without_message_init(struct discord_start_thread_without_message *self); void discord_start_thread_without_message_cleanup(struct discord_start_thread_without_message *self); + + + + + + + + + + + +void discord_list_active_threads_init(struct discord_list_active_threads *self); void discord_list_active_threads_cleanup(struct discord_list_active_threads *self); + + + + + + + + +void discord_emoji_init(struct discord_emoji *self); void discord_emoji_cleanup(struct discord_emoji *self); + + + + + + + + + + + + + +void discord_emojis_cleanup(struct discord_emojis *self); + + +void discord_create_guild_emoji_init(struct discord_create_guild_emoji *self); void discord_create_guild_emoji_cleanup(struct discord_create_guild_emoji *self); + + + + + + + +void discord_modify_guild_emoji_init(struct discord_modify_guild_emoji *self); void discord_modify_guild_emoji_cleanup(struct discord_modify_guild_emoji *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_guild_init(struct discord_guild *self); void discord_guild_cleanup(struct discord_guild *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_guilds_cleanup(struct discord_guilds *self); + + +void discord_guild_preview_init(struct discord_guild_preview *self); void discord_guild_preview_cleanup(struct discord_guild_preview *self); + + + + + + + + + + + + +void discord_guild_widget_settings_init(struct discord_guild_widget_settings *self); void discord_guild_widget_settings_cleanup(struct discord_guild_widget_settings *self); + + + + +void discord_guild_widget_init(struct discord_guild_widget *self); void discord_guild_widget_cleanup(struct discord_guild_widget *self); + + + + + + + +void discord_guild_member_init(struct discord_guild_member *self); void discord_guild_member_cleanup(struct discord_guild_member *self); + + + + + + + + + + + + + + + + + + + + + + + +void discord_guild_members_cleanup(struct discord_guild_members *self); + + +void discord_integration_init(struct discord_integration *self); void discord_integration_cleanup(struct discord_integration *self); + + + + + + + + + + + + + + + + + +void discord_integrations_cleanup(struct discord_integrations *self); + + + + + + + + + + + + + + + + +void discord_ban_init(struct discord_ban *self); void discord_ban_cleanup(struct discord_ban *self); + + + +void discord_bans_cleanup(struct discord_bans *self); + + +void discord_welcome_screen_init(struct discord_welcome_screen *self); void discord_welcome_screen_cleanup(struct discord_welcome_screen *self); + + + + + + + + + + + + + + +void discord_prune_count_init(struct discord_prune_count *self); void discord_prune_count_cleanup(struct discord_prune_count *self); + + +void discord_create_guild_init(struct discord_create_guild *self); void discord_create_guild_cleanup(struct discord_create_guild *self); + + + + + + + + + + + + + + + + + + + + + + + + +void discord_modify_guild_init(struct discord_modify_guild *self); void discord_modify_guild_cleanup(struct discord_modify_guild *self); + + + + + + + + + + + + + + + + + + + + + +void discord_create_guild_channel_init(struct discord_create_guild_channel *self); void discord_create_guild_channel_cleanup(struct discord_create_guild_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_modify_guild_channel_positions_cleanup(struct discord_modify_guild_channel_positions *self); + + +void discord_list_active_guild_threads_init(struct discord_list_active_guild_threads *self); void discord_list_active_guild_threads_cleanup(struct discord_list_active_guild_threads *self); + + + + + + + + + + + +void discord_add_guild_member_init(struct discord_add_guild_member *self); void discord_add_guild_member_cleanup(struct discord_add_guild_member *self); + + + + + + +void discord_modify_guild_member_init(struct discord_modify_guild_member *self); void discord_modify_guild_member_cleanup(struct discord_modify_guild_member *self); + + + + + + + + + + + + + + + + + + + + +void discord_modify_current_member_init(struct discord_modify_current_member *self); void discord_modify_current_member_cleanup(struct discord_modify_current_member *self); + + + + + +void discord_modify_current_user_nick_init(struct discord_modify_current_user_nick *self); void discord_modify_current_user_nick_cleanup(struct discord_modify_current_user_nick *self); + + + + + + + + + + + + + + +void discord_create_guild_ban_init(struct discord_create_guild_ban *self); void discord_create_guild_ban_cleanup(struct discord_create_guild_ban *self); + + + + + + + + +void discord_create_guild_role_init(struct discord_create_guild_role *self); void discord_create_guild_role_cleanup(struct discord_create_guild_role *self); + + + + + + + + + + + + + + + +void discord_modify_guild_role_positions_cleanup(struct discord_modify_guild_role_positions *self); + + +void discord_modify_guild_role_init(struct discord_modify_guild_role *self); void discord_modify_guild_role_cleanup(struct discord_modify_guild_role *self); + + + + + + + + + + + + + + + + + + +void discord_begin_guild_prune_init(struct discord_begin_guild_prune *self); void discord_begin_guild_prune_cleanup(struct discord_begin_guild_prune *self); + + + + + + + + + + + + + + + + + + + +void discord_modify_guild_welcome_screen_init(struct discord_modify_guild_welcome_screen *self); void discord_modify_guild_welcome_screen_cleanup(struct discord_modify_guild_welcome_screen *self); + + + + + + + +void discord_modify_current_user_voice_state_init(struct discord_modify_current_user_voice_state *self); void discord_modify_current_user_voice_state_cleanup(struct discord_modify_current_user_voice_state *self); + + + + + + +void discord_modify_user_voice_state_init(struct discord_modify_user_voice_state *self); void discord_modify_user_voice_state_cleanup(struct discord_modify_user_voice_state *self); + + + + + + + + + + + + + + + + + +void discord_guild_scheduled_event_init(struct discord_guild_scheduled_event *self); void discord_guild_scheduled_event_cleanup(struct discord_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_guild_scheduled_events_cleanup(struct discord_guild_scheduled_events *self); + + + + + + + + + + + +void discord_guild_scheduled_event_users_cleanup(struct discord_guild_scheduled_event_users *self); + + + + + +void discord_create_guild_scheduled_event_init(struct discord_create_guild_scheduled_event *self); void discord_create_guild_scheduled_event_cleanup(struct discord_create_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_modify_guild_scheduled_event_init(struct discord_modify_guild_scheduled_event *self); void discord_modify_guild_scheduled_event_cleanup(struct discord_modify_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_guild_template_init(struct discord_guild_template *self); void discord_guild_template_cleanup(struct discord_guild_template *self); + + + + + + + + + + + + +void discord_guild_templates_cleanup(struct discord_guild_templates *self); + + +void discord_create_guild_from_guild_template_init(struct discord_create_guild_from_guild_template *self); void discord_create_guild_from_guild_template_cleanup(struct discord_create_guild_from_guild_template *self); + + + + + +void discord_create_guild_template_init(struct discord_create_guild_template *self); void discord_create_guild_template_cleanup(struct discord_create_guild_template *self); + + + + + +void discord_modify_guild_template_init(struct discord_modify_guild_template *self); void discord_modify_guild_template_cleanup(struct discord_modify_guild_template *self); + + + + + + + + + + + +void discord_stage_instance_init(struct discord_stage_instance *self); void discord_stage_instance_cleanup(struct discord_stage_instance *self); + + + + + + + + + + + + +void discord_create_stage_instance_init(struct discord_create_stage_instance *self); void discord_create_stage_instance_cleanup(struct discord_create_stage_instance *self); + + + + + + + +void discord_modify_stage_instance_init(struct discord_modify_stage_instance *self); void discord_modify_stage_instance_cleanup(struct discord_modify_stage_instance *self); + + + + + + + + + + + + + + + + + + +void discord_sticker_init(struct discord_sticker *self); void discord_sticker_cleanup(struct discord_sticker *self); + + + + + + + + + + + + + + + + + + + + + + +void discord_stickers_cleanup(struct discord_stickers *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_sticker_packs_cleanup(struct discord_sticker_packs *self); + + +void discord_list_nitro_sticker_packs_init(struct discord_list_nitro_sticker_packs *self); void discord_list_nitro_sticker_packs_cleanup(struct discord_list_nitro_sticker_packs *self); + + + + + + + + + +void discord_modify_guild_sticker_init(struct discord_modify_guild_sticker *self); void discord_modify_guild_sticker_cleanup(struct discord_modify_guild_sticker *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_user_init(struct discord_user *self); void discord_user_cleanup(struct discord_user *self); + + + + + + + + + + + + + + + + +void discord_users_cleanup(struct discord_users *self); + + + + + + + + + + + + + + + +void discord_connections_cleanup(struct discord_connections *self); + + +void discord_modify_current_user_init(struct discord_modify_current_user *self); void discord_modify_current_user_cleanup(struct discord_modify_current_user *self); + + + + + + + + + + + + + + + + + + +void discord_create_dm_init(struct discord_create_dm *self); void discord_create_dm_cleanup(struct discord_create_dm *self); + + + + +void discord_create_group_dm_init(struct discord_create_group_dm *self); void discord_create_group_dm_cleanup(struct discord_create_group_dm *self); + + + + + + + +void discord_voice_state_init(struct discord_voice_state *self); void discord_voice_state_cleanup(struct discord_voice_state *self); + + + + + + + + + + + + + + + + +void discord_voice_states_cleanup(struct discord_voice_states *self); + + + + + + + + + +void discord_voice_regions_cleanup(struct discord_voice_regions *self); + + + + + + + +void discord_webhook_init(struct discord_webhook *self); void discord_webhook_cleanup(struct discord_webhook *self); + + + + + + + + + + + + + + + + + +void discord_webhooks_cleanup(struct discord_webhooks *self); + + +void discord_create_webhook_init(struct discord_create_webhook *self); void discord_create_webhook_cleanup(struct discord_create_webhook *self); + + + + + + +void discord_modify_webhook_init(struct discord_modify_webhook *self); void discord_modify_webhook_cleanup(struct discord_modify_webhook *self); + + + + + + + + + + +void discord_modify_webhook_with_token_init(struct discord_modify_webhook_with_token *self); void discord_modify_webhook_with_token_cleanup(struct discord_modify_webhook_with_token *self); + + + + + +void discord_execute_webhook_init(struct discord_execute_webhook *self); void discord_execute_webhook_cleanup(struct discord_execute_webhook *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_edit_webhook_message_init(struct discord_edit_webhook_message *self); void discord_edit_webhook_message_cleanup(struct discord_edit_webhook_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_presence_update_init(struct discord_presence_update *self); void discord_presence_update_cleanup(struct discord_presence_update *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_identify_init(struct discord_identify *self); void discord_identify_cleanup(struct discord_identify *self); + + + + + + + + + + + + + + + + + +void discord_resume_init(struct discord_resume *self); void discord_resume_cleanup(struct discord_resume *self); + + + + +void discord_request_guild_members_init(struct discord_request_guild_members *self); void discord_request_guild_members_cleanup(struct discord_request_guild_members *self); + + + + + + + + + + + +void discord_update_voice_state_init(struct discord_update_voice_state *self); void discord_update_voice_state_cleanup(struct discord_update_voice_state *self); + + + + + +void discord_ready_init(struct discord_ready *self); void discord_ready_cleanup(struct discord_ready *self); + + + + + + + +void discord_auto_moderation_action_execution_init(struct discord_auto_moderation_action_execution *self); void discord_auto_moderation_action_execution_cleanup(struct discord_auto_moderation_action_execution *self); + + + + + + + + + + + +void discord_thread_list_sync_init(struct discord_thread_list_sync *self); void discord_thread_list_sync_cleanup(struct discord_thread_list_sync *self); + + + + + +void discord_thread_members_update_init(struct discord_thread_members_update *self); void discord_thread_members_update_cleanup(struct discord_thread_members_update *self); + + + + + + +void discord_channel_pins_update_init(struct discord_channel_pins_update *self); void discord_channel_pins_update_cleanup(struct discord_channel_pins_update *self); + + + + +void discord_guild_ban_add_init(struct discord_guild_ban_add *self); void discord_guild_ban_add_cleanup(struct discord_guild_ban_add *self); + + + +void discord_guild_ban_remove_init(struct discord_guild_ban_remove *self); void discord_guild_ban_remove_cleanup(struct discord_guild_ban_remove *self); + + + +void discord_guild_emojis_update_init(struct discord_guild_emojis_update *self); void discord_guild_emojis_update_cleanup(struct discord_guild_emojis_update *self); + + + +void discord_guild_stickers_update_init(struct discord_guild_stickers_update *self); void discord_guild_stickers_update_cleanup(struct discord_guild_stickers_update *self); + + + +void discord_guild_integrations_update_init(struct discord_guild_integrations_update *self); void discord_guild_integrations_update_cleanup(struct discord_guild_integrations_update *self); + + +void discord_guild_member_remove_init(struct discord_guild_member_remove *self); void discord_guild_member_remove_cleanup(struct discord_guild_member_remove *self); + + + +void discord_guild_member_update_init(struct discord_guild_member_update *self); void discord_guild_member_update_cleanup(struct discord_guild_member_update *self); + + + + + + + + + + + + +void discord_guild_members_chunk_init(struct discord_guild_members_chunk *self); void discord_guild_members_chunk_cleanup(struct discord_guild_members_chunk *self); + + + + + + + + +void discord_guild_role_create_init(struct discord_guild_role_create *self); void discord_guild_role_create_cleanup(struct discord_guild_role_create *self); + + + +void discord_guild_role_update_init(struct discord_guild_role_update *self); void discord_guild_role_update_cleanup(struct discord_guild_role_update *self); + + + +void discord_guild_role_delete_init(struct discord_guild_role_delete *self); void discord_guild_role_delete_cleanup(struct discord_guild_role_delete *self); + + + +void discord_guild_scheduled_event_user_add_init(struct discord_guild_scheduled_event_user_add *self); void discord_guild_scheduled_event_user_add_cleanup(struct discord_guild_scheduled_event_user_add *self); + + + + +void discord_guild_scheduled_event_user_remove_init(struct discord_guild_scheduled_event_user_remove *self); void discord_guild_scheduled_event_user_remove_cleanup(struct discord_guild_scheduled_event_user_remove *self); + + + + +void discord_integration_delete_init(struct discord_integration_delete *self); void discord_integration_delete_cleanup(struct discord_integration_delete *self); + + + + +void discord_invite_create_init(struct discord_invite_create *self); void discord_invite_create_cleanup(struct discord_invite_create *self); + + + + + + + + + + + + + +void discord_invite_delete_init(struct discord_invite_delete *self); void discord_invite_delete_cleanup(struct discord_invite_delete *self); + + + + +void discord_message_delete_init(struct discord_message_delete *self); void discord_message_delete_cleanup(struct discord_message_delete *self); + + + + +void discord_message_delete_bulk_init(struct discord_message_delete_bulk *self); void discord_message_delete_bulk_cleanup(struct discord_message_delete_bulk *self); + + + + +void discord_message_reaction_add_init(struct discord_message_reaction_add *self); void discord_message_reaction_add_cleanup(struct discord_message_reaction_add *self); + + + + + + + +void discord_message_reaction_remove_init(struct discord_message_reaction_remove *self); void discord_message_reaction_remove_cleanup(struct discord_message_reaction_remove *self); + + + + + + +void discord_message_reaction_remove_all_init(struct discord_message_reaction_remove_all *self); void discord_message_reaction_remove_all_cleanup(struct discord_message_reaction_remove_all *self); + + + + +void discord_message_reaction_remove_emoji_init(struct discord_message_reaction_remove_emoji *self); void discord_message_reaction_remove_emoji_cleanup(struct discord_message_reaction_remove_emoji *self); + + + + + +void discord_typing_start_init(struct discord_typing_start *self); void discord_typing_start_cleanup(struct discord_typing_start *self); + + + + + + +void discord_voice_server_update_init(struct discord_voice_server_update *self); void discord_voice_server_update_cleanup(struct discord_voice_server_update *self); + + + + +void discord_webhooks_update_init(struct discord_webhooks_update *self); void discord_webhooks_update_cleanup(struct discord_webhooks_update *self); + + + +void discord_session_start_limit_init(struct discord_session_start_limit *self); void discord_session_start_limit_cleanup(struct discord_session_start_limit *self); + + + + + +void discord_auth_response_init(struct discord_auth_response *self); void discord_auth_response_cleanup(struct discord_auth_response *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_role_init(struct discord_role *self); void discord_role_cleanup(struct discord_role *self); + + + + + + + + + + + + + + + + + + +void discord_roles_cleanup(struct discord_roles *self); + + + + + + + + + + + + + + + +void discord_team_init(struct discord_team *self); void discord_team_cleanup(struct discord_team *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_application_command_init(struct discord_application_command *self); void discord_application_command_cleanup(struct discord_application_command *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_application_commands_cleanup(struct discord_application_commands *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_guild_application_command_permissions_cleanup(struct discord_guild_application_command_permissions *self); + + +void discord_application_command_permission_init(struct discord_application_command_permission *self); void discord_application_command_permission_cleanup(struct discord_application_command_permission *self); + + + + +void discord_application_command_permissions_cleanup(struct discord_application_command_permissions *self); + + +void discord_create_global_application_command_init(struct discord_create_global_application_command *self); void discord_create_global_application_command_cleanup(struct discord_create_global_application_command *self); + + + + + + + + + + + + + + +void discord_edit_global_application_command_init(struct discord_edit_global_application_command *self); void discord_edit_global_application_command_cleanup(struct discord_edit_global_application_command *self); + + + + + + + + + + + +void discord_create_guild_application_command_init(struct discord_create_guild_application_command *self); void discord_create_guild_application_command_cleanup(struct discord_create_guild_application_command *self); + + + + + + + + + + + + + + +void discord_edit_guild_application_command_init(struct discord_edit_guild_application_command *self); void discord_edit_guild_application_command_cleanup(struct discord_edit_guild_application_command *self); + + + + + + + + + + +void discord_bulk_overwrite_guild_application_commands_init(struct discord_bulk_overwrite_guild_application_commands *self); void discord_bulk_overwrite_guild_application_commands_cleanup(struct discord_bulk_overwrite_guild_application_commands *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_component_init(struct discord_component *self); void discord_component_cleanup(struct discord_component *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_components_cleanup(struct discord_components *self); + + + + + + + + + + + +void discord_select_options_cleanup(struct discord_select_options *self); + + + + + + + + + + + + + + +void discord_component_items_cleanup(struct discord_component_items *self); + + + + + + + + + + + + + + + + + + +void discord_interaction_init(struct discord_interaction *self); void discord_interaction_cleanup(struct discord_interaction *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_interaction_response_init(struct discord_interaction_response *self); void discord_interaction_response_cleanup(struct discord_interaction_response *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void discord_edit_original_interaction_response_init(struct discord_edit_original_interaction_response *self); void discord_edit_original_interaction_response_cleanup(struct discord_edit_original_interaction_response *self); + + + + + + + + + + + + + + + +void discord_create_followup_message_init(struct discord_create_followup_message *self); void discord_create_followup_message_cleanup(struct discord_create_followup_message *self); + + + + + + + + + + + + + + + + + + + + +void discord_edit_followup_message_init(struct discord_edit_followup_message *self); void discord_edit_followup_message_cleanup(struct discord_edit_followup_message *self); + + + + + + + + + + + + + + + +long strings_from_jsmnf(const jsmnf_pair *root, const char *js, struct strings *self); long strings_from_json(const char buf[], size_t size, struct strings *self); + + +long json_values_from_jsmnf(const jsmnf_pair *root, const char *js, struct json_values *self); long json_values_from_json(const char buf[], size_t size, struct json_values *self); + + +long snowflakes_from_jsmnf(const jsmnf_pair *root, const char *js, struct snowflakes *self); long snowflakes_from_json(const char buf[], size_t size, struct snowflakes *self); + + +long bitmasks_from_jsmnf(const jsmnf_pair *root, const char *js, struct bitmasks *self); long bitmasks_from_json(const char buf[], size_t size, struct bitmasks *self); + + +long integers_from_jsmnf(const jsmnf_pair *root, const char *js, struct integers *self); long integers_from_json(const char buf[], size_t size, struct integers *self); + + + + + + + + + + +long discord_application_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_application *self); long discord_application_from_json(const char buf[], size_t size, struct discord_application *self); + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_install_params_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_install_params *self); long discord_install_params_from_json(const char buf[], size_t size, struct discord_install_params *self); + + + +long discord_audit_log_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_audit_log *self); long discord_audit_log_from_json(const char buf[], size_t size, struct discord_audit_log *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_auto_moderation_rule_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_auto_moderation_rule *self); long discord_auto_moderation_rule_from_json(const char buf[], size_t size, struct discord_auto_moderation_rule *self); + + + + + + + + + + + + + + + + +long discord_auto_moderation_rules_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_auto_moderation_rules *self); long discord_auto_moderation_rules_from_json(const char buf[], size_t size, struct discord_auto_moderation_rules *self); + + +long discord_create_auto_moderation_rule_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_auto_moderation_rule *self); long discord_create_auto_moderation_rule_from_json(const char buf[], size_t size, struct discord_create_auto_moderation_rule *self); + + + + + + + + + + + + + + + + + + + + + +long discord_modify_auto_moderation_rule_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_auto_moderation_rule *self); long discord_modify_auto_moderation_rule_from_json(const char buf[], size_t size, struct discord_modify_auto_moderation_rule *self); + + + + + + + + + + + + + + + + + + + + +long discord_invite_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_invite *self); long discord_invite_from_json(const char buf[], size_t size, struct discord_invite *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_invites_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_invites *self); long discord_invites_from_json(const char buf[], size_t size, struct discord_invites *self); + + + + + + + + + + + + + + + + + + + +long discord_get_invite_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_get_invite *self); long discord_get_invite_from_json(const char buf[], size_t size, struct discord_get_invite *self); + + + + + + + + + +long discord_channel_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_channel *self); long discord_channel_from_json(const char buf[], size_t size, struct discord_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_channels_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_channels *self); long discord_channels_from_json(const char buf[], size_t size, struct discord_channels *self); + + + + + + + + + + + + + + + + + + + + + + + + +long discord_message_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_message *self); long discord_message_from_json(const char buf[], size_t size, struct discord_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_messages_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_messages *self); long discord_messages_from_json(const char buf[], size_t size, struct discord_messages *self); + + +long discord_followed_channel_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_followed_channel *self); long discord_followed_channel_from_json(const char buf[], size_t size, struct discord_followed_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_thread_member_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_thread_member *self); long discord_thread_member_from_json(const char buf[], size_t size, struct discord_thread_member *self); + + + + + + + +long discord_thread_members_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_thread_members *self); long discord_thread_members_from_json(const char buf[], size_t size, struct discord_thread_members *self); + + + + + + + + + + + + + +long discord_thread_tags_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_thread_tags *self); long discord_thread_tags_from_json(const char buf[], size_t size, struct discord_thread_tags *self); + + +long discord_embed_thumbnail_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed_thumbnail *self); long discord_embed_thumbnail_from_json(const char buf[], size_t size, struct discord_embed_thumbnail *self); + + + + + + + + + + + +long discord_embed_video_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed_video *self); long discord_embed_video_from_json(const char buf[], size_t size, struct discord_embed_video *self); + + + + + + + + + + + + + +long discord_embed_image_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed_image *self); long discord_embed_image_from_json(const char buf[], size_t size, struct discord_embed_image *self); + + + + + + + + + + + +long discord_embed_provider_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed_provider *self); long discord_embed_provider_from_json(const char buf[], size_t size, struct discord_embed_provider *self); + + + + + + + +long discord_embed_author_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed_author *self); long discord_embed_author_from_json(const char buf[], size_t size, struct discord_embed_author *self); + + + + + + + + + + + +long discord_embed_footer_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed_footer *self); long discord_embed_footer_from_json(const char buf[], size_t size, struct discord_embed_footer *self); + + + + + + + + +long discord_embed_field_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed_field *self); long discord_embed_field_from_json(const char buf[], size_t size, struct discord_embed_field *self); + + + + +long discord_embed_fields_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed_fields *self); long discord_embed_fields_from_json(const char buf[], size_t size, struct discord_embed_fields *self); + + +long discord_embed_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embed *self); long discord_embed_from_json(const char buf[], size_t size, struct discord_embed *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_embeds_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_embeds *self); long discord_embeds_from_json(const char buf[], size_t size, struct discord_embeds *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_attachments_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_attachments *self); long discord_attachments_from_json(const char buf[], size_t size, struct discord_attachments *self); + + + + + + + + + + + + + + + + + + + + +long discord_thread_response_body_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_thread_response_body *self); long discord_thread_response_body_from_json(const char buf[], size_t size, struct discord_thread_response_body *self); + + + + +long discord_modify_channel_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_channel *self); long discord_modify_channel_from_json(const char buf[], size_t size, struct discord_modify_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_create_message_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_message *self); long discord_create_message_from_json(const char buf[], size_t size, struct discord_create_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_edit_message_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_edit_message *self); long discord_edit_message_from_json(const char buf[], size_t size, struct discord_edit_message *self); + + + + + + + + + + + + + + + +long discord_bulk_delete_messages_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_bulk_delete_messages *self); long discord_bulk_delete_messages_from_json(const char buf[], size_t size, struct discord_bulk_delete_messages *self); + + +long discord_edit_channel_permissions_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_edit_channel_permissions *self); long discord_edit_channel_permissions_from_json(const char buf[], size_t size, struct discord_edit_channel_permissions *self); + + + + + + + + +long discord_create_channel_invite_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_channel_invite *self); long discord_create_channel_invite_from_json(const char buf[], size_t size, struct discord_create_channel_invite *self); + + + + + + + + + + + + + + + + + + + + + + +long discord_follow_news_channel_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_follow_news_channel *self); long discord_follow_news_channel_from_json(const char buf[], size_t size, struct discord_follow_news_channel *self); + + + + +long discord_group_dm_add_recipient_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_group_dm_add_recipient *self); long discord_group_dm_add_recipient_from_json(const char buf[], size_t size, struct discord_group_dm_add_recipient *self); + + + +long discord_start_thread_with_message_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_start_thread_with_message *self); long discord_start_thread_with_message_from_json(const char buf[], size_t size, struct discord_start_thread_with_message *self); + + + + + + + + +long discord_start_thread_without_message_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_start_thread_without_message *self); long discord_start_thread_without_message_from_json(const char buf[], size_t size, struct discord_start_thread_without_message *self); + + + + + + + + + + +long discord_list_active_threads_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_list_active_threads *self); long discord_list_active_threads_from_json(const char buf[], size_t size, struct discord_list_active_threads *self); + + + + + + + + +long discord_emoji_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_emoji *self); long discord_emoji_from_json(const char buf[], size_t size, struct discord_emoji *self); + + + + + + + + + + + + + +long discord_emojis_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_emojis *self); long discord_emojis_from_json(const char buf[], size_t size, struct discord_emojis *self); + + +long discord_create_guild_emoji_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild_emoji *self); long discord_create_guild_emoji_from_json(const char buf[], size_t size, struct discord_create_guild_emoji *self); + + + + + + +long discord_modify_guild_emoji_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_emoji *self); long discord_modify_guild_emoji_from_json(const char buf[], size_t size, struct discord_modify_guild_emoji *self); + + + + + + + + + + +long discord_guild_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild *self); long discord_guild_from_json(const char buf[], size_t size, struct discord_guild *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_guilds_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guilds *self); long discord_guilds_from_json(const char buf[], size_t size, struct discord_guilds *self); + + +long discord_guild_preview_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_preview *self); long discord_guild_preview_from_json(const char buf[], size_t size, struct discord_guild_preview *self); + + + + + + + + + + + + +long discord_guild_widget_settings_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_widget_settings *self); long discord_guild_widget_settings_from_json(const char buf[], size_t size, struct discord_guild_widget_settings *self); + + + +long discord_guild_widget_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_widget *self); long discord_guild_widget_from_json(const char buf[], size_t size, struct discord_guild_widget *self); + + + + + + + +long discord_guild_member_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_member *self); long discord_guild_member_from_json(const char buf[], size_t size, struct discord_guild_member *self); + + + + + + + + + + + + + + + + + + + + + + + +long discord_guild_members_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_members *self); long discord_guild_members_from_json(const char buf[], size_t size, struct discord_guild_members *self); + + +long discord_integration_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_integration *self); long discord_integration_from_json(const char buf[], size_t size, struct discord_integration *self); + + + + + + + + + + + + + + + + + +long discord_integrations_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_integrations *self); long discord_integrations_from_json(const char buf[], size_t size, struct discord_integrations *self); + + + + + + + + + + + + + + + + +long discord_ban_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_ban *self); long discord_ban_from_json(const char buf[], size_t size, struct discord_ban *self); + + + +long discord_bans_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_bans *self); long discord_bans_from_json(const char buf[], size_t size, struct discord_bans *self); + + +long discord_welcome_screen_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_welcome_screen *self); long discord_welcome_screen_from_json(const char buf[], size_t size, struct discord_welcome_screen *self); + + + + + + + + + + + + + + +long discord_prune_count_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_prune_count *self); long discord_prune_count_from_json(const char buf[], size_t size, struct discord_prune_count *self); + + +long discord_create_guild_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild *self); long discord_create_guild_from_json(const char buf[], size_t size, struct discord_create_guild *self); + + + + + + + + + + + + + + + + + + + + + + + +long discord_modify_guild_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild *self); long discord_modify_guild_from_json(const char buf[], size_t size, struct discord_modify_guild *self); + + + + + + + + + + + + + + + + + + + + +long discord_create_guild_channel_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild_channel *self); long discord_create_guild_channel_from_json(const char buf[], size_t size, struct discord_create_guild_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_modify_guild_channel_positions_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_channel_positions *self); long discord_modify_guild_channel_positions_from_json(const char buf[], size_t size, struct discord_modify_guild_channel_positions *self); + + +long discord_list_active_guild_threads_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_list_active_guild_threads *self); long discord_list_active_guild_threads_from_json(const char buf[], size_t size, struct discord_list_active_guild_threads *self); + + + +long discord_add_guild_member_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_add_guild_member *self); long discord_add_guild_member_from_json(const char buf[], size_t size, struct discord_add_guild_member *self); + + + + + + +long discord_modify_guild_member_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_member *self); long discord_modify_guild_member_from_json(const char buf[], size_t size, struct discord_modify_guild_member *self); + + + + + + + + + + + + + + + + + + + +long discord_modify_current_member_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_current_member *self); long discord_modify_current_member_from_json(const char buf[], size_t size, struct discord_modify_current_member *self); + + + + +long discord_modify_current_user_nick_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_current_user_nick *self); long discord_modify_current_user_nick_from_json(const char buf[], size_t size, struct discord_modify_current_user_nick *self); + + + + +long discord_create_guild_ban_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild_ban *self); long discord_create_guild_ban_from_json(const char buf[], size_t size, struct discord_create_guild_ban *self); + + + + +long discord_create_guild_role_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild_role *self); long discord_create_guild_role_from_json(const char buf[], size_t size, struct discord_create_guild_role *self); + + + + + + + + + + + + + + +long discord_modify_guild_role_positions_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_role_positions *self); long discord_modify_guild_role_positions_from_json(const char buf[], size_t size, struct discord_modify_guild_role_positions *self); + + +long discord_modify_guild_role_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_role *self); long discord_modify_guild_role_from_json(const char buf[], size_t size, struct discord_modify_guild_role *self); + + + + + + + + +long discord_begin_guild_prune_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_begin_guild_prune *self); long discord_begin_guild_prune_from_json(const char buf[], size_t size, struct discord_begin_guild_prune *self); + + + + + + +long discord_modify_guild_welcome_screen_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_welcome_screen *self); long discord_modify_guild_welcome_screen_from_json(const char buf[], size_t size, struct discord_modify_guild_welcome_screen *self); + + + + + + +long discord_modify_current_user_voice_state_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_current_user_voice_state *self); long discord_modify_current_user_voice_state_from_json(const char buf[], size_t size, struct discord_modify_current_user_voice_state *self); + + + + + + +long discord_modify_user_voice_state_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_user_voice_state *self); long discord_modify_user_voice_state_from_json(const char buf[], size_t size, struct discord_modify_user_voice_state *self); + + + +long discord_guild_scheduled_event_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_scheduled_event *self); long discord_guild_scheduled_event_from_json(const char buf[], size_t size, struct discord_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_guild_scheduled_events_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_scheduled_events *self); long discord_guild_scheduled_events_from_json(const char buf[], size_t size, struct discord_guild_scheduled_events *self); + + + + + + + + + + + +long discord_guild_scheduled_event_users_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_scheduled_event_users *self); long discord_guild_scheduled_event_users_from_json(const char buf[], size_t size, struct discord_guild_scheduled_event_users *self); + + +long discord_create_guild_scheduled_event_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild_scheduled_event *self); long discord_create_guild_scheduled_event_from_json(const char buf[], size_t size, struct discord_create_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_modify_guild_scheduled_event_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_scheduled_event *self); long discord_modify_guild_scheduled_event_from_json(const char buf[], size_t size, struct discord_modify_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_guild_template_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_template *self); long discord_guild_template_from_json(const char buf[], size_t size, struct discord_guild_template *self); + + + + + + + + + + + + +long discord_guild_templates_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_templates *self); long discord_guild_templates_from_json(const char buf[], size_t size, struct discord_guild_templates *self); + + +long discord_create_guild_from_guild_template_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild_from_guild_template *self); long discord_create_guild_from_guild_template_from_json(const char buf[], size_t size, struct discord_create_guild_from_guild_template *self); + + + + + +long discord_create_guild_template_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild_template *self); long discord_create_guild_template_from_json(const char buf[], size_t size, struct discord_create_guild_template *self); + + + + + +long discord_modify_guild_template_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_template *self); long discord_modify_guild_template_from_json(const char buf[], size_t size, struct discord_modify_guild_template *self); + + + + + + + +long discord_stage_instance_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_stage_instance *self); long discord_stage_instance_from_json(const char buf[], size_t size, struct discord_stage_instance *self); + + + + + + + + + + + + +long discord_create_stage_instance_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_stage_instance *self); long discord_create_stage_instance_from_json(const char buf[], size_t size, struct discord_create_stage_instance *self); + + + + + + +long discord_modify_stage_instance_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_stage_instance *self); long discord_modify_stage_instance_from_json(const char buf[], size_t size, struct discord_modify_stage_instance *self); + + + + + +long discord_sticker_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_sticker *self); long discord_sticker_from_json(const char buf[], size_t size, struct discord_sticker *self); + + + + + + + + + + + + + + + + + + + + + + +long discord_stickers_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_stickers *self); long discord_stickers_from_json(const char buf[], size_t size, struct discord_stickers *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_sticker_packs_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_sticker_packs *self); long discord_sticker_packs_from_json(const char buf[], size_t size, struct discord_sticker_packs *self); + + +long discord_list_nitro_sticker_packs_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_list_nitro_sticker_packs *self); long discord_list_nitro_sticker_packs_from_json(const char buf[], size_t size, struct discord_list_nitro_sticker_packs *self); + + +long discord_modify_guild_sticker_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_guild_sticker *self); long discord_modify_guild_sticker_from_json(const char buf[], size_t size, struct discord_modify_guild_sticker *self); + + + + + + + + + + + + + + + + + + + +long discord_user_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_user *self); long discord_user_from_json(const char buf[], size_t size, struct discord_user *self); + + + + + + + + + + + + + + + + +long discord_users_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_users *self); long discord_users_from_json(const char buf[], size_t size, struct discord_users *self); + + + + + + + + + + + + + + + +long discord_connections_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_connections *self); long discord_connections_from_json(const char buf[], size_t size, struct discord_connections *self); + + +long discord_modify_current_user_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_current_user *self); long discord_modify_current_user_from_json(const char buf[], size_t size, struct discord_modify_current_user *self); + + + + + + + +long discord_create_dm_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_dm *self); long discord_create_dm_from_json(const char buf[], size_t size, struct discord_create_dm *self); + + + + +long discord_create_group_dm_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_group_dm *self); long discord_create_group_dm_from_json(const char buf[], size_t size, struct discord_create_group_dm *self); + + + + + + + +long discord_voice_state_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_voice_state *self); long discord_voice_state_from_json(const char buf[], size_t size, struct discord_voice_state *self); + + + + + + + + + + + + + + + + +long discord_voice_states_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_voice_states *self); long discord_voice_states_from_json(const char buf[], size_t size, struct discord_voice_states *self); + + + + + + + + + +long discord_voice_regions_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_voice_regions *self); long discord_voice_regions_from_json(const char buf[], size_t size, struct discord_voice_regions *self); + + +long discord_webhook_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_webhook *self); long discord_webhook_from_json(const char buf[], size_t size, struct discord_webhook *self); + + + + + + + + + + + + + + + + + +long discord_webhooks_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_webhooks *self); long discord_webhooks_from_json(const char buf[], size_t size, struct discord_webhooks *self); + + +long discord_create_webhook_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_webhook *self); long discord_create_webhook_from_json(const char buf[], size_t size, struct discord_create_webhook *self); + + + + + +long discord_modify_webhook_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_webhook *self); long discord_modify_webhook_from_json(const char buf[], size_t size, struct discord_modify_webhook *self); + + + + + + +long discord_modify_webhook_with_token_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_modify_webhook_with_token *self); long discord_modify_webhook_with_token_from_json(const char buf[], size_t size, struct discord_modify_webhook_with_token *self); + + + + + +long discord_execute_webhook_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_execute_webhook *self); long discord_execute_webhook_from_json(const char buf[], size_t size, struct discord_execute_webhook *self); + + + + + + + + + + + + + + + + + + + + +long discord_edit_webhook_message_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_edit_webhook_message *self); long discord_edit_webhook_message_from_json(const char buf[], size_t size, struct discord_edit_webhook_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_presence_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_presence_update *self); long discord_presence_update_from_json(const char buf[], size_t size, struct discord_presence_update *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_ready_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_ready *self); long discord_ready_from_json(const char buf[], size_t size, struct discord_ready *self); + + + + + + + +long discord_auto_moderation_action_execution_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_auto_moderation_action_execution *self); long discord_auto_moderation_action_execution_from_json(const char buf[], size_t size, struct discord_auto_moderation_action_execution *self); + + + + + + + + + + + +long discord_thread_list_sync_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_thread_list_sync *self); long discord_thread_list_sync_from_json(const char buf[], size_t size, struct discord_thread_list_sync *self); + + + + + +long discord_thread_members_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_thread_members_update *self); long discord_thread_members_update_from_json(const char buf[], size_t size, struct discord_thread_members_update *self); + + + + + + +long discord_channel_pins_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_channel_pins_update *self); long discord_channel_pins_update_from_json(const char buf[], size_t size, struct discord_channel_pins_update *self); + + + + +long discord_guild_ban_add_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_ban_add *self); long discord_guild_ban_add_from_json(const char buf[], size_t size, struct discord_guild_ban_add *self); + + + +long discord_guild_ban_remove_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_ban_remove *self); long discord_guild_ban_remove_from_json(const char buf[], size_t size, struct discord_guild_ban_remove *self); + + + +long discord_guild_emojis_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_emojis_update *self); long discord_guild_emojis_update_from_json(const char buf[], size_t size, struct discord_guild_emojis_update *self); + + + +long discord_guild_stickers_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_stickers_update *self); long discord_guild_stickers_update_from_json(const char buf[], size_t size, struct discord_guild_stickers_update *self); + + + +long discord_guild_integrations_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_integrations_update *self); long discord_guild_integrations_update_from_json(const char buf[], size_t size, struct discord_guild_integrations_update *self); + + +long discord_guild_member_remove_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_member_remove *self); long discord_guild_member_remove_from_json(const char buf[], size_t size, struct discord_guild_member_remove *self); + + + +long discord_guild_member_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_member_update *self); long discord_guild_member_update_from_json(const char buf[], size_t size, struct discord_guild_member_update *self); + + + + + + + + + + + + +long discord_guild_members_chunk_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_members_chunk *self); long discord_guild_members_chunk_from_json(const char buf[], size_t size, struct discord_guild_members_chunk *self); + + + + + + + + +long discord_guild_role_create_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_role_create *self); long discord_guild_role_create_from_json(const char buf[], size_t size, struct discord_guild_role_create *self); + + + +long discord_guild_role_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_role_update *self); long discord_guild_role_update_from_json(const char buf[], size_t size, struct discord_guild_role_update *self); + + + +long discord_guild_role_delete_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_role_delete *self); long discord_guild_role_delete_from_json(const char buf[], size_t size, struct discord_guild_role_delete *self); + + + +long discord_guild_scheduled_event_user_add_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_scheduled_event_user_add *self); long discord_guild_scheduled_event_user_add_from_json(const char buf[], size_t size, struct discord_guild_scheduled_event_user_add *self); + + + + +long discord_guild_scheduled_event_user_remove_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_scheduled_event_user_remove *self); long discord_guild_scheduled_event_user_remove_from_json(const char buf[], size_t size, struct discord_guild_scheduled_event_user_remove *self); + + + + +long discord_integration_delete_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_integration_delete *self); long discord_integration_delete_from_json(const char buf[], size_t size, struct discord_integration_delete *self); + + + + +long discord_invite_create_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_invite_create *self); long discord_invite_create_from_json(const char buf[], size_t size, struct discord_invite_create *self); + + + + + + + + + + + + + +long discord_invite_delete_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_invite_delete *self); long discord_invite_delete_from_json(const char buf[], size_t size, struct discord_invite_delete *self); + + + + +long discord_message_delete_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_message_delete *self); long discord_message_delete_from_json(const char buf[], size_t size, struct discord_message_delete *self); + + + + +long discord_message_delete_bulk_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_message_delete_bulk *self); long discord_message_delete_bulk_from_json(const char buf[], size_t size, struct discord_message_delete_bulk *self); + + + + +long discord_message_reaction_add_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_message_reaction_add *self); long discord_message_reaction_add_from_json(const char buf[], size_t size, struct discord_message_reaction_add *self); + + + + + + + +long discord_message_reaction_remove_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_message_reaction_remove *self); long discord_message_reaction_remove_from_json(const char buf[], size_t size, struct discord_message_reaction_remove *self); + + + + + + +long discord_message_reaction_remove_all_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_message_reaction_remove_all *self); long discord_message_reaction_remove_all_from_json(const char buf[], size_t size, struct discord_message_reaction_remove_all *self); + + + + +long discord_message_reaction_remove_emoji_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_message_reaction_remove_emoji *self); long discord_message_reaction_remove_emoji_from_json(const char buf[], size_t size, struct discord_message_reaction_remove_emoji *self); + + + + + +long discord_typing_start_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_typing_start *self); long discord_typing_start_from_json(const char buf[], size_t size, struct discord_typing_start *self); + + + + + + +long discord_voice_server_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_voice_server_update *self); long discord_voice_server_update_from_json(const char buf[], size_t size, struct discord_voice_server_update *self); + + + + +long discord_webhooks_update_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_webhooks_update *self); long discord_webhooks_update_from_json(const char buf[], size_t size, struct discord_webhooks_update *self); + + + +long discord_session_start_limit_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_session_start_limit *self); long discord_session_start_limit_from_json(const char buf[], size_t size, struct discord_session_start_limit *self); + + + + + +long discord_auth_response_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_auth_response *self); long discord_auth_response_from_json(const char buf[], size_t size, struct discord_auth_response *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_role_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_role *self); long discord_role_from_json(const char buf[], size_t size, struct discord_role *self); + + + + + + + + + + + + + + + + + + +long discord_roles_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_roles *self); long discord_roles_from_json(const char buf[], size_t size, struct discord_roles *self); + + + + + + + + + + + +long discord_team_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_team *self); long discord_team_from_json(const char buf[], size_t size, struct discord_team *self); + + + + + + + + + + + + + + + + + + + + + + + + +long discord_application_command_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_application_command *self); long discord_application_command_from_json(const char buf[], size_t size, struct discord_application_command *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_application_commands_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_application_commands *self); long discord_application_commands_from_json(const char buf[], size_t size, struct discord_application_commands *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_guild_application_command_permissions_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_guild_application_command_permissions *self); long discord_guild_application_command_permissions_from_json(const char buf[], size_t size, struct discord_guild_application_command_permissions *self); + + +long discord_application_command_permission_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_application_command_permission *self); long discord_application_command_permission_from_json(const char buf[], size_t size, struct discord_application_command_permission *self); + + + + +long discord_application_command_permissions_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_application_command_permissions *self); long discord_application_command_permissions_from_json(const char buf[], size_t size, struct discord_application_command_permissions *self); + + +long discord_create_global_application_command_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_global_application_command *self); long discord_create_global_application_command_from_json(const char buf[], size_t size, struct discord_create_global_application_command *self); + + + + + + + + + + + + + + +long discord_edit_global_application_command_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_edit_global_application_command *self); long discord_edit_global_application_command_from_json(const char buf[], size_t size, struct discord_edit_global_application_command *self); + + + + + + + + + + + +long discord_create_guild_application_command_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_guild_application_command *self); long discord_create_guild_application_command_from_json(const char buf[], size_t size, struct discord_create_guild_application_command *self); + + + + + + + + + + + + + + +long discord_edit_guild_application_command_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_edit_guild_application_command *self); long discord_edit_guild_application_command_from_json(const char buf[], size_t size, struct discord_edit_guild_application_command *self); + + + + + + + + + + +long discord_bulk_overwrite_guild_application_commands_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_bulk_overwrite_guild_application_commands *self); long discord_bulk_overwrite_guild_application_commands_from_json(const char buf[], size_t size, struct discord_bulk_overwrite_guild_application_commands *self); + + + + + + + + + + + + + + + + +long discord_component_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_component *self); long discord_component_from_json(const char buf[], size_t size, struct discord_component *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_components_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_components *self); long discord_components_from_json(const char buf[], size_t size, struct discord_components *self); + + + + + + + + + + + +long discord_select_options_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_select_options *self); long discord_select_options_from_json(const char buf[], size_t size, struct discord_select_options *self); + + + + + + + + + + + + + + +long discord_component_items_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_component_items *self); long discord_component_items_from_json(const char buf[], size_t size, struct discord_component_items *self); + + +long discord_interaction_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_interaction *self); long discord_interaction_from_json(const char buf[], size_t size, struct discord_interaction *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_interaction_response_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_interaction_response *self); long discord_interaction_response_from_json(const char buf[], size_t size, struct discord_interaction_response *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +long discord_edit_original_interaction_response_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_edit_original_interaction_response *self); long discord_edit_original_interaction_response_from_json(const char buf[], size_t size, struct discord_edit_original_interaction_response *self); + + + + + + + + + + + + + + +long discord_create_followup_message_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_create_followup_message *self); long discord_create_followup_message_from_json(const char buf[], size_t size, struct discord_create_followup_message *self); + + + + + + + + + + + + + + + + + + +long discord_edit_followup_message_from_jsmnf(const jsmnf_pair *root, const char *js, struct discord_edit_followup_message *self); long discord_edit_followup_message_from_json(const char buf[], size_t size, struct discord_edit_followup_message *self); + + + + + + + + + + + + + + +jsonbcode strings_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct strings *self); CCORDcode strings_to_json(char *buf[], size_t *size, const struct strings *self); + + +jsonbcode json_values_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct json_values *self); CCORDcode json_values_to_json(char *buf[], size_t *size, const struct json_values *self); + + +jsonbcode snowflakes_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct snowflakes *self); CCORDcode snowflakes_to_json(char *buf[], size_t *size, const struct snowflakes *self); + + +jsonbcode bitmasks_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct bitmasks *self); CCORDcode bitmasks_to_json(char *buf[], size_t *size, const struct bitmasks *self); + + +jsonbcode integers_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct integers *self); CCORDcode integers_to_json(char *buf[], size_t *size, const struct integers *self); + + + + + + + + + + +jsonbcode discord_application_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_application *self); CCORDcode discord_application_to_json(char *buf[], size_t *size, const struct discord_application *self); + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_install_params_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_install_params *self); CCORDcode discord_install_params_to_json(char *buf[], size_t *size, const struct discord_install_params *self); + + + +jsonbcode discord_audit_log_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_audit_log *self); CCORDcode discord_audit_log_to_json(char *buf[], size_t *size, const struct discord_audit_log *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_create_auto_moderation_rule_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_auto_moderation_rule *self); CCORDcode discord_create_auto_moderation_rule_to_json(char *buf[], size_t *size, const struct discord_create_auto_moderation_rule *self); + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_modify_auto_moderation_rule_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_auto_moderation_rule *self); CCORDcode discord_modify_auto_moderation_rule_to_json(char *buf[], size_t *size, const struct discord_modify_auto_moderation_rule *self); + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_invite_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_invite *self); CCORDcode discord_invite_to_json(char *buf[], size_t *size, const struct discord_invite *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_invites_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_invites *self); CCORDcode discord_invites_to_json(char *buf[], size_t *size, const struct discord_invites *self); + + + + + + + + + + + + + + + + + + + +jsonbcode discord_get_invite_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_get_invite *self); CCORDcode discord_get_invite_to_json(char *buf[], size_t *size, const struct discord_get_invite *self); + + + + + + + + + +jsonbcode discord_channel_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_channel *self); CCORDcode discord_channel_to_json(char *buf[], size_t *size, const struct discord_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_channels_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_channels *self); CCORDcode discord_channels_to_json(char *buf[], size_t *size, const struct discord_channels *self); + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_message_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_message *self); CCORDcode discord_message_to_json(char *buf[], size_t *size, const struct discord_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_messages_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_messages *self); CCORDcode discord_messages_to_json(char *buf[], size_t *size, const struct discord_messages *self); + + +jsonbcode discord_followed_channel_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_followed_channel *self); CCORDcode discord_followed_channel_to_json(char *buf[], size_t *size, const struct discord_followed_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_thread_member_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_thread_member *self); CCORDcode discord_thread_member_to_json(char *buf[], size_t *size, const struct discord_thread_member *self); + + + + + + + +jsonbcode discord_thread_members_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_thread_members *self); CCORDcode discord_thread_members_to_json(char *buf[], size_t *size, const struct discord_thread_members *self); + + + + + + + + + + + + + +jsonbcode discord_thread_tags_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_thread_tags *self); CCORDcode discord_thread_tags_to_json(char *buf[], size_t *size, const struct discord_thread_tags *self); + + +jsonbcode discord_embed_thumbnail_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed_thumbnail *self); CCORDcode discord_embed_thumbnail_to_json(char *buf[], size_t *size, const struct discord_embed_thumbnail *self); + + + + + + + + + + + +jsonbcode discord_embed_video_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed_video *self); CCORDcode discord_embed_video_to_json(char *buf[], size_t *size, const struct discord_embed_video *self); + + + + + + + + + + + + + +jsonbcode discord_embed_image_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed_image *self); CCORDcode discord_embed_image_to_json(char *buf[], size_t *size, const struct discord_embed_image *self); + + + + + + + + + + + +jsonbcode discord_embed_provider_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed_provider *self); CCORDcode discord_embed_provider_to_json(char *buf[], size_t *size, const struct discord_embed_provider *self); + + + + + + + +jsonbcode discord_embed_author_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed_author *self); CCORDcode discord_embed_author_to_json(char *buf[], size_t *size, const struct discord_embed_author *self); + + + + + + + + + + + +jsonbcode discord_embed_footer_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed_footer *self); CCORDcode discord_embed_footer_to_json(char *buf[], size_t *size, const struct discord_embed_footer *self); + + + + + + + + +jsonbcode discord_embed_field_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed_field *self); CCORDcode discord_embed_field_to_json(char *buf[], size_t *size, const struct discord_embed_field *self); + + + + +jsonbcode discord_embed_fields_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed_fields *self); CCORDcode discord_embed_fields_to_json(char *buf[], size_t *size, const struct discord_embed_fields *self); + + +jsonbcode discord_embed_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embed *self); CCORDcode discord_embed_to_json(char *buf[], size_t *size, const struct discord_embed *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_embeds_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_embeds *self); CCORDcode discord_embeds_to_json(char *buf[], size_t *size, const struct discord_embeds *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_attachments_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_attachments *self); CCORDcode discord_attachments_to_json(char *buf[], size_t *size, const struct discord_attachments *self); + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_thread_response_body_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_thread_response_body *self); CCORDcode discord_thread_response_body_to_json(char *buf[], size_t *size, const struct discord_thread_response_body *self); + + + + +jsonbcode discord_modify_channel_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_channel *self); CCORDcode discord_modify_channel_to_json(char *buf[], size_t *size, const struct discord_modify_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_create_message_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_message *self); CCORDcode discord_create_message_to_json(char *buf[], size_t *size, const struct discord_create_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_edit_message_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_edit_message *self); CCORDcode discord_edit_message_to_json(char *buf[], size_t *size, const struct discord_edit_message *self); + + + + + + + + + + + + + + + +jsonbcode discord_bulk_delete_messages_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_bulk_delete_messages *self); CCORDcode discord_bulk_delete_messages_to_json(char *buf[], size_t *size, const struct discord_bulk_delete_messages *self); + + +jsonbcode discord_edit_channel_permissions_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_edit_channel_permissions *self); CCORDcode discord_edit_channel_permissions_to_json(char *buf[], size_t *size, const struct discord_edit_channel_permissions *self); + + + + + + + + +jsonbcode discord_create_channel_invite_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_channel_invite *self); CCORDcode discord_create_channel_invite_to_json(char *buf[], size_t *size, const struct discord_create_channel_invite *self); + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_follow_news_channel_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_follow_news_channel *self); CCORDcode discord_follow_news_channel_to_json(char *buf[], size_t *size, const struct discord_follow_news_channel *self); + + + + +jsonbcode discord_group_dm_add_recipient_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_group_dm_add_recipient *self); CCORDcode discord_group_dm_add_recipient_to_json(char *buf[], size_t *size, const struct discord_group_dm_add_recipient *self); + + + +jsonbcode discord_start_thread_with_message_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_start_thread_with_message *self); CCORDcode discord_start_thread_with_message_to_json(char *buf[], size_t *size, const struct discord_start_thread_with_message *self); + + + + + + + + +jsonbcode discord_start_thread_without_message_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_start_thread_without_message *self); CCORDcode discord_start_thread_without_message_to_json(char *buf[], size_t *size, const struct discord_start_thread_without_message *self); + + + + + + + + + + +jsonbcode discord_list_active_threads_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_list_active_threads *self); CCORDcode discord_list_active_threads_to_json(char *buf[], size_t *size, const struct discord_list_active_threads *self); + + + + + + + + +jsonbcode discord_emoji_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_emoji *self); CCORDcode discord_emoji_to_json(char *buf[], size_t *size, const struct discord_emoji *self); + + + + + + + + + + + + + +jsonbcode discord_emojis_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_emojis *self); CCORDcode discord_emojis_to_json(char *buf[], size_t *size, const struct discord_emojis *self); + + +jsonbcode discord_create_guild_emoji_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild_emoji *self); CCORDcode discord_create_guild_emoji_to_json(char *buf[], size_t *size, const struct discord_create_guild_emoji *self); + + + + + + +jsonbcode discord_modify_guild_emoji_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_emoji *self); CCORDcode discord_modify_guild_emoji_to_json(char *buf[], size_t *size, const struct discord_modify_guild_emoji *self); + + + + + + + + + + +jsonbcode discord_guild_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild *self); CCORDcode discord_guild_to_json(char *buf[], size_t *size, const struct discord_guild *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_guilds_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guilds *self); CCORDcode discord_guilds_to_json(char *buf[], size_t *size, const struct discord_guilds *self); + + +jsonbcode discord_guild_preview_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_preview *self); CCORDcode discord_guild_preview_to_json(char *buf[], size_t *size, const struct discord_guild_preview *self); + + + + + + + + + + + + +jsonbcode discord_guild_widget_settings_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_widget_settings *self); CCORDcode discord_guild_widget_settings_to_json(char *buf[], size_t *size, const struct discord_guild_widget_settings *self); + + + +jsonbcode discord_guild_widget_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_widget *self); CCORDcode discord_guild_widget_to_json(char *buf[], size_t *size, const struct discord_guild_widget *self); + + + + + + + +jsonbcode discord_guild_member_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_member *self); CCORDcode discord_guild_member_to_json(char *buf[], size_t *size, const struct discord_guild_member *self); + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_guild_members_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_members *self); CCORDcode discord_guild_members_to_json(char *buf[], size_t *size, const struct discord_guild_members *self); + + +jsonbcode discord_integration_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_integration *self); CCORDcode discord_integration_to_json(char *buf[], size_t *size, const struct discord_integration *self); + + + + + + + + + + + + + + + + + +jsonbcode discord_integrations_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_integrations *self); CCORDcode discord_integrations_to_json(char *buf[], size_t *size, const struct discord_integrations *self); + + + + + + + + + + + + + + + + +jsonbcode discord_ban_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_ban *self); CCORDcode discord_ban_to_json(char *buf[], size_t *size, const struct discord_ban *self); + + + +jsonbcode discord_bans_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_bans *self); CCORDcode discord_bans_to_json(char *buf[], size_t *size, const struct discord_bans *self); + + +jsonbcode discord_welcome_screen_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_welcome_screen *self); CCORDcode discord_welcome_screen_to_json(char *buf[], size_t *size, const struct discord_welcome_screen *self); + + + + + + + + + + + + + + +jsonbcode discord_prune_count_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_prune_count *self); CCORDcode discord_prune_count_to_json(char *buf[], size_t *size, const struct discord_prune_count *self); + + +jsonbcode discord_create_guild_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild *self); CCORDcode discord_create_guild_to_json(char *buf[], size_t *size, const struct discord_create_guild *self); + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_modify_guild_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild *self); CCORDcode discord_modify_guild_to_json(char *buf[], size_t *size, const struct discord_modify_guild *self); + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_create_guild_channel_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild_channel *self); CCORDcode discord_create_guild_channel_to_json(char *buf[], size_t *size, const struct discord_create_guild_channel *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_modify_guild_channel_positions_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_channel_positions *self); CCORDcode discord_modify_guild_channel_positions_to_json(char *buf[], size_t *size, const struct discord_modify_guild_channel_positions *self); + + +jsonbcode discord_list_active_guild_threads_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_list_active_guild_threads *self); CCORDcode discord_list_active_guild_threads_to_json(char *buf[], size_t *size, const struct discord_list_active_guild_threads *self); + + + +jsonbcode discord_add_guild_member_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_add_guild_member *self); CCORDcode discord_add_guild_member_to_json(char *buf[], size_t *size, const struct discord_add_guild_member *self); + + + + + + +jsonbcode discord_modify_guild_member_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_member *self); CCORDcode discord_modify_guild_member_to_json(char *buf[], size_t *size, const struct discord_modify_guild_member *self); + + + + + + + + + + + + + + + + + + + +jsonbcode discord_modify_current_member_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_current_member *self); CCORDcode discord_modify_current_member_to_json(char *buf[], size_t *size, const struct discord_modify_current_member *self); + + + + +jsonbcode discord_modify_current_user_nick_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_current_user_nick *self); CCORDcode discord_modify_current_user_nick_to_json(char *buf[], size_t *size, const struct discord_modify_current_user_nick *self); + + + + +jsonbcode discord_create_guild_ban_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild_ban *self); CCORDcode discord_create_guild_ban_to_json(char *buf[], size_t *size, const struct discord_create_guild_ban *self); + + + + +jsonbcode discord_create_guild_role_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild_role *self); CCORDcode discord_create_guild_role_to_json(char *buf[], size_t *size, const struct discord_create_guild_role *self); + + + + + + + + + + + + + + +jsonbcode discord_modify_guild_role_positions_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_role_positions *self); CCORDcode discord_modify_guild_role_positions_to_json(char *buf[], size_t *size, const struct discord_modify_guild_role_positions *self); + + +jsonbcode discord_modify_guild_role_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_role *self); CCORDcode discord_modify_guild_role_to_json(char *buf[], size_t *size, const struct discord_modify_guild_role *self); + + + + + + + + +jsonbcode discord_begin_guild_prune_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_begin_guild_prune *self); CCORDcode discord_begin_guild_prune_to_json(char *buf[], size_t *size, const struct discord_begin_guild_prune *self); + + + + + + +jsonbcode discord_modify_guild_welcome_screen_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_welcome_screen *self); CCORDcode discord_modify_guild_welcome_screen_to_json(char *buf[], size_t *size, const struct discord_modify_guild_welcome_screen *self); + + + + + + +jsonbcode discord_modify_current_user_voice_state_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_current_user_voice_state *self); CCORDcode discord_modify_current_user_voice_state_to_json(char *buf[], size_t *size, const struct discord_modify_current_user_voice_state *self); + + + + + + +jsonbcode discord_modify_user_voice_state_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_user_voice_state *self); CCORDcode discord_modify_user_voice_state_to_json(char *buf[], size_t *size, const struct discord_modify_user_voice_state *self); + + + +jsonbcode discord_guild_scheduled_event_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_scheduled_event *self); CCORDcode discord_guild_scheduled_event_to_json(char *buf[], size_t *size, const struct discord_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_guild_scheduled_events_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_scheduled_events *self); CCORDcode discord_guild_scheduled_events_to_json(char *buf[], size_t *size, const struct discord_guild_scheduled_events *self); + + + + + + + + + + + +jsonbcode discord_guild_scheduled_event_users_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_scheduled_event_users *self); CCORDcode discord_guild_scheduled_event_users_to_json(char *buf[], size_t *size, const struct discord_guild_scheduled_event_users *self); + + +jsonbcode discord_create_guild_scheduled_event_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild_scheduled_event *self); CCORDcode discord_create_guild_scheduled_event_to_json(char *buf[], size_t *size, const struct discord_create_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_modify_guild_scheduled_event_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_scheduled_event *self); CCORDcode discord_modify_guild_scheduled_event_to_json(char *buf[], size_t *size, const struct discord_modify_guild_scheduled_event *self); + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_guild_template_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_template *self); CCORDcode discord_guild_template_to_json(char *buf[], size_t *size, const struct discord_guild_template *self); + + + + + + + + + + + + +jsonbcode discord_guild_templates_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_templates *self); CCORDcode discord_guild_templates_to_json(char *buf[], size_t *size, const struct discord_guild_templates *self); + + +jsonbcode discord_create_guild_from_guild_template_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild_from_guild_template *self); CCORDcode discord_create_guild_from_guild_template_to_json(char *buf[], size_t *size, const struct discord_create_guild_from_guild_template *self); + + + + + +jsonbcode discord_create_guild_template_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild_template *self); CCORDcode discord_create_guild_template_to_json(char *buf[], size_t *size, const struct discord_create_guild_template *self); + + + + + +jsonbcode discord_modify_guild_template_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_template *self); CCORDcode discord_modify_guild_template_to_json(char *buf[], size_t *size, const struct discord_modify_guild_template *self); + + + + + + + +jsonbcode discord_stage_instance_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_stage_instance *self); CCORDcode discord_stage_instance_to_json(char *buf[], size_t *size, const struct discord_stage_instance *self); + + + + + + + + + + + + +jsonbcode discord_create_stage_instance_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_stage_instance *self); CCORDcode discord_create_stage_instance_to_json(char *buf[], size_t *size, const struct discord_create_stage_instance *self); + + + + + + +jsonbcode discord_modify_stage_instance_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_stage_instance *self); CCORDcode discord_modify_stage_instance_to_json(char *buf[], size_t *size, const struct discord_modify_stage_instance *self); + + + + + +jsonbcode discord_sticker_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_sticker *self); CCORDcode discord_sticker_to_json(char *buf[], size_t *size, const struct discord_sticker *self); + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_stickers_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_stickers *self); CCORDcode discord_stickers_to_json(char *buf[], size_t *size, const struct discord_stickers *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_sticker_packs_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_sticker_packs *self); CCORDcode discord_sticker_packs_to_json(char *buf[], size_t *size, const struct discord_sticker_packs *self); + + +jsonbcode discord_modify_guild_sticker_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_guild_sticker *self); CCORDcode discord_modify_guild_sticker_to_json(char *buf[], size_t *size, const struct discord_modify_guild_sticker *self); + + + + + + + + + + + + + + + + + + + +jsonbcode discord_user_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_user *self); CCORDcode discord_user_to_json(char *buf[], size_t *size, const struct discord_user *self); + + + + + + + + + + + + + + + + +jsonbcode discord_users_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_users *self); CCORDcode discord_users_to_json(char *buf[], size_t *size, const struct discord_users *self); + + + + + + + + + + + + + + + +jsonbcode discord_connections_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_connections *self); CCORDcode discord_connections_to_json(char *buf[], size_t *size, const struct discord_connections *self); + + +jsonbcode discord_modify_current_user_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_current_user *self); CCORDcode discord_modify_current_user_to_json(char *buf[], size_t *size, const struct discord_modify_current_user *self); + + + + + + + +jsonbcode discord_create_dm_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_dm *self); CCORDcode discord_create_dm_to_json(char *buf[], size_t *size, const struct discord_create_dm *self); + + + + +jsonbcode discord_create_group_dm_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_group_dm *self); CCORDcode discord_create_group_dm_to_json(char *buf[], size_t *size, const struct discord_create_group_dm *self); + + + + + + + +jsonbcode discord_voice_state_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_voice_state *self); CCORDcode discord_voice_state_to_json(char *buf[], size_t *size, const struct discord_voice_state *self); + + + + + + + + + + + + + + + + +jsonbcode discord_voice_states_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_voice_states *self); CCORDcode discord_voice_states_to_json(char *buf[], size_t *size, const struct discord_voice_states *self); + + + + + + + + + +jsonbcode discord_voice_regions_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_voice_regions *self); CCORDcode discord_voice_regions_to_json(char *buf[], size_t *size, const struct discord_voice_regions *self); + + +jsonbcode discord_webhook_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_webhook *self); CCORDcode discord_webhook_to_json(char *buf[], size_t *size, const struct discord_webhook *self); + + + + + + + + + + + + + + + + + +jsonbcode discord_webhooks_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_webhooks *self); CCORDcode discord_webhooks_to_json(char *buf[], size_t *size, const struct discord_webhooks *self); + + +jsonbcode discord_create_webhook_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_webhook *self); CCORDcode discord_create_webhook_to_json(char *buf[], size_t *size, const struct discord_create_webhook *self); + + + + + +jsonbcode discord_modify_webhook_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_webhook *self); CCORDcode discord_modify_webhook_to_json(char *buf[], size_t *size, const struct discord_modify_webhook *self); + + + + + + +jsonbcode discord_modify_webhook_with_token_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_modify_webhook_with_token *self); CCORDcode discord_modify_webhook_with_token_to_json(char *buf[], size_t *size, const struct discord_modify_webhook_with_token *self); + + + + + +jsonbcode discord_execute_webhook_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_execute_webhook *self); CCORDcode discord_execute_webhook_to_json(char *buf[], size_t *size, const struct discord_execute_webhook *self); + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_edit_webhook_message_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_edit_webhook_message *self); CCORDcode discord_edit_webhook_message_to_json(char *buf[], size_t *size, const struct discord_edit_webhook_message *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_presence_update_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_presence_update *self); CCORDcode discord_presence_update_to_json(char *buf[], size_t *size, const struct discord_presence_update *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_identify_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_identify *self); CCORDcode discord_identify_to_json(char *buf[], size_t *size, const struct discord_identify *self); + + + + + + + + + + + + + + + + + +jsonbcode discord_resume_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_resume *self); CCORDcode discord_resume_to_json(char *buf[], size_t *size, const struct discord_resume *self); + + + + +jsonbcode discord_request_guild_members_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_request_guild_members *self); CCORDcode discord_request_guild_members_to_json(char *buf[], size_t *size, const struct discord_request_guild_members *self); + + + + + + + + + + + +jsonbcode discord_update_voice_state_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_update_voice_state *self); CCORDcode discord_update_voice_state_to_json(char *buf[], size_t *size, const struct discord_update_voice_state *self); + + + + + +jsonbcode discord_auth_response_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_auth_response *self); CCORDcode discord_auth_response_to_json(char *buf[], size_t *size, const struct discord_auth_response *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_role_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_role *self); CCORDcode discord_role_to_json(char *buf[], size_t *size, const struct discord_role *self); + + + + + + + + + + + + + + + + + + +jsonbcode discord_roles_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_roles *self); CCORDcode discord_roles_to_json(char *buf[], size_t *size, const struct discord_roles *self); + + + + + + + + + + + +jsonbcode discord_team_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_team *self); CCORDcode discord_team_to_json(char *buf[], size_t *size, const struct discord_team *self); + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_application_command_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_application_command *self); CCORDcode discord_application_command_to_json(char *buf[], size_t *size, const struct discord_application_command *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_application_commands_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_application_commands *self); CCORDcode discord_application_commands_to_json(char *buf[], size_t *size, const struct discord_application_commands *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_guild_application_command_permissions_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_guild_application_command_permissions *self); CCORDcode discord_guild_application_command_permissions_to_json(char *buf[], size_t *size, const struct discord_guild_application_command_permissions *self); + + +jsonbcode discord_application_command_permission_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_application_command_permission *self); CCORDcode discord_application_command_permission_to_json(char *buf[], size_t *size, const struct discord_application_command_permission *self); + + + + +jsonbcode discord_application_command_permissions_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_application_command_permissions *self); CCORDcode discord_application_command_permissions_to_json(char *buf[], size_t *size, const struct discord_application_command_permissions *self); + + +jsonbcode discord_create_global_application_command_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_global_application_command *self); CCORDcode discord_create_global_application_command_to_json(char *buf[], size_t *size, const struct discord_create_global_application_command *self); + + + + + + + + + + + + + + +jsonbcode discord_edit_global_application_command_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_edit_global_application_command *self); CCORDcode discord_edit_global_application_command_to_json(char *buf[], size_t *size, const struct discord_edit_global_application_command *self); + + + + + + + + + + + +jsonbcode discord_create_guild_application_command_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_guild_application_command *self); CCORDcode discord_create_guild_application_command_to_json(char *buf[], size_t *size, const struct discord_create_guild_application_command *self); + + + + + + + + + + + + + + +jsonbcode discord_edit_guild_application_command_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_edit_guild_application_command *self); CCORDcode discord_edit_guild_application_command_to_json(char *buf[], size_t *size, const struct discord_edit_guild_application_command *self); + + + + + + + + + + +jsonbcode discord_bulk_overwrite_guild_application_commands_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_bulk_overwrite_guild_application_commands *self); CCORDcode discord_bulk_overwrite_guild_application_commands_to_json(char *buf[], size_t *size, const struct discord_bulk_overwrite_guild_application_commands *self); + + + + + + + + + + + + + + + + +jsonbcode discord_component_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_component *self); CCORDcode discord_component_to_json(char *buf[], size_t *size, const struct discord_component *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_components_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_components *self); CCORDcode discord_components_to_json(char *buf[], size_t *size, const struct discord_components *self); + + + + + + + + + + + +jsonbcode discord_select_options_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_select_options *self); CCORDcode discord_select_options_to_json(char *buf[], size_t *size, const struct discord_select_options *self); + + + + + + + + + + + + + + +jsonbcode discord_component_items_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_component_items *self); CCORDcode discord_component_items_to_json(char *buf[], size_t *size, const struct discord_component_items *self); + + +jsonbcode discord_interaction_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_interaction *self); CCORDcode discord_interaction_to_json(char *buf[], size_t *size, const struct discord_interaction *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_interaction_response_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_interaction_response *self); CCORDcode discord_interaction_response_to_json(char *buf[], size_t *size, const struct discord_interaction_response *self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +jsonbcode discord_edit_original_interaction_response_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_edit_original_interaction_response *self); CCORDcode discord_edit_original_interaction_response_to_json(char *buf[], size_t *size, const struct discord_edit_original_interaction_response *self); + + + + + + + + + + + + + + +jsonbcode discord_create_followup_message_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_create_followup_message *self); CCORDcode discord_create_followup_message_to_json(char *buf[], size_t *size, const struct discord_create_followup_message *self); + + + + + + + + + + + + + + + + + + +jsonbcode discord_edit_followup_message_to_jsonb(jsonb *b, char *buf[], size_t *size, const struct discord_edit_followup_message *self); CCORDcode discord_edit_followup_message_to_json(char *buf[], size_t *size, const struct discord_edit_followup_message *self); + + + + + + + + + + + + + + +#endif /* DISCORD_CODECS_H */ diff --git a/include/concord/emoji.h b/include/concord/emoji.h new file mode 100644 index 0000000..e0184d6 --- /dev/null +++ b/include/concord/emoji.h @@ -0,0 +1,95 @@ +/** + * @file emoji.h + * @author Cogmasters + * @brief Emoji public functions and datatypes + */ + +#ifndef DISCORD_EMOJI_H +#define DISCORD_EMOJI_H + +/** @defgroup DiscordAPIEmoji Emoji + * @ingroup DiscordAPI + * @brief Emoji's public API supported by Concord + * @{ */ + +/** + * @brief Get emojis of a given guild + * + * @param client the client created with discord_from_token() + * @param guild_id guild to get emojis from + * @CCORD_ret_obj{ret,emojis} + * @CCORD_return + */ +CCORDcode discord_list_guild_emojis(struct discord *client, + u64snowflake guild_id, + struct discord_ret_emojis *ret); + +/** + * @brief Get a specific emoji from a guild + * + * @param client the client created with discord_from_token() + * @param guild_id guild the emoji belongs to + * @param emoji_id the emoji to be fetched + * @CCORD_ret_obj{ret,emoji} + * @CCORD_return + */ +CCORDcode discord_get_guild_emoji(struct discord *client, + u64snowflake guild_id, + u64snowflake emoji_id, + struct discord_ret_emoji *ret); + +/** + * @brief Create a new emoji for the guild + * @note Fires a `Guild Emojis Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id guild to add the new emoji to + * @param params request parameters + * @CCORD_ret_obj{ret,emoji} + * @CCORD_return + */ +CCORDcode discord_create_guild_emoji(struct discord *client, + u64snowflake guild_id, + struct discord_create_guild_emoji *params, + struct discord_ret_emoji *ret); + +/** + * @brief Modify the given emoji + * @note Fires a `Guild Emojis Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id guild the emoji belongs to + * @param emoji_id the emoji to be modified + * @param params request parameters + * @CCORD_ret_obj{ret,emoji} + * @CCORD_return + */ +CCORDcode discord_modify_guild_emoji(struct discord *client, + u64snowflake guild_id, + u64snowflake emoji_id, + struct discord_modify_guild_emoji *params, + struct discord_ret_emoji *ret); + +/** + * @brief Deletes the given emoji + * @note Fires a `Guild Emojis Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id guild the emoji belongs to + * @param emoji_id the emoji to be deleted + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_guild_emoji(struct discord *client, + u64snowflake guild_id, + u64snowflake emoji_id, + struct discord_delete_guild_emoji *params, + struct discord_ret *ret); + +/** @example emoji.c + * Demonstrates a couple use cases of the Emoji API */ + +/** @} DiscordAPIEmoji */ + +#endif /* DISCORD_EMOJI_H */ diff --git a/include/concord/gateway.h b/include/concord/gateway.h new file mode 100644 index 0000000..e59a4d4 --- /dev/null +++ b/include/concord/gateway.h @@ -0,0 +1,76 @@ +/** + * @file gateway.h + * @author Cogmasters + * @brief Gateway public functions and datatypes + */ + +#ifndef DISCORD_GATEWAY_H +#define DISCORD_GATEWAY_H + +/** @defgroup DiscordAPIGateway Gateway + * @ingroup DiscordAPI + * @brief Gateway's public API supported by Concord + * @{ */ + +/** + * @brief Get a single valid WSS URL, which the client can use for connecting + * @note This route should be cached, and only call the function again if + * unable to properly establishing a connection with the cached version + * @warning This function blocks the running thread + * + * @param client the client created with discord_from_token() + * @param ret if successful, a @ref ccord_szbuf containing the JSON response + * @param ret a sized buffer containing the response JSON + * @CCORD_return + */ +CCORDcode discord_get_gateway(struct discord *client, struct ccord_szbuf *ret); + +/** + * @brief Get a single valid WSS URL, and additional metadata that can help + * during the operation of large bots. + * @note This route should not be cached for extended periods of time as the + * value is not guaranteed to be the same per-call, and changes as the + * bot joins/leaves guilds + * @warning This function blocks the running thread + * + * @param client the client created with discord_from_token() + * @param ret if successful, a @ref ccord_szbuf containing the JSON response + * @param ret a sized buffer containing the response JSON + * @CCORD_return + */ +CCORDcode discord_get_gateway_bot(struct discord *client, + struct ccord_szbuf *ret); + +/** @defgroup DiscordAPIGatewayHelper Helper functions + * @brief Custom helper functions + * @{ */ + +/** + * @brief Disconnect a member from voice channel + * + * @param client the client created with discord_from_token() + * @param guild_id the guild the member belongs to + * @param user_id the user to be disconnected + * @param params request parameters + * @CCORD_ret_obj{ret,guild_member} + * @CCORD_return + */ +CCORDcode discord_disconnect_guild_member( + struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_modify_guild_member *params, + struct discord_ret_guild_member *ret); + +/** + * @brief Helper function to add presence activities + * @see discord_set_presence() + */ +void discord_presence_add_activity(struct discord_presence_update *presence, + struct discord_activity *activity); + +/** @} DiscordAPIGatewayHelper */ + +/** @} DiscordAPIGateway */ + +#endif /* DISCORD_GATEWAY_H */ diff --git a/include/concord/gencodecs-process.PRE.h b/include/concord/gencodecs-process.PRE.h new file mode 100644 index 0000000..62292ab --- /dev/null +++ b/include/concord/gencodecs-process.PRE.h @@ -0,0 +1,27 @@ +#ifndef GENCODECS_READ +#error "Missing GENCODECS_READ definition" +#else + +#define DATA (1 << 1) +#define JSON_DECODER (1 << 2) +#define JSON_ENCODER (1 << 3) +#define JSON (JSON_DECODER | JSON_ENCODER) + +#define GENCODECS_RECIPE DATA +#include "recipes/struct.h" +#undef GENCODECS_RECIPE + +#define GENCODECS_RECIPE JSON_DECODER +#include "recipes/json-decoder.h" +#undef GENCODECS_RECIPE + +#define GENCODECS_RECIPE JSON_ENCODER +#include "recipes/json-encoder.h" +#undef GENCODECS_RECIPE + +#undef DATA +#undef JSON_DECODER +#undef JSON_ENCODER +#undef JSON + +#endif /* GENCODECS_READ */ diff --git a/include/concord/gencodecs.h b/include/concord/gencodecs.h new file mode 100644 index 0000000..b992cb7 --- /dev/null +++ b/include/concord/gencodecs.h @@ -0,0 +1,74 @@ +#ifndef GENCODECS_H +#define GENCODECS_H + +/* Allow symbols usage without GENCODECS_ prefix */ +#ifndef GENCODECS_USE_PREFIX +# define PP_INCLUDE GENCODECS_PP_INCLUDE +# define PP_DEFINE GENCODECS_PP_DEFINE +# define PP GENCODECS_PP + +# define COND_WRITE GENCODECS_COND_WRITE +# define COND_END GENCODECS_COND_END + +# define PUB_STRUCT GENCODECS_PUB_STRUCT +# define STRUCT GENCODECS_STRUCT +# define FIELD_CUSTOM GENCODECS_FIELD_CUSTOM +# define FIELD_PRINTF GENCODECS_FIELD_PRINTF +# define FIELD GENCODECS_FIELD +# define FIELD_STRUCT_PTR GENCODECS_FIELD_STRUCT_PTR +# define FIELD_PTR GENCODECS_FIELD_PTR +# define FIELD_ENUM GENCODECS_FIELD_ENUM +# define STRUCT_END GENCODECS_STRUCT_END + +# define PUB_LIST GENCODECS_PUB_LIST +# define LIST GENCODECS_LIST +# define LISTTYPE GENCODECS_LISTTYPE +# define LISTTYPE_STRUCT GENCODECS_LISTTYPE_STRUCT +# define LISTTYPE_PTR GENCODECS_LISTTYPE_PTR +# define LIST_END GENCODECS_LIST_END + +# define ENUM GENCODECS_ENUM +# define ENUM_END GENCODECS_ENUM_END +# define ENUMERATOR GENCODECS_ENUMERATOR +# define ENUMERATOR_LAST GENCODECS_ENUMERATOR_LAST +# define ENUMERATOR_END GENCODECS_ENUMERATOR_END +#endif /* GENCODECS_USE_PREFIX */ + +#ifndef GENCODECS_HEADER +# ifdef GENCODECS_DATA +GENCODECS_PP_INCLUDE() +GENCODECS_PP_INCLUDE() +GENCODECS_PP_INCLUDE() +# ifdef GENCODECS_INIT +GENCODECS_PP_INCLUDE("carray.h") +# endif +# if defined(GENCODECS_JSON_DECODER) && defined(GENCODECS_FORWARD) +static char * +_gc_strndup(const char *src, size_t len) +{ + char *dest = malloc(len + 1); + memcpy(dest, src, len); + dest[len] = '\0'; + return dest; +} +# endif /* GENCODECS_JSON_DECODER && GENCODECS_FORWARD */ +# endif /* GENCODECS_DATA */ +#else +GENCODECS_PP_INCLUDE() +GENCODECS_PP_INCLUDE() +# ifdef GENCODECS_JSON_DECODER +GENCODECS_PP_DEFINE(JSMN_STRICT) +GENCODECS_PP_DEFINE(JSMN_HEADER) +GENCODECS_PP_INCLUDE("jsmn.h") +GENCODECS_PP_INCLUDE("jsmn-find.h") +# endif +# ifdef GENCODECS_JSON_ENCODER +GENCODECS_PP_DEFINE(JSONB_HEADER) +GENCODECS_PP_INCLUDE("json-build.h") +# endif +#endif /* GENCODECS_HEADER */ + +#define GENCODECS_PP(_description) +#define GENCODECS_PP_DEFINE(_description) + +#endif /* GENCODECS_H */ diff --git a/include/concord/guild.h b/include/concord/guild.h new file mode 100644 index 0000000..469d73a --- /dev/null +++ b/include/concord/guild.h @@ -0,0 +1,683 @@ +/** + * @file guild.h + * @author Cogmasters + * @brief Guild public functions and datatypes + */ + +#ifndef DISCORD_GUILD_H +#define DISCORD_GUILD_H + +/** @defgroup DiscordAPIGuild Guild + * @ingroup DiscordAPI + * @brief Guild's public API supported by Concord + * @{ */ + +/** + * @brief Create a new guild + * @note Fires a `Guild Create` event + * + * @param client the client created with discord_from_token() + * @param params request parameters + * @CCORD_ret_obj{ret,guild} + * @CCORD_return + */ +CCORDcode discord_create_guild(struct discord *client, + struct discord_create_guild *params, + struct discord_ret_guild *ret); + +/** + * @brief Get the guild with given id + * @todo missing query parameters + * @note If with_counts is set to true, this endpoint will also return + * approximate_member_count and approximate_presence_count for the + * guild + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to retrieve + * @CCORD_ret_obj{ret,guild} + * @CCORD_return + */ +CCORDcode discord_get_guild(struct discord *client, + u64snowflake guild_id, + struct discord_ret_guild *ret); + +/** + * @brief Get the preview for the given guild + * @note If the user is not in the guild, then the guild must be lurkable + * + * @param client the client created with discord_from_token() + * @param guild_id guild to get preview from + * @CCORD_ret_obj{ret,guild_preview} + * @CCORD_return + */ +CCORDcode discord_get_guild_preview(struct discord *client, + u64snowflake guild_id, + struct discord_ret_guild_preview *ret); + +/** + * @brief Modify a guild's settings + * @note Requires the MANAGE_GUILD permission + * @note Fires a `Guild Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to modify + * @param params request parameters + * @CCORD_ret_obj{ret,guild} + * @CCORD_return + */ +CCORDcode discord_modify_guild(struct discord *client, + u64snowflake guild_id, + struct discord_modify_guild *params, + struct discord_ret_guild *ret); + +/** + * @brief Delete a guild permanently, user must be owner + * @note Fires a `Guild Delete` event + * + * @param client the client created with discord_from_token() + * @param guild_id id of guild to delete + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_guild(struct discord *client, + u64snowflake guild_id, + struct discord_ret *ret); + +/** + * @brief Fetch channels from given guild. Does not include threads + * + * @param client the client created with discord_from_token() + * @param guild_id id of guild to fetch channels from + * @CCORD_ret_obj{ret,channels} + * @CCORD_return + */ +CCORDcode discord_get_guild_channels(struct discord *client, + u64snowflake guild_id, + struct discord_ret_channels *ret); + +/** + * @brief Create a new guild channel + * @note Requires the MANAGE_CHANNELS permission + * @note If setting permission overwrites, only permissions your + * bot has in the guild can be allowed/denied. Setting MANAGE_ROLES + * permission in channels is only possible for guild administrators + * @note Fires a `Channel Create` event + * + * @param client the client created with discord_from_token() + * @param guild_id id of the guild to create a channel at + * @param params request parameters + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_create_guild_channel( + struct discord *client, + u64snowflake guild_id, + struct discord_create_guild_channel *params, + struct discord_ret_channel *ret); + +/** + * @brief Modify guild channel positions + * @note Requires MANAGE_CHANNELS permission + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to change the positions of the + * channels in + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_modify_guild_channel_positions( + struct discord *client, + u64snowflake guild_id, + struct discord_modify_guild_channel_positions *params, + struct discord_ret *ret); + +/** + * @brief Get guild member of a guild from given user id + * + * @param client the client created with discord_from_token() + * @param guild_id guild the member belongs to + * @param user_id unique user id of member + * @CCORD_ret_obj{ret,guild_member} + * @CCORD_return + */ +CCORDcode discord_get_guild_member(struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_ret_guild_member *ret); + +/** + * @brief Get guild members of a guild + * + * @param client the client created with discord_from_token() + * @param guild_id guild the members belongs to + * @param request parameters + * @CCORD_ret_obj{ret,guild_members} + * @CCORD_return + */ +CCORDcode discord_list_guild_members(struct discord *client, + u64snowflake guild_id, + struct discord_list_guild_members *params, + struct discord_ret_guild_members *ret); + +/** + * @brief Get guild members whose username or nickname starts with a provided + * string + * + * @param client the client created with discord_from_token() + * @param guild_id guild the members belongs to + * @param request parameters + * @CCORD_ret_obj{ret,guild_members} + * @CCORD_return + */ +CCORDcode discord_search_guild_members( + struct discord *client, + u64snowflake guild_id, + struct discord_search_guild_members *params, + struct discord_ret_guild_members *ret); + +/** + * @brief Adds a user to the guild + * @note Requires valid oauth2 access token for the user with `guilds.join` + * scope + * @note Fires a `Guild Member Add` event + * @note The bot must be a member of the guild with CREATE_INSTANT_INVITE + * permission + * + * @param client the client created with discord_from_token() + * @param guild_id guild to add the member to + * @param user_id the user to be added + * @param request parameters + * @CCORD_ret_obj{ret,guild_member} + * @CCORD_return + */ +CCORDcode discord_add_guild_member(struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_add_guild_member *params, + struct discord_ret_guild_member *ret); + +/** + * @brief Modify retibutes of a guild member + * @note Fires a `Guild Member Update` event + * @see discord_disconnect_guild_member() + * + * @param client the client created with discord_from_token() + * @param guild_id guild the member belongs to + * @param user_id the user id of member + * @param request parameters + * @CCORD_ret_obj{ret,guild_member} + * @CCORD_return + */ +CCORDcode discord_modify_guild_member( + struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_modify_guild_member *params, + struct discord_ret_guild_member *ret); + +/** + * @brief Modifies the current member in the guild + * @note Fires a `Guild Member Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild where the member exists + * @param params request parameters + * @CCORD_ret_obj{ret,guild_member} + * @CCORD_return + */ +CCORDcode discord_modify_current_member( + struct discord *client, + u64snowflake guild_id, + struct discord_modify_current_member *params, + struct discord_ret_guild_member *ret); + +/** + * @brief Adds a role to a guild member + * @note Fires a `Guild Member Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild where the member exists + * @param user_id the unique id of the user + * @param role_id the unique id of the role to be added + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_add_guild_member_role( + struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + u64snowflake role_id, + struct discord_add_guild_member_role *params, + struct discord_ret *ret); + +/** + * @brief Removes a role from a guild member + * @note Requires the MANAGE_ROLES permission + * @note Fires a `Guild Member Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild where the member exists + * @param user_id the unique id of the user + * @param role_id the unique id of the role to be removed + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_remove_guild_member_role( + struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + u64snowflake role_id, + struct discord_remove_guild_member_role *params, + struct discord_ret *ret); + +/** + * @brief Remove a member from a guild + * @note Requires the KICK_MEMBERS permission + * @note Fires a `Guild Member Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to remove the member from + * @param user_id the user to be removed + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_remove_guild_member( + struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_remove_guild_member *params, + struct discord_ret *ret); + +/** + * @brief Fetch banned users for given guild + * @note Requires the BAN_MEMBERS permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to get the list from + * @CCORD_ret_obj{ret,bans} + * @CCORD_return + */ +CCORDcode discord_get_guild_bans(struct discord *client, + u64snowflake guild_id, + struct discord_ret_bans *ret); + +/** + * @brief Fetch banned user from given guild + * @note Requires the BAN_MEMBERS permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to return the ban from + * @param user_id the user that is banned + * @CCORD_ret_obj{ret,ban} + * @CCORD_return + */ +CCORDcode discord_get_guild_ban(struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_ret_ban *ret); + +/** + * @brief Bans user from a given guild + * @note Requires the BAN_MEMBERS permission + * @note Fires a `Guild Ban Add` event + * + * @param client the client created with discord_from_token() + * @param guild_id guild the user belongs to + * @param user_id the user to be banned + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_create_guild_ban(struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_create_guild_ban *params, + struct discord_ret *ret); + +/** + * @brief Remove the ban for a user + * @note Requires the BAN_MEMBERS permission + * @note Fires a `Guild Ban Remove` event + * + * @param client the client created with discord_from_token() + * @param guild_id guild the user belonged to + * @param user_id the user to have its ban revoked + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_remove_guild_ban(struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_remove_guild_ban *params, + struct discord_ret *ret); + +/** + * @brief Get guild roles + * + * @param client the client created with discord_from_token() + * @param guild_id guild to get roles from + * @CCORD_ret_obj{ret,roles} + * @CCORD_return + */ +CCORDcode discord_get_guild_roles(struct discord *client, + u64snowflake guild_id, + struct discord_ret_roles *ret); + +/** + * @brief Create a new guild role + * @note Requires MANAGE_ROLES permission + * @note Fires a `Guild Role Create` event + * + * @param client the client created with discord_from_token() + * @param guild_id guild to add a role to + * @param params request parameters + * @CCORD_ret_obj{ret,role} + * @CCORD_return + */ +CCORDcode discord_create_guild_role(struct discord *client, + u64snowflake guild_id, + struct discord_create_guild_role *params, + struct discord_ret_role *ret); + +/** + * @brief Returns the number of members that would be removed in a prune + * operation + * @note Requires the KICK_MEMBERS permission + * @note By default will not remove users with roles. You can include specific + * roles in your prune by providing the `params.include_roles` value + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to be checked + * @param params request parameters + * @CCORD_ret_obj{ret,prune_count} + * @CCORD_return + */ +CCORDcode discord_get_guild_prune_count( + struct discord *client, + u64snowflake guild_id, + struct discord_get_guild_prune_count *params, + struct discord_ret_prune_count *ret); + +/** + * @brief Begin guild prune operation + * @note Discord recommends for larger servers to set "compute_prune_count" to + * false + * @note Requires the KICK_MEMBERS permission + * @note Fires multiple `Guild Member Remove` events + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to start the prune + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_begin_guild_prune(struct discord *client, + u64snowflake guild_id, + struct discord_begin_guild_prune *params, + struct discord_ret *ret); + +/** + * @brief Get voice regions (includes VIP servers when the guild is + * VIP-enabled) + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get voice regions from + * @CCORD_ret_obj{ret,voice_regions} + * @CCORD_return + */ +CCORDcode discord_get_guild_voice_regions( + struct discord *client, + u64snowflake guild_id, + struct discord_ret_voice_regions *ret); + +/** + * @brief Get guild invites + * @note requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get invites from + * @CCORD_ret_obj{ret,invites} + * @CCORD_return + */ +CCORDcode discord_get_guild_invites(struct discord *client, + u64snowflake guild_id, + struct discord_ret_invites *ret); + +/** + * @brief Get guild integrations + * @note requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get integrations from + * @CCORD_ret_obj{ret,integrations} + * @CCORD_return + */ +CCORDcode discord_get_guild_integrations(struct discord *client, + u64snowflake guild_id, + struct discord_ret_integrations *ret); + +/** + * @brief Deletes the integration for the guild. It will also delete any + * associated webhooks and bots + * @note Requires the MANAGE_GUILD permission + * @note Fires a `Guild Integrations Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to delete the integrations from + * @param integration_id the id of the integration to delete + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_guild_integrations( + struct discord *client, + u64snowflake guild_id, + u64snowflake integration_id, + struct discord_delete_guild_integrations *params, + struct discord_ret *ret); + +/** + * @brief Get a guild widget settings + * @note requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get widget settings from + * @CCORD_ret_obj{ret,guild_widget_settings} + * @CCORD_return + */ +CCORDcode discord_get_guild_widget_settings( + struct discord *client, + u64snowflake guild_id, + struct discord_ret_guild_widget_settings *ret); + +/** + * @brief Modify a guild widget settings + * @note requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to modify the widget settings + * from + * @param param request parameters + * @CCORD_ret_obj{ret,guild_widget_settings} + * @CCORD_return + */ +CCORDcode discord_modify_guild_widget( + struct discord *client, + u64snowflake guild_id, + struct discord_guild_widget_settings *params, + struct discord_ret_guild_widget_settings *ret); + +/** + * @brief Get the widget for the guild + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get the widget from + * @CCORD_ret_obj{ret,guild_widget} + * @CCORD_return + */ +CCORDcode discord_get_guild_widget(struct discord *client, + u64snowflake guild_id, + struct discord_ret_guild_widget *ret); + +/** + * @brief Get invite from a given guild + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get vanity url from + * @CCORD_ret_obj{ret,invite} + * @CCORD_return + */ +CCORDcode discord_get_guild_vanity_url(struct discord *client, + u64snowflake guild_id, + struct discord_ret_invite *ret); + +/* TODO: handle ContentType: image/png and add 'struct discord_png' */ +#if 0 +/** + * @brief Get a PNG image widget for the guild + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get a PNG widget image from + * @param params request parameters + * @CCORD_ret_obj{ret,png} + * @CCORD_return + */ +CCORDcode discord_get_guild_widget_image( + struct discord *client, + u64snowflake guild_id, + struct discord_get_guild_widget_image *params, + struct discord_ret_png *ret); +#endif + +/** + * @brief Get the Welcome Screen for the guild + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get welcome screen of + * @CCORD_ret_obj{ret,welcome_screen} + * @CCORD_return + */ +CCORDcode discord_get_guild_welcome_screen( + struct discord *client, + u64snowflake guild_id, + struct discord_ret_welcome_screen *ret); + +/** + * @brief Modify the Welcome Screen for the guild + * @note requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to modify welcome screen of + * @param params request parameters + * @CCORD_ret_obj{ret,welcome_screen} + * @CCORD_return + */ +CCORDcode discord_modify_guild_welcome_screen( + struct discord *client, + u64snowflake guild_id, + struct discord_modify_guild_welcome_screen *params, + struct discord_ret_welcome_screen *ret); + +/** + * @brief Updates the current user's voice state + * @see Caveats + * https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state-caveats + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to modify the current user's + * voice state + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_modify_current_user_voice_state( + struct discord *client, + u64snowflake guild_id, + struct discord_modify_current_user_voice_state *params, + struct discord_ret *ret); + +/** + * @brief Updates user's voice state + * @see Caveats + * https://discord.com/developers/docs/resources/guild#modify-user-voice-state-caveats + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to modify the user's voice state + * @param user_id the unique id of user to have its voice state modified + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_modify_user_voice_state( + struct discord *client, + u64snowflake guild_id, + u64snowflake user_id, + struct discord_modify_user_voice_state *params, + struct discord_ret *ret); + +/** + * @brief Modify the positions of a given role list for the guild + * @note Requires the MANAGE_ROLES permission + * @note Fires multiple `Guild Role Update` events + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild to get welcome screen of + * @param params request parameters + * @CCORD_ret_obj{ret,roles} + * @CCORD_return + */ +CCORDcode discord_modify_guild_role_positions( + struct discord *client, + u64snowflake guild_id, + struct discord_modify_guild_role_positions *params, + struct discord_ret_roles *ret); + +/** + * @brief Modify a guild role + * @note Requires the MANAGE_ROLES permission + * @note Fires a `Guild Role Update` event + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild that the role belongs to + * @param role_id the unique id of the role to modify + * @param params request parameters + * @CCORD_ret_obj{ret,role} + * @CCORD_return + */ +CCORDcode discord_modify_guild_role(struct discord *client, + u64snowflake guild_id, + u64snowflake role_id, + struct discord_modify_guild_role *params, + struct discord_ret_role *ret); + +/** + * @brief Delete a guild role + * @note Requires the MANAGE_ROLES permission + * @note Fires a `Guild Role Delete` event + * + * @param client the client created with discord_from_token() + * @param guild_id the unique id of the guild that the role belongs to + * @param role_id the unique id of the role to delete + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_guild_role(struct discord *client, + u64snowflake guild_id, + u64snowflake role_id, + struct discord_delete_guild_role *params, + struct discord_ret *ret); + +/** @example guild.c + * Demonstrates a couple use cases of the Guild API */ +/** @example ban.c + * Demonstrates banning and unbanning members */ + +/** @} DiscordAPIGuild */ + +#endif /* DISCORD_GUILD_H */ diff --git a/include/concord/guild_scheduled_event.h b/include/concord/guild_scheduled_event.h new file mode 100644 index 0000000..5276e85 --- /dev/null +++ b/include/concord/guild_scheduled_event.h @@ -0,0 +1,118 @@ +/** + * @file guild_scheduled_event.h + * @author Cogmasters + * @brief Guild Scheduled Event public functions and datatypes + */ + +#ifndef DISCORD_GUILD_SCHEDULED_EVENT_H +#define DISCORD_GUILD_SCHEDULED_EVENT_H + +/** @defgroup DiscordAPIGuildScheduledEvent Guild Scheduled Event + * @ingroup DiscordAPI + * @brief Guild Scheduled Event's public API supported by Concord + * @{ */ + +/** + * @brief Get a list of scheduled events for the guild + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to fetch the scheduled events from + * @param params request parameters + * @CCORD_ret_obj{ret,guild_scheduled_events} + * @CCORD_return + */ +CCORDcode discord_list_guild_scheduled_events( + struct discord *client, + u64snowflake guild_id, + struct discord_list_guild_scheduled_events *params, + struct discord_ret_guild_scheduled_events *ret); + +/** + * @brief Create a guild scheduled event + * @note A guild can have a maximum of 100 events with `SCHEDULED` or `ACTIVE` + * status at any time + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to create the scheduled event at + * @param params request parameters + * @CCORD_ret_obj{ret,guild_scheduled_event} + * @CCORD_return + */ +CCORDcode discord_create_guild_scheduled_event( + struct discord *client, + u64snowflake guild_id, + struct discord_create_guild_scheduled_event *params, + struct discord_ret_guild_scheduled_event *ret); + +/** + * @brief Get a guild scheduled event + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to fetch the scheduled event from + * @param guild_scheduled_event_id the scheduled event to be fetched + * @param params request parameters + * @CCORD_ret_obj{ret,guild_scheduled_event} + * @CCORD_return + */ +CCORDcode discord_get_guild_scheduled_event( + struct discord *client, + u64snowflake guild_id, + u64snowflake guild_scheduled_event_id, + struct discord_get_guild_scheduled_event *params, + struct discord_ret_guild_scheduled_event *ret); + +/** + * @brief Modify a guild scheduled event + * @note Silently discards `entity_metadata` for non-`EXTERNAL` events + * + * @param client the client created with discord_from_token() + * @param guild_id the guild where the scheduled event to be modified is at + * @param guild_scheduled_event_id the scheduled event to be modified + * @param params request parameters + * @CCORD_ret_obj{ret,guild_scheduled_event} + * @CCORD_return + */ +CCORDcode discord_modify_guild_scheduled_event( + struct discord *client, + u64snowflake guild_id, + u64snowflake guild_scheduled_event_id, + struct discord_modify_guild_scheduled_event *params, + struct discord_ret_guild_scheduled_event *ret); + +/** + * @brief Delete a guild scheduled event + * + * @param client the client created with discord_from_token() + * @param guild_id the guild where the scheduled event to be deleted is at + * @param guild_scheduled_event_id the scheduled event to be deleted + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_guild_scheduled_event( + struct discord *client, + u64snowflake guild_id, + u64snowflake guild_scheduled_event_id, + struct discord_ret *ret); + +/** + * @brief Get a list of members subscribed to a guild scheduled event + * @note Guild member data, if it exists, is included if the + * `params.with_member` value is set + * + * @param client the client created with discord_from_token() + * @param guild_id the guild with the scheduled event belongs to + * @param guild_scheduled_event_id the scheduled event + * @param params request parameters + * @CCORD_ret_obj{ret,guild_scheduled_event_users} + * @CCORD_return + */ +CCORDcode discord_get_guild_scheduled_event_users( + struct discord *client, + u64snowflake guild_id, + u64snowflake guild_scheduled_event_id, + struct discord_get_guild_scheduled_event_users *params, + struct discord_ret_guild_scheduled_event_users *ret); + +/** @} DiscordAPIGuildScheduledEvent */ + +#endif /* DISCORD_GUILD_SCHEDULED_EVENT_H */ diff --git a/include/concord/guild_template.h b/include/concord/guild_template.h new file mode 100644 index 0000000..138b86a --- /dev/null +++ b/include/concord/guild_template.h @@ -0,0 +1,126 @@ +/** + * @file guild_template.h + * @author Cogmasters + * @brief Guild Template public functions and datatypes + */ + +#ifndef DISCORD_GUILD_TEMPLATE_H +#define DISCORD_GUILD_TEMPLATE_H + +/** @defgroup DiscordAPIGuildTemplate Guild Template + * @ingroup DiscordAPI + * @brief Guild Template's public API supported by Concord + * @{ */ + +/** + * @brief Get a guild template for the given code + * + * @param client the client created with discord_from_token() + * @param template_code the guild template code + * @CCORD_ret_obj{ret,guild_template} + * @CCORD_return + */ +CCORDcode discord_get_guild_template(struct discord *client, + const char template_code[], + struct discord_ret_guild_template *ret); + +/** + * @brief Create a new guild based on a template + * @note This endpoint can be used only by bots in less than 10 guilds + * + * @param client the client created with discord_from_token() + * @param template_code the guild template code + * @param params the request parameters + * @CCORD_ret_obj{ret,guild} + * @CCORD_return + */ +CCORDcode discord_create_guild_from_guild_template( + struct discord *client, + const char template_code[], + struct discord_create_guild_from_guild_template *params, + struct discord_ret_guild *ret); + +/** + * @brief Returns @ref discord_guild_templates from a guild + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to fetch the templates from + * @CCORD_ret_obj{ret,guild_templates} + * @CCORD_return + */ +CCORDcode discord_get_guild_templates(struct discord *client, + u64snowflake guild_id, + struct discord_ret_guild_templates *ret); + +/** + * @brief Creates a template for the guild + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to create a template from + * @param params the request parameters + * @CCORD_ret_obj{ret,guild_template} + * @CCORD_return + */ +CCORDcode discord_create_guild_template( + struct discord *client, + u64snowflake guild_id, + struct discord_create_guild_template *params, + struct discord_ret_guild_template *ret); + +/** + * @brief Syncs the template to the guild's current state + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to sync the template from + * @param template_code the guild template code + * @CCORD_ret_obj{ret,guild_template} + * @CCORD_return + */ +CCORDcode discord_sync_guild_template(struct discord *client, + u64snowflake guild_id, + const char template_code[], + struct discord_ret_guild_template *ret); + +/** + * @brief Modifies the template's metadata + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to modify the template at + * @param template_code the guild template code + * @param params the request parameters + * @CCORD_ret_obj{ret,guild_template} + * @CCORD_return + */ +CCORDcode discord_modify_guild_template( + struct discord *client, + u64snowflake guild_id, + const char template_code[], + struct discord_modify_guild_template *params, + struct discord_ret_guild_template *ret); + +/** + * @brief Deletes the guild template + * @note Requires the `MANAGE_GUILD` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild to delete the template at + * @param template_code the guild template code + * @CCORD_ret_obj{ret,guild_template} + * @CCORD_return + */ +CCORDcode discord_delete_guild_template( + struct discord *client, + u64snowflake guild_id, + const char template_code[], + struct discord_ret_guild_template *ret); + +/** @example guild-template.c + * Demonstrates a couple use cases of the Guild Template API */ + +/** @} DiscordAPIGuildTemplate */ + +#endif /* DISCORD_GUILD_TEMPLATE_H */ diff --git a/include/concord/interaction.h b/include/concord/interaction.h new file mode 100644 index 0000000..8bf710f --- /dev/null +++ b/include/concord/interaction.h @@ -0,0 +1,152 @@ +/** + * @file interaction.h + * @author Cogmasters + * @brief Interaciton public functions and datatypes + */ + +#ifndef DISCORD_INTERACTION_H +#define DISCORD_INTERACTION_H + +/** @defgroup DiscordAPIInteractionsReact Receiving and sending + * @ingroup DiscordAPIInteractions + * @brief Receiving and sending interactions + * @{ */ + +/** + * @brief Create a response to an Interaction from the gateway + * + * @param client the client created with discord_from_token() + * @param interaction_id the unique id of the interaction + * @param interaction_token the unique token of the interaction + * @param params the request parameters + * @CCORD_ret_obj{ret,interaction_response} + * @CCORD_return + */ +CCORDcode discord_create_interaction_response( + struct discord *client, + u64snowflake interaction_id, + const char interaction_token[], + struct discord_interaction_response *params, + struct discord_ret_interaction_response *ret); + +/** + * @brief Get the initial Interaction response + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the application + * @param interaction_token the unique token of the interaction + * @CCORD_ret_obj{ret,interaction_response} + * @CCORD_return + */ +CCORDcode discord_get_original_interaction_response( + struct discord *client, + u64snowflake application_id, + const char interaction_token[], + struct discord_ret_interaction_response *ret); + +/** + * @brief Edit the initial Interaction response + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the application + * @param interaction_token the unique token of the interaction + * @param params request parameters + * @CCORD_ret_obj{ret,interaction_response} + * @CCORD_return + */ +CCORDcode discord_edit_original_interaction_response( + struct discord *client, + u64snowflake application_id, + const char interaction_token[], + struct discord_edit_original_interaction_response *params, + struct discord_ret_interaction_response *ret); + +/** + * @brief Delete the initial Interaction response + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the application + * @param interaction_token the unique token of the interaction + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_original_interaction_response( + struct discord *client, + u64snowflake application_id, + const char interaction_token[], + struct discord_ret *ret); + +/** + * @brief Create a followup message for an Interaction + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the application + * @param interaction_token the unique token of the interaction + * @param params request parameters + * @CCORD_ret_obj{ret,webhook} + * @CCORD_return + */ +CCORDcode discord_create_followup_message( + struct discord *client, + u64snowflake application_id, + const char interaction_token[], + struct discord_create_followup_message *params, + struct discord_ret_webhook *ret); + +/** + * @brief Get a followup message for an interaction + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the application + * @param interaction_token the unique token of the interaction + * @param message_id the unique id of the message + * @CCORD_ret_obj{ret,message} + * @CCORD_return + */ +CCORDcode discord_get_followup_message(struct discord *client, + u64snowflake application_id, + const char interaction_token[], + u64snowflake message_id, + struct discord_ret_message *ret); + +/** + * @brief Edits a followup message for an interaction + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the application + * @param interaction_token the unique token of the interaction + * @param message_id the unique id of the message + * @param params request parameters + * @CCORD_ret_obj{ret,message} + * @CCORD_return + */ +CCORDcode discord_edit_followup_message( + struct discord *client, + u64snowflake application_id, + const char interaction_token[], + u64snowflake message_id, + struct discord_edit_followup_message *params, + struct discord_ret_message *ret); + +/** + * @brief Edits a followup message for an interaction + * + * @param client the client created with discord_from_token() + * @param application_id the unique id of the application + * @param interaction_token the unique token of the interaction + * @param message_id the unique id of the message + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_followup_message(struct discord *client, + u64snowflake application_id, + const char interaction_token[], + u64snowflake message_id, + struct discord_ret *ret); + +/** @example components.c + * Demonstrates a couple use cases of the Message Components API */ + +/** @} DiscordAPIInteractionsReact */ + +#endif /* DISCORD_INTERACTION_H */ diff --git a/include/concord/invite.h b/include/concord/invite.h new file mode 100644 index 0000000..4b102e5 --- /dev/null +++ b/include/concord/invite.h @@ -0,0 +1,51 @@ +/** + * @file invite.h + * @author Cogmasters + * @brief Invite public functions and datatypes + */ + +#ifndef DISCORD_INVITE_H +#define DISCORD_INVITE_H + +/** @defgroup DiscordAPIInvite Invite + * @ingroup DiscordAPI + * @brief Invite's public API supported by Concord + * @{ */ + +/** + * @brief Get an invite for the given code + * + * @param client the client created with discord_from_token() + * @param invite_code the invite code + * @param params request parameters + * @CCORD_ret_obj{ret,invite} + * @CCORD_return + */ +CCORDcode discord_get_invite(struct discord *client, + char *invite_code, + struct discord_get_invite *params, + struct discord_ret_invite *ret); + +/** + * @brief Delete an invite + * @note Requires the MANAGE_CHANNELS permission on the channel this invite + * belongs to, or MANAGE_GUILD to remove any invite across the guild. + * @note Fires a `Invite Delete` event + * + * @param client the client created with discord_from_token() + * @param invite_code the invite code + * @param params request parameters + * @CCORD_ret_obj{ret,invite} + * @CCORD_return + */ +CCORDcode discord_delete_invite(struct discord *client, + char *invite_code, + struct discord_delete_invite *params, + struct discord_ret_invite *ret); + +/** @example invite.c + * Demonstrates a couple use cases of the Invite API */ + +/** @} DiscordAPIInvite */ + +#endif /* DISCORD_INVITE_H */ diff --git a/include/concord/io_poller.h b/include/concord/io_poller.h new file mode 100644 index 0000000..bc3ba5c --- /dev/null +++ b/include/concord/io_poller.h @@ -0,0 +1,118 @@ +#ifndef CONCORD_IO_POLLER_H +#define CONCORD_IO_POLLER_H + +#include +#include + +/** + * @brief The flags to poll for + */ +enum io_poller_events { + IO_POLLER_IN = 1 << 0, + IO_POLLER_OUT = 1 << 1, +}; + +/** + * @brief a socket or file descriptor + */ +typedef int io_poller_socket; + +/** + * @brief handle for watching file descriptors, sockets, and curl multis + */ +struct io_poller; + +/** + * @brief callback for when an event is triggered by the socket + */ +typedef void (*io_poller_cb)(struct io_poller *io, + enum io_poller_events events, + void *user_data); + +struct io_poller *io_poller_create(void); +void io_poller_destroy(struct io_poller *io); + +/** + * @brief wakeup the thread listening to this io_poller + * + * @param io the io_poller to wake up + */ +void +io_poller_wakeup(struct io_poller *io); + +/** + * @brief wait for events to be triggered + * @param io the io_poller to poll on + * @param milliseconds -1 for infinity, or ms to poll for + * @return -1 for error, or number of sockets that have events waiting + */ +int io_poller_poll(struct io_poller *io, int milliseconds); + +/** + * @brief performs any actions needed and clears events set by io_poller_poll + * @param io the io_poller to perform on + * @return 0 on success + */ +int io_poller_perform(struct io_poller *io); + +/** + * @brief adds or modifies a socket or file descriptor to watch list + * @param io the io_poller to add socket to + * @param sock the file descriptor or socket to handle + * @param events the events to watch for + * @param cb the callback for when any event is triggered + * @param user_data custom user data + * @return true on success + */ +bool io_poller_socket_add(struct io_poller *io, + io_poller_socket sock, + enum io_poller_events events, + io_poller_cb cb, + void *user_data); + +/** + * @brief removes a socket or file descriptor from watch list + * @param io the io_poller to remove the socket from + * @param sock the file descriptor or socket to remove + * @return true on success + */ +bool io_poller_socket_del(struct io_poller *io, io_poller_socket sock); + +/** + * @brief callback for when curl multi should be performed on + */ +typedef int (*io_poller_curl_cb)(struct io_poller *io, + CURLM *multi, + void *user_data); + +/** + * @brief add or modifies a curl multi to watch list + * @param io the io_poller to add curl multi to + * @param multi the curl multi to add or modify + * @param cb the callback for when curl multi should be performed on + * @param user_data custom user data + * @return true on success + */ +bool io_poller_curlm_add(struct io_poller *io, + CURLM *multi, + io_poller_curl_cb cb, + void *user_data); + +/** + * @brief remove curl multi from watch list + * @param io the io_poller to remove curl multi from + * @param multi the curl multi to remove + * @return true on success + */ +bool io_poller_curlm_del(struct io_poller *io, CURLM *multi); + +/** + * @brief this multi should be performed on next cycle + * causing poll to return immediately + * @param io the io_poller to enable perform on + * @param multi the multi that should be performed + * @return true on success + */ +bool io_poller_curlm_enable_perform(struct io_poller *io, CURLM *multi); + +#endif // CONCORD_IO_POLLER_H diff --git a/include/concord/jsmn-find.h b/include/concord/jsmn-find.h new file mode 100644 index 0000000..982afd1 --- /dev/null +++ b/include/concord/jsmn-find.h @@ -0,0 +1,683 @@ +#ifndef JSMN_FIND_H +#define JSMN_FIND_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef JSMN_H +#error "jsmn-find.h should be included after jsmn.h" +#else + +#define OA_HASH_HEADER +#include "oa_hash.h" +#undef OA_HASH_HEADER + +#define JSMNF_PAIR_ATTRS_const \ + /** JSON object or array pair attributes */ \ + OA_HASH_ATTRS(const); \ + /** JSON object or array fields */ \ + const struct jsmnf_pair *const fields; \ + /** key attributes */ \ + const jsmntok_t *const k; \ + /** value attribute */ \ + const jsmntok_t *const v +#define JSMNF_PAIR_ATTRS_mut \ + /** JSON object or array pair attributes */ \ + OA_HASH_ATTRS(mut); \ + /** JSON object or array fields */ \ + struct jsmnf_pair *fields; \ + /** key attributes */ \ + jsmntok_t *k; \ + /** value attribute */ \ + jsmntok_t *v +#define JSMNF_PAIR_ATTRS(_qualifier) JSMNF_PAIR_ATTRS_##_qualifier + +typedef struct jsmnf_pair { + JSMNF_PAIR_ATTRS(const); +} jsmnf_pair; + +/** @brief Bucket @ref jsmnf_pair loader, keeps track of pair array + * position */ +typedef struct jsmnf_loader { + /** jsmnf_loader can be cast to jsmn_parser */ + jsmn_parser parser; + /** next pair to allocate */ + unsigned pairnext; + /** root pair */ + const jsmnf_pair *root; +} jsmnf_loader; + +/** @brief JSON table, not supposed to be accessed by user */ +typedef struct jsmnf_table { + /** @private */ + const struct jsmntok _; + const struct jsmnf_pair __; + const struct oa_hash_entry ___; +} jsmnf_table; + +/** + * @brief Initialize a @ref jsmnf_loader + * + * @param[out] loader jsmnf_loader to be initialized + */ +JSMN_API void jsmnf_init(jsmnf_loader *loader); + +/** + * @brief Populate the @ref jsmnf_pair pairs from jsmn tokens + * + * @param[in,out] loader the @ref jsmnf_loader initialized with jsmnf_init() + * @param[in] js the JSON data string + * @param[in] len the raw JSON string length + * @param[out] tokens jsmn tokens + * @param[out] table jsmnf_table pairs array + * @param[in] table_len maximum amount of pairs provided + * @attention must not be less than the amount of tokens + * @return a `enum jsmnerr` value for error or the amount of `pairs` used + */ +JSMN_API long jsmnf_load(jsmnf_loader *loader, + const char js[], + const size_t len, + jsmnf_table table[], + const size_t table_len); + +/** + * @brief Find a @ref jsmnf_pair token by its associated key + * + * @param[in] head a @ref jsmnf_pair object or array loaded at jsmnf_init() + * @param[in] key the key too be matched + * @param[in] length length of the key too be matched + * @return the @ref jsmnf_pair `head`'s field matched to `key`, or NULL if + * not encountered + */ +JSMN_API const jsmnf_pair *jsmnf_find(const jsmnf_pair *const head, + const char key[], + const size_t length); + +/** + * @brief Find a @ref jsmnf_pair token by its full key path + * + * @param[in] head a @ref jsmnf_pair object or array loaded at jsmnf_init() + * @param[in] path an array of key path strings, from least to highest depth + * @param[in] depth the depth level of the last `path` key + * @return the @ref jsmnf_pair `head`'s field matched to `path`, or NULL if + * not encountered + */ +JSMN_API const jsmnf_pair *jsmnf_find_path(const jsmnf_pair *const head, + char *const path[], + unsigned depth); + +/** + * @brief Populate and automatically allocate the @ref jsmnf_pair pairs from + * jsmn tokens + * @brief jsmnf_load() counterpart that automatically allocates the necessary + * amount of pairs necessary for sorting the JSON tokens + * + * @param[in,out] loader the @ref jsmnf_loader initialized with jsmnf_init() + * @param[in] js the JSON data string + * @param[in] len the raw JSON string length + * @param[out] p_table pointer to @ref jsmnf_table to be dynamically increased + * @note must be `free()`'d once done being used + * @param[in,out] table_len maximum amount of pairs provided + * @return a `enum jsmnerr` value for error or the amount of `pairs` used + */ +JSMN_API long jsmnf_load_auto(jsmnf_loader *loader, + const char js[], + const size_t len, + jsmnf_table **p_table, + size_t *num_pairs); + +/** + * @brief `jsmn_parse()` counterpart that automatically allocates the necessary + * amount of tokens necessary for parsing the JSON string + * + * @param[in,out] parser the `jsmn_parser` initialized with `jsmn_init()` + * @param[in] js the JSON data string + * @param[in] len the raw JSON string length + * @param[out] p_tokens pointer to `jsmntok_t` to be dynamically increased + * @note must be `free()`'d once done being used + * @param[in,out] num_tokens amount of tokens + * @return a `enum jsmnerr` value for error or the amount of `tokens` used + */ +JSMN_API long jsmn_parse_auto(jsmn_parser *parser, + const char js[], + const size_t len, + jsmntok_t **p_tokens, + unsigned *num_tokens); + +/** + * @brief Utility function for unescaping a Unicode string + * + * @param[out] buf destination buffer + * @param[in] bufsize destination buffer size + * @param[in] src source string to be unescaped + * @param[in] length source string length + * @return length of unescaped string if successful or a negative jsmn error + * code on failure + */ +JSMN_API long jsmnf_unescape(char buf[], + size_t bufsize, + const char src[], + size_t length); + +#ifndef JSMN_HEADER + +#include +#include +#include + +#define OA_HASH_STATIC +#include "oa_hash.h" +#undef OA_HASH_STATIC + +struct _jsmnf_pair_mut { + JSMNF_PAIR_ATTRS(mut); +}; + +JSMN_API void +jsmnf_init(jsmnf_loader *loader) +{ + jsmn_init(&loader->parser); + loader->pairnext = 0; +} + +static long +_jsmnf_load_pairs(struct jsmnf_loader *loader, + const char js[], + struct _jsmnf_pair_mut *curr, + const size_t num_tokens, + struct _jsmnf_pair_mut pairs[], + struct oa_hash_entry buckets[], + const size_t table_len) +{ + int offset = 0; + + if (!num_tokens) return 0; + + switch (curr->v->type) { + case JSMN_STRING: + case JSMN_PRIMITIVE: + break; + case JSMN_OBJECT: + case JSMN_ARRAY: { + const unsigned value_size = (unsigned)curr->v->size, + top_idx = loader->pairnext + (1 + value_size), + bottom_idx = loader->pairnext; + int ret; + + if (value_size > (table_len - bottom_idx) + || top_idx > (table_len - bottom_idx)) + { + return JSMN_ERROR_NOMEM; + } + + loader->pairnext = top_idx; + + oa_hash_init((struct oa_hash *)curr, &buckets[bottom_idx], + top_idx - bottom_idx); + if (curr == NULL) { + abort(); + } + if (JSMN_OBJECT == curr->v->type) { + while (curr->length < value_size) { + struct _jsmnf_pair_mut *fields = pairs + bottom_idx, + *element = fields + curr->length; + element->k = curr->v + 1 + (offset++); + if (element->k->size > 0) { + element->v = curr->v + 1 + offset; + oa_hash_set((struct oa_hash *)curr, js + element->k->start, + element->k->end - element->k->start, element); + if ((ret = _jsmnf_load_pairs(loader, js, element, + num_tokens - offset, pairs, + buckets, table_len)) + < 0) + { + return ret; + } + curr->fields = (struct jsmnf_pair *)fields; + offset += ret; + } + else { + oa_hash_set((struct oa_hash *)curr, js + element->k->start, + element->k->end - element->k->start, NULL); + } + } + } + else if (JSMN_ARRAY == curr->v->type) { + for (; curr->length < value_size; ++curr->length) { + static jsmntok_t empty_key = { 0 }; + struct oa_hash_entry *entry = curr->buckets + curr->length; + struct _jsmnf_pair_mut *fields = pairs + bottom_idx, + *element = fields + curr->length; + entry->state = OA_HASH_ENTRY_OCCUPIED; + entry->value = element; + element->v = curr->v + 1 + offset; + element->k = &empty_key; + if ((ret = _jsmnf_load_pairs(loader, js, element, + num_tokens - offset, pairs, + buckets, table_len)) + < 0) + { + return ret; + } + curr->fields = (struct jsmnf_pair *)fields; + offset += ret; + } + } + break; + } + default: + case JSMN_UNDEFINED: + return JSMN_ERROR_INVAL; + } + + return offset + 1; +} + +JSMN_API long +jsmnf_load(struct jsmnf_loader *loader, + const char js[], + const size_t len, + struct jsmnf_table table[], + const size_t table_len) +{ + struct jsmntok *tokens = (struct jsmntok *)table; + struct _jsmnf_pair_mut + *pairs = (struct _jsmnf_pair_mut *)(((char *)tokens) + + (table_len * sizeof *tokens)), + *mut_root = &pairs[0]; + struct oa_hash_entry *buckets = + (struct oa_hash_entry *)(((char *)pairs) + + (table_len * sizeof *pairs)); + long ret; + + if (loader->pairnext == 0) { /* first run, initialize pairs */ + /* initialize tokens if not already initialized */ + if (loader->parser.toknext == 0) { + memset(tokens, 0, table_len * sizeof *tokens); + if ((ret = jsmn_parse(&loader->parser, js, len, tokens, table_len)) + < 0) + { + return jsmn_init(&loader->parser), ret; + } + } + memset(pairs, 0, table_len * sizeof *pairs); + memset(buckets, 0, table_len * sizeof *buckets); + mut_root->v = tokens + loader->pairnext++; + loader->root = (struct jsmnf_pair *)mut_root; + } + if ((ret = _jsmnf_load_pairs(loader, js, mut_root, loader->parser.toknext, + pairs, buckets, table_len)) + < 0) + { + /* TODO: rather than reseting pairnext keep the last 'bucket' ptr + * stored, so it can continue from there in the next try */ + loader->pairnext = 0; + loader->root = NULL; + } + return ret; +} + +JSMN_API const struct jsmnf_pair * +jsmnf_find(const struct jsmnf_pair *head, + const char key[], + const size_t length) +{ + if (!head || !head->v) return NULL; + if (!key && !length) return head; + + if (JSMN_OBJECT == head->v->type) { + return oa_hash_get((struct oa_hash *)head, key, length); + } + if (JSMN_ARRAY == head->v->type) { + char *endptr; + const unsigned idx = (unsigned)strtoul(key, &endptr, 10); + if (endptr != key && (idx < head->length) + && head->buckets[idx].state == OA_HASH_ENTRY_OCCUPIED) + { + return head->buckets[idx].value; + } + } + return NULL; +} + +JSMN_API const struct jsmnf_pair * +jsmnf_find_path(const struct jsmnf_pair *head, + char *const path[], + unsigned depth) +{ + const struct jsmnf_pair *iter = head, *found = NULL; + unsigned i; + for (i = 0; i < depth; ++i) { + if (!iter || !(found = jsmnf_find(iter, path[i], strlen(path[i])))) + break; + iter = found; + } + return found; +} + +#define RECALLOC_OR_ERROR(ptr, prev_size) \ + do { \ + const unsigned new_size = *(prev_size) * 2; \ + void *tmp = realloc((ptr), new_size * sizeof *(ptr)); \ + if (!tmp) return JSMN_ERROR_NOMEM; \ + (ptr) = tmp; \ + memset((ptr) + *(prev_size), 0, \ + (new_size - *(prev_size)) * sizeof *(ptr)); \ + *(prev_size) = new_size; \ + } while (0) + +JSMN_API long +jsmn_parse_auto(struct jsmn_parser *parser, + const char js[], + const size_t len, + struct jsmntok **p_tokens, + unsigned *num_tokens) +{ + int ret; + + if (NULL == *p_tokens || 0 == *num_tokens) { + *p_tokens = calloc(1, sizeof **p_tokens); + *num_tokens = 1; + } + while ((ret = jsmn_parse(parser, js, len, *p_tokens, *num_tokens)) + == JSMN_ERROR_NOMEM) + { + RECALLOC_OR_ERROR(*p_tokens, num_tokens); + } + return ret; +} + +JSMN_API long +jsmnf_load_auto(struct jsmnf_loader *loader, + const char js[], + const size_t len, + struct jsmnf_table **p_table, + size_t *table_len) +{ + int ret; + + if (NULL == *p_table || 0 == *table_len) { + if (!(*p_table = calloc(1, sizeof **p_table))) { + return JSMN_ERROR_NOMEM; + } + *table_len = 1; + } + while ((ret = jsmnf_load(loader, js, len, *p_table, *table_len)) + == JSMN_ERROR_NOMEM) + { + RECALLOC_OR_ERROR(*p_table, table_len); + } + return ret; +} + +#undef RECALLOC_OR_ERROR + +static int +_jsmnf_read_4_digits(char *s, const char *end, unsigned *p_hex) +{ + char buf[5] = { 0 }; + int i; + + if (end - s < 4) return JSMN_ERROR_PART; + + for (i = 0; i < 4; i++) { + buf[i] = s[i]; + if (('0' <= s[i] && s[i] <= '9') || ('A' <= s[i] && s[i] <= 'F') + || ('a' <= s[i] && s[i] <= 'f')) + { + continue; + } + return JSMN_ERROR_INVAL; + } + + *p_hex = (unsigned)strtoul(buf, NULL, 16); + + return 4; +} + +#define _JSMNF_UTF16_IS_FIRST_SURROGATE(c) \ + (0xD800 <= (unsigned)c && (unsigned)c <= 0xDBFF) +#define _JSMNF_UTF16_IS_SECOND_SURROGATE(c) \ + (0xDC00 <= (unsigned)c && (unsigned)c <= 0xDFFF) +#define _JSMNF_UTF16_JOIN_SURROGATE(c1, c2) \ + (((((unsigned long)c1 & 0x3FF) << 10) | ((unsigned)c2 & 0x3FF)) + 0x10000) +#define _JSMNF_UTF8_IS_VALID(c) \ + (((unsigned long)c <= 0x10FFFF) \ + && ((unsigned long)c < 0xD800 || (unsigned long)c > 0xDFFF)) +#define _JSMNF_UTF8_IS_TRAIL(c) (((unsigned char)c & 0xC0) == 0x80) +#define _JSMNF_UTF_ILLEGAL 0xFFFFFFFFu + +static int +_jsmnf_utf8_trail_length(unsigned char c) +{ + if (c < 128) return 0; + if (c < 194) return -1; + if (c < 224) return 1; + if (c < 240) return 2; + if (c <= 244) return 3; + return -1; +} + +static int +_jsmnf_utf8_width(unsigned long value) +{ + if (value <= 0x7F) return 1; + if (value <= 0x7FF) return 2; + if (value <= 0xFFFF) return 3; + return 4; +} + +/* See RFC 3629 + Based on: http://www.w3.org/International/questions/qa-forms-utf-8 */ +static unsigned long +_jsmnf_utf8_next(char **p, const char *end) +{ + unsigned char lead, tmp; + int trail_size; + unsigned long c; + + if (*p == end) return _JSMNF_UTF_ILLEGAL; + + lead = **p; + (*p)++; + + /* First byte is fully validated here */ + trail_size = _jsmnf_utf8_trail_length(lead); + + if (trail_size < 0) return _JSMNF_UTF_ILLEGAL; + + /* Ok as only ASCII may be of size = 0 also optimize for ASCII text */ + if (trail_size == 0) return lead; + + c = lead & ((1 << (6 - trail_size)) - 1); + + /* Read the rest */ + switch (trail_size) { + case 3: + if (*p == end) return _JSMNF_UTF_ILLEGAL; + tmp = **p; + (*p)++; + if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL; + c = (c << 6) | (tmp & 0x3F); + /* fall-through */ + case 2: + if (*p == end) return _JSMNF_UTF_ILLEGAL; + tmp = **p; + (*p)++; + if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL; + c = (c << 6) | (tmp & 0x3F); + /* fall-through */ + case 1: + if (*p == end) return _JSMNF_UTF_ILLEGAL; + tmp = **p; + (*p)++; + if (!_JSMNF_UTF8_IS_TRAIL(tmp)) return _JSMNF_UTF_ILLEGAL; + c = (c << 6) | (tmp & 0x3F); + } + + /* Check code point validity: no surrogates and valid range */ + if (!_JSMNF_UTF8_IS_VALID(c)) return _JSMNF_UTF_ILLEGAL; + + /* make sure it is the most compact representation */ + if (_jsmnf_utf8_width(c) != trail_size + 1) return _JSMNF_UTF_ILLEGAL; + + return c; +} + +static long +_jsmnf_utf8_validate(char *p, const char *end) +{ + const char *start = p; + while (p != end) { + if (_jsmnf_utf8_next(&p, end) == _JSMNF_UTF_ILLEGAL) + return JSMN_ERROR_INVAL; + } + return (long)(end - start); +} + +static unsigned +_jsmnf_utf8_encode(unsigned long value, char utf8_seq[4]) +{ + if (value <= 0x7F) { + utf8_seq[0] = value; + return 1; + } + if (value <= 0x7FF) { + utf8_seq[0] = (value >> 6) | 0xC0; + utf8_seq[1] = (value & 0x3F) | 0x80; + return 2; + } + if (value <= 0xFFFF) { + utf8_seq[0] = (value >> 12) | 0xE0; + utf8_seq[1] = ((value >> 6) & 0x3F) | 0x80; + utf8_seq[2] = (value & 0x3F) | 0x80; + return 3; + } + utf8_seq[0] = (value >> 18) | 0xF0; + utf8_seq[1] = ((value >> 12) & 0x3F) | 0x80; + utf8_seq[2] = ((value >> 6) & 0x3F) | 0x80; + utf8_seq[3] = (value & 0x3F) | 0x80; + return 4; +} + +static int +_jsmnf_utf8_append(unsigned long hex, char *buf_tok, const char *buf_end) +{ + char utf8_seq[4]; + unsigned utf8_seqlen = _jsmnf_utf8_encode(hex, utf8_seq); + unsigned i; + + if ((buf_tok + utf8_seqlen) >= buf_end) return JSMN_ERROR_NOMEM; + + for (i = 0; i < utf8_seqlen; ++i) + buf_tok[i] = utf8_seq[i]; + return utf8_seqlen; +} + +#define BUF_PUSH(buf_tok, c, buf_end) \ + do { \ + if (buf_tok >= buf_end) return JSMN_ERROR_NOMEM; \ + *buf_tok++ = c; \ + } while (0) + +JSMN_API long +jsmnf_unescape(char buf[], size_t bufsize, const char src[], size_t len) +{ + char *src_tok = (char *)src, *const src_end = src_tok + len; + char *buf_tok = buf, *const buf_end = buf + bufsize; + int second_surrogate_expected = 0; + unsigned first_surrogate = 0; + + while (*src_tok && src_tok < src_end) { + char c = *src_tok++; + + if (0 <= c && c <= 0x1F) return JSMN_ERROR_INVAL; + + if (c != '\\') { + if (second_surrogate_expected) return JSMN_ERROR_INVAL; + BUF_PUSH(buf_tok, c, buf_end); + continue; + } + + /* expects escaping but src is a well-formed string */ + if (!*src_tok || src_tok >= src_end) return JSMN_ERROR_PART; + + c = *src_tok++; + + if (second_surrogate_expected && c != 'u') return JSMN_ERROR_INVAL; + + switch (c) { + case '"': + case '\\': + case '/': + BUF_PUSH(buf_tok, c, buf_end); + break; + case 'b': + BUF_PUSH(buf_tok, '\b', buf_end); + break; + case 'f': + BUF_PUSH(buf_tok, '\f', buf_end); + break; + case 'n': + BUF_PUSH(buf_tok, '\n', buf_end); + break; + case 'r': + BUF_PUSH(buf_tok, '\r', buf_end); + break; + case 't': + BUF_PUSH(buf_tok, '\t', buf_end); + break; + case 'u': { + unsigned hex; + int ret = _jsmnf_read_4_digits(src_tok, src_end, &hex); + + if (ret != 4) return ret; + + src_tok += ret; + + if (second_surrogate_expected) { + if (!_JSMNF_UTF16_IS_SECOND_SURROGATE(hex)) + return JSMN_ERROR_INVAL; + + ret = _jsmnf_utf8_append( + _JSMNF_UTF16_JOIN_SURROGATE(first_surrogate, hex), buf_tok, + buf_end); + if (ret < 0) return ret; + + buf_tok += ret; + + second_surrogate_expected = 0; + } + else if (_JSMNF_UTF16_IS_FIRST_SURROGATE(hex)) { + second_surrogate_expected = 1; + first_surrogate = hex; + } + else { + ret = _jsmnf_utf8_append(hex, buf_tok, buf_end); + if (ret < 0) return ret; + + buf_tok += ret; + } + } break; + default: + return JSMN_ERROR_INVAL; + } + } + return _jsmnf_utf8_validate(buf, buf_tok); +} + +#undef BUF_PUSH + +#endif /* JSMN_HEADER */ +#endif /* JSMN_H */ + +#undef JSMNF_PAIR_ATTRS_const +#undef JSMNF_PAIR_ATTRS_mut +#undef JSMNF_PAIR_ATTRS + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_FIND_H */ diff --git a/include/concord/jsmn.h b/include/concord/jsmn.h new file mode 100644 index 0000000..3178dcc --- /dev/null +++ b/include/concord/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/include/concord/json-build.h b/include/concord/json-build.h new file mode 100644 index 0000000..a1844e1 --- /dev/null +++ b/include/concord/json-build.h @@ -0,0 +1,957 @@ +/* + * Special thanks to Christopher Wellons (aka skeeto) for giving valuable + * feedback that helped improve this lib. + * + * See: https://www.reddit.com/r/C_Programming/comments/sf95m3/comment/huojrjn + */ +#ifndef JSON_BUILD_H +#define JSON_BUILD_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSONB_STATIC +#define JSONB_API static +#else +#define JSONB_API extern +#endif + +#ifndef JSONB_MAX_DEPTH +/** + * Maximum JSON nesting depth, if default value is unwanted then it should be + * defined before json-build.h is included: + * + * #define JSONB_MAX_DEPTH 256 + * #include "json-build.h" + */ +#define JSONB_MAX_DEPTH 128 +#endif /* JSONB_MAX_DEPTH */ + +/** @brief json-builder return codes */ +typedef enum jsonbcode { + /** no error, operation was a success */ + JSONB_OK = 0, + /** string is complete, expects no more inputs */ + JSONB_END, + /** not enough tokens were provided */ + JSONB_ERROR_NOMEM = -1, + /** token doesn't match expected value */ + JSONB_ERROR_INPUT = -2, + /** operation would lead to out of boundaries access */ + JSONB_ERROR_STACK = -3, + /** buffer overflow */ + JSONB_ERROR_OVERFLOW = -4 +} jsonbcode; + +/** @brief json-builder serializing state */ +enum jsonbstate { + JSONB_INIT = 0, + JSONB_ARRAY_OR_OBJECT_OR_VALUE = JSONB_INIT, + JSONB_OBJECT_KEY_OR_CLOSE, + JSONB_OBJECT_VALUE, + JSONB_OBJECT_NEXT_KEY_OR_CLOSE, + JSONB_ARRAY_VALUE_OR_CLOSE, + JSONB_ARRAY_NEXT_VALUE_OR_CLOSE, + JSONB_ERROR, + JSONB_DONE +}; + +/** @brief Handle for building a JSON string */ +typedef struct jsonb { + /** state stack to keep track and enforce next inputs */ + enum jsonbstate stack[JSONB_MAX_DEPTH + 1]; + /** pointer to stack top */ + enum jsonbstate *top; + /** offset in the JSON buffer (current length) */ + size_t pos; +} jsonb; + +/** + * @brief Reset a jsonb handle buffer's position tracker + * (for streaming purposes) + * @note Should be used in conjunction with @ref JSONB_ERROR_NOMEM if the + * buffer is meant to be used as a stream + * + * @param builder pointer to the @ref jsonb handle + */ +#define jsonb_reset(builder) ((builder)->pos = 0) + +/** + * @brief Initialize a jsonb handle + * + * @param builder the handle to be initialized + */ +JSONB_API void jsonb_init(jsonb *builder); + +/** + * @brief Push an object to the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_object(jsonb *builder, char buf[], size_t bufsize); + +/** + * @brief @ref jsonb_object() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_object_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize); + +/** + * @brief Pop an object from the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_object_pop(jsonb *builder, + char buf[], + size_t bufsize); + +/** + * @brief @ref jsonb_object_pop() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_object_pop_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize); + +/** + * @brief Push a key to the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @param key the key to be inserted + * @param len the key length + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_key( + jsonb *builder, char buf[], size_t bufsize, const char key[], size_t len); + +/** + * @brief @ref jsonb_key() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer to the JSON buffer size + * @param key the key to be inserted + * @param len the key length + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_key_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize, + const char key[], + size_t len); + +/** + * @brief Push an array to the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_array(jsonb *builder, char buf[], size_t bufsize); + +/** + * @brief @ref jsonb_array() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer to the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_array_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize); + +/** + * @brief Pop an array from the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_array_pop(jsonb *builder, + char buf[], + size_t bufsize); + +/** + * @brief @ref jsonb_array_pop() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer to the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_array_pop_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize); + +/** + * @brief Push a raw JSON token to the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @param token the token to be inserted + * @param len the token length + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_token(jsonb *builder, + char buf[], + size_t bufsize, + const char token[], + size_t len); + +/** + * @brief @ref jsonb_token() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer to the JSON buffer size + * @param token the token to be inserted + * @param len the token length + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_token_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize, + const char token[], + size_t len); + +/** + * @brief Push a boolean token to the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @param boolean the boolean to be inserted + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_bool(jsonb *builder, + char buf[], + size_t bufsize, + int boolean); + +/** + * @brief @ref jsonb_bool() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer to the JSON buffer size + * @param boolean the boolean to be inserted + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_bool_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize, + int boolean); + +/** + * @brief Push a null token to the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_null(jsonb *builder, char buf[], size_t bufsize); + +/** + * @brief @ref jsonb_null() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer to the JSON buffer size + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_null_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize); + +/** + * @brief Push a string token to the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @param str the string to be inserted + * @param len the string length + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_string( + jsonb *builder, char buf[], size_t bufsize, const char str[], size_t len); + +/** + * @brief @ref jsonb_string() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer to the JSON buffer size + * @param str the string to be inserted + * @param len the string length + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_string_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize, + const char str[], + size_t len); + +/** + * @brief Push a number token to the builder + * + * @param builder the builder initialized with jsonb_init() + * @param buf the JSON buffer + * @param bufsize the JSON buffer size + * @param number the number to be inserted + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_number(jsonb *builder, + char buf[], + size_t bufsize, + double number); + +/** + * @brief @ref jsonb_number() with dynamic buffer + * + * @param builder the builder initialized with jsonb_init() + * @param p_buf pointer to the JSON buffer + * @param p_bufsize pointer to the JSON buffer size + * @param number the number to be inserted + * @return @ref jsonbcode value + */ +JSONB_API jsonbcode jsonb_number_auto(jsonb *builder, + char *p_buf[], + size_t *p_bufsize, + double number); + +#ifndef JSONB_HEADER +#include +#include +#ifndef JSONB_DEBUG +#define TRACE(prev, next) next +#define DECORATOR(a) +#else +static const char * +_jsonb_eval_state(enum jsonbstate state) +{ + switch (state) { + case JSONB_ARRAY_OR_OBJECT_OR_VALUE: return "array or object or value"; + case JSONB_OBJECT_KEY_OR_CLOSE: return "object key or close"; + case JSONB_OBJECT_NEXT_KEY_OR_CLOSE: return "object next key or close"; + case JSONB_OBJECT_VALUE: return "object value"; + case JSONB_ARRAY_VALUE_OR_CLOSE: return "array value or close"; + case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: return "array next value or close"; + case JSONB_ERROR: return "error"; + case JSONB_DONE: return "done"; + default: return "unknown"; + } +} +#define TRACE(prev, next) \ + do { \ + enum jsonbstate _prev = prev, _next = next; \ + fprintf(stderr, "%s():L%d | %s -> %s\n", __func__, __LINE__, \ + _jsonb_eval_state(_prev), _jsonb_eval_state(_next)); \ + } while (0) +#define DECORATOR(d) d +#endif /* JSONB_DEBUG */ + +#define STACK_HEAD(b, state) *(b)->top = (state) +#define STACK_PUSH(b, state) TRACE(*(b)->top, *++(b)->top = (state)) +#define STACK_POP(b) TRACE(*(b)->top, DECORATOR(*)--(b)->top) + +#define BUFFER_COPY_CHAR_STATIC(b, c, _pos, buf, bufsize) \ + do { \ + if ((b)->pos + (_pos) + 1 + 1 > (bufsize)) { \ + (buf)[(b)->pos] = '\0'; \ + return JSONB_ERROR_NOMEM; \ + } \ + (buf)[(b)->pos + (_pos)++] = (c); \ + (buf)[(b)->pos + (_pos)] = '\0'; \ + } while (0) +#define BUFFER_COPY_STATIC(b, value, len, _pos, buf, bufsize) \ + do { \ + size_t i; \ + if ((b)->pos + (_pos) + (len) + 1 > (bufsize)) { \ + (buf)[(b)->pos] = '\0'; \ + return JSONB_ERROR_NOMEM; \ + } \ + for (i = 0; i < (len); ++i) \ + (buf)[(b)->pos + (_pos) + i] = (value)[i]; \ + (_pos) += (len); \ + (buf)[(b)->pos + (_pos)] = '\0'; \ + } while (0) +#define BUFFER_COPY_CHAR_REALLOC(b, c, _pos, p_buf, p_bufsize) \ + do { \ + if ((b)->pos + (_pos) + 1 + 1 > *p_bufsize) { \ + char *new_buf = NULL; \ + const size_t needed = (b)->pos + (_pos) + 1 + 1; \ + size_t new_size = *p_bufsize + (*p_bufsize >> 1); /* 1.5x */ \ + if (new_size < needed) new_size = needed; \ + if (new_size < *p_bufsize) return JSONB_ERROR_OVERFLOW; \ + new_buf = realloc(*p_buf, new_size); \ + if (!new_buf) return JSONB_ERROR_NOMEM; \ + *p_buf = new_buf; \ + *p_bufsize = new_size; \ + } \ + (*p_buf)[(b)->pos + (_pos)++] = (c); \ + (*p_buf)[(b)->pos + (_pos)] = '\0'; \ + } while (0) +#define BUFFER_COPY_REALLOC(b, value, len, _pos, p_buf, p_bufsize) \ + do { \ + size_t i; \ + if ((b)->pos + (_pos) + (len) + 1 > *p_bufsize) { \ + char *new_buf = NULL; \ + const size_t needed = (b)->pos + (_pos) + (len) + 1; \ + size_t new_size = *p_bufsize + (*p_bufsize >> 1); /* 1.5x */ \ + if (new_size < needed) new_size = needed; \ + if (new_size < *p_bufsize) return JSONB_ERROR_OVERFLOW; \ + new_buf = realloc(*p_buf, new_size); \ + if (!new_buf) return JSONB_ERROR_NOMEM; \ + *p_buf = new_buf; \ + *p_bufsize = new_size; \ + } \ + for (i = 0; i < (len); ++i) \ + (*p_buf)[(b)->pos + (_pos) + i] = (value)[i]; \ + (_pos) += (len); \ + (*p_buf)[(b)->pos + (_pos)] = '\0'; \ + } while (0) + +JSONB_API void +jsonb_init(jsonb *b) +{ + static jsonb empty_builder; + *b = empty_builder; + b->top = b->stack; +} + +#define JSONB_OBJECT_EXEC(_type, buf, bufsize) \ + enum jsonbstate new_state; \ + size_t pos = 0; \ + if (b->top - b->stack >= JSONB_MAX_DEPTH) return JSONB_ERROR_STACK; \ + switch (*b->top) { \ + case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \ + BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \ + /* fall-through */ \ + case JSONB_ARRAY_VALUE_OR_CLOSE: \ + new_state = JSONB_ARRAY_NEXT_VALUE_OR_CLOSE; \ + break; \ + case JSONB_OBJECT_VALUE: \ + new_state = JSONB_OBJECT_NEXT_KEY_OR_CLOSE; \ + break; \ + case JSONB_ARRAY_OR_OBJECT_OR_VALUE: \ + new_state = JSONB_DONE; \ + break; \ + default: \ + STACK_HEAD(b, JSONB_ERROR); \ + /* fall-through */ \ + case JSONB_DONE: \ + case JSONB_ERROR: \ + return JSONB_ERROR_INPUT; \ + } \ + BUFFER_COPY_CHAR_##_type(b, '{', pos, buf, bufsize); \ + STACK_HEAD(b, new_state); \ + STACK_PUSH(b, JSONB_OBJECT_KEY_OR_CLOSE); \ + b->pos += pos; \ + return JSONB_OK + +JSONB_API jsonbcode +jsonb_object(jsonb *b, char buf[], size_t bufsize) +{ + JSONB_OBJECT_EXEC(STATIC, buf, bufsize); +} + +JSONB_API jsonbcode +jsonb_object_auto(jsonb *b, char *p_buf[], size_t *p_bufsize) +{ + JSONB_OBJECT_EXEC(REALLOC, p_buf, p_bufsize); +} + +#define JSONB_OBJECT_POP_EXEC(_type, buf, bufsize) \ + enum jsonbcode code; \ + size_t pos = 0; \ + switch (*b->top) { \ + case JSONB_OBJECT_KEY_OR_CLOSE: \ + case JSONB_OBJECT_NEXT_KEY_OR_CLOSE: \ + code = b->stack == b->top - 1 ? JSONB_END : JSONB_OK; \ + break; \ + default: \ + STACK_HEAD(b, JSONB_ERROR); \ + /* fall-through */ \ + case JSONB_DONE: \ + case JSONB_ERROR: \ + return JSONB_ERROR_INPUT; \ + } \ + BUFFER_COPY_CHAR_##_type(b, '}', pos, buf, bufsize); \ + STACK_POP(b); \ + b->pos += pos; \ + return code + +JSONB_API jsonbcode +jsonb_object_pop(jsonb *b, char buf[], size_t bufsize) +{ + JSONB_OBJECT_POP_EXEC(STATIC, buf, bufsize); +} + +JSONB_API jsonbcode +jsonb_object_pop_auto(jsonb *b, char *p_buf[], size_t *p_bufsize) +{ + JSONB_OBJECT_POP_EXEC(REALLOC, p_buf, p_bufsize); +} + +static jsonbcode +_jsonb_escape_STATIC(size_t *pos, + char buf[], + size_t bufsize, + unsigned offset, + const char str[], + size_t len) +{ + char *esc_tok = NULL, _esc_tok[8] = "\\u00"; + char *esc_buf = NULL; + int extra_bytes = 0; + size_t i; + + buf += offset; + bufsize -= offset; +second_iter: + /* 1st iteration, esc_buf is NULL and count extra_bytes needed for escaping + * 2st iteration, esc_buf is not NULL, and does escaping. */ + for (i = 0; i < len; ++i) { + unsigned char c = str[i]; + esc_tok = NULL; + switch (c) { + case 0x22: esc_tok = "\\\""; break; + case 0x5C: esc_tok = "\\\\"; break; + case '\b': esc_tok = "\\b"; break; + case '\f': esc_tok = "\\f"; break; + case '\n': esc_tok = "\\n"; break; + case '\r': esc_tok = "\\r"; break; + case '\t': esc_tok = "\\t"; break; + default: if (c <= 0x1F) { + static const char tohex[] = "0123456789abcdef"; + _esc_tok[4] = tohex[c >> 4]; + _esc_tok[5] = tohex[c & 0xF]; + _esc_tok[6] = 0; + esc_tok = _esc_tok; + } + } + if (esc_tok) { + int j; + for (j = 0; esc_tok[j]; j++) { + if (!esc_buf) /* count how many extra bytes are needed */ + continue; + *esc_buf++ = esc_tok[j]; + } + extra_bytes += j - 1; + } + else if (esc_buf) { + *esc_buf++ = c; + } + } + + if (*pos + len + extra_bytes > bufsize) { + *buf = '\0'; + return JSONB_ERROR_NOMEM; + } + + if (esc_buf) { + *pos += len + extra_bytes; + return JSONB_OK; + } + if (!extra_bytes) { + size_t j; + for (j = 0; j < len; ++j) + buf[*pos + j] = str[j]; + *pos += len; + return JSONB_OK; + } + esc_buf = buf + *pos; + extra_bytes = 0; + goto second_iter; +} + +static jsonbcode +_jsonb_escape_REALLOC(size_t *pos, + char *p_buf[], + size_t *p_bufsize, + unsigned offset, + const char str[], + size_t len) +{ + char *esc_tok = NULL, _esc_tok[8] = "\\u00"; + char *esc_buf = NULL; + int extra_bytes = 0; + size_t i; + + int second_pass = 0; + ptrdiff_t esc_buf_offset = 0; + + char *buf; + size_t bufsize; +restart: + buf = *p_buf + offset; + bufsize = *p_bufsize - offset; + + if (second_pass && esc_buf) esc_buf = buf + esc_buf_offset; + +second_iter: + /* 1st iteration, esc_buf is NULL and count extra_bytes needed for escaping + * 2st iteration, esc_buf is not NULL, and does escaping. */ + for (i = 0; i < len; ++i) { + unsigned char c = str[i]; + esc_tok = NULL; + switch (c) { case 0x22: esc_tok = "\\\""; break; + case 0x5C: esc_tok = "\\\\"; break; + case '\b': esc_tok = "\\b"; break; + case '\f': esc_tok = "\\f"; break; + case '\n': esc_tok = "\\n"; break; + case '\r': esc_tok = "\\r"; break; + case '\t': esc_tok = "\\t"; break; + default: if (c <= 0x1F) { + static const char tohex[] = "0123456789abcdef"; + _esc_tok[4] = tohex[c >> 4]; + _esc_tok[5] = tohex[c & 0xF]; + _esc_tok[6] = 0; + esc_tok = _esc_tok; + } + } + if (esc_tok) { + int j; + for (j = 0; esc_tok[j]; j++) { + if (!esc_buf) /* count how many extra bytes are needed */ + continue; + *esc_buf++ = esc_tok[j]; + } + extra_bytes += j - 1; + } + else if (esc_buf) { + *esc_buf++ = c; + } + } + + if (*pos + len + extra_bytes + 1 > bufsize) { + char *new_buf = NULL; + const size_t needed = *pos + len + extra_bytes + 1; + size_t new_size = *p_bufsize + (*p_bufsize >> 1); /* 1.5x */ + if (new_size < needed) new_size = needed; + if (new_size < *p_bufsize) return JSONB_ERROR_OVERFLOW; + new_buf = realloc(*p_buf, new_size); + if (!new_buf) return JSONB_ERROR_NOMEM; + if (esc_buf) esc_buf_offset = esc_buf - buf; + *p_buf = new_buf; + *p_bufsize = new_size; + second_pass = 1; + goto restart; + } + + if (esc_buf) { + *pos += len + extra_bytes; + return JSONB_OK; + } + if (!extra_bytes) { + size_t j; + for (j = 0; j < len; ++j) + buf[*pos + j] = str[j]; + *pos += len; + return JSONB_OK; + } + esc_buf = buf + *pos; + extra_bytes = 0; + goto second_iter; +} + +#define JSONB_KEY_EXEC(_type, buf, bufsize, key, len) \ + size_t pos = 0; \ + switch (*b->top) { \ + case JSONB_OBJECT_NEXT_KEY_OR_CLOSE: \ + BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \ + /* fall-through */ \ + case JSONB_OBJECT_KEY_OR_CLOSE: { \ + enum jsonbcode ret; \ + BUFFER_COPY_CHAR_##_type(b, '"', pos, buf, bufsize); \ + ret = _jsonb_escape_##_type(&pos, buf, bufsize, b->pos, key, len); \ + if (ret != JSONB_OK) return ret; \ + BUFFER_COPY_##_type(b, "\":", 2, pos, buf, bufsize); \ + STACK_HEAD(b, JSONB_OBJECT_VALUE); \ + } break; \ + default: \ + STACK_HEAD(b, JSONB_ERROR); \ + /* fall-through */ \ + case JSONB_DONE: \ + return JSONB_ERROR_INPUT; \ + } \ + b->pos += pos; \ + return JSONB_OK + +JSONB_API jsonbcode +jsonb_key(jsonb *b, char buf[], size_t bufsize, const char key[], size_t len) +{ + JSONB_KEY_EXEC(STATIC, buf, bufsize, key, len); +} + +JSONB_API jsonbcode +jsonb_key_auto( + jsonb *b, char *p_buf[], size_t *p_bufsize, const char key[], size_t len) +{ + JSONB_KEY_EXEC(REALLOC, p_buf, p_bufsize, key, len); +} + +#define JSONB_ARRAY_EXEC(_type, buf, bufsize) \ + enum jsonbstate new_state; \ + size_t pos = 0; \ + if (b->top - b->stack >= JSONB_MAX_DEPTH) return JSONB_ERROR_STACK; \ + switch (*b->top) { \ + case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \ + BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \ + /* fall-through */ \ + case JSONB_ARRAY_VALUE_OR_CLOSE: \ + new_state = JSONB_ARRAY_NEXT_VALUE_OR_CLOSE; \ + break; \ + case JSONB_OBJECT_VALUE: \ + new_state = JSONB_OBJECT_NEXT_KEY_OR_CLOSE; \ + break; \ + case JSONB_ARRAY_OR_OBJECT_OR_VALUE: \ + new_state = JSONB_DONE; \ + break; \ + default: \ + STACK_HEAD(b, JSONB_ERROR); \ + /* fall-through */ \ + case JSONB_DONE: \ + case JSONB_ERROR: \ + return JSONB_ERROR_INPUT; \ + } \ + BUFFER_COPY_CHAR_##_type(b, '[', pos, buf, bufsize); \ + STACK_HEAD(b, new_state); \ + STACK_PUSH(b, JSONB_ARRAY_VALUE_OR_CLOSE); \ + b->pos += pos; \ + return JSONB_OK + +JSONB_API jsonbcode +jsonb_array(jsonb *b, char buf[], size_t bufsize) +{ + JSONB_ARRAY_EXEC(STATIC, buf, bufsize); +} + +JSONB_API jsonbcode +jsonb_array_auto(jsonb *b, char *p_buf[], size_t *p_bufsize) +{ + JSONB_ARRAY_EXEC(REALLOC, p_buf, p_bufsize); +} + +#define JSONB_ARRAY_POP_EXEC(_type, buf, bufsize) \ + enum jsonbcode code; \ + size_t pos = 0; \ + switch (*b->top) { \ + case JSONB_ARRAY_VALUE_OR_CLOSE: \ + case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \ + code = b->stack == b->top - 1 ? JSONB_END : JSONB_OK; \ + break; \ + default: \ + STACK_HEAD(b, JSONB_ERROR); \ + /* fall-through */ \ + case JSONB_DONE: \ + case JSONB_ERROR: \ + return JSONB_ERROR_INPUT; \ + } \ + BUFFER_COPY_CHAR_##_type(b, ']', pos, buf, bufsize); \ + STACK_POP(b); \ + b->pos += pos; \ + return code + +JSONB_API jsonbcode +jsonb_array_pop(jsonb *b, char buf[], size_t bufsize) +{ + JSONB_ARRAY_POP_EXEC(STATIC, buf, bufsize); +} + +JSONB_API jsonbcode +jsonb_array_pop_auto(jsonb *b, char *p_buf[], size_t *p_bufsize) +{ + JSONB_ARRAY_POP_EXEC(REALLOC, p_buf, p_bufsize); +} + +#define JSONB_TOKEN_EXEC(_type, buf, bufsize, token, len) \ + enum jsonbstate next_state; \ + enum jsonbcode code; \ + size_t pos = 0; \ + switch (*b->top) { \ + case JSONB_ARRAY_OR_OBJECT_OR_VALUE: \ + next_state = JSONB_DONE; \ + code = JSONB_END; \ + break; \ + case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \ + BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \ + /* fall-through */ \ + case JSONB_ARRAY_VALUE_OR_CLOSE: \ + next_state = JSONB_ARRAY_NEXT_VALUE_OR_CLOSE; \ + code = JSONB_OK; \ + break; \ + case JSONB_OBJECT_VALUE: \ + next_state = JSONB_OBJECT_NEXT_KEY_OR_CLOSE; \ + code = JSONB_OK; \ + break; \ + default: \ + STACK_HEAD(b, JSONB_ERROR); \ + /* fall-through */ \ + case JSONB_DONE: \ + case JSONB_ERROR: \ + return JSONB_ERROR_INPUT; \ + } \ + BUFFER_COPY_##_type(b, token, len, pos, buf, bufsize); \ + STACK_HEAD(b, next_state); \ + b->pos += pos; \ + return code + +JSONB_API jsonbcode +jsonb_token( + jsonb *b, char buf[], size_t bufsize, const char token[], size_t len) +{ + JSONB_TOKEN_EXEC(STATIC, buf, bufsize, token, len); +} + +JSONB_API jsonbcode +jsonb_token_auto( + jsonb *b, char *p_buf[], size_t *p_bufsize, const char token[], size_t len) +{ + JSONB_TOKEN_EXEC(REALLOC, p_buf, p_bufsize, token, len); +} + +JSONB_API jsonbcode +jsonb_bool(jsonb *b, char buf[], size_t bufsize, int boolean) +{ + return boolean ? jsonb_token(b, buf, bufsize, "true", 4) + : jsonb_token(b, buf, bufsize, "false", 5); +} + +JSONB_API jsonbcode +jsonb_bool_auto(jsonb *b, char *p_buf[], size_t *p_bufsize, int boolean) +{ + return boolean ? jsonb_token_auto(b, p_buf, p_bufsize, "true", 4) + : jsonb_token_auto(b, p_buf, p_bufsize, "false", 5); +} + +JSONB_API jsonbcode +jsonb_null(jsonb *b, char buf[], size_t bufsize) +{ + return jsonb_token(b, buf, bufsize, "null", 4); +} + +JSONB_API jsonbcode +jsonb_null_auto(jsonb *b, char *p_buf[], size_t *p_bufsize) +{ + return jsonb_token_auto(b, p_buf, p_bufsize, "null", 4); +} + +#define JSONB_STRING_EXEC(_type, buf, bufsize, str, len) \ + enum jsonbstate next_state; \ + enum jsonbcode code, ret; \ + size_t pos = 0; \ + switch (*b->top) { \ + case JSONB_ARRAY_OR_OBJECT_OR_VALUE: \ + next_state = JSONB_DONE; \ + code = JSONB_END; \ + break; \ + case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: \ + BUFFER_COPY_CHAR_##_type(b, ',', pos, buf, bufsize); \ + /* fall-through */ \ + case JSONB_ARRAY_VALUE_OR_CLOSE: \ + next_state = JSONB_ARRAY_NEXT_VALUE_OR_CLOSE; \ + code = JSONB_OK; \ + break; \ + case JSONB_OBJECT_VALUE: \ + next_state = JSONB_OBJECT_NEXT_KEY_OR_CLOSE; \ + code = JSONB_OK; \ + break; \ + default: \ + STACK_HEAD(b, JSONB_ERROR); \ + /* fall-through */ \ + case JSONB_DONE: \ + case JSONB_ERROR: \ + return JSONB_ERROR_INPUT; \ + } \ + BUFFER_COPY_CHAR_##_type(b, '"', pos, buf, bufsize); \ + ret = _jsonb_escape_##_type(&pos, buf, bufsize, b->pos, str, len); \ + if (ret != JSONB_OK) return ret; \ + BUFFER_COPY_CHAR_##_type(b, '"', pos, buf, bufsize); \ + STACK_HEAD(b, next_state); \ + b->pos += pos; \ + return code + +JSONB_API jsonbcode +jsonb_string( + jsonb *b, char buf[], size_t bufsize, const char str[], size_t len) +{ + JSONB_STRING_EXEC(STATIC, buf, bufsize, str, len); +} + +JSONB_API jsonbcode +jsonb_string_auto( + jsonb *b, char *p_buf[], size_t *p_bufsize, const char str[], size_t len) +{ + JSONB_STRING_EXEC(REALLOC, p_buf, p_bufsize, str, len); +} + +JSONB_API jsonbcode +jsonb_number(jsonb *b, char buf[], size_t bufsize, double number) +{ + char token[32]; + const long len = sprintf(token, "%.17G", number); + return (len < 0) ? JSONB_ERROR_INPUT + : jsonb_token(b, buf, bufsize, token, len); +} + +JSONB_API jsonbcode +jsonb_number_auto(jsonb *b, char *p_buf[], size_t *p_bufsize, double number) +{ + char token[32]; + const long len = sprintf(token, "%.17G", number); + return (len < 0) ? JSONB_ERROR_INPUT + : jsonb_token_auto(b, p_buf, p_bufsize, token, len); +} + +#undef TRACE +#undef DECORATOR +#undef STACK_HEAD +#undef STACK_PUSH +#undef STACK_POP +#undef BUFFER_COPY_CHAR_STATIC +#undef BUFFER_COPY_STATIC +#undef BUFFER_COPY_CHAR_REALLOC +#undef BUFFER_COPY_REALLOC +#undef JSONB_OBJECT_EXEC +#undef JSONB_OBJECT_POP_EXEC +#undef JSONB_KEY_EXEC +#undef JSONB_ARRAY_EXEC +#undef JSONB_ARRAY_POP_EXEC +#undef JSONB_TOKEN_EXEC +#undef JSONB_STRING_EXEC + +#endif /* JSONB_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSON_BUILD_H */ diff --git a/include/concord/log.h b/include/concord/log.h new file mode 100644 index 0000000..72a0560 --- /dev/null +++ b/include/concord/log.h @@ -0,0 +1,46 @@ +/** + * @file log.h + * @author Cogmasters + * @brief Maintain support for log.c deprecated functions using logmod.h as a + * wrapper + * @attention This file is deprecated and will be removed in future releases + * @deprecated since v3.0.0 + */ + +#ifndef LOG_DEPRECATED_SUPPORT_H +#define LOG_DEPRECATED_SUPPORT_H + +#include "logmod.h" + +/** + * @brief Backwards compatible alias for logmod_log() + * @deprecated since v3.0.0 + */ +#define log_trace(...) logmod_log(TRACE, NULL, __VA_ARGS__) +/** + * @brief Backwards compatible alias for logmod_log() + * @deprecated since v3.0.0 + */ +#define log_debug(...) logmod_log(DEBUG, NULL, __VA_ARGS__) +/** + * @brief Backwards compatible alias for logmod_log() + * @deprecated since v3.0.0 + */ +#define log_info(...) logmod_log(INFO, NULL, __VA_ARGS__) +/** + * @brief Backwards compatible alias for logmod_log() + * @deprecated since v3.0.0 + */ +#define log_warn(...) logmod_log(WARN, NULL, __VA_ARGS__) +/** + * @brief Backwards compatible alias for logmod_log() + * @deprecated since v3.0.0 + */ +#define log_error(...) logmod_log(ERROR, NULL, __VA_ARGS__) +/** + * @brief Backwards compatible alias for logmod_log() + * @deprecated since v3.0.0 + */ +#define log_fatal(...) logmod_log(FATAL, NULL, __VA_ARGS__) + +#endif /* LOG_DEPRECATED_SUPPORT_H */ diff --git a/include/concord/logmod.h b/include/concord/logmod.h new file mode 100644 index 0000000..fa6a067 --- /dev/null +++ b/include/concord/logmod.h @@ -0,0 +1,1094 @@ +#ifndef LOGMOD_H +#define LOGMOD_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Define symbol visibility for LogMod API + * + * Define LOGMOD_STATIC to make all symbols static (for single-file inclusion) + */ +#ifdef LOGMOD_STATIC +#define LOGMOD_API static +#else +#define LOGMOD_API extern +#endif /* LOGMOD_STATIC */ + +#include +#include +#include + +/** + * @brief Format string checking attribute for printf-like functions + * + * This macro adds format string checking when supported by the compiler, + * helping catch format string errors at compile time. + * + * @param a Format string parameter position + * @param b First variadic argument position + */ +#if defined(__MINGW32__) \ + || (defined(__GNUC__) && __GNUC__ > 4 ? true : __GNUC_PATCHLEVEL__ >= 4) \ + || defined(__USE_MINGW_ANSI_STDIO) +#define LOGMOD_PRINTF_LIKE(a, b) __attribute__((format(gnu_printf, a, b))) +#else +#define LOGMOD_PRINTF_LIKE(a, b) +#endif + +/** + * @brief Default application ID for the fallback logger + * + * Can be overridden by defining this macro before including logmod.h + */ +#ifndef LOGMOD_FALLBACK_APPLICATION_ID +#define LOGMOD_FALLBACK_APPLICATION_ID "APPLICATION" +#endif /* LOGMOD_FALLBACK_APPLICATION_ID */ + +/** + * @brief Default context ID for the fallback logger + * + * Can be overridden by defining this macro before including logmod.h + */ +#ifndef LOGMOD_FALLBACK_CONTEXT_ID +#define LOGMOD_FALLBACK_CONTEXT_ID "GLOBAL" +#endif /* LOGMOD_FALLBACK_CONTEXT_ID */ + +/** + * @brief Log levels supported by LogMod + * + * Built-in log levels ordered by severity (lowest to highest) + * Custom log levels should start at LOGMOD_LEVEL_CUSTOM + */ +enum logmod_levels { + LOGMOD_LEVEL_TRACE = 0, /**< Most detailed logging for tracing execution */ + LOGMOD_LEVEL_DEBUG, /**< Debug information */ + LOGMOD_LEVEL_INFO, /**< Informational messages */ + LOGMOD_LEVEL_WARN, /**< Warning messages */ + LOGMOD_LEVEL_ERROR, /**< Error messages */ + LOGMOD_LEVEL_FATAL, /**< Fatal errors that cause termination */ + __LOGMOD_LEVEL_MAX, /**< Internal marker for built-in level count */ + /** user defined log level must start with this value */ + LOGMOD_LEVEL_CUSTOM = __LOGMOD_LEVEL_MAX +}; + +/** + * @brief Error codes returned by LogMod functions + */ +typedef enum { + LOGMOD_ERRNO = -2, /**< System error, check errno */ + LOGMOD_BAD_PARAMETER = -1, /**< Invalid parameter passed to function */ + LOGMOD_OK = 0, /**< Success */ + LOGMOD_OK_CONTINUE, /**< Success, continue with default behavior */ + LOGMOD_OK_SKIPPED /**< Success, logger has been skipped from logging due to + being disabled by user */ +} logmod_err; + +/** + * @brief Configuration options for a logger + */ +struct logmod_options { + FILE *logfile; /**< File to write logs to, or NULL for no file output */ + int quiet; /**< If 1, suppress console output */ + int color; /**< If 1, enable ANSI colors on console output */ + int hide_context_id; /**< If 1, suppress context ID in log messages */ + int show_application_id; /**< If 1, show application ID in log messages */ + unsigned level; /**< Minimum level to log (suppress messages below this) */ + int suppress_time; /**< If 1, suppress time in log messages */ + int hide_counter; /**< If 1, hide message counter in log messages */ +}; + +/** + * @brief Label properties for a log level + * + * @see @ref LOGMOD_LABEL_COLOR + */ +struct logmod_label { + const char *const name; /**< Display name of the log level */ + const char *const style; /**< ANSI style code for this label */ + const char *const visibility; /**< ANSI visibility code for this label */ + const char *const color; /**< ANSI color code for this label */ + const int output; /**< Output stream: 0 = stdout, 1 = stderr */ +}; + +/** + * @brief Helper macro to define label color, style, and visibility at once + * + * @param _style Style name (e.g., BOLD, REGULAR) + * @param _visibility Visibility name (e.g., FOREGROUND, BACKGROUND) + * @param _color Color name (e.g., RED, GREEN) + */ +#define LOGMOD_LABEL_COLOR(_style, _visibility, _color) \ + LOGMOD_STYLE_##_style, LOGMOD_VISIBILITY_##_visibility, \ + LOGMOD_COLOR_##_color + +/** + * @brief Information about a log entry + */ +struct logmod_info { + const unsigned line; /**< Source line number */ + const char *const filename; /**< Source filename */ + const unsigned level; /**< Log level */ + const struct logmod_label *const label; /**< Label for this log level */ + const struct tm time; /**< Time for when entry has been triggered */ +}; + +/* forward declaration */ +struct logmod_logger; +struct tm; +/**/ + +/** + * @brief Lock function type for thread safety + * + * @param logger The logger instance + * @param should_lock 1 to acquire lock, 0 to release + */ +typedef void (*logmod_lock)(const struct logmod_logger *logger, + int should_lock); + +/** + * @brief Callback function type for custom log handling + * + * @param logger The logger instance + * @param info Information about the log entry + * @param fmt Format string + * @param args Variable arguments list + * @return LOGMOD_OK to skip default handling, LOGMOD_OK_CONTINUE to also + * perform default handling + */ +typedef logmod_err (*logmod_callback)(const struct logmod_logger *logger, + const struct logmod_info *info, + const char *fmt, + va_list args); + +/** + * @brief ANSI text style values + */ +#define LOGMOD_STYLE_REGULAR "0" /**< Regular text style */ +#define LOGMOD_STYLE_BOLD "1" /**< Bold text style */ +#define LOGMOD_STYLE_UNDERLINE "4" /**< Underlined text style */ +#define LOGMOD_STYLE_STRIKETHROUGH "9" /**< Strikethrough text style */ + +/** + * @brief ANSI text visibility mode values + */ +#define LOGMOD_VISIBILITY_FOREGROUND "3" /**< Foreground color */ +#define LOGMOD_VISIBILITY_BACKGROUND "4" /**< Background color */ +#define LOGMOD_VISIBILITY_INTENSITY "9" /**< Intensity (bright) foreground */ +#define LOGMOD_VISIBILITY_BACKGROUND_INTENSITY \ + "10" /**< Intensity (bright) background */ + +/** + * @brief ANSI color values + */ +#define LOGMOD_COLOR_BLACK "0" /**< Black color */ +#define LOGMOD_COLOR_RED "1" /**< Red color */ +#define LOGMOD_COLOR_GREEN "2" /**< Green color */ +#define LOGMOD_COLOR_YELLOW "3" /**< Yellow color */ +#define LOGMOD_COLOR_BLUE "4" /**< Blue color */ +#define LOGMOD_COLOR_MAGENTA "5" /**< Magenta color */ +#define LOGMOD_COLOR_CYAN "6" /**< Cyan color */ +#define LOGMOD_COLOR_WHITE "7" /**< White color */ + +/** + * @brief Internal macro for defining logger attributes + * + * Used to define both const and non-const versions of the logger structure + * with the same fields. + * + * @param _qualifier Qualifier to apply to mutable fields (const or empty) + */ +#define __LOGMOD_LOGGER_ATTRS(_qualifier) \ + const char *context_id; \ + _qualifier struct logmod_options options; \ + const long *counter; \ + _qualifier logmod_callback callback; \ + void *user_data; \ + const struct logmod_label *_qualifier custom_labels; \ + _qualifier size_t num_custom_labels; \ + _qualifier int disabled + +#define __BLANK +/** + * @brief Mutable version of the logger structure + * + * This version allows modification of the logger properties + */ +struct logmod_mut_logger { + __LOGMOD_LOGGER_ATTRS(__BLANK); +}; +#undef __BLANK + +/** + * @brief Immutable logger structure + * + * This is the public-facing version of the logger with const-qualified fields + */ +struct logmod_logger { + __LOGMOD_LOGGER_ATTRS(const); +}; + +#undef __LOGMOD_LOGGER_ATTRS + +/** + * @brief Main logging context structure + * + * Contains the global state for a logging context + */ +struct logmod { + const char *application_id; /**< Application identifier */ + const struct logmod_logger *loggers; /**< Array of loggers */ + const size_t length; /**< Current number of loggers */ + const size_t real_length; /**< Maximum capacity of loggers array */ + long counter; /**< Global log message counter */ + const struct logmod_options + default_options; /**< Default options for new loggers */ + logmod_lock lock; /**< Lock function for thread safety */ +}; + +/** + * @brief Initialize the logging context + * + * @param logmod Pointer to the logging context structure + * @param application_id Application identifier string + * @param table Array to store loggers (must be pre-allocated) + * @param length Capacity of the table array + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_init(struct logmod *logmod, + const char *const application_id, + struct logmod_logger table[], + unsigned length); + +/** + * @brief Clean up the logging context + * + * @param logmod Pointer to the logging context structure + * @return LOGMOD_OK on success + */ +LOGMOD_API logmod_err logmod_cleanup(struct logmod *logmod); + +/** + * @brief Set the lock function for thread safety + * + * @param logmod Pointer to the logging context structure + * @param lock Lock function to use + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_set_lock(struct logmod *logmod, logmod_lock lock); + +/** + * @brief Set default options for all new loggers + * + * @param logmod Pointer to the logging context structure + * @param options Default options to use for new loggers + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_set_options(struct logmod *logmod, + struct logmod_options options); + +/** + * @brief Toggle a specific logger from logging (default is true) + * + * @param logmod Pointer to the logging context structure + * @param context_id The context id of the logger to be enabled or disabled + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_toggle_logger(struct logmod *logmod, + const char *const context_id); + +/** + * @brief Set user data for a logger + * + * @param logger Pointer to the logger + * @param user_data Pointer to user-defined data + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_data(struct logmod_logger *logger, + void *user_data); + +/** + * @brief Set callback function and custom labels for a logger + * + * @param logger Pointer to the logger + * @param custom_labels Array of custom log level labels or NULL + * @param num_custom_labels Number of custom labels in the array + * @param callback Callback function for log processing + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err +logmod_logger_set_callback(struct logmod_logger *logger, + const struct logmod_label *const custom_labels, + const size_t num_custom_labels, + logmod_callback callback); + +/** + * @brief Set all options for a logger at once + * + * @param logger Pointer to the logger + * @param options Options structure + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_options(struct logmod_logger *logger, + struct logmod_options options); + +/** + * @brief Set visibility of application ID and context ID in log messages + * + * @param logger Pointer to the logger + * @param show_app_id 1 to show application ID, 0 to hide + * @param show_context_id 1 to show context ID, 0 to hide + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_id_visibility( + struct logmod_logger *logger, int show_app_id, int show_context_id); + +/** + * @brief Set quiet mode for a logger + * + * @param logger Pointer to the logger + * @param quiet 1 to enable quiet mode (suppress console output), 0 to disable + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_quiet(struct logmod_logger *logger, + int quiet); + +/** + * @brief Set color mode for a logger + * + * @param logger Pointer to the logger + * @param color 1 to enable colored output, 0 to disable + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_color(struct logmod_logger *logger, + int color); + +/** + * @brief Set minimum log level for a logger + * + * @param logger Pointer to the logger + * @param level Minimum level to log (messages below this will be suppressed) + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_level(struct logmod_logger *logger, + unsigned level); + +/** + * @brief Set logfile for a logger + * + * @param logger Pointer to the logger + * @param logfile File pointer to write logs to, or NULL to disable + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_logfile(struct logmod_logger *logger, + FILE *logfile); + +/** + * @brief Set time display for a logger + * + * @param logger Pointer to the logger + * @param show_time 0 to suppress time in log messages, 1 to show it + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_time(struct logmod_logger *logger, + int show_time); + +/** + * @brief Set counter display for a logger + * + * @param logger Pointer to the logger + * @param show_counter 0 to hide counter in log messages, 1 to show it + * @return LOGMOD_OK on success, error code on failure + */ +LOGMOD_API logmod_err logmod_logger_set_counter(struct logmod_logger *logger, + int show_counter); + +/** + * @brief Get or create a logger by context ID + * + * @param logmod Pointer to the logging context + * @param context_id Context identifier string + * @return Pointer to the logger, or NULL if the table is full + */ +LOGMOD_API struct logmod_logger *logmod_get_logger( + struct logmod *logmod, const char *const context_id); + +/** + * @brief Get the label for a specific log level + * + * @param logger Pointer to the logger + * @param level Log level to get the label for + * @return Pointer to the label structure, or NULL if invalid + */ +LOGMOD_API const struct logmod_label *logmod_logger_get_label( + const struct logmod_logger *logger, const unsigned level); + +/** + * @brief Get the level value for a label name + * + * @param logger Pointer to the logger + * @param label Label name to look up + * @return Level value, or LOGMOD_BAD_PARAMETER if invalid + */ +LOGMOD_API long logmod_logger_get_level(const struct logmod_logger *logger, + const char *const label); + +/** + * @brief Get the global message counter for a logger + * + * @param logger Pointer to the logger + * @return Current counter value, or -1 if logger is NULL + */ +LOGMOD_API long logmod_logger_get_counter(const struct logmod_logger *logger); + +/** + * @brief Log a message (C89 compatible version) + * + * @param _level Log level (without LOGMOD_LEVEL_ prefix) + * @param _logger Logger to use, or NULL for default + * @param _parenthesized_params Format and arguments in parentheses + * @param num_params Number of arguments in the format string + */ +#define logmod_nlog(_level, _logger, _parenthesized_params, num_params) \ + _logmod_log(_logger, __LINE__, __FILE__, LOGMOD_LEVEL_##_level, \ + LOGMOD_SPREAD_TUPLE_##num_params _parenthesized_params) + +#if __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +/** + * @brief Internal helper macro for C99 variable arguments processing + * + * @param _level The log level + * @param _logger The logger instance + * @param _line Source line number + * @param _file Source file path + * @param _fmt Format string + * @param ... Additional format arguments + */ +#define _logmod_log_permissive(_level, _logger, _line, _file, _fmt, ...) \ + _logmod_log(_logger, _line, _file, _level, _fmt "%s", __VA_ARGS__) + +/** + * @brief Log a message with specified level (C99 version with variadic macro + * support) + * + * @param _level Log level (e.g., INFO, DEBUG, ERROR) + * @param _logger The logger instance or NULL for default logger + * @param ... Format string followed by format arguments + */ +#define logmod_log(_level, _logger, ...) \ + _logmod_log_permissive(LOGMOD_LEVEL_##_level, _logger, __LINE__, \ + __FILE__, __VA_ARGS__, "") +#else +/** + * @brief Alias to logmod_nlog for C89 compatibility + */ +#define logmod_log logmod_nlog +#endif /* __STDC_VERSION__ */ + +/** + * @brief Internal logging implementation function + * + * @param logger The logger instance + * @param line Source line number + * @param filename Source file path + * @param level Log level + * @param fmt Format string + * @param ... Format arguments + * @return logmod_err LOGMOD_OK on success, or error code on failure + */ +LOGMOD_API logmod_err _logmod_log(const struct logmod_logger *logger, + const unsigned line, + const char *const filename, + const unsigned level, + const char *fmt, + ...) LOGMOD_PRINTF_LIKE(5, 6); + +/** + * @brief Encodes text with ANSI colors and styles + * + * @param _style Style code (LOGMOD_STYLE_* macros) + * @param _visibility Visibility code (LOGMOD_VISIBILITY_* macros) + * @param _color Color code (LOGMOD_COLOR_* macros) + * @param buf Text buffer to encode + */ +#define LOGMOD_ENCODE(_style, _visibility, _color, buf) \ + "\x1b[" _style ";" _visibility _color "m" buf "\x1b[0m" + +/** + * @brief Static encoding of text with ANSI colors (compile-time) + * + * @param _style Style name (e.g., BOLD, REGULAR) + * @param _visibility Visibility name (e.g., FOREGROUND, BACKGROUND) + * @param _color Color name (e.g., RED, GREEN) + * @param buf The text to encode + */ +#define LOGMOD_ENCODE_STATIC(_style, _visibility, _color, buf) \ + LOGMOD_ENCODE(LOGMOD_STYLE_##_style, LOGMOD_VISIBILITY_##_visibility, \ + LOGMOD_COLOR_##_color, buf) + +/** + * @brief Dynamic encoding of text with ANSI colors (respects toggle) + * + * @param _toggle Toggle to enable or disable color encoding + * @param _style Style name (e.g., BOLD, REGULAR) + * @param _visibility Visibility name (e.g., FOREGROUND, BACKGROUND) + * @param _color Color name (e.g., RED, GREEN) + * @param buf The text to encode + * @return Colored text if toggle is enabled, or original text otherwise + */ +#define LOGMOD_ENCODE_TOGGLE(_toggle, _style, _visibility, _color, buf) \ + ((_toggle) ? LOGMOD_ENCODE_STATIC(_style, _visibility, _color, buf) : buf) + +/** + * @brief Dynamic encoding of text with ANSI colors (respects logger color + * setting) + * + * @param _logger The logger instance to check for color enabled setting + * @param _style Style name (e.g., BOLD, REGULAR) + * @param _visibility Visibility name (e.g., FOREGROUND, BACKGROUND) + * @param _color Color name (e.g., RED, GREEN) + * @param buf The text to encode + * @return Colored text if colors enabled, or original text otherwise + */ +#define LOGMOD_ENCODE_LOGGER(_logger, _style, _visibility, _color, buf) \ + LOGMOD_ENCODE_TOGGLE((_logger)->options.color, _style, _visibility, \ + _color, buf) + +/** @brief Shorthand for LOGMOD_ENCODE_STATIC */ +#define LMS LOGMOD_ENCODE_STATIC +/** @brief Shorthand for LOGMOD_ENCODE_TOGGLE */ +#define LMT LOGMOD_ENCODE_TOGGLE +/** @brief Shorthand for LOGMOD_ENCODE_LOGGER */ +#define LML LOGMOD_ENCODE_LOGGER +/** @brief Shorthand for LOGMOD_ENCODE */ +#define LME LOGMOD_ENCODE + +#define LOGMOD_SPREAD_TUPLE_0(_fmt) _fmt +#define LOGMOD_SPREAD_TUPLE_1(_fmt, _1) _fmt, _1 +#define LOGMOD_SPREAD_TUPLE_2(_fmt, _1, _2) _fmt, _1, _2 +#define LOGMOD_SPREAD_TUPLE_3(_fmt, _1, _2, _3) _fmt, _1, _2, _3 +#define LOGMOD_SPREAD_TUPLE_4(_fmt, _1, _2, _3, _4) _fmt, _2, _3, _4 +#define LOGMOD_SPREAD_TUPLE_5(_fmt, _1, _2, _3, _4, _5) \ + _fmt, _1, _2, _3, _4, _5 +#define LOGMOD_SPREAD_TUPLE_6(_fmt, _1, _2, _3, _4, _5, _6) \ + _fmt, _1, _2, _3, _4, _6 +#define LOGMOD_SPREAD_TUPLE_7(_fmt, _1, _2, _3, _4, _5, _6, _7) \ + _fmt, _1, _2, _3, _4, _5, _6, _7 +#define LOGMOD_SPREAD_TUPLE_8(_fmt, _1, _2, _3, _4, _5, _6, _7, _8) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8 +#define LOGMOD_SPREAD_TUPLE_9(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9 +#define LOGMOD_SPREAD_TUPLE_10(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10 +#define LOGMOD_SPREAD_TUPLE_11(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11 +#define LOGMOD_SPREAD_TUPLE_12(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12 +#define LOGMOD_SPREAD_TUPLE_13(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13 +#define LOGMOD_SPREAD_TUPLE_14(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14 +#define LOGMOD_SPREAD_TUPLE_15(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15 +#define LOGMOD_SPREAD_TUPLE_16(_fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16) \ + _fmt, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16 + +#ifndef LOGMOD_HEADER + +#include +#include +#include +#include +#include +#include + +#include "logmod.h" + +static const struct logmod_label default_labels[__LOGMOD_LEVEL_MAX] = { + /*[LOGMOD_LEVEL_TRACE]:*/ + { "TRACE", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND_INTENSITY, BLUE), 0 }, + /*[LOGMOD_LEVEL_DEBUG]:*/ + { "DEBUG", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, CYAN), 0 }, + /*[LOGMOD_LEVEL_INFO]: */ + { "INFO", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, GREEN), 0 }, + /*[LOGMOD_LEVEL_WARN]: */ + { "WARN", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, YELLOW), 1 }, + /*[LOGMOD_LEVEL_ERROR]:*/ + { "ERROR", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, RED), 1 }, + /*[LOGMOD_LEVEL_FATAL]:*/ + { "FATAL", LOGMOD_LABEL_COLOR(REGULAR, BACKGROUND, MAGENTA), 1 }, +}; + +/** @brief Get @ref logmod from any @ref logmod_logger */ +#define LOGMOD_FROM_LOGGER(_ctx) \ + ((struct logmod *)((char *)(_ctx)->counter \ + - offsetof(struct logmod, counter))) + +#define _LOGMOD_EXPECT(_cond, _error, _return) \ + do { \ + if (!(_cond)) { \ + switch ((_error)) { \ + case LOGMOD_BAD_PARAMETER: \ + logmod_nlog(ERROR, NULL, ("Bad parameter: %s", #_cond), 1); \ + break; \ + case LOGMOD_ERRNO: \ + logmod_nlog(ERROR, NULL, \ + ("System error (check errno): %s", #_cond), 1); \ + break; \ + default: \ + logmod_nlog(ERROR, NULL, ("Unknown error: %s", #_cond), 1); \ + break; \ + } \ + return (_return); \ + } \ + } while (0) + +/** + * @brief Check if a condition is met and log an error message if not + * + * @param _cond Condition to check + * @param _error Error code to return if the condition is false + */ +#define LOGMOD_EXPECT(_cond, _error) _LOGMOD_EXPECT(_cond, _error, _error) + +static void +_logmod_lock_noop(const struct logmod_logger *_, int __) +{ + (void)_; + (void)__; +} + +LOGMOD_API logmod_err +logmod_init(struct logmod *logmod, + const char *const application_id, + struct logmod_logger table[], + unsigned length) +{ + size_t *mut_real_length = (size_t *)&logmod->real_length; + LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER); + LOGMOD_EXPECT(application_id && *application_id, LOGMOD_BAD_PARAMETER); + LOGMOD_EXPECT(table != NULL, LOGMOD_BAD_PARAMETER); + LOGMOD_EXPECT(length > 0, LOGMOD_BAD_PARAMETER); + memset(logmod, 0, sizeof *logmod); + memset(table, 0, length * sizeof *table); + logmod->application_id = application_id; + logmod->loggers = table; + *mut_real_length = length; + logmod->lock = _logmod_lock_noop; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_cleanup(struct logmod *logmod) +{ + memset((void *)logmod->loggers, 0, + logmod->real_length * sizeof *logmod->loggers); + memset(logmod, 0, sizeof *logmod); + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_set_lock(struct logmod *logmod, logmod_lock lock) +{ + LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER); + logmod->lock = lock; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_set_options(struct logmod *logmod, struct logmod_options options) +{ + struct logmod_options *mut_default_options = + (struct logmod_options *)&logmod->default_options; + LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER); + *mut_default_options = options; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_toggle_logger(struct logmod *logmod, const char *const context_id) +{ + struct logmod_mut_logger *mut_logger = + (struct logmod_mut_logger *)logmod_get_logger(logmod, context_id); + LOGMOD_EXPECT(mut_logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->disabled = !mut_logger->disabled; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_id_visibility(struct logmod_logger *logger, + int show_app_id, + int show_context_id) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->options.show_application_id = show_app_id; + mut_logger->options.hide_context_id = !show_context_id; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_data(struct logmod_logger *logger, void *user_data) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->user_data = user_data; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_callback(struct logmod_logger *logger, + const struct logmod_label *const custom_labels, + const size_t num_custom_labels, + logmod_callback callback) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->callback = callback; + if (custom_labels != NULL) { + LOGMOD_EXPECT(num_custom_labels > 0, LOGMOD_BAD_PARAMETER); + mut_logger->custom_labels = custom_labels; + mut_logger->num_custom_labels = num_custom_labels; + } + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_options(struct logmod_logger *logger, + struct logmod_options options) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->options = options; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_quiet(struct logmod_logger *logger, int quiet) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->options.quiet = quiet; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_color(struct logmod_logger *logger, int color) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->options.color = color; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_level(struct logmod_logger *logger, unsigned level) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->options.level = level; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_logfile(struct logmod_logger *logger, FILE *logfile) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->options.logfile = logfile; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_time(struct logmod_logger *logger, int show_time) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->options.suppress_time = !show_time; + return LOGMOD_OK; +} + +LOGMOD_API logmod_err +logmod_logger_set_counter(struct logmod_logger *logger, int show_counter) +{ + struct logmod_mut_logger *mut_logger = (struct logmod_mut_logger *)logger; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + mut_logger->options.hide_counter = !show_counter; + return LOGMOD_OK; +} + +LOGMOD_API const struct logmod_label * +logmod_logger_get_label(const struct logmod_logger *logger, + const unsigned level) +{ + static const struct logmod_label unknown_label = { + "#UNKNOWN LABEL!!", + LOGMOD_LABEL_COLOR(STRIKETHROUGH, BACKGROUND_INTENSITY, YELLOW), 1 + }; + if (level < LOGMOD_LEVEL_CUSTOM) { + return &default_labels[level]; + } + if (logger && level >= LOGMOD_LEVEL_CUSTOM + && level < (LOGMOD_LEVEL_CUSTOM + logger->num_custom_labels)) + { + return &logger->custom_labels[level - LOGMOD_LEVEL_CUSTOM]; + } + logmod_nlog(ERROR, NULL, + ("Invalid log level %u for logger %s", level, + logger ? logger->context_id : "NULL"), + 2); + return &unknown_label; +} + +LOGMOD_API long +logmod_logger_get_level(const struct logmod_logger *logger, + const char *const label) +{ + size_t i; + LOGMOD_EXPECT(logger != NULL, LOGMOD_BAD_PARAMETER); + LOGMOD_EXPECT(label != NULL, LOGMOD_BAD_PARAMETER); + for (i = 0; i < __LOGMOD_LEVEL_MAX; ++i) { + if (strcmp(label, default_labels[i].name) == 0) { + return (long)i; + } + } + for (i = 0; i < logger->num_custom_labels; ++i) { + if (strcmp(label, logger->custom_labels[i].name) == 0) { + return (long)(i + LOGMOD_LEVEL_CUSTOM); + } + } + return LOGMOD_BAD_PARAMETER; +} + +LOGMOD_API long +logmod_logger_get_counter(const struct logmod_logger *logger) +{ + struct logmod *logmod = LOGMOD_FROM_LOGGER(logger); + long counter; + LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER); + logmod->lock(logger, 1); + counter = logmod->counter; + logmod->lock(logger, 0); + return counter; +} + +LOGMOD_API struct logmod_logger * +logmod_get_logger(struct logmod *logmod, const char *const context_id) +{ + size_t i; + _LOGMOD_EXPECT(logmod != NULL, LOGMOD_BAD_PARAMETER, NULL); + _LOGMOD_EXPECT(logmod->loggers != NULL, LOGMOD_BAD_PARAMETER, NULL); + _LOGMOD_EXPECT(context_id != NULL, LOGMOD_BAD_PARAMETER, NULL); + logmod->lock(NULL, 1); + for (i = 0; i < logmod->length; ++i) { + if (0 == strcmp(logmod->loggers[i].context_id, context_id)) { + struct logmod_logger *logger = + (struct logmod_logger *)&logmod->loggers[i]; + logmod->lock(logger, 0); + return logger; + } + } + if (logmod->length < logmod->real_length) { + size_t *mut_length = (size_t *)&logmod->length; + struct logmod_mut_logger *mut_logger = + (struct logmod_mut_logger *)&logmod->loggers[logmod->length]; + memset(mut_logger, 0, sizeof *mut_logger); + mut_logger->context_id = context_id; + mut_logger->counter = &logmod->counter; + mut_logger->options = logmod->default_options; + ++*mut_length; + logmod->lock((struct logmod_logger *)mut_logger, 0); + return (struct logmod_logger *)mut_logger; + } + logmod->lock(NULL, 0); + return NULL; +} + +static logmod_err +_logmod_print(const struct logmod_logger *logger, + const struct logmod_info *info, + const char *fmt, + va_list args, + const int color, + FILE *output) +{ + if (!logger->options.hide_counter) { + LOGMOD_EXPECT(fprintf(output, + LMT(color, BOLD, FOREGROUND, WHITE, "%-3ld "), + logmod_logger_get_counter(logger)) + >= 0, + LOGMOD_ERRNO); + } + if (!logger->options.suppress_time) { + LOGMOD_EXPECT( + fprintf(output, + LMT(color, UNDERLINE, FOREGROUND, WHITE, "%02d:%02d:%02d"), + info->time.tm_hour, info->time.tm_min, info->time.tm_sec) + >= 0, + LOGMOD_ERRNO); + LOGMOD_EXPECT(putc(' ', output) != EOF, LOGMOD_ERRNO); + } + if (logger->options.show_application_id) { + const struct logmod *logmod = LOGMOD_FROM_LOGGER(logger); + LOGMOD_EXPECT(fprintf(output, + LMT(color, BOLD, FOREGROUND, BLACK, "%s"), + logmod->application_id) + >= 0, + LOGMOD_ERRNO); + LOGMOD_EXPECT(fputs(LMT(color, BOLD, FOREGROUND, BLACK, " » "), output) + != EOF, + LOGMOD_ERRNO); + } + if (!logger->options.hide_context_id) { + LOGMOD_EXPECT(fprintf(output, + LMT(color, BOLD, FOREGROUND, WHITE, "%s"), + logger->context_id) + >= 0, + LOGMOD_ERRNO); + LOGMOD_EXPECT(fputs(LMT(color, BOLD, FOREGROUND, WHITE, " » "), output) + != EOF, + LOGMOD_ERRNO); + } + if (color) { + LOGMOD_EXPECT(fprintf(output, + LME("%s", "%s", "%s", "%s") " " LMS( + REGULAR, FOREGROUND, YELLOW, + "%s") LMS(BOLD, FOREGROUND, WHITE, ":") + LMS(REGULAR, FOREGROUND, WHITE, "%d") ": ", + info->label->style, info->label->visibility, + info->label->color, info->label->name, + info->filename, info->line) + >= 0, + LOGMOD_ERRNO); + } + else { + LOGMOD_EXPECT(fprintf(output, "%s %s:%d: ", info->label->name, + info->filename, info->line) + >= 0, + LOGMOD_ERRNO); + } + LOGMOD_EXPECT(vfprintf(output, fmt, args) >= 0, LOGMOD_ERRNO); + LOGMOD_EXPECT(putc('\n', output) != EOF, LOGMOD_ERRNO); + LOGMOD_EXPECT(fflush(output) != EOF, LOGMOD_ERRNO); + return LOGMOD_OK; +} + +static struct logmod g_logmod; + +/** global logger used as a fallback */ +static struct logmod_logger g_loggers[] = { + { + LOGMOD_FALLBACK_CONTEXT_ID, + { NULL, 0, 1 }, + &g_logmod.counter, + NULL, + NULL, + default_labels, + 0, + }, +}; +/** global logmod used as a fallback */ +static struct logmod g_logmod = { + LOGMOD_FALLBACK_APPLICATION_ID, + g_loggers, + sizeof(g_loggers) / sizeof *g_loggers, + sizeof(g_loggers) / sizeof *g_loggers, + 0, + { NULL, 0, 1 }, + _logmod_lock_noop, +}; + +static struct logmod_info +_logmod_info_populate(const struct logmod_logger *logger, + const unsigned line, + const char *const filename, + const unsigned level) +{ + const struct logmod *logmod = LOGMOD_FROM_LOGGER(logger); + const time_t time_raw = time(NULL); + struct logmod_info info; + unsigned *mut_line = (unsigned *)&info.line; + const char **mut_filename = (const char **)&info.filename; + unsigned *mut_level = (unsigned *)&info.level; + const struct logmod_label **mut_label = + (const struct logmod_label **)&info.label; + struct tm *mut_time = (struct tm *)&info.time; + *mut_line = line; + *mut_filename = filename; + *mut_level = level; + *mut_label = logmod_logger_get_label(logger, level); + logmod->lock(logger, 1); + *mut_time = *localtime(&time_raw); + logmod->lock(logger, 0); + return info; +} + +LOGMOD_API logmod_err +_logmod_log(const struct logmod_logger *logger, + const unsigned line, + const char *const filename, + const unsigned level, + const char *fmt, + ...) +{ + struct logmod *logmod = + LOGMOD_FROM_LOGGER(!logger ? (logger = &g_loggers[0]) : logger); + logmod_err code = LOGMOD_OK_SKIPPED; + if (!logger->disabled) { + const struct logmod_info info = + _logmod_info_populate(logger, line, filename, level); + va_list args; + code = LOGMOD_OK_CONTINUE; + if (logger->callback) { + va_start(args, fmt); + if ((code = logger->callback(logger, &info, fmt, args)) + < LOGMOD_OK) + { + goto _end; + } + va_end(args); + } + if (level >= logger->options.level && code == LOGMOD_OK_CONTINUE) { + if (!logger->options.quiet || level == LOGMOD_LEVEL_FATAL) { + va_start(args, fmt); + if ((code = _logmod_print( + logger, &info, fmt, args, logger->options.color, + info.label->output == 0 ? stdout : stderr)) + != LOGMOD_OK) + { + goto _end; + } + va_end(args); + } + if (logger->options.logfile) { + va_start(args, fmt); + code = _logmod_print(logger, &info, fmt, args, 0, + logger->options.logfile); + } + } + _end: + logmod->lock(logger, 1); + ++logmod->counter; + logmod->lock(logger, 0); + va_end(args); + } + return code; +} + +#undef LOGMOD_EXPECT + +#endif /* LOGMOD_HEADER */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LOGMOD_H */ diff --git a/include/concord/oa_hash.h b/include/concord/oa_hash.h new file mode 100644 index 0000000..bfac673 --- /dev/null +++ b/include/concord/oa_hash.h @@ -0,0 +1,345 @@ +#ifndef OA_HASH_H +#define OA_HASH_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef OA_HASH_STATIC +#define OA_HASH_API static +#else +#define OA_HASH_API extern +#endif /* OA_HASH_STATIC */ + +#include + +/** @brief Hash table entry state */ +enum oa_hash_entry_state { + OA_HASH_ENTRY_EMPTY = 0, /**< empty entry */ + OA_HASH_ENTRY_OCCUPIED, /**< occupied entry */ + OA_HASH_ENTRY_DELETED /**< deleted entry */ +}; + +/** @brief Entry holding key-value pair in hash table */ +struct oa_hash_entry { + enum oa_hash_entry_state state; /**< entry state */ + struct { + const char *buf; /**< key buffer */ + size_t length; /**< key length */ + } key; + void *value; /**< value pointer */ +}; + +#define __OA_HASH_ATTRS_const \ + const size_t length; /**< amount of entries */ \ + const size_t capacity; /**< total buckets capacity */ \ + const struct oa_hash_entry *buckets /**< entries array */ +#define __OA_HASH_ATTRS_mut \ + size_t length; /**< amount of entries */ \ + size_t capacity; /**< total buckets capacity */ \ + struct oa_hash_entry *buckets /**< entries array */ +/** @brief can be used to cast to struct oa_hash */ +#define OA_HASH_ATTRS(_qualifier) __OA_HASH_ATTRS_##_qualifier + +/** @brief Open addressing hash table */ +struct oa_hash { + OA_HASH_ATTRS(mut); +}; + +/** + * @brief Initialize hash table with given buckets array + * + * @param[out] ht the hash table to be initialized + * @param[out] buckets pre-allocated array of entries + * @param[in] capacity amount of buckets + */ +OA_HASH_API void oa_hash_init(struct oa_hash *ht, + struct oa_hash_entry buckets[], + const size_t capacity); + +/** + * @brief Clean up hash table entries and struct + * + * @param[out] ht the hash table to be cleaned + */ +OA_HASH_API void oa_hash_cleanup(struct oa_hash *ht); + +/** + * @brief Retrieve entry by key + * + * @param[in] ht the hash table + * @param[in] key the key to search for + * @param[in] len the key length + * @return entry if found, NULL otherwise + */ +OA_HASH_API const struct oa_hash_entry *oa_hash_get_entry( + const struct oa_hash *ht, const char key[], const size_t len); + +/** + * @brief Retrieve value by key (wrapper around oa_hash_get_entry) + * + * @param[in] ht the hash table + * @param[in] key the key to search for + * @param[in] len the key length + * @return value if found, NULL otherwise + */ +OA_HASH_API void *oa_hash_get(const struct oa_hash *ht, + const char key[], + const size_t len); + +/** + * @brief Insert or update entry + * + * @param[in,out] ht the hash table + * @param[in] key the key to insert/update + * @param[in] len the key length + * @param[in] value the value to be assigned + * @return entry if successful, or NULL if no space left, in which case + * oa_hash_rehash() should be called + */ +OA_HASH_API const struct oa_hash_entry *oa_hash_set_entry(struct oa_hash *ht, + const char key[], + const size_t len, + void *value); + +/** + * @brief Insert or update entry (wrapper around oa_hash_set_entry) + * + * @param[in,out] ht the hash table + * @param[in] key the key to insert/update + * @param[in] len the key length + * @param[in] value the value to be assigned + * @return value if successful, or NULL if no space left, in which case + * oa_hash_rehash() should be called + */ +OA_HASH_API void *oa_hash_set(struct oa_hash *ht, + const char key[], + const size_t len, + void *value); + +/** + * @brief Remove entry by key + * + * @param[in,out] ht the hash table + * @param[in] key the key to be removed + * @param[in] len the key length + * @return 1 if found and removed, 0 otherwise + */ +OA_HASH_API int oa_hash_remove(struct oa_hash *ht, + const char key[], + const size_t len); + +/** + * @brief Rehash table to new buckets array + * + * @param[in,out] ht the hash table + * @param[in,out] new_buckets the new buckets array + * @param[in] new_capacity the new buckets capacity + * @return pointer to old (now unused) bucket if successful, or NULL otherwise + */ +OA_HASH_API struct oa_hash_entry *oa_hash_rehash( + struct oa_hash *ht, + struct oa_hash_entry new_buckets[], + const size_t new_capacity); + +#ifndef OA_HASH_HEADER + +#include +#include + +OA_HASH_API void +oa_hash_init(struct oa_hash *ht, + struct oa_hash_entry buckets[], + const size_t capacity) +{ + memset(buckets, 0, sizeof(struct oa_hash_entry) * capacity); + ht->buckets = buckets; + ht->length = 0; + ht->capacity = capacity; +} + +OA_HASH_API void +oa_hash_cleanup(struct oa_hash *ht) +{ + if (!ht) return; + + ht->length = 0; + ht->capacity = 0; + ht->buckets = NULL; +} + +static size_t +_oa_hash_genhash(const char key[], size_t len, const size_t capacity) +{ + const unsigned char *str = (const unsigned char *)key; + unsigned long hash = 5381; /* DJB2 initial value */ + + if (!key || !capacity) return 0; + + while (len--) { + hash = ((hash & 0x7fffffff) << 5) + hash + *str++; + } + return hash % capacity; +} + +OA_HASH_API const struct oa_hash_entry * +oa_hash_get_entry(const struct oa_hash *ht, const char key[], const size_t len) +{ + const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity); + size_t slot = start_slot; + + if (!len || !ht->capacity) return NULL; + + do { + struct oa_hash_entry *entry = &ht->buckets[slot]; + + if (entry->state == OA_HASH_ENTRY_EMPTY) { + return NULL; + } + + if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length + && 0 == memcmp(entry->key.buf, key, len)) + { + return entry; + } + + slot = (slot + 1) % ht->capacity; + } while (slot != start_slot); + + return NULL; +} + +OA_HASH_API void * +oa_hash_get(const struct oa_hash *ht, const char key[], const size_t len) +{ + const struct oa_hash_entry *entry = oa_hash_get_entry(ht, key, len); + return entry ? entry->value : NULL; +} + +OA_HASH_API const struct oa_hash_entry * +oa_hash_set_entry(struct oa_hash *ht, + const char key[], + const size_t len, + void *value) +{ + const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity); + size_t slot = start_slot; + size_t first_deleted = SIZE_MAX; + + if (!len || !ht->capacity) return NULL; + + do { + struct oa_hash_entry *entry = &ht->buckets[slot]; + + if (entry->state != OA_HASH_ENTRY_OCCUPIED) { + if (first_deleted == SIZE_MAX + && entry->state == OA_HASH_ENTRY_DELETED) + { + first_deleted = slot; + } + if (entry->state == OA_HASH_ENTRY_EMPTY) { + slot = (first_deleted != SIZE_MAX) ? first_deleted : slot; + entry = &ht->buckets[slot]; + entry->key.buf = (char *)key; + entry->key.length = len; + entry->value = value; + entry->state = OA_HASH_ENTRY_OCCUPIED; + ht->length++; + return entry; + } + } + + if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length + && 0 == memcmp(entry->key.buf, key, len)) + { + entry->value = value; + return entry; + } + + slot = (slot + 1) % ht->capacity; + } while (slot != start_slot); + + return NULL; +} + +OA_HASH_API void * +oa_hash_set(struct oa_hash *ht, + const char key[], + const size_t len, + void *value) +{ + const struct oa_hash_entry *entry = oa_hash_set_entry(ht, key, len, value); + return entry ? entry->value : NULL; +} + +OA_HASH_API int +oa_hash_remove(struct oa_hash *ht, const char key[], const size_t len) +{ + const size_t start_slot = _oa_hash_genhash(key, len, ht->capacity); + size_t slot = start_slot; + + if (!len || !ht->capacity) return 0; + + do { + struct oa_hash_entry *entry = &ht->buckets[slot]; + + if (entry->state == OA_HASH_ENTRY_EMPTY) { + return 0; + } + + if (entry->state == OA_HASH_ENTRY_OCCUPIED && len == entry->key.length + && 0 == memcmp(entry->key.buf, key, len)) + { + entry->state = OA_HASH_ENTRY_DELETED; + ht->length--; + return 1; + } + + slot = (slot + 1) % ht->capacity; + } while (slot != start_slot); + + return 0; +} + +OA_HASH_API struct oa_hash_entry * +oa_hash_rehash(struct oa_hash *ht, + struct oa_hash_entry new_buckets[], + const size_t new_capacity) +{ + struct oa_hash_entry *old_buckets = ht->buckets; + const size_t old_capacity = ht->capacity; + const size_t old_length = ht->length; + size_t i; + + if (!new_buckets || new_capacity <= old_capacity) return 0; + + memset(new_buckets, 0, sizeof(struct oa_hash_entry) * new_capacity); + + /* temporarily switch to new buckets */ + ht->buckets = new_buckets; + ht->capacity = new_capacity; + ht->length = 0; + + for (i = 0; i < old_capacity; ++i) { + if (old_buckets[i].state == OA_HASH_ENTRY_OCCUPIED + && !oa_hash_set_entry(ht, old_buckets[i].key.buf, + old_buckets[i].key.length, + old_buckets[i].value)) + { + /* restore original state on failure */ + ht->buckets = old_buckets; + ht->capacity = old_capacity; + ht->length = old_length; + return NULL; + } + } + return old_buckets; +} + +#endif /* OA_HASH_HEADER */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* OA_HASH_H */ diff --git a/include/concord/oauth2.h b/include/concord/oauth2.h new file mode 100644 index 0000000..991f709 --- /dev/null +++ b/include/concord/oauth2.h @@ -0,0 +1,38 @@ +/** + * @file oauth2.h + * @author Cogmasters + * @brief OAuth2 public functions and datatypes + */ + +#ifndef DISCORD_OAUTH2_H +#define DISCORD_OAUTH2_H + +/** @defgroup DiscordAPIOAuth2 OAuth2 + * @ingroup DiscordAPI + * @brief OAuth2's public API supported by Concord + * @{ */ + +/** + * @brief Returns the bot's application object + * + * @param client the client created with discord_from_token() + * @CCORD_ret_obj{ret,application} + * @CCORD_return + */ +CCORDcode discord_get_current_bot_application_information( + struct discord *client, struct discord_ret_application *ret); + +/** + * @brief Returns info about the current authorization + * @note Requires authentication with a bearer token + * + * @param client the client created with discord_from_token() + * @CCORD_ret_obj{ret,auth_response} + * @CCORD_return + */ +CCORDcode discord_get_current_authorization_information( + struct discord *client, struct discord_ret_auth_response *ret); + +/** @} DiscordAPIOAuth2 */ + +#endif /* DISCORD_OAUTH2_H */ diff --git a/include/concord/osname.h b/include/concord/osname.h new file mode 100644 index 0000000..fcb4df0 --- /dev/null +++ b/include/concord/osname.h @@ -0,0 +1,319 @@ +#ifndef OSNAME_H +#define OSNAME_H 1 + +/* + * HackerSmacker's "Detect-It-All" OS Detector + */ + +enum OSClass { + UNIX, + WINDOWS, + DOS, + OS2, + S370, + DEC, + MACINTOSH, + AMIGA, + OTHER +}; + +#ifdef _AIX +#define OSNAME "AIX" +#define OSCLASS UNIX +#endif + +#ifdef __ANDROID__ +#define OSNAME "Android" +#define OSCLASS UNIX +#endif + +#ifdef UTS +#define OSNAME "UTS" +#define OSCLASS UNIX +#endif + +#ifdef aegis +#define OSNAME "Aegis" +#define OSCLASS UNIX +#endif + +#ifdef __BEOS__ +#define OSNAME "BeOS" +#define OSCLASS OTHER +#endif + +#ifdef __FreeBSD__ +#define OSNAME "FreeBSD" +#define OSCLASS UNIX +#endif + +#ifdef __NetBSD__ +#define OSNAME "NetBSD" +#define OSCLASS UNIX +#endif + +#ifdef __OpenBSD__ +#define OSNAME "OpenBSD" +#define OSCLASS UNIX +#endif + +#ifdef __bsdi__ +#define OSNAME "BSD/OS" +#define OSCLASS UNIX +#endif + +#ifdef __DragonFly__ +#define OSNAME "DragonFly BSD" +#define OSCLASS UNIX +#endif + +#ifdef __convex__ +#define OSNAME "ConvexOS" +#define OSCLASS UNIX +#endif + +#ifdef __CYGWIN__ +#define OSNAME "Windows NT (Cygwin)" +#define OSCLASS UNIX +#endif + +#if defined __DGUX__ || DGUX +#define OSNAME "DG/UX" +#define OSCLASS UNIX +#endif + +#if defined __SEQUENT__ || sequent +#define OSNAME "DYNIX/ptx" +#define OSCLASS UNIX +#endif + +#ifdef __ECOS +#define OSNAME "eCos" +#define OSCLASS OTHER +#endif + +#ifdef __EMX__ +#define OSNAME "OS/2 (EMX)" +#define OSCLASS UNIX +#endif + +#ifdef __gnu_hurd__ +#define OSNAME "GNU/Hurd" +#define OSCLASS UNIX +#endif + +#if defined __gnu_linux__ || defined __linux__ || defined linux +#define OSNAME "GNU/Linux" +#define OSCLASS UNIX +#endif + +#if defined _hpux || defined hpux || defined __hpux +#define OSNAME "HP-UX" +#define OSCLASS UNIX +#endif + +#ifdef __OS400__ +#define OSNAME "OS/400" +#define OSCLASS OTHER +#endif + +#if defined __sgi || defined sgi +#define OSNAME "IRIX" +#define OSCLASS UNIX +#endif + +#ifdef __INTEGRITY +#define OSNAME "INTEGRITY" +#define OSCLASS OTHER +#endif + +#ifdef __Lynx__ +#define OSNAME "LynxOS" +#define OSCLASS OTHER +#endif + +#if defined macintosh || defined Macintosh +#define OSNAME "Classic Mac OS" +#define OSTYPE MACINTOSH +#endif + +#ifdef __APPLE__ +#ifdef __MACH +#define OSNAME "Mac OS X" +#define OSCLASS UNIX +#endif +#endif + +#if defined __OS9000 || defined _OSK +#define OSNAME "OS-9" +#define OSCLASS OTHER +#endif + +#ifdef __MORPHOS__ +#define OSNAME "MorphOS" +#define OSCLASS AMIGA +#endif + +#if defined AMIGA || defined __amigaos__ +#define OSNAME "AmigaOS" +#define OSCLASS AMIGA +#endif + +#if defined mpeix || defined __mpexl +#define OSNAME "MPE/iX" +#define OSCLASS OTHER +#endif + +#if defined MSDOS || defined __MSDOS__ || defined _MSDOS || defined __DOS__ +#define OSNAME "MS-DOS" +#define OSCLASS DOS +#endif + +#ifdef __TANDEM +#define OSNAME "NonStop OS" +#define OSCLASS OTHER +#endif + +#if defined OS2 || defined _OS2 || defined __OS2__ || defined __TOS_OS2__ +#define OSNAME "OS/2" +#define OSCLASS OS2 +#endif + +#ifdef EPLAN9 +#define OSNAME "Plan 9" +#define OSCLASS OTHER +#endif + +#if defined __QNX__ || defined __QNXNTO__ +#define OSNAME "QNX" +#define OSCLASS UNIX +#endif + +#ifdef M_I386 +#define OSNAME "SCO UNIX" +#define OSCLASS UNIX +#endif + +#if defined sun || defined __sun +#if defined __SVR4 || defined __svr4 +#define OSNAME "Solaris" +#define OSCLASS UNIX +#endif +#define OSNAME "SunOS" +#define OSCLASS UNIX +#endif + +#ifdef __VOS__ +#define OSNAME "VOS" +#define OSCLASS OTHER +#endif + +#if defined __osf__ || defined __osf +#define OSNAME "OSF/1" +#define OSCLASS UNIX +#endif + +#if defined ultrix || defined __ultrix || defined __ultrix__ || __SYSTYPE_BSD +#define OSNAME "ULTRIX" +#define OSCLASS UNIX +#endif + +#if defined sco || defined _UNIXWARE7 +#define OSNAME "UnixWare" +#define OSCLASS UNIX +#endif + +#if defined VMS || defined __VMS +#define OSNAME "VMS" +#define OSCLASS VMS +#endif + +#ifdef __VM__ +#define OSNAME "VM/CMS" +#define OSCLASS S370 +#endif + +#ifdef __MVS__ +#define OSNAME "MVS" +#define OSCLASS S370 +#endif + +#ifdef __EDC_LE +#ifndef __VM__ +#define OSNAME "VSE" +#define OSCLASS S370 +#endif +#ifndef __MVS__ +#define OSNAME "VSE" +#define OSCLASS S370 +#endif +#endif + +#if defined __MCP__ +#define OSNAME "MCP" +#define OSCLASS OTHER +#endif + +#if defined _NETWARE_ || defined __NETWARE__ +#define OSNAME "NetWare" +#define OSCLASS OTHER +#endif + +#ifdef __MACH__ +#ifndef __APPLE__ +#define OSNAME "NeXTSTEP" +#define OSCLASS UNIX +#endif +#endif + +#ifdef pyr +#define OSNAME "DC/OSx" +#define OSCLASS UNIX +#endif + +#if defined sinux || defined sinix +#define OSNAME "Reliant UNIX" +#define OSCLASS UNIX +#endif + +#ifdef _UNICOS +#define OSNAME "UNICOS" +#define OSCLASS UNIX +#endif + +#if defined _CRAY || defined _crayx1 +#define OSNAME "UNICOS/mp" +#define OSCLASS UNIX +#endif + +#ifdef _UWIN +#define OSNAME "Windows NT (U/Win)" +#define OSCLASS WINDOWS +#endif + +#if defined __VXWORKS__ || defined __vxworks +#define OSNAME "VxWorks" +#define OSCLASS OTHER +#endif + +#ifdef _WIN32_WCE +#define OSNAME "Windows CE" +#define OSCLASS WINDOWS +#endif + +#if defined _WIN32 | defined _WIN64 | defined __WIN32__ +#define OSNAME "Windows NT" +#define OSCLASS WINDOWS +#endif + +#ifdef _WIN16 +#define OSNAME "Windows 3.x" +#define OSCLASS WINDOWS +#endif + +#ifndef OSNAME +#define OSNAME "POSIX" +#define OSCLASS UNIX +#endif + +#endif diff --git a/include/concord/priority_queue.h b/include/concord/priority_queue.h new file mode 100644 index 0000000..c81fafb --- /dev/null +++ b/include/concord/priority_queue.h @@ -0,0 +1,59 @@ + +// MIT License +// Copyright (c) 2022 Anotra +// https://github.com/Anotra/priority_queue + +#pragma once + +#ifndef PRIORITY_QUEUE_H +#define PRIORITY_QUEUE_H + +#include + +typedef struct priority_queue priority_queue; +typedef unsigned priority_queue_id; + +typedef enum { + priority_queue_min = 0, + priority_queue_max = 1, +} priority_queue_flags; + +priority_queue *priority_queue_create( + size_t key_size, size_t val_size, + int(*cmp)(const void *a, const void *b), + priority_queue_flags flags); + +void priority_queue_destroy(priority_queue *queue); + +size_t priority_queue_length(priority_queue *queue); + +void priority_queue_set_max_capacity( + priority_queue *queue, + size_t capacity); + +priority_queue_id priority_queue_push( + priority_queue *queue, + void *key, void *val); + +priority_queue_id priority_queue_peek( + priority_queue *queue, + void *key, void *val); + +priority_queue_id priority_queue_pop( + priority_queue *queue, + void *key, void *val); + +priority_queue_id priority_queue_get( + priority_queue *queue, + priority_queue_id id, + void *key, void *val); + +int priority_queue_del( + priority_queue *queue, + priority_queue_id id); + +int priority_queue_update(priority_queue *queue, + priority_queue_id id, + void *key, void *val); + +#endif //! PRIORITY_QUEUE_H diff --git a/include/concord/queriec.h b/include/concord/queriec.h new file mode 100644 index 0000000..3350a5f --- /dev/null +++ b/include/concord/queriec.h @@ -0,0 +1,30 @@ +#ifndef QUERIEC_H +#define QUERIEC_H + +#define QUERIEC_ADDITIONAL_LETTERS_SIZE 2 + +#define QUERIEC_ERROR_NOMEM -1 +#define QUERIEC_OK 0 + +#include "attributes.h" + +struct queriec { + int state; + size_t size; + size_t offset; +}; + +void +queriec_init(struct queriec *queriec, size_t size); + +int queriec_snprintf_add(struct queriec *queriec, char *query, + const char key[], size_t keySize, + char buffer[], size_t bufferLen, + const char *format, ...) PRINTF_LIKE(7, 8); + +int +queriec_add(struct queriec *queriec, char *query, char key[], + size_t keySize, char value[], size_t valueSize); + +#endif + diff --git a/include/concord/queue.h b/include/concord/queue.h new file mode 100644 index 0000000..697a9f6 --- /dev/null +++ b/include/concord/queue.h @@ -0,0 +1,111 @@ +/* Copyright (c) 2013, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef QUEUE_H_ +#define QUEUE_H_ + +#include + +typedef void *QUEUE[2]; + +/* Improve readability by letting user specify underlying type. */ +#define QUEUE(type) QUEUE + +/* Private macros. */ +#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) +#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) +#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q))) +#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q))) + +/* Public macros. */ +#define QUEUE_DATA(ptr, type, field) \ + ((type *) ((char *) (ptr) - offsetof(type, field))) + +/* Important note: mutating the list while QUEUE_FOREACH is + * iterating over its elements results in undefined behavior. + */ +#define QUEUE_FOREACH(q, h) \ + for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) + +#define QUEUE_EMPTY(q) \ + ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) + +#define QUEUE_HEAD(q) \ + (QUEUE_NEXT(q)) + +#define QUEUE_INIT(q) \ + do { \ + QUEUE_NEXT(q) = (q); \ + QUEUE_PREV(q) = (q); \ + } \ + while (0) + +#define QUEUE_ADD(h, n) \ + do { \ + QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \ + QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \ + QUEUE_PREV(h) = QUEUE_PREV(n); \ + QUEUE_PREV_NEXT(h) = (h); \ + } \ + while (0) + +#define QUEUE_SPLIT(h, q, n) \ + do { \ + QUEUE_PREV(n) = QUEUE_PREV(h); \ + QUEUE_PREV_NEXT(n) = (n); \ + QUEUE_NEXT(n) = (q); \ + QUEUE_PREV(h) = QUEUE_PREV(q); \ + QUEUE_PREV_NEXT(h) = (h); \ + QUEUE_PREV(q) = (n); \ + } \ + while (0) + +#define QUEUE_MOVE(h, n) \ + do { \ + if (QUEUE_EMPTY(h)) \ + QUEUE_INIT(n); \ + else { \ + QUEUE* q = QUEUE_HEAD(h); \ + QUEUE_SPLIT(h, q, n); \ + } \ + } \ + while (0) + +#define QUEUE_INSERT_HEAD(h, q) \ + do { \ + QUEUE_NEXT(q) = QUEUE_NEXT(h); \ + QUEUE_PREV(q) = (h); \ + QUEUE_NEXT_PREV(q) = (q); \ + QUEUE_NEXT(h) = (q); \ + } \ + while (0) + +#define QUEUE_INSERT_TAIL(h, q) \ + do { \ + QUEUE_NEXT(q) = (h); \ + QUEUE_PREV(q) = QUEUE_PREV(h); \ + QUEUE_PREV_NEXT(q) = (q); \ + QUEUE_PREV(h) = (q); \ + } \ + while (0) + +#define QUEUE_REMOVE(q) \ + do { \ + QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \ + QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \ + } \ + while (0) + +#endif /* QUEUE_H_ */ diff --git a/include/concord/stage_instance.h b/include/concord/stage_instance.h new file mode 100644 index 0000000..d2f755e --- /dev/null +++ b/include/concord/stage_instance.h @@ -0,0 +1,75 @@ +/** + * @file stage_instance.h + * @author Cogmasters + * @brief Stage Instance public functions and datatypes + */ + +#ifndef DISCORD_STAGE_INSTANCE_H +#define DISCORD_STAGE_INSTANCE_H + +/** @defgroup DiscordAPIStageInstance Stage Instance + * @ingroup DiscordAPI + * @brief Stage Instance's public API supported by Concord + * @{ */ + +/** + * @brief Creates a new Stage Instance associated to a Stage channel + * @note requires the user to be a moderator of the Stage channel + * + * @param client the client created with discord_from_token() + * @param params the request parameters + * @CCORD_ret_obj{ret,stage_instance} + * @CCORD_return + */ +CCORDcode discord_create_stage_instance( + struct discord *client, + struct discord_create_stage_instance *params, + struct discord_ret_stage_instance *ret); + +/** + * @brief Gets the stage instance associated with the Stage channel, if it + * exists + * + * @param client the client created with discord_from_token() + * @param channel_id the stage channel id + * @CCORD_ret_obj{ret,stage_instance} + * @CCORD_return + */ +CCORDcode discord_get_stage_instance(struct discord *client, + u64snowflake channel_id, + struct discord_ret_stage_instance *ret); + +/** + * @brief Updates fields of an existing Stage instance + * @note requires the user to be a moderator of the Stage channel + * + * @param client the client created with discord_from_token() + * @param channel_id the stage channel id + * @param params the request parameters + * @CCORD_ret_obj{ret,stage_instance} + * @CCORD_return + */ +CCORDcode discord_modify_stage_instance( + struct discord *client, + u64snowflake channel_id, + struct discord_modify_stage_instance *params, + struct discord_ret_stage_instance *ret); + +/** + * @brief Deletes the Stage instance + * @note requires the user to be a moderator of the Stage channel + * + * @param client the client created with discord_from_token() + * @param channel_id the stage channel to be deleted + * @param params the request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_stage_instance(struct discord *client, + u64snowflake channel_id, + struct discord_delete_stage_instance *params, + struct discord_ret *ret); + +/** @} DiscordAPIStageInstance */ + +#endif /* DISCORD_STAGE_INSTANCE_H */ diff --git a/include/concord/sticker.h b/include/concord/sticker.h new file mode 100644 index 0000000..4e01788 --- /dev/null +++ b/include/concord/sticker.h @@ -0,0 +1,104 @@ +/** + * @file sticker.h + * @author Cogmasters + * @brief Sticker public functions and datatypes + */ + +#ifndef DISCORD_STICKER_H +#define DISCORD_STICKER_H + +/** @defgroup DiscordAPISticker Sticker + * @ingroup DiscordAPI + * @brief Sticker's public API supported by Concord + * @{ */ + +/** + * @brief Get a sticker from a given ID + * + * @param client the client created with discord_from_token() + * @param sticker_id the sticker to be fetched + * @CCORD_ret_obj{ret,sticker} + * @CCORD_return + */ +CCORDcode discord_get_sticker(struct discord *client, + u64snowflake sticker_id, + struct discord_ret_sticker *ret); + +/** + * @brief Get a list of sticker packs available to Nitro subscribers + * + * @param client the client created with discord_from_token() + * @CCORD_ret_obj{ret,list_nitro_sticker_packs} + * @CCORD_return + */ +CCORDcode discord_list_nitro_sticker_packs( + struct discord *client, struct discord_ret_list_nitro_sticker_packs *ret); + +/** + * @brief Get stickers for the given guild + * @note includes `user` fields if the bot has the `MANAGE_EMOJIS_AND_STICKERS` + * permission + * + * @param client the client created with discord_from_token() + * @param guild_id guild to fetch the stickers from + * @CCORD_ret_obj{ret,stickers} + * @CCORD_return + */ +CCORDcode discord_list_guild_stickers(struct discord *client, + u64snowflake guild_id, + struct discord_ret_stickers *ret); + +/** + * @brief Get a sticker for the given guild and sticker ID + * @note includes the `user` field if the bot has the + * `MANAGE_EMOJIS_AND_STICKERS` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild where the sticker belongs to + * @param sticker_id the sticker to be fetched + * @CCORD_ret_obj{ret,sticker} + * @CCORD_return + */ +CCORDcode discord_get_guild_sticker(struct discord *client, + u64snowflake guild_id, + u64snowflake sticker_id, + struct discord_ret_sticker *ret); + +/** + * @brief Modify the given sticker + * @note requires the `MANAGE_EMOJIS_AND_STICKERS` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild where the sticker belongs to + * @param sticker_id the sticker to be modified + * @param params the request parameters + * @CCORD_ret_obj{ret,sticker} + * @CCORD_return + */ +CCORDcode discord_modify_guild_sticker( + struct discord *client, + u64snowflake guild_id, + u64snowflake sticker_id, + struct discord_modify_guild_sticker *params, + struct discord_ret_sticker *ret); + +/** + * @brief Delete the given sticker + * @note requires the `MANAGE_EMOJIS_AND_STICKERS` permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild where the sticker belongs to + * @param sticker_id the sticker to be deleted + * @param params the request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_guild_sticker(struct discord *client, + u64snowflake guild_id, + u64snowflake sticker_id, + struct discord_delete_guild_sticker *params, + struct discord_ret *ret); + +/** @} DiscordAPISticker */ + +#endif /* DISCORD_STICKER_H */ diff --git a/include/concord/threadpool.h b/include/concord/threadpool.h new file mode 100644 index 0000000..2f0ddbc --- /dev/null +++ b/include/concord/threadpool.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016, Mathias Brossard . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _THREADPOOL_H_ +#define _THREADPOOL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file threadpool.h + * @brief Threadpool Header File + */ + + /** + * Increase this constants at your own risk + * Large values might slow down your system + */ +#define MAX_THREADS 64 +#define MAX_QUEUE 65536 + +typedef struct threadpool_t threadpool_t; + +typedef enum { + threadpool_invalid = -1, + threadpool_lock_failure = -2, + threadpool_queue_full = -3, + threadpool_shutdown = -4, + threadpool_thread_failure = -5 +} threadpool_error_t; + +typedef enum { + threadpool_graceful = 1 +} threadpool_destroy_flags_t; + +/** + * @function threadpool_create + * @brief Creates a threadpool_t object. + * @param thread_count Number of worker threads. + * @param queue_size Size of the queue. + * @param flags Unused parameter. + * @return a newly created thread pool or NULL + */ +threadpool_t *threadpool_create(int thread_count, int queue_size, int flags); + +/** + * @function threadpool_add + * @brief add a new task in the queue of a thread pool + * @param pool Thread pool to which add the task. + * @param function Pointer to the function that will perform the task. + * @param argument Argument to be passed to the function. + * @param flags Unused parameter. + * @return 0 if all goes well, negative values in case of error (@see + * threadpool_error_t for codes). + */ +int threadpool_add(threadpool_t *pool, void (*routine)(void *), + void *arg, int flags); + +/** + * @function threadpool_destroy + * @brief Stops and destroys a thread pool. + * @param pool Thread pool to destroy. + * @param flags Flags for shutdown + * + * Known values for flags are 0 (default) and threadpool_graceful in + * which case the thread pool doesn't accept any new tasks but + * processes all pending tasks before shutdown. + */ +int threadpool_destroy(threadpool_t *pool, int flags); + +#ifdef __cplusplus +} +#endif + +#endif /* _THREADPOOL_H_ */ diff --git a/include/concord/types.h b/include/concord/types.h new file mode 100644 index 0000000..7545f9e --- /dev/null +++ b/include/concord/types.h @@ -0,0 +1,66 @@ +/** @file types.h */ + +#ifndef CONCORD_TYPES_H +#define CONCORD_TYPES_H + +#include +#include + +/** @defgroup ConcordTypes Primitives + * @brief Commonly used datatypes + * + * @note these datatypes are typedefs of C primitives, + * its purpose is to facilitate identification + * and "intent of use". + * @{ */ + +/** + * @brief Unix time in milliseconds + * + * Commonly used for fields that may store timestamps + */ +typedef uint64_t u64unix_ms; +/** + * @brief Snowflake datatype + * + * Used in APIs such as Twitter and Discord for their unique IDs + */ +typedef uint64_t u64snowflake; + +/** + * @brief Bitmask primitive + * + * Used for fields that may store values of, or perform bitwise operations + */ +typedef uint64_t u64bitmask; + +/** + * @brief Raw JSON string + * + * Used for fields that have dynamic or unreliable types. A string made out of + * `json_char` should be used to keep a raw JSON, which can then be + * parsed with the assistance of a JSON library. + */ +typedef char json_char; + +/** @brief Generic sized buffer */ +struct ccord_szbuf { + /** the buffer's start */ + char *start; + /** the buffer's size in bytes */ + size_t size; + /** true if buffer is static (else is dynamic and shall be freed) */ + bool is_static; +}; + +/** @brief Read-only generic sized buffer */ +struct ccord_szbuf_readonly { + /** the buffer's start */ + const char *start; + /** the buffer's size in bytes */ + size_t size; +}; + +/** @} ConcordTypes */ + +#endif /* CONCORD_TYPES_H */ diff --git a/include/concord/user-agent.h b/include/concord/user-agent.h new file mode 100644 index 0000000..a69887d --- /dev/null +++ b/include/concord/user-agent.h @@ -0,0 +1,400 @@ +/** @file user-agent.h */ + +#ifndef USER_AGENT_H +#define USER_AGENT_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +/** @brief HTTP methods */ +enum http_method { + HTTP_INVALID = -1, + HTTP_DELETE, + HTTP_GET, + HTTP_POST, + HTTP_MIMEPOST, + HTTP_PATCH, + HTTP_PUT +}; + +/** + * @brief Get the HTTP method name string + * + * @param method the HTTP method + * @return the HTTP method name + */ +const char *http_method_print(enum http_method method); + +/** + * @brief Get the HTTP method enumerator from a string + * + * @param method the HTTP method string + * @return the HTTP method enumerator + */ +enum http_method http_method_eval(char method[]); + +/** @defgroup HttpStatusCode + * @see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes + * @{ */ +#define HTTP_OK 200 +#define HTTP_CREATED 201 +#define HTTP_ACCEPTED 202 +#define HTTP_NON_AUTHORITATIVE_INFO 203 +#define HTTP_NO_CONTENT 204 +#define HTTP_RESET_CONTENT 205 +#define HTTP_PARTIAL_CONTENT 206 +#define HTTP_MULTI_STATUS 207 +#define HTTP_ALREADY_REPORTED 208 +#define HTTP_IM_USED 226 +#define HTTP_MULTIPLE_CHOICES 300 +#define HTTP_MOVED_PERMANENTLY 301 +#define HTTP_FOUND 302 +#define HTTP_SEE_OTHER 303 +#define HTTP_NOT_MODIFIED 304 +#define HTTP_USE_PROXY 305 +#define HTTP_TEMPORARY_REDIRECT 307 +#define HTTP_PERMANENT_REDIRECT 308 +#define HTTP_BAD_REQUEST 400 +#define HTTP_UNAUTHORIZED 401 +#define HTTP_PAYMENT_REQUIRED 402 +#define HTTP_FORBIDDEN 403 +#define HTTP_NOT_FOUND 404 +#define HTTP_METHOD_NOT_ALLOWED 405 +#define HTTP_NOT_ACCEPTABLE 406 +#define HTTP_PROXY_AUTHENTICATION 407 +#define HTTP_REQUEST_TIMEOUT 408 +#define HTTP_CONFLICT 409 +#define HTTP_GONE 410 +#define HTTP_LENGTH_REQUIRED 411 +#define HTTP_PRECONDITION_FAILED 412 +#define HTTP_PAYLOAD_TOO_LARGE 413 +#define HTTP_URI_TOO_LONG 414 +#define HTTP_UNSUPPORTED_MEDIA_TYPE 415 +#define HTTP_RANGE_NOT_SATISFIABLE 416 +#define HTTP_EXPECTATION_FAILED 417 +#define HTTP_IM_A_TEAPOT 418 +#define HTTP_MISDIRECTED_REQUEST 421 +#define HTTP_UNPROCESSABLE_ENTITY 422 +#define HTTP_LOCKED 423 +#define HTTP_FAILED_DEPENDENCY 424 +#define HTTP_TOO_EARLY 425 +#define HTTP_UPGRADE_REQUIRED 426 +#define HTTP_PRECONDITION_REQUIRED 428 +#define HTTP_TOO_MANY_REQUESTS 429 +#define HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE 431 +#define HTTP_UNAVAILABLE_FOR_LEGAL_REASONS 451 +#define HTTP_INTERNAL_SERVER_ERROR 500 +#define HTTP_NOT_IMPLEMENTED 501 +#define HTTP_BAD_GATEWAY 502 +#define HTTP_SERVICE_UNAVAILABLE 503 +#define HTTP_GATEWAY_TIMEOUT 504 +#define HTTP_VERSION_NOT_SUPPORTED 505 +#define HTTP_VARIANT_ALSO_NEGOTIATES 506 +#define HTTP_INSUFFICIENT_STORAGE 507 +#define HTTP_LOOP_DETECTED 508 +#define HTTP_NOT_EXTENDED 510 +#define HTTP_NETWORK_AUTHENTICATION_REQUIRED 511 +#define HTTP_INVALID_HTTP_CODE 999 +/** @} */ + +/** + * @brief Get the HTTP status code name string + * + * @param httpcode the HTTP status code + * @return the HTTP status code name + */ +const char *http_code_print(int httpcode); + +/** + * @brief Get the HTTP status code reason string + * + * @param httpcode the HTTP status code + * @return the HTTP status code reason + */ +const char *http_reason_print(int httpcode); + +/** + * @struct user_agent + * @brief Opaque User-Agent handle + * + * @see ua_init(), ua_cleanup(), ua_set_url(), ua_get_url(), ua_set_opt() + */ +struct user_agent; + +/* forward declaration */ +struct logmod; +/**/ + +/** + * @struct ua_conn + * @brief Opaque connection handle + * + * @see ua_conn_start(), ua_conn_setup(), ua_conn_reset(), ua_conn_stop(), + * ua_conn_easy_perform(), ua_conn_add_header(), ua_conn_print_header(), + * ua_conn_set_mime(), ua_conn_get_easy_handle() + */ +struct ua_conn; + +/** @brief Read-only generic sized buffer */ +struct ua_szbuf_readonly { + /** the buffer's start */ + const char *start; + /** the buffer's size in bytes */ + size_t size; +}; + +/** @brief header fields to have its contents hidden when logging */ +struct ua_log_filter { + /** list of headers */ + struct ua_szbuf_readonly *headers; + /** amount of headers to be filtered */ + size_t length; +}; + +/** @brief Connection attributes */ +struct ua_conn_attr { + /** the HTTP method of this transfer (GET, POST, ...) */ + enum http_method method; + /** the optional request body, can be NULL */ + char *body; + /** the request body size */ + size_t body_size; + /** the endpoint to be appended to the base URL */ + char *endpoint; + /** optional base_url to override ua_set_url(), can be NULL */ + char *base_url; + /** @brief header fields to have its contents filtered when logging */ + struct ua_log_filter log_filter; +}; + +/** Maximum amount of header pairs */ +#define UA_MAX_HEADER_PAIRS 100 + 1 + +/** @brief Structure for storing the request's response header */ +struct ua_resp_header { + /** response header buffer */ + char *buf; + /** response header string length */ + size_t len; + /** real size occupied in memory by buffer */ + size_t bufsize; + /** array of header field/value pairs */ + struct { + struct { + /** offset index of 'buf' for the start of field or value */ + size_t idx; + /** length of individual field or value */ + size_t size; + } field, value; + } pairs[UA_MAX_HEADER_PAIRS]; + /** amount of pairs initialized */ + int n_pairs; +}; + +/** @brief Structure for storing the request's response body */ +struct ua_resp_body { + /** response body buffer */ + char *buf; + /** response body string length */ + size_t len; + /** real size occupied in memory by buffer */ + size_t bufsize; +}; + +/** @brief Informational handle received on request's completion */ +struct ua_info { + /** the HTTP response code */ + long httpcode; + /** @privatesection */ + /** the response header */ + struct ua_resp_header header; + /** the response body */ + struct ua_resp_body body; +}; + +/** + * @brief Callback to be called on each libcurl's easy handle initialization + * + * @param ua the User-Handle created with ua_init() + * @param data user data to be passed along to `callback` + * @param callback the user callback + */ +void ua_set_opt(struct user_agent *ua, + void *data, + void (*callback)(struct ua_conn *conn, void *data)); + +/** + * @brief Initialize User-Agent handle + * + * @param logmod optional pre-initialized logging module + * @param fp file pointer for writing HTTP traces to + * @return the user agent handle + */ +struct user_agent *ua_init(struct logmod *logmod, FILE *fp); + +/** + * @brief Cleanup User-Agent handle resources + * + * @param ua the User-Agent handle created with ua_init() + */ +void ua_cleanup(struct user_agent *ua); + +/** + * @brief Set the request url + * + * @param ua the User-Agent handle created with ua_init() + * @param base_url the base request url + */ +void ua_set_url(struct user_agent *ua, const char base_url[]); + +/** + * @brief Get the request url + * + * @param ua the User-Agent handle created with ua_init() + * @return the request url set with ua_set_url() + */ +const char *ua_get_url(struct user_agent *ua); + +/** @brief Callback for object to be loaded by api response */ +typedef void (*ua_load_obj_cb)(char *str, size_t len, void *p_obj); + +/** @brief User callback to be called on request completion */ +struct ua_resp_handle { + /** callback called when a successful transfer occurs */ + ua_load_obj_cb ok_cb; + /** the pointer to be passed to ok_cb */ + void *ok_obj; + /** callback called when a failed transfer occurs */ + ua_load_obj_cb err_cb; + /** the pointer to be passed to err_cb */ + void *err_obj; +}; + +/** + * @brief Get a connection handle and mark it as running + * + * @param conn the User-Agent handle created with ua_init() + * @return a connection handle + */ +struct ua_conn *ua_conn_start(struct user_agent *ua); + +/** + * @brief Add a field/value pair to the request header + * + * @param conn the connection handle + * @param field header's field to be added + * @param value field's value + * @return CURLE_OK on success, otherwise an error code + */ +CURLcode ua_conn_add_header(struct ua_conn *conn, + const char field[], + const char value[]); + +/** + * @brief Remove a header field + * + * @param conn the connection handle + * @param field header's field to be removed + * @return CURLE_OK on success, otherwise an error code + */ +CURLcode ua_conn_remove_header(struct ua_conn *conn, const char field[]); + +/** + * @brief Fill a buffer with the request header + * + * @param conn the connection handle + * @param buf the user buffer to be filled + * @param bufsize the user buffer size in bytes + * @param log_filter headers to have its contents hidden when logging + * @return the user buffer + */ +char *ua_conn_print_header(struct ua_conn *conn, + char *buf, + size_t bufsize, + struct ua_log_filter *log_filter); +/** + * @brief Multipart creation callback for `conn` + * @see https://curl.se/libcurl/c/smtp-mime.html + * + * @param conn the connection handle to send multipart body + * @param data user data to be passed along to `callback` + * @param callback the user callback + */ +void ua_conn_set_mime(struct ua_conn *conn, + void *data, + void (*callback)(curl_mime *mime, void *data)); + +/** + * @brief Reset a connection handle fields + * + * @param conn connection handle to be reset + * @warning this won't deactivate the handle, for that purpose check + * ua_conn_stop() + */ +void ua_conn_reset(struct ua_conn *conn); + +/** + * @brief Stop a connection handle and mark it as idle + * + * @param conn connection handle to be deactivated + */ +void ua_conn_stop(struct ua_conn *conn); + +/** + * @brief Setup transfer attributes + * + * @param conn the connection handle + * @param attr attributes to be set for transfer + * @return CURLE_OK on success, otherwise an error code + */ +CURLcode ua_conn_setup(struct ua_conn *conn, struct ua_conn_attr *attr); + +/** + * @brief Get libcurl's easy handle assigned to `conn` + * + * @param conn the connection handle + * @return the libcurl's easy handle + */ +CURL *ua_conn_get_easy_handle(struct ua_conn *conn); + +/** + * @brief Extract information from `conn` previous request + * + * @param conn the connection handle + * @param info handle to store information on previous request + */ +void ua_info_extract(struct ua_conn *conn, struct ua_info *info); + +/** + * @brief Cleanup informational handle + * + * @param info handle containing information on previous request + */ +void ua_info_cleanup(struct ua_info *info); + +/** + * @brief Get a value's from the response header + * + * @param info handle containing information on previous request + * @param field the header field to fetch the value + * @return a @ref ua_szbuf_readonly containing the field's value + */ +struct ua_szbuf_readonly ua_info_get_header(struct ua_info *info, + char field[]); + +/** + * @brief Get the response body + * + * @param info handle containing information on previous request + * @return a @ref ua_szbuf_readonly containing the response body + */ +struct ua_szbuf_readonly ua_info_get_body(struct ua_info *info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* USER_AGENT_H */ diff --git a/include/concord/user.h b/include/concord/user.h new file mode 100644 index 0000000..3f70192 --- /dev/null +++ b/include/concord/user.h @@ -0,0 +1,115 @@ +/** + * @file user.h + * @author Cogmasters + * @brief User public functions and datatypes + */ + +#ifndef DISCORD_USER_H +#define DISCORD_USER_H + +/** @defgroup DiscordAPIUser User + * @ingroup DiscordAPI + * @brief User's public API supported by Concord + * @{ */ + +/** + * @brief Get client's user + * + * @param client the client created with discord_from_token() + * @CCORD_ret_obj{ret,user} + * @CCORD_return + */ +CCORDcode discord_get_current_user(struct discord *client, + struct discord_ret_user *ret); + +/** + * @brief Get user for a given id + * + * @param client the client created with discord_from_token() + * @param user_id user to be fetched + * @CCORD_ret_obj{ret,user} + * @CCORD_return + */ +CCORDcode discord_get_user(struct discord *client, + u64snowflake user_id, + struct discord_ret_user *ret); + +/** + * @brief Modify client's user account settings + * + * @param client the client created with discord_from_token() + * @param params request parameters + * @CCORD_ret_obj{ret,user} + * @CCORD_return + */ +CCORDcode discord_modify_current_user( + struct discord *client, + struct discord_modify_current_user *params, + struct discord_ret_user *ret); + +/** + * @brief Get guilds client is a member of + * @note Requires the `guilds` oauth2 scope + * + * @param client the client created with discord_from_token() + * @CCORD_ret_obj{ret,guilds} + * @CCORD_return + */ +CCORDcode discord_get_current_user_guilds(struct discord *client, + struct discord_ret_guilds *ret); + +/** + * @brief Leave a guild + * + * @param client the client created with discord_from_token() + * @param guild_id guild to exit from + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_leave_guild(struct discord *client, + u64snowflake guild_id, + struct discord_ret *ret); + +/** + * @brief Create a new DM channel with a given user + * @warning DMs should generally be initiated by a user action. If you open a + * significant amount of DMs too quickly, your bot may be rate limited + * or blocked from opening new ones + * + * @param client the client created with discord_from_token() + * @param params the request parameters + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_create_dm(struct discord *client, + struct discord_create_dm *params, + struct discord_ret_channel *ret); + +/** + * @brief Create a new group DM channel with multiple users + * @note DMs created with this function will not be shown in the Discord client + * @note Limited to 10 active group DMs + * + * @param client the client created with discord_from_token() + * @param params the request parameters + * @CCORD_ret_obj{ret,channel} + * @CCORD_return + */ +CCORDcode discord_create_group_dm(struct discord *client, + struct discord_create_group_dm *params, + struct discord_ret_channel *ret); + +/** + * @brief Get a list of connection objects + * @note Requires the `connections` oauth2 scope + * + * @param client the client created with discord_from_token() + * @CCORD_ret_obj{ret,connections} + * @CCORD_return + */ +CCORDcode discord_get_user_connections(struct discord *client, + struct discord_ret_connections *ret); + +/** @} DiscordAPIUser */ + +#endif /* DISCORD_USER_H */ diff --git a/include/concord/voice.h b/include/concord/voice.h new file mode 100644 index 0000000..cd812ed --- /dev/null +++ b/include/concord/voice.h @@ -0,0 +1,31 @@ +/** + * @file voice.h + * @author Cogmasters + * @brief Voice public functions and datatypes + */ + +#ifndef DISCORD_VOICE_H +#define DISCORD_VOICE_H + +/** @defgroup DiscordAPIVoice Voice + * @ingroup DiscordAPI + * @brief Voice's public API supported by Concord + * @{ */ + +/** + * @brief Get voice regions that can be used when setting a + * voice or stage channel's `rtc_region` + * + * @param client the client created with discord_from_token() + * @CCORD_ret_obj{ret,voice_regions} + * @CCORD_return + */ +CCORDcode discord_list_voice_regions(struct discord *client, + struct discord_ret_voice_regions *ret); + +/** @example voice.c + * Demonstrates a couple use cases of the Voice API */ + +/** @} DiscordAPIVoice */ + +#endif /* DISCORD_VOICE_H */ diff --git a/include/concord/webhook.h b/include/concord/webhook.h new file mode 100644 index 0000000..f9c9cb5 --- /dev/null +++ b/include/concord/webhook.h @@ -0,0 +1,211 @@ +/** + * @file webhook.h + * @author Cogmasters + * @brief Webhook public functions and datatypes + */ + +#ifndef DISCORD_WEBHOOK_H +#define DISCORD_WEBHOOK_H + +/** @defgroup DiscordAPIWebhook Webhook + * @ingroup DiscordAPI + * @brief Webhook's public API supported by Concord + * @{ */ + +/** + * @brief Create a new webhook + * @note Requires the MANAGE_WEBHOOKS permission + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the webhook belongs to + * @param params request parameters + * @CCORD_ret_obj{ret,webhook} + * @CCORD_return + */ +CCORDcode discord_create_webhook(struct discord *client, + u64snowflake channel_id, + struct discord_create_webhook *params, + struct discord_ret_webhook *ret); + +/** + * @brief Get webhooks from a given channel + * @note Requires the MANAGE_WEBHOOKS permission + * + * @param client the client created with discord_from_token() + * @param channel_id the channel that the webhooks belongs to + * @CCORD_ret_obj{ret,webhooks} + * @CCORD_return + */ +CCORDcode discord_get_channel_webhooks(struct discord *client, + u64snowflake channel_id, + struct discord_ret_webhooks *ret); + +/** + * @brief Get webhooks from a given guild webhook objects + * @note Requires the MANAGE_WEBHOOKS permission + * + * @param client the client created with discord_from_token() + * @param guild_id the guild that the webhooks belongs to + * @CCORD_ret_obj{ret,webhooks} + * @CCORD_return + */ +CCORDcode discord_get_guild_webhooks(struct discord *client, + u64snowflake guild_id, + struct discord_ret_webhooks *ret); + +/** + * @brief Get the new webhook object for the given id + * + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @CCORD_ret_obj{ret,webhook} + * @CCORD_return + */ +CCORDcode discord_get_webhook(struct discord *client, + u64snowflake webhook_id, + struct discord_ret_webhook *ret); + +/** + * Same as discord_get_webhook(), except this call does not require + * authentication and returns no user in the webhook object + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param webhook_token the webhook token + * @CCORD_ret_obj{ret,webhook} + * @CCORD_return + */ +CCORDcode discord_get_webhook_with_token(struct discord *client, + u64snowflake webhook_id, + const char webhook_token[], + struct discord_ret_webhook *ret); + +/** + * @brief Modify a webhook + * @note Requires the MANAGE_WEBHOOKS permission + * + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param params request parameters + * @CCORD_ret_obj{ret,webhook} + * @CCORD_return + */ +CCORDcode discord_modify_webhook(struct discord *client, + u64snowflake webhook_id, + struct discord_modify_webhook *params, + struct discord_ret_webhook *ret); + +/** + * Same discord_modify_webhook(), except this call does not require + * authentication and returns no user in the webhook object + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param webhook_token the webhook token + * @param params request parameters + * @CCORD_ret_obj{ret,webhook} + * @CCORD_return + */ +CCORDcode discord_modify_webhook_with_token( + struct discord *client, + u64snowflake webhook_id, + const char webhook_token[], + struct discord_modify_webhook_with_token *params, + struct discord_ret_webhook *ret); + +/** + * Delete a webhook permanently. Requires the MANAGE_WEBHOOKS permission + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_webhook(struct discord *client, + u64snowflake webhook_id, + struct discord_delete_webhook *params, + struct discord_ret *ret); + +/** + * Same discord_delete_webhook(), except this call does not require + * authentication + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param webhook_token the webhook token + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_webhook_with_token(struct discord *client, + u64snowflake webhook_id, + const char webhook_token[], + struct discord_ret *ret); + +/** + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param webhook_token the webhook token + * @param params request parameters + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_execute_webhook(struct discord *client, + u64snowflake webhook_id, + const char webhook_token[], + struct discord_execute_webhook *params, + struct discord_ret *ret); + +/** + * @brief Get previously-sent webhook message from the same token + * + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param webhook_token the webhook token + * @param message_id the message the webhook belongs to + * @CCORD_ret_obj{ret,message} + * @CCORD_return + */ +CCORDcode discord_get_webhook_message(struct discord *client, + u64snowflake webhook_id, + const char webhook_token[], + u64snowflake message_id, + struct discord_ret_message *ret); + +/** + * @brief Edits a previously-sent webhook message from the same token + * + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param webhook_token the webhook token + * @param message_id the message the webhook belongs to + * @param params request parameters + * @CCORD_ret_obj{ret,message} + * @CCORD_return + */ +CCORDcode discord_edit_webhook_message( + struct discord *client, + u64snowflake webhook_id, + const char webhook_token[], + u64snowflake message_id, + struct discord_edit_webhook_message *params, + struct discord_ret_message *ret); + +/** + * @brief Deletes a message that was created by the webhook + * + * @param client the client created with discord_from_token() + * @param webhook_id the webhook itself + * @param webhook_token the webhook token + * @param message_id the message the webhook belongs to + * @CCORD_ret{ret} + * @CCORD_return + */ +CCORDcode discord_delete_webhook_message(struct discord *client, + u64snowflake webhook_id, + const char webhook_token[], + u64snowflake message_id, + struct discord_ret *ret); + +/** @example webhook.c + * Demonstrates a couple use cases of the Webhook API */ + +/** @} DiscordAPIWebhook */ + +#endif /* DISCORD_WEBHOOK_H */ diff --git a/include/concord/websockets.h b/include/concord/websockets.h new file mode 100644 index 0000000..4ca75fa --- /dev/null +++ b/include/concord/websockets.h @@ -0,0 +1,326 @@ +/** + * @file websockets.h + */ + +#ifndef WEBSOCKETS_H +#define WEBSOCKETS_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include + +/** + * @struct websockets + * @brief Opaque handler for WebSockets + * + * @see ws_init(), ws_cleanup() + */ +struct websockets; + +/* forward declaration */ +struct logmod; +/**/ + +/** + * @brief The WebSockets client status + * + * @see ws_get_status() + */ +enum ws_status { + /** client disconnected from ws */ + WS_DISCONNECTED = 0, + /** client connected to ws */ + WS_CONNECTED, + /** client in the process of disconnecting from ws */ + WS_DISCONNECTING, + /** client in the process of connecting to ws */ + WS_CONNECTING, +}; + +/** + * @brief WebSockets CLOSE opcodes + * @see ws_close_opcode_print() + * @see https://tools.ietf.org/html/rfc6455#section-7.4.1 + */ +enum ws_close_reason { + WS_CLOSE_REASON_NORMAL = 1000, + WS_CLOSE_REASON_GOING_AWAY = 1001, + WS_CLOSE_REASON_PROTOCOL_ERROR = 1002, + WS_CLOSE_REASON_UNEXPECTED_DATA = 1003, + WS_CLOSE_REASON_NO_REASON = 1005, + WS_CLOSE_REASON_ABRUPTLY = 1006, + WS_CLOSE_REASON_INCONSISTENT_DATA = 1007, + WS_CLOSE_REASON_POLICY_VIOLATION = 1008, + WS_CLOSE_REASON_TOO_BIG = 1009, + WS_CLOSE_REASON_MISSING_EXTENSION = 1010, + WS_CLOSE_REASON_SERVER_ERROR = 1011, + WS_CLOSE_REASON_IANA_REGISTRY_START = 3000, + WS_CLOSE_REASON_IANA_REGISTRY_END = 3999, + WS_CLOSE_REASON_PRIVATE_START = 4000, + WS_CLOSE_REASON_PRIVATE_END = 4999 +}; + +/** @brief WebSockets callbacks */ +struct ws_callbacks { + /** + * @brief Called upon connection + */ + void (*on_connect)(void *data, struct websockets *ws); + + /** + * @brief Reports UTF-8 text messages. + * + * @note it's guaranteed to be NULL (\0) terminated, but the UTF-8 is + * not validated. If it's invalid, consider closing the connection + * with WS_CLOSE_REASON_INCONSISTENT_DATA. + */ + void (*on_text)(void *data, + struct websockets *ws, + const char *text, + size_t len); + + /** @brief reports binary data. */ + void (*on_binary)(void *data, + struct websockets *ws, + const void *mem, + size_t len); + /** + * @brief reports PING. + * + * @note if provided you should reply with ws_pong(). If not + * provided, pong is sent with the same message payload. + */ + void (*on_ping)(void *data, + struct websockets *ws, + const char *reason, + size_t len); + + /** @brief reports PONG. */ + void (*on_pong)(void *data, + struct websockets *ws, + const char *reason, + size_t len); + + /** + * @brief reports server closed the connection with the given reason. + * + * Clients should not transmit any more data after the server is + * closed + */ + void (*on_close)(void *data, + struct websockets *ws, + enum ws_close_reason wscode, + const char *reason, + size_t len); + + /** @brief user arbitrary data to be passed around callbacks */ + void *data; +}; + +/** + * @brief Check if a WebSockets connection is alive + * + * This will only return true if the connection status is + * different than WS_DISCONNECTED + * @param ws the WebSockets handle created with ws_init() + * @return `true` if WebSockets status is different than + * WS_DISCONNECTED, `false` otherwise. + */ +#define ws_is_alive(ws) (ws_get_status(ws) != WS_DISCONNECTED) + +/** + * @brief Check if WebSockets connection is functional + * + * This will only return true if the connection status is + * WS_CONNECTED + * @param ws the WebSockets handle created with ws_init() + * @return `true` if is functional, `false` otherwise + */ +#define ws_is_functional(ws) (ws_get_status(ws) == WS_CONNECTED) + +/** + * @brief Create a new (CURL-based) WebSockets handle + * + * @param cbs set of functions to call back when server report events. + * @param mhandle user-owned curl_multi handle for performing non-blocking + * transfers + * @param logmod optional pre-initialized logging handler + * @param fp file pointer for writing WebSockets traces to + * @return newly created WebSockets handle, free with ws_cleanup() + */ +struct websockets *ws_init(struct ws_callbacks *cbs, + CURLM *mhandle, + struct logmod *logmod, + FILE *fp); + +/** + * @brief Free a WebSockets handle created with ws_init() + * + * @param ws the WebSockets handle created with ws_init() + */ +void ws_cleanup(struct websockets *ws); + +/** + * @brief Set the URL for the WebSockets handle to connect + * + * @param ws the WebSockets handle created with ws_init() + * @param base_url the URL to connect, such as ws://echo.websockets.org + */ +void ws_set_url(struct websockets *ws, const char base_url[]); + +/** + * @brief Send a binary message of given size. + * + * Binary messages do not need to include the null terminator (\0), they + * will be read up to @a msglen. + * + * @param ws the WebSockets handle created with ws_init() + * @param msg the pointer to memory (linear) to send. + * @param msglen the length in bytes of @a msg. + * @return true if sent, false on errors. + */ +_Bool ws_send_binary(struct websockets *ws, const char msg[], size_t msglen); +/** + * @brief Send a text message of given size. + * + * Text messages do not need to include the null terminator (\0), they + * will be read up to @a len. + * + * @param ws the WebSockets handle created with ws_init() + * @param text the pointer to memory (linear) to send. + * @param len the length in bytes of @a text. + * @return true if sent, false on errors. + */ +_Bool ws_send_text(struct websockets *ws, const char text[], size_t len); +/** + * @brief Send a PING (opcode 0x9) frame with @a reason as payload. + * + * @param ws the WebSockets handle created with ws_init() + * @param reason NULL or some UTF-8 string null ('\0') terminated. + * @param len the length of @a reason in bytes. If SIZE_MAX, uses + * strlen() on @a reason if it's not NULL. + * @return true if sent, false on errors. + */ +_Bool ws_ping(struct websockets *ws, const char reason[], size_t len); +/** + * @brief Send a PONG (opcode 0xA) frame with @a reason as payload. + * + * Note that pong is sent automatically if no "on_ping" callback is + * defined. If one is defined you must send pong manually. + * + * @param ws the WebSockets handle created with ws_init() + * @param reason NULL or some UTF-8 string null ('\0') terminated. + * @param len the length of @a reason in bytes. If SIZE_MAX, uses + * strlen() on @a reason if it's not NULL. + * @return true if sent, false on errors. + */ +_Bool ws_pong(struct websockets *ws, const char reason[], size_t len); + +/** + * @brief Signals connecting state before entering the WebSockets event loop + * + * @param ws the WebSockets handle created with ws_init() + * @return the WebSockets easy_handle that is free'd at ws_end() + */ +CURL *ws_start(struct websockets *ws); + +/** + * @brief Cleanup and reset `ws` connection resources + * + * @param ws the WebSockets handle created with ws_init() + */ +void ws_end(struct websockets *ws); + +/** + * @brief Reads/Write available data from WebSockets + * @note Helper over curl_multi_wait() + * + * @param ws the WebSockets handle created with ws_init() + * @param wait_ms limit amount in milliseconds to wait for until activity + * @param tstamp get current timestamp for this iteration + * @return `true` if connection is still alive, `false` otherwise + * @note This is an easy, yet highly abstracted way of performing transfers. + * If a higher control is necessary, users are better of using + * ws_multi_socket_run() + */ +_Bool ws_easy_run(struct websockets *ws, uint64_t wait_ms, uint64_t *tstamp); + +/** + * @brief Reads/Write available data from WebSockets + * @note I/O is driven by io_poller per-socket-event via curl_multi_socket_action() + * + * @param ws the WebSockets handle created with ws_init() + * @param tstamp get current timestamp for this iteration + * @return `true` if connection is still alive, `false` otherwise + */ +_Bool ws_multi_socket_run(struct websockets *ws, uint64_t *tstamp); + +/** + * @brief Returns the WebSockets handle connection status + * + * @param ws the WebSockets handle created with ws_init() + * @return a ws_status opcode + */ +enum ws_status ws_get_status(struct websockets *ws); + +/** + * @brief Returns a enum ws_close_reason opcode in a string format + * + * @param opcode the opcode to be converted to string + * @return a read-only string literal of the opcode + */ +const char *ws_close_opcode_print(enum ws_close_reason opcode); + +/** + * @brief The WebSockets event-loop concept of "now" + * + * @param ws the WebSockets handle created with ws_init() + * @return the timestamp in milliseconds from when ws_timestamp_update() was + * last called + * @note the timestamp is updated at the start of each event-loop iteration + */ +uint64_t ws_timestamp(struct websockets *ws); + +/** + * @brief Update the WebSockets event-loop concept of "now" + * + * @param ws the WebSockets handle created with ws_init() + * @return the timestamp in milliseconds + */ +uint64_t ws_timestamp_update(struct websockets *ws); + +/** + * @brief Thread-safe way to stop websockets connection + * + * This will activate a internal WS_USER_CMD_EXIT flag that will + * force disconnect when the next iteration begins. + * @note it will create a copy of the reason string + * @param ws the WebSockets handle created with ws_init() + * @param code the WebSockets CLOSE opcode + * @param reason the close reason + * @param len the reason length + */ +void ws_close(struct websockets *ws, + const enum ws_close_reason code, + const char reason[], + const size_t len); + +/** + * @brief Add a header field/value pair + * + * @param ws the WebSockets handle created with ws_init() + * @param field the header field + * @param value the header value + */ +void ws_add_header(struct websockets *ws, + const char field[], + const char value[]); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* WEBSOCKETS_H */ diff --git a/include/dlibc/vector.h b/include/dlibc/vector.h new file mode 100644 index 0000000..1739f3e --- /dev/null +++ b/include/dlibc/vector.h @@ -0,0 +1,872 @@ +/* +vector.h - Vectors for C +Version: 1.0.0 + +Description: +Header-only library for vectors in C. Memory safe against the mistakes it can +detect, but not thread-safe (you must implement your own synchronization +mechanisms). +Designed for C23 onwards, but should be compatible with older standards (tested +from C99 onwards). + +Guarantees: + - A vector_t must never be duplicated by copying the struct. Two vector_t values + sharing one data pointer both believe they own it, and whichever is destroyed + or moved from first leaves the other holding freed memory. Copying the pointer + (vector_t* b = a;) is fine - that is one vector with two names, and it must be + destroyed exactly once. + - To hand a vector's data to another vector, use vector_move(). The source is + emptied, freed and its pointer set to NULL, so there is only ever one owner. + - To get a second, independent vector with the same contents, use + vector_deep_copy(), which copies the data as well as the vector itself. Deep + copies are only available for vectors without an element destructor, as a + byte-wise copy of owning elements would result in a double free. + - No gaps in the raw data array. + - When a vector is resized, the data is reallocated to a new memory location, + so any pointers into the old data are invalidated. There is no way to + preserve them; re-fetch with vector_get() after the resize. + - Order of elements is preserved. + - It is safe to pass a pointer into a vector's own data as the source element + of vector_push_back(), vector_insert() and vector_set(). Each of them handles + the aliasing in whichever way suits it, so the value survives the reallocation + or the shifting of elements. On a vector with an element destructor this is + rejected with -1 instead, as a byte-wise copy would leave two slots owning the + same memory. To duplicate an owning element, copy what it owns yourself and + push that. + - vector_take_at() and vector_take_back() transfer ownership of the element to the + caller, and do not call the destructor on it. It is the caller's responsibility + to free whatever the element owns. + +Element destructors: +A vector may be given an element destructor with vector_set_destructor(). It is +called for every element that leaves the vector, i.e. by vector_pop_back(), +vector_pop_at(), vector_set() (on the element being overwritten), vector_clear() +and vector_destroy(). + +The destructor is NOT called by vector_take_back() or vector_take_at(), as these +functions transfer ownership of the element to the caller. It is the caller's +responsibility to free whatever the element owns. + +The destructor receives a pointer to the element's slot inside the vector's data +array - NOT a pointer that was returned by malloc(). It must free whatever the +element owns, and must never free the pointer it was handed, as that memory +belongs to the vector. For a vector of char* this means: + + void free_str(void* element) { free(*(char**)element); } + + vector_t* vec = vector_create(sizeof(char*)); + vector_set_destructor(vec, free_str); + // ... use the vector ... + vector_destroy(&vec); // Takes the address of your pointer, and NULLs it + +A warning regarding misuse: +The vector manages its own buffer correctly: no leaks, no double frees of the data +array, no use of stale pointers internally. It cannot reason about what your +elements own — that is what the destructor is for, and it is your responsibility +to set one and to avoid duplicating owned pointers between slots. + +License: +MIT License + +Copyright (c) 2026 DcruBro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include +#include + +typedef void (*vector_destructor_t)(void* element); // Function pointer type for element destructor + +/* + A vector. Do not construct, copy or modify this struct directly - the fields are + visible only because the functions below are inline. Create vectors with + vector_create() and use the accessors. A vector_t that did not come from + vector_create() is not a valid vector, and the functions will reject it where + they can and misbehave where they cannot. +*/ +typedef struct { + size_t size; // Number of elements in the vector + size_t capacity; // Allocated capacity of the vector + size_t element_size; // Size of each element in the vector + void *data; // Pointer to the raw data array + vector_destructor_t destructor; // Function pointer to the element destructor +} vector_t; + +/* Forward declarations */ +static inline int vector_is_aliased(const vector_t* vec, const void* ptr); +static inline vector_t* vector_create(size_t element_size); +static inline int vector_set_destructor(vector_t* vec, vector_destructor_t destructor); +static inline vector_destructor_t vector_get_destructor(const vector_t* vec); +static inline int vector_reserve(vector_t* vec, size_t new_capacity); +static inline int vector_grow(vector_t* vec); +static inline int vector_prune(vector_t* vec); +static inline int vector_push_back(vector_t* vec, const void* element); +static inline int vector_pop_back(vector_t* vec); +static inline int vector_pop_at(vector_t* vec, size_t index); +static inline int vector_take_at(vector_t* vec, size_t index, void* out); +static inline int vector_take_back(vector_t* vec, void* out); +static inline int vector_insert(vector_t* vec, size_t index, const void* element); +static inline int vector_clear(vector_t* vec); +static inline int vector_set(vector_t* vec, size_t index, const void* element); +static inline int vector_is_empty(const vector_t* vec); +static inline void* vector_front(vector_t* vec); +static inline const void* vector_front_const(const vector_t* vec); +static inline void* vector_back(vector_t* vec); +static inline const void* vector_back_const(const vector_t* vec); +static inline void* vector_get(vector_t* vec, size_t index); +static inline const void* vector_get_const(const vector_t* vec, size_t index); +static inline size_t vector_size(const vector_t* vec); +static inline size_t vector_capacity(const vector_t* vec); +static inline size_t vector_element_size(const vector_t* vec); +static inline const void* vector_as_c_array(const vector_t* vec); +static inline void* vector_as_c_array_mutable(vector_t* vec); +static inline int vector_move(vector_t* dest, vector_t** src); +static inline vector_t* vector_deep_copy(const vector_t* vec); +static inline int vector_destroy(vector_t** vec); + +/* + @brief Checks whether a pointer points inside the vector's own data array. Used internally to make the write functions safe against self-referential input. + @param vec A pointer to the vector to check against. + @param ptr The pointer to check. + @return 1 if the pointer lies within the vector's allocated buffer, 0 otherwise. + @attention This is an internal helper. You are not expected to call it directly, but it is harmless if you do. +*/ +static inline int vector_is_aliased(const vector_t* vec, const void* ptr) { + if (!vec || !ptr || !vec->data) { + return 0; // Nothing to alias + } + + // Compared as integers rather than pointers, as comparing pointers into + // different objects is not well defined. + uintptr_t base = (uintptr_t)vec->data; + uintptr_t end = base + (vec->capacity * vec->element_size); + uintptr_t target = (uintptr_t)ptr; + + return target >= base && target < end; +} + +/* + @brief Creates a new vector with the specified element size. + @param element_size The size of each element in the vector. Call with sizeof(type) + @return A pointer to the newly created vector, or NULL if allocation fails. The vector lives on the heap. + @attention The vector must be destroyed with vector_destroy() to free its memory. Failing to do so will result in a memory leak. + @attention The vector's initial capacity is set to 10. If you want to change the initial capacity globally, set the DLIBC_VECTOR_INITIAL_CAPACITY macro before including this header file. The initial capacity must be greater than 0. + @attention The vector's element size must be greater than 0. If you pass 0, the function will return NULL. + @attention The vector is created without an element destructor. If your elements own memory of their own, set one with vector_set_destructor(). +*/ +static inline vector_t* vector_create(size_t element_size) { + if (element_size == 0) { + return NULL; // Invalid element size + } + + #ifndef DLIBC_VECTOR_INITIAL_CAPACITY + size_t initial_capacity = 10; // Default initial capacity + #else + #if DLIBC_VECTOR_INITIAL_CAPACITY <= 0 + #error "DLIBC_VECTOR_INITIAL_CAPACITY must be greater than 0" + #endif + size_t initial_capacity = DLIBC_VECTOR_INITIAL_CAPACITY; // Use the macro if defined + #endif + + if (SIZE_MAX / element_size < initial_capacity) { + return NULL; // Prevent overflow + } + + vector_t* vec = (vector_t*)malloc(sizeof(vector_t)); + if (!vec) { + return NULL; // Allocation failed + } + + vec->size = 0; + vec->capacity = initial_capacity; + vec->element_size = element_size; + vec->destructor = NULL; // Initialize destructor to NULL + vec->data = malloc(vec->capacity * vec->element_size); + if (!vec->data) { + free(vec); + return NULL; // Allocation failed + } + + return vec; +} + +/* + @brief Sets the destructor function for the vector's elements. This function will be called on each element as it leaves the vector, allowing for custom cleanup of dynamically allocated memory within the elements. + @param vec A pointer to the vector for which to set the destructor. + @param destructor A function pointer to the destructor function, or NULL to remove the current one. The function should take a single void* parameter, which will be a pointer to the element to be destroyed. + @return 0 on success, -1 if the vector is NULL. + @attention If you do not set a destructor function, the vector will not automatically free any dynamically allocated memory within its elements when it is destroyed. You must ensure that you free any such memory manually before destroying the vector to avoid memory leaks. + @attention The destructor is passed a pointer to the element's slot inside the vector's data array, not a pointer returned by malloc(). It must free what the element owns and must never free the pointer it is given. For a vector of char*, the destructor body is free(*(char**)element); + @attention This assumes that your destructor function is valid and safe to call. If it isn't, bad things will happen and it will not be nice to watch. + @attention Set the destructor before adding any elements. Setting it on a vector that already holds elements is allowed, but elements that were removed beforehand will not have been destroyed. +*/ +static inline int vector_set_destructor(vector_t* vec, vector_destructor_t destructor) { + if (!vec) { + return -1; // Invalid vector + } + + vec->destructor = destructor; + return 0; // Success +} + +/* + @brief Gets the destructor function currently set on the vector. + @param vec A pointer to the vector whose destructor is to be retrieved. + @return The vector's destructor function pointer, or NULL if the vector is NULL or has no destructor set. +*/ +static inline vector_destructor_t vector_get_destructor(const vector_t* vec) { + if (!vec) { + return NULL; // Invalid vector + } + + return vec->destructor; +} + +/* + @brief Ensures the vector has room for at least the specified number of elements. + @param vec A pointer to the vector for which to reserve space. + @param new_capacity The minimum capacity the vector should have. + @return 0 on success, -1 if the vector is NULL, its element size is zero, or allocation fails. + @attention Capacity never decreases. If the vector already has room, this is a no-op and reports success. + @attention If the buffer does grow, it is reallocated, so any pointers into the vector's data are invalidated. Use vector_prune() to release unused memory. +*/ +static inline int vector_reserve(vector_t* vec, size_t new_capacity) { + if (!vec) { + return -1; // Invalid vector + } + + if (vec->element_size == 0) { + return -1; // Not a usable vector; also guards the division below + } + + if (new_capacity <= vec->capacity) { + return 0; // Already have the room. Never shrink + } + + if (new_capacity > SIZE_MAX / vec->element_size) { + return -1; // Prevent overflow + } + + void* new_data = realloc(vec->data, new_capacity * vec->element_size); + if (!new_data) { + return -1; // Allocation failed + } + + vec->data = new_data; + vec->capacity = new_capacity; + + return 0; // Success +} + +/* + @brief Grows the vector's capacity to make room for at least one more element. Used internally by the functions that add elements. + @param vec A pointer to the vector to grow. + @return 0 on success, -1 if the vector is NULL or allocation fails. + @attention This is an internal helper. You are not expected to call it directly. +*/ +static inline int vector_grow(vector_t* vec) { + if (!vec) { + return -1; // Invalid vector + } + + if (vec->capacity > SIZE_MAX / 2) { + return -1; // Prevent overflow + } + + size_t new_capacity = vec->capacity > 0 ? vec->capacity * 2 : 1; // Double the capacity, or set to 1 if it was 0 + return vector_reserve(vec, new_capacity); +} + +/* + @brief Prunes the vector to free unused memory. If the vector's size is less than its capacity, this function will reallocate the vector's data array to match its size, freeing any unused memory. + @param vec A pointer to the vector to be pruned. + @return 0 on success, -1 if the vector is NULL or allocation fails. + @attention After calling this function, the vector's capacity will be equal to its size. Any pointers to the old data will be invalidated. + @attention Capacity may never drop below 1, even if the vector is empty. +*/ +static inline int vector_prune(vector_t* vec) { + if (!vec) { + return -1; // Invalid vector + } + + if (vec->element_size == 0) { + return -1; // Not a usable vector; also guards the division below + } + + if (vec->size < vec->capacity) { + size_t new_capacity = vec->size > 0 ? vec->size : 1; // Ensure capacity is at least 1 + + if (new_capacity > SIZE_MAX / vec->element_size) { + return -1; // Prevent overflow + } + + void* new_data = realloc(vec->data, new_capacity * vec->element_size); + if (!new_data) { + return -1; // Allocation failed + } + + vec->data = new_data; + vec->capacity = new_capacity; + } + + return 0; // Success +} + +/* + @brief Adds an element to the end of the vector. + @param vec A pointer to the vector to which the element will be added. + @param element A pointer to the element to be added. The element will be copied into the vector's data array. + @return 0 on success, -1 if the vector is NULL, the element is NULL, if the element is an owning element that is aliased in the vector and a destructor is set, or if reservation fails. + @attention The element must be a pointer to a valid memory location containing data of the same type as the vector's element type. The vector will make a copy of the data, so the original element can be modified or freed after this function returns. + @attention The element may point into the vector's own data array. If growing the vector moves the data, the pointer is followed to its new location automatically. +*/ +static inline int vector_push_back(vector_t* vec, const void* element) { + if (!vec || !element) { + return -1; // Invalid vector or element + } + + if (vec->destructor && vector_is_aliased(vec, element)) { + return -1; // Refuse to push an owning element that lives inside the vector + } + + if (vec->size >= vec->capacity) { + // If the element lives inside our own data array, remember where it sits + // so that we can find it again after the data has been reallocated. + // This must happen before the growth, while the old buffer is still valid + size_t offset = 0; + int aliased = vector_is_aliased(vec, element); + if (aliased) { + offset = (size_t)((const char*)element - (const char*)vec->data); + } + + if (vector_grow(vec) != 0) { + return -1; // Failed to grow the vector + } + + if (aliased) { + element = (const char*)vec->data + offset; + } + } + + // Copy the new element into the vector's data array + memmove((char*)vec->data + (vec->size * vec->element_size), element, vec->element_size); + vec->size++; + + return 0; // Success +} + +/* + @brief Removes the last element from the vector. + @param vec A pointer to the vector from which the element will be removed. + @return 0 on success, -1 if the vector is NULL or empty. + @attention After calling this function, the vector's size will be reduced by one. The memory occupied by the removed element will not be freed automatically unless a destructor is set (in which case, the destructor will be called on the element being removed if it is set). Use vector_take_back() to retrieve the last element, remove it from the vector, and NOT call the destructor on it (hands ownership to the caller). +*/ +static inline int vector_pop_back(vector_t* vec) { + if (!vec || vec->size == 0) { + return -1; // Invalid vector or empty vector + } + + if (vec->destructor) { + void* element = (char*)vec->data + ((vec->size - 1) * vec->element_size); + vec->destructor(element); // Call the destructor for the last element + } + + vec->size--; + return 0; // Success +} + +/* + @brief Removes the element at the specified index from the vector. + @param vec A pointer to the vector from which the element will be removed. + @param index The index of the element to be removed. + @return 0 on success, -1 if the vector is NULL or the index is out of bounds. + @attention After calling this function, the vector's size will be reduced by one. The memory occupied by the removed element will not be freed automatically unless a destructor is set (in which case, the destructor will be called on the element being removed if it is set). Use vector_take_at() to retrieve the element at the specified index, remove it from the vector, and NOT call the destructor on it (hands ownership to the caller). +*/ +static inline int vector_pop_at(vector_t* vec, size_t index) { + if (!vec || index >= vec->size) { + return -1; // Invalid vector or index out of bounds + } + + if (vec->destructor) { + void* element = (char*)vec->data + (index * vec->element_size); + vec->destructor(element); // Call the destructor for the element to be removed + } + + // Move elements after the index one position to the left + if (index + 1 < vec->size) { + memmove((char*)vec->data + (index * vec->element_size), + (char*)vec->data + ((index + 1) * vec->element_size), + (vec->size - index - 1) * vec->element_size); + } + + vec->size--; + return 0; // Success +} + +/* + @brief Removes the element at the specified index and transfers ownership of it to the caller. + @param vec A pointer to the vector from which the element will be taken. + @param index The index of the element to take. + @param out A pointer to a buffer of at least vector_element_size(vec) bytes, which the element is copied into. + @return 0 on success, -1 if the vector is NULL, out is NULL, out points into the vector's own data, or the index is out of bounds. + @attention The element destructor is deliberately NOT called. Whatever the element owns becomes the caller's responsibility to free. + @attention out must not point into the vector's own data array. Doing so would leave two slots owning the same memory and is rejected with -1. +*/ +static inline int vector_take_at(vector_t* vec, size_t index, void* out) { + if (!vec || index >= vec->size || !out) { + return -1; // Invalid vector, index out of bounds, or output pointer is NULL + } + + if (vector_is_aliased(vec, out)) { + return -1; // Refuse to take an element into a pointer that lives inside the vector + } + + void* slot = (char*)vec->data + (index * vec->element_size); + memcpy(out, slot, vec->element_size); // Copy the element to the output pointer + + // Move elements after the index one position to the left + if (index + 1 < vec->size) { + memmove(slot, + (char*)vec->data + ((index + 1) * vec->element_size), + (vec->size - index - 1) * vec->element_size); + } + + vec->size--; + return 0; // Success +} + +/* + @brief Removes the last element from the vector and transfers ownership of it to the caller. + @param vec A pointer to the vector from which the element will be taken. + @param out A pointer to a buffer of at least vector_element_size(vec) bytes, which the element is copied into. + @return 0 on success, -1 if the vector is NULL, empty, if out aliases the vector's data, or out is NULL. + @attention The element destructor is deliberately NOT called. Whatever the element owns becomes the caller's responsibility to free. +*/ +static inline int vector_take_back(vector_t* vec, void* out) { + if (!vec || vec->size == 0 || !out) { + return -1; // Invalid vector, empty vector, or output pointer is NULL + } + + return vector_take_at(vec, vec->size - 1, out); // Take the last element +} + +/* + @brief Inserts an element at the specified index in the vector. + @param vec A pointer to the vector into which the element will be inserted. + @param index The index at which to insert the element. + @param element A pointer to the element to be inserted. The element will be copied into the vector's data array. + @return 0 on success, -1 if the vector is NULL, the element is NULL, the index is out of bounds, if the element is an owning element that is aliased in the vector and a destructor is set, or if reservation fails. + @attention After calling this function, the vector's size will be increased by one and every element from the index onwards will have moved one position to the right. Any pointers into the vector are invalidated. + @attention The element may point into the vector's own data array. Its value is copied aside first, so that neither the reallocation nor the shifting of elements can corrupt it. +*/ +static inline int vector_insert(vector_t* vec, size_t index, const void* element) { + if (!vec || !element || index > vec->size) { + return -1; // Invalid vector, element, or index out of bounds + } + + if (vec->destructor && vector_is_aliased(vec, element)) { + return -1; // Refuse to insert an owning element that lives inside the vector + } + + // If the element lives inside our own data array, take a copy of it now. Both + // the reservation below and the shifting of elements would otherwise destroy + // the value before it can be read + void* temp = NULL; + if (vector_is_aliased(vec, element)) { + temp = malloc(vec->element_size); + if (!temp) { + return -1; // Allocation failed + } + + memcpy(temp, element, vec->element_size); + element = temp; + } + + if (vec->size >= vec->capacity) { + if (vector_grow(vec) != 0) { + free(temp); + return -1; // Failed to grow the vector + } + } + + // Move elements after the index one position to the right + memmove((char*)vec->data + ((index + 1) * vec->element_size), + (char*)vec->data + (index * vec->element_size), + (vec->size - index) * vec->element_size); + + // Copy the new element into the vector's data array at the specified index + memcpy((char*)vec->data + (index * vec->element_size), element, vec->element_size); + vec->size++; + + free(temp); // Harmless if no copy was needed, as temp is NULL in that case + return 0; // Success +} + +/* + @brief Clears all elements from the vector. + @param vec A pointer to the vector to be cleared. + @return 0 on success, -1 if the vector is NULL. + @attention After calling this function, the vector's size will be zero. The memory occupied by the elements will not be freed automatically unless a destructor is set. + @attention The vector's capacity is left untouched, so the vector may be refilled without reallocating. Call vector_prune() afterwards if you want the memory back. +*/ +static inline int vector_clear(vector_t* vec) { + if (!vec) { + return -1; // Invalid vector + } + + if (vec->destructor) { + for (size_t i = 0; i < vec->size; ++i) { + void* element = (char*)vec->data + (i * vec->element_size); + vec->destructor(element); // Call the destructor for each element + } + } + + vec->size = 0; + return 0; // Success +} + +/* + @brief Sets the element at the specified index in the vector, overwriting whatever was there before. + @param vec A pointer to the vector in which to set the element. + @param index The index of the element to set. + @param element A pointer to the element to set. The element will be copied into the vector's data array. + @return 0 on success, -1 if the vector is NULL, the element is NULL, if the element is an owning element that is aliased in the vector and a destructor is set, or if the index is out of bounds. + @attention This never grows the vector. The index must be below the current size. + @attention If a destructor is set, it is called on the element being overwritten before the new value is copied in. + @attention Setting an element to itself is a no-op and reports success. + @attention On a vector without a destructor, the element may point into the + vector's own data array; the copy handles any overlap. On a vector with a + destructor this is refused with -1, as it would leave two slots owning the + same memory. +*/ +static inline int vector_set(vector_t* vec, size_t index, const void* element) { + if (!vec || !element || index >= vec->size) { + return -1; // Invalid vector, element, or index out of bounds + } + + void* slot = (char*)vec->data + (index * vec->element_size); + if (slot == element) { + return 0; // Setting the element to itself, no action needed + } + + if (vec->destructor && vector_is_aliased(vec, element)) { + return -1; // Refuse to set an owning element that lives inside the vector + } + + if (vec->destructor) { + vec->destructor(slot); // Call the destructor for the element being overwritten + } + + memmove(slot, element, vec->element_size); // memmove, as the element may overlap the slot + + return 0; // Success +} + +/* + @brief Checks if the vector is empty. + @param vec A pointer to the vector to check. + @return 1 if the vector is empty, 0 if it is not empty. + @attention If the vector is NULL, this function will return 1 (is empty) to indicate that the vector is invalid. +*/ +static inline int vector_is_empty(const vector_t* vec) { + if (!vec) { + return 1; // Invalid vector + } + + return vec->size == 0; +} + +/* + @brief Gets a pointer to the first element in the vector. + @param vec A pointer to the vector from which to get the element. + @return A pointer to the first element in the vector, or NULL if the vector is NULL or empty. + @attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed. +*/ +static inline void* vector_front(vector_t* vec) { + if (!vec || vec->size == 0) { + return NULL; // Invalid vector or empty vector + } + + return vec->data; +} + +/* + @brief Gets a const pointer to the first element in the vector. Cannot be used to modify the element. + @param vec A pointer to the vector from which to get the element. + @return A constant pointer to the first element in the vector, or NULL if the vector is NULL or empty. + @attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed. +*/ +static inline const void* vector_front_const(const vector_t* vec) { + if (!vec || vec->size == 0) { + return NULL; // Invalid vector or empty vector + } + + return vec->data; +} + +/* + @brief Gets a pointer to the last element in the vector. + @param vec A pointer to the vector from which to get the element. + @return A pointer to the last element in the vector, or NULL if the vector is NULL or empty. + @attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed. +*/ +static inline void* vector_back(vector_t* vec) { + if (!vec || vec->size == 0) { + return NULL; // Invalid vector or empty vector + } + + return (char*)vec->data + ((vec->size - 1) * vec->element_size); +} + +/* + @brief Gets a const pointer to the last element in the vector. Cannot be used to modify the element. + @param vec A pointer to the vector from which to get the element. + @return A constant pointer to the last element in the vector, or NULL if the vector is NULL or empty. + @attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed. +*/ +static inline const void* vector_back_const(const vector_t* vec) { + if (!vec || vec->size == 0) { + return NULL; // Invalid vector or empty vector + } + + return (const char*)vec->data + ((vec->size - 1) * vec->element_size); +} + +/* + @brief Gets a pointer to the element at the specified index in the vector. + @param vec A pointer to the vector from which to get the element. + @param index The index of the element to get. + @return A pointer to the element at the specified index, or NULL if the vector is NULL or the index is out of bounds. + @attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed. + @attention For most read-only operations, consider using vector_get_const() instead, which returns a const pointer to the element. +*/ +static inline void* vector_get(vector_t* vec, size_t index) { + if (!vec || index >= vec->size) { + return NULL; // Invalid vector or index out of bounds + } + + return (char*)vec->data + (index * vec->element_size); +} + +/* + @brief Gets a constant pointer to the element at the specified index in the vector. Cannot be used to modify the element. + @param vec A pointer to the vector from which to get the element. + @param index The index of the element to get. + @return A constant pointer to the element at the specified index, or NULL if the vector is NULL or the index is out of bounds. + @attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed. +*/ +static inline const void* vector_get_const(const vector_t* vec, size_t index) { + if (!vec || index >= vec->size) { + return NULL; // Invalid vector or index out of bounds + } + + return (const char*)vec->data + (index * vec->element_size); +} + +/* + @brief Gets the size of the vector. + @param vec A pointer to the vector whose size is to be retrieved. + @return The size of the vector, or 0 if the vector is NULL. +*/ +static inline size_t vector_size(const vector_t* vec) { + if (!vec) { + return 0; // Invalid vector + } + + return vec->size; +} + +/* + @brief Gets the capacity of the vector. + @param vec A pointer to the vector whose capacity is to be retrieved. + @return The capacity of the vector, or 0 if the vector is NULL. +*/ +static inline size_t vector_capacity(const vector_t* vec) { + if (!vec) { + return 0; // Invalid vector + } + + return vec->capacity; +} + +/* + @brief Gets the size of each element in the vector. + @param vec A pointer to the vector whose element size is to be retrieved. + @return The size of each element in the vector, or 0 if the vector is NULL. +*/ +static inline size_t vector_element_size(const vector_t* vec) { + if (!vec) { + return 0; // Invalid vector + } + + return vec->element_size; +} + +/* + @brief Gets a pointer to the underlying C array of the vector. Cannot modify the vector through this pointer. + @param vec A pointer to the vector whose underlying array is to be retrieved. + @return A pointer to the underlying C array, or NULL if the vector is NULL. + @attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed. +*/ +static inline const void* vector_as_c_array(const vector_t* vec) { + if (!vec) { + return NULL; // Invalid vector + } + + return vec->data; +} + +/* + @brief Gets a pointer to the underlying C array of the vector. Can be used to modify the vector's elements. + @param vec A pointer to the vector whose underlying array is to be retrieved. + @return A pointer to the underlying C array, or NULL if the vector is NULL. + @attention The returned pointer is a generic. You should cast it to the appropriate type before using it. The pointer will become invalid if the vector is resized or destroyed. +*/ +static inline void* vector_as_c_array_mutable(vector_t* vec) { + if (!vec) { + return NULL; // Invalid vector + } + + return vec->data; +} + +/* + @brief Moves a vector to another vector, transferring ownership of the data. The destination vector will take ownership of the source vector's data, its element size and its destructor. + @param dest A pointer to the destination vector. + @param src A pointer to the pointer holding the source vector. It will be set to NULL. + @return 0 on success, -1 if the destination vector is NULL, the source pointer is NULL, the source vector is NULL, or both vectors share the same data pointer. + @attention After calling this function, the source vector will be freed (excluding data) and the source pointer will be set to NULL, so it cannot be used again. + @attention The destination vector's existing elements will be destroyed (using the destination's own destructor, if it has one) and its data freed. Ensure that you do not need the existing data before calling this function. + @attention Moving a vector onto itself is a no-op and reports success, leaving the vector untouched. + @attention Two vectors can only share a data pointer if a vector_t was duplicated by copying the struct, which is never valid. The move is refused rather than freeing a buffer the source still points at. +*/ +static inline int vector_move(vector_t* dest, vector_t** src) { + if (!dest || !src || !*src) { + return -1; // Invalid vectors + } + + if (dest == *src) { + return 0; // Moving to itself, no action needed + } + + if (dest->data == (*src)->data) { + return -1; // Refuse to move a vector onto another vector that shares its data + // Realistically, this is unreachable via legal use, but... safety. + // Or something. You WILL trigger this if you do shallow copies. DO NOT! + } + + // Destroy the destination vector's existing elements with its own destructor, + // then free its data, as it is about to be replaced + vector_clear(dest); + free(dest->data); + + // Transfer ownership of the source vector's data to the destination vector + dest->size = (*src)->size; + dest->capacity = (*src)->capacity; + dest->element_size = (*src)->element_size; + dest->data = (*src)->data; + dest->destructor = (*src)->destructor; // The elements keep the cleanup they came with + + // Reset the source vector to an empty state + (*src)->size = 0; + (*src)->capacity = 0; + (*src)->element_size = 0; + (*src)->data = NULL; + (*src)->destructor = NULL; + + free(*src); // Free the source vector structure, but not its data (ownership transferred) + *src = NULL; + + return 0; // Success +} + +/* + @brief Creates a deep copy of the vector, including its data. + @param vec A pointer to the vector to be copied. + @return A pointer to the newly created deep copy of the vector, or NULL if allocation fails, if the input vector is NULL, or if the input vector has a destructor set. + @attention The returned vector must be destroyed with vector_destroy() to free its memory. Failing to do so will result in a memory leak. + @attention Vectors with a destructor cannot be deep copied and this function will return NULL for them. The elements are copied byte for byte, so any memory they own would end up owned by both vectors and freed twice. If you need to copy such a vector, do it by hand: create a new vector and push copies of the elements into it yourself. +*/ +static inline vector_t* vector_deep_copy(const vector_t* vec) { + if (!vec) { + return NULL; // Invalid vector + } + + if (vec->destructor) { + return NULL; // Refuse to byte-copy elements that own memory + } + + vector_t* new_vec = vector_create(vec->element_size); + if (!new_vec) { + return NULL; // Allocation failed + } + + int r = vector_reserve(new_vec, vec->capacity); + if (r != 0) { + vector_destroy(&new_vec); + return NULL; // Allocation failed + } + + if (vec->size > 0) { + memcpy(new_vec->data, vec->data, vec->size * vec->element_size); + new_vec->size = vec->size; + } // vector_create() already sets size to 0 by default + + return new_vec; +} + +/* + @brief Destroys the vector and frees its memory. + @param vec A pointer to the pointer holding the vector to be destroyed. It will be set to NULL. + @return 0 on success, or if the vector was already NULL. -1 only if the pointer itself is NULL. + @attention After calling this function, the vector pointer should not be used again (It will be set to NULL). Accessing it after destruction will lead to undefined behavior. + @attention Only the pointer you pass in is set to NULL. Copies of that pointer held elsewhere are left dangling and must not be used. + @attention If stored elements own memory of their own, set a destructor with vector_set_destructor() to have it cleaned up here. Otherwise, the vector will only free the memory allocated for the data array and the vector structure itself, but not any dynamically allocated memory within the elements. +*/ +static inline int vector_destroy(vector_t** vec) { + if (!vec) { + return -1; // Invalid pointer + } + + if (!(*vec)) { + return 0; // Already NULL, nothing to destroy + } + + vector_t* target = *vec; + + if (target->destructor) { + for (size_t i = 0; i < target->size; ++i) { + void* element = (char*)target->data + (i * target->element_size); + target->destructor(element); // Call the destructor for each element + } + } + + free(target->data); + free(target); + *vec = NULL; // Set the pointer to NULL to avoid dangling references + + return 0; // Success +} + +#endif // VECTOR_H diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..63f8805 --- /dev/null +++ b/src/main.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("Hello, World!\n"); + return 0; +}