diff --git a/.gitignore b/.gitignore index a3d2f19..b475434 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,13 @@ * !guides +!guides/* !include +!include/* !lib +!lib/* +!external +!external/* !.gitignore !Doxyfile !LICENSE diff --git a/Makefile b/Makefile index 21a0994..9a33a0c 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,15 @@ -CC = clang +CC = gcc PREFIX := /usr/local LIBDIR := $(PREFIX)/lib INCLUDEDIR := $(PREFIX)/include/coglink DOCS_DIR = ../CoglinDocs -SRC_DIR = lib +SRC_DIR = lib external OBJ_DIR = obj CFLAGS := -Wall -Wextra -Wpedantic -LDFLAGS := -std=gnu99 -Iinclude -Iexternal +LDFLAGS := -std=c99 -D_POSIX_C_SOURCE=200112L -Iinclude -Iexternal LFLAGS := -lcurl -ldiscord SRCS = $(foreach dir,$(SRC_DIR),$(wildcard $(dir)/*.c)) @@ -21,6 +21,9 @@ CogLink: $(OBJS) $(OBJ_DIR)/%.o: lib/%.c | $(OBJ_DIR) $(CC) -c -fPIC $< -o $@ $(LDFLAGS) $(CFLAGS) +$(OBJ_DIR)/%.o: external/%.c | $(OBJ_DIR) + $(CC) -c -fPIC $< -o $@ $(LDFLAGS) $(CFLAGS) + $(OBJ_DIR): mkdir -p $@ @@ -37,6 +40,10 @@ install: cp include/* $(INCLUDEDIR) mv libcoglink.a $(LIBDIR) +uninstall: + rm -rf $(INCLUDEDIR) + rm -f $(LIBDIR)/libcoglink.a + gen_docs: doxygen Doxyfile rm -f $(DOCS_DIR)/* || true diff --git a/README.md b/README.md index 0ec9111..8d0c2eb 100644 --- a/README.md +++ b/README.md @@ -1,154 +1,81 @@ -# Coglink +# CogLink -[![Discord Server](https://img.shields.io/discord/1036045973039890522?color=5865F2&logo=discord&logoColor=white)](https://discord.gg/YcaK3puy49) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/acbabb99b4354f5ab182e511dd35aee4)](https://www.codacy.com/gh/ThePedroo/Coglink/dashboard?utm_source=github.com&utm_medium=referral&utm_content=ThePedroo/Coglink&utm_campaign=Badge_Grade) +A Concord (C99) LavaLink/NodeLink client. -![Coglink logo](guides/images//CoglinkLogo.png "Coglink logo") +## Features -Coglink, the C Lavalink client, with the most performance above all Lavalink wrappers. +- C99 compatible +- Low memory usage (> 7MB of RAM a working bot) +- Portable -## About - -Coglink is a C99 Lavalink client, which has full coverage of Lavalink API, allowing easy low-level control of LavaLink. - -Made to be portable, Coglink is the least resource-consuming client, allowing you to run easily anywhere. (It can use less than 10MB of RAM) - -## Why? - -One of the main purposes of Coglink is to be the most performant LavaLink client, while still being light-weight and portable, especially with a low overhead. - -In comparison with other NodeJs clients, even against FastLink, Coglink is way more portable, which makes it a good option for big bots, which needs to have the least resource consumption possible. - -Coglink is from PerformanC, and our missions is to make our softwares stable enough that it could survive months without issues, and while this is hard for a big project, we are trying to make it possible, which for now, it's working. - -Performance, stability and portability, that's what Coglink is about. - -## Stability - -Stability on this branch cannot be confirmed, LavaLink v4 is still in development with some pending features to be added. - -While this branch is more stable than the main, LavaLink is not, so be aware when using this branch. - -*If marked, considered stable, if not, bugs may be found.* - -- [x] Search (Highly stable) -- [x] loadType/track/playlist/error parsing (loadType/track highly stable) -- [x] Play (Highly stable) -- [x] Join voice channel (Highly stable) -- [x] All cleanups (Highly stable) -- [x] DecodeTrack(s) & ParseTrack(s) -- [x] Parse track, pause track, stop track, seek track, set volume -- [x] Set filter -- [x] Get Players & parse get Players -- [x] Get/parse Info -- [ ] Get/parse RouterPlanner -- [x] Get/parse Lavalink Stats (Event included) -- [x] Get Lavalink Version -- [x] Connect/disconnect Node (Highly stable) -- [x] Set event (Highly stable) -- [x] Websocket (**Known bugs with close event**) -- [x] IO poller (Highly stable) +## Dependencies -## Compiling +- [`Concord`](https://github.com/Cogmasters/Concord) >= 2.2.0 -Before starting, you will need to install the required libraries for Coglink, this can be done by the package manager of your OS, you will need to install the mentioned libraries below: +> [!NOTE] +> CogLink only relies on dependencies already included in Concord. -```text -make clang git -``` +## Installation -**Warning**: Remember that their names can change depending on your OS. +### 1. Install Concord -After installing the libraries, you will need to [compile Concord](https://github.com/Cogmasters/concord). +The installation guide for Concord can be found [in their repository](https://github.com/Cogmasters/concord?tab=readme-ov-file#build-instructions). -After compiling Concord, you can proceed with the installation of Coglink, first, we will need to clone the repository: +### 2. Clone the repository -```console -$ git clone https://github.com/PerformanC/Coglink +```shell +$ git clone https://github.com/PerformanC/CogLink ``` -When the repository is cloned, enter in the folder and run the `make` command, this will generate the header files, which will be used to compile Coglink, and after it, install into the system. +### 3. Compile -```console -# cd Coglink && make && make install +```shell +$ cd CogLink +$ make -j4 ``` -**Warning**: When compiling your bot, remember to use the `-lcoglink` flag. - -## Using +> [!NOTE] +> The `-j4` flag is optional, but it will speed up the compilation process. The number after `-j` is the number of threads to use for compilation. -See below the purpose of Coglink header files: +### 4. Install -```c -#include // Websocket related Coglink functions -#include // File with the definitions of macros, like COGLINK_SUCCESS -#include // Functions which get information from Lavalink -#include // Functions related to network, like the router planner -#include // The functions related to the music player -#include // For the use of plugins -#include // Functions related to tracks, like decoding, searching, etc +```shell +$ sudo make install ``` -As for the usage, you can see the `guides/example` folder, with a complete example of Coglink usage. +## Usage -## Documentation - -You can see [here](https://performanc.github.io/CoglinDocs/) the documentation for Coglink, made with Doxygen. - -In case you want to build the documentation yourself, you can use the `make gen_docs` command, but be sure that you have Doxygen installed. - -## Coglink plugins - -For most functions, Coglink allows plugins to interfere with it, allowing the community to add more features to Coglink, without the need to modify the source code, but for the protection of the users, Coglink has some security measures to avoid malicious plugins. - -The security measures are: - -- Plugins are not allowed to change the members of the struct `lavaInfo`/`client` -- Concord's client struct has hidden members (OPTIONAL) -- Plugins are not allowed to read neither the bot token nor the Concord websocket struct by default. +... TODO -### Setting up a plugin - -Plugins can't straight ahead work in Coglink, you need to pass the functions to Coglink from the plugin, so it can execute when some function from Coglink is used, see the function below that allow this to happen: +## Documentation -```c -#include -#include "somePlugin.h" +[CogLink documentation](https://performanc.github.io/CoglinDocs/) is made with Doxygen, hosted on GitHub Pages. -struct coglink_pluginEvents event = { - .onSearchRequest = &functionToBeExecuted -} +The documentation can be built with the following command: -coglink_setPluginEvents(&lavaInfo, &event); +```shell +$ make gen_docs ``` -Done, now when the function coglink_searchSong is executed, this will be executed first before Coglink processes it. - -**Warning**: The first plugins have the priority, so if you want to make a plugin that will be executed first, you will need to set it first. - -## Plugin list - -For now, there are no plugins for Coglink, but if you want to make one, you can see the [Setting up a plugin](#setting-up-a-plugin) section, and after it, you can add it to the list below. +> [!NOTE] +> Doxygen is required to build the documentation. ## Support -In case of any issue using it (except bugs, that should be reported on GitHub Issues), you are free to ask on PerformanC's [Discord server](https://discord.gg/uPveNfTuCJ). +Any question or issue related to CogLink or other PerformanC projects can be can be made in [PerformanC's Discord server](https://discord.gg/uPveNfTuCJ). -## Credits +For verified issues, please also create a GitHub issue for tracking the issue. -Although Coglink is made only by one person, many people contributed to it, thanks [Cogmaster](https://discord.gg/YcaK3puy49) members for this. And special thanks to `müller#1001`, without you, we wouldn't be here. +## Contributing -Some people that helped on Coglink related things: +CogLink follows the PerformanC's [contribution guidelines](https://github.com/PerformanC/contributing). It is necessary to follow the guidelines to contribute to CogLink and other PerformanC projects. -- müller -- HackerSmacker -- Goo +## Projects using CogLink -Thanks you all for the help! ^^ +None known yet, but if you are using CogLink, please let us know in [PerformanC's Discord server](https://discord.gg/uPveNfTuCJ) and we will add your project here. -## Dependencies +## License + +CogLink is licensed under PerformanC's License, which is a modified version of the MIT License, focusing on the protection of the source code and the rights of the PerformanC team over the source code. -- `libcurl` >= 7.56.1 -- `Concord (master/dev)` 2.2.0 -- `jsmn-find` latest (included on Concord 2.2.0) -- `jsmn` latest (included on Concord 2.2.0) -- `tablec` v2.2.3 (Open-addressing) +* This project uses the latest standards of The PerformanC Organization. \ No newline at end of file diff --git a/external/tablec.c b/external/tablec.c new file mode 100644 index 0000000..b49261c --- /dev/null +++ b/external/tablec.c @@ -0,0 +1,97 @@ +#include +#include +#include + +#include "tablec.h" + +void tablec_init(struct tablec_ht *tablec, size_t max_capacity) { + tablec->length = 0; + tablec->capacity = max_capacity; + tablec->buckets = calloc(sizeof(struct tablec_bucket) * max_capacity, 1); +} + +static size_t __tablec_hash(struct tablec_ht *tablec, char *key) { + size_t hash = 0, i = 0; + + while (key[i] != '\0') hash = hash * 37 + (key[i++] & 255); + + return hash % tablec->capacity; +} + +void tablec_resize(struct tablec_ht *tablec, size_t new_max_capacity) { + struct tablec_ht newHashtable; + tablec_init(&newHashtable, new_max_capacity); + + while (tablec->capacity--) { + if (!tablec->buckets[tablec->capacity].key) continue; + + tablec_set(&newHashtable, tablec->buckets[tablec->capacity].key, tablec->buckets[tablec->capacity].value); + } + + tablec_cleanup(tablec); + *tablec = newHashtable; +} + +void tablec_set(struct tablec_ht *tablec, char *key, void *value) { + size_t hash; + + if (tablec->length - 1 == tablec->capacity) tablec_resize(tablec, tablec->capacity * 2); + + hash = __tablec_hash(tablec, key); + + while (hash != tablec->capacity) { + if (!tablec->buckets[hash].key) { + tablec->buckets[hash].key = key; + tablec->buckets[hash].value = value; + + tablec->length++; + + return; + } + + hash++; + } + + tablec_resize(tablec, tablec->capacity * 2); + tablec_set(tablec, key, value); + + return; +} + +void tablec_del(struct tablec_ht *tablec, char *key) { + size_t hash = __tablec_hash(tablec, key); + + while (hash != tablec->capacity) { + if (tablec->buckets[hash].key && strcmp(tablec->buckets[hash].key, key) == 0) { + tablec->buckets[hash].key = NULL; + tablec->buckets[hash].value = NULL; + + tablec->length--; + + return; + } + + hash++; + } +} + +struct tablec_bucket *tablec_get(struct tablec_ht *tablec, char *key) { + size_t hash = __tablec_hash(tablec, key); + + while (hash != tablec->capacity) { + if (tablec->buckets[hash].key && strcmp(tablec->buckets[hash].key, key) == 0) + return &tablec->buckets[hash]; + + hash++; + } + + return NULL; +} + +int tablec_full(struct tablec_ht *tablec) { + return (int)(tablec->capacity == tablec->length) ? -1 : (int)(tablec->capacity - tablec->length); +} + +void tablec_cleanup(struct tablec_ht *tablec) { + free(tablec->buckets); +} diff --git a/external/tablec.h b/external/tablec.h new file mode 100644 index 0000000..1d519b3 --- /dev/null +++ b/external/tablec.h @@ -0,0 +1,29 @@ +#ifndef TABLEC_H +#define TABLEC_H + +struct tablec_bucket { + char *key; + void *value; +}; + +struct tablec_ht { + size_t length; + size_t capacity; + struct tablec_bucket *buckets; +}; + +void tablec_init(struct tablec_ht *tablec, size_t max_capacity); + +void tablec_resize(struct tablec_ht *tablec, size_t new_max_capacity); + +void tablec_set(struct tablec_ht *tablec, char *key, void *value); + +void tablec_del(struct tablec_ht *tablec, char *key); + +struct tablec_bucket *tablec_get(struct tablec_ht *tablec, char *key); + +int tablec_full(struct tablec_ht *tablec); + +void tablec_cleanup(struct tablec_ht *tablec); + +#endif diff --git a/guides/en-US/applying_effects.md b/guides/en-US/applying_effects.md deleted file mode 100644 index cfd9ad3..0000000 --- a/guides/en-US/applying_effects.md +++ /dev/null @@ -1,35 +0,0 @@ -# Applying effects - -## Effects? Which effects? - -The Lavalink allows you to add effects into your tracks, of all kinds, allowing you to change the bands, allowing effects like karaoke, nightcore, bassbost and etc. - -## Adding effects - -In this guide, the example of how to change the rotation effect will be used. - -Coglink have the `coglink_setEffect` functions, that you can use to change all effects that Lavalink have, your use is deadly easy, see the example below: - -```c -coglink_setEffect(&lavaInfo, message->guild_id, COGLINK_FILTER_ROTATION, "{\"rotationHz\":0.2}"); -``` - -Hm? "Where is the rest of the code"? Yep, it's only this, incredible, isn't it? - -This code makes the famous "8d" effect in your track, easy, and even cool, don't you think? - -## Removendo efeitos - -The last topic was about how to add an effect, but how about remove it? - -The file `definitions.h` of Coglink offers you a bunch of macros to be used, one of them is `FILTER_REMOVE`, which defines the value to the functions of Coglink understand that you want to remove the effect. - -See the example below: - -```c -coglink_setEffect(&lavaInfo, message->guild_id, COGLINK_FILTER_REMOVE, NULL); -``` - -And done! You will have the effect removed, in case you have added one. - -* *Guide made by*: @ThePedroo diff --git a/guides/en-US/connecting_lavalink.md b/guides/en-US/connecting_lavalink.md deleted file mode 100644 index c7f5d7e..0000000 --- a/guides/en-US/connecting_lavalink.md +++ /dev/null @@ -1,54 +0,0 @@ -# Connecting Lavalink Nodes - -## Connecting - -By default, every Lavalink is being hosted on port `2333`, we will assume in this guide that yours will as well be hosted on that port. - -For you to connect a node, you will need to know 5 different pieces of information, the hostname, the port, the password, has SSL enabled, the shard count of the bot and the bot Id. If you know those, you can proceed to the next step, if you don't, find them before proceeding, you will need those. - -Now with all this, we can proceed using the `coglink_connectNode` function, see the example below: - -```c -struct lavaInfo lavaInfo = { - .debug = 0 -}; - -... - -struct coglink_lavalinkNode nodes[] = { - { - .name = "Node1", - .hostname = "Node hostname:port if exists", - .password = "youshallnotpass", - .ssl = 0 - } -}; - -struct coglink_lavalinkNodes nodeArray = { - .nodes = nodes, - .size = 1 -}; - -struct coglink_nodeInfo nodeInfos[1]; -coglink_connectNode(&lavaInfo, client, &nodeArray, nodeInfos); - -... - -// After discord_run - -coglink_connectNodeCleanup(&lavaInfo, client); -``` - -Done, you will be now connecting to the Lavalink successfully, if not, check the values and check your firewall from both machines. - -## Disconnecting - -For you to close the WebSocket connection with the Lavalink, you can use the `coglink_disconnectNode` function, see the example below: - -```c -coglink_disconnectNode(&lavaInfo, 0); -``` - -Yep, it's that simple and done, the WebSocket connection with the Lavalink will be closed. - -* *Guide made by*: @ThePedroo diff --git a/guides/en-US/main.md b/guides/en-US/main.md deleted file mode 100644 index 32c553d..0000000 --- a/guides/en-US/main.md +++ /dev/null @@ -1,19 +0,0 @@ -# Lavalink - -## What is it? - -[Lavalink](https://github.com/freyacodes/Lavalink) is a powerful audio processing system, which allows you to play audio from videos of YouTube into Discord. Lavalink nodes are not fully connected directly to your bot, they can be run in another machine with no problem, and you can have multiple nodes either in the same machine or multiple machines, providing better stability for big bots, not overloading one single node and causing it to rate limit. - -## Why? - -Besides being slow and heavy, Lavalink is an easy option for playing YouTube videos in Discord, since doing it manually would require a lot of work for people who don't already have a base, and it provides effects and a lot of other features that would be hard to implement manually. It also provides an easy way to play songs from other sources, like SoundCloud, Bandcamp, Twitch, Vimeo, Mixer, and more. - -## How? - -Lavalink is powered by Java, together with Kotlin, when you download its binaries and run it, it will start a [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) server on port 2333 (unless you change the port in [`application.yml`](https://github.com/freyacodes/Lavalink/blob/master/LavalinkServer/application.yml.example)) which you will be able to connect via a [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) client, for example, [Concord](https://github.com/Cogmasters/concord)'s [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) client, there are a lot of libraries which already connects into the Lavalink node and give you the functions to easily work with the Lavalink, like Coglink. - -## Concord? Why should I use Concord instead of other libraries? - -Concord is an asynchronous C99 [Discord](https://discord.com) API wrapper, providing low-level functions to control your bot with it, and still providing high performance, not losing the original structures of the Discord API responses, making it easy to maintain, fix, improve, update and much more. - -* *Guide made by*: @ThePedroo diff --git a/guides/en-US/parsing_search.md b/guides/en-US/parsing_search.md deleted file mode 100644 index 514197f..0000000 --- a/guides/en-US/parsing_search.md +++ /dev/null @@ -1,51 +0,0 @@ -# Parsing song search - -## Why parsing? - -Parsing the search request is important since it will allow you to get the song name, the author, the duration, the thumbnail, the song URL, and much more. If you didn't, you won't be able to play the song. - -## Parsing - -To parse the song search JSON, we will be using the `coglink_parseSearch` function, which will fill the last parameter with the information of the music you requested for it to parse. - -For example, you will mostly want it to parse the first song, so follow the example above to parse the first song: - -```c -struct coglink_parsedTrackStruct parsedStruct; -coglink_initParseTrack(&lavaInfo, &parsedStruct, &res); - -struct parsedTrack song; -int parseRes = coglink_parseTrack(&lavaInfo, &parsedStruct, &res, "0", &song); - -if (parseRes != 0) - log_fatal("Error parsing song: %d", parseRes); - -log_info("Song name: %s", song->name); -``` - -Done, now the `parsedTrack` struct will be filled with the information of the song. - -## Parsing + loop - -Let's be fancy here, in case you want to parse all the 10 first song founds, you can use the for loop, see the example below: - -```c -struct parsedTrack song; - -for (int i = 0; i < 10; i++) { - char iString[16]; - char iString[16]; - snprintf(iString, sizeof(iString), "%d", i); - - int parseRes = coglink_parseTrack(&lavaInfo, &parsedStruct, &res, iString, &song); - - if (parseRes != 0) - log_fatal("Error parsing song: %d", parseRes); - - log_info("Song name: %s", song->name); -} -``` - -Done, it will print the name of the first 10 songs found. - -* *Guide made by*: @ThePedroo diff --git a/guides/en-US/searching_music.md b/guides/en-US/searching_music.md deleted file mode 100644 index e2d8363..0000000 --- a/guides/en-US/searching_music.md +++ /dev/null @@ -1,37 +0,0 @@ -# Searching song - -## Search from WHERE? - -Lavalink allows you to search from a lot of sources, and in this guide, you will be able to customize the search to search from a specific source, or from all sources. - -## Searching - -For searching for songs, we will be using the `coglink_searchSong` function, it will fill the last parameter, `struct requestInformation *` with the body (JSON) of the Lavalink response. - -### Why Coglink doesn't parse it automatically - -Coglink was made with performance in mind, if it was parsing the whole JSON, it would cause a lot of performance loss, so we decided to create a function that you can choose to select to parse the first, second, and third on any song in that position, so it will be faster and better. - -Follow the example above of the `coglink_searchSong` function: - -```c -struct coglink_requestInformation res; - -int resStatus = coglink_searchSong(&lavaInfo, "Very ordinary life", &res); - -if (resStatus != 0) { - log_fatal("Error searching song: %d", resStatus); -} - -log_debug("Body response of Lavalink: %s", res.body); - -/* ... */ - -coglink_searchSongCleanup(&res); -``` - -Done, it will print the response of the Lavalink search request, but in case it fails, it will return the int value of what happened wrong. - -You can proceed following the next guide, the [`parsing search song`](/guides/en-US/parsing_search.md) guide. - -* *Guide made by*: @ThePedroo diff --git a/guides/example.c b/guides/example.c new file mode 100644 index 0000000..9a67d16 --- /dev/null +++ b/guides/example.c @@ -0,0 +1,290 @@ +#include +#include + +#include + +#include + +void on_ready(struct discord *client, const struct discord_ready *bot) { + (void)client; + + log_trace("[DISCORD_GATEWAY] Logged in as %s#%s!", bot->user->username, bot->user->discriminator); +} + +// int onRaw(struct coglink_lavaInfo *lavaInfo, const char *text, size_t length) { +// (void)lavaInfo, (void)length; + +// log_debug("[COGLINK] On Raw event: %s", text); + +// return COGLINK_PROCEED; // Let's allow coglink handle it, here, we say to Coglink that it can proceed handling it. +// } + +void on_coglink_ready(struct coglink_ready_payload *ready) { + log_info("[COGLINK] Node connected [%s]", ready->session_id); +} + +// void onClose(enum ws_close_reason wscode, const char *reason) { +// log_info("[COGLINK] Node closed. [%d/%s]", wscode, reason); +// } + +// void onTrackStart(char *guildId, struct coglink_parsedTrack *track) { +// log_info("[COGLINK] Track started. [%s/%s]", guildId, track->title); +// } + +// void onTrackEnd(char *guildId, struct coglink_parsedTrack *track, char *reason) { +// log_info("[COGLINK] Track ended. [%s/%s/%s]", guildId, track->title, reason); +// } + +// void onTrackException(char *guildId, struct coglink_parsedTrack *track, char *message, char *severity, char *cause) { +// log_error("[COGLINK] Track exception. [%s/%s/%s/%s/%s]", guildId, track->title, message, severity, cause); +// } + +// void onTrackStuck(char *guildId, char *thresholdMs, struct coglink_parsedTrack *track) { +// log_error("[COGLINK] Track stuck. [%s/%d/%s]", guildId, thresholdMs, track->title); +// } + +// void onWebSocketClosed(char *guildId, char *code, char *reason, int byRemote) { +// log_error("[COGLINK] Websocket closed. [%s/%s/%s/%d]", guildId, code, reason, byRemote); +// } + +// void onUnknownEvent(char *guildId, char *type, const char *text) { +// log_error("[COGLINK] Unknown event. [%s/%s/%s]", guildId, type, text); +// } + +void onStats(struct coglink_stats_payload *stats) { + printf("InfoMemory:\n > Free: %d\n > Used: %d\n > Reservable: %d\n", stats->memory->free, stats->memory->used, stats->memory->reservable); + printf("InfoCPU:\n > Cores: %d\n > SystemLoad: %d\n > LavalinkLoad: %d\n", stats->cpu->cores, stats->cpu->systemLoad, stats->cpu->lavalinkLoad); + if (stats->frameStats) printf("InfoFrameStats:\n > Sent: %d\n > Nulled: %d\n > Deficit: %d\n", stats->frameStats->sent, stats->frameStats->nulled, stats->frameStats->deficit); + printf("PlayingPlayers: %d\nPlayers: %d\nUptime: %d\n", stats->playingPlayers, stats->players, stats->uptime); +} + +// void onPlayerUpdate(char *guildId, char *time, char *position, int connected, char *ping) { +// log_trace("[COGLINK] GuildId: %s\n Time: %s\n Position: %s\n Connected: %d\n Ping: %s", guildId, time, position, connected, ping); +// } + +// void onUnknownOp(char *op, const char *text) { +// log_error("[COGLINK] Unknown OP. [%s/%s]", op, text); +// } + +void on_message(struct discord *client, const struct discord_message *message) { + if (message->author->bot) return; + + // if (0 == strncmp(".play ", message->content, 6)) { + // char *songName = message->content + 6; + + // if (songName[0] == '\0') { + // struct discord_embed embed[] = { + // { + // .description = "Sorry, you must put a music name after the command.", + // .footer = + // &(struct discord_embed_footer){ + // .text = "Powered by Coglink and Concord", + // .icon_url = "https://raw.githubusercontent.com/Cogmasters/concord/master/docs/static/concord-small.png", + // }, + // .timestamp = discord_timestamp(client), + // .color = 16711680 + // } + // }; + + // struct discord_create_message params = { + // .flags = 0, + // .embeds = + // &(struct discord_embeds){ + // .size = 1, + // .array = embed, + // }, + // }; + + // discord_create_message(client, message->channel_id, ¶ms, NULL); + // return; + // } + + // struct coglink_requestInformation res; + // coglink_searchSong(&lavaInfo, songName, &res); + + // struct coglink_parsedTrackStruct parsedStruct; + // coglink_initParseTrack(&lavaInfo, &parsedStruct, &res); + + // int loadType; + // coglink_parseLoadtype(&lavaInfo, &parsedStruct, &res, &loadType); + + // switch(loadType) { + // case COGLINK_LOADTYPE_SEARCH_RESULT: { + // struct coglink_parsedTrack song; + + // coglink_parseTrack(&lavaInfo, &parsedStruct, &res, "0", &song); + // coglink_playSong(&lavaInfo, song.encoded, message->guild_id); + + // char description[256]; + // snprintf(description, sizeof(description), "Now playing `%s` from `%s`", song.title, song.author); + + // struct discord_embed embed[] = { + // { + // .title = song.title, + // .url = song.uri, + // .description = description, + // .footer = + // &(struct discord_embed_footer){ + // .text = "Powered by Coglink and Concord", + // .icon_url = "https://raw.githubusercontent.com/Cogmasters/concord/master/docs/static/concord-small.png", + // }, + // .timestamp = discord_timestamp(client), + // .color = 15615 + // } + // }; + + // struct discord_create_message params = { + // .flags = 0, + // .embeds = + // &(struct discord_embeds){ + // .size = 1, + // .array = embed, + // }, + // }; + + // discord_create_message(client, message->channel_id, ¶ms, NULL); + // break; + // } + // case COGLINK_LOADTYPE_EMPTY: { + // struct discord_embed embed[] = { + // { + // .description = "Hmmm... The node was unable to find that music. :/", + // .footer = + // &(struct discord_embed_footer){ + // .text = "Powered by Coglink and Concord", + // .icon_url = "https://raw.githubusercontent.com/Cogmasters/concord/master/docs/static/concord-small.png", + // }, + // .timestamp = discord_timestamp(client), + // .color = 16711680 + // } + // }; + + // struct discord_create_message params = { + // .flags = 0, + // .embeds = + // &(struct discord_embeds){ + // .size = 1, + // .array = embed, + // }, + // }; + + // discord_create_message(client, message->channel_id, ¶ms, NULL); + // break; + // } + // case COGLINK_LOADTYPE_LOAD_FAILED: { + // struct discord_embed embed[] = { + // { + // .description = "Oop- Some wild error happened while searching the music, what so ever, it is not a Coglink error, but a node one, phew!", + // .footer = + // &(struct discord_embed_footer){ + // .text = "Powered by Coglink and Concord", + // .icon_url = "https://raw.githubusercontent.com/Cogmasters/concord/master/docs/static/concord-small.png", + // }, + // .timestamp = discord_timestamp(client), + // .color = 16711680 + // } + // }; + + // struct discord_create_message params = { + // .flags = 0, + // .embeds = + // &(struct discord_embeds){ + // .size = 1, + // .array = embed, + // }, + // }; + + // discord_create_message(client, message->channel_id, ¶ms, NULL); + // break; + // } + // } + + // coglink_joinUserVoiceChannel(&lavaInfo, client, message->author->id, message->guild_id); + + // coglink_searchSongCleanup(&res); + // } + // if (0 == strcmp(".stop", message->content)) { + // coglink_stopPlayer(&lavaInfo, message->guild_id); + // } + // if (0 == strcmp(".pause", message->content)) { + // coglink_pausePlayer(&lavaInfo, message->guild_id, "true"); + // } + // if (0 == strcmp(".resume", message->content)) { + // coglink_pausePlayer(&lavaInfo, message->guild_id, "false"); + // } + // if (0 == strncmp(".seek ", message->content, 6)) { + // char *seek = message->content + 6; + + // coglink_seekTrack(&lavaInfo, message->guild_id, seek); + // } + // if (0 == strncmp(".volume ", message->content, 8)) { + // char *volume = message->content + 8; + + // coglink_setPlayerVolume(&lavaInfo, message->guild_id, volume); + // } + // if (0 == strcmp(message->content, ".8d")) { + // coglink_setEffect(&lavaInfo, message->guild_id, COGLINK_FILTER_ROTATION, "0.2"); + // } + // if (0 == strcmp(".destroy", message->content)) { + // coglink_destroyPlayer(&lavaInfo, message->guild_id); + // } + // if (0 == strcmp(".closeNode", message->content)) { + // coglink_disconnectNode(&lavaInfo, 0); + // } + // if (0 == strcmp(".getinfo", message->content)) { + // struct coglink_requestInformation res; + + // coglink_getLavalinkInfo(&lavaInfo, message->guild_id, &res); + + // log_debug("[COGLING] Lavalink Info: %s", res.body); + // } + // if (0 == strcmp(".getrouter", message->content)) { + // struct coglink_requestInformation res; + + // coglink_getRouterPlanner(&lavaInfo, message->guild_id, &res); + + // log_debug("[COGLING] Router planner: %s", res.body); + // } +} + +int main(void) { + struct discord *client = discord_config_init("config.json"); + + struct coglink_client c_client = { + .bot_id = "1038648927693590619", + .events = &(struct coglink_events){ + // .onRaw = &onRaw, + .onReady = &on_coglink_ready, + .onStats = &onStats + }, + .num_shards = "1", + }; + + struct coglink_nodes nodes = { + .array = (struct coglink_node[]){ + { + .name = "Node1", + .hostname = "127.0.0.1", + .port = 2333, + .password = "youshallnotpass", + .ssl = false + } + }, + .size = 1 + }; + + coglink_connect_nodes(&c_client, client, &nodes); + + /* Or to set events, you can also use */ + // coglink_setEvents(&lavaInfo, &lavaInfo->events); + + discord_set_on_ready(client, &on_ready); + discord_set_on_message_create(client, &on_message); + + discord_add_intents(client, DISCORD_GATEWAY_GUILD_VOICE_STATES); + discord_add_intents(client, DISCORD_GATEWAY_MESSAGE_CONTENT); + + discord_run(client); + + // coglink_connectNodeCleanup(&lavaInfo, client); +} \ No newline at end of file diff --git a/guides/examples/full_example.c b/guides/examples/full_example.c deleted file mode 100644 index e4fd37a..0000000 --- a/guides/examples/full_example.c +++ /dev/null @@ -1,319 +0,0 @@ -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -void on_ready(struct discord *client, const struct discord_ready *bot) { - (void)client; - - log_trace("[DISCORD_GATEWAY] Logged in as %s#%s!", bot->user->username, bot->user->discriminator); -} - -int onRaw(struct coglink_lavaInfo *lavaInfo, const char *text, size_t length) { - (void)lavaInfo, (void)length; - - log_debug("[COGLINK] On Raw event: %s", text); - - return COGLINK_PROCEED; // Let's allow coglink handle it, here, we say to Coglink that it can proceed handling it. -} - -void onConnect(char *sessionId) { - log_info("[COGLINK] Lavalink connected [%s]", sessionId); -} - -void onClose(enum ws_close_reason wscode, const char *reason) { - log_info("[COGLINK] Lavalink closed. [%d/%s]", wscode, reason); -} - -void onTrackStart(char *guildId, struct coglink_parsedTrack *track) { - log_info("[COGLINK] Track started. [%s/%s]", guildId, track->title); -} - -void onTrackEnd(char *guildId, struct coglink_parsedTrack *track, char *reason) { - log_info("[COGLINK] Track ended. [%s/%s/%s]", guildId, track->title, reason); -} - -void onTrackException(char *guildId, struct coglink_parsedTrack *track, char *message, char *severity, char *cause) { - log_error("[COGLINK] Track exception. [%s/%s/%s/%s/%s]", guildId, track->title, message, severity, cause); -} - -void onTrackStuck(char *guildId, char *thresholdMs, struct coglink_parsedTrack *track) { - log_error("[COGLINK] Track stuck. [%s/%d/%s]", guildId, thresholdMs, track->title); -} - -void onWebSocketClosed(char *guildId, char *code, char *reason, int byRemote) { - log_error("[COGLINK] Websocket closed. [%s/%s/%s/%d]", guildId, code, reason, byRemote); -} - -void onUnknownEvent(char *guildId, char *type, const char *text) { - log_error("[COGLINK] Unknown event. [%s/%s/%s]", guildId, type, text); -} - -void onStats(struct coglink_lavalinkStats *stats) { - printf("InfoMemory:\n > Free: %s\n > Used: %s\n > Reservable: %s\n", stats->memory->free, stats->memory->used, stats->memory->reservable); - printf("InfoCPU:\n > Cores: %s\n > SystemLoad: %s\n > LavalinkLoad: %s\n", stats->cpu->cores, stats->cpu->systemLoad, stats->cpu->lavalinkLoad); - if (stats->frameStats) printf("InfoFrameStats:\n > Sent: %s\n > Nulled: %s\n > Deficit: %s\n", stats->frameStats->sent, stats->frameStats->nulled, stats->frameStats->deficit); - printf("PlayingPlayers: %s\nPlayers: %s\nUptime: %s\n", stats->playingPlayers, stats->players, stats->uptime); -} - -void onPlayerUpdate(char *guildId, char *time, char *position, int connected, char *ping) { - log_trace("[COGLINK] GuildId: %s\n Time: %s\n Position: %s\n Connected: %d\n Ping: %s", guildId, time, position, connected, ping); -} - -void onUnknownOp(char *op, const char *text) { - log_error("[COGLINK] Unknown OP. [%s/%s]", op, text); -} - -struct coglink_lavaInfo lavaInfo = { - .events = - &(struct coglink_lavalinkEvents) { - .onRaw = &onRaw, - - .onConnect = &onConnect, - .onClose = &onClose, - - .onTrackStart = &onTrackStart, - .onTrackEnd = &onTrackEnd, - .onTrackException = &onTrackException, - .onTrackStuck = &onTrackStuck, - .onWebSocketClosed = &onWebSocketClosed, - .onUnknownEvent = &onUnknownEvent, - - .onStats = &onStats, - .onPlayerUpdate = &onPlayerUpdate, - .onUnknownOp = &onUnknownOp - }, - .debugging = - &(struct coglink_coglinkDebugging) { - .allDebugging = true - }, - .shards = "1", - .botId = "123456789012345678", - .allowResuming = 0, - .allowCachingVoiceChannelIds = 1 -}; - -void on_message(struct discord *client, const struct discord_message *message) { - if (message->author->bot) return; - if (0 == strncmp(".play ", message->content, 6)) { - char *songName = message->content + 6; - - if (songName[0] == '\0') { - struct discord_embed embed[] = { - { - .description = "Sorry, you must put a music name after the command.", - .footer = - &(struct discord_embed_footer){ - .text = "Powered by Coglink and Concord", - .icon_url = "https://raw.githubusercontent.com/Cogmasters/concord/master/docs/static/concord-small.png", - }, - .timestamp = discord_timestamp(client), - .color = 16711680 - } - }; - - struct discord_create_message params = { - .flags = 0, - .embeds = - &(struct discord_embeds){ - .size = 1, - .array = embed, - }, - }; - - discord_create_message(client, message->channel_id, ¶ms, NULL); - return; - } - - struct coglink_requestInformation res; - coglink_searchSong(&lavaInfo, songName, &res); - - struct coglink_parsedTrackStruct parsedStruct; - coglink_initParseTrack(&lavaInfo, &parsedStruct, &res); - - int loadType; - coglink_parseLoadtype(&lavaInfo, &parsedStruct, &res, &loadType); - - switch(loadType) { - case COGLINK_LOADTYPE_SEARCH_RESULT: { - struct coglink_parsedTrack song; - - coglink_parseTrack(&lavaInfo, &parsedStruct, &res, "0", &song); - coglink_playSong(&lavaInfo, song.encoded, message->guild_id); - - char description[256]; - snprintf(description, sizeof(description), "Now playing `%s` from `%s`", song.title, song.author); - - struct discord_embed embed[] = { - { - .title = song.title, - .url = song.uri, - .description = description, - .footer = - &(struct discord_embed_footer){ - .text = "Powered by Coglink and Concord", - .icon_url = "https://raw.githubusercontent.com/Cogmasters/concord/master/docs/static/concord-small.png", - }, - .timestamp = discord_timestamp(client), - .color = 15615 - } - }; - - struct discord_create_message params = { - .flags = 0, - .embeds = - &(struct discord_embeds){ - .size = 1, - .array = embed, - }, - }; - - discord_create_message(client, message->channel_id, ¶ms, NULL); - break; - } - case COGLINK_LOADTYPE_EMPTY: { - struct discord_embed embed[] = { - { - .description = "Hmmm... Lavalink was unable to find that music. :/", - .footer = - &(struct discord_embed_footer){ - .text = "Powered by Coglink and Concord", - .icon_url = "https://raw.githubusercontent.com/Cogmasters/concord/master/docs/static/concord-small.png", - }, - .timestamp = discord_timestamp(client), - .color = 16711680 - } - }; - - struct discord_create_message params = { - .flags = 0, - .embeds = - &(struct discord_embeds){ - .size = 1, - .array = embed, - }, - }; - - discord_create_message(client, message->channel_id, ¶ms, NULL); - break; - } - case COGLINK_LOADTYPE_LOAD_FAILED: { - struct discord_embed embed[] = { - { - .description = "Oop- Some wild error happened while searching the music, what so ever, it is not a Coglink error, but a Lavalink one, phew!", - .footer = - &(struct discord_embed_footer){ - .text = "Powered by Coglink and Concord", - .icon_url = "https://raw.githubusercontent.com/Cogmasters/concord/master/docs/static/concord-small.png", - }, - .timestamp = discord_timestamp(client), - .color = 16711680 - } - }; - - struct discord_create_message params = { - .flags = 0, - .embeds = - &(struct discord_embeds){ - .size = 1, - .array = embed, - }, - }; - - discord_create_message(client, message->channel_id, ¶ms, NULL); - break; - } - } - - coglink_joinUserVoiceChannel(&lavaInfo, client, message->author->id, message->guild_id); - - coglink_searchSongCleanup(&res); - } - if (0 == strcmp(".stop", message->content)) { - coglink_stopPlayer(&lavaInfo, message->guild_id); - } - if (0 == strcmp(".pause", message->content)) { - coglink_pausePlayer(&lavaInfo, message->guild_id, "true"); - } - if (0 == strcmp(".resume", message->content)) { - coglink_pausePlayer(&lavaInfo, message->guild_id, "false"); - } - if (0 == strncmp(".seek ", message->content, 6)) { - char *seek = message->content + 6; - - coglink_seekTrack(&lavaInfo, message->guild_id, seek); - } - if (0 == strncmp(".volume ", message->content, 8)) { - char *volume = message->content + 8; - - coglink_setPlayerVolume(&lavaInfo, message->guild_id, volume); - } - if (0 == strcmp(message->content, ".8d")) { - coglink_setEffect(&lavaInfo, message->guild_id, COGLINK_FILTER_ROTATION, "0.2"); - } - if (0 == strcmp(".destroy", message->content)) { - coglink_destroyPlayer(&lavaInfo, message->guild_id); - } - if (0 == strcmp(".closeNode", message->content)) { - coglink_disconnectNode(&lavaInfo, 0); - } - if (0 == strcmp(".getinfo", message->content)) { - struct coglink_requestInformation res; - - coglink_getLavalinkInfo(&lavaInfo, message->guild_id, &res); - - log_debug("[COGLING] Lavalink Info: %s", res.body); - } - if (0 == strcmp(".getrouter", message->content)) { - struct coglink_requestInformation res; - - coglink_getRouterPlanner(&lavaInfo, message->guild_id, &res); - - log_debug("[COGLING] Router planner: %s", res.body); - } -} - -int main(void) { - struct discord *client = discord_config_init("config.json"); - - struct coglink_lavalinkNode nodes[] = { - { - .name = "Node1", - .hostname = "Node hostname:port if exists", - .password = "youshallnotpass", - .ssl = 0 - } - }; - - struct coglink_lavalinkNodes nodeArray = { - .nodes = nodes, - .size = 1 - }; - - struct coglink_nodeInfo nodeInfos[1]; - coglink_connectNode(&lavaInfo, client, &nodeArray, nodeInfos); - - /* Or to set events, you can also use */ - // coglink_setEvents(&lavaInfo, &lavaInfo->events); - - discord_set_on_ready(client, &on_ready); - discord_set_on_message_create(client, &on_message); - - discord_add_intents(client, DISCORD_GATEWAY_GUILD_VOICE_STATES); - discord_add_intents(client, DISCORD_GATEWAY_MESSAGE_CONTENT); - - discord_run(client); - - coglink_connectNodeCleanup(&lavaInfo, client); -} diff --git a/guides/images/CoglinkLogo.png b/guides/images/CoglinkLogo.png deleted file mode 100644 index 2fafb8c..0000000 Binary files a/guides/images/CoglinkLogo.png and /dev/null differ diff --git a/guides/pt-BR/aplicando_efeitos.md b/guides/pt-BR/aplicando_efeitos.md deleted file mode 100644 index ee99026..0000000 --- a/guides/pt-BR/aplicando_efeitos.md +++ /dev/null @@ -1,36 +0,0 @@ -# Aplicando efeitos - -## Efeitos? Quais efeitos? - -A Lavalink permite que você adicione efeitos nas suas tracks, de vários tipos, possibilitando até você alterar as bands, possibilitando efeitos como karaoke, nightcore, bassbost, entre outros. - -## Adicionando efeitos - -Nessa guide, um exemplo de como alterar o efeito rotation será usado. - -O Coglink oferece a função `coglink_setEffect`, que você pode alterar todos os efeitos que a Lavalink oferece, seu uso é bem fácil, veja a seguir um exemplo: - -```c -coglink_setEffect(&lavaInfo, message->guild_id, COGLINK_FILTER_ROTATION, "{\"rotationHz\":0.2}"); -``` - -Oi? "Cade o resto do código"? É só isso mesmo, incrível, não? - -Esse código fará o famoso efeito "8d" em sua track, além de fácil, é legal, não acha? - -## Removendo efeitos - -O tópico acima é de como adicionar um efeito, mas como removemos? - -O arquivo `definitions.h` do Coglink te oferece várias macros para ser usadas, uma delas é a `FILTER_REMOVE`, onde que define o valor para que as funções do Coglink entenda que você quer remover o efeito. - -Veja o exemplo a seguir: - -```c -coglink_setEffect(&lavaInfo, message->guild_id, COGLINK_FILTER_REMOVE, NULL); -``` - -E pronto! Você vai ter removido o efeito, caso tenha adicionado um. - -* *Guia por*: @ThePedroo -* Feito com amor por um brasileiro! ❤️ diff --git a/guides/pt-BR/conectando_lavalink.md b/guides/pt-BR/conectando_lavalink.md deleted file mode 100644 index ce6bab4..0000000 --- a/guides/pt-BR/conectando_lavalink.md +++ /dev/null @@ -1,55 +0,0 @@ -# Conectando nodes da lavalink - -## Conectando - -De padrão, toda Lavalink é hosteada na porta `2333`, que nesse guia, a gente vai assumir que a sua também está sendo hosteada nessa mesma porta. - -Para você conectar uma node, você vai precisar de 5 informações diferentes, o hostname, a porta, a senha, se tem criptografia, quantas shards o BOT tem, e o Id do BOT. Caso você já saiba esses, prossiga para a próxima etapa, caso não, ache eles antes de continuar, já que os mesmos são necessários. - -Com tudo isso, a gente pode prosseguir usando a função `coglink_connectNode`, olhe o exemplo a baixo: - -```c -struct lavaInfo lavaInfo = { - .debug = 0 -}; - -... - -struct coglink_lavalinkNode nodes[] = { - { - .name = "Node1", - .hostname = "Hostname do node:port caso exista", - .password = "você não deve passar", - .ssl = 0 - } -}; - -struct coglink_lavalinkNodes nodeArray = { - .nodes = nodes, - .size = 1 -}; - -struct coglink_nodeInfo nodeInfos[1]; -coglink_connectNode(&lavaInfo, client, &nodeArray, nodeInfos); - -... - -// Depois do discord_run - -coglink_connectNodeCleanup(&lavaInfo, client); -``` - -Pronto, agora você já vai estar conectando à sua Lavalink sem problemas, caso não, olhe as fields das estruturas e o firewall das duas máquinas. - -## Desconectando - -Para você fechar a conexão WebSocket com a Lavalink, você pode usar a função `coglink_disconnectNode`, olhe o exemplo a baixo: - -```c -coglink_disconnectNode(&lavaInfo, 0); -``` - -Sim, é fácil assim, e pronto, a conexão WebSocket com a Lavalink já vai estar fechada. - -* *Guia por*: @ThePedroo -* Feito com amor por um brasileiro! ❤️ diff --git a/guides/pt-BR/main.md b/guides/pt-BR/main.md deleted file mode 100644 index 1bbb837..0000000 --- a/guides/pt-BR/main.md +++ /dev/null @@ -1,20 +0,0 @@ -# Lavalink - -## O que é isso? - -[Lavalink](https://github.com/freyacodes/Lavalink) é um sistema de processamento de áudio poderoso, que permite que você toque áudios de vídeos do YouTube no Discord. Lavalink nodes não são totalmente conectadas diretamente com seu BOT, elas podem ser hosteadas em outas máquinas sem problemas, e você pode ter várias nodes em uma máquina ou em várias, providenciando melhor estabilidade para BOTs grandes, não deixando uma node específica sobrecarregar e causar dela tomar rate limit. - -## Por quê? - -Mesma sendo lenta e pesada, a Lavalink é uma opção fácil para tocar vídeos do YouTube no Discord, já que fazer isso manualmente daria muito trabalho para pessoas que já não tem uma base, e ela providencia efeitos e muitos outras funções que seria difíceis de serem adicionadas manualmente. Ela também providencia uma maneira fácil de tocar músicas de outras fontes, tipo, SoundCloud, Bandcamp, Twitch, Vimeo, Mixer, e mais. - -## Como? - -A Lavalink é feita com Java, junto com Kotlin. Quando você da download nos seus binários, e executa eles, eles vão criar um servidor WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) na porta 2333 (só caso você não trocar a porta no arquivo [`application.yml`](https://github.com/freyacodes/Lavalink/blob/master/LavalinkServer/application.yml.example)), que você irá conseguir acessar com um cliente WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API), por exemplo, o cliente [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) do [Concord](https://github.com/Cogmasters/concord). Tem várias livrarias que conectam na Lavalink node e providencia à você funções para gerenciar a Lavalink, tipo o Coglink. - -## Concord? Por que eu deveria usar o Concord invés de outras livrarias? - -Concord é uma livraria assincrónica do [Discord](https://discord.com) usando o C99, providenciando funções low-level para contrar seu bot com elas, e mesmo assim, providenciando uma performance alta, não perdendo as estruturas originais das repostas da Discord API, fazendo ser fácil de se manter, ajeitar, melhor, atualizar, e muito mais. - -* *Guia por*: @ThePedroo -* Feito com amor por um brasileiro! ❤️ diff --git a/guides/pt-BR/parse_no_search.md b/guides/pt-BR/parse_no_search.md deleted file mode 100644 index f306d20..0000000 --- a/guides/pt-BR/parse_no_search.md +++ /dev/null @@ -1,51 +0,0 @@ -# Dando parse na resposta do search - -## Parsing? Por quê? - -Dar parse no search é algo muito importate, já que permite você pegar o nome da música, a duração dela, sua thumbnail, seu URL, e muito mais. Caso você não de, você nem vai conseguir tocar a música. - -## Parsing - -Para dar parse, você vai precisar usar a função `coglink_parseSearch`, que vai preencher o último parâmetro com as informações da música que tu pediu para dar parse. - -Por exemplo, você vai provavelmente querer dar parse na primeira música, não é? Então siga o exemplo abaixo para fazer isso: - -```c -struct coglink_parsedTrackStruct parsedStruct; -coglink_initParseTrack(&lavaInfo, &parsedStruct, &res); - -struct parsedTrack song; -int parseRes = coglink_parseTrack(&lavaInfo, &parsedStruct, &res, "0", &song); - -if (parseRes != 0) - log_fatal("Erro dando parse: %d", parseRes); - -log_info("Nome da música: %s", song->name); -``` - -Pronto, agora a estrutura `parsedTrack` vai estar com as informações da música. - -## Parsing + loop - -Vamos fazer algo top aqui, caso você queira dar parse nas primeiras 10 músicas achadas, você pode usar o loop for, olhe o exemplo abaixo: - -```c -struct parsedTrack song; - -for (int i = 0; i < 10; i++) { - char iString[16]; - snprintf(iString, sizeof(iString), "%d", i); - - int parseRes = coglink_parseTrack(&lavaInfo, &parsedStruct, &res, iString, &song); - - if (parseRes != 0) - log_fatal("Erro dando parse na música: %d", parseRes); - - log_info("Nome da música: %s", song->name); -} -``` - -Pronto, isso vai dar print nas primeiras 10 músicas achadas. - -* *Guia por*: @ThePedroo -* Feito com amor por um brasileiro! ❤️ diff --git a/guides/pt-BR/procurando_musicas.md b/guides/pt-BR/procurando_musicas.md deleted file mode 100644 index 6604b75..0000000 --- a/guides/pt-BR/procurando_musicas.md +++ /dev/null @@ -1,38 +0,0 @@ -# Procurando uma música - -## Procurando de ONDE? - -A Lavalink permite que você procure uma música de várias fontes, e nesse guia, você vai poder customizar a função de search para procurar de uma fonte específica ou de todas as fontes. - -## Searching - -Para procurar por músicas, a gente vai usar a função `coglink_searchSong`, que vai preencher o último parâmetro, a estrutura `struct requestInformation *` com o body (JSON) da resposta da Lavalink. - -### Por que o Coglink não da parse automaticamente - -O Coglink foi feito pensando em performance, se fosse para dar parse no JSON inteiro, causaria uma perda de performance enorme, então a gente decidiu criar uma função que você pode escolher qual música você quer dar parse, a primeira, a segunda, a terceira, e assim por diante, fazendo ficar mais ágil e melhor. - -Olhe o exemplo abaixo da função `coglink_searchSong` sendo usada: - -```c -struct coglink_requestInformation res; - -int resStatus = coglink_searchSong(&lavaInfo, "Very ordinary life", &res); - -if (resStatus != 0) { - log_fatal("Erro dando parse na música: %d", resStatus); -} - -log_debug("Body da resposta da Lavalink: %s", res.body); - -/* ... */ - -coglink_searchSongCleanup(&res); -``` - -Pronto, o código acima vai dar print na resposta da search da Lavalink, e caso ele falhe, irá retornar um valor int de o que deu errado. - -Você pode prosseguir com o próximo guia, o guia [`dando parse na resposta do search`](/guides/pt-BR/parse_no_search.md) - -* *Guia por*: @ThePedroo -* Feito com amor por um brasileiro! ❤️ diff --git a/include/codecs.h b/include/codecs.h new file mode 100644 index 0000000..e92133a --- /dev/null +++ b/include/codecs.h @@ -0,0 +1,142 @@ +#ifndef COGLINK_CODECS +#define COGLINK_CODECS + +#include "utils.h" + +#include + +/* coglink_parse_websocket_data */ + +#define COGLINK_PARSE_ERROR -1 + +struct coglink_ready_payload { + char *session_id; + bool resumed; +}; + +#define COGLINK_READY 0 + +struct coglink_player_state_payload { + int time; + int position; + bool connected; + int ping; +}; + +struct coglink_player_update_payload { + u64snowflake guildId; + struct coglink_player_state_payload *state; +}; + +#define COGLINK_PLAYER_UPDATE 2 + +struct coglink_stats_memory_payload { + int free; + int used; + int allocated; + int reservable; +}; + +struct coglink_stats_cpu_payload { + int cores; + int systemLoad; + int lavalinkLoad; +}; + +struct coglink_stats_frame_stats_payload { + int sent; + int nulled; + int deficit; +}; + +struct coglink_stats_payload { + int players; + int playingPlayers; + int uptime; + struct coglink_stats_memory_payload *memory; + struct coglink_stats_cpu_payload *cpu; + struct coglink_stats_frame_stats_payload *frameStats; +}; + +#define COGLINK_STATS 3 + +struct coglink_partial_track { + char identifier[64]; + bool isSeekable; + char author[128]; + size_t length; + bool isStream; + size_t position; + char title[256]; + char uri[256]; + char isrc[64]; + char artworkUrl[256]; + char sourceName[16]; +}; + +struct coglink_track { + char encoded[512]; + struct coglink_partial_track *info; +}; + +struct coglink_track_start_payload { + u64snowflake guildId; + struct coglink_track *track; +}; + +#define COGLINK_TRACK_START 41 + +struct coglink_track_end_payload { + u64snowflake guildId; + struct coglink_track *track; + char reason[16]; +}; + +#define COGLINK_TRACK_END 42 + +/* coglink_parse_load_tracks_response */ + +struct coglink_tracks { + struct coglink_partial_track *array; + size_t size; +}; + +enum coglink_load_type { + COGLINK_LOAD_TYPE_TRACK, + COGLINK_LOAD_TYPE_PLAYLIST, + COGLINK_LOAD_TYPE_SEARCH, + COGLINK_LOAD_TYPE_EMPTY, + COGLINK_LOAD_TYPE_ERROR +}; + +struct coglink_load_tracks_response { + enum coglink_load_type type; + struct coglink_tracks *tracks; +}; + +/* coglink_parse_voice_state */ + +struct coglink_voice_state { + u64snowflake guild_id; + u64snowflake channel_id; + u64snowflake user_id; + char *session_id; +}; + +/* coglink_parse_voice_server_update */ + +struct coglink_voice_server_update { + char *token; + char *endpoint; + u64snowflake guild_id; +}; + +void *coglink_parse_websocket_data(int *type, const char *json, size_t length); + +struct coglink_load_tracks_response *coglink_parse_load_tracks_response(const char *json, size_t length); + +struct coglink_voice_state *coglink_parse_voice_state(const char *json, size_t length); + +struct coglink_voice_server_update *coglink_parse_voice_server_update(const char *json, size_t length); + +#endif diff --git a/include/definitions.h b/include/definitions.h deleted file mode 100755 index c9f5792..0000000 --- a/include/definitions.h +++ /dev/null @@ -1,106 +0,0 @@ -/** \file - * File containing the macros of Coglink. - */ - -#ifndef DEFINATIONS_H -#define DEFINATIONS_H - -/** Macro for defininition of volume filter. */ -#define COGLINK_FILTER_VOLUME 0 -/** Macro for defininition of equalizer filte. */ -#define COGLINK_FILTER_EQUALIZER 1 -/** Macro for defininition of karaoke filte. */ -#define COGLINK_FILTER_KARAOKE 2 -/** Macro for defininition of timescale filte. */ -#define COGLINK_FILTER_TIMESCALE 3 -/** Macro for defininition of tremolo filte. */ -#define COGLINK_FILTER_TREMOLO 4 -/** Macro for defininition of rotation filter. */ -#define COGLINK_FILTER_ROTATION 5 -/** Macro for defininition of distortion filter. */ -#define COGLINK_FILTER_DISTORTION 6 -/** Macro for defininition of channelMix filter. */ -#define COGLINK_FILTER_CHANNELMIX 7 -/** Macro for defininition of lowPass filter. */ -#define COGLINK_FILTER_LOWPASS 8 -/** Macro for defininition of "remove filter". */ -#define COGLINK_FILTER_REMOVE 9 - -#define COGLINK_ROUTERPLANNER_CLASS_LENGTH 26 -#define COGLINK_LAVALINK_SESSIONID_LENGTH 17 - -#define COGLINK_VOLUME_LENGTH 8 -#define COGLINK_TOKEN_LENGTH 128 -#define COGLINK_ENDPOINT_LENGTH 64 -#define COGLINK_SESSIONID_LENGTH 16 -#define COGLINK_PING_LENGTH 8 -#define COGLINK_BANDS_LENGTH 512 -#define COGLINK_GAIN_LENGTH 128 - -#define COGLINK_DISCORD_TOKEN_LENGTH 17 -#define COGLINK_VOICE_ID_LENGTH 20 -#define COGLINK_GUILD_ID_LENGTH 20 -#define COGLINK_USER_ID_LENGTH 20 -#define COGLINK_SESSION_ID_LENGTH 128 -#define COGLINK_TRUE_FALSE_LENGTH 8 - -#define COGLINK_TRACK_LENGTH 512 -#define COGLINK_IDENTIFIER_LENGTH 13 -#define COGLINK_TRACK_TITLE_LENGTH 100 -#define COGLINK_PLAYLIST_NAME_LENGTH 64 -#define COGLINK_AUTHOR_NAME_LENGTH 30 -#define COGLINK_VIDEO_LENGTH 16 -#define COGLINK_TIME_LENGTH 16 -#define COGLINK_URL_LENGTH 44 -#define COGLINK_SOURCENAME_LENGTH 10 - -#define COGLINK_WAIT 103 - -/** Macro for defininition of when Lavalink did not found any match to query. */ -#define COGLINK_LOADTYPE_EMPTY 5 -/** Macro for defininition of when **Lavalink** failed to load result. */ -#define COGLINK_LOADTYPE_LOAD_FAILED 4 -/** Macro for defininition of when Lavalink returned the search resukt. */ -#define COGLINK_LOADTYPE_SEARCH_RESULT 3 -/** Macro for defininition of when Lavalink found a playlist. */ -#define COGLINK_LOADTYPE_PLAYLIST_LOADED 2 -/** Macro for defininition of when Lavalink found a track. */ -#define COGLINK_LOADTYPE_TRACK_LOADED 1 - -/** Macro for defininition of when Coglink should proceed handling event. */ -#define COGLINK_PROCEED 1 -/** Macro for defininition of when the function occured with no issues. */ -#define COGLINK_SUCCESS 0 -/** Macro for defininition of when Coglink should not proceeding handling event. */ -#define COGLINK_STOP -1 - -/** Macro for defininition of when jsmnf failed to parse the JSON. */ -#define COGLINK_JSMNF_ERROR_PARSE -2 -/** Macro for defininition of when jsmnf failed to load. */ -#define COGLINK_JSMNF_ERROR_LOAD -3 -/** Macro for defininition of when jsmnf failed to find the JSON key. */ -#define COGLINK_JSMNF_ERROR_FIND -4 - -/** Macro for defininition of when libcurl failed to initialize. */ -#define COGLINK_LIBCURL_FAILED_INITIALIZE -5 -/** Macro for defininition of when libcurl failed to execute setopt function. */ -#define COGLINK_LIBCURL_FAILED_SETOPT -6 -/** Macro for defininition of when libcurl failed to send a request. */ -#define COGLINK_LIBCURL_FAILED_PERFORM -7 - -/** Macro for defininition of when a router planner was not set. */ -#define COGLINK_ROUTERPLANNER_NOT_SET -8 - -/** Macro for defininition of when some unknown error happened. */ -#define COGLINK_ERROR -9 - -/** Macro for defininition of when there is no players. */ -#define COGLINK_NO_PLAYERS -10 - -/** Macro for defination of when TableC does not find anything related to the key. */ -#define COGLINK_TABLEC_NOT_FOUND -11 - -/** Macro for defination of when there is no nodes avaible. */ -#define COGLINK_NO_NODES -12 - -#endif diff --git a/include/events.h b/include/events.h new file mode 100644 index 0000000..93559d4 --- /dev/null +++ b/include/events.h @@ -0,0 +1,23 @@ +#ifndef COGLINK_LAVALINK_EVENTS_H +#define COGLINK_LAVALINK_EVENTS_H + +#include "lavalink.h" +// #include "utils.h" +#include "codecs.h" + +struct coglink_events { + int (*onRaw)(struct coglink_client *c_client, const char *data, size_t length); + void (*onReady)(struct coglink_ready_payload *ready); + void (*onClose)(enum ws_close_reason wscode, const char *reason); + // void (*onTrackStart)(char *guild_id, struct coglink_partial_track *track); + // void (*onTrackEnd)(char *guild_id, struct coglink_partial_track *track, char *reason); + // void (*onTrackException)(char *guild_id, struct coglink_partial_track *track, char *message, char *severity, char *cause); + // void (*onTrackStuck)(char *guild_id, char *thresholdMs, struct coglink_track *track); + // void (*onWebSocketClosed)(char *guild_id, char *code, char *reason, int byRemote); + // void (*onUnknownEvent)(char *guild_id, char *type, const char *text); + void (*onPlayerUpdate)(struct coglink_player_update_payload *playerUpdate); + void (*onStats)(struct coglink_stats_payload *stats); + // void (*onUnknownOp)(char *op, const char *text); +}; + +#endif /* COGLINK_LAVALINK_EVENTS_H */ diff --git a/include/information.h b/include/information.h deleted file mode 100755 index b4f04f7..0000000 --- a/include/information.h +++ /dev/null @@ -1,90 +0,0 @@ -/** \file - * File containing the Lavalink information related functions. - */ - -#ifndef MISCELLANEOUS_H -#define MISCELLANEOUS_H - -struct coglink_lavalinkInfoVersion { - char semver[8]; - char major[4]; - char minor[4]; - char patch[4]; - char preRelease[8]; -}; - -struct coglink_lavalinkInfoGit { - char branch[16]; - char commit[32]; - char commitTime[16]; -}; - -struct coglink_lavalinkInfo { - struct coglink_lavalinkInfoVersion *version; - char buildTime[16]; - struct coglink_lavalinkInfoGit *git; - char jvm[8]; - char lavaplayer[8]; - char sourceManagers[128]; - char filters[128]; - char plugins[128]; -}; - -/** - * Retrieves the Lavalink version. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild. - * @param version String with Lavalink version. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_getLavalinkVersion(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char **version); - -/** - * Retrieves the informations of the Lavalink node Lavalink.jar. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild. - * @param res Structure with the information of the request. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_getLavalinkInfo(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, struct coglink_requestInformation *res); - -/** - * Parses the response body of coglink_getLavalinkInfo function. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param res Structure with the information of the request. - * @param lavalinkInfoStruct Structure with the parsed information. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseLavalinkInfo(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, struct coglink_lavalinkInfo *lavalinkInfoStruct); - -/** - * Frees the allocations generated while performing the function coglink_getLavalinkInfo. - * @param res Structure with the information of the request. - */ -void coglink_getLavalinkInfoCleanup(struct coglink_requestInformation *res); - -/** - * Retrieves the stats of the Lavalink node. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild. - * @param res Structure with the information of the request. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_getLavalinkStats(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, struct coglink_requestInformation *res); - -/** - * Parses the response body of coglink_getLavalinkStats function. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param res Structure with the information of the request. - * @param lavalinkStatsStruct Structure with the parsed information. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseLavalinkStats(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, struct coglink_lavalinkStats *lavalinkStatsStruct); - -/** - * Frees the allocations generated while performing the function coglink_getLavalinkStats. - * @param res Structure with the information of the request. - */ -void coglink_getLavalinkStatsCleanup(struct coglink_requestInformation *res); - -#endif diff --git a/include/lavalink-internal.h b/include/lavalink-internal.h deleted file mode 100755 index 4d7c3eb..0000000 --- a/include/lavalink-internal.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef LAVALINK_INTERNAL_H -#define LAVALINK_INTERNAL_H - -#include - -#include -#include - -struct coglink_lavaInfo; -struct coglink_requestInformation; -struct coglink_nodeInfo; -struct io_poller; - -#define __COGLINK_GET_REQ 0 -#define __COGLINK_POST_REQ 1 -#define __COGLINK_DELETE_REQ 2 -#define __COGLINK_PATCH_REQ 3 - -struct __coglink_requestConfig { - int requestType; - int additionalDebuggingSuccess; - int additionalDebuggingError; - char *path; - int pathLength; - int useVPath; - char *body; - long bodySize; - int getResponse; - CURL *usedCURL; -}; - -int _coglink_performRequest(struct coglink_lavaInfo *lavaInfo, struct coglink_nodeInfo *nodeInfo, struct coglink_requestInformation *res, struct __coglink_requestConfig *config); - -int _coglink_checkParse(struct coglink_lavaInfo *lavaInfo, jsmnf_pair *field, char *fieldName); - -int _coglink_selectBestNode(struct coglink_lavaInfo *lavaInfo); - -int _coglink_findNode(struct coglink_lavaInfo *lavaInfo, char *hostname); - -int _coglink_IOPoller(struct io_poller *io, CURLM *multi, void *user_data); - -struct coglink_parsedTrack _coglink_buildTrackStruct(struct coglink_lavaInfo *lavaInfo, jsmnf_pair pairs[], const char *text); - -#endif diff --git a/include/lavalink.h b/include/lavalink.h old mode 100755 new mode 100644 index 5b2c885..ed492b0 --- a/include/lavalink.h +++ b/include/lavalink.h @@ -1,234 +1,51 @@ -/** \file - * File containing the Lavalink WebSocket related functions. - */ - -#ifndef LAVALINK_H -#define LAVALINK_H +#ifndef COGLINK_LAVALINK_H +#define COGLINK_LAVALINK_H +#include #include +#include -#include "plugins.h" -#include "definitions.h" - -struct coglink_requestInformation { - char *body; - size_t size; -}; - -struct coglink_parsedTrack { - char encoded[COGLINK_TRACK_LENGTH]; - char identifier[COGLINK_IDENTIFIER_LENGTH]; - char isSeekable[COGLINK_TRUE_FALSE_LENGTH]; - char author[COGLINK_AUTHOR_NAME_LENGTH]; - char length[COGLINK_VIDEO_LENGTH]; - char isStream[COGLINK_TRUE_FALSE_LENGTH]; - char position[COGLINK_VIDEO_LENGTH]; - char title[COGLINK_TRACK_TITLE_LENGTH]; - char uri[COGLINK_URL_LENGTH]; - char isrc[64]; - char artworkUrl[256]; - char sourceName[COGLINK_SOURCENAME_LENGTH]; -}; - -struct coglink_parsedPlaylist { - char name[COGLINK_PLAYLIST_NAME_LENGTH]; - char selectedTrack[8]; -}; - -struct coglink_parsedError { - char message[128]; - char severity[16]; -}; - -struct coglink_lavalinkStatsMemory { - char free[16]; - char used[16]; - char allocated[16]; - char reservable[16]; -}; - -struct coglink_lavalinkStatsFrameStats { - char sent[16]; - char deficit[16]; - char nulled[16]; -}; - -struct coglink_lavalinkStatsCPU { - char cores[8]; - char systemLoad[16]; - char lavalinkLoad[16]; -}; - -struct coglink_lavalinkStats { - char players[8]; - char playingPlayers[8]; - char uptime[32]; - struct coglink_lavalinkStatsMemory *memory; - struct coglink_lavalinkStatsCPU *cpu; - struct coglink_lavalinkStatsFrameStats *frameStats; -}; - -struct coglink_coglinkDebugging { - int allDebugging; - int sendPayloadErrorsDebugging; - int sendPayloadSuccessDebugging; - int checkParseErrorsDebugging; - int checkParseSuccessDebugging; - int joinVoiceDebugging; - int jsmnfErrorsDebugging; - int jsmnfSuccessDebugging; - int handleSchedulerVoiceStateDebugging; - int handleSchedulerVoiceServerDebugging; - int joinUserVoiceChannelDebugging; - int tablecErrorsDebugging; - int tablecSuccessDebugging; - int parseTrackErrorsDebugging; - int parseTrackSuccessDebugging; - int parsePlaylistErrorsDebugging; - int parsePlaylistSuccessDebugging; - int parseErrorsDebugging; - int parseSuccessDebugging; - int parseLoadtypeErrorsDebugging; - int parseLoadtypeSuccessDebugging; - int searchSongErrorsDebugging; - int searchSongSuccessDebugging; - int curlErrorsDebugging; - int curlSuccessDebugging; - int memoryDebugging; -}; - -struct coglink_lavalinkEvents { - int (*onRaw)(struct coglink_lavaInfo *lavaInfo, const char *data, size_t length); - void (*onConnect)(char *sessionId); - void (*onClose)(enum ws_close_reason wscode, const char *reason); - void (*onTrackStart)(char *guildId, struct coglink_parsedTrack *track); - void (*onTrackEnd)(char *guildId, struct coglink_parsedTrack *track, char *reason); - void (*onTrackException)(char *guildId, struct coglink_parsedTrack *track, char *message, char *severity, char *cause); - void (*onTrackStuck)(char *guildId, char *thresholdMs, struct coglink_parsedTrack *track); - void (*onWebSocketClosed)(char *guildId, char *code, char *reason, int byRemote); - void (*onUnknownEvent)(char *guildId, char *type, const char *text); - void (*onPlayerUpdate)(char *guildId, char *time, char *position, int connected, char *ping); - void (*onStats)(struct coglink_lavalinkStats *stats); - void (*onUnknownOp)(char *op, const char *text); -}; +#include "types.h" -struct _coglink_nodeStats { - int cores; - double systemLoad; -}; - -struct coglink_lavalinkNode { +struct coglink_node { + /* Params */ char *name; char *hostname; + int port; char *password; - int ssl; -}; - -struct coglink_lavalinkNodes { - struct coglink_lavalinkNode *nodes; - int size; -}; - -struct coglink_nodeInfo { - struct websockets *ws; - struct coglink_lavalinkNode node; - struct _coglink_nodeStats stats; - char sessionId[COGLINK_LAVALINK_SESSIONID_LENGTH]; + bool ssl; + /* Public info */ + char *session_id; + /* todo: use a smaller struct */ + struct coglink_stats_payload *stats; + /* Internal */ uint64_t tstamp; CURLM *mhandle; + struct websockets *ws; }; -struct coglink_lavaInfo { - struct coglink_nodeInfo *nodes; - struct coglink_lavalinkEvents *events; - struct coglink_coglinkPlugins *plugins; - struct coglink_coglinkDebugging *debugging; - char *botId; - char *shards; - int nodeId; - int nodeCount; - int allowResuming; - int allowCachingVoiceChannelIds; +struct coglink_nodes { + struct coglink_node *array; + size_t size; }; -struct coglink_voiceData { - char *token; - char *endpoint; - char *sessionId; +struct coglink_client { + /* Params */ + u64snowflake bot_id; + char *num_shards; + struct coglink_events *events; + /* Public info */ + /* todo: replace for a TableC hashtable? */ + struct coglink_nodes *nodes; + struct coglink_players *players; + struct coglink_users *users; + bool allow_resuming; + bool allow_caching_voice_channel_ids; }; -void _coglink_createPlayer(u64snowflake guildId, int data); - -int _coglink_findPlayerNode(u64snowflake guildId); - -void onConnectEvent(void *data, struct websockets *ws, struct ws_info *info, const char *protocols); - -void onCloseEvent(void *data, struct websockets *ws, struct ws_info *info, enum ws_close_reason wscode, const char *reason, size_t len); - -void onTextEvent(void *data, struct websockets *ws, struct ws_info *info, const char *text, size_t len); - -/** - * Joins a voice channel. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param client Concord's client stucture generated with discord_init. - * @param voiceChannelId ID of the voice channel that the bot will join. - * @param guildId ID of the guild of the voice channel that the bot will join. - */ -int coglink_joinVoiceChannel(struct coglink_lavaInfo *lavaInfo, struct discord *client, u64snowflake voiceChannelId, u64snowflake guildId); - -/** - * Joins a voice channel that a user is in, must have the optional settings enabled for it. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param client Concord's client stucture generated with discord_init. - * @param userId ID of the user that the bot will join in its voice channel. - * @param guildId ID of the guild of the voice channel that the bot will join. - */ -int coglink_joinUserVoiceChannel(struct coglink_lavaInfo *lavaInfo, struct discord *client, u64snowflake userId, u64snowflake guildId); - -/** - * Retrieves the voice channel that a user is in, must have the optional settings enabled for it. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param userId ID of the user that the bot will join in its voice channel. - * @param channelId Pointer to a char pointer that will be filled with the voice channel ID. - * @param channelIdSize Size of the char pointer that will be filled with the voice channel ID. - */ -int coglink_getUserVoiceChannel(struct coglink_lavaInfo *lavaInfo, u64snowflake userId, char channelId[], int channelIdSize); - -/** - * Removes all information in the lavaInfo struct - * @param lavaInfo Structure with important informations of the Lavalink. - */ -void coglink_freeNodeInfo(struct coglink_lavaInfo *lavaInfo); - -/** - * Closes the WebSocket connection with the Lavalink node. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param nodePos Position of the node in the nodes array. - */ -void coglink_disconnectNode(struct coglink_lavaInfo *lavaInfo, int nodePos); - -/** - * Sets the Lavalink events when not including it directly into lavaInfo structure. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param lavalinkEvents Lavalink event's structure with all pointers to the functions that will be executed when the event it emitted. - */ -void coglink_setEvents(struct coglink_lavaInfo *lavaInfo, struct coglink_lavalinkEvents *lavalinkEvents); - -/** - * Frees all mallocs used while connecting to a node and in other functions. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param client Concord's client stucture generated with discord_init. - */ -void coglink_connectNodeCleanup(struct coglink_lavaInfo *lavaInfo, struct discord *client); - -/** - * Creates a WebSocket connecting with the Lavalink node. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param client Concord's client stucture generated with discord_init. - * @param nodesArr Structure with all Lavalink nodes information. - * @param nodesBuf Structure buffer to be used internally for store the nodes informations. - * @returns COGLINK_SUCCESS - */ -int coglink_connectNode(struct coglink_lavaInfo *lavaInfo, struct discord *client, struct coglink_lavalinkNodes *nodesArr, struct coglink_nodeInfo nodesBuf[]); +#include "codecs.h" +#include "websocket.h" +#include "events.h" +#include "rest.h" -#endif +#endif /* COGLINK_LAVALINK_H */ diff --git a/include/network.h b/include/network.h deleted file mode 100755 index d5f5245..0000000 --- a/include/network.h +++ /dev/null @@ -1,76 +0,0 @@ -/** \file - * File containing the Lavalink network related functions. - */ - -#ifndef NETWORK_H -#define NETWORK_H - -struct coglink_lavalinkDetailsIpBlock { - char type[16]; - char size[16]; -}; - -struct coglink_lavalinkDetailsFailingAddress { - char address[8]; - char failingTimestamp[16]; - char failingTime[16]; -}; - -struct coglink_lavalinkRouterDetails { - struct coglink_lavalinkDetailsIpBlock *ipBlock; - struct coglink_lavalinkDetailsFailingAddress *failingAddress; - char blockIndex[16]; - char currentAddressIndex[16]; - char rotateIndex[16]; - char ipIndex[16]; - char currentAddress[8]; -}; - -struct coglink_lavalinkRouter { - char class[16]; - struct coglink_lavalinkRouterDetails *details; -}; - -/** - * Retrieves the Lavalink router planner. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild. - * @param res Structure with the information of the request. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_getRouterPlanner(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, struct coglink_requestInformation *res); - -/** - * Parses the response body of coglink_getRouterPlanner function. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param res Structure with the information of the request. - * @param ipPosition Position of the IP that will be parsed - * @param lavalinkRouterStruct Structure with the parsed information. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseRouterPlanner(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, char *ipPosition, struct coglink_lavalinkRouter *lavalinkRouterStruct); - -/** - * Frees the allocations generated while performing the function coglink_getRouterPlanner. - * @param res Structure with the information of the request. - */ -void coglink_getRouterPlannerCleanup(struct coglink_requestInformation *res); - -/** - * Removes an IP from failing address list. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild. - * @param ip IP that will be removed from failing address list. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_freeFailingAddress(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char *ip); - -/** - * Removes all IPs from failing address list. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_freeFailingAllAddresses(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId); - -#endif diff --git a/include/player.h b/include/player.h deleted file mode 100755 index 602fcc5..0000000 --- a/include/player.h +++ /dev/null @@ -1,178 +0,0 @@ -/** \file - * File containing the Lavalink player related functions. - */ - -#ifndef PLAYER_H -#define PLAYER_H - -struct coglink_equalizerStruct { - char bands[COGLINK_BANDS_LENGTH]; - char gain[COGLINK_GAIN_LENGTH]; -}; - -struct coglink_karaokeStruct { - char level[16]; - char monoLevel[16]; - char filterBand[16]; - char filterWidth[16]; -}; - -struct coglink_timescaleStruct { - char speed[8]; - char pitch[8]; - char rate[8]; -}; - -struct coglink_frequencyDepthStruct { - char frequency[8]; - char depth[4]; -}; - -struct coglink_distortionStruct { - char sinOffset[8]; - char sinScale[8]; - char cosOffset[8]; - char cosScale[8]; - char tanOffset[8]; - char tanScale[8]; - char offset[8]; - char scale[8]; -}; - -struct coglink_channelMixStruct { - char leftToLeft[4]; - char leftToRight[4]; - char rightToLeft[4]; - char rightToRight[4]; -}; - -struct coglink_playerInfoTrack { - char encoded[COGLINK_TRACK_LENGTH]; - struct coglink_parsedTrack *info; -}; - -struct coglink_playerInfoState { - char time[COGLINK_TIME_LENGTH]; - char position[COGLINK_VIDEO_LENGTH]; - char connected[COGLINK_TRUE_FALSE_LENGTH]; - char ping[COGLINK_PING_LENGTH]; -}; - -struct coglink_playerInfoVoice { - char token[COGLINK_TOKEN_LENGTH]; - char endpoint[COGLINK_ENDPOINT_LENGTH]; - char sessionId[COGLINK_SESSIONID_LENGTH]; -}; - -struct coglink_playerInfoFilters { - char volume[COGLINK_VOLUME_LENGTH]; - struct coglink_equalizerStruct *equalizer; - struct coglink_karaokeStruct *karaoke; - struct coglink_timescaleStruct *timescale; - struct coglink_frequencyDepthStruct *tremolo; - struct coglink_frequencyDepthStruct *vibrato; - char rotation[8]; - struct coglink_distortionStruct *distortion; - struct coglink_channelMixStruct *channelMix; - char lowPass[8]; -}; - -struct coglink_playerInfo { - char guildId[COGLINK_GUILD_ID_LENGTH]; - struct coglink_playerInfoTrack *track; - char volume[COGLINK_VOLUME_LENGTH]; - char paused[COGLINK_TRUE_FALSE_LENGTH]; - struct coglink_playerInfoState *state; - struct coglink_playerInfoVoice *voice; - struct coglink_playerInfoFilters *filters; -}; - -/** - * Creates a player for the specified guild. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild. - */ -int coglink_createPlayer(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId); - -/** - * Retrieves the players. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild. - * @param res Structure with the information of the request. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_getPlayers(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, struct coglink_requestInformation *res); - -/** - * Parses the response body of the function coglink_getPlayers. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param res Structure with the information of the request. - * @param pos Position of the player that will be parsed - * @param playerInfoStruct Structure with the parsed information. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseGetPlayers(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, char *pos, struct coglink_playerInfo *playerInfoStruct); - -/** - * Frees the allocations generated while performing the function coglink_getPlayers. - * @param res Structure with the information of the request. - */ -void coglink_getPlayersCleanup(struct coglink_requestInformation *res); - -/** - * Starts playing a track in the player of the specified guild. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param track Track that will be played. - * @param guildId ID of the guild that the track will be played. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_playSong(struct coglink_lavaInfo *lavaInfo, char *track, u64snowflake guildId); - -/** - * Destroys the player of the specified guild. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild of the player. - */ -void coglink_destroyPlayer(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId); - -/** - * Stops the player of the specified guild. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild of the player. - */ -void coglink_stopPlayer(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId); - -/** - * Pauses the player of the specified guild. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild of the player. - * @param pause String that can be true or false to whatever pause or not. - */ -void coglink_pausePlayer(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char *pause); - -/** - * Makes the track go back to play at x ms. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild of the player. - * @param position String number of the time in ms. - */ -void coglink_seekTrack(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char *position); - -/** - * Changes the volume of the player of the specified guild. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild of the player. - * @param volume String containing the volume that will be set. - */ -void coglink_setPlayerVolume(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char *volume); - -/** - * Sets a effect to the player of the specified guild. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param guildId ID of the guild of the player. - * @param effect Type of the effect that will be set. - * @param value Value of the effect. - */ -void coglink_setEffect(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, int effect, char *value); - -#endif diff --git a/include/plugins.h b/include/plugins.h deleted file mode 100755 index 991cddf..0000000 --- a/include/plugins.h +++ /dev/null @@ -1,59 +0,0 @@ -/** \file - * File containing the functions for plugin support. - */ - -#ifndef PLUGIN_H -#define PLUGIN_H - -#include -#include -#include - -struct coglink_lavaInfo; -struct coglink_requestInformation; -struct coglink_parsedTrack; - -struct coglink_pluginEvents { - int (*onSearchRequest)(struct coglink_lavaInfo *lavaInfo, char *song, struct coglink_requestInformation *res); - int (*onPlayRequest)(struct coglink_lavaInfo *lavaInfo, char *track, u64snowflake guildId); - int (*onLavalinkPacketReceived)(struct coglink_lavaInfo *lavaInfo, const char *text, size_t length); - int (*onLavalinkClose)(struct coglink_lavaInfo *lavaInfo, struct ws_info *info, enum ws_close_reason wscode, const char *reason, size_t length); - int (*onCoglinkScheduler)(struct coglink_lavaInfo *lavaInfo, struct discord *client, const char data[], size_t size, enum discord_gateway_events event); - int (*onDecodeTrackRequest)(struct coglink_lavaInfo *lavaInfo, char *track, struct coglink_requestInformation *res); - int (*onDecodeTrackParseRequest)(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, struct coglink_parsedTrack *parsedTrackStruct); - int (*onDecodeTracksRequest)(struct coglink_lavaInfo *lavaInfo, char *trackArray, long trackArrayLength, struct coglink_requestInformation *res); - int (*onDecodeTracksParseRequest)(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, char *songPos, struct coglink_parsedTrack *parsedTrackStruct); -}; - -struct coglink_pluginEventsInternal { - int (*onSearchRequest[8])(struct coglink_lavaInfo *lavaInfo, char *song, struct coglink_requestInformation *res); - int (*onPlayRequest[8])(struct coglink_lavaInfo *lavaInfo, char *track, u64snowflake guildId); - int (*onLavalinkPacketReceived[8])(struct coglink_lavaInfo *lavaInfo, const char *text, size_t length); - int (*onLavalinkClose[8])(struct coglink_lavaInfo *lavaInfo, struct ws_info *info, enum ws_close_reason wscode, const char *reason, size_t length); - int (*onCoglinkScheduler[8])(struct coglink_lavaInfo *lavaInfo, struct discord *client, const char data[], size_t size, enum discord_gateway_events event); - int (*onDecodeTrackRequest[8])(struct coglink_lavaInfo *lavaInfo, char *track, struct coglink_requestInformation *res); - int (*onDecodeTrackParseRequest[8])(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, struct coglink_parsedTrack *parsedTrackStruct); - int (*onDecodeTracksRequest[8])(struct coglink_lavaInfo *lavaInfo, char *trackArray, long trackArrayLength, struct coglink_requestInformation *res); - int (*onDecodeTracksParseRequest[8])(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, char *songPos, struct coglink_parsedTrack *parsedTrackStruct); -}; - -struct coglink_pluginSecurity { - _Bool allowReadWebsocket; - _Bool allowReadBotToken; - _Bool allowReadConcordWebsocket; -}; - -struct coglink_coglinkPlugins { - struct coglink_pluginEventsInternal *events; - struct coglink_pluginSecurity *security; - int amount; -}; - -/** - * Set the events that will be executed when performing a certain function. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param pluginEvents Structure with the pointer to the functions. - */ -void coglink_setPluginEvents(struct coglink_lavaInfo *lavaInfo, struct coglink_pluginEvents *pluginEvents); - -#endif diff --git a/include/rest.h b/include/rest.h new file mode 100644 index 0000000..f3811fb --- /dev/null +++ b/include/rest.h @@ -0,0 +1,54 @@ +#ifndef COGLINK_LAVALINK_REST_H +#define COGLINK_LAVALINK_REST_H + +#include + +struct coglink_player_queue { + char **array; + size_t size; +}; + +struct coglink_player { + size_t node; + u64snowflake guild_id; + struct coglink_voice_data *voice_data; + struct coglink_player_queue *queue; +}; + +struct coglink_players { + struct coglink_player *array; + size_t size; +}; + +struct coglink_user { + u64snowflake id; + u64snowflake channel_id; +}; + +struct coglink_users { + struct coglink_user *array; + size_t size; +}; + +struct coglink_user *coglink_get_user(struct coglink_client *c_client, u64snowflake user_id); + +int coglink_join_voice_channel(struct discord *client, u64snowflake guild_id, u64snowflake channel_id); + +struct coglink_player *coglink_create_player(struct coglink_client *c_client, u64snowflake guild_id); + +struct coglink_player *coglink_get_player(struct coglink_client *c_client, u64snowflake guild_id); + +/* Experimental */ +struct coglink_player_queue *coglink_get_player_queue(struct coglink_client *c_client, struct coglink_player *player); + +int coglink_add_track_to_queue(struct coglink_client *c_client, struct coglink_player *player, char *track); + +int coglink_remove_track_from_queue(struct coglink_client *c_client, struct coglink_player *player, char *track); + +int coglink_remove_player(struct coglink_client *c_client, struct coglink_player *player); + +int coglink_load_tracks(struct coglink_client *c_client, struct coglink_player *player, char *identifier); + +int coglink_play_track(struct coglink_client *c_client, struct coglink_player *player, char *track); + +#endif \ No newline at end of file diff --git a/include/track.h b/include/track.h deleted file mode 100755 index e5b9dcd..0000000 --- a/include/track.h +++ /dev/null @@ -1,124 +0,0 @@ -/** \file - * File containing the Lavalink track related functions. - */ - -#ifndef TRACK_H -#define TRACK_H - -struct coglink_parsedTrackStruct { - jsmnf_pair pairs[1024]; -}; - -/** - * Searches for the input query in YouTube. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param song Input query, can be either a link or a video title. - * @param res Structure with the information of the request. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_searchSong(struct coglink_lavaInfo *lavaInfo, char *song, struct coglink_requestInformation *res); - -/** - * Frees the allocations generated while performing the function coglink_searchSong. - * @param res Structure with the information of the request. - */ -void coglink_searchSongCleanup(struct coglink_requestInformation *res); - -/** - * Decodes a single track. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param track Track string that will be decoded - * @param res Structure with the information of the request. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_decodeTrack(struct coglink_lavaInfo *lavaInfo, char *track, struct coglink_requestInformation *res); - -/** - * Initializes the structure that will be used to parse the response body of any function that used it. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param pStruct Structure that will be filled with the pairs. - * @param res Structure with the information of the request. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_initParseTrack(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res); - -/** - * Parses the response body of the function coglink_decodeTrack. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param pStruct Structure that is filled with the pairs. - * @param res Structure with the information of the request. - * @param parsedTrackStruct Structure with the parsed information. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseDecodeTrack(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, struct coglink_parsedTrack *parsedTrackStruct); - -/** - * Frees the allocations generated while performing the function coglink_decodeTrack and coglink_decodeTracks. - * @param res Structure with the information of the request. - */ -void coglink_decodeTrackCleanup(struct coglink_requestInformation *res); - -/** - * Decodes multiple tracks at a single time. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param trackArray Track string that will be decoded - * @param trackArrayLength Length of the array of tracks that will be decoded. - * @param res Structure with the information of the request. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_decodeTracks(struct coglink_lavaInfo *lavaInfo, char *trackArray, long trackArrayLength, struct coglink_requestInformation *res); - -/** - * Parses the response body of the function coglink_decodeTracks. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param pStruct Structure that is filled with the pairs. - * @param res Structure with the information of the request. - * @param songPos Position of the song that will be parsed. - * @param parsedTrackStruct Structure with the parsed information. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseDecodeTracks(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, char *songPos, struct coglink_parsedTrack *parsedTrackStruct); - -/** - * Parses the loadType returned by coglink_searchSong. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param pStruct Structure that is filled with the pairs. - * @param res Structure with the information of the request. - * @param loadTypeValue type of the LoadType parsed. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseLoadtype(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, int *loadTypeValue); - -/** - * Parses the track returned by coglink_searchSong. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param pStruct Structure that is filled with the pairs. - * @param res Structure with the information of the request. - * @param songPos Position of the track that will be parsed. - * @param parsedTrackStruct Structure that will be filled with the parsed information of the track. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseTrack(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, char *songPos, struct coglink_parsedTrack *parsedTrackStruct); - -/** - * Parses the playlist returned by coglink_searchSong. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param pStruct Structure that is filled with the pairs. - * @param res Structure with the information of the request. - * @param parsedPlaylistStruct Structure that will be filled with the parsed information of the playlist. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parsePlaylist(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, struct coglink_parsedPlaylist *parsedPlaylistStruct); - -/** - * Parses the error returned by coglink_searchSong. - * @param lavaInfo Structure with important informations of the Lavalink. - * @param pStruct Structure that is filled with the pairs. - * @param res Structure with the information of the request. - * @param parsedErrorStruct Structure that will be filled with the parsed information of the error. - * @returns COGLINK_SUCCESS / ERROR - */ -int coglink_parseError(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, struct coglink_parsedError *parsedErrorStruct); - -#endif - diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..f80d43c --- /dev/null +++ b/include/types.h @@ -0,0 +1,16 @@ +#ifndef COGLINK_TYPES_H +#define COGLINK_TYPES_H + +#define COGLINK_NODE_READY -5 +#define COGLINK_CURL_ERROR -4 +#define COGLINK_NOT_FOUND -3 +#define COGLINK_ALREADY_EXISTS -2 +#define COGLINk_FAILED -1 +#define COGLINK_SUCCESS 0 +#define COGLINK_PROCEED 1 + +#define bool _Bool +#define true 1 +#define false 0 + +#endif /* COGLINK_TYPES_H */ diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..adcb37d --- /dev/null +++ b/include/utils.h @@ -0,0 +1,62 @@ +#ifndef COGLINK_UTILS_H +#define COGLINK_UTILS_H + +#define COGLINK_SESSION_ID_LENGTH 16 + +#define FIND_FIELD(field, fieldName) \ + jsmnf_pair *field = jsmnf_find(pairs, json, fieldName, sizeof(fieldName) - 1); \ + \ + if (field == NULL) { \ + return NULL; \ + } + +#define FIND_FIELD_PATH(field, fieldName, pathSize) \ + jsmnf_pair *field = jsmnf_find_path(pairs, json, path, pathSize); \ + \ + if (field == NULL) { \ + return NULL; \ + } + +#define PAIR_TO_SIZET(pair, fieldName, outputName, size) \ + char fieldName[size]; \ + memcpy(fieldName, json + pair->v.pos, pair->v.len); \ + \ + outputName = strtoull(fieldName, NULL, 10); + +#ifdef COGLINK_DEBUG +// Only if there's a arg + #define FATAL(...) \ + log_fatal(__VA_ARGS__); \ + \ + exit(1); + + #define DEBUG(...) log_debug(__VA_ARGS__) + #define INFO(...) log_info(__VA_ARGS__) + #define WARN(...) log_warn(__VA_ARGS__) + #define ERROR(...) log_error(__VA_ARGS__) +#else + #define FATAL(...) exit(1); + #define DEBUG(...) + #define INFO(...) + #define WARN(...) + #define ERROR(...) +#endif + +#include "lavalink.h" + +struct coglink_request_params { + char *endpoint; + char *method; + char *body; + size_t body_length; + bool get_response; +}; + +struct coglink_response { + char *body; + size_t size; +}; + +int _coglink_perform_request(struct coglink_node *nodeInfo, struct coglink_request_params *req, struct coglink_response *res); + +#endif diff --git a/include/websocket.h b/include/websocket.h new file mode 100644 index 0000000..ce5a601 --- /dev/null +++ b/include/websocket.h @@ -0,0 +1,20 @@ +#ifndef COGLINK_LAVALINK_WEBSOCKET_H +#define COGLINK_LAVALINK_WEBSOCKET_H + +#include "lavalink.h" +#include "codecs.h" + +struct _coglink_websocket_data { + struct coglink_client *c_client; + size_t node_id; +}; + +struct coglink_voice_data { + char *token; + char *endpoint; + char *session_id; +}; + +int coglink_connect_nodes(struct coglink_client *c_client, struct discord *client, struct coglink_nodes *nodes); + +#endif \ No newline at end of file diff --git a/lib/codecs.c b/lib/codecs.c new file mode 100644 index 0000000..23ee18d --- /dev/null +++ b/lib/codecs.c @@ -0,0 +1,514 @@ +/* TODO: Use cog-chef (https://github.com/Cogmasters/cog-chef)*/ + +#include + +#include +#include + +#include "utils.h" + +#include "codecs.h" + +#define _coglink_parse_track(pairs, json) \ + struct coglink_track *track_info = malloc(sizeof(struct coglink_partial_track)); \ + track_info->info = malloc(sizeof(struct coglink_partial_track)); \ + \ + char *path[] = { "encoded", NULL }; \ + FIND_FIELD_PATH(encoded, "encoded", 1); \ + snprintf(track_info->encoded, sizeof(track_info->encoded), "%.*s", (int)encoded->v.len, json + encoded->v.pos); \ + \ + path[0] = "info"; \ + path[1] = "identifier"; \ + FIND_FIELD_PATH(identifier, "identifier", 2); \ + snprintf(track_info->info->identifier, sizeof(track_info->info->identifier), "%.*s", (int)identifier->v.len, json + identifier->v.pos); \ + \ + path[1] = "isSeekable"; \ + FIND_FIELD_PATH(isSeekable, "isSeekable", 2); \ + if (json[isSeekable->v.pos] == 't') track_info->info->isSeekable = true; \ + else track_info->info->isSeekable = false; \ + \ + path[1] = "author"; \ + FIND_FIELD_PATH(author, "author", 2); \ + snprintf(track_info->info->author, sizeof(track_info->info->author), "%.*s", (int)author->v.len, json + author->v.pos); \ + \ + path[1] = "length"; \ + FIND_FIELD_PATH(length, "length", 2); \ + PAIR_TO_SIZET(length, lengthStr, track_info->info->length, 16); \ + \ + path[1] = "isStream"; \ + FIND_FIELD_PATH(isStream, "isStream", 2); \ + if (json[isStream->v.pos] == 't') track_info->info->isStream = true; \ + else track_info->info->isStream = false; \ + \ + path[1] = "position"; \ + FIND_FIELD_PATH(position, "position", 2); \ + PAIR_TO_SIZET(position, positionStr, track_info->info->position, 16); \ + \ + path[1] = "title"; \ + FIND_FIELD_PATH(title, "title", 2); \ + snprintf(track_info->info->title, sizeof(track_info->info->title), "%.*s", (int)title->v.len, json + title->v.pos); \ + \ + path[1] = "uri"; \ + FIND_FIELD_PATH(uri, "uri", 2); \ + snprintf(track_info->info->uri, sizeof(track_info->info->uri), "%.*s", (int)uri->v.len, json + uri->v.pos); \ + \ + path[1] = "isrc"; \ + FIND_FIELD_PATH(isrc, "isrc", 2); \ + snprintf(track_info->info->isrc, sizeof(track_info->info->isrc), "%.*s", (int)isrc->v.len, json + isrc->v.pos); \ + \ + path[1] = "artworkUrl"; \ + FIND_FIELD_PATH(artworkUrl, "artworkUrl", 2); \ + snprintf(track_info->info->artworkUrl, sizeof(track_info->info->artworkUrl), "%.*s", (int)artworkUrl->v.len, json + artworkUrl->v.pos); \ + \ + path[1] = "sourceName"; \ + FIND_FIELD_PATH(sourceName, "sourceName", 2); \ + snprintf(track_info->info->sourceName, sizeof(track_info->info->sourceName), "%.*s", (int)sourceName->v.len, json + sourceName->v.pos); + +void *coglink_parse_websocket_data(int *event_type, const char *json, size_t length) { + jsmn_parser parser; + jsmntok_t tokens[64]; + + jsmn_init(&parser); + int r = jsmn_parse(&parser, json, length, tokens, sizeof(tokens)); + + if (r < 0) { + ERROR("[coglink:jsmn-find] Failed to parse JSON."); + + *event_type = COGLINK_PARSE_ERROR; + + return NULL; + } + + jsmnf_loader loader; + jsmnf_pair pairs[64]; + + jsmnf_init(&loader); + r = jsmnf_load(&loader, json, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); + + if (r < 0) { + FATAL("[coglink:jsmn-find] Failed to load jsmn-find."); + + *event_type = COGLINK_PARSE_ERROR; + + return NULL; + } + + jsmnf_pair *op = jsmnf_find(pairs, json, "op", sizeof("op") - 1); + if (!op) { + *event_type = COGLINK_PARSE_ERROR; + + return NULL; + } + + const char *Op = json + op->v.pos; + + switch(Op[0]) { + case 'r': { /* ready */ + jsmnf_pair *sessionId = jsmnf_find(pairs, json, "sessionId", sizeof("sessionId") - 1); + if (!sessionId) { + *event_type = COGLINK_PARSE_ERROR; + + return NULL; + } + + jsmnf_pair *resumed = jsmnf_find(pairs, json, "resumed", sizeof("resumed") - 1); + + struct coglink_ready_payload *ready = malloc(sizeof(struct coglink_ready_payload)); + + ready->session_id = malloc(sessionId->v.len + 1); + snprintf(ready->session_id, sessionId->v.len + 1, "%.*s", (int)sessionId->v.len, json + sessionId->v.pos); + + if (json[resumed->v.pos] == 't') ready->resumed = true; + else ready->resumed = false; + + *event_type = COGLINK_READY; + + return ready; + } + case 'e': { + FIND_FIELD(type, "type"); + FIND_FIELD(guildId, "guildId"); + + switch(json[type->v.pos + 7]) { + case 'a': { /* TrackStartEvent */ + struct coglink_track_start_payload *parsedTrack = malloc(sizeof(struct coglink_track_start_payload)); + + PAIR_TO_SIZET(guildId, guildIdStr, parsedTrack->guildId, 18); + + _coglink_parse_track(pairs, json); /* Defines track_info */ + + parsedTrack->track = track_info; + + *event_type = COGLINK_TRACK_START; + + return parsedTrack; + } + case 'd': { /* TrackEndEvent */ + FIND_FIELD(reason, "reason"); + + struct coglink_track_end_payload *parsedTrack = malloc(sizeof(struct coglink_track_end_payload)); + + PAIR_TO_SIZET(guildId, guildIdStr, parsedTrack->guildId, 18); + + _coglink_parse_track(pairs, json); /* Defines track_info */ + + parsedTrack->track = track_info; + + snprintf(parsedTrack->reason, sizeof(parsedTrack->reason), "%.*s", (int)reason->v.len, json + reason->v.pos); + + *event_type = COGLINK_TRACK_END; + + return parsedTrack; + } + // case 'c': { /* TrackExceptionEvent */ + // struct coglink_parsedTrack parsedTrack = _coglink_buildTrackStruct(c_info, pairs, json); + // if (parsedTrack.encoded[0] == '\0') return; + + // char *path[] = { "exception", "message" }; + // FIND_FIELD_PATH("message", sizeof("message") - 1, 2); + + // path[1] = "severity"; + // FIND_FIELD_PATH("severity", sizeof("severity") - 1, 2); + + // path[1] = "cause"; + // FIND_FIELD_PATH("cause", sizeof("cause") - 1, 2); + + // char Message[128], Severity[16], Cause[256]; + // snprintf(Message, sizeof(Message), "%.*s", (int)message->v.len, json + message->v.pos); + // snprintf(Severity, sizeof(Severity), "%.*s", (int)severity->v.len, json + severity->v.pos); + // snprintf(Cause, sizeof(Cause), "%.*s", (int)cause->v.len, json + cause->v.pos); + + // c_info->c_info->events->onTrackException(guildId, &parsedTrack, Message, Severity, Cause); + // break; + // } + // case 'u': { /* TrackStuckEvent */ + // struct coglink_parsedTrack parsedTrack = _coglink_buildTrackStruct(c_info, pairs, json); + // if (parsedTrack.encoded[0] == '\0') return; + + // jsmnf_pair *thresholdMs = FIND_FIELD("thresholdMs", sizeof("thresholdMs") - 1); + + // size_t ThresholdMs; + // PAIR_TO_SIZET(thresholdMs, "thresholdMs", "ThresholdMs", 16); + + // c_info->c_info->events->onTrackStuck(guildId, ThresholdMs, &parsedTrack); + // break; + // } + // case 't': { /* WebSocketClosedEvent */ + // if (!c_info->c_info->events->onWebSocketClosed) return; + + // jsmnf_pair *code = FIND_FIELD("code", sizeof("code") - 1); + // jsmnf_pair *reason = FIND_FIELD("reason", sizeof("reason") - 1); + // jsmnf_pair *byRemote = FIND_FIELD("byRemote", sizeof("byRemote") - 1); + + // char Code[16], Reason[128]; + // bool ByRemote; + + // snprintf(Code, sizeof(Code), "%.*s", (int)code->v.len, json + code->v.pos); + // snprintf(Reason, sizeof(Reason), "%.*s", (int)reason->v.len, json + reason->v.pos); + + // if (json[byRemote->v.pos] == 't') ByRemote = true; + // else ByRemote = false; + + // c_info->c_info->events->onWebSocketClosed(guildId, Code, Reason, ByRemote); + // break; + // } + // default: { + // if (c_info->c_info->events->onUnknownEvent) c_info->c_info->events->onUnknownEvent(guildId, Type, json); + + // break; + // } + } + + break; + } + case 's': { /* Stats */ + FIND_FIELD(players, "players"); + FIND_FIELD(playingPlayers, "playingPlayers"); + FIND_FIELD(uptime, "uptime"); + FIND_FIELD(memory, "memory"); + + char *path[] = { "memory", "free" }; + FIND_FIELD_PATH(lavaFree, "free", 2); + + path[1] = "used"; + FIND_FIELD_PATH(used, "used", 2); + + path[1] = "allocated"; + FIND_FIELD_PATH(allocated, "allocated", 2); + + path[1] = "reservable"; + FIND_FIELD_PATH(reservable, "reservable", 2); + + path[0] = "cpu"; + path[1] = "cores"; + FIND_FIELD_PATH(cores, "cores", 2); + + path[1] = "systemLoad"; + FIND_FIELD_PATH(systemLoad, "systemLoad", 2); + + path[1] = "lavalinkLoad"; + FIND_FIELD_PATH(lavalinkLoad, "lavalinkLoad", 2); + + path[0] = "frameStats"; + path[1] = "sent"; + FIND_FIELD_PATH(sent, "sent", 2); + + path[1] = "deficit"; + FIND_FIELD_PATH(deficit, "deficit", 2); + + path[1] = "nulled"; + FIND_FIELD_PATH(nulled, "nulled", 2); + + struct coglink_stats_payload *stats = malloc(sizeof(struct coglink_stats_payload)); + stats->memory = malloc(sizeof(struct coglink_stats_memory_payload)); + stats->cpu = malloc(sizeof(struct coglink_stats_cpu_payload)); + stats->frameStats = malloc(sizeof(struct coglink_stats_frame_stats_payload)); + + PAIR_TO_SIZET(players, playersStr, stats->players, 8); + PAIR_TO_SIZET(playingPlayers, playingPlayersStr, stats->playingPlayers, 16); + PAIR_TO_SIZET(uptime, uptimeStr, stats->uptime, 8); + PAIR_TO_SIZET(lavaFree, freeStr, stats->memory->free, 8); + PAIR_TO_SIZET(used, usedStr, stats->memory->used, 8); + PAIR_TO_SIZET(allocated, allocatedStr, stats->memory->allocated, 8); + PAIR_TO_SIZET(reservable, reservableStr, stats->memory->reservable, 8); + PAIR_TO_SIZET(cores, coresStr, stats->cpu->cores, 8); + PAIR_TO_SIZET(systemLoad, systemLoadStr, stats->cpu->systemLoad, 8); + PAIR_TO_SIZET(lavalinkLoad, lavalinkLoadStr, stats->cpu->lavalinkLoad, 8); + PAIR_TO_SIZET(sent, sentStr, stats->frameStats->sent, 8); + PAIR_TO_SIZET(deficit, deficitStr, stats->frameStats->deficit, 8); + PAIR_TO_SIZET(nulled, nulledStr, stats->frameStats->nulled, 8); + + DEBUG("[coglink:jsmn-find] Parsed error search json, results:\n> Players: %s\n> PlayingPlayers: %s\n> Uptime: %s\n> Free: %s\n> Used: %s\n> Allocated: %s\n> Reservable: %s\n> Cores: %s\n> SystemLoad: %s\n> LavalinkLoad: %s\n> Sent: %s\n> Nulled: %s\n> Deficit: %s", playersStr, playingPlayersStr, uptimeStr, freeStr, usedStr, allocatedStr, reservableStr, coresStr, systemLoadStr, lavalinkLoadStr, sentStr, nulledStr, deficitStr); + + *event_type = COGLINK_STATS; + + return stats; + } + case 'p': { /* PlayerUpdate */ + FIND_FIELD(guildId, "guildId"); + + char *path[] = { "state", "time" }; + FIND_FIELD_PATH(time, "time", 2); + + path[1] = "position"; + FIND_FIELD_PATH(position, "position", 2); + + path[1] = "connected"; + FIND_FIELD_PATH(connected, "connected", 2); + + path[1] = "ping"; + FIND_FIELD_PATH(ping, "ping", 2); + + struct coglink_player_update_payload *playerUpdate = malloc(sizeof(struct coglink_player_update_payload)); + playerUpdate->state = malloc(sizeof(struct coglink_player_state_payload)); + + PAIR_TO_SIZET(guildId, guildIdStr, playerUpdate->guildId, 18); + PAIR_TO_SIZET(time, timeStr, playerUpdate->state->time, 16); + PAIR_TO_SIZET(position, positionStr, playerUpdate->state->position, 16); + PAIR_TO_SIZET(ping, pingStr, playerUpdate->state->ping, 8); + if (json[connected->v.pos] == 't') playerUpdate->state->connected = true; + else playerUpdate->state->connected = false; + + *event_type = COGLINK_PLAYER_UPDATE; + + return playerUpdate; + } + // default: { + // if (c_info->c_info->events->onUnknownOp) c_info->c_info->events->onUnknownOp(Op, json); + // break; + // } + } + + return NULL; +} + +struct coglink_load_tracks_response *coglink_parse_load_tracks_response(const char *json, size_t length) { + jsmn_parser parser; + jsmntok_t tokens[64]; + + jsmn_init(&parser); + int r = jsmn_parse(&parser, json, length, tokens, sizeof(tokens)); + + if (r < 0) { + ERROR("[coglink:jsmn-find] Failed to parse JSON."); + + return NULL; + } + + jsmnf_loader loader; + jsmnf_pair pairs[64]; + + jsmnf_init(&loader); + r = jsmnf_load(&loader, json, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); + + if (r < 0) { + FATAL("[coglink:jsmn-find] Failed to load jsmn-find."); + + return NULL; + } + + jsmnf_pair *loadType = jsmnf_find(pairs, json, "loadType", sizeof("loadType") - 1); + if (!loadType) return NULL; + + switch (json[loadType->v.pos + 1]) { + // case 'r': { /* tRack */ + // struct coglink_tracks *tracks = malloc(sizeof(struct coglink_tracks)); + // tracks->array = malloc(sizeof(struct coglink_partial_track) * 1); + // tracks->size = 1; + + // _coglink_parse_track(pairs, json); /* Defines track_info */ + + // tracks->array[0] = *track_info->info; + + // struct coglink_load_tracks_response *response = malloc(sizeof(struct coglink_load_tracks_response)); + // response->type = COGLINK_LOAD_TYPE_TRACK; + // response->tracks = tracks; + + // return response; + // } + // case 'l': { /* pLaylist */ + // struct coglink_tracks *tracks = malloc(sizeof(struct coglink_tracks)); + // tracks->array = malloc(sizeof(struct coglink_partial_track) * 1); + // tracks->size = 1; + + // _coglink_parse_track(pairs, json); /* Defines track_info */ + + // tracks->array[0] = *track_info->info; + + // struct coglink_load_tracks_response *response = malloc(sizeof(struct coglink_load_tracks_response)); + // response->type = COGLINK_LOAD_TYPE_PLAYLIST; + // response->tracks = tracks; + + // return response; + // } + case 'e': { /* sEarch */ + struct coglink_tracks *tracks = malloc(sizeof(struct coglink_tracks)); + tracks->array = malloc(sizeof(struct coglink_partial_track) * 1); + tracks->size = 1; + + _coglink_parse_track(pairs, json); /* Defines track_info */ + + tracks->array[0] = *track_info->info; + + struct coglink_load_tracks_response *response = malloc(sizeof(struct coglink_load_tracks_response)); + response->type = COGLINK_LOAD_TYPE_SEARCH; + response->tracks = tracks; + + return response; + } + // case 'm': { /* eMpty */ + // struct coglink_load_tracks_response *response = malloc(sizeof(struct coglink_load_tracks_response)); + // response->type = COGLINK_LOAD_TYPE_EMPTY; + // response->tracks = NULL; + + // return response; + // } + // case 'r': { /* eRror */ + // struct coglink_load_tracks_response *response = malloc(sizeof(struct coglink_load_tracks_response)); + // response->type = COGLINK_LOAD_TYPE_ERROR; + // response->tracks = NULL; + + // return response; + // } + } + + return NULL; +} + +struct coglink_voice_state *coglink_parse_voice_state(const char *json, size_t length) { + jsmn_parser parser; + jsmntok_t tokens[128]; + + jsmn_init(&parser); + int r = jsmn_parse(&parser, json, length, tokens, sizeof(tokens)); + + if (r < 0) { + ERROR("[coglink:jsmn-find] Failed to parse JSON."); + + return NULL; + } + + jsmnf_loader loader; + jsmnf_pair pairs[128]; + + jsmnf_init(&loader); + r = jsmnf_load(&loader, json, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); + + if (r < 0) { + FATAL("[coglink:jsmn-find] Failed to load jsmn-find."); + + return NULL; + } + + jsmnf_pair *guild_id = jsmnf_find(pairs, json, "guild_id", sizeof("guild_id") - 1); + if (!guild_id) return NULL; + + jsmnf_pair *channel_id = jsmnf_find(pairs, json, "channel_id", sizeof("channel_id") - 1); + + jsmnf_pair *user_id = jsmnf_find(pairs, json, "user_id", sizeof("user_id") - 1); + if (!user_id) return NULL; + + jsmnf_pair *session_id = jsmnf_find(pairs, json, "session_id", sizeof("session_id") - 1); + if (!session_id) return NULL; + + struct coglink_voice_state *voiceState = malloc(sizeof(struct coglink_voice_state)); + + PAIR_TO_SIZET(guild_id, guild_id_str, voiceState->guild_id, 18); + PAIR_TO_SIZET(channel_id, channel_id_str, voiceState->channel_id, 18); + if (channel_id) { + PAIR_TO_SIZET(user_id, user_id_str, voiceState->user_id, 18); + } else { + voiceState->user_id = 0; + } + voiceState->session_id = malloc(session_id->v.len + 1); + snprintf(voiceState->session_id, session_id->v.len + 1, "%.*s", (int)session_id->v.len, json + session_id->v.pos); + + return voiceState; +} + +struct coglink_voice_server_update *coglink_parse_voice_server_update(const char *json, size_t length) { + jsmn_parser parser; + jsmntok_t tokens[128]; + + jsmn_init(&parser); + int r = jsmn_parse(&parser, json, length, tokens, sizeof(tokens)); + + if (r < 0) { + ERROR("[coglink:jsmn-find] Failed to parse JSON."); + + return NULL; + } + + jsmnf_loader loader; + jsmnf_pair pairs[128]; + + jsmnf_init(&loader); + r = jsmnf_load(&loader, json, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); + + if (r < 0) { + FATAL("[coglink:jsmn-find] Failed to load jsmn-find."); + + return NULL; + } + + jsmnf_pair *token = jsmnf_find(pairs, json, "token", sizeof("token") - 1); + if (!token) return NULL; + + jsmnf_pair *endpoint = jsmnf_find(pairs, json, "endpoint", sizeof("endpoint") - 1); + if (!endpoint) return NULL; + + jsmnf_pair *guild_id = jsmnf_find(pairs, json, "guild_id", sizeof("guild_id") - 1); + if (!guild_id) return NULL; + + struct coglink_voice_server_update *voiceServerUpdate = malloc(sizeof(struct coglink_voice_server_update)); + + voiceServerUpdate->token = malloc(token->v.len + 1); + snprintf(voiceServerUpdate->token, token->v.len + 1, "%.*s", (int)token->v.len, json + token->v.pos); + + voiceServerUpdate->endpoint = malloc(endpoint->v.len + 1); + snprintf(voiceServerUpdate->endpoint, endpoint->v.len + 1, "%.*s", (int)endpoint->v.len, json + endpoint->v.pos); + + PAIR_TO_SIZET(guild_id, guild_id_str, voiceServerUpdate->guild_id, 18); + + return voiceServerUpdate; +} \ No newline at end of file diff --git a/lib/information.c b/lib/information.c deleted file mode 100644 index 1700175..0000000 --- a/lib/information.c +++ /dev/null @@ -1,237 +0,0 @@ -#include - -#include -#include - -#include "lavalink-internal.h" -#include "lavalink.h" -#include "definitions.h" -#include "information.h" - -int coglink_getLavalinkVersion(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char **version) { - int node = _coglink_findPlayerNode(guildId); - - struct coglink_requestInformation res; - - int status = _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], &res, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_GET_REQ, - .path = "/version", - .pathLength = 8, - .getResponse = 1, - .useVPath = 0 - }); - if (status != COGLINK_SUCCESS) return status; - - *version = res.body; - - free(res.body); - - return COGLINK_SUCCESS; -} - -int coglink_getLavalinkInfo(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, struct coglink_requestInformation *res) { - int node = _coglink_findPlayerNode(guildId); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], res, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_GET_REQ, - .path = "/info", - .pathLength = 6, - .getResponse = 1 - }); -} - -int coglink_parseLavalinkInfo(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, struct coglink_lavalinkInfo *lavalinkInfoStruct) { - jsmn_parser parser; - jsmntok_t tokens[64]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, res->body, res->size, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to parse JSON."); - return COGLINK_JSMNF_ERROR_PARSE; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[jsmn-find] Successfully parsed JSON."); - - jsmnf_loader loader; - jsmnf_pair pairs[64]; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, res->body, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return COGLINK_JSMNF_ERROR_LOAD; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[coglink:jsmn-find] Successfully loaded jsmn-find."); - - char *path[] = { "version", "semver" }; - jsmnf_pair *semver = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, semver, "semver") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "major"; - jsmnf_pair *major = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, major, "major") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "minor"; - jsmnf_pair *minor = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, minor, "minor") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "patch"; - jsmnf_pair *patch = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, patch, "patch") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "preRelease"; - jsmnf_pair *preRelease = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, preRelease, "preRelease") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - jsmnf_pair *buildTime = jsmnf_find(pairs, res->body, "buildTime", sizeof("buildTime") - 1); - - path[0] = "git"; - path[1] = "branch"; - jsmnf_pair *branch = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, branch, "branch") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "commit"; - jsmnf_pair *commit = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, commit, "commit") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "commitTime"; - jsmnf_pair *commitTime = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, commitTime, "commitTime") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - jsmnf_pair *jvm = jsmnf_find(pairs, res->body, "jvm", sizeof("jvm") - 1); - if (_coglink_checkParse(lavaInfo, jvm, "jvm")) return COGLINK_JSMNF_ERROR_FIND; - - jsmnf_pair *lavaplayer = jsmnf_find(pairs, res->body, "lavaplayer", sizeof("lavaplayer") - 1); - if (_coglink_checkParse(lavaInfo, lavaplayer, "lavaplayer")) return COGLINK_JSMNF_ERROR_FIND; - - jsmnf_pair *sourceManagers = jsmnf_find(pairs, res->body, "sourceManagers", sizeof("sourceManagers") - 1); - if (_coglink_checkParse(lavaInfo, sourceManagers, "sourceManagers")) return COGLINK_JSMNF_ERROR_FIND; - - jsmnf_pair *filters = jsmnf_find(pairs, res->body, "filters", sizeof("filters") - 1); - if (_coglink_checkParse(lavaInfo, filters, "filters")) return COGLINK_JSMNF_ERROR_FIND; - - jsmnf_pair *plugins = jsmnf_find(pairs, res->body, "plugins", sizeof("plugins") - 1); - if (_coglink_checkParse(lavaInfo, plugins, "plugins")) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(lavalinkInfoStruct->version->semver, sizeof(lavalinkInfoStruct->version->semver), "%.*s", (int)semver->v.len, res->body + semver->v.pos); - snprintf(lavalinkInfoStruct->version->major, sizeof(lavalinkInfoStruct->version->major), "%.*s", (int)major->v.len, res->body + major->v.pos); - snprintf(lavalinkInfoStruct->version->minor, sizeof(lavalinkInfoStruct->version->minor), "%.*s", (int)minor->v.len, res->body + minor->v.pos); - snprintf(lavalinkInfoStruct->version->patch, sizeof(lavalinkInfoStruct->version->patch), "%.*s", (int)patch->v.len, res->body + patch->v.pos); - if (preRelease) snprintf(lavalinkInfoStruct->version->preRelease, sizeof(lavalinkInfoStruct->version->preRelease), "%.*s", (int)preRelease->v.len, res->body + preRelease->v.pos); - snprintf(lavalinkInfoStruct->buildTime, sizeof(lavalinkInfoStruct->buildTime), "%.*s", (int)buildTime->v.len, res->body + buildTime->v.pos); - snprintf(lavalinkInfoStruct->git->branch, sizeof(lavalinkInfoStruct->git->branch), "%.*s", (int)branch->v.len, res->body + branch->v.pos); - snprintf(lavalinkInfoStruct->git->commit, sizeof(lavalinkInfoStruct->git->commit), "%.*s", (int)commit->v.len, res->body + commit->v.pos); - snprintf(lavalinkInfoStruct->git->commitTime, sizeof(lavalinkInfoStruct->git->commitTime), "%.*s", (int)commitTime->v.len, res->body + commitTime->v.pos); - snprintf(lavalinkInfoStruct->jvm, sizeof(lavalinkInfoStruct->jvm), "%.*s", (int)jvm->v.len, res->body + jvm->v.pos); - snprintf(lavalinkInfoStruct->lavaplayer, sizeof(lavalinkInfoStruct->lavaplayer), "%.*s", (int)lavaplayer->v.len, res->body + lavaplayer->v.pos); - snprintf(lavalinkInfoStruct->sourceManagers, sizeof(lavalinkInfoStruct->sourceManagers), "%.*s", (int)sourceManagers->v.len, res->body + sourceManagers->v.pos); - snprintf(lavalinkInfoStruct->filters, sizeof(lavalinkInfoStruct->filters), "%.*s", (int)filters->v.len, res->body + filters->v.pos); - snprintf(lavalinkInfoStruct->plugins, sizeof(lavalinkInfoStruct->plugins), "%.*s", (int)plugins->v.len, res->body + plugins->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed error search json, results:\n> semver: %s\n> major: %s\n> minor: %s\n> patch: %s\n> preRelease: %s\n> buildTime: %s\n> branch: %s\n> commit: %s\n> commitTime: %s\n> jvm: %s\n> lavaplayer: %s\n> sourceManagers: %s\n> filters: %s\n> plugins: %s", lavalinkInfoStruct->version->semver, lavalinkInfoStruct->version->major, lavalinkInfoStruct->version->minor, lavalinkInfoStruct->version->patch, lavalinkInfoStruct->version->preRelease, lavalinkInfoStruct->buildTime, lavalinkInfoStruct->git->branch, lavalinkInfoStruct->git->commit, lavalinkInfoStruct->git->commitTime, lavalinkInfoStruct->jvm, lavalinkInfoStruct->lavaplayer, lavalinkInfoStruct->sourceManagers, lavalinkInfoStruct->filters, lavalinkInfoStruct->plugins); - - return COGLINK_SUCCESS; -} - -void coglink_getLavalinkInfoCleanup(struct coglink_requestInformation *res) { - free(res->body); -} - -int coglink_getLavalinkStats(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, struct coglink_requestInformation *res) { - int node = _coglink_findPlayerNode(guildId); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], res, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_GET_REQ, - .path = "/stats", - .pathLength = 6, - .getResponse = 1 - }); -} - -int coglink_parseLavalinkStats(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, struct coglink_lavalinkStats *lavalinkStatsStruct) { - jsmn_parser parser; - jsmntok_t tokens[32]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, res->body, res->size, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to parse JSON."); - return COGLINK_JSMNF_ERROR_PARSE; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[jsmn-find] Successfully parsed JSON."); - - jsmnf_loader loader; - jsmnf_pair pairs[32]; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, res->body, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return COGLINK_JSMNF_ERROR_LOAD; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[coglink:jsmn-find] Successfully loaded jsmn-find."); - - jsmnf_pair *players = jsmnf_find(pairs, res->body, "players", sizeof("players") - 1); - if (_coglink_checkParse(lavaInfo, players, "players") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - jsmnf_pair *playingPlayers = jsmnf_find(pairs, res->body, "playingPlayers", sizeof("playingPlayers") - 1); - if (_coglink_checkParse(lavaInfo, playingPlayers, "playingPlayers") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - jsmnf_pair *uptime = jsmnf_find(pairs, res->body, "uptime", sizeof("uptime") - 1); - if (_coglink_checkParse(lavaInfo, uptime, "uptime") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - char *path[] = { "memory", "free" }; - jsmnf_pair *lavaFree = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, lavaFree, "lavaFree") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "used"; - jsmnf_pair *used = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, used, "used") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "allocated"; - jsmnf_pair *allocated = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, allocated, "allocated") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "reservable"; - jsmnf_pair *reservable = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, reservable, "reservable") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[0] = "cpu"; - path[1] = "cores"; - jsmnf_pair *cores = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, cores, "cores") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "systemLoad"; - jsmnf_pair *systemLoad = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, systemLoad, "systemLoad") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "lavalinkLoad"; - jsmnf_pair *lavalinkLoad = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, lavalinkLoad, "lavalinkLoad") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(lavalinkStatsStruct->players, sizeof(lavalinkStatsStruct->players), "%.*s", (int)players->v.len, res->body + players->v.pos); - snprintf(lavalinkStatsStruct->playingPlayers, sizeof(lavalinkStatsStruct->playingPlayers), "%.*s", (int)playingPlayers->v.len, res->body + playingPlayers->v.pos); - snprintf(lavalinkStatsStruct->uptime, sizeof(lavalinkStatsStruct->uptime), "%.*s", (int)uptime->v.len, res->body + uptime->v.pos); - snprintf(lavalinkStatsStruct->memory->free, sizeof(lavalinkStatsStruct->memory->free), "%.*s", (int)lavaFree->v.len, res->body + lavaFree->v.pos); - snprintf(lavalinkStatsStruct->memory->used, sizeof(lavalinkStatsStruct->memory->used), "%.*s", (int)used->v.len, res->body + used->v.pos); - snprintf(lavalinkStatsStruct->memory->allocated, sizeof(lavalinkStatsStruct->memory->allocated), "%.*s", (int)allocated->v.len, res->body + allocated->v.pos); - snprintf(lavalinkStatsStruct->memory->reservable, sizeof(lavalinkStatsStruct->memory->reservable), "%.*s", (int)reservable->v.len, res->body + reservable->v.pos); - snprintf(lavalinkStatsStruct->cpu->cores, sizeof(lavalinkStatsStruct->cpu->cores), "%.*s", (int)cores->v.len, res->body + cores->v.pos); - snprintf(lavalinkStatsStruct->cpu->systemLoad, sizeof(lavalinkStatsStruct->cpu->systemLoad), "%.*s", (int)systemLoad->v.len, res->body + systemLoad->v.pos); - snprintf(lavalinkStatsStruct->cpu->lavalinkLoad, sizeof(lavalinkStatsStruct->cpu->lavalinkLoad), "%.*s", (int)lavalinkLoad->v.len, res->body + lavalinkLoad->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[coglink:jsmn-find] Parsed error search json, results:\n> Players: %s\n> PlayingPlayers: %s\n> Uptime: %s\n> Free: %s\n> Used: %s\n> Allocated: %s\n> Reservable: %s\n> Cores: %s\n> SystemLoad: %s\n> LavalinkLoad: %s", lavalinkStatsStruct->players, lavalinkStatsStruct->playingPlayers, lavalinkStatsStruct->uptime, lavalinkStatsStruct->memory->free, lavalinkStatsStruct->memory->used, lavalinkStatsStruct->memory->allocated, lavalinkStatsStruct->memory->reservable, lavalinkStatsStruct->cpu->cores, lavalinkStatsStruct->cpu->systemLoad, lavalinkStatsStruct->cpu->lavalinkLoad); - - return COGLINK_SUCCESS; -} - -void coglink_getLavalinkStatsCleanup(struct coglink_requestInformation *res) { - free(res->body); -} diff --git a/lib/lavalink-internal.c b/lib/lavalink-internal.c deleted file mode 100644 index bd26c46..0000000 --- a/lib/lavalink-internal.c +++ /dev/null @@ -1,275 +0,0 @@ -#include -#include - -#include -#include - -#include "lavalink-internal.h" -#include "lavalink.h" -#include "definitions.h" - -size_t __coglink_WriteMemoryCallback(void *data, size_t size, size_t nmemb, void *userp) { - size_t writeSize = size * nmemb; - struct coglink_requestInformation *mem = userp; - - char *ptr = realloc(mem->body, mem->size + writeSize + 1); - if (!ptr) { - perror("[SYSTEM] Not enough memory to realloc.\n"); - return 1; - } - - mem->body = ptr; - memcpy(&(mem->body[mem->size]), data, writeSize); - mem->size += writeSize; - mem->body[mem->size] = 0; - - return writeSize; -} - -size_t __coglink_WriteMemoryCallbackNoSave(void *data, size_t size, size_t nmemb, void *userp) { - (void) data; (void) size; (void) userp; - return nmemb; -} - -int __coglink_checkCurlCommand(struct coglink_lavaInfo *lavaInfo, CURL *curl, CURLcode cRes, char *pos, int additionalDebugging, int getResponse, struct coglink_requestInformation *res) { - if (cRes != CURLE_OK) { - if (lavaInfo->debugging->allDebugging || additionalDebugging || lavaInfo->debugging->curlErrorsDebugging) log_fatal("[coglink:libcurl] curl_easy_setopt [%s] failed: %s\n", pos, curl_easy_strerror(cRes)); - - curl_easy_cleanup(curl); - curl_global_cleanup(); - if (getResponse) free(res->body); - - return COGLINK_LIBCURL_FAILED_SETOPT; - } - return COGLINK_SUCCESS; -} - -int _coglink_performRequest(struct coglink_lavaInfo *lavaInfo, struct coglink_nodeInfo *nodeInfo, struct coglink_requestInformation *res, struct __coglink_requestConfig *config) { - if (!config->usedCURL) curl_global_init(CURL_GLOBAL_ALL); - if (!config->useVPath) config->useVPath = 1; - - CURL *curl = config->usedCURL; - if (!config->usedCURL) curl = curl_easy_init(); - - if (!curl) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->curlErrorsDebugging) log_fatal("[coglink:libcurl] Error while initializing libcurl."); - - curl_global_cleanup(); - - return COGLINK_LIBCURL_FAILED_INITIALIZE; - } - - char lavaURL[128 + 12 + 9 + config->pathLength]; - - if (config->useVPath) { - if (nodeInfo->node.ssl) snprintf(lavaURL, sizeof(lavaURL), "https://%s/v4%s", nodeInfo->node.hostname, config->path); - else snprintf(lavaURL, sizeof(lavaURL), "http://%s/v4%s", nodeInfo->node.hostname, config->path); - } else { - if (nodeInfo->node.ssl) snprintf(lavaURL, sizeof(lavaURL), "https://%s%s", nodeInfo->node.hostname, config->path); - else snprintf(lavaURL, sizeof(lavaURL), "http://%s%s", nodeInfo->node.hostname, config->path); - } - - CURLcode cRes = curl_easy_setopt(curl, CURLOPT_URL, lavaURL); - if (__coglink_checkCurlCommand(lavaInfo, curl, cRes, "1", config->additionalDebuggingError, config->getResponse, res) != COGLINK_SUCCESS) return COGLINK_LIBCURL_FAILED_SETOPT; - - struct curl_slist *chunk = NULL; - - if (nodeInfo->node.password) { - char AuthorizationH[256]; - snprintf(AuthorizationH, sizeof(AuthorizationH), "Authorization: %s", nodeInfo->node.password); - chunk = curl_slist_append(chunk, AuthorizationH); - - if (lavaInfo->debugging->allDebugging || config->additionalDebuggingSuccess || lavaInfo->debugging->curlSuccessDebugging) log_debug("[coglink:libcurl] Authorization header set."); - } - - if (config->body) { - chunk = curl_slist_append(chunk, "Content-Type: application/json"); - if (lavaInfo->debugging->allDebugging || config->additionalDebuggingSuccess || lavaInfo->debugging->curlSuccessDebugging) log_debug("[coglink:libcurl] Content-Type header set."); - } - - chunk = curl_slist_append(chunk, "Client-Name: Coglink"); - if (lavaInfo->debugging->allDebugging || config->additionalDebuggingSuccess || lavaInfo->debugging->curlSuccessDebugging) log_debug("[coglink:libcurl] Client-Name header set."); - - chunk = curl_slist_append(chunk, "User-Agent: libcurl"); - if (lavaInfo->debugging->allDebugging || config->additionalDebuggingSuccess || lavaInfo->debugging->curlSuccessDebugging) log_debug("[coglink:libcurl] User-Agent header set."); - - cRes = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); - if (__coglink_checkCurlCommand(lavaInfo, curl, cRes, "2", config->additionalDebuggingError, config->getResponse, res) != COGLINK_SUCCESS) { - curl_slist_free_all(chunk); - return COGLINK_LIBCURL_FAILED_SETOPT; - } - - if (config->requestType == __COGLINK_DELETE_REQ || config->requestType == __COGLINK_PATCH_REQ) { - if (config->requestType == __COGLINK_DELETE_REQ) cRes = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - else if (config->requestType == __COGLINK_PATCH_REQ) cRes = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); - - if (__coglink_checkCurlCommand(lavaInfo, curl, cRes, "3", config->additionalDebuggingError, config->getResponse, res) != COGLINK_SUCCESS) { - curl_slist_free_all(chunk); - return COGLINK_LIBCURL_FAILED_SETOPT; - } - } - - if (config->getResponse) { - (*res).body = malloc(1); - (*res).size = 0; - - cRes = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, __coglink_WriteMemoryCallback); - - if (__coglink_checkCurlCommand(lavaInfo, curl, cRes, "4", config->additionalDebuggingError, config->getResponse, res) != COGLINK_SUCCESS) { - curl_slist_free_all(chunk); - return COGLINK_LIBCURL_FAILED_SETOPT; - } - - cRes = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&(*res)); - if (__coglink_checkCurlCommand(lavaInfo, curl, cRes, "5", config->additionalDebuggingError, config->getResponse, res) != COGLINK_SUCCESS) { - curl_slist_free_all(chunk); - return COGLINK_LIBCURL_FAILED_SETOPT; - } - } else { - cRes = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, __coglink_WriteMemoryCallbackNoSave); - - if (__coglink_checkCurlCommand(lavaInfo, curl, cRes, "6", config->additionalDebuggingError, config->getResponse, res) != COGLINK_SUCCESS) { - curl_slist_free_all(chunk); - return COGLINK_LIBCURL_FAILED_SETOPT; - } - } - - if (config->body) { - cRes = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->body); - if (__coglink_checkCurlCommand(lavaInfo, curl, cRes, "7", config->additionalDebuggingError, config->getResponse, res) != COGLINK_SUCCESS) { - curl_slist_free_all(chunk); - return COGLINK_LIBCURL_FAILED_SETOPT; - } - - cRes = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->bodySize); - if (__coglink_checkCurlCommand(lavaInfo, curl, cRes, "8", config->additionalDebuggingError, config->getResponse, res) != COGLINK_SUCCESS) { - curl_slist_free_all(chunk); - return COGLINK_LIBCURL_FAILED_SETOPT; - } - } - - cRes = curl_easy_perform(curl); - if (cRes != CURLE_OK) { - if (lavaInfo->debugging->allDebugging || config->additionalDebuggingError || lavaInfo->debugging->curlErrorsDebugging) log_fatal("[coglink:libcurl] curl_easy_perform failed: %s", curl_easy_strerror(cRes)); - - curl_easy_cleanup(curl); - curl_slist_free_all(chunk); - curl_global_cleanup(); - if (config->getResponse) free((*res).body); - - return COGLINK_LIBCURL_FAILED_PERFORM; - } - - curl_easy_cleanup(curl); - curl_slist_free_all(chunk); - curl_global_cleanup(); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->curlSuccessDebugging) log_debug("[coglink:libcurl] Performed request successfully."); - - return COGLINK_SUCCESS; -} - -int _coglink_checkParse(struct coglink_lavaInfo *lavaInfo, jsmnf_pair *field, char *fieldName) { - if (!field) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->checkParseErrorsDebugging) log_error("[coglink:jsmn-find] Failed to find %s field.", fieldName); - return COGLINK_JSMNF_ERROR_FIND; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->checkParseSuccessDebugging) log_debug("[coglink:jsmn-find] Successfully found %s field.", fieldName); - return COGLINK_PROCEED; -} - -int _coglink_selectBestNode(struct coglink_lavaInfo *lavaInfo) { - int i = -1; - int bestStatsNode = 0, bestStats = -1; - while (i++ <= lavaInfo->nodeCount - 1) { - int systemLoad = lavaInfo->nodes[i].stats.systemLoad ? lavaInfo->nodes[i].stats.systemLoad : 0; - int cores = lavaInfo->nodes[i].stats.cores ? lavaInfo->nodes[i].stats.cores : 0; - - int stats = (systemLoad / cores) * 100; - - if (stats < bestStats) { - bestStats = stats; - bestStatsNode = i; - } - } - - return bestStatsNode; -} - -int _coglink_findNode(struct coglink_lavaInfo *lavaInfo, char *hostname) { - for (int i = 0; i < lavaInfo->nodeCount; i++) { - if (strcmp(lavaInfo->nodes[i].node.hostname, hostname) == 0) return i; - } - return -1; -} - -int _coglink_IOPoller(struct io_poller *io, CURLM *multi, void *data) { - (void) io; (void) multi; - struct coglink_nodeInfo *nodeInfo = data; - return !ws_multi_socket_run(nodeInfo->ws, &nodeInfo->tstamp) ? COGLINK_WAIT : COGLINK_SUCCESS; -} - -struct coglink_parsedTrack _coglink_buildTrackStruct(struct coglink_lavaInfo *lavaInfo, jsmnf_pair pairs[], const char *text) { - char *path[] = { "track", "encoded", NULL }; - jsmnf_pair *encoded = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, encoded, "encoded") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - path[1] = "info"; - path[2] = "identifier"; - jsmnf_pair *identifier = jsmnf_find_path(pairs, text, path, 3); - if (_coglink_checkParse(lavaInfo, identifier, "identifier") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - path[2] = "isSeekable"; - jsmnf_pair *isSeekable = jsmnf_find_path(pairs, text, path, 3); - if (_coglink_checkParse(lavaInfo, isSeekable, "isSeekable") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - path[2] = "author"; - jsmnf_pair *author = jsmnf_find_path(pairs, text, path, 3); - if (_coglink_checkParse(lavaInfo, author, "author") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - path[2] = "length"; - jsmnf_pair *length = jsmnf_find_path(pairs, text, path, 3); - if (_coglink_checkParse(lavaInfo, length, "length") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - path[2] = "isStream"; - jsmnf_pair *isStream = jsmnf_find_path(pairs, text, path, 3); - if (_coglink_checkParse(lavaInfo, isStream, "isStream") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - path[2] = "position"; - jsmnf_pair *position = jsmnf_find_path(pairs, text, path, 3); - if (_coglink_checkParse(lavaInfo, position, "position") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - path[2] = "title"; - jsmnf_pair *title = jsmnf_find_path(pairs, text, path, 3); - if (_coglink_checkParse(lavaInfo, title, "title") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - path[2] = "uri"; - jsmnf_pair *uri = jsmnf_find_path(pairs, text, path, 3); - - path[2] = "artworkUrl"; - jsmnf_pair *artworkUrl = jsmnf_find_path(pairs, text, path, 3); - - path[2] = "isrc"; - jsmnf_pair *isrc = jsmnf_find_path(pairs, text, path, 3); - - path[2] = "sourceName"; - jsmnf_pair *sourceName = jsmnf_find_path(pairs, text, path, 3); - if (_coglink_checkParse(lavaInfo, sourceName, "sourceName") != COGLINK_PROCEED) return (struct coglink_parsedTrack) { 0 }; - - struct coglink_parsedTrack parsedTrack; - - snprintf(parsedTrack.encoded, sizeof(parsedTrack.encoded), "%.*s", (int)encoded->v.len, text + encoded->v.pos); - snprintf(parsedTrack.identifier, sizeof(parsedTrack.identifier), "%.*s", (int)identifier->v.len, text + identifier->v.pos); - snprintf(parsedTrack.isSeekable, sizeof(parsedTrack.isSeekable), "%.*s", (int)isSeekable->v.len, text + isSeekable->v.pos); - snprintf(parsedTrack.author, sizeof(parsedTrack.author), "%.*s", (int)author->v.len, text + author->v.pos); - snprintf(parsedTrack.length, sizeof(parsedTrack.length), "%.*s", (int)length->v.len, text + length->v.pos); - snprintf(parsedTrack.isStream, sizeof(parsedTrack.isStream), "%.*s", (int)isStream->v.len, text + isStream->v.pos); - snprintf(parsedTrack.position, sizeof(parsedTrack.position), "%.*s", (int)position->v.len, text + position->v.pos); - snprintf(parsedTrack.title, sizeof(parsedTrack.title), "%.*s", (int)title->v.len, text + title->v.pos); - if (_coglink_checkParse(lavaInfo, uri, "uri") == COGLINK_PROCEED) snprintf(parsedTrack.uri, sizeof(parsedTrack.uri), "%.*s", (int)uri->v.len, text + uri->v.pos); - if (_coglink_checkParse(lavaInfo, artworkUrl, "artworkUrl") == COGLINK_PROCEED) snprintf(parsedTrack.artworkUrl, sizeof(parsedTrack.artworkUrl), "%.*s", (int)artworkUrl->v.len, text + artworkUrl->v.pos); - if (_coglink_checkParse(lavaInfo, isrc, "isrc") == COGLINK_PROCEED) snprintf(parsedTrack.isrc, sizeof(parsedTrack.isrc), "%.*s", (int)isrc->v.len, text + isrc->v.pos); - snprintf(parsedTrack.sourceName, sizeof(parsedTrack.sourceName), "%.*s", (int)sourceName->v.len, text + sourceName->v.pos); - - return parsedTrack; -} diff --git a/lib/lavalink.c b/lib/lavalink.c deleted file mode 100644 index d092c07..0000000 --- a/lib/lavalink.c +++ /dev/null @@ -1,876 +0,0 @@ -#include -#include -#include - -#include -#include - -#include -#include - -#include "lavalink.h" -#include "lavalink-internal.h" -#include "definitions.h" -#include "player.h" -#include "tablec.h" - -struct tablec_ht coglink_hashtable; - -void _coglink_createPlayer(u64snowflake guildId, int node) { - char key[32]; - snprintf(key, sizeof(key), "player:%" PRIu64 "", guildId); - - void *data; - memcpy(&data, &node, sizeof(node)); - - tablec_set(&coglink_hashtable, key, data); -} - -int _coglink_findPlayerNode(u64snowflake guildId) { - char key[32]; - snprintf(key, sizeof(key), "player:%" PRIu64 "", guildId); - - struct tablec_bucket *value = tablec_get(&coglink_hashtable, key); - - if (!value) return -1; - - int node; - memcpy(&node, value->value, sizeof(node)); - - return node; -} - -void onCloseEvent(void *data, struct websockets *ws, struct ws_info *info, enum ws_close_reason wscode, const char *reason, size_t length) { - (void) ws; - - struct coglink_lavaInfo *lavaInfo = data; - - if (lavaInfo->plugins && lavaInfo->plugins->events->onLavalinkClose[0]) { - if (lavaInfo->plugins->security->allowReadWebsocket) { - for (int i = 0;i <= lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onLavalinkClose[i]) break; - - if (lavaInfo->plugins->events->onLavalinkClose[i](lavaInfo, info, wscode, reason, length) != COGLINK_PROCEED) return; - } - } else { - struct coglink_lavaInfo *lavaInfoPlugin = lavaInfo; - lavaInfoPlugin->nodes = NULL; - - for (int i = 0;i <= lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onLavalinkClose[i]) break; - - if (lavaInfo->plugins->events->onLavalinkClose[i](lavaInfoPlugin, info, wscode, reason, length) != COGLINK_PROCEED) return; - } - } - } - - if (lavaInfo->events->onClose) lavaInfo->events->onClose(wscode, reason); -} - -void onTextEvent(void *data, struct websockets *ws, struct ws_info *info, const char *text, size_t length) { - (void) ws; (void) info; - struct coglink_lavaInfo *lavaInfo = data; - - int node = lavaInfo->nodeId; - - if (lavaInfo->plugins && lavaInfo->plugins->events->onLavalinkPacketReceived[0]) { - if (lavaInfo->plugins->security->allowReadWebsocket) { - for (int i = 0;i <= lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onLavalinkPacketReceived[i]) break; - - if (lavaInfo->plugins->events->onLavalinkPacketReceived[i](lavaInfo, text, length) != COGLINK_PROCEED) return; - } - } else { - struct coglink_lavaInfo *lavaInfoPlugin = lavaInfo; - lavaInfoPlugin->nodes = NULL; - - for (int i = 0;i <= lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onLavalinkPacketReceived[i]) break; - - if (lavaInfo->plugins->events->onLavalinkPacketReceived[i](lavaInfoPlugin, text, length) != COGLINK_PROCEED) return; - } - } - } - - if (lavaInfo->events->onRaw && lavaInfo->events->onRaw(lavaInfo, text, length) != COGLINK_PROCEED) return; - - jsmn_parser parser; - jsmntok_t tokens[64]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, text, length, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to parse JSON."); - return; - } - - jsmnf_loader loader; - jsmnf_pair pairs[64]; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, text, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return; - } - - jsmnf_pair *op = jsmnf_find(pairs, text, "op", sizeof("op") - 1); - if (_coglink_checkParse(lavaInfo, op, "op") != COGLINK_PROCEED) return; - - char Op[16]; - - snprintf(Op, sizeof(Op), "%.*s", (int)op->v.len, text + op->v.pos); - - switch(Op[0]) { - case 'r': { /* ready */ - jsmnf_pair *sessionId = jsmnf_find(pairs, text, "sessionId", sizeof("sessionId") - 1); - if (_coglink_checkParse(lavaInfo, sessionId, "sessionId") != COGLINK_PROCEED) return; - - snprintf(lavaInfo->nodes[node].sessionId, COGLINK_LAVALINK_SESSIONID_LENGTH, "%.*s", (int)sessionId->v.len, text + sessionId->v.pos); - - if (lavaInfo->events->onConnect) lavaInfo->events->onConnect(lavaInfo->nodes[node].sessionId); - break; - } - case 'e': { - jsmnf_pair *type = jsmnf_find(pairs, text, "type", sizeof("type") - 1); - if (_coglink_checkParse(lavaInfo, type, "type") != COGLINK_PROCEED) return; - - jsmnf_pair *jsmnf_guildId = jsmnf_find(pairs, text, "guildId", sizeof("guildId") - 1); - if (_coglink_checkParse(lavaInfo, jsmnf_guildId, "guildId") != COGLINK_PROCEED) return; - - char Type[32], guildId[COGLINK_GUILD_ID_LENGTH]; - - snprintf(Type, sizeof(Type), "%.*s", (int)type->v.len, text + type->v.pos); - snprintf(guildId, sizeof(guildId), "%.*s", (int)jsmnf_guildId->v.len, text + jsmnf_guildId->v.pos); - - switch(Type[7]) { - case 'a': { /* TrackStartEvent */ - if (!lavaInfo->events->onTrackStart) return; - - struct coglink_parsedTrack parsedTrack = _coglink_buildTrackStruct(lavaInfo, pairs, text); - if (parsedTrack.encoded[0] == '\0') return; - - lavaInfo->events->onTrackStart(guildId, &parsedTrack); - break; - } - case 'd': { /* TrackEndEvent */ - if (!lavaInfo->events->onTrackEnd) return; - - jsmnf_pair *reason = jsmnf_find(pairs, text, "reason", sizeof("reason") - 1); - if (_coglink_checkParse(lavaInfo, reason, "reason") != COGLINK_PROCEED) return; - - struct coglink_parsedTrack parsedTrack = _coglink_buildTrackStruct(lavaInfo, pairs, text); - if (parsedTrack.encoded[0] == '\0') return; - - char Reason[16]; - - snprintf(Reason, sizeof(Reason), "%.*s", (int)reason->v.len, text + reason->v.pos); - - lavaInfo->events->onTrackEnd(guildId, &parsedTrack, Reason); - break; - } - case 'c': { /* TrackExceptionEvent */ - if (!lavaInfo->events->onTrackException) return; - - struct coglink_parsedTrack parsedTrack = _coglink_buildTrackStruct(lavaInfo, pairs, text); - if (parsedTrack.encoded[0] == '\0') return; - - char *path[] = { "exception", "message" }; - jsmnf_pair *message = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, message, "message") != COGLINK_PROCEED) return; - - path[1] = "severity"; - jsmnf_pair *severity = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, severity, "severity") != COGLINK_PROCEED) return; - - path[1] = "cause"; - jsmnf_pair *cause = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, cause, "cause") != COGLINK_PROCEED) return; - - char Message[128], Severity[16], Cause[256]; - - snprintf(Message, sizeof(Message), "%.*s", (int)message->v.len, text + message->v.pos); - snprintf(Severity, sizeof(Severity), "%.*s", (int)severity->v.len, text + severity->v.pos); - snprintf(Cause, sizeof(Cause), "%.*s", (int)cause->v.len, text + cause->v.pos); - - lavaInfo->events->onTrackException(guildId, &parsedTrack, Message, Severity, Cause); - break; - } - case 'u': { /* TrackStuckEvent */ - if (!lavaInfo->events->onTrackStuck) return; - - struct coglink_parsedTrack parsedTrack = _coglink_buildTrackStruct(lavaInfo, pairs, text); - if (parsedTrack.encoded[0] == '\0') return; - - jsmnf_pair *thresholdMs = jsmnf_find(pairs, text, "thresholdMs", sizeof("thresholdMs") - 1); - if (_coglink_checkParse(lavaInfo, thresholdMs, "thresholdMs") != COGLINK_PROCEED) return; - - char ThresholdMs[16]; - - snprintf(ThresholdMs, sizeof(ThresholdMs), "%.*s", (int)thresholdMs->v.len, text + thresholdMs->v.pos); - - lavaInfo->events->onTrackStuck(guildId, ThresholdMs, &parsedTrack); - break; - } - case 't': { /* WebSocketClosedEvent */ - if (!lavaInfo->events->onWebSocketClosed) return; - - jsmnf_pair *code = jsmnf_find(pairs, text, "code", sizeof("code") - 1); - if (_coglink_checkParse(lavaInfo, code, "code") != COGLINK_PROCEED) return; - - jsmnf_pair *reason = jsmnf_find(pairs, text, "reason", sizeof("reason") - 1); - if (_coglink_checkParse(lavaInfo, reason, "reason") != COGLINK_PROCEED) return; - - jsmnf_pair *byRemote = jsmnf_find(pairs, text, "byRemote", sizeof("byRemote") - 1); - if (_coglink_checkParse(lavaInfo, byRemote, "byRemote") != COGLINK_PROCEED) return; - - char Code[16], Reason[128], ByRemote[COGLINK_TRUE_FALSE_LENGTH]; - - snprintf(Code, sizeof(Code), "%.*s", (int)code->v.len, text + code->v.pos); - snprintf(Reason, sizeof(Reason), "%.*s", (int)reason->v.len, text + reason->v.pos); - snprintf(ByRemote, sizeof(ByRemote), "%.*s", (int)byRemote->v.len, text + byRemote->v.pos); - - lavaInfo->events->onWebSocketClosed(guildId, Code, Reason, (ByRemote[0] == 't' ? 1 : 0)); - break; - } - default: { - if (lavaInfo->events->onUnknownEvent) lavaInfo->events->onUnknownEvent(guildId, Type, text); - break; - } - } - break; - } - case 's': { /* Stats */ - if (!lavaInfo->events->onStats) return; - - jsmnf_pair *players = jsmnf_find(pairs, text, "players", sizeof("players") - 1); - if (_coglink_checkParse(lavaInfo, players, "players") != COGLINK_PROCEED) return; - - jsmnf_pair *playingPlayers = jsmnf_find(pairs, text, "playingPlayers", sizeof("playingPlayers") - 1); - if (_coglink_checkParse(lavaInfo, playingPlayers, "playingPlayers") != COGLINK_PROCEED) return; - - jsmnf_pair *uptime = jsmnf_find(pairs, text, "uptime", sizeof("uptime") - 1); - if (_coglink_checkParse(lavaInfo, uptime, "uptime") != COGLINK_PROCEED) return; - - char *path[] = { "memory", "free" }; - jsmnf_pair *lavaFree = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, lavaFree, "lavaFree") != COGLINK_PROCEED) return; - - path[1] = "used"; - jsmnf_pair *used = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, used, "used") != COGLINK_PROCEED) return; - - path[1] = "allocated"; - jsmnf_pair *allocated = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, allocated, "allocated") != COGLINK_PROCEED) return; - - path[1] = "reservable"; - jsmnf_pair *reservable = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, reservable, "reservable") != COGLINK_PROCEED) return; - - path[0] = "cpu"; - path[1] = "cores"; - jsmnf_pair *cores = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, cores, "cores") != COGLINK_PROCEED) return; - - path[1] = "systemLoad"; - jsmnf_pair *systemLoad = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, systemLoad, "systemLoad") != COGLINK_PROCEED) return; - - path[1] = "lavalinkLoad"; - jsmnf_pair *lavalinkLoad = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, lavalinkLoad, "lavalinkLoad") != COGLINK_PROCEED) return; - - struct coglink_lavalinkStats *lavalinkStatsStruct = &(struct coglink_lavalinkStats) { - .players = "", - .playingPlayers = "", - .uptime = "", - .memory = &(struct coglink_lavalinkStatsMemory) { - .free = "", - .used = "", - .allocated = "", - .reservable = "" - }, - .cpu = &(struct coglink_lavalinkStatsCPU) { - .cores = "", - .systemLoad = "", - .lavalinkLoad = "" - }, - .frameStats = &(struct coglink_lavalinkStatsFrameStats) { - .sent = "", - .deficit = "", - .nulled = "" - } - }; - - path[0] = "frameStats"; - path[1] = "sent"; - jsmnf_pair *sent = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, sent, "sent") == COGLINK_PROCEED) { - snprintf(lavalinkStatsStruct->frameStats->sent, sizeof(lavalinkStatsStruct->frameStats->sent), "%.*s", (int)sent->v.len, text + sent->v.pos); - - path[1] = "deficit"; - jsmnf_pair *deficit = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, deficit, "deficit") != COGLINK_PROCEED) return; - - snprintf(lavalinkStatsStruct->frameStats->deficit, sizeof(lavalinkStatsStruct->frameStats->deficit), "%.*s", (int)deficit->v.len, text + deficit->v.pos); - - path[1] = "nulled"; - jsmnf_pair *nulled = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, nulled, "nulled") != COGLINK_PROCEED) return; - - snprintf(lavalinkStatsStruct->frameStats->nulled, sizeof(lavalinkStatsStruct->frameStats->nulled), "%.*s", (int)nulled->v.len, text + nulled->v.pos); - } - - snprintf(lavalinkStatsStruct->players, sizeof(lavalinkStatsStruct->players), "%.*s", (int)players->v.len, text + players->v.pos); - snprintf(lavalinkStatsStruct->playingPlayers, sizeof(lavalinkStatsStruct->playingPlayers), "%.*s", (int)playingPlayers->v.len, text + playingPlayers->v.pos); - snprintf(lavalinkStatsStruct->uptime, sizeof(lavalinkStatsStruct->uptime), "%.*s", (int)uptime->v.len, text + uptime->v.pos); - snprintf(lavalinkStatsStruct->memory->free, sizeof(lavalinkStatsStruct->memory->free), "%.*s", (int)lavaFree->v.len, text + lavaFree->v.pos); - snprintf(lavalinkStatsStruct->memory->used, sizeof(lavalinkStatsStruct->memory->used), "%.*s", (int)used->v.len, text + used->v.pos); - snprintf(lavalinkStatsStruct->memory->allocated, sizeof(lavalinkStatsStruct->memory->allocated), "%.*s", (int)allocated->v.len, text + allocated->v.pos); - snprintf(lavalinkStatsStruct->memory->reservable, sizeof(lavalinkStatsStruct->memory->reservable), "%.*s", (int)reservable->v.len, text + reservable->v.pos); - snprintf(lavalinkStatsStruct->cpu->cores, sizeof(lavalinkStatsStruct->cpu->cores), "%.*s", (int)cores->v.len, text + cores->v.pos); - snprintf(lavalinkStatsStruct->cpu->systemLoad, sizeof(lavalinkStatsStruct->cpu->systemLoad), "%.*s", (int)systemLoad->v.len, text + systemLoad->v.pos); - snprintf(lavalinkStatsStruct->cpu->lavalinkLoad, sizeof(lavalinkStatsStruct->cpu->lavalinkLoad), "%.*s", (int)lavalinkLoad->v.len, text + lavalinkLoad->v.pos); - - struct _coglink_nodeStats nodeStats = { - .cores = atoi(lavalinkStatsStruct->cpu->cores), - .systemLoad = atof(lavalinkStatsStruct->cpu->systemLoad) - }; - - lavaInfo->nodes[node].stats = nodeStats; - - if (sent && (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging)) log_debug("[coglink:jsmn-find] Parsed error search json, results:\n> Players: %s\n> PlayingPlayers: %s\n> Uptime: %s\n> Free: %s\n> Used: %s\n> Allocated: %s\n> Reservable: %s\n> Cores: %s\n> SystemLoad: %s\n> LavalinkLoad: %s\n> Sent: %s\n> Nulled: %s\n> Deficit: %s", lavalinkStatsStruct->players, lavalinkStatsStruct->playingPlayers, lavalinkStatsStruct->uptime, lavalinkStatsStruct->memory->free, lavalinkStatsStruct->memory->used, lavalinkStatsStruct->memory->allocated, lavalinkStatsStruct->memory->reservable, lavalinkStatsStruct->cpu->cores, lavalinkStatsStruct->cpu->systemLoad, lavalinkStatsStruct->cpu->lavalinkLoad, lavalinkStatsStruct->frameStats->sent, lavalinkStatsStruct->frameStats->nulled, lavalinkStatsStruct->frameStats->deficit); - else if (!sent && (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging)) log_debug("[coglink:jsmn-find] Parsed error search json, results:\n> Players: %s\n> PlayingPlayers: %s\n> Uptime: %s\n> Free: %s\n> Used: %s\n> Allocated: %s\n> Reservable: %s\n> Cores: %s\n> SystemLoad: %s\n> LavalinkLoad: %s", lavalinkStatsStruct->players, lavalinkStatsStruct->playingPlayers, lavalinkStatsStruct->uptime, lavalinkStatsStruct->memory->free, lavalinkStatsStruct->memory->used, lavalinkStatsStruct->memory->allocated, lavalinkStatsStruct->memory->reservable, lavalinkStatsStruct->cpu->cores, lavalinkStatsStruct->cpu->systemLoad, lavalinkStatsStruct->cpu->lavalinkLoad); - - lavaInfo->events->onStats(lavalinkStatsStruct); - break; - } - case 'p': { /* PlayerUpdate */ - if (!lavaInfo->events->onPlayerUpdate) return; - - jsmnf_pair *jsmnf_guildId = jsmnf_find(pairs, text, "guildId", sizeof("guildId") - 1); - if (_coglink_checkParse(lavaInfo, jsmnf_guildId, "guildId") != COGLINK_PROCEED) return; - - char *path[] = { "state", "time" }; - jsmnf_pair *time = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, time, "time") != COGLINK_PROCEED) return; - - path[1] = "position"; - jsmnf_pair *position = jsmnf_find_path(pairs, text, path, 2); - - path[1] = "connected"; - jsmnf_pair *connected = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, connected, "connected") != COGLINK_PROCEED) return; - - path[1] = "ping"; - jsmnf_pair *ping = jsmnf_find_path(pairs, text, path, 2); - if (_coglink_checkParse(lavaInfo, ping, "ping") != COGLINK_PROCEED) return; - - char guildId[COGLINK_GUILD_ID_LENGTH], Time[16], Position[16], Connected[COGLINK_TRUE_FALSE_LENGTH], Ping[8]; - - snprintf(guildId, sizeof(guildId), "%.*s", (int)jsmnf_guildId->v.len, text + jsmnf_guildId->v.pos); - snprintf(Time, sizeof(Time), "%.*s", (int)time->v.len, text + time->v.pos); - snprintf(Position, sizeof(Position), "%.*s", (int)position->v.len, text + position->v.pos); - snprintf(Ping, sizeof(Ping), "%.*s", (int)ping->v.len, text + ping->v.pos); - snprintf(Connected, sizeof(Connected), "%.*s", (int)connected->v.len, text + connected->v.pos); - - lavaInfo->events->onPlayerUpdate(guildId, Time, Position, (Connected[0] == 't' ? 1 : 0), Ping); - break; - } - default: { - if (lavaInfo->events->onUnknownOp) lavaInfo->events->onUnknownOp(Op, text); - break; - } - } -} - -enum discord_event_scheduler __coglink_handleScheduler(struct discord *client, const char data[], size_t length, enum discord_gateway_events event) { - struct coglink_lavaInfo *lavaInfo = discord_get_data(client); - - if (lavaInfo->plugins && lavaInfo->plugins->events->onCoglinkScheduler[0]) { - struct discord *clientPlugin = client; - if (lavaInfo->plugins->security->allowReadBotToken) clientPlugin->token = NULL; - if (lavaInfo->plugins->security->allowReadConcordWebsocket) clientPlugin->gw = (struct discord_gateway) { 0 }; - - if (lavaInfo->plugins->security->allowReadWebsocket) { - for (int i = 0;i <= lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onCoglinkScheduler[i]) break; - - int pluginResultCode = lavaInfo->plugins->events->onCoglinkScheduler[i](lavaInfo, clientPlugin, data, length, event); - if (pluginResultCode != COGLINK_PROCEED) return pluginResultCode; - } - } else { - struct coglink_lavaInfo *lavaInfoPlugin = lavaInfo; - lavaInfoPlugin->nodes = NULL; - - clientPlugin->io_poller = NULL; - - for (int i = 0;i <= lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onCoglinkScheduler[i]) break; - - int pluginResultCode = lavaInfo->plugins->events->onCoglinkScheduler[i](lavaInfoPlugin, clientPlugin, data, length, event); - if (pluginResultCode != COGLINK_PROCEED) return pluginResultCode; - } - } - } - - switch(event) { - case DISCORD_EV_VOICE_STATE_UPDATE: { - jsmn_parser parser; - jsmntok_t tokens[128]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, data, length, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging) log_error("[coglink:jsmn-find] Failed to parse JSON."); - return DISCORD_EVENT_IGNORE; - } - - jsmnf_loader loader; - jsmnf_pair pairs[128]; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, data, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return DISCORD_EVENT_IGNORE; - } - - jsmnf_pair *VGI = jsmnf_find(pairs, data, "guild_id", sizeof("guild_id") - 1); - if (_coglink_checkParse(lavaInfo, VGI, "guild_id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - jsmnf_pair *VUI = jsmnf_find(pairs, data, "user_id", sizeof("user_id") - 1); - if (_coglink_checkParse(lavaInfo, VUI, "user_id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char *userId = malloc(COGLINK_USER_ID_LENGTH); - char *guildId = malloc(COGLINK_GUILD_ID_LENGTH); - - snprintf(userId, COGLINK_USER_ID_LENGTH, "%.*s", (int)VUI->v.len, data + VUI->v.pos); - snprintf(guildId, COGLINK_GUILD_ID_LENGTH, "%.*s", (int)VGI->v.len, data + VGI->v.pos); - - if (0 == strcmp(userId, lavaInfo->botId)) { - jsmnf_pair *SSI = jsmnf_find(pairs, data, "session_id", sizeof("session_id") - 1); - if (_coglink_checkParse(lavaInfo, SSI, "session_id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char *sessionId = malloc(COGLINK_SESSION_ID_LENGTH); - snprintf(sessionId, COGLINK_SESSION_ID_LENGTH, "%.*s", (int)SSI->v.len, data + SSI->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->memoryDebugging) log_debug("[coglink:memory] Allocated %d bytes for sessionId to be saved in the hashtable.", sizeof(sessionId)); - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging) log_debug("[coglink:tablec] Parsed voice state update json, results:\n> guild_id: %s\n> user_id: %s\n> session_id: %s", guildId, userId, sessionId); - - if (sessionId[0] != 'n') { - struct coglink_voiceData *vcData = malloc(sizeof(struct coglink_voiceData)); - vcData->token = NULL; - vcData->endpoint = NULL; - vcData->sessionId = sessionId; - - tablec_set(&coglink_hashtable, guildId, vcData); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging || lavaInfo->debugging->tablecSuccessDebugging) log_debug("[coglink:tablec] The user that got updated is the bot, saving the sessionId."); - } else { - free(sessionId); - - struct tablec_bucket *value = tablec_get(&coglink_hashtable, guildId); - - if (!value->value) { - free(guildId); - free(userId); - - return DISCORD_EVENT_IGNORE; - } - - struct coglink_voiceData *vcData = value->value; - - if (vcData->token) { - free(vcData->token); - vcData->token = NULL; - } - if (vcData->endpoint) { - free(vcData->endpoint); - vcData->endpoint = NULL; - } - if (vcData->sessionId) { - free(vcData->sessionId); - vcData->sessionId = NULL; - } - free(vcData); - free(value->key); - - tablec_del(&coglink_hashtable, guildId); - - free(guildId); - free(userId); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging || lavaInfo->debugging->tablecSuccessDebugging) log_debug("[coglink:tablec] The user that got updated is the bot, but the sessionId is NULL, removing the sessionId from the hashtable."); - } - } else if (lavaInfo->allowCachingVoiceChannelIds) { - jsmnf_pair *VCI = jsmnf_find(pairs, data, "channel_id", sizeof("channel_id") - 1); - if (_coglink_checkParse(lavaInfo, VCI, "channel_id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char *channelId = malloc(COGLINK_VOICE_ID_LENGTH); - snprintf(channelId, COGLINK_VOICE_ID_LENGTH, "%.*s", (int)VCI->v.len, data + VCI->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->memoryDebugging) log_debug("[coglink:memory] Allocated %d bytes for voiceId to be saved in the hashtable.", sizeof(channelId)); - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging) log_debug("[coglink:tablec] Parsed voice state update json, results:\n> guild_id: %s\n> user_id: %s\n> channel_id: %s", guildId, userId, channelId); - - if (channelId[0] != 'n') { - tablec_set(&coglink_hashtable, userId, channelId); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging || lavaInfo->debugging->tablecSuccessDebugging) log_debug("[coglink:tablec] Optional save members channel ID is enabled, saving the channelId."); - } else { - free(channelId); - - struct tablec_bucket *value = tablec_get(&coglink_hashtable, userId); - - if (!value->value) { - free(userId); - free(guildId); - - return DISCORD_EVENT_IGNORE; - } - - free(value->value); - free(value->key); - - tablec_del(&coglink_hashtable, userId); - - free(guildId); - free(userId); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging || lavaInfo->debugging->tablecSuccessDebugging) log_debug("[coglink:tablec] Optional save members channel ID is enabled, but the channelId is NULL, removing the channelId from the hashtable."); - } - } - } return DISCORD_EVENT_IGNORE; - case DISCORD_EV_VOICE_SERVER_UPDATE: { - jsmn_parser parser; - jsmntok_t tokens[256]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, data, length, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceServerDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[jsmn-find] Failed to parse JSON."); - return DISCORD_EVENT_IGNORE; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceServerDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[jsmn-find] Successfully parsed JSON."); - - jsmnf_loader loader; - jsmnf_pair pairs[128]; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, data, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceServerDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return DISCORD_EVENT_IGNORE; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceServerDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[coglink:jsmn-find] Successfully loaded jsmn-find."); - - jsmnf_pair *VGI = jsmnf_find(pairs, data, "guild_id", sizeof("guild_id") - 1); - if (_coglink_checkParse(lavaInfo, VGI, "guild_id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char guildId[COGLINK_GUILD_ID_LENGTH]; - snprintf(guildId, sizeof(guildId), "%.*s", (int)VGI->v.len, data + VGI->v.pos); - - struct tablec_bucket *dataVcData = tablec_get(&coglink_hashtable, guildId); - if (!dataVcData->value) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceServerDebugging || lavaInfo->debugging->tablecErrorsDebugging) log_error("[coglink:TableC] The hashtable does not contain any data related to the guildId."); - return DISCORD_EVENT_IGNORE; - } - struct coglink_voiceData *vcData = dataVcData->value; - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceServerDebugging || lavaInfo->debugging->tablecSuccessDebugging) log_debug("[coglink:TableC] Successfully found the sessionID in the hashtable."); - - jsmnf_pair *token = jsmnf_find(pairs, data, "token", sizeof("token") - 1); - if (_coglink_checkParse(lavaInfo, token, "token") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - jsmnf_pair *endpoint = jsmnf_find(pairs, data, "endpoint", sizeof("endpoint") - 1); - if (_coglink_checkParse(lavaInfo, endpoint, "endpoint") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char key[32]; - snprintf(key, sizeof(key), "player:%s", guildId); - - struct tablec_bucket *value = tablec_get(&coglink_hashtable, key); - - int node; - memcpy(&node, &value->value, sizeof(node)); - - char reqPath[128]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players/%s", lavaInfo->nodes[node].sessionId, guildId); - - char *tokenStr = malloc(COGLINK_TOKEN_LENGTH); - snprintf(tokenStr, COGLINK_TOKEN_LENGTH, "%.*s", (int)token->v.len, data + token->v.pos); - - char *endpointStr = malloc(COGLINK_ENDPOINT_LENGTH); - snprintf(endpointStr, COGLINK_ENDPOINT_LENGTH, "%.*s", (int)endpoint->v.len, data + endpoint->v.pos); - - char payload[256]; - int payloadLen = snprintf(payload, sizeof(payload), "{\"voice\":{\"token\":\"%s\",\"endpoint\":\"%s\",\"sessionId\":\"%s\"}}", tokenStr, endpointStr, vcData->sessionId); - - _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_PATCH_REQ, - .additionalDebuggingSuccess = lavaInfo->debugging->handleSchedulerVoiceServerDebugging, - .additionalDebuggingError = lavaInfo->debugging->handleSchedulerVoiceServerDebugging, - .path = reqPath, - .pathLength = pathLen, - .body = payload, - .bodySize = payloadLen - }); - - struct coglink_voiceData *newVcData = malloc(sizeof(struct coglink_voiceData)); - newVcData->token = tokenStr; - newVcData->endpoint = endpointStr; - newVcData->sessionId = vcData->sessionId; - - if (vcData->token) { - free(vcData->token); - vcData->token = NULL; - } - if (vcData->endpoint) { - free(vcData->endpoint); - vcData->endpoint = NULL; - } - free(vcData); - vcData = NULL; - - tablec_del(&coglink_hashtable, guildId); - tablec_set(&coglink_hashtable, guildId, newVcData); - - } return DISCORD_EVENT_IGNORE; - case DISCORD_EV_GUILD_CREATE: { - jsmn_parser parser; - jsmntok_t tokens[5096 * 4]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, data, length, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging) log_error("[coglink:jsmn-find] Failed to parse JSON."); - return DISCORD_EVENT_IGNORE; - } - - jsmnf_loader loader; - jsmnf_pair pairs[5096 * 4]; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, data, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return DISCORD_EVENT_IGNORE; - } - - jsmnf_pair *voice_states = jsmnf_find(pairs, data, "voice_states", sizeof("voice_states") - 1); - - if (!voice_states) return DISCORD_EVENT_IGNORE; - - jsmnf_pair *VGI = jsmnf_find(pairs, data, "id", sizeof("id") - 1); - if (_coglink_checkParse(lavaInfo, VGI, "id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char *guildId = malloc(COGLINK_GUILD_ID_LENGTH); - snprintf(guildId, COGLINK_GUILD_ID_LENGTH, "%.*s", (int)VGI->v.len, data + VGI->v.pos); - - jsmnf_pair *GVS = jsmnf_find(pairs, data, "voice_states", sizeof("voice_states") - 1); - - int i = GVS->size; - while (i > 0) { - char iStr[16]; - snprintf(iStr, sizeof(iStr), "%d", i - 1); - - char *path[] = { "voice_states", iStr, "user_id" }; - - jsmnf_pair *VUI = jsmnf_find_path(pairs, data, path, 3); - if (_coglink_checkParse(lavaInfo, VUI, "user_id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char *userId = malloc(COGLINK_USER_ID_LENGTH); - - snprintf(userId, COGLINK_USER_ID_LENGTH, "%.*s", (int)VUI->v.len, data + VUI->v.pos); - - if (0 == strcmp(userId, lavaInfo->botId)) { - path[2] = "session_id"; - jsmnf_pair *SSI = jsmnf_find_path(pairs, data, path, 3); - if (_coglink_checkParse(lavaInfo, SSI, "session_id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char *sessionId = malloc(COGLINK_SESSION_ID_LENGTH); - snprintf(sessionId, COGLINK_SESSION_ID_LENGTH, "%.*s", (int)SSI->v.len, data + SSI->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->memoryDebugging) log_debug("[coglink:memory] Allocated %d bytes for sessionId to be saved in the hashtable.", sizeof(sessionId)); - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging) log_debug("[coglink:tablec] Parsed voice state update json, results:\n> guild_id: %s\n> user_id: %s\n> session_id: %s", guildId, userId, sessionId); - - struct coglink_voiceData *vcData = malloc(sizeof(struct coglink_voiceData)); - vcData->sessionId = sessionId; - - tablec_set(&coglink_hashtable, guildId, vcData); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging || lavaInfo->debugging->tablecSuccessDebugging) log_debug("[coglink:tablec] The user that got updated is the bot, saving the sessionId."); - } else if (lavaInfo->allowCachingVoiceChannelIds) { - path[2] = "channel_id"; - jsmnf_pair *VCI = jsmnf_find_path(pairs, data, path, 3); - if (_coglink_checkParse(lavaInfo, VCI, "channel_id") != COGLINK_PROCEED) return DISCORD_EVENT_IGNORE; - - char *channelId = malloc(COGLINK_VOICE_ID_LENGTH); - snprintf(channelId, COGLINK_VOICE_ID_LENGTH, "%.*s", (int)VCI->v.len, data + VCI->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->memoryDebugging) log_debug("[coglink:memory] Allocated %d bytes for voiceId to be saved in the hashtable.", sizeof(channelId)); - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging) log_debug("[coglink:tablec] Parsed voice state update json, results:\n> guild_id: %s\n> user_id: %s\n> channel_id: %s", guildId, userId, channelId); - - tablec_set(&coglink_hashtable, userId, channelId); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->handleSchedulerVoiceStateDebugging || lavaInfo->debugging->tablecSuccessDebugging) log_debug("[coglink:tablec] Optional save members channel ID is enabled, saving the channelId."); - } - - i--; - } - } return DISCORD_EVENT_IGNORE; - default: - return DISCORD_EVENT_MAIN_THREAD; - } -} - -int coglink_joinVoiceChannel(struct coglink_lavaInfo *lavaInfo, struct discord *client, u64snowflake voiceChannelId, u64snowflake guildId) { - char joinVCPayload[512]; - int payloadLen = snprintf(joinVCPayload, sizeof(joinVCPayload), "{\"op\":4,\"d\":{\"guild_id\":%"PRIu64",\"channel_id\":\"%"PRIu64"\",\"self_mute\":false,\"self_deaf\":true}}", guildId, voiceChannelId); - - if (!ws_send_text(client->gw.ws, NULL, joinVCPayload, payloadLen)) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->sendPayloadErrorsDebugging) log_fatal("[coglink:libcurl] Something went wrong while sending a payload with op 4 to Discord."); - return COGLINK_ERROR; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->sendPayloadSuccessDebugging) log_debug("[coglink:libcurl] Successfully sent the payload with op 4 to Discord."); - - return COGLINK_SUCCESS; -} - -int coglink_getUserVoiceChannel(struct coglink_lavaInfo *lavaInfo, u64snowflake userId, char *channelId, int channelIdSize) { - char userIdStr[COGLINK_USER_ID_LENGTH]; - snprintf(userIdStr, sizeof(userIdStr), "%"PRIu64"", userId); - - struct tablec_bucket *voiceId = tablec_get(&coglink_hashtable, userIdStr); - - if (!voiceId->value) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->joinUserVoiceChannelDebugging || lavaInfo->debugging->tablecErrorsDebugging) log_error("[coglink:tablec] The hashtable does not contain any data related to the userId."); - - return COGLINK_TABLEC_NOT_FOUND; - } - - snprintf(channelId, channelIdSize, "%s", (char *)voiceId->value); - - return COGLINK_SUCCESS; -} - -int coglink_joinUserVoiceChannel(struct coglink_lavaInfo *lavaInfo, struct discord *client, u64snowflake userId, u64snowflake guildId) { - char userIdStr[COGLINK_USER_ID_LENGTH]; - snprintf(userIdStr, sizeof(userIdStr), "%"PRIu64"", userId); - - struct tablec_bucket *voiceId = tablec_get(&coglink_hashtable, userIdStr); - - if (!voiceId->value) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->joinUserVoiceChannelDebugging || lavaInfo->debugging->tablecErrorsDebugging) log_error("[coglink:tablec] The hashtable does not contain any data related to the userId."); - - return COGLINK_TABLEC_NOT_FOUND; - } - - char joinVCPayload[512]; - int payloadLen = snprintf(joinVCPayload, sizeof(joinVCPayload), "{\"op\":4,\"d\":{\"guild_id\":%"PRIu64",\"channel_id\":\"%s\",\"self_mute\":false,\"self_deaf\":true}}", guildId, (char *)voiceId->value); - - if (!ws_send_text(client->gw.ws, NULL, joinVCPayload, payloadLen)) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->sendPayloadErrorsDebugging) log_fatal("[coglink:libcurl] Something went wrong while sending a payload with op 4 to Discord."); - - return COGLINK_ERROR; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->sendPayloadSuccessDebugging) log_debug("[coglink:libcurl] Successfully sent the payload with op 4 to Discord."); - - return COGLINK_SUCCESS; -} - -void coglink_freeNodeInfo(struct coglink_lavaInfo *lavaInfo) { - if (lavaInfo) lavaInfo = NULL; -} - -void coglink_setEvents(struct coglink_lavaInfo *lavaInfo, struct coglink_lavalinkEvents *lavalinkEvents) { - lavaInfo->events = lavalinkEvents; -} - -void coglink_disconnectNode(struct coglink_lavaInfo *lavaInfo, int nodePos) { - ws_close(lavaInfo->nodes[nodePos].ws, 1000, "Requested to be closed", 23); - ws_cleanup(lavaInfo->nodes[nodePos].ws); - curl_multi_cleanup(lavaInfo->nodes[nodePos].mhandle); - free(&lavaInfo->nodes[nodePos]); -} - -void coglink_connectNodeCleanup(struct coglink_lavaInfo *lavaInfo, struct discord *client) { - tablec_cleanup(&coglink_hashtable); - - int i = -1; - while (i++ <= lavaInfo->nodeCount) { - ws_close(lavaInfo->nodes[i].ws, 1000, "Normal close", 13); - io_poller_curlm_del(client->io_poller, lavaInfo->nodes[i].mhandle); - ws_cleanup(lavaInfo->nodes[i].ws); - curl_multi_cleanup(lavaInfo->nodes[i].mhandle); - free(&lavaInfo->nodes[i]); - } - - curl_global_cleanup(); - coglink_freeNodeInfo(lavaInfo); -} - -// TODO: Add reconnect logic. Requires Concord's built-in websocket library to have its close event working. -int coglink_connectNode(struct coglink_lavaInfo *lavaInfo, struct discord *client, struct coglink_lavalinkNodes *nodesArr, struct coglink_nodeInfo nodesBuf[]) { - tablec_init(&coglink_hashtable, 128); - - discord_set_data(client, lavaInfo); - discord_set_event_scheduler(client, __coglink_handleScheduler); - - if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->curlErrorsDebugging) log_fatal("[coglink:libcurl] Something went wrong while initializing libcurl (global)."); - return COGLINK_ERROR; - } - - int i = -1; - while (i++ <= nodesArr->size - 2) { - char hostname[128 + 21]; - - if (nodesArr->nodes[i].ssl) snprintf(hostname, sizeof(hostname), "wss://%s/v4/websocket", nodesArr->nodes[i].hostname); - else snprintf(hostname, sizeof(hostname), "ws://%s/v4/websocket", nodesArr->nodes[i].hostname); - - struct coglink_nodeInfo *nodeInfo = malloc(sizeof(struct coglink_nodeInfo)); - nodeInfo->node = nodesArr->nodes[i]; - nodeInfo->mhandle = curl_multi_init(); - nodeInfo->tstamp = (uint64_t)0; - - lavaInfo->nodeId = i; - - struct ws_callbacks callbacks = { - .on_text = onTextEvent, - .on_close = onCloseEvent, - .data = lavaInfo - }; - - nodeInfo->ws = ws_init(&callbacks, client->gw.mhandle, NULL); - - ws_set_url(nodeInfo->ws, hostname, NULL); - ws_start(nodeInfo->ws); - - if (lavaInfo->allowResuming && lavaInfo->nodes != NULL) ws_add_header(nodeInfo->ws, "Session-Id", lavaInfo->nodes[i].sessionId); - ws_add_header(nodeInfo->ws, "Authorization", nodesArr->nodes[i].password); - ws_add_header(nodeInfo->ws, "Num-Shards", lavaInfo->shards); - ws_add_header(nodeInfo->ws, "User-Id", lavaInfo->botId); - ws_add_header(nodeInfo->ws, "Client-Name", "Coglink"); - ws_add_header(nodeInfo->ws, "Sec-WebSocket-Protocol", "13"); - - io_poller_curlm_add(client->io_poller, nodeInfo->mhandle, _coglink_IOPoller, nodeInfo); - io_poller_curlm_enable_perform(client->io_poller, nodeInfo->mhandle); - - nodesBuf[i] = *nodeInfo; - } - - lavaInfo->nodes = nodesBuf; - lavaInfo->nodeCount = nodesArr->size - 2; - - return COGLINK_SUCCESS; -} diff --git a/lib/network.c b/lib/network.c deleted file mode 100644 index 0da080a..0000000 --- a/lib/network.c +++ /dev/null @@ -1,174 +0,0 @@ -#include -#include - -#include -#include - -#include "lavalink-internal.h" -#include "lavalink.h" -#include "definitions.h" -#include "network.h" - -int coglink_getRouterPlanner(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, struct coglink_requestInformation *res) { - int node = _coglink_findPlayerNode(guildId); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], res, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_GET_REQ, - .path = "/routeplanner/status", - .pathLength = 21, - .getResponse = 1 - }); -} - -int coglink_parseRouterPlanner(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, char *ipPosition, struct coglink_lavalinkRouter *lavalinkRouterStruct) { - jsmn_parser parser; - jsmntok_t tokens[128]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, res->body, res->size, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to parse JSON."); - return COGLINK_JSMNF_ERROR_PARSE; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[jsmn-find] Successfully parsed JSON."); - - jsmnf_loader loader; - jsmnf_pair pairs[128]; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, res->body, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return COGLINK_JSMNF_ERROR_LOAD; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[coglink:jsmn-find] Successfully loaded jsmn-find."); - - jsmnf_pair *class = jsmnf_find(pairs, res->body, "class", sizeof("class") - 1); - if (_coglink_checkParse(lavaInfo, class, "class") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(lavalinkRouterStruct->class, sizeof(lavalinkRouterStruct->class), "%.*s", (int)class->v.len, res->body + class->v.pos); - - if (lavalinkRouterStruct->class[0] == 'n') return COGLINK_ROUTERPLANNER_NOT_SET; - - char *path[] = { "details", "ipBlock", "type", NULL }; - jsmnf_pair *type = jsmnf_find_path(pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, type, "type") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[2] = "size"; - jsmnf_pair *size = jsmnf_find_path(pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, size, "size") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "failingAddresses"; - path[2] = ipPosition; - path[3] = "address"; - jsmnf_pair *address = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, address, "address") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "failingTimestamp"; - jsmnf_pair *failingTimestamp = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, failingTimestamp, "failingTimestamp") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "failingTime"; - jsmnf_pair *failingTime = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, failingTime, "failingTime") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - if (!type || !size || !address || !failingTimestamp || !failingTime) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_fatal("[coglink:jsmnf-find] Error while trying to find %s field.", !type ? "type" : !size ? "size" : !address ? "address" : !failingTimestamp ? "failingTimestamp" : !failingTime ? "failingTime" : "???"); - return COGLINK_JSMNF_ERROR_FIND; - } - - if (lavalinkRouterStruct->class[0] == 'R') { - path[1] = "rotateIndex"; - jsmnf_pair *rotateIndex = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, rotateIndex, "rotateIndex") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "ipIndex"; - jsmnf_pair *ipIndex = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, ipIndex, "ipIndex") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "currentAddress"; - jsmnf_pair *currentAddress = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, currentAddress, "currentAddress") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(lavalinkRouterStruct->details->ipBlock->type, sizeof(lavalinkRouterStruct->details->ipBlock->type), "%.*s", (int)type->v.len, res->body + type->v.pos); - snprintf(lavalinkRouterStruct->details->ipBlock->size, sizeof(lavalinkRouterStruct->details->ipBlock->size), "%.*s", (int)size->v.len, res->body + size->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->address, sizeof(lavalinkRouterStruct->details->failingAddress->address), "%.*s", (int)address->v.len, res->body + address->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->failingTimestamp, sizeof(lavalinkRouterStruct->details->failingAddress->failingTimestamp), "%.*s", (int)failingTimestamp->v.len, res->body + failingTimestamp->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->failingTime, sizeof(lavalinkRouterStruct->details->failingAddress->failingTime), "%.*s", (int)failingTime->v.len, res->body + failingTime->v.pos); - snprintf(lavalinkRouterStruct->details->rotateIndex, sizeof(lavalinkRouterStruct->details->rotateIndex), "%.*s", (int)rotateIndex->v.len, res->body + rotateIndex->v.pos); - snprintf(lavalinkRouterStruct->details->ipIndex, sizeof(lavalinkRouterStruct->details->ipIndex), "%.*s", (int)ipIndex->v.len, res->body + ipIndex->v.pos); - snprintf(lavalinkRouterStruct->details->currentAddress, sizeof(lavalinkRouterStruct->details->currentAddress), "%.*s", (int)currentAddress->v.len, res->body + currentAddress->v.pos); - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed error search json, results:\n> class: %s\n> type: %s\n> size: %s\n> address: %s\n> failingTimestamp: %s\n> failingTime: %s\n> rotateIndex: %s\n> ipIndex: %s\n> currentAddress: %s\n", lavalinkRouterStruct->class, lavalinkRouterStruct->details->ipBlock->type, lavalinkRouterStruct->details->ipBlock->size, lavalinkRouterStruct->details->failingAddress->address, lavalinkRouterStruct->details->failingAddress->failingTimestamp, lavalinkRouterStruct->details->failingAddress->failingTime, lavalinkRouterStruct->details->rotateIndex, lavalinkRouterStruct->details->ipIndex, lavalinkRouterStruct->details->currentAddress); - } else if (lavalinkRouterStruct->class[0] == 'N') { - path[1] = "currentAddressIndex"; - jsmnf_pair *currentAddressIndex = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, currentAddressIndex, "currentAddressIndex") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(lavalinkRouterStruct->details->ipBlock->type, sizeof(lavalinkRouterStruct->details->ipBlock->type), "%.*s", (int)type->v.len, res->body + type->v.pos); - snprintf(lavalinkRouterStruct->details->ipBlock->size, sizeof(lavalinkRouterStruct->details->ipBlock->size), "%.*s", (int)size->v.len, res->body + size->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->address, sizeof(lavalinkRouterStruct->details->failingAddress->address), "%.*s", (int)address->v.len, res->body + address->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->failingTimestamp, sizeof(lavalinkRouterStruct->details->failingAddress->failingTimestamp), "%.*s", (int)failingTimestamp->v.len, res->body + failingTimestamp->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->failingTime, sizeof(lavalinkRouterStruct->details->failingAddress->failingTime), "%.*s", (int)failingTime->v.len, res->body + failingTime->v.pos); - snprintf(lavalinkRouterStruct->details->currentAddressIndex, sizeof(lavalinkRouterStruct->details->currentAddressIndex), "%.*s", (int)currentAddressIndex->v.len, res->body + currentAddressIndex->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed error search json, results:\n> class: %s\n> type: %s\n> size: %s\n> address: %s\n> failingTimestamp: %s\n> failingTime: %s\n> currentAddressIndex: %s\n", lavalinkRouterStruct->class, lavalinkRouterStruct->details->ipBlock->type, lavalinkRouterStruct->details->ipBlock->size, lavalinkRouterStruct->details->failingAddress->address, lavalinkRouterStruct->details->failingAddress->failingTimestamp, lavalinkRouterStruct->details->failingAddress->failingTime, lavalinkRouterStruct->details->currentAddressIndex); - } else { - path[1] = "blockIndex"; - jsmnf_pair *blockIndex = jsmnf_find_path(pairs, res->body, path, 2); - - path[1] = "currentAddressIndex"; - jsmnf_pair *currentAddressIndex = jsmnf_find_path(pairs, res->body, path, 2); - - if (!currentAddressIndex) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_fatal("[coglink:jsmnf-find] Error while trying to find currentAddressIndex field."); - return COGLINK_JSMNF_ERROR_FIND; - } - - snprintf(lavalinkRouterStruct->details->ipBlock->type, sizeof(lavalinkRouterStruct->details->ipBlock->type), "%.*s", (int)type->v.len, res->body + type->v.pos); - snprintf(lavalinkRouterStruct->details->ipBlock->size, sizeof(lavalinkRouterStruct->details->ipBlock->size), "%.*s", (int)size->v.len, res->body + size->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->address, sizeof(lavalinkRouterStruct->details->failingAddress->address), "%.*s", (int)address->v.len, res->body + address->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->failingTimestamp, sizeof(lavalinkRouterStruct->details->failingAddress->failingTimestamp), "%.*s", (int)failingTimestamp->v.len, res->body + failingTimestamp->v.pos); - snprintf(lavalinkRouterStruct->details->failingAddress->failingTime, sizeof(lavalinkRouterStruct->details->failingAddress->failingTime), "%.*s", (int)failingTime->v.len, res->body + failingTime->v.pos); - snprintf(lavalinkRouterStruct->details->blockIndex, sizeof(lavalinkRouterStruct->details->blockIndex), "%.*s", (int)blockIndex->v.len, res->body + blockIndex->v.pos); - snprintf(lavalinkRouterStruct->details->currentAddressIndex, sizeof(lavalinkRouterStruct->details->currentAddressIndex), "%.*s", (int)currentAddressIndex->v.len, res->body + currentAddressIndex->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed error search json, results:\n> class: %s\n> type: %s\n> size: %s\n> address: %s\n> failingTimestamp: %s\n> failingTime: %s\n> blockIndex: %s\n> currentAddressIndex: %s\n", lavalinkRouterStruct->class, lavalinkRouterStruct->details->ipBlock->type, lavalinkRouterStruct->details->ipBlock->size, lavalinkRouterStruct->details->failingAddress->address, lavalinkRouterStruct->details->failingAddress->failingTimestamp, lavalinkRouterStruct->details->failingAddress->failingTime, lavalinkRouterStruct->details->blockIndex, lavalinkRouterStruct->details->currentAddressIndex); - } - - return COGLINK_SUCCESS; -} - -void coglink_getRouterPlannerCleanup(struct coglink_requestInformation *res) { - free(res->body); -} - -int coglink_freeFailingAddress(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char *ip) { - int node = _coglink_findPlayerNode(guildId); - - char payload[16]; - int payloadLen = snprintf(payload, sizeof(payload), "{\"address\":\"%s\"}", ip); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_POST_REQ, - .path = "/routeplanner/free/address", - .pathLength = 27, - .body = payload, - .bodySize = payloadLen - }); -} - -int coglink_freeFailingAllAddresses(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId) { - int node = _coglink_findPlayerNode(guildId); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_POST_REQ, - .path = "/routeplanner/free/all", - .pathLength = 23, - .useVPath = 1 - }); -} diff --git a/lib/player.c b/lib/player.c deleted file mode 100644 index 2b063e0..0000000 --- a/lib/player.c +++ /dev/null @@ -1,529 +0,0 @@ -#include -#include - -#include -#include - -#include "lavalink-internal.h" -#include "lavalink.h" -#include "definitions.h" -#include "player.h" - -int coglink_createPlayer(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId) { - int node = _coglink_selectBestNode(lavaInfo); - - if (node == -1) return COGLINK_NO_NODES; - - _coglink_createPlayer(guildId, node); - - return node; -} - -int coglink_getPlayers(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, struct coglink_requestInformation *res) { - int node = _coglink_findPlayerNode(guildId); - - char reqPath[35]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players", lavaInfo->nodes[node].sessionId); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], res, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_GET_REQ, - .path = reqPath, - .pathLength = pathLen, - .getResponse = 1 - }); -} - -int coglink_parseGetPlayers(struct coglink_lavaInfo *lavaInfo, struct coglink_requestInformation *res, char *pos, struct coglink_playerInfo *playerInfoStruct) { - if (res->body[0] == '[' && res->body[1] == ']') return COGLINK_NO_PLAYERS; - - jsmn_parser parser; - jsmntok_t tokens[1024]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, res->body, res->size, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to parse JSON."); - return COGLINK_JSMNF_ERROR_PARSE; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[jsmn-find] Successfully parsed JSON."); - - jsmnf_loader loader; - jsmnf_pair pairs[1024]; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, res->body, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof(*pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return COGLINK_JSMNF_ERROR_LOAD; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[coglink:jsmn-find] Successfully loaded jsmn-find."); - - char *path[] = { pos, "guildId", NULL, NULL, NULL }; - - jsmnf_pair *guildId = jsmnf_find_path(pairs, res->body, path, 2); - - char GuildId[COGLINK_GUILD_ID_LENGTH]; - snprintf(GuildId, sizeof(GuildId), "%.*s", (int)guildId->v.len, res->body + guildId->v.pos); - - path[1] = "track"; - path[2] = "encoded"; - jsmnf_pair *encoded = jsmnf_find_path(pairs, res->body, path, 2); - - if (_coglink_checkParse(lavaInfo, encoded, "encoded") == COGLINK_SUCCESS) { - path[2] = "info"; - path[3] = "identifier"; - jsmnf_pair *identifier = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, identifier, "identifier") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[3] = "isSeekable"; - jsmnf_pair *isSeekable = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, isSeekable, "isSeekable") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[3] = "author"; - jsmnf_pair *author = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, author, "author") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[3] = "length"; - jsmnf_pair *length = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, length, "length") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[3] = "isStream"; - jsmnf_pair *isStream = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, isStream, "isStream") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[3] = "position"; - jsmnf_pair *position = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, position, "position") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[3] = "title"; - jsmnf_pair *title = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, title, "title") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[3] = "uri"; - jsmnf_pair *uri = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "artworkUrl"; - jsmnf_pair *artworkUrl = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "isrc"; - jsmnf_pair *isrc = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "sourceName"; - jsmnf_pair *sourceName = jsmnf_find_path(pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, sourceName, "sourceName") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - snprintf(playerInfoStruct->track->encoded, sizeof(playerInfoStruct->track->encoded), "%.*s", (int)encoded->v.len, res->body + encoded->v.pos); - snprintf(playerInfoStruct->track->info->identifier, sizeof(playerInfoStruct->track->info->identifier), "%.*s", (int)identifier->v.len, res->body + identifier->v.pos); - snprintf(playerInfoStruct->track->info->isSeekable, sizeof(playerInfoStruct->track->info->isSeekable), "%.*s", (int)isSeekable->v.len, res->body + isSeekable->v.pos); - snprintf(playerInfoStruct->track->info->author, sizeof(playerInfoStruct->track->info->author), "%.*s", (int)author->v.len, res->body + author->v.pos); - snprintf(playerInfoStruct->track->info->length, sizeof(playerInfoStruct->track->info->length), "%.*s", (int)length->v.len, res->body + length->v.pos); - snprintf(playerInfoStruct->track->info->isStream, sizeof(playerInfoStruct->track->info->isStream), "%.*s", (int)isStream->v.len, res->body + isStream->v.pos); - snprintf(playerInfoStruct->track->info->position, sizeof(playerInfoStruct->track->info->position), "%.*s", (int)position->v.len, res->body + position->v.pos); - snprintf(playerInfoStruct->track->info->title, sizeof(playerInfoStruct->track->info->title), "%.*s", (int)title->v.len, res->body + title->v.pos); - if (_coglink_checkParse(lavaInfo, uri, "uri") == COGLINK_SUCCESS) snprintf(playerInfoStruct->track->info->uri, sizeof(playerInfoStruct->track->info->uri), "%.*s", (int)uri->v.len, res->body + uri->v.pos); - if (_coglink_checkParse(lavaInfo, artworkUrl, "artworkUrl") == COGLINK_SUCCESS) snprintf(playerInfoStruct->track->info->artworkUrl, sizeof(playerInfoStruct->track->info->artworkUrl), "%.*s", (int)artworkUrl->v.len, res->body + artworkUrl->v.pos); - if (_coglink_checkParse(lavaInfo, isrc, "isrc") == COGLINK_SUCCESS) snprintf(playerInfoStruct->track->info->isrc, sizeof(playerInfoStruct->track->info->isrc), "%.*s", (int)isrc->v.len, res->body + isrc->v.pos); - snprintf(playerInfoStruct->track->info->sourceName, sizeof(playerInfoStruct->track->info->sourceName), "%.*s", (int)sourceName->v.len, res->body + sourceName->v.pos); - } - - path[1] = "volume"; - jsmnf_pair *volume = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, volume, "volume") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "paused"; - jsmnf_pair *paused = jsmnf_find_path(pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, paused, "paused") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(playerInfoStruct->volume, sizeof(playerInfoStruct->volume), "%.*s", (int)volume->v.len, res->body + volume->v.pos); - snprintf(playerInfoStruct->paused, sizeof(playerInfoStruct->paused), "%.*s", (int)paused->v.len, res->body + paused->v.pos); - - path[1] = "state"; - path[2] = "time"; - jsmnf_pair *time = jsmnf_find_path(pairs, res->body, path, 3); - - path[2] = "position"; - jsmnf_pair *position = jsmnf_find_path(pairs, res->body, path, 3); - - path[2] = "connected"; - jsmnf_pair *connected = jsmnf_find_path(pairs, res->body, path, 3); - - path[2] = "ping"; - jsmnf_pair *ping = jsmnf_find_path(pairs, res->body, path, 3); - - if (!time || !position || !connected || !ping) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_fatal("[coglink:jsmnf-find] Error while trying to find %s field.", !time ? "time" : !position ? "position" : !connected ? "connected" : "ping"); - return COGLINK_JSMNF_ERROR_FIND; - } - - snprintf(playerInfoStruct->state->time, sizeof(playerInfoStruct->state->time), "%.*s", (int)time->v.len, res->body + time->v.pos); - snprintf(playerInfoStruct->state->position, sizeof(playerInfoStruct->state->position), "%.*s", (int)position->v.len, res->body + position->v.pos); - snprintf(playerInfoStruct->state->connected, sizeof(playerInfoStruct->state->connected), "%.*s", (int)connected->v.len, res->body + connected->v.pos); - snprintf(playerInfoStruct->state->ping, sizeof(playerInfoStruct->state->ping), "%.*s", (int)ping->v.len, res->body + ping->v.pos); - - path[1] = "voice"; - path[2] = "token"; - jsmnf_pair *token = jsmnf_find_path(pairs, res->body, path, 3); - - path[2] = "endpoint"; - jsmnf_pair *endpoint = jsmnf_find_path(pairs, res->body, path, 3); - - path[2] = "sessionId"; - jsmnf_pair *sessionId = jsmnf_find_path(pairs, res->body, path, 3); - - if (!token || !endpoint || !sessionId) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_fatal("[coglink:jsmnf-find] Error while trying to find %s field.", !token ? "token" : !endpoint ? "endpoint": !sessionId ? "sessionId" : !connected ? "connected" : "ping"); - return COGLINK_JSMNF_ERROR_FIND; - } - - snprintf(playerInfoStruct->voice->token, sizeof(playerInfoStruct->voice->token), "%.*s", (int)token->v.len, res->body + token->v.pos); - snprintf(playerInfoStruct->voice->endpoint, sizeof(playerInfoStruct->voice->endpoint), "%.*s", (int)endpoint->v.len, res->body + endpoint->v.pos); - snprintf(playerInfoStruct->voice->sessionId, sizeof(playerInfoStruct->voice->sessionId), "%.*s", (int)sessionId->v.len, res->body + sessionId->v.pos); - - path[1] = "filters"; - path[2] = "volume"; - - jsmnf_pair *volumeFilter = jsmnf_find_path(pairs, res->body, path, 3); - if (volumeFilter) - snprintf(playerInfoStruct->filters->volume, sizeof(playerInfoStruct->filters->volume), "%.*s", (int)volumeFilter->v.len, res->body + volumeFilter->v.pos); - - path[2] = "equalizer"; - jsmnf_pair *equalizer = jsmnf_find_path(pairs, res->body, path, 3); - if (equalizer) { - path[3] = "bands"; - jsmnf_pair *bands = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "gain"; - jsmnf_pair *gain = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->equalizer->bands, sizeof(playerInfoStruct->filters->equalizer->bands), "%.*s", (int)bands->v.len, res->body + bands->v.pos); - snprintf(playerInfoStruct->filters->equalizer->gain, sizeof(playerInfoStruct->filters->equalizer->gain), "%.*s", (int)gain->v.len, res->body + gain->v.pos); - } - - path[2] = "karaoke"; - jsmnf_pair *karaoke = jsmnf_find_path(pairs, res->body, path, 3); - if (karaoke) { - path[3] = "level"; - jsmnf_pair *level = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "monoLevel"; - jsmnf_pair *monoLevel = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "filterBand"; - jsmnf_pair *filterBand = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "filterWidth"; - jsmnf_pair *filterWidth = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->karaoke->level, sizeof(playerInfoStruct->filters->karaoke->level), "%.*s", (int)level->v.len, res->body + level->v.pos); - snprintf(playerInfoStruct->filters->karaoke->monoLevel, sizeof(playerInfoStruct->filters->karaoke->monoLevel), "%.*s", (int)monoLevel->v.len, res->body + monoLevel->v.pos); - snprintf(playerInfoStruct->filters->karaoke->filterBand, sizeof(playerInfoStruct->filters->karaoke->filterBand), "%.*s", (int)filterBand->v.len, res->body + filterBand->v.pos); - snprintf(playerInfoStruct->filters->karaoke->filterWidth, sizeof(playerInfoStruct->filters->karaoke->filterWidth), "%.*s", (int)filterWidth->v.len, res->body + filterWidth->v.pos); - } - - path[2] = "timescale"; - jsmnf_pair *timescale = jsmnf_find_path(pairs, res->body, path, 3); - if (timescale) { - path[3] = "speed"; - jsmnf_pair *speed = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "pitch"; - jsmnf_pair *pitch = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "rate"; - jsmnf_pair *rate = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->timescale->speed, sizeof(playerInfoStruct->filters->timescale->speed), "%.*s", (int)speed->v.len, res->body + speed->v.pos); - snprintf(playerInfoStruct->filters->timescale->pitch, sizeof(playerInfoStruct->filters->timescale->pitch), "%.*s", (int)pitch->v.len, res->body + pitch->v.pos); - snprintf(playerInfoStruct->filters->timescale->rate, sizeof(playerInfoStruct->filters->timescale->rate), "%.*s", (int)rate->v.len, res->body + rate->v.pos); - } - - path[2] = "tremolo"; - jsmnf_pair *tremolo = jsmnf_find_path(pairs, res->body, path, 3); - if (tremolo) { - path[3] = "frequency"; - jsmnf_pair *frequency = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "depth"; - jsmnf_pair *depth = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->tremolo->frequency, sizeof(playerInfoStruct->filters->tremolo->frequency), "%.*s", (int)frequency->v.len, res->body + frequency->v.pos); - snprintf(playerInfoStruct->filters->tremolo->depth, sizeof(playerInfoStruct->filters->tremolo->depth), "%.*s", (int)depth->v.len, res->body + depth->v.pos); - } - - path[2] = "vibrato"; - jsmnf_pair *vibrato = jsmnf_find_path(pairs, res->body, path, 3); - if (vibrato) { - path[3] = "frequency"; - jsmnf_pair *frequency = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "depth"; - jsmnf_pair *depth = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->vibrato->frequency, sizeof(playerInfoStruct->filters->vibrato->frequency), "%.*s", (int)frequency->v.len, res->body + frequency->v.pos); - snprintf(playerInfoStruct->filters->vibrato->depth, sizeof(playerInfoStruct->filters->vibrato->depth), "%.*s", (int)depth->v.len, res->body + depth->v.pos); - } - - path[2] = "rotation"; - jsmnf_pair *rotation = jsmnf_find_path(pairs, res->body, path, 3); - if (rotation) { - path[3] = "rotationHz"; - jsmnf_pair *rotationHz = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->rotation, sizeof(playerInfoStruct->filters->rotation), "%.*s", (int)rotationHz->v.len, res->body + rotationHz->v.pos); - } - - path[2] = "distortion"; - jsmnf_pair *distortion = jsmnf_find_path(pairs, res->body, path, 3); - if (distortion) { - path[3] = "sinOffset"; - jsmnf_pair *sinOffset = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "sinScale"; - jsmnf_pair *sinScale = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "cosOffset"; - jsmnf_pair *cosOffset = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "cosScale"; - jsmnf_pair *cosScale = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "tanOffset"; - jsmnf_pair *tanOffset = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "tanScale"; - jsmnf_pair *tanScale = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "offset"; - jsmnf_pair *offset = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "scale"; - jsmnf_pair *scale = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->distortion->sinOffset, sizeof(playerInfoStruct->filters->distortion->sinOffset), "%.*s", (int)sinOffset->v.len, res->body + sinOffset->v.pos); - snprintf(playerInfoStruct->filters->distortion->sinScale, sizeof(playerInfoStruct->filters->distortion->sinScale), "%.*s", (int)sinScale->v.len, res->body + sinScale->v.pos); - snprintf(playerInfoStruct->filters->distortion->cosOffset, sizeof(playerInfoStruct->filters->distortion->cosOffset), "%.*s", (int)cosOffset->v.len, res->body + cosOffset->v.pos); - snprintf(playerInfoStruct->filters->distortion->cosScale, sizeof(playerInfoStruct->filters->distortion->cosScale), "%.*s", (int)cosScale->v.len, res->body + cosScale->v.pos); - snprintf(playerInfoStruct->filters->distortion->tanOffset, sizeof(playerInfoStruct->filters->distortion->tanOffset), "%.*s", (int)tanOffset->v.len, res->body + tanOffset->v.pos); - snprintf(playerInfoStruct->filters->distortion->tanScale, sizeof(playerInfoStruct->filters->distortion->tanScale), "%.*s", (int)tanScale->v.len, res->body + tanScale->v.pos); - snprintf(playerInfoStruct->filters->distortion->offset, sizeof(playerInfoStruct->filters->distortion->offset), "%.*s", (int)offset->v.len, res->body + offset->v.pos); - snprintf(playerInfoStruct->filters->distortion->scale, sizeof(playerInfoStruct->filters->distortion->scale), "%.*s", (int)scale->v.len, res->body + scale->v.pos); - } - - path[2] = "channelMix"; - jsmnf_pair *channelMix = jsmnf_find_path(pairs, res->body, path, 3); - if (channelMix) { - path[3] = "leftToLeft"; - jsmnf_pair *leftToLeft = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "leftToRight"; - jsmnf_pair *leftToRight = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "rightToLeft"; - jsmnf_pair *rightToLeft = jsmnf_find_path(pairs, res->body, path, 4); - - path[3] = "rightToRight"; - jsmnf_pair *rightToRight = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->channelMix->leftToLeft, sizeof(playerInfoStruct->filters->channelMix->leftToLeft), "%.*s", (int)leftToLeft->v.len, res->body + leftToLeft->v.pos); - snprintf(playerInfoStruct->filters->channelMix->leftToRight, sizeof(playerInfoStruct->filters->channelMix->leftToRight), "%.*s", (int)leftToRight->v.len, res->body + leftToRight->v.pos); - snprintf(playerInfoStruct->filters->channelMix->rightToLeft, sizeof(playerInfoStruct->filters->channelMix->rightToLeft), "%.*s", (int)rightToLeft->v.len, res->body + rightToLeft->v.pos); - snprintf(playerInfoStruct->filters->channelMix->rightToRight, sizeof(playerInfoStruct->filters->channelMix->rightToRight), "%.*s", (int)rightToRight->v.len, res->body + rightToRight->v.pos); - } - - path[2] = "lowPass"; - jsmnf_pair *lowPass = jsmnf_find_path(pairs, res->body, path, 3); - if (lowPass) { - path[3] = "smoothing"; - jsmnf_pair *lowPass = jsmnf_find_path(pairs, res->body, path, 4); - - snprintf(playerInfoStruct->filters->lowPass, sizeof(playerInfoStruct->filters->lowPass), "%.*s", (int)lowPass->v.len, res->body + lowPass->v.pos); - } - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->memoryDebugging) log_debug("[coglink:memory-strncpy] Set the value for struct members of playerInfoStruct."); - - return COGLINK_SUCCESS; -} - -void coglink_getPlayersCleanup(struct coglink_requestInformation *res) { - free(res->body); -} - -int coglink_playSong(struct coglink_lavaInfo *lavaInfo, char *encodedTrack, u64snowflake guildId) { - if (lavaInfo->plugins && lavaInfo->plugins->events->onPlayRequest[0]) { - for (int i = 0;i <+ lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onPlayRequest[i]) break; - - int pluginResultCode = lavaInfo->plugins->events->onPlayRequest[i](lavaInfo, encodedTrack, guildId); - if (pluginResultCode != COGLINK_PROCEED) return pluginResultCode; - } - } - - int node = _coglink_findPlayerNode(guildId); - - char reqPath[64]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players/%"PRIu64"", lavaInfo->nodes[node].sessionId, guildId); - - char payload[1024]; - int payloadLen = snprintf(payload, sizeof(payload), "{\"track\":{\"encoded\":\"%s\"}}", encodedTrack); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_PATCH_REQ, - .path = reqPath, - .pathLength = pathLen, - .body = payload, - .bodySize = payloadLen - }); -} - -void coglink_destroyPlayer(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId) { - int node = _coglink_findPlayerNode(guildId); - - char reqPath[64]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players/%"PRIu64"", lavaInfo->nodes[node].sessionId, guildId); - - _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_DELETE_REQ, - .path = reqPath, - .pathLength = pathLen - }); -} - -void coglink_stopPlayer(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId) { - int node = _coglink_findPlayerNode(guildId); - - char reqPath[64]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players/%"PRIu64"", lavaInfo->nodes[node].sessionId, guildId); - - _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_PATCH_REQ, - .path = reqPath, - .pathLength = pathLen, - .body = "{\"track\":{\"encoded\":null}}", - .bodySize = 21 - }); -} - -void coglink_pausePlayer(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char *pause) { - int node = _coglink_findPlayerNode(guildId); - - char reqPath[64]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players/%"PRIu64"", lavaInfo->nodes[node].sessionId, guildId); - - char payload[32]; - int payloadLen = snprintf(payload, sizeof(payload), "{\"pause\":%s}", pause); - - _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_PATCH_REQ, - .path = reqPath, - .pathLength = pathLen, - .body = payload, - .bodySize = payloadLen - }); -} - -void coglink_seekTrack(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char *position) { - int node = _coglink_findPlayerNode(guildId); - - char reqPath[64]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players/%"PRIu64"", lavaInfo->nodes[node].sessionId, guildId); - - char payload[32]; - int payloadLen = snprintf(payload, sizeof(payload), "{\"position\":%s}", position); - - _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_PATCH_REQ, - .path = reqPath, - .pathLength = pathLen, - .body = payload, - .bodySize = payloadLen - }); -} - -void coglink_setPlayerVolume(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, char *volume) { - int node = _coglink_findPlayerNode(guildId); - - char reqPath[64]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players/%"PRIu64"", lavaInfo->nodes[node].sessionId, guildId); - - char payload[32]; - int payloadLen = snprintf(payload, sizeof(payload), "{\"volume\":%s}", volume); - - _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_PATCH_REQ, - .path = reqPath, - .pathLength = pathLen, - .body = payload, - .bodySize = payloadLen - }); -} - -void coglink_setEffect(struct coglink_lavaInfo *lavaInfo, u64snowflake guildId, int effect, char *value) { - char payload[512 + 128], effectStr[11] = "VOLUME"; - switch (effect) { - case COGLINK_FILTER_VOLUME: { - snprintf(effectStr, sizeof(effectStr), "volume"); - break; - } - case COGLINK_FILTER_EQUALIZER: { - snprintf(effectStr, sizeof(effectStr), "equalizer"); - break; - } - case COGLINK_FILTER_KARAOKE: { - snprintf(effectStr, sizeof(effectStr), "karaoke"); - break; - } - case COGLINK_FILTER_TIMESCALE: { - snprintf(effectStr, sizeof(effectStr), "timescale"); - break; - } - case COGLINK_FILTER_TREMOLO: { - snprintf(effectStr, sizeof(effectStr), "tremolo"); - break; - } - case COGLINK_FILTER_ROTATION: { - snprintf(effectStr, sizeof(effectStr), "rotation"); - break; - } - case COGLINK_FILTER_DISTORTION: { - snprintf(effectStr, sizeof(effectStr), "distortion"); - break; - } - case COGLINK_FILTER_CHANNELMIX: { - snprintf(effectStr, sizeof(effectStr), "channelMix"); - break; - } - case COGLINK_FILTER_LOWPASS: { - snprintf(effectStr, sizeof(effectStr), "lowPass"); - break; - } - case COGLINK_FILTER_REMOVE: { - break; - } - } - - int node = _coglink_findPlayerNode(guildId); - - char reqPath[64]; - int payloadLen = 0, pathLen = snprintf(reqPath, sizeof(reqPath), "/sessions/%s/players/%"PRIu64"", lavaInfo->nodes[node].sessionId, guildId); - - if (effect != COGLINK_FILTER_REMOVE) payloadLen = snprintf(payload, sizeof(payload), "{\"filters\":{\"%s\":%s}}", effectStr, value); - else payloadLen = snprintf(payload, sizeof(payload), "{\"filters\":{}}"); - - _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], NULL, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_PATCH_REQ, - .path = reqPath, - .pathLength = pathLen, - .body = payload, - .bodySize = payloadLen - }); -} diff --git a/lib/plugins.c b/lib/plugins.c deleted file mode 100644 index 8ff1feb..0000000 --- a/lib/plugins.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "lavalink-internal.h" -#include "lavalink.h" -#include "plugins.h" - -void coglink_setPluginEvents(struct coglink_lavaInfo *lavaInfo, struct coglink_pluginEvents *pluginEvents) { - if (pluginEvents->onSearchRequest) lavaInfo->plugins->events->onSearchRequest[lavaInfo->plugins->amount] = pluginEvents->onSearchRequest; - if (pluginEvents->onPlayRequest) lavaInfo->plugins->events->onPlayRequest[lavaInfo->plugins->amount] = pluginEvents->onPlayRequest; - if (pluginEvents->onLavalinkPacketReceived) lavaInfo->plugins->events->onLavalinkPacketReceived[lavaInfo->plugins->amount] = pluginEvents->onLavalinkPacketReceived; - if (pluginEvents->onLavalinkClose) lavaInfo->plugins->events->onLavalinkClose[lavaInfo->plugins->amount] = pluginEvents->onLavalinkClose; - if (pluginEvents->onCoglinkScheduler) lavaInfo->plugins->events->onCoglinkScheduler[lavaInfo->plugins->amount] = pluginEvents->onCoglinkScheduler; - if (pluginEvents->onDecodeTrackRequest) lavaInfo->plugins->events->onDecodeTrackRequest[lavaInfo->plugins->amount] = pluginEvents->onDecodeTrackRequest; - if (pluginEvents->onDecodeTrackParseRequest) lavaInfo->plugins->events->onDecodeTrackParseRequest[lavaInfo->plugins->amount] = pluginEvents->onDecodeTrackParseRequest; - if (pluginEvents->onDecodeTracksRequest) lavaInfo->plugins->events->onDecodeTracksRequest[lavaInfo->plugins->amount] = pluginEvents->onDecodeTracksRequest; - if (pluginEvents->onDecodeTracksParseRequest) lavaInfo->plugins->events->onDecodeTracksParseRequest[lavaInfo->plugins->amount] = pluginEvents->onDecodeTracksParseRequest; -} diff --git a/lib/rest.c b/lib/rest.c new file mode 100644 index 0000000..6bba954 --- /dev/null +++ b/lib/rest.c @@ -0,0 +1,223 @@ +#include +#include + +#include +#include +#include + +#include "lavalink.h" +#include "utils.h" + +#include "rest.h" + +/* todo: option for more algorithms */ +int _coglink_select_node(struct coglink_client *c_client) { + size_t i = 0; + int bestStatsNode = 0, bestStats = -1; + + do { + if (!c_client->nodes->array[i].stats) continue; + + int systemLoad = c_client->nodes->array[i].stats->cpu->systemLoad; + int cores = c_client->nodes->array[i].stats->cpu->cores; + + int stats = (systemLoad / cores) * 100; + + if (stats < bestStats) { + bestStats = stats; + bestStatsNode = i; + } + } while (++i < c_client->nodes->size); + + return bestStatsNode; +} + +struct coglink_user *coglink_get_user(struct coglink_client *c_client, u64snowflake user_id) { + for (size_t i = 0; i < c_client->users->size; i++) { + struct coglink_user *user = &c_client->users->array[i]; + + if (user->id == user_id) return user; + } + + return NULL; +} + +int coglink_join_voice_channel(struct discord *client, u64snowflake guild_id, u64snowflake channel_id) { + char payload[((sizeof("{" + "\"op\":4," + "\"d\":{" + "\"guild_id\":\"\"," + "\"channel_id\":\"\"," + "\"self_mute\":false," + "\"self_deaf\":true" + "}") - 1) + (19 * 2) + 1)]; + + size_t payload_size = snprintf(payload, sizeof(payload), + "{" + "\"op\":4," + "\"d\":{" + "\"guild_id\":%"PRIu64"," + "\"channel_id\":\"%"PRIu64"\"," + "\"self_mute\":false," + "\"self_deaf\":true" + "}" + "}", guild_id, channel_id); + + if (!ws_send_text(client->gw.ws, NULL, payload, payload_size)) { + FATAL("[coglink:cws] Something went wrong while sending a payload with op 4 to Discord."); + + return -1; + } + + return 0; +} + +struct coglink_player *coglink_create_player(struct coglink_client *c_client, u64snowflake guild_id) { + int selected_node = _coglink_select_node(c_client); + size_t i = 0; + + do { + struct coglink_player *player = &c_client->players->array[i]; + + if (player->guild_id == guild_id) return player; + + if (player->guild_id == 0) { + player->guild_id = guild_id; + player->node = selected_node; + player->queue = malloc(sizeof(struct coglink_player_queue)); + player->queue->size = 0; + player->queue->array = NULL; + + c_client->players->size++; + + return player; + } + } while (++i < c_client->players->size); + + c_client->players->array = realloc(c_client->players->array, sizeof(struct coglink_player) * (c_client->players->size + 1)); + c_client->players->size++; + + /* todo: (?) use goto to reduce duplicated code */ + struct coglink_player *player = &c_client->players->array[c_client->players->size - 1]; + player->guild_id = guild_id; + player->node = selected_node; + player->queue = malloc(sizeof(struct coglink_player_queue)); + player->queue->size = 0; + player->queue->array = NULL; + + c_client->players->size++; + + return player; +} + +struct coglink_player *coglink_get_player(struct coglink_client *c_client, u64snowflake guild_id) { + for (size_t i = 0; i < c_client->players->size; i++) { + struct coglink_player *player = &c_client->players->array[i]; + + if (player->guild_id == guild_id) return player; + } + + return NULL; +} + +/* Experimental */ +struct coglink_player_queue *coglink_get_player_queue(struct coglink_client *c_client, struct coglink_player *player) { + (void) c_client; /* Standard */ + return player->queue; +} + +int coglink_add_track_to_queue(struct coglink_client *c_client, struct coglink_player *player, char *track) { + struct coglink_player_queue *queue = coglink_get_player_queue(c_client, player); + + /* realloc will automatically allocate if it's invalid */ + queue->array = realloc(queue->array, sizeof(char *) * (queue->size + 1)); + queue->size++; + + queue->array[queue->size - 1] = track; + + return COGLINK_SUCCESS; +} + +int coglink_remove_track_from_queue(struct coglink_client *c_client, struct coglink_player *player, char *track) { + struct coglink_player_queue *queue = coglink_get_player_queue(c_client, player); + + for (size_t i = 0; i < queue->size; i++) { + if (strcmp(queue->array[i], track) == 0) { + queue->array[i] = '\0'; + + for (size_t j = i; j < queue->size - 1; j++) { + queue->array[j] = queue->array[j + 1]; + } + + queue->array = realloc(queue->array, sizeof(char *) * (queue->size - 1)); + + return COGLINK_SUCCESS; + } + } + + return COGLINK_NOT_FOUND; +} + +int coglink_remove_player(struct coglink_client *c_client, struct coglink_player *player) { + player->guild_id = 0; + player->node = 0; + player->queue->size = 0; + player->queue->array = NULL; + + c_client->players->size--; + + return COGLINK_SUCCESS; +} + +int coglink_load_tracks(struct coglink_client *c_client, struct coglink_player *player, char *identifier) { + struct coglink_node *node = &c_client->nodes->array[player->node]; + + if (node->session_id == NULL) return COGLINK_NODE_READY; + + size_t endpoint_size = (sizeof("/loadtracks?identifier=") - 1) + strlen(identifier) + 1; + char *endpoint = malloc(endpoint_size); + snprintf(endpoint, endpoint_size, "/loadtracks?identifier=%s", identifier); + + struct coglink_response *res = malloc(sizeof(struct coglink_response)); + + _coglink_perform_request(node, &(struct coglink_request_params) { + .endpoint = endpoint, + .method = "GET", + .body = NULL, + .body_length = 0, + .get_response = true + }, res); + + printf("%s\n", res->body); + + free(endpoint); + + return COGLINK_SUCCESS; +} + +int coglink_play_track(struct coglink_client *c_client, struct coglink_player *player, char *track) { + struct coglink_node *node = &c_client->nodes->array[player->node]; + + if (node->session_id == NULL) return COGLINK_NODE_READY; + + size_t payload_size = (sizeof("{\"track\":{\"encoded\":\"\"}}") - 1) + strlen(track) + 1; + char *payload = malloc(payload_size); + snprintf(payload, payload_size, "{\"track\":{\"encoded\":\"%s\"}}", track); + + size_t endpoint_size = (sizeof("/sessions//guilds/") - 1) + COGLINK_SESSION_ID_LENGTH + 19 + 1; + char *endpoint = malloc(endpoint_size); + snprintf(endpoint, endpoint_size, "/sessions/%s/guilds/%" PRIu64 "", node->session_id, player->guild_id); + + _coglink_perform_request(node, &(struct coglink_request_params) { + .endpoint = endpoint, + .method = "POST", + .body = payload, + .body_length = payload_size, + .get_response = false + }, NULL); + + free(payload); + free(endpoint); + + return COGLINK_SUCCESS; +} \ No newline at end of file diff --git a/lib/track.c b/lib/track.c deleted file mode 100644 index 4fa0833..0000000 --- a/lib/track.c +++ /dev/null @@ -1,417 +0,0 @@ -#include -#include - -#include -#include - -#include "lavalink-internal.h" -#include "lavalink.h" -#include "definitions.h" -#include "track.h" - -int coglink_searchSong(struct coglink_lavaInfo *lavaInfo, char *song, struct coglink_requestInformation *res) { - int node = _coglink_selectBestNode(lavaInfo); - - if (lavaInfo->plugins && lavaInfo->plugins->events->onSearchRequest[0]) { - for (int i = 0;i <+ lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onSearchRequest[i]) break; - - int pluginResultCode = lavaInfo->plugins->events->onSearchRequest[i](lavaInfo, song, res); - if (pluginResultCode != COGLINK_PROCEED) return pluginResultCode; - } - } - - if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->curlErrorsDebugging) log_fatal("[coglink:libcurl] Something went wrong while initializing libcurl (global)."); - return COGLINK_ERROR; - } - - CURL *curl = curl_easy_init(); - - if (!curl) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->curlErrorsDebugging) log_fatal("[coglink:libcurl] Error while initializing libcurl."); - curl_global_cleanup(); - return COGLINK_ERROR; - } - - char *songEncoded = curl_easy_escape(curl, song, strlen(song) + 1); - - char reqPath[2000 + 33]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/loadtracks?identifier="); - - if (0 != strncmp(songEncoded, "https://", sizeof("https://"))) strncat(reqPath, "ytsearch:", sizeof(reqPath) - pathLen - 1); - strncat(reqPath, songEncoded, sizeof(reqPath) - pathLen - 1); - - curl_free(songEncoded); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], res, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_GET_REQ, - .additionalDebuggingSuccess = lavaInfo->debugging->searchSongSuccessDebugging, - .additionalDebuggingError = lavaInfo->debugging->searchSongErrorsDebugging, - .path = reqPath, - .pathLength = strlen(reqPath), - .getResponse = 1, - .usedCURL = curl - }); -} - -void coglink_searchSongCleanup(struct coglink_requestInformation *res) { - free(res->body); -} - -int coglink_decodeTrack(struct coglink_lavaInfo *lavaInfo, char *encodedTrack, struct coglink_requestInformation *res) { - int node = _coglink_selectBestNode(lavaInfo); - - if (lavaInfo->plugins && lavaInfo->plugins->events->onDecodeTrackRequest[0]) { - for (int i = 0;i <+ lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onDecodeTrackRequest[i]) break; - - int pluginResultCode = lavaInfo->plugins->events->onDecodeTrackRequest[i](lavaInfo, encodedTrack, res); - if (pluginResultCode != COGLINK_PROCEED) return pluginResultCode; - } - } - - char reqPath[COGLINK_TRACK_LENGTH + 26]; - int pathLen = snprintf(reqPath, sizeof(reqPath), "/decodetrack?encodedTrack=%s", encodedTrack); - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], res, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_GET_REQ, - .path = reqPath, - .pathLength = pathLen, - .getResponse = 1 - }); -} - -int coglink_initParseTrack(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *cStruct, struct coglink_requestInformation *res) { - jsmn_parser parser; - jsmntok_t tokens[1024 * 4]; - - jsmn_init(&parser); - int r = jsmn_parse(&parser, res->body, res->size, tokens, sizeof(tokens)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseTrackErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to parse JSON."); - return COGLINK_JSMNF_ERROR_PARSE; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseTrackSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[jsmn-find] Successfully parsed JSON."); - - jsmnf_loader loader; - - jsmnf_init(&loader); - r = jsmnf_load(&loader, res->body, tokens, parser.toknext, cStruct->pairs, sizeof(cStruct->pairs) / sizeof(*cStruct->pairs)); - - if (r < 0) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseTrackErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to load jsmn-find."); - return COGLINK_JSMNF_ERROR_LOAD; - } - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseTrackSuccessDebugging || lavaInfo->debugging->jsmnfSuccessDebugging) log_debug("[coglink:jsmn-find] Successfully loaded jsmn-find."); - - return COGLINK_SUCCESS; -} - -int coglink_parseDecodeTrack(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, struct coglink_parsedTrack *parsedTrackStruct) { - if (lavaInfo->plugins && lavaInfo->plugins->events->onDecodeTrackParseRequest[0]) { - for (int i = 0;i <+ lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onDecodeTrackParseRequest[i]) break; - - int pluginResultCode = lavaInfo->plugins->events->onDecodeTrackParseRequest[i](lavaInfo, res, parsedTrackStruct); - if (pluginResultCode != COGLINK_PROCEED) return pluginResultCode; - } - } - - char *path[] = { "info", "identifier" }; - jsmnf_pair *identifier = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, identifier, "identifier") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[1] = "isSeekable"; - jsmnf_pair *isSeekable = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, isSeekable, "isSeekable") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[1] = "author"; - jsmnf_pair *author = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, author, "author") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[1] = "length"; - jsmnf_pair *length = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, length, "length") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[1] = "isStream"; - jsmnf_pair *isStream = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, isStream, "isStream") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[1] = "position"; - jsmnf_pair *position = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, position, "position") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[1] = "title"; - jsmnf_pair *title = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, title, "title") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[1] = "uri"; - jsmnf_pair *uri = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - - path[1] = "artworkUrl"; - jsmnf_pair *artworkUrl = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - - path[1] = "isrc"; - jsmnf_pair *isrc = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - - path[1] = "sourceName"; - jsmnf_pair *sourceName = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, sourceName, "sourceName") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - if (!identifier || !isSeekable || !author || !length || !isStream || !position || !title || !uri || !sourceName) { - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseTrackErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_fatal("[coglink:jsmnf-find] Error while trying to find %s field.", !identifier ? "identifier": !isSeekable ? "isSeekable" : !author ? "author" : !length ? "length" : !isStream ? "isStream" : !position ? "position" : !title ? "title" : !uri ? "uri" : !sourceName ? "sourceName" : "???"); - return COGLINK_JSMNF_ERROR_FIND; - } - - snprintf(parsedTrackStruct->identifier, sizeof(parsedTrackStruct->identifier), "%.*s", (int)identifier->v.len, res->body + identifier->v.pos); - snprintf(parsedTrackStruct->isSeekable, sizeof(parsedTrackStruct->isSeekable), "%.*s", (int)isSeekable->v.len, res->body + isSeekable->v.pos); - snprintf(parsedTrackStruct->author, sizeof(parsedTrackStruct->author), "%.*s", (int)author->v.len, res->body + author->v.pos); - snprintf(parsedTrackStruct->length, sizeof(parsedTrackStruct->length), "%.*s", (int)length->v.len, res->body + length->v.pos); - snprintf(parsedTrackStruct->isStream, sizeof(parsedTrackStruct->isStream), "%.*s", (int)isStream->v.len, res->body + isStream->v.pos); - snprintf(parsedTrackStruct->position, sizeof(parsedTrackStruct->position), "%.*s", (int)position->v.len, res->body + position->v.pos); - snprintf(parsedTrackStruct->title, sizeof(parsedTrackStruct->title), "%.*s", (int)title->v.len, res->body + title->v.pos); - if (_coglink_checkParse(lavaInfo, uri, "uri") == COGLINK_SUCCESS) snprintf(parsedTrackStruct->uri, sizeof(parsedTrackStruct->uri), "%.*s", (int)uri->v.len, res->body + uri->v.pos); - if (_coglink_checkParse(lavaInfo, artworkUrl, "artworkUrl") == COGLINK_SUCCESS) snprintf(parsedTrackStruct->artworkUrl, sizeof(parsedTrackStruct->artworkUrl), "%.*s", (int)artworkUrl->v.len, res->body + artworkUrl->v.pos); - if (_coglink_checkParse(lavaInfo, isrc, "isrc") == COGLINK_SUCCESS) snprintf(parsedTrackStruct->isrc, sizeof(parsedTrackStruct->isrc), "%.*s", (int)isrc->v.len, res->body + isrc->v.pos); - snprintf(parsedTrackStruct->sourceName, sizeof(parsedTrackStruct->sourceName), "%.*s", (int)sourceName->v.len, res->body + sourceName->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseTrackSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed song search json, results:\n> identifier: %s\n> isSeekable: %s\n> author: %s\n> length: %s\n> isStream: %s\n> position: %s\n> title: %s\n> uri: %s\n> sourceName: %s", parsedTrackStruct->identifier, parsedTrackStruct->isSeekable, parsedTrackStruct->author, parsedTrackStruct->length, parsedTrackStruct->isStream, parsedTrackStruct->position, parsedTrackStruct->title, parsedTrackStruct->uri, parsedTrackStruct->sourceName); - - return COGLINK_SUCCESS; -} - -void coglink_decodeTrackCleanup(struct coglink_requestInformation *res) { - free(res->body); -} - -int coglink_decodeTracks(struct coglink_lavaInfo *lavaInfo, char *trackArray, long trackArrayLength, struct coglink_requestInformation *res) { - int node = _coglink_selectBestNode(lavaInfo); - - if (lavaInfo->plugins && lavaInfo->plugins->events->onDecodeTracksRequest[0]) { - for (int i = 0;i <+ lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onDecodeTracksRequest[i]) break; - - int pluginResultCode = lavaInfo->plugins->events->onDecodeTracksRequest[i](lavaInfo, trackArray, trackArrayLength, res); - if (pluginResultCode != COGLINK_PROCEED) return pluginResultCode; - } - } - - return _coglink_performRequest(lavaInfo, &lavaInfo->nodes[node], res, - &(struct __coglink_requestConfig) { - .requestType = __COGLINK_POST_REQ, - .path = "/decodetracks", - .pathLength = 14, - .useVPath = 1, - .body = trackArray, - .bodySize = strlen(trackArray), - .getResponse = 1 - }); -} - -int coglink_parseDecodeTracks(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, char *songPos, struct coglink_parsedTrack *parsedTrackStruct) { - if (lavaInfo->plugins && lavaInfo->plugins->events->onDecodeTracksParseRequest[0]) { - for (int i = 0;i <+ lavaInfo->plugins->amount;i++) { - if (!lavaInfo->plugins->events->onDecodeTracksParseRequest[i]) break; - - int pluginResultCode = lavaInfo->plugins->events->onDecodeTracksParseRequest[i](lavaInfo, res, songPos, parsedTrackStruct); - if (pluginResultCode != COGLINK_PROCEED) return pluginResultCode; - } - } - - char *path[] = { songPos, "encoded", NULL }; - jsmnf_pair *encoded = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, encoded, "encoded") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[1] = "info"; - path[2] = "identifier"; - jsmnf_pair *identifier = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, identifier, "identifier") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[2] = "isSeekable"; - jsmnf_pair *isSeekable = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, isSeekable, "isSeekable") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[2] = "author"; - jsmnf_pair *author = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, author, "author") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[2] = "length"; - jsmnf_pair *length = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, length, "length") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[2] = "isStream"; - jsmnf_pair *isStream = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, isStream, "isStream") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[2] = "position"; - jsmnf_pair *position = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, position, "position") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[2] = "title"; - jsmnf_pair *title = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, title, "title") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - path[2] = "uri"; - jsmnf_pair *uri = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - - path[2] = "artworkUrl"; - jsmnf_pair *artworkUrl = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - - path[2] = "isrc"; - jsmnf_pair *isrc = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - - path[2] = "sourceName"; - jsmnf_pair *sourceName = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, sourceName, "sourceName") != COGLINK_SUCCESS) return COGLINK_JSMNF_ERROR_PARSE; - - snprintf(parsedTrackStruct->encoded, sizeof(parsedTrackStruct->encoded), "%.*s", (int)encoded->v.len, res->body + encoded->v.pos); - snprintf(parsedTrackStruct->identifier, sizeof(parsedTrackStruct->identifier), "%.*s", (int)identifier->v.len, res->body + identifier->v.pos); - snprintf(parsedTrackStruct->isSeekable, sizeof(parsedTrackStruct->isSeekable), "%.*s", (int)isSeekable->v.len, res->body + isSeekable->v.pos); - snprintf(parsedTrackStruct->author, sizeof(parsedTrackStruct->author), "%.*s", (int)author->v.len, res->body + author->v.pos); - snprintf(parsedTrackStruct->length, sizeof(parsedTrackStruct->length), "%.*s", (int)length->v.len, res->body + length->v.pos); - snprintf(parsedTrackStruct->isStream, sizeof(parsedTrackStruct->isStream), "%.*s", (int)isStream->v.len, res->body + isStream->v.pos); - snprintf(parsedTrackStruct->position, sizeof(parsedTrackStruct->position), "%.*s", (int)position->v.len, res->body + position->v.pos); - snprintf(parsedTrackStruct->title, sizeof(parsedTrackStruct->title), "%.*s", (int)title->v.len, res->body + title->v.pos); - if (_coglink_checkParse(lavaInfo, uri, "uri") == COGLINK_SUCCESS) snprintf(parsedTrackStruct->uri, sizeof(parsedTrackStruct->uri), "%.*s", (int)uri->v.len, res->body + uri->v.pos); - if (_coglink_checkParse(lavaInfo, artworkUrl, "artworkUrl") == COGLINK_SUCCESS) snprintf(parsedTrackStruct->artworkUrl, sizeof(parsedTrackStruct->artworkUrl), "%.*s", (int)artworkUrl->v.len, res->body + artworkUrl->v.pos); - if (_coglink_checkParse(lavaInfo, isrc, "isrc") == COGLINK_SUCCESS) snprintf(parsedTrackStruct->isrc, sizeof(parsedTrackStruct->isrc), "%.*s", (int)isrc->v.len, res->body + isrc->v.pos); - snprintf(parsedTrackStruct->sourceName, sizeof(parsedTrackStruct->sourceName), "%.*s", (int)sourceName->v.len, res->body + sourceName->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseTrackSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed song search json, results:\n> encoded: %s\n> identifier: %s\n> isSeekable: %s\n> author: %s\n> length: %s\n> isStream: %s\n> position: %s\n> title: %s\n> uri: %s\n> sourceName: %s", parsedTrackStruct->encoded, parsedTrackStruct->identifier, parsedTrackStruct->isSeekable, parsedTrackStruct->author, parsedTrackStruct->length, parsedTrackStruct->isStream, parsedTrackStruct->position, parsedTrackStruct->title, parsedTrackStruct->uri, parsedTrackStruct->sourceName); - - return COGLINK_SUCCESS; -} - -int coglink_parseLoadtype(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, int *loadTypeValue) { - jsmnf_pair *loadType = jsmnf_find(pStruct->pairs, res->body, "loadType", sizeof("loadType") - 1); - if (_coglink_checkParse(lavaInfo, loadType, "loadType") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - char LoadType[16]; - snprintf(LoadType, sizeof(LoadType), "%.*s", (int)loadType->v.len, res->body + loadType->v.pos); - - switch(LoadType[0]) { - case 't': - *loadTypeValue = COGLINK_LOADTYPE_TRACK_LOADED; - break; - case 'p': - *loadTypeValue = COGLINK_LOADTYPE_PLAYLIST_LOADED; - break; - case 's': - *loadTypeValue = COGLINK_LOADTYPE_SEARCH_RESULT; - break; - case 'e': - if (LoadType[1] == 'm') *loadTypeValue = COGLINK_LOADTYPE_EMPTY; - else *loadTypeValue = COGLINK_LOADTYPE_LOAD_FAILED; - break; - default: - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseLoadtypeErrorsDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_error("[coglink:jsmn-find] Failed to recognize loadType."); - *loadTypeValue = COGLINK_JSMNF_ERROR_FIND; - return COGLINK_JSMNF_ERROR_FIND; - break; - } - - return COGLINK_SUCCESS; -} - -int coglink_parseTrack(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, char *songPos, struct coglink_parsedTrack *parsedTrackStruct) { - char *path[] = { "data", songPos, "encoded", NULL }; - jsmnf_pair *encoded = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, encoded, "encoded") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[2] = "info"; - path[3] = "identifier"; - jsmnf_pair *identifier = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, identifier, "identifier") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "isSeekable"; - jsmnf_pair *isSeekable = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, isSeekable, "isSeekable") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "author"; - jsmnf_pair *author = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, author, "author") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "length"; - jsmnf_pair *length = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, length, "length") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "isStream"; - jsmnf_pair *isStream = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, isStream, "isStream") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "position"; - jsmnf_pair *position = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, position, "position") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "title"; - jsmnf_pair *title = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, title, "title") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[3] = "uri"; - jsmnf_pair *uri = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - - path[3] = "artworkUrl"; - jsmnf_pair *artworkUrl = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - - path[3] = "isrc"; - jsmnf_pair *isrc = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - - path[3] = "sourceName"; - jsmnf_pair *sourceName = jsmnf_find_path(pStruct->pairs, res->body, path, 4); - if (_coglink_checkParse(lavaInfo, sourceName, "sourceName") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(parsedTrackStruct->encoded, sizeof(parsedTrackStruct->encoded), "%.*s", (int)encoded->v.len, res->body + encoded->v.pos); - snprintf(parsedTrackStruct->identifier, sizeof(parsedTrackStruct->identifier), "%.*s", (int)identifier->v.len, res->body + identifier->v.pos); - snprintf(parsedTrackStruct->isSeekable, sizeof(parsedTrackStruct->isSeekable), "%.*s", (int)isSeekable->v.len, res->body + isSeekable->v.pos); - snprintf(parsedTrackStruct->author, sizeof(parsedTrackStruct->author), "%.*s", (int)author->v.len, res->body + author->v.pos); - snprintf(parsedTrackStruct->length, sizeof(parsedTrackStruct->length), "%.*s", (int)length->v.len, res->body + length->v.pos); - snprintf(parsedTrackStruct->isStream, sizeof(parsedTrackStruct->isStream), "%.*s", (int)isStream->v.len, res->body + isStream->v.pos); - snprintf(parsedTrackStruct->position, sizeof(parsedTrackStruct->position), "%.*s", (int)position->v.len, res->body + position->v.pos); - snprintf(parsedTrackStruct->title, sizeof(parsedTrackStruct->title), "%.*s", (int)title->v.len, res->body + title->v.pos); - if (_coglink_checkParse(lavaInfo, uri, "uri") == COGLINK_PROCEED) snprintf(parsedTrackStruct->uri, sizeof(parsedTrackStruct->uri), "%.*s", (int)uri->v.len, res->body + uri->v.pos); - if (_coglink_checkParse(lavaInfo, artworkUrl, "artworkUrl") == COGLINK_PROCEED) snprintf(parsedTrackStruct->artworkUrl, sizeof(parsedTrackStruct->artworkUrl), "%.*s", (int)artworkUrl->v.len, res->body + artworkUrl->v.pos); - if (_coglink_checkParse(lavaInfo, isrc, "isrc") == COGLINK_PROCEED) snprintf(parsedTrackStruct->isrc, sizeof(parsedTrackStruct->isrc), "%.*s", (int)isrc->v.len, res->body + isrc->v.pos); - snprintf(parsedTrackStruct->sourceName, sizeof(parsedTrackStruct->sourceName), "%.*s", (int)sourceName->v.len, res->body + sourceName->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseTrackSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed song search json, results:\n> encoded: %s\n> identifier: %s\n> isSeekable: %s\n> author: %s\n> length: %s\n> isStream: %s\n> position: %s\n> title: %s\n> uri: %s\n> sourceName: %s", parsedTrackStruct->encoded, parsedTrackStruct->identifier, parsedTrackStruct->isSeekable, parsedTrackStruct->author, parsedTrackStruct->length, parsedTrackStruct->isStream, parsedTrackStruct->position, parsedTrackStruct->title, parsedTrackStruct->uri, parsedTrackStruct->sourceName); - - return COGLINK_SUCCESS; -} - -int coglink_parsePlaylist(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, struct coglink_parsedPlaylist *parsedPlaylistStruct) { - char *path[] = { "data", "info", "name" }; - jsmnf_pair *name = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, name, "name") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - path[2] = "selectedTrack"; - jsmnf_pair *selectedTrack = jsmnf_find_path(pStruct->pairs, res->body, path, 3); - if (_coglink_checkParse(lavaInfo, selectedTrack, "selectedTrack") != COGLINK_PROCEED) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(parsedPlaylistStruct->name, sizeof(parsedPlaylistStruct->name), "%.*s", (int)name->v.len, res->body + name->v.pos); - snprintf(parsedPlaylistStruct->selectedTrack, sizeof(parsedPlaylistStruct->selectedTrack), "%.*s", (int)selectedTrack->v.len, res->body + selectedTrack->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parsePlaylistSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed playlist search json, results:\n> name: %s\n> selectedTrack: %s", parsedPlaylistStruct->name, parsedPlaylistStruct->selectedTrack); - - return COGLINK_SUCCESS; -} - -int coglink_parseError(struct coglink_lavaInfo *lavaInfo, struct coglink_parsedTrackStruct *pStruct, struct coglink_requestInformation *res, struct coglink_parsedError *parsedErrorStruct) { - char *path[] = { "data", "message" }; - jsmnf_pair *message = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, message, "message") == COGLINK_ERROR) return COGLINK_JSMNF_ERROR_FIND; - - path[1] = "severity"; - jsmnf_pair *severity = jsmnf_find_path(pStruct->pairs, res->body, path, 2); - if (_coglink_checkParse(lavaInfo, severity, "severity") == COGLINK_ERROR) return COGLINK_JSMNF_ERROR_FIND; - - snprintf(parsedErrorStruct->message, sizeof(parsedErrorStruct->message), "%.*s", (int)message->v.len, res->body + message->v.pos); - snprintf(parsedErrorStruct->severity, sizeof(parsedErrorStruct->severity), "%.*s", (int)severity->v.len, res->body + severity->v.pos); - - if (lavaInfo->debugging->allDebugging || lavaInfo->debugging->parseSuccessDebugging || lavaInfo->debugging->jsmnfErrorsDebugging) log_debug("[coglink:jsmn-find] Parsed error search json, results:\n> message: %s\n> severity: %s", parsedErrorStruct->message, parsedErrorStruct->severity); - - return COGLINK_SUCCESS; -} diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..c9c14bc --- /dev/null +++ b/lib/utils.c @@ -0,0 +1,85 @@ +#include +#include + +#include + +#include "websocket.h" + +#include "utils.h" + +size_t _coglink_write_cb(void *data, size_t size, size_t nmemb, void *userp) { + size_t write_size = size * nmemb; + struct coglink_response *mem = userp; + + char *ptr = realloc(mem->body, mem->size + write_size + 1); + if (!ptr) FATAL("[coglink:libcurl] Not enough memory to realloc.\n"); + + mem->body = ptr; + memcpy(&(mem->body[mem->size]), data, write_size); + mem->size += write_size; + mem->body[mem->size] = 0; + + return write_size; +} + +int _coglink_perform_request(struct coglink_node *nodeInfo, struct coglink_request_params *req, struct coglink_response *res) { + CURL *curl = curl_easy_init(); + + size_t url_size = (nodeInfo->ssl ? 1 : 0) + (sizeof("http://:/v4") - 1) + strlen(nodeInfo->hostname) + 4 + strlen(req->endpoint); + char *full_url = malloc(url_size + 1); + url_size = snprintf(full_url, url_size + 1, "http%s://%s:%d/v4%s", nodeInfo->ssl ? "s" : "", nodeInfo->hostname, nodeInfo->port, req->endpoint); + + CURLcode c_res = curl_easy_setopt(curl, CURLOPT_URL, full_url); + + struct curl_slist *chunk = NULL; + char *authorization = NULL; + + if (nodeInfo->password) { + authorization = malloc(17 + strlen(nodeInfo->password) + 1); + snprintf(authorization, 17 + strlen(nodeInfo->password) + 1, "Authorization: %s", nodeInfo->password); + + chunk = curl_slist_append(chunk, authorization); + } + + if (req->body) { + chunk = curl_slist_append(chunk, "Content-Type: application/json"); + + c_res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->body); + c_res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->body_length); + } + + chunk = curl_slist_append(chunk, "User-Agent: libcurl"); + + c_res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); + c_res = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, req->method); + + /* NodeLink compression compliance, useless while using LavaLink without spring boot changes */ + c_res = curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); + + if (req->get_response) { + (*res).body = malloc(1); + (*res).size = 0; + + c_res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _coglink_write_cb); + c_res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)res); + } else { + /* todo: Is it necessary? */ + // c_res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, __coglink_WriteMemoryCallbackNoSave); + } + + c_res = curl_easy_perform(curl); + if (c_res != CURLE_OK) { + /* todo: return int errors over crashes */ + FATAL("[coglink:libcurl] curl_easy_perform failed: %s", curl_easy_strerror(c_res)); + } + + curl_easy_cleanup(curl); + curl_slist_free_all(chunk); + free(authorization); + free(full_url); + + DEBUG("[coglink:libcurl] Performed request successfully."); + + return COGLINK_SUCCESS; +} + diff --git a/lib/websocket.c b/lib/websocket.c new file mode 100644 index 0000000..85bcf60 --- /dev/null +++ b/lib/websocket.c @@ -0,0 +1,246 @@ +#include +#include + +#include +#include +#include +#include + +#include "tablec.h" +#include "utils.h" +#include "codecs.h" +#include "lavalink.h" +#include "events.h" +#include "rest.h" + +#include "websocket.h" + +struct tablec_ht coglink_hashtable; + +int _IO_poller(struct io_poller *io, CURLM *multi, void *data) { + (void) io; (void) multi; + struct coglink_node *node = data; + + return !ws_multi_socket_run(node->ws, &node->tstamp) ? CCORD_DISCORD_CONNECTION : CCORD_OK; +} + +void _ws_on_close(void *data, struct websockets *ws, struct ws_info *info, enum ws_close_reason wscode, const char *reason, size_t length) { + (void)ws; (void)info; (void)length; + + struct _coglink_websocket_data *c_info = data; + + if (c_info->c_client->events->onClose == NULL) return; + + c_info->c_client->events->onClose(wscode, reason); +} + +void _ws_on_text(void *data, struct websockets *ws, struct ws_info *info, const char *text, size_t length) { + (void) ws; (void) info; + struct _coglink_websocket_data *c_info = data; + + if (c_info->c_client->events->onRaw && c_info->c_client->events->onRaw(c_info->c_client, text, length) != COGLINK_PROCEED) return; + + int type; + void *payload = coglink_parse_websocket_data(&type, text, length); + + switch (type) { + case COGLINK_READY: { + c_info->c_client->nodes->array[c_info->node_id].session_id = ((struct coglink_ready_payload *)payload)->session_id; + + if (c_info->c_client->events->onReady) c_info->c_client->events->onReady((struct coglink_ready_payload *)payload); + + break; + } + case COGLINK_PLAYER_UPDATE: { + if (c_info->c_client->events->onPlayerUpdate) c_info->c_client->events->onPlayerUpdate((struct coglink_player_update_payload *)payload); + + break; + } + case COGLINK_STATS: { + if (c_info->c_client->events->onStats) c_info->c_client->events->onStats((struct coglink_stats_payload *)payload); + + break; + } + case COGLINK_PARSE_ERROR: { + break; + } + } +} + +/* todo: replace for direct events (?) */ +enum discord_event_scheduler _coglink_handle_scheduler(struct discord *client, const char data[], size_t length, enum discord_gateway_events event) { + struct coglink_client *c_client = discord_get_data(client); + + switch (event) { + case DISCORD_EV_VOICE_STATE_UPDATE: { + struct coglink_voice_state *voice_state = coglink_parse_voice_state(data, length); + + if (voice_state->channel_id) { + if (c_client->bot_id == voice_state->user_id) { + struct coglink_player *player = coglink_get_player(c_client, voice_state->guild_id); + + if (player == NULL) return DISCORD_EVENT_MAIN_THREAD; + + player->voice_data = malloc(sizeof(struct coglink_voice_data)); + player->voice_data->session_id = voice_state->session_id; + } else { + size_t i = 0; + + while (c_client->users->size != i) { + if (c_client->users->array[i].id == 0) { + c_client->users->array[i].channel_id = voice_state->channel_id; + + return DISCORD_EVENT_MAIN_THREAD; + } + + i++; + } + + c_client->users->array = realloc(c_client->users->array, (c_client->users->size + 1) * sizeof(struct coglink_user)); + c_client->users->array[c_client->users->size].id = voice_state->user_id; + c_client->users->array[c_client->users->size].channel_id = voice_state->channel_id; + c_client->users->size++; + + return DISCORD_EVENT_IGNORE; + } + } else { + size_t i = 0; + + while (c_client->users->size > i) { + if (c_client->users->array[i].id == voice_state->user_id) { + c_client->users->array[i].id = 0; + c_client->users->array[i].channel_id = 0; + + return DISCORD_EVENT_IGNORE; + } + + i++; + } + + return DISCORD_EVENT_IGNORE; + } + + break; + } + case DISCORD_EV_VOICE_SERVER_UPDATE: { + struct coglink_voice_server_update *voice_server_update = coglink_parse_voice_server_update(data, length); + + struct coglink_player *player = coglink_get_player(c_client, voice_server_update->guild_id); + + if (player == NULL) { + DEBUG("[coglink] Player not found for guild %" PRIu64 "", voice_server_update->guild_id); + + free(voice_server_update->endpoint); + free(voice_server_update->token); + free(voice_server_update); + + return DISCORD_EVENT_IGNORE; + } + + struct coglink_node *node = &c_client->nodes->array[player->node]; + + char url_path[(sizeof("/sessions//players/") - 1) + 16 + 19 + 1]; + snprintf(url_path, sizeof(url_path), "/sessions/%s/players/%" PRIu64 "", node->session_id, voice_server_update->guild_id); + + /* todo: is that redudancy needed for readability? */ + size_t payload_size = ( + (sizeof("{" + "\"voice\":{" + "\"token\":\"\"," + "\"endpoint\":\"\"," + "\"sessionId\":\"\"" + "}" + "}") - 1) + strlen(voice_server_update->token) + strlen(voice_server_update->endpoint) + strlen(player->voice_data->session_id) + 1); + char *payload = malloc(payload_size * sizeof(char)); + snprintf(payload, payload_size, + "{" + "\"voice\":{" + "\"token\":\"%s\"," + "\"endpoint\":\"%s\"," + "\"sessionId\":\"%s\"" + "}" + "}", voice_server_update->token, voice_server_update->endpoint, player->voice_data->session_id); + + _coglink_perform_request(node, &(struct coglink_request_params) { + .endpoint = url_path, + .method = "PATCH", + .body = payload, + .body_length = payload_size - 1, + .get_response = false + }, NULL); + + free(payload); + + free(voice_server_update->endpoint); + free(voice_server_update->token); + free(voice_server_update); + + break; + } + default: { + return DISCORD_EVENT_MAIN_THREAD; + } + } + + return DISCORD_EVENT_MAIN_THREAD; +} + +int coglink_connect_nodes(struct coglink_client *c_client, struct discord *client, struct coglink_nodes *nodes) { + /* Initialize TableC */ + tablec_init(&coglink_hashtable, 16); + + /* libcurl should be already initialized by Concord */ + + c_client->nodes = nodes; + c_client->players = malloc(sizeof(struct coglink_players)); + c_client->players->array = malloc(sizeof(struct coglink_player)); + c_client->players->size = 0; + + c_client->users = malloc(sizeof(struct coglink_users)); + c_client->users->array = malloc(sizeof(struct coglink_user)); + c_client->users->size = 0; + + char bot_id_str[32]; + snprintf(bot_id_str, sizeof(bot_id_str), "%" PRIu64, c_client->bot_id); + + for (size_t i = 0;i <= nodes->size - 1;i++) { + struct coglink_node *node_info = &c_client->nodes->array[i]; + + char hostname[128 + 19 + 4 + 1]; + snprintf(hostname, sizeof(hostname), "ws%s://%s%s%d/v4/websocket", node_info->ssl ? "s" : "", node_info->hostname, node_info->port ? ":" : "", node_info->port); + + node_info->mhandle = curl_multi_init(); + node_info->tstamp = 0; + + struct _coglink_websocket_data *c_info = malloc(sizeof(struct _coglink_websocket_data)); + c_info->c_client = c_client; + c_info->node_id = i; + + struct ws_callbacks callbacks = { + .data = c_info, + // .on_connect = _ws_on_connect, + .on_text = _ws_on_text, + .on_close = _ws_on_close + }; + + nodes->array[i].ws = ws_init(&callbacks, client->gw.mhandle, NULL); + + ws_set_url(node_info->ws, hostname, NULL); + ws_start(node_info->ws); + + if (c_client->allow_resuming && c_client->nodes != NULL) ws_add_header(node_info->ws, "Session-Id", node_info->session_id); + ws_add_header(node_info->ws, "Authorization", node_info->password); + ws_add_header(node_info->ws, "Num-Shards", c_client->num_shards); + ws_add_header(node_info->ws, "User-Id", bot_id_str); + /* NodeLink/FrequenC Client-Name format */ + ws_add_header(node_info->ws, "Client-Name", "Coglink/3.0.0 (https://github.com/PerformanC/CogLink)"); + ws_add_header(node_info->ws, "Sec-WebSocket-Protocol", "13"); /* If not set, will be undefined */ + + io_poller_curlm_add(client->io_poller, node_info->mhandle, _IO_poller, node_info); + } + + discord_set_data(client, c_client); + discord_set_event_scheduler(client, _coglink_handle_scheduler); + + return COGLINK_SUCCESS; +}