test build
This commit is contained in:
@@ -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"
|
||||
@@ -0,0 +1,67 @@
|
||||
// MIT License
|
||||
// Copyright (c) 2022 Anotra
|
||||
// https://github.com/Anotra/anomap
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ANOMAP_H
|
||||
#define ANOMAP_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 <YEAR> <NAME>' 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 <[email protected]>, 19 Sept 2022
|
||||
* - __carray_init() should initialize its `size` value
|
||||
*
|
||||
* Modified by Lucas Müller <[email protected]>, 15 Feb 2022
|
||||
* - CARRAY_RESIZE() has a fallback value (+1)
|
||||
*
|
||||
* Modified by Lucas Müller <[email protected]>, 06 Feb 2022
|
||||
* - __carray_init() accept initial length
|
||||
*
|
||||
* Modified by Lucas Müller <[email protected]>, 02 Feb 2022
|
||||
* - remove free(carray) at __carrray_free()
|
||||
*
|
||||
* Modified by Lucas Müller <[email protected]>, 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 <[email protected]>, 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
|
||||
@@ -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 */
|
||||
@@ -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 <YEAR> <NAME>' 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 ([email protected]), 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
|
||||
@@ -0,0 +1,464 @@
|
||||
/* Clocks (v1)
|
||||
* Portable Snippets - https://github.com/nemequ/portable-snippets
|
||||
* Created by Evan Nemerson <[email protected]>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
# 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 <assert.h>
|
||||
|
||||
#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
|
||||
<limits.h> (<features.h> isn't available everywhere). */
|
||||
#if defined(__unix__) || defined(__unix) || defined(__linux__)
|
||||
# include <limits.h>
|
||||
# include <unistd.h>
|
||||
#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 <time.h>
|
||||
# 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 <time.h>
|
||||
#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 <sys/time.h>
|
||||
#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 <windows.h>
|
||||
#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 <sys/time.h>
|
||||
# include <sys/resource.h>
|
||||
#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 <CoreServices/CoreServices.h>
|
||||
# include <mach/mach.h>
|
||||
# include <mach/mach_time.h>
|
||||
#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) */
|
||||
@@ -0,0 +1,137 @@
|
||||
#ifndef COG_UTILS_H
|
||||
#define COG_UTILS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "gencodecs.h"
|
||||
|
||||
#ifdef GENCODECS_HEADER
|
||||
PP_INCLUDE(<inttypes.h>)
|
||||
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"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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(<stdio.h>)
|
||||
GENCODECS_PP_INCLUDE(<stdlib.h>)
|
||||
GENCODECS_PP_INCLUDE(<string.h>)
|
||||
# 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(<stddef.h>)
|
||||
GENCODECS_PP_INCLUDE(<stdbool.h>)
|
||||
# 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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef CONCORD_IO_POLLER_H
|
||||
#define CONCORD_IO_POLLER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
/**
|
||||
* @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
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 */
|
||||
@@ -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 <stddef.h>
|
||||
|
||||
#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 */
|
||||
@@ -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 <stddef.h>
|
||||
|
||||
#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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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 */
|
||||
@@ -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 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <stddef.h>
|
||||
|
||||
/** @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 <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 <stdlib.h>
|
||||
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/* Copyright (c) 2013, Ben Noordhuis <[email protected]>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
|
||||
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_ */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Mathias Brossard <[email protected]>.
|
||||
* 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_ */
|
||||
@@ -0,0 +1,66 @@
|
||||
/** @file types.h */
|
||||
|
||||
#ifndef CONCORD_TYPES_H
|
||||
#define CONCORD_TYPES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/** @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 */
|
||||
@@ -0,0 +1,400 @@
|
||||
/** @file user-agent.h */
|
||||
|
||||
#ifndef USER_AGENT_H
|
||||
#define USER_AGENT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
/** @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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,326 @@
|
||||
/**
|
||||
* @file websockets.h
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETS_H
|
||||
#define WEBSOCKETS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
Reference in New Issue
Block a user