Giter Site home page Giter Site logo

cogmasters / concord Goto Github PK

View Code? Open in Web Editor NEW
480.0 3.0 24.0 36.14 MB

A Discord API wrapper library made in C

Home Page: https://cogmasters.github.io/concord/

License: MIT License

Makefile 0.84% C 99.11% Shell 0.03% M4 0.02%
c linux windows discord websockets discord-bot api-rest api-client api-wrapper discord-library discord-api bsd

concord's Introduction

Concord Logo

Concord - C Discord API library

discord-shield migrating-shield

About

Concord is an asynchronous C99 Discord API library with minimal external dependencies, and a low-level translation of the Discord official documentation to C code.

Examples

The following are minimalistic examples, refer to examples/ for a better overview.

Slash Commands (new method)

Note: you need to replace GUILD_ID with an actual guild ID, or this example won't compile! You can use a macro to do this: #define GUILD_ID 1234567898765431

#include <string.h>
#include <concord/discord.h>

void on_ready(struct discord *client, const struct discord_ready *event) {
    struct discord_create_guild_application_command params = {
        .name = "ping",
        .description = "Ping command!"
    };
    discord_create_guild_application_command(client, event->application->id,
                                             GUILD_ID, &params, NULL);
}

void on_interaction(struct discord *client, const struct discord_interaction *event) {
    if (event->type != DISCORD_INTERACTION_APPLICATION_COMMAND)
        return; /* return if interaction isn't a slash command */

    if (strcmp(event->data->name, "ping") == 0) {
          struct discord_interaction_response params = {
                .type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE,
                .data = &(struct discord_interaction_callback_data){
                      .content = "pong"
                }
          };
          discord_create_interaction_response(client, event->id,
                                              event->token, &params, NULL);
    }
}

int main(void) {
    struct discord *client = discord_init(BOT_TOKEN);
    discord_set_on_ready(client, &on_ready);
    discord_set_on_interaction_create(client, &on_interaction);
    discord_run(client);
}

Message Commands (old method)

#include <string.h>
#include <concord/discord.h>
#include <concord/log.h>

void on_ready(struct discord *client, const struct discord_ready *event) {
    log_info("Logged in as %s!", event->user->username);
}

void on_message(struct discord *client, const struct discord_message *event) {
    if (strcmp(event->content, "ping") == 0) {
        struct discord_create_message params = { .content = "pong" };
        discord_create_message(client, event->channel_id, &params, NULL);
    }
}

int main(void) {
    struct discord *client = discord_init(BOT_TOKEN);
    discord_add_intents(client, DISCORD_GATEWAY_MESSAGE_CONTENT);
    discord_set_on_ready(client, &on_ready);
    discord_set_on_message_create(client, &on_message);
    discord_run(client);
}

Supported operating systems (minimum requirements)

  • GNU/Linux 4.x
  • FreeBSD 12
  • NetBSD 8.1
  • Windows 7 (Cygwin)
  • GNU/Hurd 0.9
  • Mac OS X 10.9

Note: big-endian processors running certain OSes like SPARC Solaris, PowerPC AIX, System Z z/OS or Linux, or MIPS IRIX are NOT supported. There are currently a few issues that prevent some of the logic from correctly on big-endian systems. This will be fixed soon.

Build Instructions

The only dependency is curl-7.56.1 or higher. If you are compiling libcurl from source, you will need to build it with SSL support.

On Windows

  • Install Cygwin
  • Make sure that you installed libcurl, gcc, make, and git when you ran the Cygwin installer!
  • You will want to check the Windows tutorial here!
  • Mingw64 and Msys2 are currently NOT supported. Please see this for more information.
  • Once installed, compile it normally like you would on UNIX/Linux/OS X/BSD.
  • Note: you will likely need to include -L/usr/local/lib -I/usr/local/include on your gcc command, or in your CFLAGS variable in your Makefile for your bot.

On Linux, BSD, and Mac OS X

(note -- # means that you should be running as root)

Ubuntu and Debian

# apt update && apt install -y libcurl4-openssl-dev

Void Linux

# xbps-install -S libcurl-devel

Alpine

# apk add curl-dev

FreeBSD

$ pkg install curl

OS X

  • Note: you will need to install Xcode, or at a minimum, the command-line tools with xcode-select --install.
$ brew install curl (Homebrew)
$ port install curl (MacPorts)

Arch Linux / Manjaro (Arch based)

git clone https://aur.archlinux.org/concord-git.git
cd concord-git
makepkg -Acs
pacman -U concord-git-version-any.pkg.tar.zst

Alternatively, you can use an AUR helper:

yay -S concord-git

Setting up your environment

Clone Concord into your workspace

$ git clone https://github.com/cogmasters/concord.git && cd concord

Compile Concord

$ make

Special notes for non-Linux systems

You might run into trouble with the compiler and linker not finding your Libcurl headers. You can do something like this:

$ CFLAGS=-I<some_path> LDFLAGS=-L<some_path> make

For instance, on a FreeBSD system:

$ CFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib make

On OS X using MacPorts:

$ CFLAGS=-I/opt/local/include LDFLAGS=-L/opt/local/lib make

On OS X using a self-compiled libcurl:

$ CFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/include make

On Windows with Cygwin, you might need to pass both arguments to use POSIX threading:

$ CFLAGS="-pthread -lpthread" make

Special note about linking Concord against another library

In some cases, you might want to link Concord into a shared object, or link it as a shared object into another shared object. In that case, you will need to compile Concord with CFLAGS="-fpic" make.

Configuring Concord

discord_config_init() is the initialization method that allows configuring your bot without recompiling.

The following outlines config.json fields:

{
  "logging": { // logging directives
    "level": "trace",        // trace, debug, info, warn, error, fatal
    "filename": "bot.log",   // the log output file
    "quiet": false,          // change to true to disable logs in console
    "overwrite": true,       // overwrite file if already exists, append otherwise
    "use_color": true,       // display color for log entries
    "http": {
      "enable": true,        // generate http specific logging
      "filename": "http.log" // the HTTP log output file
    },
    "disable_modules": ["WEBSOCKETS", "USER_AGENT"] // disable logging for these modules
  },
  "discord": { // discord directives
    "token": "YOUR-BOT-TOKEN",         // replace with your bot token
    "default_prefix": {                 
      "enable": false,                 // enable default command prefix
      "prefix": "YOUR-COMMANDS-PREFIX" // replace with your prefix
    }
  },
  ... // here you can add your custom fields *
}

* Your custom field contents can be fetched with discord_config_get_field()

Test Copycat-Bot

  1. Get your bot token and add it to config.json, by assigning it to discord's "token" field. There are well written instructions from discord-irc explaining how to get your bot token and adding it to a server.
  2. Build example executables:
    $ make examples
  3. Run Copycat-Bot:
    $ cd examples && ./copycat

Get Copycat-Bot Response

Type a message in any channel the bot is part of and the bot should send an exact copy of it in return.

Terminate Copycat-Bot

With Ctrl+c or with Ctrl+|

Configure your build

The following outlines special flags and targets to override the default Makefile build with additional functionalities.

Special compilation flags

  • -DCCORD_SIGINTCATCH
    • By default Concord will not shutdown gracefully when a SIGINT is received (i.e. Ctrl+c), enable this flag if you wish it to be handled for you.
  • -DCCORD_DEBUG_WEBSOCKETS
    • Enable verbose debugging for WebSockets communication.
  • -DCCORD_DEBUG_HTTP
    • Enable verbose debugging for HTTP communication.

Example:

$ CFLAGS="-DCCORD_SIGINTCATCH -DCCORD_DEBUG_HTTP" make

Special targets

  • make shared
    • Produce a dynamically-linked version of Concord. This Makefile is intended for GNU-style compilers, such as gcc or clang.
  • make shared_osx
    • Produce a dynamically-linked version of Concord, for OS X and Darwin systems.
  • make voice
    • Enable experimental Voice Connection handling - not production ready.
  • make debug
    • Enable some flags useful while developing, such as -O0 and -g

Installing Concord

(note -- # means that you should be running as root)

# make install

This will install the headers and library files into $PREFIX. You can override this as such:

# PREFIX=/opt/concord make install

Included dependencies

The following are stable and well documented dependencies that are packaged with Concord and can be included to your projects:

File Description
cog-utils General purpose functions aimed at portability
log.c* A simple C99 logging library
carray* Macro-based implementation of type-safe arrays
anomap* Sorted key/value storage for C99
chash* Macro-based implementation of type-safe hashtables
json-build Tiny, zero-allocation JSON serializer
jsmn-find Tiny, zero-allocation JSON tokenizer

* Concord uses its own modified version that may be not up to date with the original

Note that included headers must be concord/ prefixed:

#include <concord/discord.h>
#include <concord/log.h>

Standalone executable

GCC

$ gcc myBot.c -o myBot -pthread -ldiscord -lcurl

Clang

$ clang myBot.c -o myBot -pthread -ldiscord -lcurl

UNIX C compilers

This includes the following compilers:
  • IBM XL C/C++ (AIX, z/OS, IBM i)
  • Sun/Oracle Studio (Solaris)
  • IRIX MIPSpro C++ (IRIX) -- NOTE: currently not supported
  • HP aCC (HP-UX)
  • Compaq C (Tru64 UNIX) -- NOTE: also currently not supported Note: if you want to actually compile this on one of the systems listed above, please see the "Compiling on old computers" guide.
$ cc myBot.c -o myBot -ldiscord -lcurl -lpthread

Note: some systems such as Cygwin require you to do this:

$ gcc myBot.c -o myBot -pthread -lpthread -ldiscord -lcurl

(this links against libpthread.a in /usr/lib)

Recommended debuggers

First, make sure your executable is compiled with the -g flag to ensure human-readable debugger messages.

Valgrind

Using valgrind to check for memory leaks:

valgrind --leak-check=full ./myBot

For a more comprehensive guide check Valgrind's Quick Start.

GDB

Using GDB to check for runtime errors, such as segmentation faults:

$ gdb ./myBot

And then execute your bot from the gdb environment:

(gdb) run

If the program has crashed, get a backtrace of the function calls leading to it:

(gdb) bt

For a more comprehensive guide check Beej's Quick Guide to GDB

Support

Problems? Check out our Discord Server

Contributing

All kinds of contributions are welcome, all we ask is to abide to our guidelines! If you want to help but is unsure where to get started then our Discord API Roadmap is a good starting point. Check our links for more helpful information.

Getting Started

Useful links

concord's People

Contributors

anotra avatar azbantium avatar bchiu3 avatar frityet avatar furmissile avatar hackersmacker avatar interlinked1 avatar jdeokkim avatar julienraynal avatar lcsmuller avatar mateuskater avatar mlite avatar mrtuxa avatar papaulogamerofc avatar probablynotartyom avatar robherc avatar scinscinscin avatar soujithenria avatar straight-into-the-wall avatar synth9283 avatar tarbomb avatar thechosenprometheanking avatar thepedroo avatar tristanwellman avatar yattaro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

concord's Issues

<struct discord_message>.timestamp is not being set

Describe the bug
Describe what you see that (you think) is wrong.

The library is not setting the timestamp member in discord_message struct instances.

Expected behavior

I expect the member to be set to the timestamp of when the message was sent

Screenshots
image
image

To Reproduce

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "concord/discord.h"

void on_message_create(struct discord *client, const struct discord_message *msg)
{
	if (msg->author->bot)
		return;

	printf("A message was created at: %lu\n", msg->timestamp);
}

int main(int argc, char *argv[])
{
	const char *config_file;
	if (argc > 1)
		config_file = argv[1];
	else
		config_file = "./config.json";

	ccord_global_init();
	struct discord *client = discord_config_init(config_file);
	assert(NULL != client && "Couldn't initialize client");

	discord_set_on_message_create(client, &on_message_create);
	discord_run(client);

	discord_cleanup(client);
	ccord_global_cleanup();
}

Version
I installed the library from source yesterday, but I deleted the cloned repo so I can't give the top hash

No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.4 LTS
Release:	20.04
Codename:	focal

Build fails on OpenBSD 7.1

Describe the bug

There is a bit of a problem when building on OpenBSD, which seems to be from the codecs generation.
This happens on a fresh OpenBSD installation, with all dependencies for Concord installed.

Expected behavior

The expected behavior is that the build should complete correctly-- it does not.

Screenshots

Here is a textual error log, as well as some screenshots.
image

To Reproduce

Install a new OpenBSD virtual machine, pkg_add git, clone Concord, and perform make

Version

072e414

Signal Safety in concord-once.h

Describe the bug

When using cflag CCORD_SIGINTCATCH, the signal handler will call fputs(unsafe), and locks and unlocks a mutex(unsafe).

Version

fputs has been there since inception, mutex was introduced in PR #140

Just a question

Can I see an example of bot which doesn't use config file (datas are already given while coding)
Thank

Make Error - Windows, Cygwin

Describe the bug

I was trying to build the bot and encountered a Make error, that I don't know how to solve. I tried running the Cygwin terminal as administrator along with cygstart --action=runas make and no luck.

Expected behavior

Expected it to be built just fine.

To Reproduce

Install Cygwin normally, along with the packages required by the repository, clone the repository, enter, run make and this happens.

Version

Latest, as of writing this.
Windows 10 64-bit.

Stack trace

make[1]: Entering directory 'C:/cygwin64/home/user11/concord/core'
cc -O2 -std=c99 -pthread -D_XOPEN_SOURCE=600 -DLOG_USE_COLOR -I. -I/usr/local/in
clude -c -o cog-utils.o cog-utils.c
process_begin: CreateProcess(C:\cygwin64\bin\cc, cc -O2 -std=c99 -pthread -D_XOP
EN_SOURCE=600 -DLOG_USE_COLOR -I. -I/usr/local/include -c -o cog-utils.o cog-uti
ls.c, ...) failed.
make (e=5): Access is denied.
make[1]: *** [: cog-utils.o] Error 5
make[1]: Leaving directory 'C:/cygwin64/home/user11/concord/core'
make: *** [Makefile:26: static] Error 2

Add guides to Concord

Describe the feature

This issue is for keeping track of the many guides that should help make Concord a more welcoming place to newcomers. If you got any suggestions, feel free to write them down below!

NOTE: all of the following should be added to docs/guides/

Acceptance criteria

  • Add installation & preparations guide
    • How to install concord
    • Setting up a bot application
    • Adding bot to servers
  • Getting the bot started
    • Setting up with the config.json files
    • Setting up with env variables
  • Miscellaneous
    • FAQ
    • Concord's async workflow
    • Concord's timers
    • Concord's caching
    • Concord's dispatcher
    • Sending attachments
    • Slash Commands
    • Message Components
    • Threads
    • Embeds
    • Reactions
    • Permissions
    • Gateway intents
    • Webhooks
    • Errors
    • Audit Logs
  • Other topics
    • Using a DB with concord
    • Debugging (GDB, Valgrind, ...)
    • Managing your bot process
    • Architecture
    • C99 syntax quirks used by Concord (with emphasis on Designated Initializers and Compound Literals)
    • Concord on old systems
    • Using an external SSL proxy
  • Internal
    • Gencodecs
    • Makefile build-system (explain BSD Make quirks)

Version

v2.1.0

ThreadSanitizer reports destruction of locked mutex in threadpool_free.

Describe the bug

When I call discord_cleanup in a build using TSAN, I get diagnostic messages stating a locked mutex is being destroyed.

WARNING: ThreadSanitizer: destroy of a locked mutex (pid=24515)
    #0 pthread_mutex_destroy ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1244 (libtsan.so.0+0x39398)
    #1 threadpool_free <null> (game.so+0xcea1d)
    [redacted]

  and:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
    #1 threadpool_free <null> (game.so+0xcea15)
    [redacted]

Looking in threadpool.c and the threadpool_free function it seems you are intentionally locking the mutex just prior to deletion. I don't think this impacts the program's functionality in any way, but it is undefined behaviour according to the spec.

Expected behavior

If the lock is just to make sure anything else that could have been using it has terminated, then you should be able to add in an unlock immediately after the lock and prior to the destroy. If there is a concern that something else could try to initiate a new lock on the mutex at this point in the code where you're destroying it, then I'd imagine that is a completely separate concurrency issue that would need to be resolved elsewhere.

Version

commit 1da91a6 (HEAD, tag: v2.1.0)

Distributor ID: Ubuntu
Description: Ubuntu 22.04.1 LTS
Release: 22.04
Codename: jammy

Additional context

I am not attempting to debug or QA concord's threading implementation. I am merely debugging my own threaded code in my library that uses concord and was very confused upon seeing these diagnostics. The similar orca library has the same "issue" fyi and given that it does not seem to actually have any negative impact aside from that noise in tsan when closing my application, it's understandable if you elect to just leave it as it is.

`discord_bulk_overwrite_global_application_command` doesn't work with partial commands

Describe the bug
Calling discord_bulk_overwrite_global_application_command with partial application command data results in an API error, stating that "the command has an invalid ID".

Expected behavior
Bulk overwriting of commands should work with partial application command data.

To Reproduce
Any call to discord_bulk_overwrite_global_application_command with application commands with no IDs should trigger the bug.

Version
aa57b19 (master)

Stack trace
N/A

Additional context
I briefly looked at the issue with a debugger, looks like the issue is that the library serializes the ID of the partial commands (which is 0), which results in the Discord API erroring out. The correct behavior would be to omit the ID from the body.

Trying to enable logging via functions doesn't work.

Describe the bug
If you try to use the log functions to enable logging, it won't enable.

Expected behavior
It would enable the logging.

To Reproduce

struct logconf *conf = discord_get_logconf(client);
logconf_set_quiet(conf, false);

Version
Latest, dev

discord_modify_channel rtc_region issue

Hello,

Using discord_modify_channel results in the following error :
[31mERROR๏ฟฝ[0m ๏ฟฝ[90mdiscord-rest_request.c:391:๏ฟฝ[0m [DISCORD_REQUEST] {"code": 50035, "errors": {"rtc_region": {"_errors": [{"code": "BASE_TYPE_CHOICES", "message": "Value must be one of ('brazil', 'hongkong', 'india', 'japan', 'rotterdam', 'russia', 'singapore', 'south-korea', 'southafrica', 'sydney', 'us-central', 'us-east', 'us-south', 'us-west')."}]}}, "message": "Invalid Form Body"}

It happens when I want to rename a discord channel. The channel doesn't get renamed, the request fails.

It doesn't work on the the version 2.1.0, 2.2.0, and the current master branch.

Do you observe the same issue ?

Thanks,

Support HTTP 1.1

Describe the feature

Update from HTTP 1.0 to HTTP 1.1, should be a straightforward curl_easy_setopt() operation.

Acceptance criteria

  • Locate libcurl's option for enabling HTTP 1.1, and enable it at discord-rest_request.c
  • Make sure tests are still functional

Version

V2.1.0

Websocket payloads taking long to be sent

Describe the bug

One of the payloads requested to be sent (not the Discord websocket) sometimes takes too long to be sent (up to ~15 seconds), while the first one rapidly is sent.

Expected behavior

The websocket payload in some milliseconds after concord websocket say it has been sent, the server receive the payload.

To Reproduce

You can reproduce it compiling my repo of a Concord music bot and start playing a music.

Version

Concord version: latest dev
Flags used while compiling Concord: -DCCORD_SIGINTCATCH
OS: Ubuntu 22 latests [x64]

Additional context

The server (Lavalink) is running locally, and the first payload is rapidly sent (in milliseconds), just the second one that takes way too long (much more than expected).
Older versions of the codebase of this bot, manually implementing the websocket directly to the discord_run (custom version) is much faster than the actual one using on_cycle to send payloads.

Code:

void on_cycle(struct discord *client) {
  (void) client;
  uint64_t tstamp;
  ws_easy_run(g_ws, 5, &tstamp);
}
...

// Function used to send payloads:
void sendPayload(char payload[], char *payloadOP) {
  if (ws_send_text(g_ws, NULL, payload, strlen(payload)) == false) {
    log_fatal("[LIBCURL] Something went wrong while sending a payload with op %s to Lavalink.", payloadOP);
    return;
  } else {
    log_debug("[LIBCURL] Sucessfully sent a payload with op %s to Lavalink.", payloadOP);
  }
}

// Inside main function:
  struct ws_callbacks cbs = {
    .on_text = &on_text,
    .on_connect = &on_connect,
    .on_close = &on_close,
    .data = client
  };

  CURLM *mhandle = curl_multi_init();
  g_ws = ws_init(&cbs, mhandle, NULL); // MEMORY LEAK (2)

  struct ccord_szbuf_readonly value = discord_config_get_field(client, (char *[2]){ "lavalink", "hostname" }, 2);
  snprintf(lavaHostname, sizeof(lavaHostname), "%.*s", (int)value.size, value.start);

  char lavaWs[256];
  snprintf(lavaWs, sizeof(lavaWs), "wss://%s", lavaHostname);

  ws_set_url(g_ws, lavaWs, NULL);

  ws_start(g_ws);

  value = discord_config_get_field(client, (char *[2]){ "lavalink", "password" }, 2);
  snprintf(lavaPasswd, sizeof(lavaPasswd), "%.*s", (int)value.size, value.start);

  const struct discord_user *bot = discord_get_self(client);

  snprintf(botID, sizeof(botID), "%"PRIu64"", bot->id);

  ws_add_header(g_ws, "Authorization", lavaPasswd);
  ws_add_header(g_ws, "Num-Shards", "1"); // You may want to change this.
  ws_add_header(g_ws, "User-Id", botID);
  ws_add_header(g_ws, "Client-Name", "MusicBotWithConcord");

Support `X-Audit-Log-Reason` header

Describe the feature

We should support the X-Audit-Log-Reason header for any request that may include it, and the option of passing the reason string

Acceptance criteria

  • Add an optional reason parameter for requests that may accept it, and included, send the request with a X-Audit-Log-Reason header field included

Version

v2.1.0

Additional context

Audit Log

Raspberry Pi (64-bit) library setup problem

Describe the bug

Something with the installation went wrong.

Screenshots

image

To Reproduce

Commands run on Raspberry Pi (64-bit):
apt update && apt install -y libcurl4-openssl-dev
git clone https://github.com/cogmasters/concord.git && cd concord
make
gcc myBot.c -o myBot -pthread -ldiscord -lcurl (either in concord or just outside of concord, still does not work)

Version

No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 11 (bullseye)
Release: 11
Codename: bullseye

Bucket is frozen after a 429 is triggered

Describe the bug

A bucket is frozen once a 429 is triggered, making so consecutive requests of the same bucket group never reach Discord.

Expected behavior

It shouldn't lock the bucket's incoming requests.

To Reproduce

The following example from @Anotra can trigger the issue:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <concord/discord.h>

#define TEST_FILE "..." // change to some file to be sent over Discord

static void
on_message_create(struct discord *client, const struct discord_message *message) {
  if (strcmp(message->content, "quit") == 0) {
    discord_shutdown(client);
  } else if (!message->author->bot) {
      discord_create_message(client, message->channel_id,
        &(struct discord_create_message) {
          .content = message->content,
        }, NULL);
      discord_create_message(client, message->channel_id,
        &(struct discord_create_message) {
          .attachments = &(struct discord_attachments) {
            .realsize = 1, .size = 1,
            .array = &(struct discord_attachment) {
              .filename = TEST_FILE,
            },
          }
        }, NULL);
  }
}

int
main() {
  struct discord *client = discord_config_init("../config.json");
  discord_add_intents(client, DISCORD_GATEWAY_MESSAGE_CONTENT);
  discord_set_on_message_create(client, on_message_create);
  discord_run(client);
  discord_cleanup(client);
}

Version

2.1.0

Unable to link concord static library to a shared library.

Describe the bug

I am receiving the following errors when linking libdiscord.a to a shared library.

/usr/bin/ld: concord-prefix/lib/libdiscord.a(discord-worker.o): warning: relocation against `g_tpool' in read-only section `.text'
/usr/bin/ld: concord-prefix/lib/libdiscord.a(user-agent.o): relocation R_X86_64_PC32 against symbol `L' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: bad value

Expected behavior

My personal preference would be that static libraries use -fPIC by default so they can be linked into either executables or shared libraries. Barring that, I think it would be perfectly fine though to just maybe have a simple mention in the README.md build instructions that you might need to run CLFLAGS="-fPIC" make if linking into a shared library. Alternatively a separate static_pic target could be added to the makefile, but I think that might be overkill when its easy enough to add to the CFLAGS as long as you know that's what you need to do.

To Reproduce

  • Clone fresh copy of repository.
  • Execute make or make static.
  • Compile a separate shared library (.so) and link to the generated libdiscord.a.

Version

This occurs on both master and dev branches.

commit 072e414 (HEAD -> master, origin/master, origin/HEAD)
commit d703210 (HEAD -> dev, origin/dev)

Distributor ID: Ubuntu
Description: Ubuntu 22.04.1 LTS
Release: 22.04
Codename: jammy

Support ETF formatting

Describe the feature

Support ETF formatting for WebSockets payloads, which is supposed to be a much more lightweight alternative to JSON.

Acceptance criteria

  • Create a gencodecs specs for initializing some data from its unique xxx_from_etf() method
  • We should be able to deserialize incoming ETF payloads.

Version

v2.1.0

Additional context

Erlpack implementation example
ETF details

Chash (used on Concord) possible not working on FreeBSD

Describe the bug

Chash seems to not be saving properly data, being unable to find the saved data when after passed some seconds of saving it.

Expected behavior

Chash should properly find the data related to the key.

To Reproduce

A example of this bug is in my lib repo, where it only NOT works with FreeBSD, where this could also affect Concord ratelimiting system, that also uses chash.

Version

Bug found on Concord latest dev, but could affect all versions with chash.

Additional context

The FreeBSD version is 13.1. and this issue was made, even not being directly related to Concord, because chash not working could cause the Concord ratelimiting to not work. This issue is NOT confirmed, if someone is able to test on FreeBSD (please check that Concord ratelimit functions that uses chash to save info only are called with some specific tasks, basic bots, like a ping-pong bot shouldn't use it in a normal condition).
Please check that the same code using chash was tested on Ubuntu, Arch Linux and Termux, all of them worked properly, except in FreeBSD.

Add alternative method for initializing a client with some settings

Describe the feature

Add an alternative method for initializing a client with some settings (such as logging), without requiring the config.json file. This would add greater flexibility to the user, as they might use their own custom logic for parsing of their configuration fields.

Possible implementation:

struct discord_settings settings = {
  .token = BOT_TOKEN,
  .prefix = "!",
  .logging = {
    .level = DISCORD_LOG_LEVEL_TRACE,
    .filename = "bot.log",
    .quiet = false,
    .http = "http.log",
  },
};
struct discord *client = discord_settings_init(&settings);

Acceptance criteria

  • Allows all options currently available at config.json
  • Modify discord_config_init() to be calling this new method underneath

Version

v2.1.0

messages: Support silent flag

Describe the feature

I use this library for an IRC relay (bot), and it would be helpful to be able to use the @silent feature that seems to exist now, particularly for IRC join/part/quit messages, to avoid notifying everyone on Discord when these events happen, as there are currently a lot of complaints about this.

The message flag is documented here: https://github.com/discord/discord-api-docs/pull/5894/files

I confirmed by analyzing the network requests that the only different when using this feature is the flag is indeed set to 4096 (1 << 12), as opposed to 0, so this appears to be correct.

It appears that this would be the right place to add the flag: https://github.com/Cogmasters/concord/blob/master/gencodecs/api/channel.PRE.h#L30

I'm less certain about how the flag would be used. The only place I see any flags being used is here:

.flags = DISCORD_MESSAGE_EPHEMERAL // 1 << 6

However, I would think that flags would need to be provided in create_message, e.g. here:

discord_create_message(struct discord *client,

Since I don't see this, would the ability to specify flags when sending a message need to be added as well, or is the other example the proper way to send messages if flags need to be added? I'm less sure about this, otherwise I think this should be a fairly straightforward addition.

Support Message Collectors

Describe the feature

Although Discord is no longer endorsing message-content-based bots, smaller bots should still be able to take full advantage of it in the future. For this reason, adding an API for Message Collectors can be of great use.

Acceptance criteria

  • Bots should be able to react to additional input after the first command is sent

Version

v2.1.0

Additional context

For references, see discord.js Collectors

HTTP_MIMEPOST and HTTP_POST should be the same value when generating a bucket key

Describe the bug

Because those are technically the same HTTP method, it should also be the same value when generating a bucket key. Currently Concord will generate two different buckets for the same route, leading to wrong ratelimiting handling for those buckets.

Expected behavior

Generate the same bucket key for some route using either method.

To Reproduce

The following code from @Anotra can reproduce the error, it will trigger a 429 even though we're handling ratelimiting:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <concord/discord.h>

#define TEST_FILE "..." // change to some file to be sent over Discord

static void
on_message_create(struct discord *client, const struct discord_message *message) {
  if (strcmp(message->content, "quit") == 0) {
    discord_shutdown(client);
  } else if (!message->author->bot) {
      discord_create_message(client, message->channel_id,
        &(struct discord_create_message) {
          .content = message->content,
        }, NULL);
      discord_create_message(client, message->channel_id,
        &(struct discord_create_message) {
          .attachments = &(struct discord_attachments) {
            .realsize = 1, .size = 1,
            .array = &(struct discord_attachment) {
              .filename = TEST_FILE,
            },
          }
        }, NULL);
  }
}

int
main() {
  struct discord *client = discord_config_init("../config.json");
  discord_add_intents(client, DISCORD_GATEWAY_MESSAGE_CONTENT);
  discord_set_on_message_create(client, on_message_create);
  discord_run(client);
  discord_cleanup(client);
}

Version

v2.1.0

Ship a pkg-config file

Describe the feature

Shipping a simple pkg-config file would make adding this library to CMake- or Meson-based projects more straightforward.

Version

Any, to my knowledge.

Additional context

This is what file would probably look like when installed:

prefix=/usr
libdir=${prefix}/lib
includedir=${prefix}/include

Name: Concord
Description: A Discord API wrapper library made in C
URL: https://cogmasters.github.io/concord/
Version: 2.2.1
Libs: -L${libdir} -ldiscord
Requires.private: libcurl
Cflags: -I${includedir}

The first line should be set to prefix used for installation. Probably just echoing into file directly from Makefile (and then calling install on it) is sufficient in this case.

Missing things.

discord_start_thread_with_message_params is missing rate_limit_per_user.
discord_start_thread_without_message_params is missing invitable & rate_limit_per_user.

zlib decompressing

Describe the feature

Support zlib payload decompressing for incoming WebSockets payloads.

Acceptance criteria

  • Compressed WebSockets payload should be decompressed on our end.

Version

v2.1.0

Additional context

Payload compression

Voice connection rewrite

Describe the feature

The current API for Voice Connection should be rewritten, as it relies on the outdated paradigm of starting a thread per voice connection, and does not support sending/receiving data packets. Voice adds extra dependencies, therefore, including it in the build should be optional.

Acceptance criteria

  • Support sending/receiving data packets
  • Support the basics such as joining a Voice Channel, disconnecting, and kicking a member

Version

v2.1.0

Additional context

Voice Connections
RTP Header
Opus
libsodium

User-defined memory functions

tl;dr

user-specified memory functions are hyperbased for a whole host of reasons, we've thoroughly discussed it in the discord, and I will make a PR to implement them Soonโ„ข unless someone objects, in which case we go back to the drawing board.

Status quo

Currently, concord uses the dynamic memory allocation routines provided by the OS. It does not check their return values, even though they can fail. This is probably fine -- not elegant or correct, but perfectly usable.

Proposed change

This backwards-compatible change would introduce a function ccord_global_init_memory, which would take as arguments the following five function pointers:

void *malloc_fn(size_t length), a malloc equivalent.
void *calloc_fn(size_t nmemb, size_t length), a calloc equivalent.
void *realloc_fn(void *old_ptr, size_t length), a realloc equivalent.
void free_fn(void *ptr), a free equivalent.
char *strdup_fn(const char *str), a strdup equivalent.

The names are irrelevant, please reply if you wish for them to be changed.

All functions must be thread-safe. They need not be async-signal-safe (Async-signal-safety would be impractical for many implementations).
The functions must behave as follows:

  • malloc_fn allocates exactly length bytes of memory and returns a pointer to it. It must return a valid pointer or abort execution. The returned pointer must be suitably aligned for any type. If length is 0 or exceeds PTRDIFF_MAX, the behavior is undefined.
  • calloc_fn allocates memory for exactly length items of size nmemb and returns a pointer to it. It must return a valid pointer or abort execution. The returned pointer must be suitably aligned for any type. If length or nmemb are 0, or the value of length * nmemb exceeds PTRDIFF_MAX, or the multiplication overflows, the behavior is undefined.
  • realloc_fn returns a pointer to a new allocation of length length for the data in old_ptr . It must return a valid pointer or abort execution. It need not (but may) return old_ptr. The returned pointer must be suitably aligned for any type. If length is 0 or exceeds PTRDIFF_MAX, the behavior is undefined.
  • free_fn releases the memory pointed to by ptr. It must succeed or abort execution. If ptr is a null pointer, it must return without any operation being performed.
  • strdup_fn returns a new allocation for the null-terminated string pointed to by str. It must return a valid pointer or abort execution. The returned pointer need not be aligned. If str is a null pointer, or the length of str exceeds PTRDIFF_MAX, the behavior is undefined.

The function ccord_global_init_memory would:

  • Initialize global variables with the specified function pointers.
  • Call curl_global_init_mem with callbacks derived from these function pointers, as specified by libcurl.
  • Perform all work previously performed in ccord_global_init.

The implementation of ccord_global_init would change as follows:

  • Call ccord_global_init_memory with callbacks derived from the system's implementation of these functions.

The interface and behavior of ccord_global_init would not change. This change is entirely backwards compatible.

To prevent decision fatigue and confusion for the library user, a note stating that ccord_global_init_memory is reserved for advanced use cases exclusively, and that ccord_global_init should be used, would be prominently displayed in the documentation.

All calls to malloc, calloc, realloc, free, and strdup would need to be converted to calls to internal functions with identical signatures. No other alterations to the calling code would be required, making porting trivial. These functions would, in debugging builds of concord, assert compliance with the interface specified above, verifying the existing assumption that memory allocation functions cannot fail.

Use cases

  • Using concord on memory-constrained systems, like SBCs.
  • Using concord with a limit on memory usage.
  • Using concord with extra memory allocation information or logging.
  • Using concord without dynamic allocation by allocating within a statically or automatically allocated buffer.
  • Using concord with bindings from programming languages that provide their own memory allocation routines or have standard mechanisms that allow the user to choose how to allocate memory.
  • Using concord with custom allocators, like jemalloc or mimalloc.
  • Using concord with a GC.

Additional context

I have sought critique on the discord server. This led to the following reasoning for the choices made:

  • These functions must not fail because the existing code relies on this behavior already and I intend to make this change as minimally invasive as possible.
  • The reasoning for the curl memory allocation functions to be set based on the concord memory allocation functions is to enable the user to control memory allocation for the entire program. A possible second reason would have been to resolve the "curl free bug", apparently caused by us failing to use curl_free to release memory acquired by libcurl. This is invalid, as curl's memory allocation debugging is implemented on top of the memory allocation functions provided to it.
  • Thread safety is required because it is assumed that a library user using this interface knows what they are doing and the performance penalty of implementing thread safety with a mutex on top of the user-provided memory allocation functions, possibly redundantly, may be too great.
  • Performance considerations should generally be taken seriously, however, according to this StackOverflow answer, an indirect call only takes approximately 7ns more than a direct call. Thus, I consider performance concerns to be unfounded until a benchmark indicates otherwise.
  • Concerns revolving around decision fatigue and confusion are addressed by documentation and complete backwards compatibility.

I would happily implement this. I may underestimate the scope of this change, but I do not expect it to be particularly technically challenging, difficult to implement cleanly and elegantly, or labor-intensive.

Thank you for considering this proposal.

Incomplete presence info and guild members

Opening a new ticket for this, there was some initial discussion of this on #135

I'm now able to get presence data, but it seems presence->size can be smaller than members->size in the event.

From looking at http.log, it seems the JSON array really only is that large, so perhaps this is an API issue:

discord/discord-api-docs#666

discord/discord-api-docs@12b414e

Since this was initially "undocumented", I wonder whether this is also an "undocumented limitation". It's also not consistent, now when I last tried it, I only got 28 presences, instead of 34 like before (still far under 77). I can confirm there's only one chunk received, so it's not as if the presences are getting split.

One workaround may be to retry any users we didn't get presence data in another request, filtered to such user(s), although it would be nice if the API response worked properly in the first place... oh well. It's very possible there's not much that can be done in this case.

Returning to the other thing I had mentioned, this seems like something that may not be quite right:

			struct discord_channel dchannel;
			struct discord_ret_channel ret2 = { .sync = &dchannel };
			cp->channel_id = channel->id;
			code = discord_get_channel(client, channel->id, &ret2);
			if (code != CCORD_OK) {
				continue;
			}
			/* At this point dchannel.member is NULL */

(It's hard to parse the official APIs, but this suggests that accessing recipients is the right way to get channel members: https://stackoverflow.com/questions/50840580/how-to-retrieve-a-list-of-discord-users-in-a-channel)

I thought perhaps maybe member is not set by default (like how the other API requires .presences = true in order to get presence data, but there doesn't seem to be any such boolean flag, so I would assume members should always be returned. Some of the other data is set, but this one is NULL.

This is from the HTTP logs:

17:42:25 TRACE discord-rest_ratelimit.c:252: [DISCORD_RATELIMIT] [null] Couldn't match known buckets to ':1:channels:REDACTED'
17:42:25 DEBUG discord-rest_ratelimit.c:301: [DISCORD_RATELIMIT] [miss] Match ':1:channels:REDACTED' to bucket
17:42:25 DEBUG discord-rest_ratelimit.c:357: [DISCORD_RATELIMIT] [miss] Remaining = 1 | Reset = 0
[2023-02-06 17:42:25.817]  ERROR[653098]: mod_discord.c:401 fetch_channels: Failed to fetch member list for channel REDACTED

Edit: I realize I should be using recipients rather than members, but both are actually NULL.

Looking at http.log, unfortunately this data doesn't seem to be present, either, which is incongruent with their API documentation to me.

So I'm not sure if either of these is very actionable, but I know there was a fix for something, so I guess that can be linked against this issue instead now.

Segfault if default prefix is enabled

Bug

If you enable default_prefix in config.json you immediately segfault.

Reproduce

{
  "logging": {
    "level": "trace",
    "filename": "logs/bot.log",
    "quiet": true,
    "overwrite": true,
    "use_color": true,
    "http": {
      "enable": true,
      "filename": "logs/http.log"
    },
    "disable_modules": ["WEBSOCKETS", "USER_AGENT"]
  },
  "discord": {
    "token": "ODIzMzExMDAxMTcxNjU2NzU0.YFe-Hw.qslT7vAgbxU_sZi4J5hqWQcld4A",
    "default_prefix": {
      "enable": true,
      "prefix": "."
    }
  }
}

Output Segmentation fault

Version

I am at the latest change in the master branch. I am running under kali-rolling in WSL2. Although I had this same behavior in arch and debian.

Stack Trace

GDB

(gdb) run
Starting program: /home/mazy/programming/discode/discode/build/discode 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff6f69640 (LWP 2845)]
[New Thread 0x7ffff6768640 (LWP 2846)]

Thread 1 "discode" received signal SIGSEGV, Segmentation fault.
__strnlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:74
74      ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.

Valgrind

==2905== Memcheck, a memory error detector
==2905== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2905== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==2905== Command: ./discode
==2905== 
==2905== Invalid read of size 1
==2905==    at 0x48457B9: strnlen (vg_replace_strmem.c:461)
==2905==    by 0x4989F4D: __vfprintf_internal (vfprintf-internal.c:1647)
==2905==    by 0x499C175: __vsnprintf_internal (vsnprintf.c:114)
==2905==    by 0x4976B41: snprintf (snprintf.c:31)
==2905==    by 0x1142FA: discord_gateway_init (discord-gateway.c:1480)
==2905==    by 0x10D2F8: _discord_init (discord-client.c:19)
==2905==    by 0x10D5B2: discord_config_init (discord-client.c:80)
==2905==    by 0x10B705: main (main.c:124)
==2905==  Address 0x29 is not stack'd, malloc'd or (recently) free'd
==2905== 
==2905== 
==2905== Process terminating with default action of signal 11 (SIGSEGV)
==2905==  Access not within mapped region at address 0x29
==2905==    at 0x48457B9: strnlen (vg_replace_strmem.c:461)
==2905==    by 0x4989F4D: __vfprintf_internal (vfprintf-internal.c:1647)
==2905==    by 0x499C175: __vsnprintf_internal (vsnprintf.c:114)
==2905==    by 0x4976B41: snprintf (snprintf.c:31)
==2905==    by 0x1142FA: discord_gateway_init (discord-gateway.c:1480)
==2905==    by 0x10D2F8: _discord_init (discord-client.c:19)
==2905==    by 0x10D5B2: discord_config_init (discord-client.c:80)
==2905==    by 0x10B705: main (main.c:124)
==2905==  If you believe this happened as a result of a stack
==2905==  overflow in your program's main thread (unlikely but
==2905==  possible), you can try to increase the size of the
==2905==  main thread stack using the --main-stacksize= flag.
==2905==  The main thread stack size used in this run was 8388608.
==2905== 
==2905== HEAP SUMMARY:
==2905==     in use at exit: 209,505 bytes in 3,648 blocks
==2905==   total heap usage: 5,174 allocs, 1,526 frees, 277,335 bytes allocated
==2905== 
==2905== LEAK SUMMARY:
==2905==    definitely lost: 0 bytes in 0 blocks
==2905==    indirectly lost: 0 bytes in 0 blocks
==2905==      possibly lost: 640 bytes in 2 blocks
==2905==    still reachable: 208,865 bytes in 3,646 blocks
==2905==         suppressed: 0 bytes in 0 blocks
==2905== Rerun with --leak-check=full to see details of leaked memory
==2905== 
==2905== For lists of detected and suppressed errors, rerun with: -s
==2905== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault

WebSocket upgrade fails with 'server didn't accept the websocket upgrade' error when using an HTTP proxy

Describe the bug

I am encountering a "server didn't accept the websocket upgrade" error when trying to connect to Discord's server using an HTTP proxy.

Expected behavior

I expected to be able to connect to the server without encountering any errors. Investigate whether libcurl is able to detect if an HTTP proxy is being used and where we can go from there:

  1. Update to HTTPS proxy instead?
  2. Give a better logging error

Screenshots

N/A

To Reproduce

  1. Set up a connection to Discord's server using an HTTP proxy
  2. Attempt to connect bot to Discord
  3. Observe the "server didn't accept the websocket upgrade" error

Version

  • Concord version: v2.2.0
  • Platform: Ubuntu 20

Stack trace

== Info: Uses proxy env variable no_proxy == '127.0.0.0/8,localhost,::1'
== Info: Uses proxy env variable https_proxy == 'http://127.0.0.1:48157'
== Info: Hostname 127.0.0.1 was found in DNS cache
== Info:   Trying 127.0.0.1:48157...
== Info: TCP_NODELAY set
== Info: Connected to 127.0.0.1 (127.0.0.1) port 48157 (#2)
== Info: allocate connect buffer!
== Info: Establish HTTP proxy tunnel to gateway.discord.gg:443
=> Send header, 0000000103 bytes (0x00000067)
0000: 43 4f 4e 4e 45 43 54 20 67 61 74 65 77 61 79 2e CONNECT gateway.
0010: 64 69 73 63 6f 72 64 2e 67 67 3a 34 34 33 20 48 discord.gg:443 H
0020: 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 67 TTP/1.1
0029: 48 6f 73 74 3a 20 67 61 74 65 77 61 79 2e 64 69 Host: gateway.di
0039: 73 63 6f 72 64 2e 67 67 3a 34 34 33 0d 0a 50 72 scord.gg:443
0047: 50 72 6f 78 79 2d 43 6f 6e 6e 65 63 74 69 6f 6e Proxy-Connection
0057: 3a 20 4b 65 65 70 2d 41 6c 69 76 65 0d 0a 0d 0a : Keep-Alive
0065: 0d 0a                                           
<= Recv header, 0000000017 bytes (0x00000011)
0000: 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d HTTP/1.1 200 OK
<= Recv header, 0000000037 bytes (0x00000025)
0000: 64 61 74 65 3a 20 54 75 65 2c 20 30 39 20 4d 61 date: Tue, 09 Ma
0010: 79 20 32 30 32 33 20 31 36 3a 31 34 3a 33 38 20 y 2023 16:14:38 
0020: 47 4d 54 0d 0a                                  GMT
<= Recv header, 0000000002 bytes (0x00000002)
0000: 0d 0a

Additional context

  • Firewall status: Disabled
  • Proxy type: HTTP

Easy server member permission checking

Describe the feature

Check to see if server member has a permission of some sort.

u64bitmask discord_get_member_permissions(const struct discord_guild_member* member) {
    u64bitmask permissions = 0;
    for (int i = 0; i < member->roles->size; ++i) {
        permissions |= member->roles->array[i].permissions;
    }
    return permissions;
}

Though, maybe a function like bool discord_member_has_permission(const struct discord_guild_member* member, unsigned int code) (maybe unsigned int should be something else) could also be implemented.

Additional context

Discord chat (credit goes to @lcsmuller).

Cannot build on MacOS

Describe the bug
I cannot build on MacOS monterey

Expected behavior
a built binary

Screenshots
Screen Shot 2022-06-15 at 17 06 11

To Reproduce
type make in the repo in MacOS montery

Version
v2.0.0, MacOS montery: Darwin 21.5.0 Darwin Kernel Version 21.5.0: Tue Apr 26 21:08:22 PDT 2022; root:xnu-8020.121.3~4/RELEASE_X86_64

Segfault due to race conditions in discord_cleanup when called from another thread

Describe the bug

I'm using libdiscord in a shared library module that I built for a large multithreaded application, e.g. Concord is only a very small part of the entire program, and there are lots of other threads. Everything is pretty much working, but I'm having segmentation faults on shut down, and it appears it may be because I'm shutting down the client in one thread, and perhaps this isn't legal. I looked at some of the code and it appears if have_sigint is set, then discord_run will exit. I'm not using SIGINT because that only makes sense for a standalone program, but perhaps this suggests that there's no way to terminate a session "out of band" (apart from this mechanism)? Is this the right understanding?

Essentially, I have this (some stuff removed for conciseness):

static void *discord_relay(void *varg)
{
	struct discord *client = varg;
	discord_run(client);
	return NULL;
}

static int load_module(void)
{
	ccord_global_init();
    discord_client = discord_init(token);
	if (!discord_client) {
		return -1;
	}

    discord_add_intents(discord_client, DISCORD_GATEWAY_MESSAGE_CONTENT | DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_GUILD_MEMBERS);

    discord_set_on_ready(discord_client, &on_ready);
    discord_set_on_message_create(discord_client, &on_message_create);
    discord_set_on_message_update(discord_client, &on_message_update);
	discord_set_on_guild_members_chunk(discord_client, &on_guild_members_chunk);

	if (pthread_create(&discord_thread, NULL, discord_relay, discord_client)) {
		discord_shutdown(discord_client);
		discord_cleanup(discord_client);
		ccord_global_cleanup();
		return -1;
	}
	return 0;
}

static int unload_module(void)
{
	discord_shutdown(discord_client);
	pthread_join(discord_thread, NULL);
	discord_cleanup(discord_client);

    ccord_global_cleanup();
	return 0;
}

I initially had this ordering:

discord_shutdown(discord_client);
discord_cleanup(discord_client);
pthread_join(discord_thread, NULL);

When I did this, I got segmentation faults in the thread with discord_run, suggesting that perhaps it is not legal to call discord_shutdown in another thread. However, I need to be able to do that... how else can I tell the client to exit?

With the ordering shown in the larger snippet, pthread_join never returns, so the unload blocks forever (though no crash).

Is there an example or suggested approaching for doing an out of band shutdown as I'm trying to do here? I'd think this would be a common use case, but I didn't find any examples of it. It seems like there is a race condition here with the way I'm currently doing it that leads to the discord_run thread trying to read invalid memory... and if I don't call discord_shutdown, then the discord_run thread won't exit, so I'm not sure if there's a way around that.

Perhaps something like the following could be added to src/concord-once.c? (Although I'm not positive if this would be the right fix or not.)

static void ccord_shutdown_notify(int signum)
{
    ccord_has_sigint = 1;
}

My thinking is this would allow the discord_thread to gracefully exit on its own, and then I could call pthread_join directly, since the discord_run thread itself appears to call discord_shutdown on its own. So really, maybe what's need is just a way to asynchronously shut down using a public function, generically, not just the internal SIGINT handler which doesn't apply to every use case. (Correct me if I'm wrong, but I don't think this currently exists from what I could find.)

This happens consistently if I shut down my program (which unloads all dynamic modules), but not always (only sometimes) if I only attempt to unload the Discord module:

Stack Trace for full shutdown


==534690== 1 errors in context 16 of 29:
==534690== Invalid read of size 8
==534690==    at 0x4F273D3: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x4EF747F: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x4F0A32F: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x4F286AF: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x4F298D6: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x4F2A225: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x4F2A562: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x4F2A679: curl_multi_socket_all (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x9253F2C: ws_multi_socket_run (in /usr/local/lib/libdiscord.so)
==534690==    by 0x91CAA39: discord_gateway_perform (in /usr/local/lib/libdiscord.so)
==534690==    by 0x91C9EFF: _discord_on_gateway_perform (in /usr/local/lib/libdiscord.so)
==534690==    by 0x924E86F: io_poller_perform (in /usr/local/lib/libdiscord.so)
==534690==  Address 0xb485e50 is 192 bytes inside a block of size 6,440 free'd
==534690==    at 0x48399AB: free (vg_replace_malloc.c:538)
==534690==    by 0x4F0A89A: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x4F00B78: curl_easy_cleanup (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.7.0)
==534690==    by 0x92550AE: _cws_cleanup (in /usr/local/lib/libdiscord.so)
==534690==    by 0x9256F10: cws_free (in /usr/local/lib/libdiscord.so)
==534690==    by 0x9253325: ws_cleanup (in /usr/local/lib/libdiscord.so)
==534690==    by 0x91CA291: discord_gateway_cleanup (in /usr/local/lib/libdiscord.so)
==534690==    by 0x91C56FB: discord_cleanup (in /usr/local/lib/libdiscord.so)
==534690==    by 0x9183699: unload_module (mod_discord.c:409)

Stack Trace for explicit unload

==534944== 2 errors in context 17 of 34:
==534944== Invalid read of size 4
==534944==    at 0x4FE09F9: pthread_mutex_trylock (pthread_mutex_trylock.c:42)
==534944==    by 0xFB6EEBB: discord_requestor_dispatch_responses (in /usr/local/lib/libdiscord.so)
==534944==    by 0xFB761AB: discord_run (in /usr/local/lib/libdiscord.so)
==534944==    by 0x9183207: discord_relay (mod_discord.c:322)
==534944==    by 0x134D81: thread_run (thread.c:252)
==534944==    by 0x4FDDEA6: start_thread (pthread_create.c:477)
==534944==    by 0x50F4A2E: clone (clone.S:95)
==534944==  Address 0xfac8be0 is 96 bytes inside a block of size 120 free'd
==534944==    at 0x48399AB: free (vg_replace_malloc.c:538)
==534944==    by 0xFB6E213: discord_requestor_cleanup (in /usr/local/lib/libdiscord.so)
==534944==    by 0xFB6D93F: discord_rest_cleanup (in /usr/local/lib/libdiscord.so)
==534944==    by 0xFB736E9: discord_cleanup (in /usr/local/lib/libdiscord.so)
==534944==    by 0x9183699: unload_module (mod_discord.c:409)

Only first attachment is being detected

ERROR discord-rest_request.c:382: [DISCORD_REQUEST] {"code": 50035, "errors": {"attachments": {"1": {"_errors": [{"code": "ATTACHMENT_NOT_FOUND", "message": "Attachment data not found"}]}}}, "message": "Invalid Form Body"}

This error only occurs when more than one attachment has been included. Otherwise, using one or the other works.
Replacing the strings with proper files should reproduce the error:

struct discord_attachment load_attachment(char* filename) {
  char full_path[128];
  snprintf(full_path, sizeof(full_path), "File/Path/%s", filename);

  size_t fsize = 0;
  char  *fbuf  = cog_load_whole_file(full_path, &fsize);
  struct discord_attachment attachment = {
    .filename = filename, 
    .content = fbuf, 
    .size = fsize
  };

  return attachment;
}

void embed_create(struct discord *client, const struct discord_message *msg) {
  if (msg->author->bot) return;

  struct discord_embed embed = {
    .image = &(struct discord_embed_image) {
      .url = "attachment://file_name.png"
    },
    .thumbnail = &(struct discord_embed_thumbnail) {
      .url = "attachment://file_name.png"
    }
  };

  struct discord_attachment add_image = load_attachment("file_name.png");
  struct discord_attachment add_thumbnail = load_attachment("file_name.png");

  struct discord_create_message params = {
    .embeds = &(struct discord_embeds) {
      .size = 1,
      .array = &embed
    },
    .attachments = &(struct discord_attachments) {
      .size = 2,
      .array = (struct discord_attachment[]) {add_image, add_thumbnail}
    }
  };

  discord_create_message(client, msg->channel_id, &params, NULL);
}

Out of Bounds Write Attempt - Fatal

The assertion
Assert Failed: (size_t)len < (size_t)sizeof(b->hash)
aborts at src/discord-ratelimit.c:33:.

I believe this is due to DISCORD_ROUTE_LEN being defined as 64 and the
length of the string which is being given to the assertion is 71. The string
generated can be found in the stack trace:

#0 0x00007ffff7d9e34c in __pthread_kill_implementation () from /usr/lib/libc.so.6
#1 0x00007ffff7d514b8 in raise () from /usr/lib/libc.so.6
#2 0x00007ffff7d3b534 in abort () from /usr/lib/libc.so.6
#3 0x00005555555d4e93 in _discord_route_init (adapter=0x55555561a6b0,
route=0x55555580e5ec ":5:applications:%lu:guilds:896679108350722109:commands:%lu:permissions", b=0x555555819520)
at src/discord-ratelimit.c:33
#4 0x00005555555d73e2 in _discord_bucket_get_match (adapter=0x55555561a6b0,
route=0x55555580e5ec ":5:applications:%lu:guilds:896679108350722109:commands:%lu:permissions",
info=0x7fffffffd8c0) at src/discord-ratelimit.c:205
#5 0x00005555555d7c5f in discord_bucket_build (adapter=0x55555561a6b0, b=0x5555556194a0,
route=0x55555580e5ec ":5:applications:%lu:guilds:896679108350722109:commands:%lu:permissions",
info=0x7fffffffd8c0) at src/discord-ratelimit.c:371
#6 0x00005555555d3e28 in _discord_adapter_check_action (adapter=0x55555561a6b0, msg=0x55555561ff50)
at src/discord-adapter.c:810
#7 0x00005555555d4279 in discord_adapter_perform (adapter=0x55555561a6b0) at src/discord-adapter.c:882
#8 0x00005555555cfdbd in on_io_poller_curl (io=0x555555618ec0, mhandle=0x5555556188e0, user_data=0x55555561a6b0)
at src/discord-adapter.c:37
#9 0x00005555555cb38e in io_poller_perform (io=0x555555618ec0) at core/io_poller.c:111
#10 0x000055555555a016 in discord_run (client=0x55555561a600) at src/discord-client.c:353
#11 0x0000555555558888 in main (argc=1, argv=0x7fffffffe808) at wikid.c:78

Source code can be found here: https://github.com/arcnyxx/wikid

Using Concord with latest commit hash fe93008.

Insufficient buffer size causes truncation, malformed request

There are some static buffer sizes in discord-gateway_dispatch.c that can be too small for large requests, causing truncation of the JSON payload and causing the Discord API to disconnect us, aborting any requests in progress. For example, this limit can be hit when requesting guild members with a payload of between 40 and 45 users, with a buffer size of 1024.

This commit partially addresses this with a stopgap fix: b860d9f

Seems like dynamic allocation should be used instead?

Add client callbacks for On Reconnect, On Shutdown and On Error

Describe the feature

Add client callbacks for On Reconnect, On Shutdown and On Error. Clients should be able to deal with those events on-demand, as they might have a method of logging or resource management that requires listening to these events.

Acceptance criteria

  • Add discord_set_on_xxx() methods for each one of the events

Version

v2.1.0

Wrong compiler for discord_codecs breaks cross

Describe the bug

$(OUT_O): $(OUT_C) $(OUT_H)
$(CC) -c $(CFLAGS) $< -o $@

The OUT_O produced here gets linked with the rest of the code that's built with the cross-compiling CC for the target platform, but this currently uses the same CC that's used in the rest of gencodecs to target the host platform. Attempting to cross-compile to a different platform will thus abort at the linking step, when the linker rejects the object in the wrong format.

aarch64-unknown-linux-gnu-gcc -shared -lcurl -o ../lib/libdiscord.so concord-once.o discord-refcount.o discord-rest.o discord-rest_request.o discord-rest_ratelimit.o discord-client.o discord-events.o discord-cache.o discord-loop.o discord-gateway.o discord-gateway_dispatch.o discord-messagecommands.o discord-timer.o discord-misc.o discord-worker.o application_command.o auto_moderation.o interaction.o audit_log.o channel.o emoji.o gateway.o guild.o guild_scheduled_event.o guild_template.o invite.o oauth2.o user.o voice.o webhook.o ../gencodecs/discord_codecs.o ../core/cog-utils.o ../core/io_poller.o ../core/user-agent.o ../core/websockets.o ../core/curl-websocket.o ../core/jsmn-find.o ../core/json-build.o ../core/log.o ../core/logconf.o ../core/priority_queue.o ../core/anomap.o ../core/sha1.o ../core/threadpool.o ../core/queriec.o
/nix/store/lvr57hbrpjvg7jyh0j2a1bx2cvwypjqh-aarch64-unknown-linux-gnu-binutils-2.40/bin/aarch64-unknown-linux-gnu-ld: ../gencodecs/discord_codecs.o: Relocations in generic ELF (EM: 62)
/nix/store/lvr57hbrpjvg7jyh0j2a1bx2cvwypjqh-aarch64-unknown-linux-gnu-binutils-2.40/bin/aarch64-unknown-linux-gnu-ld: ../gencodecs/discord_codecs.o: Relocations in generic ELF (EM: 62)
/nix/store/lvr57hbrpjvg7jyh0j2a1bx2cvwypjqh-aarch64-unknown-linux-gnu-binutils-2.40/bin/aarch64-unknown-linux-gnu-ld: ../gencodecs/discord_codecs.o: error adding symbols: file in wrong format
collect2: error: ld returned 1 exit status
/tmp/nix-build-concord-aarch64-unknown-linux-gnu-2.2.1.drv-0/source/src/discord-refcount.o: ELF 64-bit LSB relocatable, ARM aarch64, version 1 (SYSV), with debug_info, not stripped
/tmp/nix-build-concord-aarch64-unknown-linux-gnu-2.2.1.drv-0/source/gencodecs/discord_codecs.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

Expected behavior

  1. gencodecs/Makefile uses separate HOST_CC & HOST_CPP variables (or whatever standard name they might have, if any) for the host-platform-targeting things, and inherits the target-platform-targeting CC from the main Makefile
  2. All but the linked-above line use HOST_CC / HOST_CPP to target the host platform, while the linked line uses CC to cross-compile for the target platform

Screenshots

n/a

To Reproduce

Use a cross-compiling CC for a target where objects for the host & target platform cannot be mixed (i.e. x86_64 and aarch64, not glibc x86_64 and musl x86_64)

Version

Concord 2.2.1
Linux, 64-bit

Distributor ID:	NixOS
Description:	NixOS 23.05 (Stoat)
Release:	23.05
Codename:	stoat

Support Sharding

Describe the feature

Sharding is a must-have feature for larger bots that require scaling through multiple servers. At its current state, concord is not a viable option for building large-scale bots, adding this feature would be a great step in the right direction.

Acceptance criteria

  • Bots should be able to infer shard information where it might be needed (such as WebSockets callbacks)
  • (optional) Support more than one method of sharding, such as across multiple processes, or multiple machines.

Version

v2.1.0

Additional context

Sharding

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.