Design Principles

Plugins

Discord++'s plugin system brings two major pros: flexible dependencies and optional addons, both without modifying the core library code.

Mixins

Mixins allow a given function can be overridden multiple times, e.g. doCall is implemented in the REST module but it's also overridden by Plugin: RateLimit to stop calls from going out too fast for the API's liking.

Mixins play nicely with IntelliSense tools, e.g. Plugin: Endpoints can add methods like createMessage and deleteChannel without needing to do something weird like looking up std::function(void*)

Additional Reading:

Submodules

Submodules are used to grab a defined version of plugins but can be a bit clunky. In the future I plan to move to CMake's FetchContent or a wrapper of it like CPM.

Build-A-Bot

Setting up a project involves adding plugins to two places in CMakeLists.txt, two places in include.hh, and extern.cc. As such, I created Build-A-Bot as a handy Bash script to automated the initial process. It makes use of select to provide a handy menu system for picking plugins and branches and configures the bot to use them.

Calls

It used to be that to call an API endpoint you called one function with the form of call("GET", "/url/", {payload}, [](){}, [](json){}). This wasn't very extensible- it required a ton of definitions for the different numbers of parameters with/without shared pointers, it would've been really difficult to shoehorn in file uploading, and deriving it for endpoint-specific calls would have been incredibly complicated.

Parametric Macro Class Construction

To define a Call class you define the macros Bot (The class the Call is inside), Parent or BASECALL (The parent of the Call or BASECALL if it's the base Call class), function (a list of methods to call in the Bot to create Call objects), and Fields (a list of macros to define the fields of the Call). You then #include "macros/defineCallOpen.hh" and #include "macros/defineCallClose.hh" with any custom method definitions in between. This consumes the parameter macros so that they don't affect any of the following code or you can start again with a new class.

Fluent Parameters

Fluent design was the method I settled on for defining calls to minimize code surface without decreasing usability. the base Call object now takes a simple std::string body with conversion from a json payload to the std::string handled by the derived class JsonCall. There's also the new addition of FileCall, derived from JsonCall. FileCall adds the file, file_type and file_name parameters needed to upload a file.

The fluent structure of calls also allow for Plugin: Endpoints which adds derivations of the 3 basic Call classes that cover up the base method, target, and payload fields with specific parameters like channel_id and content.