1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00

Add D++ library.

This commit is contained in:
Sandu Liviu Catalin 2023-03-23 20:20:44 +02:00
parent b08a024298
commit cbd8f8b028
568 changed files with 131250 additions and 0 deletions

93
vendor/DPP/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,93 @@
#
# D++ (DPP), The Lightweight C++ Discord Library
#
# Copyright 2021 Craig Edwards <support@brainbox.cc>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
cmake_minimum_required (VERSION 3.16)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(BUILD_VOICE_SUPPORT "Build voice support" ON)
option(RUN_LDCONFIG "Run ldconfig after installation" ON)
option(DPP_INSTALL "Generate the install target" ON)
option(DPP_BUILD_TEST "Build the test program" ON)
option(DPP_NO_VCPKG "No VCPKG" OFF)
option(DPP_CORO "Experimental support for C++20 coroutines" OFF)
option(DPP_USE_EXTERNAL_JSON "Use an external installation of nlohmann::json" OFF)
include(CheckCXXSymbolExists)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_definitions(DPP_BUILD)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/dpp/version.h" version_h)
if(NOT version_h MATCHES "DPP_VERSION_SHORT ([0-9][0-9])([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get DPP_VERSION_SHORT from version.h")
endif()
math(EXPR DPP_VERSION_MAJOR "${CMAKE_MATCH_1}")
math(EXPR DPP_VERSION_MINOR "${CMAKE_MATCH_2}")
math(EXPR DPP_VERSION_PATCH "${CMAKE_MATCH_3}")
string(CONCAT DPP_VERSION "${DPP_VERSION_MAJOR}.${DPP_VERSION_MINOR}.${DPP_VERSION_PATCH}")
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_SOURCE_DIR}/cmake/")
if (DPP_NO_VCPKG)
message("-- INFO: Explicitly disabling VCPKG as running inside the CI action.")
else()
message("-- INFO: Using VCPKG if detected")
endif()
if (WIN32 AND NOT MINGW AND BUILD_SHARED_LIBS)
message("-- INFO: Configuring .rc resource script")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/dpp/dpp.rc.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/dpp/dpp.rc" NEWLINE_STYLE WIN32)
endif()
if (NOT DPP_NO_VCPKG AND EXISTS "${_VCPKG_ROOT_DIR}")
set(PROJECT_NAME "dpp")
project(
"${PROJECT_NAME}"
VERSION "${DPP_VERSION}"
LANGUAGES CXX
HOMEPAGE_URL "https://dpp.dev/"
DESCRIPTION "An incredibly lightweight C++ Discord library."
)
add_subdirectory(library-vcpkg)
else()
set(PROJECT_NAME "libdpp")
project(
"${PROJECT_NAME}"
VERSION "${DPP_VERSION}"
LANGUAGES CXX
HOMEPAGE_URL "https://dpp.dev/"
DESCRIPTION "An incredibly lightweight C++ Discord library."
)
add_subdirectory(library)
endif()
if(DPP_USE_EXTERNAL_JSON)
# We do nothing here, we just assume it is on the include path.
# nlohmann::json's cmake stuff does all kinds of weird, and is more hassle than it's worth.
# This functionality is here mostly for package maintainers so if you enable it you should
# know what you are doing.
message("-- Using external nlohmann::json")
target_compile_definitions(dpp PUBLIC DPP_USE_EXTERNAL_JSON)
else()
# Add the nlohmann single include to the include path. Note that nlohmann::json is kinda
# fussy, this is an older version because trying to use v3.11.2 gave a bunch of parse errors
# that made no sense, it seems they may have changed their parsing rules somehow.
message("-- Using bundled nlohmann::json")
endif()

15
vendor/DPP/CMakeSettings.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
}
]
}

134
vendor/DPP/CODE_OF_CONDUCT.md vendored Normal file
View File

@ -0,0 +1,134 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement in the
[Discord][discord] server.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[discord]: https://discord.gg/dpp
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

20
vendor/DPP/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,20 @@
# Contributing
When contributing to this repository, please do not feel intimidated! We welcome PRs from developers of all levels of experience and we were all new once.
## Pull Request Process
1. Pull requests should be made against the `dev` branch.
2. Ensure that the changed library can be built on your target system. Do not introduce any platform-
specific code.
3. Ensure that all methods and functions you add are **fully documented** using doxygen style comments.
4. Test your commit! Make a simple single-file test bot to demonstrate the change, include this with the PR
as an attached file on a comment, so we can test and see how it works.
5. Ensure that you do not break any existing API calls without discussing on Discord first!
6. Be sure to follow the coding style guide (if you are not sure, match the code style of existing files
including indent style etc).
7. Your PR must pass the CI actions before being allowed to be merged. Our PR actions check that the
build will compile on various platforms before release and makes precompiled versions of the library.
8. If you are on the discord server for the project and your PR is accepted, let a moderator know and we
will grant you the 'Contributors' role.

14
vendor/DPP/Dockerfile vendored Normal file
View File

@ -0,0 +1,14 @@
FROM ubuntu:focal
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install --no-install-recommends -y libssl-dev zlib1g-dev libsodium-dev libopus-dev cmake pkg-config g++ gcc git make && apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /usr/src/DPP
COPY . .
WORKDIR /usr/src/DPP/build
RUN cmake .. -DDPP_BUILD_TEST=OFF
RUN make -j "$(nproc)"
RUN make install

2284
vendor/DPP/Doxyfile vendored Normal file

File diff suppressed because it is too large Load Diff

201
vendor/DPP/LICENSE vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2021 Craig Edwards and D++ Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

134
vendor/DPP/README.md vendored Normal file
View File

@ -0,0 +1,134 @@
<div align="center"><img src="docpages/DPP-markdown-logo.png" alt="DPP"/>
<h3>An incredibly lightweight C++ Discord library</h3>
<br />
[![Discord](https://img.shields.io/discord/825407338755653642?style=flat)](https://discord.gg/dpp)
![Downloads](https://dl.dpp.dev/dlcount.php)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/39b054c38bba411d9b25b39524016c9e)](https://www.codacy.com/gh/brainboxdotcc/DPP/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=brainboxdotcc/DPP&amp;utm_campaign=Badge_Grade)
![Lines of code](https://img.shields.io/tokei/lines/github/brainboxdotcc/DPP)
[![D++ CI](https://github.com/brainboxdotcc/DPP/actions/workflows/ci.yml/badge.svg)](https://github.com/brainboxdotcc/DPP/actions/workflows/ci.yml)
[![AUR version](https://img.shields.io/aur/version/dpp)](https://aur.archlinux.org/packages/dpp) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
</div>
<hr />
D++ is a lightweight and efficient library for **Discord** written in **modern C++**, covering as much of the API specification as possible with an **incredibly small memory footprint** even when caching large amounts of data.
### Library features:
* Support for Discord API v10
* Really small memory footprint
* Efficient caching system for guilds, channels, guild members, roles, users
* Sharding and clustering (Many shards, one process: specify the number of shards, or let the library decide)
* Highly optimised ETF (Erlang Term Format) support for very fast websocket throughput
* [Slash Commands/Interactions support](https://dpp.dev/slashcommands.html)
* [Voice support](https://dpp.dev/soundboard.html) (sending **and** receiving audio)
* The entire Discord API is available for use in the library
* Stable [Windows support](https://dpp.dev/buildwindows.html)
* Ready-made compiled packages for Windows, Raspberry Pi (ARM64/ARM7/ARMv6), Debian x86/x64 and RPM based distributions
* Highly scalable for large amounts of guilds and users
Want to help? Drop me a line or send a PR.
This library is in use on [TriviaBot](https://triviabot.co.uk/) and [Sporks bot](https://sporks.gg) and many other bots!
## Documentation
The documentation is constantly evolving and improving, generated from the code comments and markdown examples using Doxygen.
#### [View the D++ library documentation](https://dpp.dev/)
### Example
This is a simple ping-pong example using slash commands.
```c++
#include <dpp/dpp.h>
#include <cstdlib>
int main() {
dpp::cluster bot(std::getenv("BOT_TOKEN"));
bot.on_slashcommand([](auto event) {
if (event.command.get_command_name() == "ping") {
event.reply("Pong!");
}
});
bot.on_ready([&bot](auto event) {
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_command_create(
dpp::slashcommand("ping", "Ping pong!", bot.me.id)
);
}
});
bot.start(dpp::st_wait);
}
```
You can find more examples in our [example page](https://dpp.dev/md_docpages_03_example_programs.html).
## Supported Systems
### Linux
The library runs ideally on **Linux**.
### Mac OS X and FreeBSD
The library is well-functional and stable on **Mac OS X** and **FreeBSD** too.
### Raspberry Pi
For running your bot on a **Raspberry Pi**, we offer a prebuilt .deb package for ARM64, ARM6, and ARM7 so that you do not have to wait for it to compile.
### Windows
**Windows** is well-supported with ready-made compiled DLL and LIB files, please check out our [Windows Bot Template repository](https://github.com/brainboxdotcc/windows-bot-template). The Windows Bot repository can be cloned and integrated immediately into any Visual Studio 2019 and 2022 project in a matter of minutes.
### Other OS
The library should work fine on other operating systems as well, and if you run a D++ bot on something not listed here, please let us know!
## 🤝 Contributing
Contributions, issues and feature requests are welcome. After cloning and setting up project locally, you can just submit
a PR to this repo and it will be deployed once it's accepted.
Please read the [D++ Code Style Guide](https://dpp.dev/coding-standards.html) for more information on how we format pull requests.
## 💬 Get in touch
If you have various suggestions, questions or want to discuss things with our community, [Join our discord server](https://discord.gg/dpp)!
Make a humorous reference to brains in your nickname to get access to a secret brain cult channel! :)
[![Discord](https://img.shields.io/discord/825407338755653642?style=flat)](https://discord.gg/dpp)
## Show your support
We love people's support in growing and improving. Be sure to leave a ⭐️ if you like the project and also be sure to contribute, if you're interested!
## Dependencies
### Build requirements
* [cmake](https://cmake.org/) (version 3.13+)
* A supported C++ compiler from the list below
### Supported compilers
* [g++](https://gcc.gnu.org) (version 8 or higher)
* [clang](https://clang.llvm.org/)
* AppleClang (12.0 or higher)
* Microsoft Visual Studio 2019 or 2022 (16.x/17.x)
* [mingw-w64](https://www.mingw-w64.org/) (gcc version 8 or higher)
Other compilers may work (either newer versions of those listed above, or different compilers entirely) but have not been tested by us.
### External Dependencies (You must install these)
* [OpenSSL](https://openssl.org/) (whichever `-dev` package comes with your OS)
* [zlib](https://zlib.net) (whichever `-dev` package comes with your OS)
#### Optional Dependencies
For voice support you require both of:
* [LibOpus](https://www.opus-codec.org)
* [libsodium](https://github.com/jedisct1/libsodium)
### Included Dependencies (Packaged with the library)
* [nlohmann::json](https://github.com/nlohmann/json)

14
vendor/DPP/SECURITY.md vendored Normal file
View File

@ -0,0 +1,14 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 10.0.x | ✅ |
| 9.0.x | ⛔ |
| 1.0.x | ⛔ |
## Reporting a Vulnerability
To report a vulnerability please contact the development team confidentially on Discord. We will discuss via a group chat if necessary and action
the fix. Once the fix has been put into a release version, the vulnerability will be made public.

139
vendor/DPP/buildtools/changelog.php vendored Normal file
View File

@ -0,0 +1,139 @@
<?php
// D++ changelog generator, saves 15 minutes for each release :-)
// Pattern list
$categories = [
'break' => '💣 Breaking Changes',
'breaking' => '💣 Breaking Changes',
'feat' => '✨ New Features',
'feature' => '✨ New Features',
'add' => '✨ New Features',
'added' => '✨ New Features',
'fix' => '🐞 Bug Fixes',
'bug' => '🐞 Bug Fixes',
'bugfix' => '🐞 Bug Fixes',
'fixed' => '🐞 Bug Fixes',
'fixes' => '🐞 Bug Fixes',
'perf' => '🚀 Performance Improvements',
'performance' => '🚀 Performance Improvements',
'impro' => '♻️ Refactoring',
'improved' => '♻️ Refactoring',
'improvement' => '♻️ Refactoring',
'refactor' => '♻️ Refactoring',
'refactored' => '♻️ Refactoring',
'deprecated' => '♻️ Refactoring',
'deprecate' => '♻️ Refactoring',
'remove' => '♻️ Refactoring',
'change' => '♻️ Refactoring',
'changed' => '♻️ Refactoring',
'test' => '🚨 Testing',
'tests' => '🚨 Testing',
'testing' => '🚨 Testing',
'ci' => '👷 Build/CI',
'build' => '👷 Build/CI',
'docs' => '📚 Documentation',
'documentation' => '📚 Documentation',
'style' => '💎 Style Changes',
'chore' => '🔧 Chore',
'misc' => '📜 Miscellaneous Changes',
'update' => '📜 Miscellaneous Changes',
'updated' => '📜 Miscellaneous Changes',
];
$catgroup = [];
$changelog = [];
$githubstyle = true;
if (count($argv) > 2 && $argv[1] == '--discord') {
$githubstyle = false;
}
// Magic sauce
exec("git log --format=\"%s\" $(git log --no-walk --tags | head -n1 | cut -d ' ' -f 2)..HEAD | grep -v '^Merge '", $changelog);
// Leadin
if ($githubstyle) {
echo "The changelog is listed below:\n\nRelease Changelog\n===========\n";
} else {
echo "The changelog is listed below:\n\n__**Release Changelog**__\n";
}
// Case insensitive removal of duplicates
$changelog = array_intersect_key($changelog, array_unique(array_map("strtolower", $changelog)));
// remove duplicates where two entries are the same but one ends with a GitHub pull request link
foreach ($changelog as $item) {
$entryWithoutPrLink = preg_replace('/( \(#\d+\))$/', '', $item);
if ($entryWithoutPrLink === $item) {
continue;
}
// if $item ends with (#123)
foreach ($changelog as $key => $change) {
if ($entryWithoutPrLink === $change) {
unset($changelog[$key]);
break;
}
}
}
foreach ($changelog as $change) {
// Wrap anything that looks like a symbol name in backticks
$change = preg_replace('/([a-zA-Z][\w_\/\-]+\.\w+|\S+\(\)|\w+::\w+|dpp::\w+|utility::\w+|(\w+_\w+)+)/', '`$1`', $change);
$change = preg_replace("/vs(\d+)/", "Visual Studio $1", $change);
$change = preg_replace("/\bfaq\b/", "FAQ", $change);
$change = preg_replace("/\bdiscord\b/", "Discord", $change);
$change = preg_replace("/\bmicrosoft\b/", "Microsoft", $change);
$change = preg_replace("/\bwindows\b/", "Windows", $change);
$change = preg_replace("/\blinux\b/", "Linux", $change);
$change = preg_replace("/\sarm(\d+)\s/i", ' ARM$1 ', $change);
$change = preg_replace("/\b(was|is|wo)nt\b/i", '$1n\'t', $change);
$change = preg_replace("/\bfreebsd\b/", 'FreeBSD', $change);
$change = preg_replace("/``/", "`", $change);
// Match keywords against categories
$matched = false;
foreach ($categories as $cat => $header) {
// Purposefully ignored: comments that are one word, merge commits, and version bumps
if (strpos($change, ' ') === false || preg_match("/^Merge (branch|pull request|remote-tracking branch) /", $change) || preg_match("/version bump/i", $change)) {
$matched = true;
continue;
}
// Groupings
if ((preg_match("/^" . $cat . ":/i", $change)) || (preg_match("/^\[" . $cat . "\//i", $change)) || (preg_match("/^\[" . $cat . "\]/i", $change)) || (preg_match("/^\[" . $cat . ":/i", $change)) || (preg_match("/^" . $cat . "\//i", $change)) || (preg_match("/^" . $cat . ":/i", $change))) {
if (!isset($catgroup[$header])) {
$catgroup[$header] = [];
}
$matched = true;
$catgroup[$header][] = preg_replace("/^\S+\s+/", "", $change);
break;
} else if (preg_match("/^" . $cat . " /i", $change)) {
if (!isset($catgroup[$header])) {
$catgroup[$header] = [];
}
$matched = true;
$catgroup[$header][] = $change;
break;
}
}
}
// Output tidy formatting
foreach ($catgroup as $cat => $list) {
echo "\n" . ($githubstyle ? '## ' : '__**') . $cat . ($githubstyle ? '' : '**__') . "\n";
foreach ($list as $item) {
// Exclude bad commit messages like 'typo fix', 'test push' etc by pattern
if (!preg_match("/^(typo|test|fix)\s\w+$/", $item) && strpos($item, ' ') !== false) {
echo ($githubstyle ? '-' : '•') . ' ' . ucfirst(str_replace('@', '', $item)) . "\n";
}
}
}
// Leadout
echo "\n\n**Thank you for using D++!**\n\n";
if (!$githubstyle) {
$version = $argv[2];
echo 'The ' . $version . ' download can be found here: <https://dl.dpp.dev/' . $version . '>';
echo "\n";
}

View File

@ -0,0 +1,131 @@
<?php
namespace Dpp\Generator;
use Dpp\StructGeneratorInterface;
/**
* Generate header and .cpp file for coroutine calls (starting with 'co_')
*/
class CoroGenerator implements StructGeneratorInterface
{
/**
* @inheritDoc
*/
public function generateHeaderStart(): string
{
return <<<EOT
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2022 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
/* Auto @generated by buildtools/make_coro_struct.php.
*
* DO NOT EDIT BY HAND!
*
* To re-generate this header file re-run the script!
*/
EOT;
}
/**
* @inheritDoc
*/
public function generateCppStart(): string
{
return $this->generateHeaderStart() . <<<EOT
#include <dpp/export.h>
#include <dpp/snowflake.h>
#include <dpp/cluster.h>
#include <dpp/coro.h>
namespace dpp {
EOT;
}
/**
* @inheritDoc
*/
public function checkForChanges(): bool
{
/* Check if we need to re-generate by comparing modification times */
$us = file_exists('include/dpp/cluster_coro_calls.h') ? filemtime('include/dpp/cluster_coro_calls.h') : 0;
$them = filemtime('include/dpp/cluster.h');
if ($them <= $us) {
echo "-- No change required.\n";
return false;
}
echo "-- Autogenerating include/dpp/cluster_coro_calls.h\n";
return true;
}
/**
* @inheritDoc
*/
public function generateHeaderDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterNames): string
{
$parameterNames = preg_replace('/^, /', '', $parameterNames);
if (!empty($parameterNames)) {
$parameterNames .= ', ';
}
return "auto inline co_{$currentFunction}($noDefaults) {\n\treturn dpp::awaitable(this, [&] (auto cc) { this->$currentFunction({$parameterNames}cc); }); \n}\n\n";
}
/**
* @inheritDoc
*/
public function generateCppDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterNames): string
{
return '';
}
/**
* @inheritDoc
*/
public function getCommentArray(): array
{
return [" * \memberof dpp::cluster"];
}
/**
* @inheritDoc
*/
public function saveHeader(string $content): void
{
$content .= "auto inline co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap<std::string, std::string> &headers = {}) {\n\treturn dpp::awaitable(this, [&] (auto cc) { this->request(url, method, cc, mimetype, headers); }); \n}\n\n";
file_put_contents('include/dpp/cluster_coro_calls.h', $content);
}
/**
* @inheritDoc
*/
public function saveCpp(string $cppcontent): void
{
/* No cpp file to save, code is all inline */
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace Dpp\Generator;
use Dpp\StructGeneratorInterface;
/**
* Generate header and .cpp file for synchronous calls (ending in '_sync')
*/
class SyncGenerator implements StructGeneratorInterface
{
/**
* @inheritDoc
*/
public function generateHeaderStart(): string
{
return <<<EOT
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2022 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
/* Auto @generated by buildtools/make_sync_struct.php.
*
* DO NOT EDIT BY HAND!
*
* To re-generate this header file re-run the script!
*/
EOT;
}
/**
* @inheritDoc
*/
public function generateCppStart(): string
{
return $this->generateHeaderStart() . <<<EOT
#include <dpp/export.h>
#include <dpp/snowflake.h>
#include <dpp/cluster.h>
namespace dpp {
EOT;
}
/**
* @inheritDoc
*/
public function checkForChanges(): bool
{
/* Check if we need to re-generate by comparing modification times */
$us = file_exists('include/dpp/cluster_sync_calls.h') ? filemtime('include/dpp/cluster_sync_calls.h') : 0;
$them = filemtime('include/dpp/cluster.h');
if ($them <= $us) {
echo "-- No change required.\n";
return false;
}
echo "-- Autogenerating include/dpp/cluster_sync_calls.h\n";
echo "-- Autogenerating src/dpp/cluster_sync_calls.cpp\n";
return true;
}
/**
* @inheritDoc
*/
public function generateHeaderDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterNames): string
{
return "$returnType {$currentFunction}_sync($parameters);\n\n";
}
/**
* @inheritDoc
*/
public function generateCppDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterNames): string
{
return "$returnType cluster::{$currentFunction}_sync($noDefaults) {\n\treturn dpp::sync<$returnType>(this, &cluster::$currentFunction$parameterNames);\n}\n\n";
}
/**
* @inheritDoc
*/
public function getCommentArray(): array
{
return [
" * \memberof dpp::cluster",
" * @throw dpp::rest_exception upon failure to execute REST function",
" * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.",
" * Avoid direct use of this function inside an event handler.",
];
}
/**
* @inheritDoc
*/
public function saveHeader(string $content): void
{
file_put_contents('include/dpp/cluster_sync_calls.h', $content);
}
/**
* @inheritDoc
*/
public function saveCpp(string $cppcontent): void
{
file_put_contents('src/dpp/cluster_sync_calls.cpp', $cppcontent);
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace Dpp;
/**
* Represents a header/cpp generator used to auto-generate cpp/.h files.
*/
interface StructGeneratorInterface
{
/**
* Generate the start of the header file
*
* @return string header content
*/
public function generateHeaderStart(): string;
/**
* Generate the start of the cpp file
*
* @return string cpp content
*/
public function generateCppStart(): string;
/**
* Check if the script should run and re-generate content or not
*
* @return string true if the script should run, false to exit
*/
public function checkForchanges(): bool;
/**
* Generate header definition for a function
*
* @param string $returnType Return type of function
* @param string $currentFunction Current function name
* @param string $parameters Current function parameters with default values
* @param string $noDefaults Current function parameters without default values
* @param string $parameterNames Parameter names only
* @return string header content to append
*/
public function generateHeaderDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterNames): string;
/**
* Generate cpp definition for a function
*
* @param string $returnType Return type of function
* @param string $currentFunction Current function name
* @param string $parameters Current function parameters with default values
* @param string $noDefaults Current function parameters without default values
* @param string $parameterNames Parameter names only
* @return string cpp content to append
*/
public function generateCppDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterNames): string;
/**
* Return comment lines to add to each header definition
*
* @return array Comment lines to add
*/
public function getCommentArray(): array;
/**
* Save the .h file
*
* @param string $content Content to save
*/
public function saveHeader(string $content): void;
/**
* Save the .cpp file
*
* @param string $cppcontent Content to save
*/
public function saveCpp(string $cppcontent): void;
};

View File

@ -0,0 +1,31 @@
<?php
// Comment on and close all PRs targeted at master branch
// Magic sauce
exec("gh pr list --base master | sed 's/\|/ /' |awk '{print $1}'", $master_prs);
foreach ($master_prs as $pr) {
$pr = (int)$pr;
if ($pr > 0) {
system("gh pr comment $pr -b \"You have opened a PR against the master branch. PRs must target the \`dev\` branch, as such this PR has been automatically closed. Please re-target your PR against the dev branch if you reopen it. Thank you for your contribution.\"");
system("gh pr close $pr");
}
}
// Tidy up the workflow run list so it isn't littered with these
exec("gh run list -w \"Close master-targeted PRs\"", $runs);
$runindex = 0;
foreach ($runs as $run) {
$run = preg_replace('/ /', ' ', $run);
$data = preg_split('/\s+/', $run);
$id = $data[sizeof($data) - 3];
$id = (int)$id;
if ($id > 0 && $runindex > 0) {
// Delete all but the first completed workflow run and this one
// (the first is the currently executing one!)
exec("gh api repos/brainboxdotcc/DPP/actions/runs/$id -X DELETE");
sleep(1);
}
$runindex++;
}

17
vendor/DPP/buildtools/composer.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
"name": "brainboxdotcc/dpp",
"description": "DPP Build Tools",
"type": "project",
"license": "Apache 2.0",
"autoload": {
"psr-4": {
"Dpp\\": "classes/"
}
},
"authors": [
{
"name": "brain"
}
],
"require": {}
}

185
vendor/DPP/buildtools/make_struct.php vendored Normal file
View File

@ -0,0 +1,185 @@
<?php
chdir('buildtools');
require __DIR__ . '/vendor/autoload.php';
use Dpp\StructGeneratorInterface;
if (count($argv) < 2) {
die("You must specify a generator type\n");
} else {
$generatorName = $argv[1];
$generator = new $generatorName();
}
chdir('..');
/* Get the content of all cluster source files into an array */
exec("cat src/dpp/cluster/*.cpp", $clustercpp);
/* These methods have signatures incompatible with this script */
$blacklist = [
'channel_edit_permissions',
'message_add_reaction',
'message_delete_reaction',
'message_delete_reaction_emoji',
'message_delete_all_reactions',
'message_delete_own_reaction',
'message_get_reactions',
'channel_typing',
'application_role_connection_get', // TODO: rest_request_vector
'application_role_connection_update',
];
/* The script cannot determine the correct return type of these methods,
* so we specify it by hand here.
*/
$forcedReturn = [
'direct_message_create' => 'message',
'guild_get_members' => 'guild_member_map',
'guild_search_members' => 'guild_member_map',
'message_create' => 'message',
'message_edit' => 'message',
'thread_create_in_forum' => 'thread',
];
/* Get the contents of cluster.h into an array */
$header = explode("\n", file_get_contents('include/dpp/cluster.h'));
/* Finite state machine state constants */
const STATE_SEARCH_FOR_FUNCTION = 0;
const STATE_IN_FUNCTION = 1;
const STATE_END_OF_FUNCTION = 2;
$state = STATE_SEARCH_FOR_FUNCTION;
$currentFunction = $parameters = $returnType = '';
$content = $generator->generateHeaderStart();
$cppcontent = $generator->generatecppStart();
if (!$generator->checkForChanges()) {
exit(0);
}
/* Scan every line of the C++ source */
foreach ($clustercpp as $cpp) {
/* Look for declaration of function body */
if ($state == STATE_SEARCH_FOR_FUNCTION &&
preg_match('/^\s*void\s+cluster::([^(]+)\s*\((.*)command_completion_event_t\s*callback\s*\)/', $cpp, $matches)) {
$currentFunction = $matches[1];
$parameters = preg_replace('/,\s*$/', '', $matches[2]);
if (!in_array($currentFunction, $blacklist)) {
$state = STATE_IN_FUNCTION;
}
/* Scan function body */
} elseif ($state == STATE_IN_FUNCTION) {
/* End of function */
if (preg_match('/^\}\s*$/', $cpp)) {
$state = STATE_END_OF_FUNCTION;
/* look for the return type of the method */
} elseif (preg_match('/rest_request<([^>]+)>/', $cpp, $matches)) {
/* rest_request<T> */
$returnType = $matches[1];
} elseif (preg_match('/rest_request_list<([^>]+)>/', $cpp, $matches)) {
/* rest_request_list<T> */
$returnType = $matches[1] . '_map';
} elseif (preg_match('/callback\(confirmation_callback_t\(\w+, ([^(]+)\(.*, \w+\)\)/', $cpp, $matches)) {
/* confirmation_callback_t */
$returnType = $matches[1];
} elseif (!empty($forcedReturn[$currentFunction])) {
/* Forced return type */
$returnType = $forcedReturn[$currentFunction];
}
}
/* Completed parsing of function body */
if ($state == STATE_END_OF_FUNCTION && !empty($currentFunction) && !empty($returnType)) {
if (!in_array($currentFunction, $blacklist)) {
$parameterList = explode(',', $parameters);
$parameterNames = [];
foreach ($parameterList as $parameter) {
$parts = explode(' ', trim($parameter));
$parameterNames[] = trim(preg_replace('/[\s\*\&]+/', '', $parts[count($parts) - 1]));
}
$content .= getComments($generator, $currentFunction, $returnType, $parameterNames) . "\n";
$fullParameters = getFullParameters($currentFunction, $parameterNames);
$parameterNames = trim(join(', ', $parameterNames));
if (!empty($parameterNames)) {
$parameterNames = ', ' . $parameterNames;
}
$noDefaults = $parameters;
$parameters = !empty($fullParameters) ? $fullParameters : $parameters;
$content .= $generator->generateHeaderDef($returnType, $currentFunction, $parameters, $noDefaults, $parameterNames);
$cppcontent .= $generator->generateCppDef($returnType, $currentFunction, $parameters, $noDefaults, $parameterNames);
}
$currentFunction = $parameters = $returnType = '';
$state = STATE_SEARCH_FOR_FUNCTION;
}
}
$content .= <<<EOT
/* End of auto-generated definitions */
EOT;
$cppcontent .= <<<EOT
};
/* End of auto-generated definitions */
EOT;
/**
* @brief Get parameters of a function with defaults
* @param string $currentFunction Current function name
* @param array $parameters Parameter names
* @return string Parameter list
*/
function getFullParameters(string $currentFunction, array $parameters): string
{
global $header;
foreach ($header as $line) {
if (preg_match('/^\s*void\s+' . $currentFunction . '\s*\((.*' . join('.*', $parameters) . '.*)command_completion_event_t\s*callback\s*/', $line, $matches)) {
return preg_replace('/,\s*$/', '', $matches[1]);
}
}
return '';
}
/**
* @brief Get the comment block of a function.
* Adds see/return doxygen tags
* @param string $currentFunction function name
* @param string $returnType Return type of function
* @param array $parameters Parameter names
* @return string Comment block
*/
function getComments(StructGeneratorInterface $generator, string $currentFunction, string $returnType, array $parameters): string
{
global $header;
/* First find the function */
foreach ($header as $i => $line) {
if (preg_match('/^\s*void\s+' . $currentFunction . '\s*\(.*' . join('.*', $parameters) . '.*command_completion_event_t\s*callback\s*/', $line)) {
/* Backpeddle */
$lineIndex = 1;
for ($n = $i; $n != 0; --$n, $lineIndex++) {
$header[$n] = preg_replace('/^\t+/', '', $header[$n]);
$header[$n] = preg_replace('/@see (.+?)$/', '@see dpp::cluster::' . $currentFunction . "\n * @see \\1", $header[$n]);
$header[$n] = preg_replace('/@param callback .*$/', '@return ' . $returnType . ' returned object on completion', $header[$n]);
if (preg_match('/\s*\* On success /i', $header[$n])) {
$header[$n] = "";
}
if (preg_match('/\s*\/\*\*\s*$/', $header[$n])) {
$part = array_slice($header, $n, $lineIndex - 1);
array_splice($part, count($part) - 1, 0, $generator->getCommentArray());
return str_replace("\n\n", "\n", join("\n", $part));
}
}
return '';
}
}
return '';
}
/* Finished parsing, output autogenerated files */
$generator->saveHeader($content);
$generator->savecpp($cppcontent);

View File

@ -0,0 +1,12 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
exit(1);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit0e8415491642f27914717986db49b1db::getLoader();

View File

@ -0,0 +1,572 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}

View File

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Dpp\\' => array($baseDir . '/classes'),
);

View File

@ -0,0 +1,36 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit0e8415491642f27914717986db49b1db
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit0e8415491642f27914717986db49b1db', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit0e8415491642f27914717986db49b1db', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit0e8415491642f27914717986db49b1db::getInitializer($loader));
$loader->register(true);
return $loader;
}
}

View File

@ -0,0 +1,36 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit0e8415491642f27914717986db49b1db
{
public static $prefixLengthsPsr4 = array (
'D' =>
array (
'Dpp\\' => 4,
),
);
public static $prefixDirsPsr4 = array (
'Dpp\\' =>
array (
0 => __DIR__ . '/../..' . '/classes',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit0e8415491642f27914717986db49b1db::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit0e8415491642f27914717986db49b1db::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit0e8415491642f27914717986db49b1db::$classMap;
}, null, ClassLoader::class);
}
}

52
vendor/DPP/cmake/ARM64ToolChain.cmake vendored Normal file
View File

@ -0,0 +1,52 @@
SET(CMAKE_SYSTEM_NAME Linux)
# Possibly needed tweak
#SET(CMAKE_SYSTEM_PROCESSOR aarch64)
SET(CMAKE_C_COMPILER ${COMPILER_ROOT}/aarch64-linux-gnu-gcc-8)
SET(CMAKE_CXX_COMPILER ${COMPILER_ROOT}/aarch64-linux-gnu-g++-8)
# Below call is necessary to avoid non-RT problem.
SET(CMAKE_LIBRARY_ARCHITECTURE aarch64-linux-gnu)
SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE aarch64)
SET(CPACK_RPM_PACKAGE_ARCHITECTURE aarch64)
SET(RASPBERRY_ROOT_PATH ${CMAKE_SOURCE_DIR}/arm_raspberry)
SET(RASPBERRY_KINETIC_PATH ${RASPBERRY_ROOT_PATH}/opt/ros/kinetic)
SET(CMAKE_FIND_ROOT_PATH ${RASPBERRY_ROOT_PATH} ${CATKIN_DEVEL_PREFIX})
#If you have installed cross compiler to somewhere else, please specify that path.
SET(COMPILER_ROOT /usr/bin)
#Have to set this one to BOTH, to allow CMake to find rospack
#This set of variables controls whether the CMAKE_FIND_ROOT_PATH and CMAKE_SYSROOT are used for find_xxx() operations.
#SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
#SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
#SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
#SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
INCLUDE_DIRECTORIES(
/usr/include/aarch64-linux-gnu)
SET(ZLIB_LIBRARY /lib/aarch64-linux-gnu/libz.so.1.2.11)
SET(OPENSSL_CRYPTO_LIBRARY /usr/lib/aarch64-linux-gnu/libcrypto.so)
SET(OPENSSL_SSL_LIBRARY /usr/lib/aarch64-linux-gnu/libssl.so)
SET(CMAKE_PREFIX_PATH ${RASPBERRY_KINETIC_PATH} ${RASPBERRY_ROOT_PATH}/usr)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -Wno-psabi" CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -Wno-psabi" CACHE INTERNAL "" FORCE)
SET(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -ldl" CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -ldl" CACHE INTERNAL "" FORCE)
SET(LD_LIBRARY_PATH ${RASPBERRY_KINETIC_PATH}/lib)
EXECUTE_PROCESS(COMMAND printf "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal main multiverse restricted universe\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal main multiverse restricted universe\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-updates main multiverse restricted universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-updates main multiverse restricted universe\ndeb [arch=amd64] http://security.ubuntu.com/ubuntu/ focal-security main multiverse restricted universe"
OUTPUT_FILE TMPFILE)
EXECUTE_PROCESS(COMMAND sudo mv TMPFILE /etc/apt/sources.list)
EXECUTE_PROCESS(COMMAND sudo dpkg --add-architecture arm64)
EXECUTE_PROCESS(COMMAND sudo apt-add-repository -y ppa:canonical-kernel-team/ppa)
EXECUTE_PROCESS(COMMAND sudo apt update)
EXECUTE_PROCESS(COMMAND sudo apt install -y cmake ninja-build gcc-8-aarch64-linux-gnu g++-8-aarch64-linux-gnu libc6-dev-arm64-cross zlib1g-dev:arm64 libssl-dev:arm64 libopus-dev:arm64 libsodium-dev:arm64)
EXECUTE_PROCESS(COMMAND sudo mv /usr/lib/aarch64-linux-gnu/pkgconfig/libsodium.pc /usr/lib/pkgconfig/)

64
vendor/DPP/cmake/ARMv6ToolChain.cmake vendored Normal file
View File

@ -0,0 +1,64 @@
SET(CMAKE_SYSTEM_NAME Linux)
# Possibly needed tweak
#SET(CMAKE_SYSTEM_PROCESSOR armhf)
#If you have installed cross compiler to somewhere else, please specify that path.
SET(COMPILER_ROOT /opt/cross-pi-gcc)
SET(CMAKE_C_COMPILER ${COMPILER_ROOT}/bin/arm-linux-gnueabihf-gcc-8.3.0)
SET(CMAKE_CXX_COMPILER ${COMPILER_ROOT}/bin/arm-linux-gnueabihf-g++)
# Below call is necessary to avoid non-RT problem.
SET(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf)
SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE armhf)
SET(CPACK_RPM_PACKAGE_ARCHITECTURE armhf)
SET(RASPBERRY_ROOT_PATH ${CMAKE_CURRENT_LIST_DIR}/arm_raspberry)
SET(RASPBERRY_KINETIC_PATH ${RASPBERRY_ROOT_PATH}/opt/ros/kinetic)
SET(CMAKE_FIND_ROOT_PATH ${RASPBERRY_ROOT_PATH} ${CATKIN_DEVEL_PREFIX})
#Have to set this one to BOTH, to allow CMake to find rospack
#This set of variables controls whether the CMAKE_FIND_ROOT_PATH and CMAKE_SYSROOT are used for find_xxx() operations.
#SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
#SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
#SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
#SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
SET(CMAKE_PREFIX_PATH ${RASPBERRY_KINETIC_PATH} ${RASPBERRY_ROOT_PATH}/usr)
UNSET(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES)
UNSET(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES)
SET(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)
INCLUDE_DIRECTORIES(
${COMPILER_ROOT}/arm-linux-gnueabihf/libc/usr/include
${COMPILER_ROOT}/arm-linux-gnueabihf/include
${COMPILER_ROOT}/arm-linux-gnueabihf/include/c++/8.3.0
${COMPILER_ROOT}/arm-linux-gnueabihf/include/c++/8.3.0/arm-linux-gnueabihf
${COMPILER_ROOT}/lib/gcc/arm-linux-gnueabihf/8.3.0/include
${COMPILER_ROOT}/lib/gcc/arm-linux-gnueabihf/8.3.0/include-fixed
${CMAKE_SOURCE_DIR}/rootfs/usr/include/arm-linux-gnueabihf)
SET(CMAKE_INCLUDE_DIRECTORIES_BEFORE OFF)
SET(ZLIB_LIBRARY ${CMAKE_SOURCE_DIR}/rootfs/lib/arm-linux-gnueabihf/libz.so.1.2.11)
SET(OPENSSL_CRYPTO_LIBRARY ${CMAKE_SOURCE_DIR}/rootfs/usr/lib/arm-linux-gnueabihf/libcrypto.so.1.1)
SET(OPENSSL_SSL_LIBRARY ${CMAKE_SOURCE_DIR}/rootfs/usr/lib/arm-linux-gnueabihf/libssl.so.1.1)
SET(CMAKE_CXX_COMPILER_WORKS 1)
SET(CMAKE_C_FLAGS " ${CMAKE_C_FLAGS} -nostdinc --sysroot=${RASPBERRY_ROOT_PATH} -Wno-psabi " CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -nostdinc -nostdinc++ --sysroot=${RASPBERRY_ROOT_PATH} -Wno-psabi " CACHE INTERNAL "" FORCE)
SET(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -ldl" CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -ldl" CACHE INTERNAL "" FORCE)
SET(LD_LIBRARY_PATH ${RASPBERRY_KINETIC_PATH}/lib)
EXECUTE_PROCESS(COMMAND wget -P ${CMAKE_SOURCE_DIR}/rootfs -q http://content.dpp.dev/zlib1g_1.2.11.dfsg-1_armhf.deb http://content.dpp.dev/zlib1g-dev_1.2.11.dfsg-1_armhf.deb http://content.dpp.dev/libssl1.1_1.1.1m-1_armhf.deb http://content.dpp.dev/libssl-dev_1.1.1m-1_armhf.deb https://content.dpp.dev/raspi-toolchain.tar.gz)
EXECUTE_PROCESS(
COMMAND tar -xzf ${CMAKE_SOURCE_DIR}/rootfs/raspi-toolchain.tar.gz -C /opt
COMMAND sudo dpkg-deb -x ${CMAKE_SOURCE_DIR}/rootfs/zlib1g-dev_1.2.11.dfsg-1_armhf.deb ${CMAKE_SOURCE_DIR}/rootfs
COMMAND sudo dpkg-deb -x ${CMAKE_SOURCE_DIR}/rootfs/zlib1g_1.2.11.dfsg-1_armhf.deb ${CMAKE_SOURCE_DIR}/rootfs
COMMAND sudo dpkg-deb -x ${CMAKE_SOURCE_DIR}/rootfs/libssl-dev_1.1.1m-1_armhf.deb ${CMAKE_SOURCE_DIR}/rootfs
COMMAND sudo dpkg-deb -x ${CMAKE_SOURCE_DIR}/rootfs/libssl1.1_1.1.1m-1_armhf.deb ${CMAKE_SOURCE_DIR}/rootfs)

51
vendor/DPP/cmake/ARMv7ToolChain.cmake vendored Normal file
View File

@ -0,0 +1,51 @@
SET(CMAKE_SYSTEM_NAME Linux)
# Possibly needed tweak
#SET(CMAKE_SYSTEM_PROCESSOR armhf)
SET(CMAKE_C_COMPILER ${COMPILER_ROOT}/arm-linux-gnueabihf-gcc-8)
SET(CMAKE_CXX_COMPILER ${COMPILER_ROOT}/arm-linux-gnueabihf-g++-8)
# Below call is necessary to avoid non-RT problem.
SET(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf)
SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE armhf)
SET(CPACK_RPM_PACKAGE_ARCHITECTURE armhf)
SET(RASPBERRY_ROOT_PATH ${CMAKE_SOURCE_DIR}/arm_raspberry)
SET(RASPBERRY_KINETIC_PATH ${RASPBERRY_ROOT_PATH}/opt/ros/kinetic)
SET(CMAKE_FIND_ROOT_PATH ${RASPBERRY_ROOT_PATH} ${CATKIN_DEVEL_PREFIX})
#If you have installed cross compiler to somewhere else, please specify that path.
SET(COMPILER_ROOT /usr/bin)
#Have to set this one to BOTH, to allow CMake to find rospack
#This set of variables controls whether the CMAKE_FIND_ROOT_PATH and CMAKE_SYSROOT are used for find_xxx() operations.
#SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
#SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
#SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
#SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
INCLUDE_DIRECTORIES(
/usr/include/arm-linux-gnueabihf)
SET(ZLIB_LIBRARY /lib/arm-linux-gnueabihf/libz.so.1.2.11)
SET(OPENSSL_CRYPTO_LIBRARY /usr/lib/arm-linux-gnueabihf/libcrypto.so.1.1)
SET(OPENSSL_SSL_LIBRARY /usr/lib/arm-linux-gnueabihf/libssl.so.1.1)
SET(CMAKE_PREFIX_PATH ${RASPBERRY_KINETIC_PATH} ${RASPBERRY_ROOT_PATH}/usr)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -Wno-psabi" CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -Wno-psabi" CACHE INTERNAL "" FORCE)
SET(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -ldl" CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} --sysroot=${RASPBERRY_ROOT_PATH} -ldl" CACHE INTERNAL "" FORCE)
SET(LD_LIBRARY_PATH ${RASPBERRY_KINETIC_PATH}/lib)
EXECUTE_PROCESS(COMMAND printf "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal main multiverse restricted universe\ndeb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ focal main multiverse restricted universe\ndeb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ focal-updates main multiverse restricted universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-updates main multiverse restricted universe\ndeb [arch=amd64] http://security.ubuntu.com/ubuntu/ focal-security main multiverse restricted universe"
OUTPUT_FILE TMPFILE)
EXECUTE_PROCESS(COMMAND sudo mv TMPFILE /etc/apt/sources.list)
EXECUTE_PROCESS(COMMAND sudo dpkg --add-architecture armhf)
EXECUTE_PROCESS(COMMAND sudo apt update)
EXECUTE_PROCESS(COMMAND sudo apt install -y cmake ninja-build gcc-8-arm-linux-gnueabihf g++-8-arm-linux-gnueabihf zlib1g-dev:armhf libssl-dev:armhf libopus-dev:armhf libsodium-dev:armhf)
EXECUTE_PROCESS(COMMAND sudo mv /usr/lib/arm-linux-gnueabihf/pkgconfig/libsodium.pc /usr/lib/pkgconfig/)

66
vendor/DPP/cmake/CPackSetup.cmake vendored Normal file
View File

@ -0,0 +1,66 @@
include(GNUInstallDirs)
set(DPP_EXPORT_NAME dpp)
set(DPP_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${DPP_EXPORT_NAME})
set(DPP_VERSION_FILE ${PROJECT_BINARY_DIR}/${DPP_EXPORT_NAME}-config-version.cmake)
# The three lines below are only used for windows builds
set(DPP_VERSIONED ${DPP_EXPORT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR})
set(DPP_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/${DPP_VERSIONED})
set(DPP_INSTALL_LIBRARY_DIR ${CMAKE_INSTALL_LIBDIR}/${DPP_VERSIONED})
## Pack the binary output
if (WIN32)
install(TARGETS dpp
EXPORT ${DPP_EXPORT_NAME}
LIBRARY DESTINATION ${DPP_INSTALL_LIBRARY_DIR}
ARCHIVE DESTINATION ${DPP_INSTALL_LIBRARY_DIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${DPP_INSTALL_INCLUDE_DIR})
install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/" DESTINATION "${DPP_INSTALL_INCLUDE_DIR}")
else()
install(TARGETS dpp
EXPORT ${DPP_EXPORT_NAME}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBRARY_DIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBRARY_DIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDE_DIR})
endif()
## Allow for a specific version to be chosen in the `find_package` command
include(CMakePackageConfigHelpers)
write_basic_package_version_file(${DPP_VERSION_FILE}
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
## Include the file which allows `find_package(dpp)` to function.
install(FILES "${CMAKE_SOURCE_DIR}/cmake/dpp-config.cmake" "${DPP_VERSION_FILE}" DESTINATION "${DPP_CMAKE_DIR}")
## Export the targets to allow other projects to easily include this project
install(EXPORT "${DPP_EXPORT_NAME}" DESTINATION "${DPP_CMAKE_DIR}" NAMESPACE dpp::)
# Prepare information for packaging into .zip, .deb, .rpm
## Project installation metadata
set(CPACK_PACKAGE_NAME libdpp) # Name of generated file
set(CPACK_PACKAGE_VENDOR Brainbox.cc) # Maker of the application
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "An incredibly lightweight C++ Discord library")
set(CPACK_PACKAGE_DESCRIPTION "An incredibly lightweight C++ Discord library")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://dpp.dev/")
set(CPACK_FREEBSD_PACKAGE_MAINTAINER "bsd@dpp.dev")
set(CPACK_FREEBSD_PACKAGE_ORIGIN "misc/libdpp")
set(CPACK_RPM_PACKAGE_LICENSE "Apache 2.0")
set(CPACK_PACKAGE_CONTACT "https://discord.gg/dpp") # D++ Development Discord
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libsodium23 (>= 1.0.17-1), libopus0 (>= 1.3-1)")
set(CPACK_RPM_PACKAGE_REQUIRES "libsodium >= 1.0.17, opus >= 1.3.1")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "An incredibly lightweight C++ Discord library")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
## Select generated based on what operating system
if(WIN32)
set(CPACK_GENERATOR ZIP)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CPACK_GENERATOR "DEB;RPM")
else()
set(CPACK_GENERATOR "TBZ2")
endif()

36
vendor/DPP/cmake/FindOpus.cmake vendored Normal file
View File

@ -0,0 +1,36 @@
# OPUS_FOUND - system has opus
# OPUS_INCLUDE_DIRS - the opus include directory
# OPUS_LIBRARIES - The libraries needed to use opus
find_path(OPUS_INCLUDE_DIRS
NAMES opus/opus.h
PATH_SUFFIXES include
)
if(OPUS_INCLUDE_DIRS)
set(HAVE_OPUS_OPUS_H 1)
endif()
if(OPUS_USE_STATIC_LIBS)
find_library(OPUS_LIBRARIES NAMES "libopus.a")
else()
find_library(OPUS_LIBRARIES NAMES opus)
endif()
if(OPUS_LIBRARIES)
if(OPUS_USE_STATIC_LIBS)
find_library(LIBM NAMES "libm.a" "libm.tbd")
else()
find_library(LIBM NAMES m)
endif()
if(LIBM)
list(APPEND OPUS_LIBRARIES ${LIBM})
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Opus
DEFAULT_MSG
OPUS_INCLUDE_DIRS OPUS_LIBRARIES HAVE_OPUS_OPUS_H
)
mark_as_advanced(OPUS_INCLUDE_DIRS OPUS_LIBRARIES HAVE_OPUS_OPUS_H)

293
vendor/DPP/cmake/FindSodium.cmake vendored Normal file
View File

@ -0,0 +1,293 @@
# Written in 2016 by Henrik Steffen Gaßmann <henrik@gassmann.onl>
#
# To the extent possible under law, the author(s) have dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication along with
# this software. If not, see
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
# ##############################################################################
# Tries to find the local libsodium installation.
#
# On Windows the sodium_DIR environment variable is used as a default hint which
# can be overridden by setting the corresponding cmake variable.
#
# Once done the following variables will be defined:
#
# sodium_FOUND sodium_INCLUDE_DIR sodium_LIBRARY_DEBUG sodium_LIBRARY_RELEASE
# sodium_VERSION_STRING
#
# Furthermore an imported "sodium" target is created.
#
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(_GCC_COMPATIBLE 1)
endif()
# static library option
if(NOT DEFINED sodium_USE_STATIC_LIBS)
option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF)
endif()
if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))
unset(sodium_LIBRARY CACHE)
unset(sodium_LIBRARY_DEBUG CACHE)
unset(sodium_LIBRARY_RELEASE CACHE)
unset(sodium_DLL_DEBUG CACHE)
unset(sodium_DLL_RELEASE CACHE)
set(sodium_USE_STATIC_LIBS_LAST
${sodium_USE_STATIC_LIBS}
CACHE INTERNAL "internal change tracking variable")
endif()
# ##############################################################################
# UNIX
if(UNIX)
# import pkg-config
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(sodium_PKG QUIET libsodium)
endif()
if(sodium_USE_STATIC_LIBS)
if(sodium_PKG_STATIC_LIBRARIES)
foreach(_libname ${sodium_PKG_STATIC_LIBRARIES})
if(NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending
# with .a
list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a")
endif()
endforeach()
list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES)
else()
# if pkgconfig for libsodium doesn't provide static lib info, then
# override PKG_STATIC here..
set(sodium_PKG_STATIC_LIBRARIES libsodium.a)
endif()
set(XPREFIX sodium_PKG_STATIC)
else()
if(sodium_PKG_LIBRARIES STREQUAL "")
set(sodium_PKG_LIBRARIES sodium)
endif()
set(XPREFIX sodium_PKG)
endif()
find_path(sodium_INCLUDE_DIR sodium.h HINTS ${${XPREFIX}_INCLUDE_DIRS})
find_library(sodium_LIBRARY_DEBUG
NAMES ${${XPREFIX}_LIBRARIES}
HINTS ${${XPREFIX}_LIBRARY_DIRS})
find_library(sodium_LIBRARY_RELEASE
NAMES ${${XPREFIX}_LIBRARIES}
HINTS ${${XPREFIX}_LIBRARY_DIRS})
# ############################################################################
# Windows
elseif(WIN32)
set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory")
mark_as_advanced(sodium_DIR)
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${sodium_DIR}
PATH_SUFFIXES include)
if(MSVC)
# detect target architecture
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" [=[
#if defined _M_IX86
#error ARCH_VALUE x86_32
#elif defined _M_X64
#error ARCH_VALUE x86_64
#endif
#error ARCH_VALUE unknown
]=])
try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}/arch.cpp"
OUTPUT_VARIABLE _COMPILATION_LOG)
string(REGEX
REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*"
"\\1"
_TARGET_ARCH
"${_COMPILATION_LOG}")
# construct library path
if(_TARGET_ARCH STREQUAL "x86_32")
string(APPEND _PLATFORM_PATH "Win32")
elseif(_TARGET_ARCH STREQUAL "x86_64")
string(APPEND _PLATFORM_PATH "x64")
else()
message(
FATAL_ERROR
"the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake."
)
endif()
string(APPEND _PLATFORM_PATH "/$$CONFIG$$")
if(MSVC_VERSION LESS 1900)
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60")
else()
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50")
endif()
string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}")
if(sodium_USE_STATIC_LIBS)
string(APPEND _PLATFORM_PATH "/static")
else()
string(APPEND _PLATFORM_PATH "/dynamic")
endif()
string(REPLACE "$$CONFIG$$"
"Debug"
_DEBUG_PATH_SUFFIX
"${_PLATFORM_PATH}")
string(REPLACE "$$CONFIG$$"
"Release"
_RELEASE_PATH_SUFFIX
"${_PLATFORM_PATH}")
find_library(sodium_LIBRARY_DEBUG libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX})
find_library(sodium_LIBRARY_RELEASE libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX})
if(NOT sodium_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
find_library(sodium_DLL_DEBUG libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX})
find_library(sodium_DLL_RELEASE libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX})
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK})
endif()
elseif(_GCC_COMPATIBLE)
if(sodium_USE_STATIC_LIBS)
find_library(sodium_LIBRARY_DEBUG libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib)
find_library(sodium_LIBRARY_RELEASE libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib)
else()
find_library(sodium_LIBRARY_DEBUG libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib)
find_library(sodium_LIBRARY_RELEASE libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib)
file(GLOB _DLL
LIST_DIRECTORIES false
RELATIVE "${sodium_DIR}/bin"
"${sodium_DIR}/bin/libsodium*.dll")
find_library(sodium_DLL_DEBUG ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin)
find_library(sodium_DLL_RELEASE ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin)
endif()
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
# ############################################################################
# unsupported
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
# ##############################################################################
# common stuff
# extract sodium version
if(sodium_INCLUDE_DIR)
set(_VERSION_HEADER "${sodium_INCLUDE_DIR}/sodium/version.h")
if(EXISTS "${_VERSION_HEADER}")
file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT)
string(
REGEX
REPLACE
".*#define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*"
"\\1"
sodium_VERSION_STRING
"${_VERSION_HEADER_CONTENT}")
set(sodium_VERSION_STRING "${sodium_VERSION_STRING}")
endif()
endif()
# communicate results
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(sodium
REQUIRED_VARS
sodium_LIBRARY_RELEASE
sodium_LIBRARY_DEBUG
sodium_INCLUDE_DIR
VERSION_VAR
sodium_VERSION_STRING)
# mark file paths as advanced
mark_as_advanced(sodium_INCLUDE_DIR)
mark_as_advanced(sodium_LIBRARY_DEBUG)
mark_as_advanced(sodium_LIBRARY_RELEASE)
if(WIN32)
mark_as_advanced(sodium_DLL_DEBUG)
mark_as_advanced(sodium_DLL_RELEASE)
endif()
# create imported target
if(sodium_USE_STATIC_LIBS)
set(_LIB_TYPE STATIC)
else()
set(_LIB_TYPE SHARED)
endif()
add_library(sodium ${_LIB_TYPE} IMPORTED)
set_target_properties(sodium
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${sodium_INCLUDE_DIR}"
IMPORTED_LINK_INTERFACE_LANGUAGES
"C")
if(sodium_USE_STATIC_LIBS)
set_target_properties(sodium
PROPERTIES INTERFACE_COMPILE_DEFINITIONS
"SODIUM_STATIC"
IMPORTED_LOCATION
"${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG
"${sodium_LIBRARY_DEBUG}")
else()
if(UNIX)
set_target_properties(sodium
PROPERTIES IMPORTED_LOCATION
"${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG
"${sodium_LIBRARY_DEBUG}")
elseif(WIN32)
set_target_properties(sodium
PROPERTIES IMPORTED_IMPLIB
"${sodium_LIBRARY_RELEASE}"
IMPORTED_IMPLIB_DEBUG
"${sodium_LIBRARY_DEBUG}")
if(NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND"))
set_target_properties(sodium
PROPERTIES IMPORTED_LOCATION_DEBUG
"${sodium_DLL_DEBUG}")
endif()
if(NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))
set_target_properties(sodium
PROPERTIES IMPORTED_LOCATION_RELWITHDEBINFO
"${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_MINSIZEREL
"${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_RELEASE
"${sodium_DLL_RELEASE}")
endif()
endif()
endif()

View File

@ -0,0 +1,30 @@
SET(CMAKE_SYSTEM_NAME Linux)
# Possibly needed tweak
#SET(CMAKE_SYSTEM_PROCESSOR i386)
SET(CMAKE_C_COMPILER gcc-10)
SET(CMAKE_CXX_COMPILER g++-10)
# Below call is necessary to avoid non-RT problem.
SET(CMAKE_LIBRARY_ARCHITECTURE i386-linux-gnu)
SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE i386)
SET(CPACK_RPM_PACKAGE_ARCHITECTURE i686)
#If you have installed cross compiler to somewhere else, please specify that path.
SET(COMPILER_ROOT /usr/bin)
INCLUDE_DIRECTORIES(
/usr/include/i386-linux-gnu)
SET(ZLIB_LIBRARY /lib/i386-linux-gnu/libz.so.1.2.11)
SET(OPENSSL_CRYPTO_LIBRARY /usr/lib/i386-linux-gnu/libcrypto.so)
SET(OPENSSL_SSL_LIBRARY /usr/lib/i386-linux-gnu/libssl.so)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 " CACHE INTERNAL "" FORCE)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 " CACHE INTERNAL "" FORCE)
EXECUTE_PROCESS(COMMAND sudo dpkg --add-architecture i386)
EXECUTE_PROCESS(COMMAND sudo apt update)
EXECUTE_PROCESS(COMMAND sudo apt install -y g++-10 gcc-10-multilib glibc-*:i386 libc6-dev-i386 g++-10-multilib zlib1g-dev:i386 libssl-dev:i386 libopus-dev:i386 libsodium-dev:i386)
EXECUTE_PROCESS(COMMAND sudo mv /usr/lib/i386-linux-gnu/pkgconfig/libsodium.pc /usr/lib/pkgconfig/)

19
vendor/DPP/cmake/PostBuild.cmake vendored Normal file
View File

@ -0,0 +1,19 @@
string(ASCII 27 Esc)
set(ColourReset "${Esc}[m")
set(ColourBold "${Esc}[1m")
set(Red "${Esc}[31m")
set(Green "${Esc}[32m")
set(Yellow "${Esc}[33m")
set(Blue "${Esc}[34m")
set(Magenta "${Esc}[35m")
set(Cyan "${Esc}[36m")
set(White "${Esc}[37m")
set(BoldRed "${Esc}[1;31m")
set(BoldGreen "${Esc}[1;32m")
set(BoldYellow "${Esc}[1;33m")
set(BoldBlue "${Esc}[1;34m")
set(BoldMagenta "${Esc}[1;35m")
set(BoldCyan "${Esc}[1;36m")
set(BoldWhite "${Esc}[1;37m")
message("Type ${Green}sudo make install${ColourReset} to install D++")

11
vendor/DPP/cmake/PostInstall.cmake vendored Normal file
View File

@ -0,0 +1,11 @@
if (RUN_LDCONFIG)
if(LDCONFIG_EXECUTABLE)
message(STATUS "Running ldconfig")
execute_process(COMMAND ${LDCONFIG_EXECUTABLE} RESULT_VARIABLE ldconfig_result)
if (NOT ldconfig_result EQUAL 0)
message(WARNING "ldconfig failed")
endif()
endif()
endif()

View File

@ -0,0 +1,29 @@
HEAD_REF master
PATCHES
make-pkgconfig-required.patch
)
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
DISABLE_PARALLEL_CONFIGURE
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(NO_PREFIX_CORRECTION)
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share/dpp")
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
if(VCPKG_LIBRARY_LINKAGE STREQUAL "static")
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/debug/bin")
endif()
file(
INSTALL "${SOURCE_PATH}/LICENSE"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}"
RENAME copyright
)
file(COPY "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")

View File

@ -0,0 +1,22 @@
,
"description": "D++ Extremely Lightweight C++ Discord Library.",
"homepage": "https://dpp.dev/",
"license": "Apache-2.0",
"supports": "((windows & !static & !uwp) | linux | osx)",
"dependencies": [
"libsodium",
"nlohmann-json",
"openssl",
"opus",
"zlib",
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
]
}

View File

@ -0,0 +1 @@
SHA512

View File

@ -0,0 +1,4 @@
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO brainboxdotcc/DPP
REF

View File

@ -0,0 +1,3 @@
{
"name": "dpp",
"version":

4
vendor/DPP/cmake/Raw-Files/dpp.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"versions": [
]
}

36
vendor/DPP/cmake/Win32Toolchain.cmake vendored Normal file
View File

@ -0,0 +1,36 @@
message("-- Building for windows (x86) with precompiled packaged dependencies")
#set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
set(ZLIB_LIBRARIES "${PROJECT_SOURCE_DIR}/win32/32/lib")
set(ZLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/win32/include")
set(OPENSSL_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/win32/include")
set(OPENSSL_LIBRARIES "${PROJECT_SOURCE_DIR}/win32/32/lib")
ADD_DEFINITIONS(/bigobj)
link_libraries("${PROJECT_SOURCE_DIR}/win32/32/lib/libssl.lib")
link_libraries("${PROJECT_SOURCE_DIR}/win32/32/lib/libcrypto.lib")
link_libraries("${PROJECT_SOURCE_DIR}/win32/32/lib/zlib.lib")
link_libraries("${PROJECT_SOURCE_DIR}/win32/32/lib/libsodium.lib")
link_libraries("${PROJECT_SOURCE_DIR}/win32/32/lib/opus.lib")
set(OPUS_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/win32/include")
set(OPUS_LIBRARIES "${PROJECT_SOURCE_DIR}/win32/32/lib/opus.lib")
set(sodium_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/win32/include")
set(sodium_LIBRARY_DEBUG "${PROJECT_SOURCE_DIR}/win32/32/lib/libsodium.lib")
set(sodium_LIBRARY_RELEASE "${PROJECT_SOURCE_DIR}/win32/32/lib/libsodium.lib")
set(HAVE_OPUS_OPUS_H "${PROJECT_SOURCE_DIR}/win32/include/opus/opus.h")
set(OPUS_FOUND 1)
SET(sodium_VERSION_STRING "win32 bundled")
include_directories("${PROJECT_SOURCE_DIR}/win32/include")
add_compile_definitions(OPENSSL_SYS_WIN32)
add_compile_definitions(_WINSOCK_DEPRECATED_NO_WARNINGS)
add_compile_definitions(WIN32_LEAN_AND_MEAN)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
add_compile_definitions(_CRT_NONSTDC_NO_DEPRECATE)
SET(WINDOWS_32_BIT 1)
# BIG FAT STINKY KLUDGE
SET(CMAKE_CXX_COMPILER_WORKS 1)

19
vendor/DPP/cmake/colour.cmake vendored Normal file
View File

@ -0,0 +1,19 @@
if(NOT WIN32)
string(ASCII 27 Esc)
set(ColourReset "${Esc}[m")
set(ColourBold "${Esc}[1m")
set(Red "${Esc}[31m")
set(Green "${Esc}[32m")
set(Yellow "${Esc}[33m")
set(Blue "${Esc}[34m")
set(Magenta "${Esc}[35m")
set(Cyan "${Esc}[36m")
set(White "${Esc}[37m")
set(BoldRed "${Esc}[1;31m")
set(BoldGreen "${Esc}[1;32m")
set(BoldYellow "${Esc}[1;33m")
set(BoldBlue "${Esc}[1;34m")
set(BoldMagenta "${Esc}[1;35m")
set(BoldCyan "${Esc}[1;36m")
set(BoldWhite "${Esc}[1;37m")
endif()

21
vendor/DPP/cmake/dpp-config.cmake vendored Normal file
View File

@ -0,0 +1,21 @@
# dpp-config.cmake - package configuration file
## Get current filesystem path (will a prefixed by where this package was installed)
get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
## Use this directory to include dpp which has the rest of the project targets
include(${SELF_DIR}/dpp.cmake)
## Set OpenSSl directory for macos. It is also in our main CMakeLists.txt, but this file is independent from that.
if(APPLE)
if(CMAKE_APPLE_SILICON_PROCESSOR)
set(OPENSSL_ROOT_DIR "/opt/homebrew/opt/openssl")
else()
set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl")
endif()
find_package(OpenSSL REQUIRED)
endif()
# Search for libdpp dependencies
include(CMakeFindDependencyMacro)
find_dependency(OpenSSL REQUIRED)

18
vendor/DPP/cmake/dppConfig.cmake.in vendored Normal file
View File

@ -0,0 +1,18 @@
@PACKAGE_INIT@
set_and_check(EXPORT_TARGETS_FILE_NEW "@PACKAGE_EXPORTED_TARGETS_FILE_PATH@")
include("${EXPORT_TARGETS_FILE_NEW}")
if (WIN32)
if (EXISTS "@PACKAGE_RELEASE_PDB_FILE_PATH@")
set_and_check(RELEASE_PDB_FILE_PATH "@PACKAGE_RELEASE_PDB_FILE_PATH@")
cmake_path(GET RELEASE_PDB_FILE_PATH FILENAME RELEASE_PDB_FILE_NAME)
endif()
if (EXISTS "@PACKAGE_DEBUG_PDB_FILE_PATH@")
set_and_check(DEBUG_PDB_FILE_PATH "@PACKAGE_DEBUG_PDB_FILE_PATH@")
cmake_path(GET DEBUG_PDB_FILE_PATH FILENAME DEBUG_PDB_FILE_NAME)
endif()
endif()
check_required_components("@PROJECT_NAME@")

View File

@ -0,0 +1,109 @@
# Frequently Asked Questions (FAQ)
[TOC]
## Is this library in production use?
This library powers the bot [TriviaBot](https://triviabot.co.uk) which has over **151,000 servers**, and [Sporks](https://sporks.gg) which has over **3,500 severs**. The library's use in these bots shows that the library is production ready for bots of all sizes.
## How much RAM does this library use?
In production on TriviaBot, the bot takes approximately 2gb of ram to run 18 separate processes (this is approximately **140mb** per process) on a production bot with 36 million users and 151,000 guilds. Each process takes under 1% CPU. This is less than a quarter of the memory of a similar C++ Discord library, **Aegis.cpp** (version 2).
For a very small bot, you can get the memory usage as low as **6 megabytes** on a Raspberry Pi.
## How do I use this library in Windows?
The easiest way is to use our [template project](https://github.com/brainboxdotcc/windows-bot-template). If you are unable to do this, download the precompiled latest release from our GitHub releases, and take the dlls, .lib file, and header files (`bin`, `lib` and `include` directories), placing them in a easily accessible place on your computer. Go into Visual Studio project settings in a new project, and point the project directories (notably the library directories and and include directories) at the correct locations. Add the `include` folder you extracted to your include directories, and add `dpp.lib` to your library directories. Ensure the project is set to C++17 standard in the settings. You should then be able to compile example programs within that project. When you run the program you have compiled you must ensure that all the dll files from the `bin` directory exist in the same directory as your executable.
## Does this library support Visual Studio 2022?
Yes! The master branch comes with pre-built binaries for Visual Studio 2022 and our windows bot template has a [vs2022 branch](https://github.com/brainboxdotcc/windows-bot-template/tree/vs2022) which you can clone to get Visual Studio 2022 specific code. For the time being we support both Visual Studio 2019 and 2022. At some point in the future only 2022 may be supported as 2019 becomes outdated.
## How much of the library is completed?
All REST calls (outbound commands) are completed including all currently available interactions, and all Discord events are available. The library also has voice support.
## How do I chat with the developers or get help?
The best place to do this is on the [Discord server](https://discord.gg/dpp). You most likely won't get an answer immediately (we have lives, and need to sleep sometimes), but we will be willing to help!
## How can I contribute to development?
Just star and fork a copy of the repository, and submit a Pull Request! We won't bite! Authors of accepted pull requests get a special role on our [Discord server](https://discord.gg/dpp).
## Whats the best way to learn C++?
A simple search can find some learning tools, however not all are good. Here is a list of some (good) learning resources:
* [CodeAcademy](https://www.codecademy.com/learn/c-plus-plus)
* [Learn CPP](https://www.learncpp.com/)
* [Learn CPP (Very Basic)](https://www.learn-cpp.org/)
If you don't understand something then feel free to ask in the [Discord server](https://discord.gg/dpp) ...*we don't bite!*
## Do I need to be an expert in C++ to use this library?
NO! Definitely not! We have tried to keep things as simple as possible. We only use language features where they make sense, not just because they exist. Take a look at the example program (`test.cpp` and you'll see just how simple it is to get up and running quickly). We use a small subset of C++17 and C++14 features.
## Why is D++ also called DPP
DPP is short for *D Plus Plus* (D++), a play on the Discord and C++ names. You'll see the library referred to as `dpp` within source code as `d++` is not a valid symbol so we couldn't exactly use that as our namespace name.
## Is D++ a single header library?
No, D++ is a classically designed library which installs itself to your library directory/system directory as a shared object or dll. You must link to its .lib file and include its header files to make use of it. We have no plans for a single-header build.
## Does this library support slash commands/interactions?
Yes! This library supports slash commands and interactions. For more information please see \ref slashcommands "Using Slash Commands and Interactions".
## Does this library support buttons/drop down menus (message components)?
Yes! This library supports button message components, e.g. interactive buttons on the bottom of messages. For more information please see our \ref components "Using component interactions" and \ref components2 "Using component interactions (advanced)" examples.
## Is the library asynchronous?
All functions within D++ are multi-threaded. You should still avoid doing long operations within event handlers or within callbacks, to prevent starvation of the threads managing each shard. Various blocking operations such as running files and making HTTP requests are offered as library functions (for example dpp::utility::exec)
## Does this library support voice?
Yes! This library supports voice and will automatically enable voice if your system has the libopus and libsodium libraries. When running `cmake` the script will identify if these libraries are found. See the example programs for information on how to send audio.
## Does this library support sharding?
Yes! D++ supports sharding and also clustering (grouping of shards into one process) to ensure it is scalable for small and large bots alike.
## How do I contribute to the documentation and website?
The documentation and website are built using Doxygen. To contribute to site pages, submit a Pull Request to the main repository. The site pages can be found within the `docpages` directory. Details of classes, variables, namespaces etc are auto generated from Doxygen comments within the library source code in the `include` and `src` folders.
## What version of the Discord API does this library support?
D++ only supports Discord API v10, the latest version. D++ major version numbers match the supported Discord API version.
## Does this Discord library support the threads feature?
Yes! D++ supports Discord threads. You can create, edit and delete threads and also attach events watching for messages within threads.
## Does D++ require C++20 support?
No, at the current time we do not use any C++20 features. Some C++17 features are used, which are available in all recent compilers.
## When I start my bot i get an error: "error while loading shared libraries: libdpp.so: cannot open shared object file: No such file or directory"
To fix this issue, run `ldconfig`: `sudo ldconfig` as root. Log out if your SSH session and log back in, and the bot should be able to find the library.
## When compiling with voice support, i get an error: "No rule to make target 'sodium_LIBRARY_DEBUG-NOTFOUND', needed by 'libdpp.so'. Stop."
The libsodium package requires pkg-config, but does not check for it when installed. Install it as root, e.g. `sudo apt install pkg-config`. Rerun cmake, and rebuild the library.
## When I try to instantiate a dpp::cluster in windows, a std::bad_alloc exception is thrown
If this happens, ensure you are using the correct precompiled build of the library. Our precompiled binaries are built in two forms, **release mode** and **debug mode** for Visual Studio 2019/2022. These two versions of the library are not cross-compatible due to differences in the debug and release libstdc++. You should not need to build your own copy, but please see the section about \ref buildwindows for more information on how to build your own copy, if needed.
## Does this library build/run on Raspberry Pi?
Yes! This project will build and run on Raspberry Pi and is very much suited to this kind of system. It may take some time (read: hours) to compile the project on your Raspberry Pi unless you build it using a cross compiler. We offer pre-built `.deb` files for arm6, arm7 and arm64, you should use these where possible to avoid having to compile it by hand, or you can use a cross-compiler to build it on your PC then transfer the compiled binaries across.
## There are so many versions! Which deb file do i need for my Raspberry Pi?
Depending on which Raspberry Pi version you have, you will need to download a different release binary:
<table>
<tr>
<th>Raspberry Pi Model</th>
<th>Deb file to install</th>
<th>Arch</th>
</tr>
<tr><td>Raspberry Pi Zero/Zero W</td><td>`libdpp-x.x.x-linux-rpi-arm6.deb`</td><td>ARMv6</td></tr>
<tr><td>Raspberry Pi 3</td><td>`libdpp-x.x.x-linux-rpi-arm7hf.deb`</td><td>ARMv7HF</td></tr>
<tr><td>Raspberry Pi 4</td><td>`libdpp-x.x.x-linux-rpi-arm7hf.deb`</td><td>ARMv7HF</td></tr>
<tr><td>Raspberry Pi 4 with 64 Bit Linux</td><td>`libdpp-x.x.x-linux-rpi-arm64.deb`</td><td>ARM64</td></tr>
</table>
## Are other ARM devices supported?
Yes! We have confirmed that the D++ deb file will successfully install and operate on various forms of cellphone or non-pi ARM devices. If you get it working on any other devices please let us know and we can build a compatibility chart.
## Can I run a D++ bot in repl.it?
Yes! You can indeed run your bot in a repl.it container. [You can find a ready to go demo repl here](https://replit.com/@braindigitalis/dpp-demo-bot). We also have a [guide showing how to do this](https://dpp.dev/building-a-cpp-discord-bot-in-repl.html).
## Why do the "get" functions like "messages_get" return void rather than what I'm after?
All the functions that obtain data directly from Discord (as opposed to the cache) perform HTTPS requests and may have to wait, either for the request itself or for their turn in a queue to respect rate limits. As such, it does not make sense that they should return a value, as this would mean they block execution of your event. Instead, each has a lambda, a function handler which receives the result of the request, which you can then read from within that function to get the data you are interested in. Note that this result will arrive on a different thread to the one which made the request. If you instead want the function to return a value, use the methods ending with `_sync` that will block until they have a response. Note that these forms of call will throw an exception on failure.
## Can i use a user token with this library (as opposed to a bot token)?
No. This feature is not supported as it is against the Discord Terms Of Service, and therefore we have no plans to ever support it. You should not automate any user token. Some libraries used to support this but it is a legacy feature of those libraries (where still available) dating back to before Discord offered an official public API. Please be aware that if Discord ever catch you automating a user token (or making a user client that uses a bot token) they can and do ban people for this.

11
vendor/DPP/docpages/01_installing.md vendored Normal file
View File

@ -0,0 +1,11 @@
# Installing D++
There are many ways to install D++, either from a package manager, or from source. Please choose your desired option from the list below:
* \subpage install-linux-deb
* \subpage install-linux-rpm
* \subpage install-vcpkg
* \subpage install-arch-aur
* \subpage install-windows-zip
* \subpage install-xmake
* \subpage install-from-source

View File

@ -0,0 +1,14 @@
# Creating a Discord Bot
If you are wanting to build a bot using C++, you're in the right place! The fast and easy tutorials below will guide you through how to build a bot using the D++ library on either a UNIX-like (e.g. Linux) system with CMake or with Windows using Visual Studio 2019.
Click on a link below for a guide specifically for your system:
* \subpage creating-a-bot-application "Creating a Bot Token"
* \subpage build-a-discord-bot-windows-visual-studio "Building a discord bot in Windows using Visual Studio"
* \subpage build-a-discord-bot-windows-wsl "Building a discord bot in Windows using WSL (Windows Subsystem for Linux)"
* \subpage build-a-discord-bot-linux-clion "Building a discord bot in Linux using CLion"
* \subpage buildcmake "Building a Discord Bot using CMake/UNIX"
* \subpage buildmeson "Building a Discord Bot using Meson"
* \subpage building-a-cpp-discord-bot-in-repl "Creating a Discord bot in Repl.it"

View File

@ -0,0 +1,10 @@
# Example Programs
There are example programs here for all skill levels demonstrating a great many features of the bot. New examples are added frequently, please check regularly for new content.
* \subpage the-basics
* \subpage interactions-and-components
* \subpage music-and-audio
* \subpage misc
Is the example you are looking for missing from these sections? Pop over to our [discord server](https://discord.com/dpp) and let us know what you need... Or, even better, if you know how to do something and want to contribute an example of your own, just submit a PR!

View File

@ -0,0 +1,7 @@
# Advanced Reference
* \subpage clusters-shards-guilds "Clusters, Shards and Guilds"
* \subpage thread-model "Thread Model"
* \subpage coding-standards "Coding Style Standards"
* \subpage unit-tests "Unit Tests"
* \subpage lambdas-and-locals "Ownership of local variables and safely transferring into a lambda"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

77
vendor/DPP/docpages/INDEX.md vendored Normal file
View File

@ -0,0 +1,77 @@
# D++: A C++ Discord API Library for Bots
## What is D++ (DPP)?
D++ is a lightweight and simple library for Discord written in modern C++. It is designed to cover as much of the API specification as possible and to have a incredibly small memory footprint, even when caching large amounts of data.
It is created by the developer of [TriviaBot](https://triviabot.co.uk) and contributed to by a dedicated team of developers.
*This project is in stable development and accepting PRs and feature requests — Don't be a stranger! If you want to contribute, just get in touch via [GitHub](https://github.com/brainboxdotcc/DPP) or our official [Discord server](https://discord.gg/dpp)!*
<img src="code_editor.png" style="margin-top: 2rem; margin-bottom: 2rem"/><br />
## Downloads
The following downloads are for the most recent version:
* [Source Code](https://github.com/brainboxdotcc/DPP)
* [x64 Linux .deb (64 bit Debian, Ubuntu etc)](https://dl.dpp.dev/latest)
* [x86 Linux .deb (32 bit Debian, Ubuntu etc)](https://dl.dpp.dev/latest/linux-i386)
* [x64 Linux .rpm (64 bit Redhat, CentOS etc)](https://dl.dpp.dev/latest/linux-x64/rpm)
* [x86 Linux .rpm (32 bit Redhat, CentOS etc)](https://dl.dpp.dev/latest/linux-i386/rpm)
* [x64 Windows (64 bit vs2019 release build)](https://dl.dpp.dev/latest/win64-release-vs2019)
* [x64 Windows (64 bit vs2022 release build)](https://dl.dpp.dev/latest/win64-release-vs2022)
* [x64 Windows (64 bit vs2019 debug build)](https://dl.dpp.dev/latest/win64-debug-vs2019)
* [x64 Windows (64 bit vs2022 debug build)](https://dl.dpp.dev/latest/win64-debug-vs2022)
* [ARM6 Linux .deb (32 bit Raspberry Pi 1, 2)](https://dl.dpp.dev/latest/linux-rpi-arm6)
* [ARM7 Linux .deb (32 bit Raspberry Pi 3, 4)](https://dl.dpp.dev/latest/linux-rpi-arm7hf)
* [ARM64 Linux .deb (64 bit Raspberry Pi 4, Smartphones)](https://dl.dpp.dev/latest/linux-rpi-arm64)
You can find further releases in other architectures and formats or the source code on the [GitHub Repository](https://github.com/brainboxdotcc/DPP/releases). For a realtime JSON format list of all download links, click [here](https://dl.dpp.dev/json)
## Library features
* Support for Discord API v10
* Really small memory footprint
* Efficient caching system for guilds, channels, guild members, roles, users
* Sharding and clustering (Many shards, one process: specify the number of shards, or let the library decide)
* Highly optimised ETF (Erlang Term Format) support for very fast websocket throughput (*no other C++ Discord library has this!*)
* [Slash Commands/Interactions support](https://dpp.dev/slashcommands.html)
* [Voice support](https://dpp.dev/soundboard.html) (sending **and** receiving audio)
* The entire Discord API is available for use in the library
* Stable [Windows support](https://dpp.dev/buildwindows.html)
* Ready-made compiled packages for Windows, Raspberry Pi (ARM64/ARM7/ARMv6), Debian x86/x64 and RPM based distributions
* Highly scalable for large amounts of guilds and users
## Supported Operating Systems
### Linux
The library runs ideally on **Linux**.
### Mac OS X and FreeBSD
The library is well-functional and stable on **Mac OS X** and **FreeBSD** too.
### Raspberry Pi
For running your bot on a **Raspberry Pi**, we offer a prebuilt .deb package for ARM64, ARM6, and ARM7 so that you do not have to wait for it to compile.
### Windows
**Windows** is well-supported with ready-made compiled DLL and LIB files, please check out our [Windows Bot Template repository](https://github.com/brainboxdotcc/windows-bot-template). The Windows Bot repository can be cloned and integrated immediately into any Visual Studio 2019 and 2022 project in a matter of minutes.
### Other OS
The library should work fine on other operating systems as well, and if you run a D++ bot on something not listed here, please let us know!
## Getting started
* [GitHub Repository](https://github.com/brainboxdotcc/DPP)
* [Discord Server](https://discord.gg/dpp)
* [Frequently Asked Questions](/md_docpages_01_frequently_asked_questions.html)
* [Installing D++](/md_docpages_01_installing.html)
* [Example Programs](/md_docpages_03_example_programs.html)
## Architecture
* \ref clusters-shards-guilds
* \ref thread-model
## Learning Resources
* [C++ for JavaScript Developers](https://pawelgrzybek.com/cpp-for-javascript-developers/)
* [C++ In Four Hours](https://www.youtube.com/watch?v=vLnPwxZdW4Y&vl=en)

View File

@ -0,0 +1,178 @@
\page clusters-shards-guilds Clusters, Shards and Guilds
D++ takes a three-tiered highly scalable approach to bots, with three levels known as Clusters, Shards and Guilds as documented below.
\dot
digraph "Clusters, Shards and Guilds" {
node [colorscheme="blues9",fontname="helvetica"];
subgraph Bot {
node [style=filled, color=1];
label = "Bot"
"Bot" [shape=folder, label="Bot", bordercolor=black];
};
subgraph Processes {
node [style=filled, color=2];
label = "Processes"
"Bot" -> "Process 1"
"Bot" -> "Process 2"
"Process 1" [shape=record, label="Process"];
"Process 2" [shape=record, label="Process"];
};
subgraph Clusters {
node [style=filled, color=3];
label = "Clusters"
"Process 1" -> "Cluster 1"
"Process 2" -> "Cluster 2"
"Cluster 1" [shape=record, label="Cluster"];
"Cluster 2" [shape=record, label="Cluster"];
};
subgraph Shards {
node [style=filled, color=4];
label = "Shards"
"Shard 1" [shape=record, label="Shard"];
"Shard 2" [shape=record, label="Shard"];
"Shard 3" [shape=record, label="Shard"];
"Shard 4" [shape=record, label="Shard"];
"Shard 5" [shape=record, label="Shard"];
"Shard 6" [shape=record, label="Shard"];
"Shard 7" [shape=record, label="Shard"];
"Shard 8" [shape=record, label="Shard"];
"Cluster 1" -> "Shard 1"
"Cluster 1" -> "Shard 3"
"Cluster 2" -> "Shard 2"
"Cluster 2" -> "Shard 4"
"Cluster 1" -> "Shard 5"
"Cluster 1" -> "Shard 7"
"Cluster 2" -> "Shard 6"
"Cluster 2" -> "Shard 8"
};
subgraph Guilds {
node [style=filled, color=5];
label = "Guilds";
"Guild 1" [shape=record, label="Guild"];
"Guild 2" [shape=record, label="Guild"];
"Guild 3" [shape=record, label="Guild"];
"Guild 4" [shape=record, label="Guild"];
"Guild 5" [shape=record, label="Guild"];
"Guild 6" [shape=record, label="Guild"];
"Guild 7" [shape=record, label="Guild"];
"Guild 8" [shape=record, label="Guild"];
"Guild 9" [shape=record, label="Guild"];
"Guild 10" [shape=record, label="Guild"];
"Guild 11" [shape=record, label="Guild"];
"Guild 12" [shape=record, label="Guild"];
"Guild 13" [shape=record, label="Guild"];
"Guild 14" [shape=record, label="Guild"];
"Guild 15" [shape=record, label="Guild"];
"Guild 16" [shape=record, label="Guild"];
"Shard 1" -> "Guild 1"
"Shard 1" -> "Guild 5"
"Shard 2" -> "Guild 2"
"Shard 2" -> "Guild 6"
"Shard 3" -> "Guild 3"
"Shard 3" -> "Guild 7"
"Shard 4" -> "Guild 4"
"Shard 4" -> "Guild 8"
"Shard 5" -> "Guild 9"
"Shard 5" -> "Guild 11"
"Shard 6" -> "Guild 10"
"Shard 6" -> "Guild 12"
"Shard 7" -> "Guild 13"
"Shard 7" -> "Guild 15"
"Shard 8" -> "Guild 14"
"Shard 8" -> "Guild 16"
};
}
\enddot
## Clusters
A bot may be made of one or more clusters. Each cluster maintains a queue of commands waiting to be sent to Discord, a queue of replies from Discord for all commands executed, and zero or more **shards**. Usually, each process has one cluster, but the D++ library does not enforce this as a restriction. Small bots will require just one cluster. Clusters will split the required number of shards equally across themselves. There is no communication between clusters unless you add some yourself, they all remain independent without any central "controller" process. This ensures that there is no single point of failure in the design. Whenever you instantiate the library, you generally instantiate a cluster:
```cpp
#include <dpp/dpp.h>
int main()
{
/* This is a cluster */
dpp::cluster bot("Token goes here");
}
```
## Shards
A cluster contains zero or more shards. Each shard maintains a persistent websocket connection to Discord via a websocket, which receives all events the bot is made aware of, e.g. messages, channel edits, etc. Requests to the API on the other hand go out to Discord as separate HTTP requests.
Small bots will require only one shard and this is the default when you instantiate a cluster. The library will automatically determine and create the correct number of shards needed, if you do not configure it by hand. If you do want to specify a number of shards, you can specify this when creating a cluster:
```cpp
#include <dpp/dpp.h>
int main()
{
/* This is a cluster */
int total_shards = 10;
dpp::cluster bot("Token goes here", dpp::i_default_intents, total_shards);
}
```
Remember that if there are multiple clusters, the number of shards you request will be split equally across these clusters!
@note To spawn multiple clusters, you can specify this as the 4th and 5th parameter of the dpp::cluster constructor. You must do this, if you want this functionality. The library will not create additional clusters for you, as what you require is dependent upon your system specifications. It is your responsibility to somehow get the cluster id and total clusters into the process, e.g. via a command line argument. An example of this is shown below based on the cluster setup code of **TriviaBot**:
```cpp
#include <dpp/dpp.h>
#include <iostream>
#include <stdlib.h>
#include <getopt.h>
#include <string>
int main(int argc, char** argv)
{
int total_shards = 64;
int index;
char arg;
bool clusters_defined = false;
uint32_t clusterid = 0;
uint32_t maxclusters = 1;
/* Parse command line parameters using getopt() */
struct option longopts[] =
{
{ "clusterid", required_argument, NULL, 'c' },
{ "maxclusters", required_argument, NULL, 'm' },
{ 0, 0, 0, 0 }
};
opterr = 0;
while ((arg = getopt_long_only(argc, argv, "", longopts, &index)) != -1) {
switch (arg) {
case 'c':
clusterid = std::stoul(optarg);
clusters_defined = true;
break;
case 'm':
maxclusters = std::stoul(optarg);
break;
default:
std::cerr << "Unknown parameter '" << argv[optind - 1] << "'\n";
exit(1);
break;
}
}
if (clusters_defined && maxclusters == 0) {
std::cerr << "ERROR: You have defined a cluster id with -clusterid but no cluster count with -maxclusters.\n";
exit(2);
}
dpp::cluster bot("Token goes here", dpp::default_intents, total_shards, clusterid, maxclusters);
}
```
### Large Bot Sharding
Discord restricts how many shards you can connect to at any one time to one per five seconds, unless your bot is in at least 150,000 guilds. Once you reach 150,000 guilds, Discord allow your bot to connect to more guilds concurrently, and your number of shards must divide cleanly into this value. By default, at 150,000 guilds this concurrency value is 16 meaning D++ will attempt to connect 16 shards in parallel, then wait for all these to connect and then connect another 16, until all shards are connected. In practice, this means a large bot with many shards (read: hundreds!) will connect significantly faster after a full restart. **You do not need to manually configure large bot sharding and connection concurrency, the D++ library will handle this for you if you are able to use it**.
## Guilds
Guilds are what servers are known as to the Discord API. There can be up to **2500** of these per shard. Once you reach 2500 guilds on your bot, Discord force your bot to shard, the D++ library will automatically create additional shards to accomodate if not explicitly configured with a larger number. Discord *does not restrict sharding* to bots on 2500 guilds or above. You can shard at any size of bot, although it would be a waste of resources to do so unless it is required.

View File

@ -0,0 +1,116 @@
\page coding-standards Coding Style Standards
This page lists the coding style we stick to when maintaining the D++ library. If you are submitting a pull request or other code contribution to the library, you should stick to the styles listed below. If something is not covered here, ask on the [official discord server](https://discord.gg/dpp)!
## Class names, function names and method names
All class, variable/member, function and method names should use `snake_case`, similar to the style of the C++ standard library.
## Enums
Enums and their values should be `snake_case` as with class, function and method names. You do not need to use `enum class`, so make sure that enum values are prefixed with a prefix to make them unique and grouped within the IDE, e.g. `ll_debug`, `ll_trace` etc.
## Curly Braces, Brackets etc
Open curly braces on the same line as the keyword, for example:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
if (a == b) {
c();
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use a space after the comma in parameter lists, and after opening brackets and before closing brackets except when calling a function, e.g.:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
std::vector<std::string> clowns = { "pennywise", "bobo" };
evaluate_clown(clowns[0], evilness(2.5, factor));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Indentation
Indentation should always be tab characters. It is up to you how wide you set tab characters in your editor for your personal tastes. All code blocks delimited within curly braces should be indented neatly and uniformly.
## Constants and \#define macros
Constants and macros should be all `UPPERCASE` with `SNAKE_CASE` to separate words. Macros should not have any unexpected side effects.
## Comments
All comments should be in `doxygen` format (similar to javadoc). Please see existing class definitions for an example. You should use doxygen style comments in a class definition inside a header file, and can use any other comment types within the .cpp file. Be liberal with comments, especially if your code makes any assumptions!
## Symbol exporting
If you export a class which is to be accessible to users, be sure to prefix it with the `DPP_EXPORT` macro, for example:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
class DPP_EXPORT my_new_class {
public:
int hats;
int clowns;
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The `DPP_EXPORT` macro ensures that on certain platforms (notably Windows) the symbol is exported to be available to the library user.
## Public vs private vs protected
It is a design philosophy of D++ that everything possible in a class should be public, unless the user really does not need it (you should consider justifying in comments why) or user adjustment of the variable could badly break the functioning of the library. Avoid the use of accessors for setting/getting values in a class, except for bit fields, where you should provide accessors for setting and getting individual bits (for example, see `user.h`), or in the event you want to provide a "fluent" interface. The exception to this is where you want to provide a logic validation of a field, for example if you have a string field with a minimum and maximum length, you can provide a setter the user can *optionally use* which will validate their input.
## Exceptions
All exceptions thrown should derive from dpp::exception (see dpp/exception.h) - when validating string lengths, a string which is too long should be truncated using dpp::utility::utf8substr and any strings that are too short should throw a dpp::length_exception.
## Inheritance
Keep levels of inheritance low. If you need to inherit more than 3 levels deep, it is probable that the design could be simplified. Remember that at scale, there can be tens of millions of certain classes and each level of virtual nesting adds to the `vtable` of that object's instance in RAM.
## Bit field packing
Where discord provides boolean flags, if the user is expected to store many of the object in RAM, or in cache, you should pack all these booleans into bit fields (see `user.h` and `channel.h` for examples). In the event that the object is transient, such as an interaction or a message, packing the data into bit fields is counter intuitive. Remember that you should provide specific accessors for bit field values!
## Keep dependencies internal!
Where you are making use of an external dependency such as `opus` or `libssl`, do not place references to the types/structs, or the header files of these external libraries within the header files of D++. Doing so adds that library as a public dependency to the project (which is bad!). Instead make an opaque class, and/or forward-declare the structs (for examples see `sslclient.h` and `discordvoiceclient.h`).
## API type names
Where discord provide a name in PascalCase we should stick as closely to that name as possible but convert it to `snake_case`. For example, GuildMember would become `guild_member`.
## Don't introduce any platform-specific code
Do not introduce platform specific (e.g. windows only) code or libc functions. If you really must use these functions safely wrap them e.g. in `#ifdef _WIN32` and provide a cross-platform alternative so that it works for everyone.
## Select the right size type for numeric types
If a value will only hold values up to 255, use `uint8_t`. If a value cannot hold over 65536, use `uint16_t`. These types can help use a lot less ram at scale.
## Fluent design
Where possible, if you are adding methods to a class you should consider fluent design. Fluent design is the use of class methods tha return a reference to self (via `return *this`), so that you can chain object method calls together (in the way `dpp::message` and `dpp::embed` do). For example:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
class DPP_EXPORT my_new_class {
public:
int hats;
int clowns;
my_new_class& set_hats(int new_hats);
my_new_class& set_clowns(int new_clowns);
};
my_new_class& my_new_class::set_hats(int new_hats) {
hats = new_hats;
return *this;
}
my_new_class& my_new_class::set_clowns(int new_clowns) {
clowns = new_clowns;
return *this;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This would allow the user to do this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
dpp::my_new_class nc;
nc.set_hats(3).set_clowns(9001);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Keep all D++ related types in the dpp namespace
All types for the library should be within the `dpp` namespace. There are a couple of additional namespaces, e.g. `dpp::utility` for static standalone helper functions and helper classes, and `dpp::events` for internal websocket event handlers.
## Commit messages and Git
All pull requests ("PRs") should be submitted against the `dev` branch in GitHub. Its good to have descriptive commit messages, or PR titles so that other contributors can understand about your commit or the PR Created. Read [conventional commits](https://www.conventionalcommits.org/en/v1.0.0-beta.3/) for information on how we like to format commit messages.
All PRs must pass the [GitHub Actions](https://github.com/brainboxdotcc/DPP/actions) tests before being allowed to be merged. This is to ensure that no code committed into the project fails to compile on any of our officially supported platforms or architectures.

View File

@ -0,0 +1,56 @@
\page lambdas-and-locals Ownership of local variables and safely transferring into a lambda
If you are reading this page, you have likely been sent here by someone helping you diagnose why your bot is crashing or why seemingly invalid values are being passed into lambdas within your program that uses D++.
It is important to remember that when you put a lambda callback onto a function in D++, that this lambda will execute at some point in the **future**. As with all things in the future and as 80s Sci Fi movies will tell you, when you reach the future things may well have changed!
\image html delorean-time-travel.gif
To explain this situation and how it causes issues i'd like you to imagine the age old magic trick, where a magician sets a fine table full of cutlery, pots, pans and wine. He indicates to the audience that this is authentic, then with a whip of his wrist, he whips the tablecloth away, leaving the cutlery and other tableware in place (if he is any good as a magician!)
Now imagine the following code scenario. We will describe this code scenario as the magic trick above, in the steps below:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
bot.on_message_create([&bot](const dpp::message_create_t & event) {
int myvar = 0;
bot.message_create(dpp::message(event.msg.channel_id, "foobar"), [&](const auto & cc) {
myvar = 42;
});
});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In this scenario, the outer event, `on_message_create` is your tablecloth. The lambda inside the `bot.message_create` is the tableware and cutlery. The following chain of events happens in this code:
* The magician executes his magic trick (D++ the `bot.on_message_create entering` the outer lambda)
* Your code executes `bot.message_create()` inside this outer lambda
* D++ inserts your request to send a message into its queue, in another thread. The inner lambda, where you might later set `myvar = 42` is safely copied into the queue for later calling.
* The tablecloth is whipped away... in other words, `bot.on_message_create` ends, and all local variables including `myvar` become invalid
* At a later time (usually 80ms through to anything up to 4 seconds depending on rate limits!) the message is sent, and your inner lambda which was saved at the earlier step is called.
* Your inner lambda attempts to set `myvar` to 42... but `myvar` no longer exists, as the outer lambda has been destroyed....
* The table wobbles... the cutlery shakes... and...
* Best case scenario: you access invalid RAM no longer owned by your program by trying to write to `myvar`, and [your bot outright crashes horribly](https://www.youtube.com/watch?v=sm8qb2kP-fQ)!
* Worse case scenario: you silently corrupt ram and end up spending days trying to track down a bug that subtly breaks your bot...
The situation i am trying to describe here is one of object and variable ownership. When you call a lambda, **always assume that every non global reference outside of that lambda will be invalid when the lambda is called**! For any non-global variable always take a **copy** of the variable (not reference, or pointer). Global variables or those declared directly in `main()` are safe to pass as references.
For example, if we were to fix the broken code above, we could rewrite it like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
bot.on_message_create([&bot](const dpp::message_create_t & event) {
int myvar = 0;
bot.message_create(dpp::message(event.msg.channel_id, "foobar"), [myvar](const auto & cc) {
myvar = 42;
});
std::cout << "here\n";
});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note however that when you set myvar within the inner lambda, this does **not effect** the value of the var outside it. Lambdas should be considered self-contained silos, and as they execute in other threads should not be relied upon to set anything that exists **outside of that lambda**.
\warning Always avoid just using `[&]` in a lambda to access all in the scope above. It is unlikely that half of this scope will still even be valid by the time you get a look at it!
Similarly, and important to note, your program **will not wait for bot.message_create to send its message and call its lambda** before continuing on to print `here`. It will instantly insert the request into its queue and bail straight back out (see the steps above) and immediately print the text.
If you do want to get variables out of your lambda, create a class, or call a separate function, and pass what you need into that function from the lambda **by value** or alternatively, you can use `std::bind` to bind a lambda directly to an object's method instead (this is great for modular bots).
If you are stuck, as this is a complex subject please do feel free to ask on the [official support server](https://discord.gg/dpp)!

View File

@ -0,0 +1,81 @@
\page thread-model Thread Model
\dot
digraph "Thread Model" {
graph [ranksep=1];
node [colorscheme="blues9",fontname="helvetica"];
"Discord Events" -> "Your Program"
"Your Program" [style=filled, color=1, shape=rect]
"Cluster" [style=filled, color=1, shape=rect]
subgraph cluster_4 {
style=filled;
color=lightgrey;
node [style=filled,color=2]
"Your Program"
"Cluster"
label = "User Code";
}
subgraph cluster_0 {
style=filled;
color=lightgrey;
node [style=filled,color=4]
"Shard 1" [style=filled, color=4]
"Shard 2"
"Shard 3..."
label = "Shards (Each is a thread, one per 2500 discord guilds)";
}
subgraph cluster_1 {
style=filled
color=lightgrey;
node [style=filled,color=4]
"REST Requests"
"Request In Queue 1"
"Request In Queue 2"
"Request In Queue 3..."
"Request Out Queue"
label = "REST Requests (Each in queue, and the out queue, are threads)"
}
subgraph cluster_3 {
style=filled
color=lightgrey;
node [style=filled,color=4]
"Discord Events" [style=filled,color=4]
"User Callback Functions"
label = "Events and Callbacks"
}
"Cluster" [shape=rect]
"REST Requests" [shape=rect]
"Request In Queue 1" [shape=rect]
"Request In Queue 2" [shape=rect]
"Request In Queue 3..." [shape=rect]
"Shard 1" [shape=rect]
"Shard 2" [shape=rect]
"Shard 3..." [shape=rect]
"Request Out Queue" [shape=rect]
"Discord Events" [shape=rect]
"User Callback Functions" [shape=rect]
"Cluster" -> "REST Requests"
"Shard 1" -> "Discord Events"
"Shard 2" -> "Discord Events"
"Shard 3..." -> "Discord Events"
"Your Program" -> "Cluster"
"Cluster" -> "Shard 1"
"Cluster" -> "Shard 2"
"Cluster" -> "Shard 3..."
"REST Requests" -> "Request In Queue 1"
"REST Requests" -> "Request In Queue 2"
"REST Requests" -> "Request In Queue 3..."
"Request In Queue 1" -> "Request Out Queue"
"Request In Queue 2" -> "Request Out Queue"
"Request In Queue 3..." -> "Request Out Queue"
"Request Out Queue" -> "User Callback Functions"
"User Callback Functions" -> "Your Program"
}
\enddot

View File

@ -0,0 +1,28 @@
\page unit-tests Unit Tests
## Running Unit Tests
If you are adding functionality to DPP, make sure to run unit tests. This makes sure that the changes do not break anything. All pull requests must pass all unit tests before merging.
Before running test cases, create a test server for your test bot. You should:
* Make sure that the server only has you and your test bot, and no one else
* Give your bot the administrator permission
* Enable community for the server
* Make an event
* Create at least one voice channel
* Create at least one text channel
Then, set the following variables to the appropriate values. (Below is a fake token, don't bother trying to use it)
export DPP_UNIT_TEST_TOKEN="ODI2ZSQ4CFYyMzgxUzkzzACy.HPL5PA.9qKR4uh8po63-pjYVrPAvQQO4ln"
export TEST_GUILD_ID="907951970017480704"
export TEST_TEXT_CHANNEL_ID="907951970017480707"
export TEST_VC_ID="907951970017480708"
export TEST_USER_ID="826535422381391913"
export TEST_EVENT_ID="909928577951203360"
Then, after cloning and building DPP, run `cd build && ctest -VV` for unit test cases.
If you do not specify the `DPP_UNIT_TEST_TOKEN` environment variable, a subset of the tests will run which do not require discord connectivity.

View File

@ -0,0 +1,10 @@
\page install-from-source Building D++ From Source
The way you build D++ varies from system to system. Please follow the guide below for your OS:
* \subpage buildlinux "Building on Linux"
* \subpage buildwindows "Building on Windows"
* \subpage buildosx "Building on OSX"
* \subpage buildfreebsd "Building on FreeBSD"
@warning Note that you most likely don't need to build D++ from source if you're on Linux or Windows. We offer prebuilt binaries for these platforms and are listed in package managers! Check the downloads in the releases section on github.

49
vendor/DPP/docpages/building/freebsd.md vendored Normal file
View File

@ -0,0 +1,49 @@
\page buildfreebsd Building on FreeBSD
## 1. Toolchain
This project uses CMake. Install it with `pkg install cmake`
## 2. Install External Dependencies
Your FreeBSD base system should have all the required dependencies installed by default.
For voice support, additional dependencies are required
pkg install libsodium opus pkgconf
## 3. Build Source Code
cmake -B ./build
cmake --build ./build -j8
Replace the number after -j with a number suitable for your setup, usually the same as the number of cores on your machine. `cmake` will fetch any dependencies that are required for you and ensure they are compiled alongside the library.
## 4. Install globally
cd build; make install
## 5. Installation to a different directory
If you want to install the library, its dependencies and header files to a different directory, specify this directory when running `cmake`:
cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install
Then once the build is complete, run `make install` to install to the location you specified.
## 7. Using the library
Once installed, you can make use of the library in standalone programs simply by including it and linking to it:
clang++ -std=c++17 -ldpp mydppbot.cpp -o dppbot
The important flags in this command-line are:
* `-std=c++17` - Required to compile the headers
* `-ldpp` - Link to libdpp.dylib
* `mydppbot.cpp` - Your source code
* `dppbot` - The name of the executable to make
Of course, this is just a proof of concept - you should really use a more robust build system like [`cmake`](@ref buildcmake).
If you are having trouble setting up CMake, you can try [our template bot](https://github.com/brainboxdotcc/templatebot).
**Have fun!**

42
vendor/DPP/docpages/building/linux.md vendored Normal file
View File

@ -0,0 +1,42 @@
\page buildlinux Building on Linux
\note You might not need to build a copy of the library for Linux - precompiled deb files for 64 bit and 32 bit Debian and Ubuntu are provided in the GitHub version releases. Unless you are on a different Linux distribution which does not support the installation of deb files, or wish to submit fixes and enhancements to the library itself you should have an easier time installing the precompiled version instead.
## 1. Build Source Code
cmake -B ./build
cmake --build ./build -j8
Replace the number after -j with a number suitable for your setup, usually the same as the number of cores on your machine. `cmake` will fetch any dependencies that are required for you and ensure they are compiled alongside the library.
## 2. Install to /usr/local/include and /usr/local/lib
cd build; sudo make install
## 3. Installation to a different directory
If you want to install the library, its dependencies and header files to a different directory, specify this directory when running `cmake`:
cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install
Then once the build is complete, run `make install` to install to the location you specified.
## 4. Using the library
Once installed to the /usr/local directory, you can make use of the library in standalone programs simply by including it and linking to it:
g++ -std=c++17 mydppbot.cpp -o dppbot -ldpp
The important flags in this command-line are:
* `-std=c++17` - Required to compile the headers
* `mydppbot.cpp` - Your source code
* `dppbot` - The name of the executable to make
Of course, this is just a proof of concept — you should really use a more robust build system like GNU `make` or [`cmake`](@ref buildcmake).
If you are having trouble setting up CMake, you can try [our template bot](https://github.com/brainboxdotcc/templatebot).
**Have fun!**

55
vendor/DPP/docpages/building/osx.md vendored Normal file
View File

@ -0,0 +1,55 @@
\page buildosx Building on OSX
## 1. Toolchain
Before compiling make sure you have all the tools installed.
1. To install the dependencies, this guide will use homebrew which has [installation instructions on their project page](https://brew.sh/).
2. This project uses CMake to generate the makefiles. Install it with `brew install cmake`.
## 2. Install External Dependencies
brew install openssl
For voice support, additional dependencies are required:
brew install libsodium opus
## 3. Build Source Code
cmake -B ./build
cmake --build ./build -j8
Replace the number after -j with a number suitable for your setup, usually the same as the number of cores on your machine. `cmake` will fetch any dependencies that are required for you and ensure they are compiled alongside the library.
## 4. Install globally
cd build; sudo make install
## 5. Installation to a different directory
If you want to install the library, its dependencies and header files to a different directory, specify this directory when running `cmake`:
cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install
Then once the build is complete, run `make install` to install to the location you specified.
## 6. Using the library
Once installed, you can make use of the library in standalone programs simply by including it and linking to it:
clang++ -std=c++17 -ldpp mydppbot.cpp -o dppbot
The important flags in this command-line are:
* `-std=c++17` - Required to compile the headers
* `-ldpp` - Link to libdpp.dylib
* `mydppbot.cpp` - Your source code
* `dppbot` - The name of the executable to make
Of course, this is just a proof of concept - you should really use a more robust build system like GNU `make` or [`cmake`](@ref buildcmake).
If you are having trouble setting up CMake, you can try [our template bot](https://github.com/brainboxdotcc/templatebot).
**Have fun!**

29
vendor/DPP/docpages/building/windows.md vendored Normal file
View File

@ -0,0 +1,29 @@
\page buildwindows Building on Windows
To build on windows follow these steps *exactly*. The build process depends on specific libraries being installed on your system in specific locations.
## Wait a minute! Read this first!
\warning **You do not need to follow this tutorial unless you plan to contribute to or modify the library itself**. Unless you consider yourself an **advanced user** with a specific **requirement to build from source** you should [obtain a pre-made visual studio template containing the latest D++ build (for 32 and 64 bit, release and debug profiles) by clicking here](https://github.com/brainboxdotcc/windows-bot-template/) and completely skip this guide! Instead, read \ref build-a-discord-bot-windows-visual-studio.
## If you are absolutely sure you need this guide, read on:
1. Make sure you have Visual Studio 2019 or Visual Studio 2022. The Community, Professional or Enterprise versions all work, however you will probably want to install Community. You do **NOT** want to use *Visual Studio Code* for this. You can [download the correct version here](https://visualstudio.microsoft.com/downloads/).
2. Check out the DPP project source using git
3. From within Visual Studio 2019, click the "File" menu, choose "Open" then "CMake", and select the CMakeLists.txt within the project folder
\image html winbuild_1.png
\image html winbuild_2.png
4. Go to the "Build" menu and choose "Build all" or just press F7
\image html winbuild_3.png
5. Check that compilation succeeded. You may now use the library in your projects!
\image html winbuild_4.png
## Troubleshooting
* If you do not have an option to open the CMakeLists.txt, ensure that you have installed the C++ development portions of visual studio (not just web development portions) with at least the default options.
* If the project does not build, please ask for help on the [official discord server](https://discord.gg/dpp).
## After compiling
After compilation you can directly reference the compiled project in your own CMakeLists.txt as a library or use the lib/dll/headers as you wish. Note that `openssl` and `zlib` will also be an indirect dependency of your program (as `DLL` files) and should be copied alongside `dpp.dll`.

View File

@ -0,0 +1,47 @@
<?php
$githubApi = "https://api.github.com/repos/brainboxdotcc/dpp/releases";
$json = json_decode(
file_get_contents(
$githubApi,
false,
stream_context_create(
[
"http" => [
"method" => "GET",
"header" => "User-Agent: DPP/Website"
]
]
)
)
);
$downloads = 0;
foreach ($json as $index => $release) {
$releaseDownloads = 0;
foreach ($release->assets as $asset) {
$releaseDownloads += $asset->download_count;
}
$downloads += $releaseDownloads;
}
header("Content-Type: image/svg+xml");
echo <<<IMG
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="106" height="20" role="img" aria-label="downloads: {$downloads}">
<title>downloads: {$downloads}</title>
<linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r">
<rect width="106" height="20" rx="3" fill="#fff"/>
</clipPath>
<g clip-path="url(#r)">
<rect width="69" height="20" fill="#555"/><rect x="69" width="37" height="20" fill="#97ca00"/>
<rect width="106" height="20" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
<text aria-hidden="true" x="355" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="590">downloads</text>
<text x="355" y="140" transform="scale(.1)" fill="#fff" textLength="590">downloads</text>
<text aria-hidden="true" x="865" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">{$downloads}</text>
<text x="865" y="140" transform="scale(.1)" fill="#fff" textLength="270">{$downloads}</text>
</g>
</svg>
IMG;

View File

@ -0,0 +1,68 @@
<?php
// Force no caching of the download
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Status: 200 OK");
// Split up url and set defaults
list($version, $arch, $type) = explode('/', preg_replace('/https:\/\/dl\.dpp\.dev\//', '', $_SERVER['REDIRECT_SCRIPT_URI']), 3);
$version = !empty($version) ? $version : 'latest';
$arch = !empty($arch) ? $arch : 'linux-x64';
$type = !empty($type) ? $type : 'deb';
$urls = [];
// All windows downloads are of type 'zip', if not specified, it is defaulted
if (preg_match('/^win/i', $arch) && $type === 'deb') {
$type = 'zip';
}
// the short word 'win' is a shorthand for the vs2019 release build
if ($arch === 'win') {
$arch = 'win32-release-vs2019';
}
// A crontab keeps this updated so we only perform one github api request per 5 minutes
$json = json_decode(file_get_contents('release.json'));
// If the user asked for 'latest', we find out what the latest release tag name is
if ($version === 'latest') {
$version = $json[0]->tag_name;
}
// Build search filename
$searchName = 'libdpp-' . preg_replace('/^v/', '', $version) . '-' . $arch . '.' . $type;
// Iterate list of release artifacts across all releases
foreach ($json as $index => $release) {
foreach ($release->assets as $index2 => $asset) {
$url = $asset->browser_download_url;
$name = $asset->name;
$thisVersion = $release->tag_name;
// We found a matching file, stream it to the user
if (strtoupper($searchName) == strtoupper($name)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $name . '"');
readfile($url);
exit;
}
$urls[] = [
'name' => $name,
'url' => $url,
'version' => $thisVersion,
];
}
}
if ($version === 'json') {
header('Content-Type: application/json');
echo json_encode($urls);
} else {
// Nothing found, offer up some useful info
foreach ($urls as $thisUrl) {
printf("%s - <a href='%s' target='_blank'>%s</a><br />", $thisUrl['version'], $thisUrl['url'], $thisUrl['name']);
}
}

View File

@ -0,0 +1,14 @@
\page interactions-and-components Interactions And Components
The example programs listed here demonstrate lots of things to do with interactions, application commands (slash commands) and message components. If you're looking to make your bot **modern and user friendly** these examples are what you need.
* \subpage slashcommands "Using Slash Commands and Interactions"
* \subpage context-menu "Context Menus"
* \subpage subcommands "Slash command sub-commands"
* \subpage components "Using button components"
* \subpage components3 "Using select menu components"
* \subpage components2 "Advanced components"
* \subpage modal-dialog-interactions "Modal Dialogs"
* \subpage commandhandler "Unified message/slash command handler"
* \subpage application-command-autocomplete "Slash command auto completion"
* \subpage discord-application-command-file-upload "Using file parameters in slash commands"

View File

@ -0,0 +1,72 @@
\page application-command-autocomplete Slash command auto completion
Discord now supports sending auto completion lists for slash command choices. To use this feature you can use code such as the example below:
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main()
{
dpp::cluster bot("token");
bot.on_log(dpp::utility::cout_logger());
bot.on_ready([&bot](const dpp::ready_t & event) {
if (dpp::run_once<struct register_bot_commands>()) {
/* Create a new global command once on ready event */
bot.global_command_create(dpp::slashcommand("blep", "Send a random adorable animal photo", bot.me.id)
.add_option(
/* If you set the auto complete setting on a command option, it will trigger the on_autocomplete
* event whenever discord needs to fill information for the choices. You cannot set any choices
* here if you set the auto complete value to true.
*/
dpp::command_option(dpp::co_string, "animal", "The type of animal").set_auto_complete(true)
)
);
}
});
/* The interaction create event is fired when someone issues your commands */
bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) {
/* Check which command they ran */
if (event.command.get_command_name() == "blep") {
/* Fetch a parameter value from the command parameters */
std::string animal = std::get<std::string>(event.get_parameter("animal"));
/* Reply to the command. There is an overloaded version of this
* call that accepts a dpp::message so you can send embeds.
*/
event.reply("Blep! You chose " + animal);
}
});
/* The on_autocomplete event is fired whenever discord needs information to fill in a command options's choices.
* You must reply with a REST event within 500ms, so make it snappy!
*/
bot.on_autocomplete([&bot](const dpp::autocomplete_t & event) {
for (auto & opt : event.options) {
/* The option which has focused set to true is the one the user is typing in */
if (opt.focused) {
/* In a real world usage of this function you should return values that loosely match
* opt.value, which contains what the user has typed so far. The opt.value is a variant
* and will contain the type identical to that of the slash command parameter.
* Here we can safely know it is string.
*/
std::string uservalue = std::get<std::string>(opt.value);
bot.interaction_response_create(event.command.id, event.command.token, dpp::interaction_response(dpp::ir_autocomplete_reply)
.add_autocomplete_choice(dpp::command_option_choice("squids", "lots of squids"))
.add_autocomplete_choice(dpp::command_option_choice("cats", "a few cats"))
.add_autocomplete_choice(dpp::command_option_choice("dogs", "bucket of dogs"))
.add_autocomplete_choice(dpp::command_option_choice("elephants", "bottle of elephants"))
);
bot.log(dpp::ll_debug, "Autocomplete " + opt.name + " with value '" + uservalue + "' in field " + event.name);
break;
}
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~

View File

@ -0,0 +1,68 @@
\page commandhandler Using a command handler object
If you have many commands in your bot, and want to handle commands from multiple sources (for example modern slash commands, and more regular
prefixed channel messages) you should consider instantiating a dpp::commandhandler object. This object can be used to automatically route
commands and their parameters to functions in your program. A simple example of using this object to route commands is shown below, and will
route both the /ping (global slash command) and .ping (prefixed channel message command) to a lambda where a reply can be generated.
\note This example automatically hooks the dpp::cluster::on_message_create and dpp::cluster::on_slashcommand events. This can be overridden if needed to allow you to still make use of these functions for your own code, if you need to do this please see the constructor documentation for dpp::commandhandler.
Note that because the dpp::commandhandler::add_command method accepts a std::function as the command handler, you may point a command handler
at a simple lambda (as shown in this example), a function pointer, or an instantiated class method of an object. This is extremely flexible
and allows you to decide how and where commands should be routed, either to an object oriented system or to a lambda based system.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main()
{
dpp::cluster bot("token");
bot.on_log(dpp::utility::cout_logger());
/* Create command handler, and specify prefixes */
dpp::commandhandler command_handler(&bot);
/* Specifying a prefix of "/" tells the command handler it should also expect slash commands */
command_handler.add_prefix(".").add_prefix("/");
bot.on_ready([&command_handler](const dpp::ready_t &event) {
command_handler.add_command(
/* Command name */
"ping",
/* Parameters */
{
{"testparameter", dpp::param_info(dpp::pt_string, true, "Optional test parameter") }
},
/* Command handler */
[&command_handler](const std::string& command, const dpp::parameter_list_t& parameters, dpp::command_source src) {
std::string got_param;
if (!parameters.empty()) {
got_param = std::get<std::string>(parameters[0].second);
}
command_handler.reply(dpp::message("Pong! -> " + got_param), src);
},
/* Command description */
"A test ping command",
/* Guild id (omit for a global command) */
819556414099554344
);
/* NOTE: We must call this to ensure slash commands are registered.
* This does a bulk register, which will replace other commands
* that are registered already!
*/
command_handler.register_commands();
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,54 @@
\page components Using button components
Discord's newest features support sending buttons alongside messages, which when clicked by the user trigger an interaction which is routed by
D++ as an on_button_click event. To make use of this, use code as in this example.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iostream>
#include <dpp/message.h>
int main() {
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* Message handler to look for a command called !button */
bot.on_message_create([&bot](const dpp::message_create_t & event) {
if (event.msg.content == "!button") {
/* Create a message containing an action row, and a button within the action row. */
bot.message_create(
dpp::message(event.msg.channel_id, "this text has buttons").add_component(
dpp::component().add_component(
dpp::component().set_label("Click me!").
set_type(dpp::cot_button).
set_emoji(u8"😄").
set_style(dpp::cos_danger).
set_id("myid")
)
)
);
}
});
/* When a user clicks your button, the on_button_click event will fire,
* containing the custom_id you defined in your button.
*/
bot.on_button_click([&bot](const dpp::button_click_t & event) {
/* Button clicks are still interactions, and must be replied to in some form to
* prevent the "this interaction has failed" message from Discord to the user.
*/
event.reply("You clicked: " + event.custom_id);
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When the feature is functioning, the code below will produce buttons on the reply message like in the image below:
\image html button.png

View File

@ -0,0 +1,55 @@
\page components2 Advanced components
This example demonstrates receiving button clicks and sending response messages.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
using json = nlohmann::json;
int main() {
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); // Privileged intent required to receive message content
bot.on_log(dpp::utility::cout_logger());
bot.on_button_click([&bot](const dpp::button_click_t & event) {
if (event.custom_id == "10") {
event.reply(dpp::message("Correct").set_flags(dpp::m_ephemeral));
} else {
event.reply(dpp::message("Incorrect").set_flags(dpp::m_ephemeral));
}
});
bot.on_message_create([&bot](const dpp::message_create_t & event) {
if (event.msg.content == "!ping2") {
bot.message_create(
dpp::message(event.msg.channel_id, "What is 5+5?").add_component(
dpp::component().add_component(
dpp::component().set_label("9").
set_style(dpp::cos_primary).
set_id("9")
).add_component(
dpp::component().set_label("10").
set_style(dpp::cos_primary).
set_id("10")
).add_component(
dpp::component().set_label("11").
set_style(dpp::cos_primary).
set_id("11")
)
)
);
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This code will send a different message for correct and incorrect answers.
\image html button_2.png

View File

@ -0,0 +1,47 @@
\page components3 Using select menu components
This example demonstrates receiving select menu clicks and sending response messages.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
using json = nlohmann::json;
int main() {
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* Message handler to look for a command called !select */
bot.on_message_create([&bot](const dpp::message_create_t & event) {
if (event.msg.content == "!select") {
/* Create a message containing an action row, and a select menu within the action row. */
dpp::message m(event.msg.channel_id, "this text has a select menu");
m.add_component(
dpp::component().add_component(
dpp::component().set_type(dpp::cot_selectmenu).
set_placeholder("Pick something").
add_select_option(dpp::select_option("label1","value1","description1").set_emoji(u8"😄")).
add_select_option(dpp::select_option("label2","value2","description2").set_emoji(u8"🙂")).
set_id("myselid")
)
);
bot.message_create(m);
}
});
/* When a user clicks your select menu , the on_select_click event will fire,
* containing the custom_id you defined in your select menu.
*/
bot.on_select_click([&bot](const dpp::select_click_t & event) {
/* Select clicks are still interactions, and must be replied to in some form to
* prevent the "this interaction has failed" message from Discord to the user.
*/
event.reply("You clicked " + event.custom_id + " and chose: " + event.values[0]);
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,51 @@
\page context-menu Context Menus
Context menus are application commands that appear on the context menu (right click or tap) of users or messages to perform context-specific actions. They can be created using `dpp::slashcommand`. Once you create a context menu, try right-clicking either a user or message to see it in your server!
\image html context_menu_user_command.png
The following example shows how to create and handle **user context menus**.
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iostream>
int main()
{
dpp::cluster bot("token");
bot.on_log(dpp::utility::cout_logger());
bot.on_ready([&bot](const dpp::ready_t &event) {
if (dpp::run_once<struct register_bot_commands>()) {
/* Register the command */
bot.guild_command_create(
dpp::slashcommand()
.set_type(dpp::ctxm_user)
.set_name("High Five")
.set_application_id(bot.me.id),
857692897221033129 // you need to put your guild-id in here
);
}
});
/* Use the on_user_context_menu event to look for user context menu actions */
bot.on_user_context_menu([&](const dpp::user_context_menu_t &event) {
/* check if the context menu name is High Five */
if (event.command.get_command_name() == "High Five") {
dpp::user user = event.get_user(); // the user who the command has been issued on
dpp::user author = event.command.get_issuing_user(); // the user who clicked on the context menu
event.reply(author.get_mention() + " slapped " + user.get_mention());
}
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~
It registers a guild command that can be called by right-click a user and click on the created menu.
\image html context_menu_user_command_showcase.png

View File

@ -0,0 +1,78 @@
\page modal-dialog-interactions Modal Dialog Interactions
Modal dialog interactions are a new Discord API feature that allow you to have pop-up windows which prompt the user to input information. Once the user has filled in this information, your program will receive an `on_form_submit` event which will contain the data which was input. You must use a slash command interaction response to submit your modal form data to Discord, via the `on_slashcommand` event. From here calling the `dialog` method of the `interaction_create_t` event object will trigger the dialog to appear.
Each dialog box may have up to five rows of input fields. The example below demonstrates a simple setup with just one text input:
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iostream>
int main(int argc, char const *argv[])
{
dpp::cluster bot("token");
bot.on_log(dpp::utility::cout_logger());
bot.on_ready([&](const dpp::ready_t & event) {
if (dpp::run_once<struct register_bot_commands>()) {
/* Create a slash command and register it as a global command */
bot.global_command_create(dpp::slashcommand("dialog", "Make a modal dialog box", bot.me.id));
}
});
bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) {
/* Check for our /dialog command */
if (event.command.get_command_name() == "dialog") {
/* Instantiate an interaction_modal_response object */
dpp::interaction_modal_response modal("my_modal", "Please enter stuff");
/* Add a text component */
modal.add_component(
dpp::component().
set_label("Short type rammel").
set_id("field_id").
set_type(dpp::cot_text).
set_placeholder("gumd").
set_min_length(5).
set_max_length(50).
set_text_style(dpp::text_short)
);
/* Add another text component in the next row, as required by Discord */
modal.add_row();
modal.add_component(
dpp::component().
set_label("Type rammel").
set_id("field_id2").
set_type(dpp::cot_text).
set_placeholder("gumf").
set_min_length(1).
set_max_length(2000).
set_text_style(dpp::text_paragraph)
);
/* Trigger the dialog box. All dialog boxes are ephemeral */
event.dialog(modal);
}
});
/* This event handles form submission for the modal dialog we create above */
bot.on_form_submit([&](const dpp::form_submit_t & event) {
/* For this simple example we know the first element of the first row ([0][0]) is value type string.
* In the real world it may not be safe to make such assumptions!
*/
std::string v = std::get<std::string>(event.components[0].components[0].value);
dpp::message m;
m.set_content("You entered: " + v).set_flags(dpp::m_ephemeral);
/* Emit a reply. Form submission is still an interaction and must generate some form of reply! */
event.reply(m);
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~
If you compile and run this program and wait for the global command to register, typing `/dialog` will present you with a dialog box like the one below:
\image html modal_dialog.png

View File

@ -0,0 +1,61 @@
\page slashcommands Using Slash Commands and Interactions
Slash commands and interactions are a newer feature of Discord which allow bot's commands to be registered centrally within the system and for users to easily explore and get help with available commands through the client itself.
To add a slash command you should use the dpp::cluster::global_command_create method for global commands (available to all guilds) or dpp::cluster::guild_command_create to create a local command (available only to one guild).
When a user issues these commands the reply will arrive via the `on_slashcommand` event which you can hook, and take action when you see your commands. It is possible to reply to an interaction by using either the dpp::interaction_create_t::reply method, or by manually instantiating an object of type dpp::interaction_response and attaching a dpp::message object to it.
dpp::interaction_create_t::reply has two overloaded versions of the method, one of which accepts simple std::string replies, for basic text-only messages (if your message is 'ephemeral' you must use this) and one which accepts a dpp::message for more advanced replies. Please note that at present, Discord only supports a small subset of message and embed features within an interaction response object.
\note You can also use the unified command handler, which lets you combine channel based message commands and slash commands under the same lambda with the same code like they were one and the same. Note that after August of 2022 Discord will be discouraging bots from using commands that are prefixed messages via means of a privileged message intent. It is advised that you exclusively use slash commands, or the unified handler with only a prefix of "/" going forward for any new bots you create and look to migrating existing bots to this setup.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main()
{
dpp::cluster bot("token");
bot.on_log(dpp::utility::cout_logger());
/* The event is fired when someone issues your commands */
bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) {
/* Check which command they ran */
if (event.command.get_command_name() == "blep") {
/* Fetch a parameter value from the command parameters */
std::string animal = std::get<std::string>(event.get_parameter("animal"));
/* Reply to the command. There is an overloaded version of this
* call that accepts a dpp::message so you can send embeds.
*/
event.reply(std::string("Blep! You chose") + animal);
}
});
bot.on_ready([&bot](const dpp::ready_t & event) {
if (dpp::run_once<struct register_bot_commands>()) {
/* Create a new global command on ready event */
dpp::slashcommand newcommand("blep", "Send a random adorable animal photo", bot.me.id);
newcommand.add_option(
dpp::command_option(dpp::co_string, "animal", "The type of animal", true).
add_choice(dpp::command_option_choice("Dog", std::string("animal_dog"))).
add_choice(dpp::command_option_choice("Cat", std::string("animal_cat"))).
add_choice(dpp::command_option_choice("Penguin", std::string("animal_penguin")
)
)
);
/* Register the command */
bot.global_command_create(newcommand);
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\note For demonstration purposes, and small bots, this code is OK, but in the real world once your bot gets big, it's not recommended to create slash commands in the `on_ready` event because it gets called often (discord forces reconnections and sometimes these do not resume). You could for example add a commandline parameter to your bot (`argc`, `argv`) so that if you want the bot to register commands it must be launched with a specific command line argument.

View File

@ -0,0 +1,78 @@
\page subcommands Using sub-commands in slash commands
This demonstrates how to use sub-commands within slash commands. Also shown below is an example of how to get a "resolved" parameter without having to use the cache or an extra API call.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iostream>
int main() {
dpp::cluster bot("token");
bot.on_log(dpp::utility::cout_logger());
/* Executes on ready. */
bot.on_ready([&bot](const dpp::ready_t & event) {
if (dpp::run_once<struct register_bot_commands>()) {
/* Define a slash command. */
dpp::slashcommand image("image", "Send a specific image.", bot.me.id);
image.add_option(
/* Create a subcommand type option for "dog". */
dpp::command_option(dpp::co_sub_command, "dog", "Send a picture of a dog.").
add_option(dpp::command_option(dpp::co_user, "user", "User to turn into a dog.", false))
);
image.add_option(
/* Create another subcommand type option for "cat". */
dpp::command_option(dpp::co_sub_command, "cat", "Send a picture of a cat.").
add_option(dpp::command_option(dpp::co_user, "user", "User to turn into a cat.", false))
);
/* Create command */
bot.global_command_create(image);
}
});
/* Use the on_slashcommand event to look for commands */
bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) {
dpp::interaction interaction = event.command;
dpp::command_interaction cmd_data = interaction.get_command_interaction();
/* Check if the command is the image command. */
if (interaction.get_command_name() == "image") {
/* Get the sub command */
auto subcommand = cmd_data.options[0];
/* Check if the subcommand is "dog" */
if (subcommand.name == "dog") {
/* Checks if the subcommand has any options. */
if (!subcommand.options.empty()) {
/* Get the user from the parameter */
dpp::user user = interaction.get_resolved_user(
subcommand.get_value<dpp::snowflake>(0)
);
event.reply(user.get_mention() + " has now been turned into a dog.");
} else {
/* Reply if there were no options.. */
event.reply("No user specified");
}
}
/* Check if the subcommand is "cat" */
if (subcommand.name == "cat") {
/* Checks if the subcommand has any options. */
if (!subcommand.options.empty()) {
/* Get the user from the parameter */
dpp::user user = interaction.get_resolved_user(
subcommand.get_value<dpp::snowflake>(0)
);
event.reply(user.get_mention() + " has now been turned into a cat.");
} else {
/* Reply if there were no options.. */
event.reply("No user specified");
}
}
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,48 @@
\page discord-application-command-file-upload Using file parameters for application commands (slash commands)
The program below demonstrates how to use the 'file' type parameter to an application command (slash command).
You must first get the file_id via std::get, and then you can find the attachment details in the 'resolved'
section, `event.command.resolved`.
The file is uploaded to Discord's CDN so if you need it locally you should fetch the `.url` value, e.g. by using
something like `dpp::cluster::request()`.
~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main()
{
dpp::cluster bot("token");
bot.on_log(dpp::utility::cout_logger());
bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) {
if (event.command.type == dpp::it_application_command) {
dpp::command_interaction cmd_data = std::get<dpp::command_interaction>(event.command.data);
if (cmd_data.name == "show") {
dpp::snowflake file_id = std::get<dpp::snowflake>(event.get_parameter("file"));
auto iter = event.command.resolved.attachments.find(file_id);
if (iter != event.command.resolved.attachments.end()) {
const dpp::attachment& att = iter->second;
event.reply(att.url);
}
}
}
});
bot.on_ready([&bot](const dpp::ready_t & event) {
if (dpp::run_once<struct register_bot_commands>()) {
dpp::slashcommand newcommand("show", "Show an uploaded file", bot.me.id);
newcommand.add_option(dpp::command_option(dpp::co_attachment, "file", "Select an image"));
bot.global_command_create(newcommand);
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,9 @@
\page misc Miscellaneous Examples
This section lists examples that do not fit neatly into any of the categories above.
* \subpage making_a_http_request "Making arbitrary HTTP requests using D++"
* \subpage spdlog "Integrating with spdlog"
* \subpage caching-messages "Caching messages"
* \subpage collecting-reactions "Collecting Reactions"
* \subpage cpp-eval-command-discord "Making an eval command in C++"

View File

@ -0,0 +1,61 @@
\page caching-messages Caching Messages
By default D++ does not cache messages. The example program below demonstrates how to instantiate a custom cache using dpp::cache which will allow you to cache messages and query the cache for messages by ID.
This can be adjusted to cache any type derived from dpp::managed including types you define yourself.
@note This example will cache and hold onto messages forever! In a real world situation this would be bad. If you do use this,
you should use the dpp::cache::remove() method periodically to remove stale items. This is left out of this example as a learning
exercise to the reader. For further reading please see the documentation of dpp::cache
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <sstream>
int main() {
/* Create bot */
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
/* Create a cache to contain types of dpp::message */
dpp::cache<dpp::message> message_cache;
bot.on_log(dpp::utility::cout_logger());
/* Message handler */
bot.on_message_create([&](const dpp::message_create_t &event) {
/* Make a permanent pointer using new, for each message to be cached */
dpp::message* m = new dpp::message();
/* Store the message into the pointer by copying it */
*m = event.msg;
/* Store the new pointer to the cache using the store() method */
message_cache.store(m);
/* Simple ghetto command handler. In the real world, use slashcommand or commandhandler here. */
std::stringstream ss(event.msg.content);
std::string cmd;
dpp::snowflake msg_id;
ss >> cmd;
/* Look for our command */
if (cmd == "!get") {
ss >> msg_id;
/* Search our cache for a cached message */
dpp::message* find_msg = message_cache.find(msg_id);
if (find_msg != nullptr) {
/* Found a cached message, echo it out */
bot.message_create(dpp::message(event.msg.channel_id, "This message had the following content: " + find_msg->content));
} else {
/* Nothing like that here. Have you checked under the carpet? */
bot.message_create(dpp::message(event.msg.channel_id, "There is no message cached with this ID"));
}
}
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~

View File

@ -0,0 +1,55 @@
\page collecting-reactions Collecting Reactions
D++ comes with many useful helper classes, but amongst these is something called dpp::collector. Collector is a template which can be specialised to automatically collect objects of a pre-determined type from events for a specific interval of time. Once this time period is up, or the class is otherwise signalled, a method is called with the complete set of collected objects.
In the example below we will use it to collect all reactions on a message.
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
/* To create a collector we must derive from dpp::collector. As dpp::collector is a complicated template,
* various pre-made forms exist such as this one, reaction_collector.
*/
class react_collector : public dpp::reaction_collector {
public:
/* Collector will run for 20 seconds */
react_collector(dpp::cluster* cl, snowflake id) : dpp::message_collector(cl, 20, id) { }
/* On completion just output number of collected reactions to as a message. */
virtual void completed(const std::vector<dpp::collected_reaction>& list) {
if (list.size()) {
owner->message_create(dpp::message(list[0].channel_id, "I collected " + std::to_string(list.size()) + " reactions!"));
} else {
owner->message_create(dpp::message("... I got nothin'."));
}
}
};
int main() {
/* Create bot */
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
/* Pointer to reaction collector */
react_collector* r = nullptr;
bot.on_log(dpp::utility::cout_logger());
/* Message handler */
bot.on_message_create([&](const dpp::message_create_t &event) {
/* If someone sends a message that has the text 'collect reactions!' start a reaction collector */
if (event.msg.content == "collect reactions!" && r == nullptr) {
/* Create a new reaction collector to collect reactions */
r = new react_collector(&bot, event.msg.id);
}
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~

View File

@ -0,0 +1,244 @@
\page cpp-eval-command-discord Making an eval command in C++
### What is an eval command anyway?
Many times people will ask: "how do i make a command like 'eval' in C++". For the uninitiated, an `eval` command is a command often found in interpreted languages such as Javascript and Python, which allows the developer to pass in raw interpreter statements which are then executed within the context of the running program, without any sandboxing. Eval commands are plain **evil**, if not properly coded in.
Needless to say, this is very dangerous. If you are asking how to do this, and want to put this into your bot, we trust that you have a very good reason to do so and have considered alternatives before resorting to this. The code below is for educational purposes only and makes several assumptions:
1. This code will only operate on UNIX-like systems such as Linux (not **Darwin**)
2. It assumes you use GCC, and have `g++` installed on your server and in your $PATH
3. The program will attempt to write to the current directory
4. No security checks will be done against the code, except for to check that it is being run by the bot's developer by snowflake id. It is entirely possible to send an `!eval exit(0);` and make the bot quit, for example, or delete files from the host operating system, if misused or misconfigured.
5. You're willing to wait a few seconds for compilation before your evaluated code runs. There isn't a way around this, as C++ is a compiled language.
To create this program you must create two files, `eval.h` and `eval.cpp`. The header file lists forward declarations of functions that you will be able to use directly within your `eval` code. As well as this the entire of D++ will be available to the eval command via the local variable `bot`, and the entire `on_message_create` event variable via a local variable called `event`.
The evaluated code will run within its own thread, so can execute for as long as it needs (but use common sense, don't go spawning a tight `while` loop that runs forever, you'll lock a thread at 100% CPU that won't ever end!).
### Implementation details
This code operates by outputting your provided code to be evaluated into a simple boilerplate program which can be compiled to a
shared object library (.so file). This .so file is then compiled with g++, using the `-shared` and `-fPIC` flags. If the program can be successfully compiled, it is then loaded using `dlopen()`, and the symbol `so_exec()` searched for within it, and called. This `so_exec()` function will contain the body of the code given to the eval command. Once this has been called and it has returned,
the `dlclose()` function is called to unload the library, and finally any temporary files (such as the .so file and its corresponding .cpp file) are cleaned up.
Docker is definitely recommended if you code on Windows/Mac OS, because docker desktop still uses a linux VM, so your code can easily use `.so` file and your code runs the same on your vps (if it also uses Linux distro)
### Source code
\warning If you manage to get your system, network, or anything else harmed by use or misuse of this code, we are not responsible. Don't say we didn't warn you! Find another way to solve your problem!
#### eval.h
Remember that eval.h contains forward-declarations of any functions you want to expose to the eval command. It is included both by the bot itself, and by any shared object files compiled for evaluation.
~~~~~~~~~~~~~~~~{.cpp}
#pragma once
/* This is the snowflake ID of the bot's developer.
* The eval command will be restricted to this user.
*/
#define MY_DEVELOPER 189759562910400512
/* Any functions you want to be usable from within an eval,
* that are not part of D++ itself or the message event, you
* can put here as forward declarations. The test_function()
* serves as an example.
*/
int test_function();
~~~~~~~~~~~~~~~~
#### eval.cpp
This is the main body of the example program.
~~~~~~~~~~~~~~~~{.cpp}
/**
* D++ eval command example.
* This is dangerous and for educational use only, here be dragons!
*/
#include <dpp/dpp.h>
#include <fmt/format.h>
#include <fstream>
#include <iostream>
/* We have to define this to make certain functions visible */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <link.h>
#include <dlfcn.h>
#include "eval.h"
/* This is an example function you can expose to your eval command */
int test_function() {
return 42;
}
/* Important: This code is for UNIX-like systems only, e.g.
* Linux, BSD, OSX. It will NOT work on Windows!
* Note for OSX you'll probably have to change all references
* from .so to .dylib.
*/
int main()
{
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* This won't work in a slash command very well yet, as there is not yet
* a multi-line slash command input type.
*/
bot.on_message_create([&bot](const auto & event) {
if (dpp::utility::utf8substr(event.msg.content, 0, 5) == "!eval") {
/**
* THIS IS CRITICALLY IMPORTANT!
* Never EVER make an eval command that isn't restricted to a specific developer by user id.
* With access to this command the person who invokes it has at best full control over
* your bot's user account and at worst, full control over your entire network!!!
* Eval commands are Evil (pun intended) and could even be considered a security
* vulnerability. YOU HAVE BEEN WARNED!
*/
if (event.msg.author.id != MY_DEVELOPER) {
bot.message_create(dpp::message(event.msg.channel_id, "On the day i do this for you, Satan will be ice skating to work."));
return;
}
/* We start by creating a string that contains a cpp program for a simple library.
* The library will contain one exported function called so_exec() that is called
* containing the raw C++ code to eval.
*/
std::string code = "#include <iostream>\n\
#include <string>\n\
#include <map>\n\
#include <unordered_map>\n\
#include <stdint.h>\n\
#include <dpp/dpp.h>\n\
#include <nlohmann/json.hpp>\n\
#include <fmt/format.h>\n\
#include \"eval.h\"\n\
extern \"C\" void so_exec(dpp::cluster& bot, dpp::message_create_t event) {\n\
" + dpp::utility::utf8substr(
event.msg.content,
6,
dpp::utility::utf8len(event.msg.content)
) + ";\n\
return;\n\
}";
/* Next we output this string full of C++ to a cpp file on disk.
* This code assumes the current directory is writeable. The file will have a
* unique name made from the user's id and the message id.
*/
std::string source_filename = std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".cpp";
std::fstream code_file(source_filename, std::fstream::binary | std::fstream::out);
if (!code_file.is_open()) {
bot.message_create(dpp::message(event.msg.channel_id, "Unable to create source file for `eval`"));
return;
}
code_file << code;
code_file.close();
/* Now to actually compile the file. We use dpp::utility::exec to
* invoke a compiler. This assumes you are using g++, and it is in your path.
*/
double compile_start = dpp::utility::time_f();
dpp::utility::exec("g++", {
"-std=c++17",
"-shared", /* Build the output as a .so file */
"-fPIC",
std::string("-o") + std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".so",
std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".cpp",
"-ldpp",
"-ldl"
}, [event, &bot, source_filename, compile_start](const std::string &output) {
/* After g++ is ran we end up inside this lambda with the output as a string */
double compile_time = dpp::utility::time_f() - compile_start;
/* Delete our cpp file, we don't need it any more */
std::string del_file = std::string(getenv("PWD")) + std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".cpp";
unlink(del_file.c_str());
/* On successful compilation g++ outputs nothing, so any output here is error output */
if (output.length()) {
bot.message_create(dpp::message(event.msg.channel_id, "Compile error: ```\n" + output + "\n```"));
} else {
/* Now for the meat of the function. To actually load
* our shared object we use dlopen() to load it into the
* memory space of our bot. If dlopen() returns a nullptr,
* the shared object could not be loaded. The user probably
* did something odd with the symbols inside their eval.
*/
std::string dl = std::string(getenv("PWD")) + std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".so";
auto shared_object_handle = dlopen(dl.c_str(), RTLD_NOW);
if (!shared_object_handle) {
const char *dlsym_error = dlerror();
bot.message_create(dpp::message(event.msg.channel_id, "Shared object load error: ```\n" +
std::string(dlsym_error ? dlsym_error : "Unknown error") +"\n```"));
return;
}
/* This type represents the "void so_exec()" function inside
* the shared object library file.
*/
using function_pointer = void(*)(dpp::cluster&, dpp::message_create_t);
/* Attempt to find the function called so_exec() inside the
* library we just loaded. If we can't find it, then the user
* did something really strange in their eval. Also note it's
* important we call dlerror() here to reset it before trying
* to use it a second time. It's weird-ass C code and is just
* like that.
*/
dlerror();
function_pointer exec_run = (function_pointer)dlsym(shared_object_handle, "so_exec");
const char *dlsym_error = dlerror();
if (dlsym_error) {
bot.message_create(dpp::message(event.msg.channel_id, "Shared object load error: ```\n" + std::string(dlsym_error) +"\n```"));
dlclose(shared_object_handle);
return;
}
/* Now we have a function pointer to our actual exec code in
* 'exec_run', so lets call it, and pass it a reference to
* the cluster, and also a copy of the message_create_t.
*/
double run_start = dpp::utility::time_f();
exec_run(bot, event);
double run_time = dpp::utility::time_f() - run_start;
/* When we're done with a .so file we must always dlclose() it */
dlclose(shared_object_handle);
/* We are now done with the compiled code too */
unlink(dl.c_str());
/* Output some statistics */
bot.message_create(dpp::message(event.msg.channel_id,
"Execution completed. Compile time: " + std::to_string(compile_time) +
"s, execution time " + std::to_string(run_time) + "s"));
}
});
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~
### Compilation
To compile this program you must link against `libdl`. It is also critically important to include the `-rdynamic` flag. For example:
```
g++ -std=c++17 -rdynamic -oeval eval.cpp -ldpp -ldl
```
### Example usage
\image html eval_example.png

View File

@ -0,0 +1,33 @@
\page making_a_http_request Making arbitrary HTTP requests using D++
If you wish to make arbitrary HTTP(S) requests to websites and APIs, e.g. to update statistics on bot lists, you can use code similar to the code below. You may pass any arbitrary POST data:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main() {
dpp::cluster bot("TOKEN GOES HERE");
bot.on_log(dpp::utility::cout_logger());
bot.on_ready([&bot](const dpp::ready_t& event) {
// Arbitrary post data as a string
std::string mypostdata = "{\"value\": 42}";
// Make a HTTP POST request. HTTP and HTTPS are supported here.
bot.request(
"http://www.somebotlist.com/api/servers", dpp::m_post, [](const dpp::http_request_completion_t & cc) {
// This callback is called when the HTTP request completes. See documentation of
// dpp::http_request_completion_t for information on the fields in the parameter.
std::cout << "I got reply: " << cc.body << " with HTTP status code: " << cc.status << "\n";
},
mypostdata,
"application/json",
{
{"Authorization", "Bearer tokengoeshere"}
}
);
});
bot.start(dpp::st_wait);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,63 @@
\page spdlog Integrating with spdlog
If you want to make your bot use spdlog, like aegis does, you can attach it to the on_log event. You can do this as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iomanip>
#include <dpp/dpp.h>
#include <fmt/format.h>
int main(int argc, char const *argv[])
{
dpp::cluster bot("token");
const std::string log_name = "mybot.log";
/* Set up spdlog logger */
std::shared_ptr<spdlog::logger> log;
spdlog::init_thread_pool(8192, 2);
std::vector<spdlog::sink_ptr> sinks;
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >();
auto rotating = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(log_name, 1024 * 1024 * 5, 10);
sinks.push_back(stdout_sink);
sinks.push_back(rotating);
log = std::make_shared<spdlog::async_logger>("logs", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
spdlog::register_logger(log);
log->set_pattern("%^%Y-%m-%d %H:%M:%S.%e [%L] [th#%t]%$ : %v");
log->set_level(spdlog::level::level_enum::debug);
/* Integrate spdlog logger to D++ log events */
bot.on_log([&bot, &log](const dpp::log_t & event) {
switch (event.severity) {
case dpp::ll_trace:
log->trace("{}", event.message);
break;
case dpp::ll_debug:
log->debug("{}", event.message);
break;
case dpp::ll_info:
log->info("{}", event.message);
break;
case dpp::ll_warning:
log->warn("{}", event.message);
break;
case dpp::ll_error:
log->error("{}", event.message);
break;
case dpp::ll_critical:
default:
log->critical("{}", event.message);
break;
}
});
/* Add the rest of your events */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,9 @@
\page music-and-audio Music and Audio
If you want to make noise, or capture noise, you're in the right place. You'll find examples here for creating basic music bots, or recording voice, amongst other things.
* \subpage soundboard "Creating a Sound Board"
* \subpage oggopus "Streaming Ogg Opus file"
* \subpage stream-mp3-discord-bot "Streaming MP3 files"
* \subpage record-user "Record yourself in a VC"
* \subpage joinvc "Join or switch to the voice channel of the user issuing a command"

View File

@ -0,0 +1,75 @@
\page joinvc Join or switch to the voice channel of the user issuing a command
When a user issues a command you may want to join their voice channel, e.g. in a music bot. If you are already on the same voice channel, the bot should do nothing (but be ready to instantly play audio) and if the user is on a different voice channel, the bot should switch to it. If the user is on no voice channel at all, this should be considered an error. This example shows how to do this.
\note Please be aware this example sends no audio, but indicates clearly in the comments where and how you should do so.
~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iomanip>
#include <sstream>
int main(int argc, char const *argv[])
{
/* Setup the bot */
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); // Privileged intent required to receive message content
bot.on_log(dpp::utility::cout_logger());
/* Use the on_message_create event to look for commands */
bot.on_message_create([&bot, robot, robot_size](const dpp::message_create_t & event) {
std::stringstream ss(event.msg.content);
std::string command;
ss >> command;
/* Switch to or join the vc the user is on. Syntax: .join */
if (command == ".join") {
dpp::guild * g = dpp::find_guild(event.msg.guild_id);
auto current_vc = event.from->get_voice(event.msg.guild_id);
bool join_vc = true;
/* Check if we are currently on any vc */
if (current_vc) {
/* Find the channel id that the user is currently on */
auto users_vc = g->voice_members.find(event.msg.author.id);
/* See if we currently share a channel with the user */
if (users_vc != g->voice_members.end() && current_vc->channel_id == users_vc->second.channel_id) {
join_vc = false;
/* We are on this voice channel, at this point we can send any audio instantly to vc:
* current_vc->send_audio_raw(...)
*/
} else {
/* We are on a different voice channel. Leave it, then join the new one
* by falling through to the join_vc branch below.
*/
event.from->disconnect_voice(event.msg.guild_id);
join_vc = true;
}
}
/* If we need to join a vc at all, join it here if join_vc == true */
if (join_vc) {
if (!g->connect_member_voice(event.msg.author.id)) {
/* The user issuing the command is not on any voice channel, we can't do anything */
bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :("));
} else {
/* We are now connecting to a vc. Wait for on_voice_ready
* event, and then send the audio within that event:
*
* event.voice_client->send_audio_raw(...);
*
* NOTE: We can't instantly send audio, as we have to wait for
* the connection to the voice server to be established!
*/
}
}
}
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,107 @@
\page stream-mp3-discord-bot Streaming MP3 files
To stream MP3 files via D++ you need to link an additional dependency to your bot, namely `libmpg123`. It is relatively simple when linking this library to your bot to then decode audio to PCM and send it to the dpp::discord_voice_client::send_audio_raw function as shown below:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <nlohmann/json.hpp>
#include <fmt/format.h>
#include <iomanip>
#include <sstream>
#include <vector>
#include <fstream>
#include <iostream>
#include <mpg123.h>
#include <out123.h>
/* For an example we will hardcode a path to some awesome music here */
#define MUSIC_FILE "/media/music/Rick Astley/Whenever You Need Somebody/Never Gonna Give You Up.mp3"
int main(int argc, char const *argv[])
{
/* This will hold the decoded MP3.
* The D++ library expects PCM format, which are raw sound
* data, 2 channel stereo, 16 bit signed 48000Hz.
*/
std::vector<uint8_t> pcmdata;
mpg123_init();
int err = 0;
unsigned char* buffer;
size_t buffer_size, done;
int channels, encoding;
long rate;
/* Note it is important to force the frequency to 48000 for Discord compatibility */
mpg123_handle *mh = mpg123_new(NULL, &err);
mpg123_param(mh, MPG123_FORCE_RATE, 48000, 48000.0);
/* Decode entire file into a vector. You could do this on the fly, but if you do that
* you may get timing issues if your CPU is busy at the time and you are streaming to
* a lot of channels/guilds.
*/
buffer_size = mpg123_outblock(mh);
buffer = new unsigned char[buffer_size];
/* Note: In a real world bot, this should have some error logging */
mpg123_open(mh, MUSIC_FILE);
mpg123_getformat(mh, &rate, &channels, &encoding);
unsigned int counter = 0;
for (int totalBytes = 0; mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK; ) {
for (auto i = 0; i < buffer_size; i++) {
pcmdata.push_back(buffer[i]);
}
counter += buffer_size;
totalBytes += done;
}
delete buffer;
mpg123_close(mh);
mpg123_delete(mh);
/* Setup the bot */
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* Use the on_message_create event to look for commands */
bot.on_message_create([&bot, &pcmdata](const dpp::message_create_t & event) {
std::stringstream ss(event.msg.content);
std::string command;
ss >> command;
/* Tell the bot to join the discord voice channel the user is on. Syntax: .join */
if (command == ".join") {
dpp::guild * g = dpp::find_guild(event.msg.guild_id);
if (!g->connect_member_voice(event.msg.author.id)) {
bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :("));
}
}
/* Tell the bot to play the mp3 file. Syntax: .mp3 */
if (command == ".mp3") {
dpp::voiceconn* v = event.from->get_voice(event.msg.guild_id);
if (v && v->voiceclient && v->voiceclient->is_ready()) {
/* Stream the already decoded MP3 file. This passes the PCM data to the library to be encoded to OPUS */
v->voiceclient->send_audio_raw((uint16_t*)pcmdata.data(), pcmdata.size());
}
}
});
/* Start bot */
bot.start(dpp::st_wait);
/* Clean up */
mpg123_exit();
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To compile this program you must remember to specify `libmpg123` alongside `libdpp` in the build command, for example:
` g++ -std=c++17 -o musictest musictest.cpp -lmpg123 -ldpp`

View File

@ -0,0 +1,268 @@
\page oggopus Streaming Ogg Opus file
This example shows how to stream an Ogg Opus file to a voice channel. This example requires some additional dependencies, namely `libogg` and `opusfile`.
~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iomanip>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ogg/ogg.h>
#include <opus/opusfile.h>
int main(int argc, char const *argv[])
{
/* Load an ogg opus file into memory.
* The bot expects opus packets to be 2 channel stereo, 48000Hz.
*
* You may use ffmpeg to encode songs to ogg opus:
* ffmpeg -i /path/to/song -c:a libopus -ar 48000 -ac 2 -vn -b:a 96K /path/to/opus.ogg
*/
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* Use the on_message_create event to look for commands */
bot.on_message_create([&bot](const dpp::message_create_t & event) {
std::stringstream ss(event.msg.content);
std::string command;
ss >> command;
/* Tell the bot to join the discord voice channel the user is on. Syntax: .join */
if (command == ".join") {
dpp::guild * g = dpp::find_guild(event.msg.guild_id);
if (!g->connect_member_voice(event.msg.author.id)) {
bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :("));
}
}
/* Tell the bot to play the sound file */
if (command == ".play") {
dpp::voiceconn* v = event.from->get_voice(event.msg.guild_id);
if (v && v->voiceclient && v->voiceclient->is_ready()) {
ogg_sync_state oy;
ogg_stream_state os;
ogg_page og;
ogg_packet op;
OpusHead header;
char *buffer;
FILE *fd;
fd = fopen("/path/to/opus.ogg", "rb");
fseek(fd, 0L, SEEK_END);
size_t sz = ftell(fd);
rewind(fd);
ogg_sync_init(&oy);
int eos = 0;
int i;
buffer = ogg_sync_buffer(&oy, sz);
fread(buffer, 1, sz, fd);
ogg_sync_wrote(&oy, sz);
/**
* We must first verify that the stream is indeed ogg opus
* by reading the header and parsing it
*/
if (ogg_sync_pageout(&oy, &og) != 1)
{
fprintf(stderr,"Does not appear to be ogg stream.\n");
exit(1);
}
ogg_stream_init(&os, ogg_page_serialno(&og));
if (ogg_stream_pagein(&os,&og) < 0) {
fprintf(stderr,"Error reading initial page of ogg stream.\n");
exit(1);
}
if (ogg_stream_packetout(&os,&op) != 1)
{
fprintf(stderr,"Error reading header packet of ogg stream.\n");
exit(1);
}
/* We must ensure that the ogg stream actually contains opus data */
if (!(op.bytes > 8 && !memcmp("OpusHead", op.packet, 8)))
{
fprintf(stderr,"Not an ogg opus stream.\n");
exit(1);
}
/* Parse the header to get stream info */
int err = opus_head_parse(&header, op.packet, op.bytes);
if (err)
{
fprintf(stderr,"Not a ogg opus stream\n");
exit(1);
}
/* Now we ensure the encoding is correct for Discord */
if (header.channel_count != 2 && header.input_sample_rate != 48000)
{
fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n");
exit(1);
}
/* Now loop though all the pages and send the packets to the vc */
while (ogg_sync_pageout(&oy, &og) == 1){
ogg_stream_init(&os, ogg_page_serialno(&og));
if(ogg_stream_pagein(&os,&og)<0){
fprintf(stderr,"Error reading page of Ogg bitstream data.\n");
exit(1);
}
while (ogg_stream_packetout(&os,&op) != 0)
{
/* Read remaining headers */
if (op.bytes > 8 && !memcmp("OpusHead", op.packet, 8))
{
int err = opus_head_parse(&header, op.packet, op.bytes);
if (err)
{
fprintf(stderr,"Not a ogg opus stream\n");
exit(1);
}
if (header.channel_count != 2 && header.input_sample_rate != 48000)
{
fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n");
exit(1);
}
continue;
}
/* Skip the opus tags */
if (op.bytes > 8 && !memcmp("OpusTags", op.packet, 8))
continue;
/* Send the audio */
int samples = opus_packet_get_samples_per_frame(op.packet, 48000);
v->voiceclient->send_audio_opus(op.packet, op.bytes, samples / 48);
}
}
/* Cleanup */
ogg_stream_clear(&os);
ogg_sync_clear(&oy);
}
}
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~
You can compile this example using the following command
c++ /path/to/source.cc -ldpp -lopus -lopusfile -logg -I/usr/include/opus
## Using `liboggz`
You can use `liboggz` to stream an Ogg Opus file to discord voice channel.
`liboggz` provides higher level abstraction and useful APIs. Some API `liboggz` provide includes seeking and timestamp interpretation.
Read more on the [documentation](https://www.xiph.org/oggz/doc/).
~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iomanip>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <oggz/oggz.h>
int main(int argc, char const *argv[])
{
/* Load an ogg opus file into memory.
* The bot expects opus packets to be 2 channel stereo, 48000Hz.
*
* You may use ffmpeg to encode songs to ogg opus:
* ffmpeg -i /path/to/song -c:a libopus -ar 48000 -ac 2 -vn -b:a 96K /path/to/opus.ogg
*/
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* Use the on_message_create event to look for commands */
bot.on_message_create([&bot](const dpp::message_create_t & event) {
std::stringstream ss(event.msg.content);
std::string command;
ss >> command;
/* Tell the bot to join the discord voice channel the user is on. Syntax: .join */
if (command == ".join") {
dpp::guild * g = dpp::find_guild(event.msg.guild_id);
if (!g->connect_member_voice(event.msg.author.id)) {
bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :("));
}
}
/* Tell the bot to play the sound file */
if (command == ".play") {
dpp::voiceconn* v = event.from->get_voice(event.msg.guild_id);
if (v && v->voiceclient && v->voiceclient->is_ready()) {
// load the audio file with oggz
OGGZ *track_og = oggz_open("/path/to/opus.ogg", OGGZ_READ);
if (track_og) {
// set read callback, this callback will be called on packets with the serialno,
// -1 means every packet will be handled with this callback
oggz_set_read_callback(
track_og, -1,
[](OGGZ *oggz, oggz_packet *packet, long serialno,
void *user_data) {
dpp::voiceconn *voiceconn = (dpp::voiceconn *)user_data;
// send the audio
voiceconn->voiceclient->send_audio_opus(packet->op.packet,
packet->op.bytes);
// make sure to always return 0 here, read more on oggz documentation
return 0;
},
// this will be the value of void *user_data
(void *)v);
// read loop
while (v && v->voiceclient && !v->voiceclient->terminating) {
// you can tweak this to whatever. Here I use BUFSIZ, defined in
// stdio.h as 8192
static const constexpr long CHUNK_READ = BUFSIZ * 2;
const long read_bytes = oggz_read(track_og, CHUNK_READ);
// break on eof
if (!read_bytes)
break;
}
} else {
fprintf(stderr, "Error opening file\n");
}
// don't forget to free the memory
oggz_close(track_og);
}
}
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~
You can compile this example using the following command
c++ /path/to/source.cc -ldpp -loggz

View File

@ -0,0 +1,67 @@
\page record-user Record yourself in a VC
DPP supports receiving audio. This examples show how to use it to record some user in a VC.
\note Voice receiving by bots is not officially supported by the Discord API. We cannot guarantee that this feature will work in the future.
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iomanip>
#include <sstream>
int main(int argc, char const *argv[])
{
/* Example to record a user in a VC
*
* Recording is output as './me.pcm' and you can play it via the soundboard example
* or use ffmpeg 'ffplay -f s16le -ar 48000 -ac 2 -i ./me.pcm'
*/
/* Replace with the user's id you wish to record */
dpp::snowflake user_id = 407877550216314882;
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
FILE *fd;
fd = fopen("./me.pcm", "wb");
bot.on_log(dpp::utility::cout_logger());
/* Use the on_message_create event to look for commands */
bot.on_message_create([&bot, &fd](const dpp::message_create_t & event) {
std::stringstream ss(event.msg.content);
std::string command;
ss >> command;
/* Tell the bot to record */
if (command == ".record") {
dpp::guild * g = dpp::find_guild(event.msg.guild_id);
if (!g->connect_member_voice(event.msg.author.id)) {
bot.message_create(dpp::message(
event.msg.channel_id,
"You don't seem to be on a voice channel! :("
));
}
}
/* Tell the bot to stop recording */
if (command == ".stop") {
event.from->disconnect_voice(event.msg.guild_id);
fclose(fd);
}
});
bot.on_voice_receive([&bot, &fd, &user_id](const dpp::voice_receive_t &event) {
if (event.user_id == user_id) {
fwrite((char *)event.audio, 1, event.audio_size, fd);
}
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~

View File

@ -0,0 +1,68 @@
\page soundboard Creating a Sound Board
This example script shows how to send a sound file to a voice channel. A few shortcuts are taken here, for more advanced techniques for connecting to a voice channel see the tutorial \ref joinvc
~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
#include <iomanip>
#include <sstream>
int main(int argc, char const *argv[])
{
/* Load a sound file called Robot.pcm into memory.
* The bot expects PCM format, which are raw sound data,
* 2 channel stereo, 16 bit signed 48000Hz.
*
* You can use audacity to export these from WAV or MP3 etc.
*
* If you wanted to send a more complicated format, you could
* use a separate library to decode that audio to PCM. For
* example purposes, a raw PCM will suffice. This PCM file can
* be found within the bot's github repo.
*/
uint8_t* robot = nullptr;
size_t robot_size = 0;
std::ifstream input ("../testdata/Robot.pcm", std::ios::in|std::ios::binary|std::ios::ate);
if (input.is_open()) {
robot_size = input.tellg();
robot = new uint8_t[robot_size];
input.seekg (0, std::ios::beg);
input.read ((char*)robot, robot_size);
input.close();
}
/* Setup the bot */
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); // Privileged intent required to receive message content
bot.on_log(dpp::utility::cout_logger());
/* Use the on_message_create event to look for commands */
bot.on_message_create([&bot, robot, robot_size](const dpp::message_create_t & event) {
std::stringstream ss(event.msg.content);
std::string command;
ss >> command;
/* Tell the bot to join the discord voice channel the user is on. Syntax: .join */
if (command == ".join") {
dpp::guild * g = dpp::find_guild(event.msg.guild_id);
if (!g->connect_member_voice(event.msg.author.id)) {
bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :("));
}
}
/* Tell the bot to play the sound file 'Robot.pcm'. Syntax: .robot */
if (command == ".robot") {
dpp::voiceconn* v = event.from->get_voice(event.msg.guild_id);
if (v && v->voiceclient && v->voiceclient->is_ready()) {
v->voiceclient->send_audio_raw((uint16_t*)robot, robot_size);
}
}
});
/* Start bot */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,8 @@
\page the-basics The Basics
These example programs are great to get started with simple things in the D++ library, ideal for beginners to the language or to the Discord API.
* \subpage firstbot "Creating Your First Bot"
* \subpage embed-message "Sending Embeds"
* \subpage attach-file "Attaching a file"
* \subpage webhooks "Webhooks"

View File

@ -0,0 +1,112 @@
\page attach-file Attaching a file to a message
Attached files must be locally stored.
To attach a file to a message, you can upload a local image.
D++ has this helper function to read a file: `dpp::utility::read_file`.
An example program:
@note Because these examples utilizes message content, they require the message content privileged intent.
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main() {
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* Message handler to look for a command called !file */
bot.on_message_create([&bot](const dpp::message_create_t &event) {
if (event.msg.content == "!file") {
// create a message
dpp::message msg(event.msg.channel_id, "Hey there, i've got a new file!");
// attach the file to the message
msg.add_file("foobar.txt", dpp::utility::read_file("path_to_your_file.txt"));
// send the message
bot.message_create(msg);
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~
Attachments via an url aren't possible. But there's a workaround for. You can download the file and then attach it to the message.
To make requests, D++ also has a helper function: `dpp::cluster::request`.
The following example program shows how to request a file and attach it to a message.
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main() {
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* Message handler to look for a command called !file */
bot.on_message_create([&bot](const dpp::message_create_t &event) {
if (event.msg.content == "!file") {
// request an image
bot.request("https://dpp.dev/DPP-Logo.png", dpp::m_get, [&bot, channel_id = event.msg.channel_id](const dpp::http_request_completion_t & httpRequestCompletion) {
// create a message
dpp::message msg(channel_id, "This is my new attachment:");
// attach the image on success
if (httpRequestCompletion.status == 200) {
msg.add_file("logo.png", httpRequestCompletion.body);
}
// send the message
bot.message_create(msg);
});
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~
Here's another example of how to add a local image to an embed.
Upload the image in the same message as the embed and then reference it in the embed.
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main() {
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
bot.on_log(dpp::utility::cout_logger());
/* Message handler to look for a command called !file */
bot.on_message_create([&bot](const dpp::message_create_t &event) {
if (event.msg.content == "!file") {
// create a message
dpp::message msg(event.msg.channel_id, "");
// attach the image to the message
msg.add_file("image.jpg", dpp::utility::read_file("path_to_your_image.jpg"));
dpp::embed embed;
embed.set_image("attachment://image.jpg"); // reference to the attached file
msg.add_embed(embed);
// send the message
bot.message_create(msg);
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~

View File

@ -0,0 +1,56 @@
\page embed-message Sending Embeds
You might have seen these special messages, often sent by bots. In this section, we will show how to create an embed.
@note Because this example utilizes message content, it requires the message content privileged intent.
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main() {
/* Setup the bot */
dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content);
/* Message handler to look for a command called !embed */
bot.on_message_create([&bot](const dpp::message_create_t & event) {
if (event.msg.content == "!embed") {
/* create the embed */
dpp::embed embed = dpp::embed().
set_color(dpp::colors::sti_blue).
set_title("Some name").
set_url("https://dpp.dev/").
set_author("Some name", "https://dpp.dev/", "https://dpp.dev/DPP-Logo.png").
set_description("Some description here").
set_thumbnail("https://dpp.dev/DPP-Logo.png").
add_field(
"Regular field title",
"Some value here"
).
add_field(
"Inline field title",
"Some value here",
true
).
add_field(
"Inline field title",
"Some value here",
true
).
set_image("https://dpp.dev/DPP-Logo.png").
set_footer(dpp::embed_footer().set_text("Some footer text here").set_icon("https://dpp.dev/DPP-Logo.png")).
set_timestamp(time(0));
/* reply with the created embed */
bot.message_create(dpp::message(event.msg.channel_id, embed).set_reference(event.msg.id));
}
});
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~
The code will send the following message.
\image html embed.png

View File

@ -0,0 +1,244 @@
\page firstbot Creating Your First Bot
In this example we will create a C++ version of the [discord.js](https://discord.js.org/#/) example program.
The two programs can be seen side by side below:
<table>
<tr>
<th>D++</th>
<th>Discord.js</td>
</tr>
<tr>
<td>
~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
const std::string BOT_TOKEN = "add your token here";
int main() {
dpp::cluster bot(BOT_TOKEN);
bot.on_log(dpp::utility::cout_logger());
bot.on_slashcommand([](const dpp::slashcommand_t& event) {
if (event.command.get_command_name() == "ping") {
event.reply("Pong!");
}
});
bot.on_ready([&bot](const dpp::ready_t& event) {
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_command_create(
dpp::slashcommand("ping", "Ping pong!", bot.me.id)
);
}
});
bot.start(dpp::st_wait);
}
~~~~~~~~~~~~~~~
</td>
<td>
~~~~~~~~~~~~~~~{.cpp}
let Discord = require('discord.js');
let BOT_TOKEN = 'add your token here';
let bot = new Discord.Client({ intents: [] });
bot.on('interactionCreate', (interaction) => {
if (interaction.isCommand() && interaction.commandName === 'ping') {
interaction.reply({content: 'Pong!'});
}
});
bot.once('ready', async () => {
await client.commands.create({
name: 'ping',
description: "Ping pong!"
});
});
bot.login(BOT_TOKEN);
~~~~~~~~~~~~~~~
</td>
</tr>
</table>
Let's break this program down step by step:
### 1. Start with an empty C++ program
Make sure to include the header file for the D++ library with the instruction \#include `<dpp/dpp.h>`!
~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main() {
}
~~~~~~~~~~~~~~
### 2. Create an instance of dpp::cluster
To make use of the library you must create a dpp::cluster object. This object is the main object in your program like the `Discord.Client` object in Discord.js.
You can instantiate this class as shown below. Remember to put your bot token in the constant!
~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
const std::string BOT_TOKEN = "add your token here";
int main() {
dpp::cluster bot(BOT_TOKEN);
}
~~~~~~~~~~~~~~~
### 3. Attach to an event
To have a bot that does something, you should attach to some events. Let's start by attaching to the `on_ready` event (dpp::cluster::on_ready) which will notify your program when the bot is connected. In this event, we will register a slash
command called 'ping'. Note that we must wrap our registration of the command in a template called `dpp::run_once` which prevents it from being re-run
every time your bot does a full reconnection (e.g. if the connection fails).
~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
const std::string BOT_TOKEN = "add your token here";
int main() {
dpp::cluster bot(BOT_TOKEN);
bot.on_ready([&bot](const dpp::ready_t& event) {
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id));
}
});
}
~~~~~~~~~~~~~~~~
### 4. Attach to another event to receive slash commands
If you want to handle a slash command, you should also attach your program to the `on_slashcommand` event (dpp::cluster::on_slashcommand) which is basically the same as the Discord.js `interactionCreate` event. Lets add this to the program before the `on_ready` event:
~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
const std::string BOT_TOKEN = "add your token here";
int main() {
dpp::cluster bot(BOT_TOKEN);
bot.on_slashcommand([](const dpp::slashcommand_t& event) {
});
bot.on_ready([&bot](const dpp::ready_t& event) {
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id));
}
});
}
~~~~~~~~~~~~~~
### 5 . Add some content to the events
Attaching to an event is a good start, but to make a bot you should actually put some program code into the interaction event. We will add some code to the `on_slashcommand` to look for our slash command '/ping' and reply with `Pong!`:
~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
const std::string BOT_TOKEN = "add your token here";
int main() {
dpp::cluster bot(BOT_TOKEN);
bot.on_slashcommand([](const dpp::slashcommand_t& event) {
if (event.command.get_command_name() == "ping") {
event.reply("Pong!");
}
});
bot.on_ready([&bot](const dpp::ready_t& event) {
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id));
}
});
}
~~~~~~~~~~~~~~~~~~~~~~~
Let's break down the code in the `on_slashcommand` event so that we can discuss what it is doing:
~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
bot.on_slashcommand([](const dpp::slashcommand_t& event) {
if (event.command.get_command_name() == "ping") {
event.reply("Pong!");
}
});
~~~~~~~~~~~~~~~~~~~~~~~
This code is simply comparing the command name `event.command.get_command_name()` (dpp::interaction::get_command_name) against the value in a constant string value `"ping"`. If they match, then the `event.reply` method is called.
The `event.reply` function (dpp::slashcommand_t::reply) replies to a slash command with a message. There are many ways to call this function to send embed messages, upload files, and more, but for this simple demonstration we will just send some message text.
### 6. Add code to start the bot!
To make the bot start, we must call the cluster::start method, e.g. in our program by using `bot.start(dpp::st_wait)`.
We also add a line to tell the library to output all its log information to the console, `bot.on_log(dpp::utility::cout_logger());` - if you wanted to do something more advanced, you can replace this parameter with a lambda just like all other events.
The parameter which we set to false indicates if the function should return once all shards are created. Passing `false` here tells the program you do not need to do anything once `bot.start` is called.
~~~~~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
const std::string BOT_TOKEN = "add your token here";
int main() {
dpp::cluster bot(BOT_TOKEN);
bot.on_log(dpp::utility::cout_logger());
bot.on_slashcommand([](const dpp::slashcommand_t& event) {
if (event.command.get_command_name() == "ping") {
event.reply("Pong!");
}
});
bot.on_ready([&bot](const dpp::ready_t& event) {
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id));
}
});
bot.start(dpp::st_wait);
}
~~~~~~~~~~~~~~
### 7. Compile and run your bot
Compile your bot using `g++ -std=c++17 -o bot bot.cpp -ldpp` (if your .cpp file is called `bot.cpp`) and run it with `./bot`.
### 8. Inviting your bot to your server
When you invite your bot, you must use the `applications.commands` and `bots` scopes to ensure your bot can create guild slash commands. For example:
`https://discord.com/oauth2/authorize?client_id=YOUR-BOTS-ID-HERE&scope=bot+applications.commands&permissions=BOT-PERMISSIONS-HERE`
Replace `YOUR-BOTS-ID-HERE` with your bot's user ID, and `BOT-PERMISSIONS-HERE` with the permissions your bot requires.
**Congratulations** - you now have a working bot written using the D++ library!

View File

@ -0,0 +1,26 @@
\page webhooks Webhooks
Webhooks are a simple way to post messages from other apps and websites into Discord. They allow getting automated messages and data updates sent to a text channel in your server. [Read more](https://support.discord.com/hc/en-us/articles/228383668) in this article about Webhooks.
The following code shows how to send messages in a channel using a webhook.
~~~~~~~~~~{.cpp}
#include <dpp/dpp.h>
int main()
{
dpp::cluster bot(""); // normally, you put your bot token in here. But to just run a webhook its not required
bot.on_log(dpp::utility::cout_logger());
/* construct a webhook object using the URL you got from Discord */
dpp::webhook wh("https://discord.com/api/webhooks/833047646548133537/ntCHEYYIoHSLy_GOxPx6pmM0sUoLbP101ct-WI6F-S4beAV2vaIcl_Id5loAMyQwxqhE");
/* send a message with this webhook */
bot.execute_webhook_sync(wh, dpp::message("Have a great time here :smile:"));
return 0;
}
~~~~~~~~~~
The above is just a very simple example. You can also send embed messages. All you have to do is to add an embed to the message you want to send. If you want to, you can also send it into a thread.

58
vendor/DPP/docpages/footer.html vendored Normal file
View File

@ -0,0 +1,58 @@
<div id="nav-path" class="navpath">
<ul>
$navpath
</ul>
</div>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-QTH6YHBNG5"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/base16/dracula.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@100&display=swap" rel="stylesheet">
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-QTH6YHBNG5');
$(function() {
$(".fragment").each(function(i,node) {
var $node = $(node);
$node.children(":not(.line)").remove();
$node.html("<pre><code class='stan'>" + $node.text().trim().replaceAll("<", "&lt;").replaceAll(">", "&gt;") + "</code></pre>");
hljs.configure({
languages: ['cpp','diff','cmake','bash','sh','text'],
ignoreUnescapedHTML: true
});
hljs.highlightAll(node);
hljs.initLineNumbersOnLoad(node);
});
$(".fragment").parent().parent().parent().parent().removeClass('doxtable');
});
</script>
<style>
.hljs-ln-code, code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span {
font-family: 'JetBrains Mono', monospace !important;
font-size: 0.8rem !important;
}
.fragment {
padding: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
background: none !important;
border: 0 !important;
}
.hljs-ln-n::before {
content: attr(data-line-number);
padding-right: 1rem !important;
}
</style>
<div style="z-index: -9999; position: absolute; right: 0; top: 0; font-size: 0.0001rem;color:transparent;background:none">
<!-- For crawlability of past versions -->
<a href='/9.0.13/'>D++ Library version 9.0.13</a><a href='/9.0.12/'>D++ Library version 9.0.12</a><a href='/9.0.11/'>D++ Library version 9.0.11</a><a href='/9.0.10/'>D++ Library version 9.0.10</a><a href='/9.0.9/'>D++ Library version 9.0.9</a><a href='/9.0.8/'>D++ Library version 9.0.8</a><a href='/9.0.7/'>D++ Library version 9.0.7</a><a href='/9.0.6/'>D++ Library version 9.0.6</a><a href='/9.0.5/'>D++ Library version 9.0.5</a><a href='/9.0.4/'>D++ Library version 9.0.4</a><a href='/9.0.3/'>D++ Library version 9.0.3</a><a href='/9.0.2/'>D++ Library version 9.0.2</a><a href='/9.0.1/'>D++ Library version 9.0.1</a><a href='/9.0.0/'>D++ Library version 9.0.0</a><a href='/1.0.2/'>D++ Library version 1.0.2</a><a href='/1.0.1/'>D++ Library version 1.0.1</a><a href='/1.0.0/'>D++ Library version 1.0.0</a>
</div>
</body>
</html>

View File

@ -0,0 +1,63 @@
<div id="nav-path" class="navpath">
<ul>
$navpath
</ul>
</div>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-QTH6YHBNG5"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/base16/dracula.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@100&display=swap" rel="stylesheet">
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-QTH6YHBNG5');
$(function() {
$(".fragment").each(function(i,node) {
var $node = $(node);
$node.children(":not(.line)").remove();
$node.html("<pre><code class='stan'>" + $node.text().trim().replaceAll("<", "&lt;").replaceAll(">", "&gt;") + "</code></pre>");
hljs.configure({
languages: ['cpp','diff','cmake','bash','sh','text'],
ignoreUnescapedHTML: true
});
hljs.highlightAll(node);
hljs.initLineNumbersOnLoad(node);
});
$(".fragment").parent().parent().parent().parent().removeClass('doxtable');
});
</script>
<style>
.hljs-ln-code, code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span {
font-family: 'JetBrains Mono', monospace !important;
font-size: 0.8rem !important;
}
.fragment {
padding: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
background: none !important;
border: 0 !important;
}
.hljs-ln-n::before {
content: attr(data-line-number);
padding-right: 1rem !important;
}
table.markdownTable code td, table.markdownTable code th, table.fieldtable code td, table.fieldtable code th, table.doxtable code td, table.doxtable code th {
border: 0 !important;
padding: 0 !important;
}
</style>
<div style="z-index: -9999; position: absolute; right: 0; top: 0; font-size: 0.0001rem;color:transparent;background:none">
<!-- For crawlability of past versions -->
###PREV###
</div>
</body>
</html>

66
vendor/DPP/docpages/header.html vendored Normal file
View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<meta property="og:title" content="$title - D++ - The lightweight C++ Discord API Library">
<meta property="og:description" content="A lightweight C++ Discord API library supporting the entire Discord API, including Slash Commands, Voice/Audio, Sharding, Clustering and more!">
<meta name="description" content="$title - D++ - A lightweight C++ Discord API library supporting the entire Discord API, including Slash Commands, Voice/Audio, Sharding, Clustering and more!">
<meta property="og:image" content="https://dpp.dev/DPP-Logo.png">
<meta property="og:url" content="https://dpp.dev/">
<meta property="og:type" content="website">
<meta property="twitter:title" content="$title - D++ - The lightweight C++ Discord API Library">
<!--BEGIN PROJECT_NAME--><title>$title - D++ - The lightweight C++ Discord API Library</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title - D++ - The lightweight C++ Discord API Library</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<script>
setTimeout(function() {
$("#main-menu").html($("#main-menu").html() + "<li><select name='vsv' onchange='window.location.href=this.options[this.selectedIndex].value'><option value='/'>master</option><option value='/9.0.13/'>9.0.13</option><option value='/9.0.12/'>9.0.12</option><option value='/9.0.11/'>9.0.11</option><option value='/9.0.10/'>9.0.10</option><option value='/9.0.9/'>9.0.9</option><option value='/9.0.8/'>9.0.8</option><option value='/9.0.7/'>9.0.7</option><option value='/9.0.6/'>9.0.6</option><option value='/9.0.5/'>9.0.5</option><option value='/9.0.4/'>9.0.4</option><option value='/9.0.3/'>9.0.3</option><option value='/9.0.2/'>9.0.2</option><option value='/9.0.1/'>9.0.1</option><option value='/9.0.0/'>9.0.0</option><option value='/1.0.2/'>1.0.2</option><option value='/1.0.1/'>1.0.1</option><option value='/1.0.0/'>1.0.0</option></select></li>");
}, 500);
</script>
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->
<label for="MSearchField" style="display: none">Search</label>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<meta property="og:title" content="$title - D++ - The lightweight C++ Discord API Library">
<meta property="og:description" content="A lightweight C++ Discord API library supporting the entire Discord API, including Slash Commands, Voice/Audio, Sharding, Clustering and more!">
<meta name="description" content="$title - D++ - A lightweight C++ Discord API library supporting the entire Discord API, including Slash Commands, Voice/Audio, Sharding, Clustering and more!">
<meta property="og:image" content="https://dpp.dev/DPP-Logo.png">
<meta property="og:url" content="https://dpp.dev/">
<meta property="og:type" content="website">
<meta property="twitter:title" content="$title - D++ - The lightweight C++ Discord API Library">
<!--BEGIN PROJECT_NAME--><title>$title - D++ - The lightweight C++ Discord API Library</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title - D++ - The lightweight C++ Discord API Library</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<script>
setTimeout(function() {
$("#main-menu").html($("#main-menu").html() + "<li><select name='vsv' onchange='window.location.href=this.options[this.selectedIndex].value'>##VERSION_OPTIONS##</select></li>");
}, 500);
</script>
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->
<label for="MSearchField" style="display: none">Search</label>

BIN
vendor/DPP/docpages/images/DPP-Logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
vendor/DPP/docpages/images/DPP-Small.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

View File

@ -0,0 +1,499 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1503pt" height="965pt" viewBox="0 0 1503 965" version="1.1">
<defs>
<g>
<symbol overflow="visible" id="glyph0-0">
<path style="stroke:none;" d="M 8.238281 -8.609375 L 8.238281 -7.277344 C 7.8125 -7.671875 7.359375 -7.964844 6.878906 -8.164063 C 6.398438 -8.355469 5.886719 -8.453125 5.347656 -8.457031 C 4.277344 -8.453125 3.460938 -8.128906 2.898438 -7.480469 C 2.332031 -6.828125 2.050781 -5.886719 2.050781 -4.652344 C 2.050781 -3.425781 2.332031 -2.484375 2.898438 -1.832031 C 3.460938 -1.179688 4.277344 -0.855469 5.347656 -0.855469 C 5.886719 -0.855469 6.398438 -0.953125 6.878906 -1.148438 C 7.359375 -1.34375 7.8125 -1.636719 8.238281 -2.035156 L 8.238281 -0.71875 C 7.796875 -0.417969 7.328125 -0.191406 6.835938 -0.0429688 C 6.339844 0.105469 5.820313 0.175781 5.273438 0.179688 C 3.859375 0.175781 2.746094 -0.253906 1.9375 -1.113281 C 1.121094 -1.976563 0.714844 -3.15625 0.71875 -4.652344 C 0.714844 -6.152344 1.121094 -7.332031 1.9375 -8.199219 C 2.746094 -9.058594 3.859375 -9.492188 5.273438 -9.496094 C 5.828125 -9.492188 6.351563 -9.417969 6.847656 -9.273438 C 7.339844 -9.121094 7.804688 -8.898438 8.238281 -8.609375 Z M 8.238281 -8.609375 "/>
</symbol>
<symbol overflow="visible" id="glyph0-1">
<path style="stroke:none;" d="M 1.207031 -9.71875 L 2.355469 -9.71875 L 2.355469 0 L 1.207031 0 Z M 1.207031 -9.71875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-2">
<path style="stroke:none;" d="M 1.085938 -2.761719 L 1.085938 -6.996094 L 2.234375 -6.996094 L 2.234375 -2.804688 C 2.230469 -2.140625 2.359375 -1.644531 2.621094 -1.3125 C 2.878906 -0.980469 3.269531 -0.8125 3.785156 -0.816406 C 4.402344 -0.8125 4.890625 -1.011719 5.253906 -1.410156 C 5.613281 -1.804688 5.792969 -2.34375 5.796875 -3.03125 L 5.796875 -6.996094 L 6.945313 -6.996094 L 6.945313 0 L 5.796875 0 L 5.796875 -1.074219 C 5.515625 -0.644531 5.191406 -0.332031 4.824219 -0.128906 C 4.453125 0.0742188 4.027344 0.175781 3.542969 0.179688 C 2.734375 0.175781 2.125 -0.0703125 1.710938 -0.566406 C 1.292969 -1.0625 1.082031 -1.792969 1.085938 -2.761719 Z M 1.085938 -2.761719 "/>
</symbol>
<symbol overflow="visible" id="glyph0-3">
<path style="stroke:none;" d="M 5.664063 -6.789063 L 5.664063 -5.703125 C 5.339844 -5.867188 5.003906 -5.992188 4.652344 -6.078125 C 4.300781 -6.15625 3.9375 -6.199219 3.566406 -6.203125 C 2.992188 -6.199219 2.5625 -6.113281 2.28125 -5.941406 C 1.992188 -5.765625 1.851563 -5.5 1.855469 -5.152344 C 1.851563 -4.882813 1.953125 -4.675781 2.160156 -4.523438 C 2.363281 -4.371094 2.773438 -4.226563 3.390625 -4.089844 L 3.785156 -4.003906 C 4.597656 -3.824219 5.175781 -3.578125 5.523438 -3.261719 C 5.863281 -2.941406 6.035156 -2.496094 6.039063 -1.929688 C 6.035156 -1.277344 5.777344 -0.765625 5.265625 -0.386719 C 4.75 -0.0117188 4.042969 0.175781 3.148438 0.179688 C 2.769531 0.175781 2.378906 0.140625 1.976563 0.0703125 C 1.566406 0 1.140625 -0.109375 0.695313 -0.257813 L 0.695313 -1.441406 C 1.117188 -1.222656 1.535156 -1.054688 1.949219 -0.945313 C 2.359375 -0.832031 2.765625 -0.777344 3.171875 -0.78125 C 3.710938 -0.777344 4.128906 -0.871094 4.421875 -1.058594 C 4.710938 -1.242188 4.855469 -1.503906 4.859375 -1.84375 C 4.855469 -2.152344 4.75 -2.390625 4.542969 -2.5625 C 4.332031 -2.726563 3.871094 -2.886719 3.160156 -3.042969 L 2.761719 -3.136719 C 2.042969 -3.28125 1.527344 -3.511719 1.214844 -3.824219 C 0.894531 -4.132813 0.738281 -4.558594 0.742188 -5.101563 C 0.738281 -5.757813 0.972656 -6.265625 1.441406 -6.625 C 1.90625 -6.980469 2.566406 -7.160156 3.429688 -7.164063 C 3.851563 -7.160156 4.253906 -7.128906 4.628906 -7.070313 C 5.003906 -7.003906 5.347656 -6.910156 5.664063 -6.789063 Z M 5.664063 -6.789063 "/>
</symbol>
<symbol overflow="visible" id="glyph0-4">
<path style="stroke:none;" d="M 2.34375 -8.984375 L 2.34375 -6.996094 L 4.710938 -6.996094 L 4.710938 -6.101563 L 2.34375 -6.101563 L 2.34375 -2.304688 C 2.34375 -1.730469 2.421875 -1.363281 2.578125 -1.203125 C 2.734375 -1.039063 3.050781 -0.960938 3.53125 -0.960938 L 4.710938 -0.960938 L 4.710938 0 L 3.53125 0 C 2.640625 0 2.027344 -0.164063 1.691406 -0.496094 C 1.351563 -0.824219 1.183594 -1.425781 1.1875 -2.304688 L 1.1875 -6.101563 L 0.34375 -6.101563 L 0.34375 -6.996094 L 1.1875 -6.996094 L 1.1875 -8.984375 Z M 2.34375 -8.984375 "/>
</symbol>
<symbol overflow="visible" id="glyph0-5">
<path style="stroke:none;" d="M 7.191406 -3.785156 L 7.191406 -3.222656 L 1.90625 -3.222656 C 1.953125 -2.429688 2.191406 -1.824219 2.621094 -1.414063 C 3.042969 -0.996094 3.636719 -0.789063 4.402344 -0.792969 C 4.839844 -0.789063 5.269531 -0.84375 5.6875 -0.953125 C 6.097656 -1.058594 6.507813 -1.222656 6.921875 -1.441406 L 6.921875 -0.355469 C 6.507813 -0.179688 6.085938 -0.0507813 5.652344 0.0429688 C 5.21875 0.128906 4.78125 0.175781 4.335938 0.179688 C 3.21875 0.175781 2.332031 -0.144531 1.683594 -0.792969 C 1.027344 -1.441406 0.703125 -2.320313 0.707031 -3.429688 C 0.703125 -4.570313 1.011719 -5.480469 1.632813 -6.15625 C 2.25 -6.824219 3.085938 -7.160156 4.136719 -7.164063 C 5.074219 -7.160156 5.816406 -6.855469 6.367188 -6.253906 C 6.914063 -5.644531 7.1875 -4.824219 7.191406 -3.785156 Z M 6.039063 -4.121094 C 6.027344 -4.746094 5.851563 -5.25 5.511719 -5.628906 C 5.164063 -6 4.710938 -6.1875 4.148438 -6.191406 C 3.507813 -6.1875 2.992188 -6.003906 2.609375 -5.644531 C 2.21875 -5.277344 1.996094 -4.769531 1.941406 -4.117188 Z M 6.039063 -4.121094 "/>
</symbol>
<symbol overflow="visible" id="glyph0-6">
<path style="stroke:none;" d="M 5.257813 -5.921875 C 5.125 -5.996094 4.984375 -6.050781 4.835938 -6.085938 C 4.679688 -6.121094 4.515625 -6.140625 4.335938 -6.140625 C 3.683594 -6.140625 3.183594 -5.925781 2.835938 -5.503906 C 2.488281 -5.078125 2.3125 -4.472656 2.316406 -3.683594 L 2.316406 0 L 1.160156 0 L 1.160156 -6.996094 L 2.316406 -6.996094 L 2.316406 -5.910156 C 2.554688 -6.332031 2.871094 -6.648438 3.257813 -6.855469 C 3.644531 -7.058594 4.113281 -7.160156 4.671875 -7.164063 C 4.75 -7.160156 4.839844 -7.15625 4.933594 -7.148438 C 5.027344 -7.136719 5.132813 -7.121094 5.253906 -7.101563 Z M 5.257813 -5.921875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-7">
<path style="stroke:none;" d="M 6.847656 -9.019531 L 6.847656 -7.789063 C 6.367188 -8.015625 5.914063 -8.1875 5.492188 -8.300781 C 5.0625 -8.410156 4.652344 -8.464844 4.261719 -8.46875 C 3.570313 -8.464844 3.042969 -8.332031 2.671875 -8.070313 C 2.296875 -7.800781 2.109375 -7.421875 2.113281 -6.933594 C 2.109375 -6.519531 2.234375 -6.210938 2.484375 -6 C 2.730469 -5.789063 3.199219 -5.617188 3.890625 -5.492188 L 4.652344 -5.335938 C 5.59375 -5.152344 6.289063 -4.835938 6.738281 -4.386719 C 7.183594 -3.933594 7.40625 -3.328125 7.410156 -2.574219 C 7.40625 -1.667969 7.101563 -0.984375 6.5 -0.519531 C 5.890625 -0.0546875 5.003906 0.175781 3.835938 0.179688 C 3.390625 0.175781 2.921875 0.125 2.425781 0.03125 C 1.925781 -0.0703125 1.410156 -0.214844 0.878906 -0.414063 L 0.878906 -1.710938 C 1.390625 -1.421875 1.890625 -1.203125 2.386719 -1.0625 C 2.875 -0.914063 3.359375 -0.84375 3.835938 -0.84375 C 4.554688 -0.84375 5.109375 -0.984375 5.503906 -1.265625 C 5.890625 -1.546875 6.085938 -1.949219 6.089844 -2.480469 C 6.085938 -2.933594 5.945313 -3.292969 5.667969 -3.554688 C 5.382813 -3.808594 4.921875 -4.003906 4.285156 -4.136719 L 3.515625 -4.285156 C 2.574219 -4.46875 1.890625 -4.761719 1.472656 -5.164063 C 1.050781 -5.5625 0.84375 -6.121094 0.84375 -6.832031 C 0.84375 -7.65625 1.132813 -8.304688 1.714844 -8.78125 C 2.292969 -9.253906 3.09375 -9.492188 4.117188 -9.496094 C 4.550781 -9.492188 4.996094 -9.453125 5.453125 -9.375 C 5.902344 -9.296875 6.367188 -9.175781 6.847656 -9.019531 Z M 6.847656 -9.019531 "/>
</symbol>
<symbol overflow="visible" id="glyph0-8">
<path style="stroke:none;" d="M 7.019531 -4.222656 L 7.019531 0 L 5.871094 0 L 5.871094 -4.183594 C 5.871094 -4.847656 5.742188 -5.34375 5.484375 -5.671875 C 5.226563 -6 4.839844 -6.164063 4.324219 -6.164063 C 3.699219 -6.164063 3.207031 -5.964844 2.851563 -5.570313 C 2.492188 -5.171875 2.3125 -4.632813 2.316406 -3.953125 L 2.316406 0 L 1.160156 0 L 1.160156 -9.71875 L 2.316406 -9.71875 L 2.316406 -5.910156 C 2.589844 -6.328125 2.914063 -6.640625 3.289063 -6.851563 C 3.65625 -7.054688 4.085938 -7.160156 4.578125 -7.164063 C 5.378906 -7.160156 5.988281 -6.910156 6.402344 -6.417969 C 6.8125 -5.917969 7.019531 -5.1875 7.019531 -4.222656 Z M 7.019531 -4.222656 "/>
</symbol>
<symbol overflow="visible" id="glyph0-9">
<path style="stroke:none;" d="M 4.386719 -3.515625 C 3.453125 -3.515625 2.808594 -3.40625 2.453125 -3.195313 C 2.09375 -2.980469 1.914063 -2.621094 1.917969 -2.113281 C 1.914063 -1.703125 2.050781 -1.378906 2.320313 -1.140625 C 2.585938 -0.898438 2.949219 -0.777344 3.417969 -0.78125 C 4.050781 -0.777344 4.5625 -1.003906 4.949219 -1.457031 C 5.332031 -1.90625 5.523438 -2.507813 5.527344 -3.261719 L 5.527344 -3.515625 Z M 6.675781 -3.992188 L 6.675781 0 L 5.527344 0 L 5.527344 -1.0625 C 5.261719 -0.632813 4.9375 -0.320313 4.546875 -0.121094 C 4.15625 0.078125 3.675781 0.175781 3.109375 0.179688 C 2.390625 0.175781 1.820313 -0.0234375 1.402344 -0.421875 C 0.976563 -0.820313 0.765625 -1.355469 0.769531 -2.035156 C 0.765625 -2.820313 1.03125 -3.414063 1.558594 -3.816406 C 2.082031 -4.214844 2.867188 -4.417969 3.917969 -4.417969 L 5.527344 -4.417969 L 5.527344 -4.527344 C 5.523438 -5.054688 5.351563 -5.464844 5.003906 -5.753906 C 4.65625 -6.042969 4.167969 -6.1875 3.542969 -6.191406 C 3.140625 -6.1875 2.75 -6.140625 2.371094 -6.046875 C 1.992188 -5.949219 1.628906 -5.804688 1.28125 -5.617188 L 1.28125 -6.675781 C 1.699219 -6.835938 2.105469 -6.960938 2.503906 -7.042969 C 2.894531 -7.121094 3.28125 -7.160156 3.660156 -7.164063 C 4.671875 -7.160156 5.425781 -6.898438 5.925781 -6.375 C 6.421875 -5.851563 6.671875 -5.054688 6.675781 -3.992188 Z M 6.675781 -3.992188 "/>
</symbol>
<symbol overflow="visible" id="glyph0-10">
<path style="stroke:none;" d="M 5.808594 -5.933594 L 5.808594 -9.71875 L 6.957031 -9.71875 L 6.957031 0 L 5.808594 0 L 5.808594 -1.050781 C 5.5625 -0.628906 5.257813 -0.320313 4.890625 -0.121094 C 4.519531 0.078125 4.078125 0.175781 3.566406 0.179688 C 2.71875 0.175781 2.03125 -0.160156 1.5 -0.832031 C 0.96875 -1.503906 0.703125 -2.390625 0.707031 -3.492188 C 0.703125 -4.589844 0.96875 -5.476563 1.5 -6.152344 C 2.03125 -6.824219 2.71875 -7.160156 3.566406 -7.164063 C 4.078125 -7.160156 4.519531 -7.058594 4.890625 -6.859375 C 5.257813 -6.65625 5.5625 -6.347656 5.808594 -5.933594 Z M 1.894531 -3.492188 C 1.890625 -2.644531 2.066406 -1.980469 2.414063 -1.5 C 2.761719 -1.019531 3.238281 -0.777344 3.847656 -0.78125 C 4.453125 -0.777344 4.933594 -1.019531 5.285156 -1.5 C 5.632813 -1.980469 5.804688 -2.644531 5.808594 -3.492188 C 5.804688 -4.335938 5.632813 -4.996094 5.285156 -5.480469 C 4.933594 -5.957031 4.453125 -6.199219 3.847656 -6.203125 C 3.238281 -6.199219 2.761719 -5.957031 2.414063 -5.480469 C 2.066406 -4.996094 1.890625 -4.335938 1.894531 -3.492188 Z M 1.894531 -3.492188 "/>
</symbol>
<symbol overflow="visible" id="glyph0-11">
<path style="stroke:none;" d="M 5.679688 -4.371094 C 5.945313 -4.277344 6.210938 -4.082031 6.46875 -3.785156 C 6.722656 -3.484375 6.976563 -3.070313 7.238281 -2.546875 L 8.519531 0 L 7.164063 0 L 5.972656 -2.390625 C 5.660156 -3.015625 5.363281 -3.429688 5.074219 -3.632813 C 4.785156 -3.835938 4.390625 -3.9375 3.890625 -3.941406 L 2.515625 -3.941406 L 2.515625 0 L 1.253906 0 L 1.253906 -9.324219 L 4.105469 -9.324219 C 5.167969 -9.320313 5.960938 -9.097656 6.488281 -8.65625 C 7.011719 -8.207031 7.277344 -7.535156 7.277344 -6.640625 C 7.277344 -6.046875 7.140625 -5.558594 6.867188 -5.175781 C 6.59375 -4.785156 6.195313 -4.519531 5.679688 -4.371094 Z M 2.515625 -8.289063 L 2.515625 -4.976563 L 4.105469 -4.976563 C 4.710938 -4.972656 5.167969 -5.113281 5.480469 -5.398438 C 5.785156 -5.675781 5.941406 -6.089844 5.945313 -6.640625 C 5.941406 -7.179688 5.785156 -7.589844 5.480469 -7.871094 C 5.167969 -8.144531 4.710938 -8.285156 4.105469 -8.289063 Z M 2.515625 -8.289063 "/>
</symbol>
<symbol overflow="visible" id="glyph0-12">
<path style="stroke:none;" d="M 1.253906 -9.324219 L 7.152344 -9.324219 L 7.152344 -8.265625 L 2.515625 -8.265625 L 2.515625 -5.503906 L 6.957031 -5.503906 L 6.957031 -4.441406 L 2.515625 -4.441406 L 2.515625 -1.0625 L 7.265625 -1.0625 L 7.265625 0 L 1.253906 0 Z M 1.253906 -9.324219 "/>
</symbol>
<symbol overflow="visible" id="glyph0-13">
<path style="stroke:none;" d="M -0.0390625 -9.324219 L 7.851563 -9.324219 L 7.851563 -8.265625 L 4.542969 -8.265625 L 4.542969 0 L 3.273438 0 L 3.273438 -8.265625 L -0.0390625 -8.265625 Z M -0.0390625 -9.324219 "/>
</symbol>
<symbol overflow="visible" id="glyph0-14">
<path style="stroke:none;" d=""/>
</symbol>
<symbol overflow="visible" id="glyph0-15">
<path style="stroke:none;" d="M 1.894531 -3.492188 C 1.890625 -2.644531 2.066406 -1.980469 2.414063 -1.5 C 2.761719 -1.019531 3.238281 -0.777344 3.847656 -0.78125 C 4.453125 -0.777344 4.933594 -1.019531 5.285156 -1.5 C 5.632813 -1.980469 5.804688 -2.644531 5.808594 -3.492188 C 5.804688 -4.335938 5.632813 -4.996094 5.285156 -5.480469 C 4.933594 -5.957031 4.453125 -6.199219 3.847656 -6.203125 C 3.238281 -6.199219 2.761719 -5.957031 2.414063 -5.480469 C 2.066406 -4.996094 1.890625 -4.335938 1.894531 -3.492188 Z M 5.808594 -1.050781 C 5.5625 -0.628906 5.257813 -0.320313 4.890625 -0.121094 C 4.519531 0.078125 4.078125 0.175781 3.566406 0.179688 C 2.71875 0.175781 2.03125 -0.160156 1.5 -0.832031 C 0.96875 -1.503906 0.703125 -2.390625 0.707031 -3.492188 C 0.703125 -4.589844 0.96875 -5.476563 1.5 -6.152344 C 2.03125 -6.824219 2.71875 -7.160156 3.566406 -7.164063 C 4.078125 -7.160156 4.519531 -7.058594 4.890625 -6.859375 C 5.257813 -6.65625 5.5625 -6.347656 5.808594 -5.933594 L 5.808594 -6.996094 L 6.957031 -6.996094 L 6.957031 2.660156 L 5.808594 2.660156 Z M 5.808594 -1.050781 "/>
</symbol>
<symbol overflow="visible" id="glyph0-16">
<path style="stroke:none;" d="M 5.039063 -8.46875 C 4.125 -8.464844 3.398438 -8.125 2.859375 -7.445313 C 2.320313 -6.761719 2.050781 -5.832031 2.050781 -4.652344 C 2.050781 -3.476563 2.320313 -2.546875 2.859375 -1.867188 C 3.398438 -1.183594 4.125 -0.84375 5.039063 -0.84375 C 5.953125 -0.84375 6.675781 -1.183594 7.214844 -1.867188 C 7.746094 -2.546875 8.015625 -3.476563 8.019531 -4.652344 C 8.015625 -5.832031 7.746094 -6.761719 7.214844 -7.445313 C 6.675781 -8.125 5.953125 -8.464844 5.039063 -8.46875 Z M 6.808594 -0.167969 L 8.46875 1.648438 L 6.945313 1.648438 L 5.566406 0.15625 C 5.425781 0.160156 5.320313 0.164063 5.25 0.171875 C 5.175781 0.171875 5.105469 0.175781 5.039063 0.179688 C 3.726563 0.175781 2.675781 -0.257813 1.894531 -1.132813 C 1.105469 -2.007813 0.714844 -3.183594 0.71875 -4.652344 C 0.714844 -6.125 1.105469 -7.300781 1.894531 -8.179688 C 2.675781 -9.054688 3.726563 -9.492188 5.039063 -9.496094 C 6.347656 -9.492188 7.390625 -9.054688 8.175781 -8.179688 C 8.957031 -7.300781 9.351563 -6.125 9.351563 -4.652344 C 9.351563 -3.570313 9.132813 -2.644531 8.699219 -1.875 C 8.261719 -1.101563 7.632813 -0.53125 6.808594 -0.167969 Z M 6.808594 -0.167969 "/>
</symbol>
<symbol overflow="visible" id="glyph0-17">
<path style="stroke:none;" d="M 2.316406 -1.050781 L 2.316406 2.660156 L 1.160156 2.660156 L 1.160156 -6.996094 L 2.316406 -6.996094 L 2.316406 -5.933594 C 2.554688 -6.347656 2.859375 -6.65625 3.230469 -6.859375 C 3.597656 -7.058594 4.039063 -7.160156 4.554688 -7.164063 C 5.398438 -7.160156 6.089844 -6.824219 6.625 -6.152344 C 7.152344 -5.476563 7.417969 -4.589844 7.421875 -3.492188 C 7.417969 -2.390625 7.152344 -1.503906 6.625 -0.832031 C 6.089844 -0.160156 5.398438 0.175781 4.554688 0.179688 C 4.039063 0.175781 3.597656 0.078125 3.230469 -0.121094 C 2.859375 -0.320313 2.554688 -0.628906 2.316406 -1.050781 Z M 6.226563 -3.492188 C 6.222656 -4.335938 6.046875 -4.996094 5.703125 -5.480469 C 5.351563 -5.957031 4.875 -6.199219 4.273438 -6.203125 C 3.660156 -6.199219 3.183594 -5.957031 2.835938 -5.480469 C 2.488281 -4.996094 2.3125 -4.335938 2.316406 -3.492188 C 2.3125 -2.644531 2.488281 -1.980469 2.835938 -1.5 C 3.183594 -1.019531 3.660156 -0.777344 4.273438 -0.78125 C 4.875 -0.777344 5.351563 -1.019531 5.703125 -1.5 C 6.046875 -1.980469 6.222656 -2.644531 6.226563 -3.492188 Z M 6.226563 -3.492188 "/>
</symbol>
<symbol overflow="visible" id="glyph0-18">
<path style="stroke:none;" d="M 4.117188 0.648438 C 3.789063 1.480469 3.472656 2.023438 3.164063 2.277344 C 2.855469 2.53125 2.441406 2.660156 1.929688 2.660156 L 1.011719 2.660156 L 1.011719 1.699219 L 1.6875 1.699219 C 2 1.699219 2.242188 1.621094 2.421875 1.472656 C 2.59375 1.320313 2.789063 0.96875 3.003906 0.414063 L 3.210938 -0.113281 L 0.382813 -6.996094 L 1.597656 -6.996094 L 3.785156 -1.523438 L 5.972656 -6.996094 L 7.191406 -6.996094 Z M 4.117188 0.648438 "/>
</symbol>
<symbol overflow="visible" id="glyph0-19">
<path style="stroke:none;" d="M 1.113281 -9.324219 L 2.378906 -9.324219 L 2.378906 -3.660156 C 2.378906 -2.65625 2.558594 -1.9375 2.921875 -1.5 C 3.285156 -1.0625 3.871094 -0.84375 4.683594 -0.84375 C 5.488281 -0.84375 6.074219 -1.0625 6.441406 -1.5 C 6.800781 -1.9375 6.984375 -2.65625 6.984375 -3.660156 L 6.984375 -9.324219 L 8.25 -9.324219 L 8.25 -3.503906 C 8.25 -2.285156 7.949219 -1.367188 7.347656 -0.75 C 6.746094 -0.132813 5.855469 0.175781 4.683594 0.179688 C 3.503906 0.175781 2.617188 -0.132813 2.015625 -0.75 C 1.414063 -1.367188 1.113281 -2.285156 1.113281 -3.503906 Z M 1.113281 -9.324219 "/>
</symbol>
<symbol overflow="visible" id="glyph0-20">
<path style="stroke:none;" d="M 6.226563 -3.492188 C 6.222656 -4.335938 6.046875 -4.996094 5.703125 -5.480469 C 5.351563 -5.957031 4.875 -6.199219 4.273438 -6.203125 C 3.660156 -6.199219 3.183594 -5.957031 2.835938 -5.480469 C 2.488281 -4.996094 2.3125 -4.335938 2.316406 -3.492188 C 2.3125 -2.644531 2.488281 -1.980469 2.835938 -1.5 C 3.183594 -1.019531 3.660156 -0.777344 4.273438 -0.78125 C 4.875 -0.777344 5.351563 -1.019531 5.703125 -1.5 C 6.046875 -1.980469 6.222656 -2.644531 6.226563 -3.492188 Z M 2.316406 -5.933594 C 2.554688 -6.347656 2.859375 -6.65625 3.230469 -6.859375 C 3.597656 -7.058594 4.039063 -7.160156 4.554688 -7.164063 C 5.398438 -7.160156 6.089844 -6.824219 6.625 -6.152344 C 7.152344 -5.476563 7.417969 -4.589844 7.421875 -3.492188 C 7.417969 -2.390625 7.152344 -1.503906 6.625 -0.832031 C 6.089844 -0.160156 5.398438 0.175781 4.554688 0.179688 C 4.039063 0.175781 3.597656 0.078125 3.230469 -0.121094 C 2.859375 -0.320313 2.554688 -0.628906 2.316406 -1.050781 L 2.316406 0 L 1.160156 0 L 1.160156 -9.71875 L 2.316406 -9.71875 Z M 2.316406 -5.933594 "/>
</symbol>
<symbol overflow="visible" id="glyph0-21">
<path style="stroke:none;" d="M 6.242188 -6.726563 L 6.242188 -5.652344 C 5.914063 -5.828125 5.589844 -5.960938 5.261719 -6.054688 C 4.933594 -6.140625 4.601563 -6.1875 4.273438 -6.191406 C 3.523438 -6.1875 2.945313 -5.949219 2.535156 -5.480469 C 2.121094 -5.003906 1.914063 -4.34375 1.917969 -3.492188 C 1.914063 -2.636719 2.121094 -1.972656 2.535156 -1.5 C 2.945313 -1.027344 3.523438 -0.789063 4.273438 -0.792969 C 4.601563 -0.789063 4.933594 -0.835938 5.261719 -0.925781 C 5.589844 -1.015625 5.914063 -1.148438 6.242188 -1.332031 L 6.242188 -0.269531 C 5.914063 -0.117188 5.582031 -0.0078125 5.242188 0.0664063 C 4.898438 0.136719 4.535156 0.175781 4.148438 0.179688 C 3.09375 0.175781 2.253906 -0.152344 1.636719 -0.8125 C 1.011719 -1.472656 0.703125 -2.367188 0.707031 -3.492188 C 0.703125 -4.632813 1.015625 -5.527344 1.644531 -6.183594 C 2.269531 -6.832031 3.128906 -7.160156 4.222656 -7.164063 C 4.574219 -7.160156 4.917969 -7.125 5.257813 -7.054688 C 5.59375 -6.980469 5.921875 -6.871094 6.242188 -6.726563 Z M 6.242188 -6.726563 "/>
</symbol>
<symbol overflow="visible" id="glyph0-22">
<path style="stroke:none;" d="M 1.160156 -9.71875 L 2.316406 -9.71875 L 2.316406 -3.980469 L 5.746094 -6.996094 L 7.214844 -6.996094 L 3.503906 -3.722656 L 7.371094 0 L 5.871094 0 L 2.316406 -3.417969 L 2.316406 0 L 1.160156 0 Z M 1.160156 -9.71875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-23">
<path style="stroke:none;" d="M 1.253906 -9.324219 L 6.613281 -9.324219 L 6.613281 -8.265625 L 2.515625 -8.265625 L 2.515625 -5.515625 L 6.214844 -5.515625 L 6.214844 -4.453125 L 2.515625 -4.453125 L 2.515625 0 L 1.253906 0 Z M 1.253906 -9.324219 "/>
</symbol>
<symbol overflow="visible" id="glyph0-24">
<path style="stroke:none;" d="M 7.019531 -4.222656 L 7.019531 0 L 5.871094 0 L 5.871094 -4.183594 C 5.871094 -4.847656 5.742188 -5.34375 5.484375 -5.671875 C 5.226563 -6 4.839844 -6.164063 4.324219 -6.164063 C 3.699219 -6.164063 3.207031 -5.964844 2.851563 -5.570313 C 2.492188 -5.171875 2.3125 -4.632813 2.316406 -3.953125 L 2.316406 0 L 1.160156 0 L 1.160156 -6.996094 L 2.316406 -6.996094 L 2.316406 -5.910156 C 2.589844 -6.328125 2.914063 -6.640625 3.289063 -6.851563 C 3.65625 -7.054688 4.085938 -7.160156 4.578125 -7.164063 C 5.378906 -7.160156 5.988281 -6.910156 6.402344 -6.417969 C 6.8125 -5.917969 7.019531 -5.1875 7.019531 -4.222656 Z M 7.019531 -4.222656 "/>
</symbol>
<symbol overflow="visible" id="glyph0-25">
<path style="stroke:none;" d="M 1.207031 -6.996094 L 2.355469 -6.996094 L 2.355469 0 L 1.207031 0 Z M 1.207031 -9.71875 L 2.355469 -9.71875 L 2.355469 -8.265625 L 1.207031 -8.265625 Z M 1.207031 -9.71875 "/>
</symbol>
<symbol overflow="visible" id="glyph0-26">
<path style="stroke:none;" d="M 3.917969 -6.191406 C 3.296875 -6.1875 2.808594 -5.945313 2.453125 -5.46875 C 2.09375 -4.984375 1.914063 -4.328125 1.917969 -3.492188 C 1.914063 -2.652344 2.09375 -1.992188 2.449219 -1.511719 C 2.804688 -1.03125 3.292969 -0.789063 3.917969 -0.792969 C 4.527344 -0.789063 5.011719 -1.03125 5.371094 -1.515625 C 5.726563 -1.996094 5.90625 -2.65625 5.910156 -3.492188 C 5.90625 -4.320313 5.726563 -4.976563 5.371094 -5.460938 C 5.011719 -5.945313 4.527344 -6.1875 3.917969 -6.191406 Z M 3.917969 -7.164063 C 4.914063 -7.160156 5.699219 -6.835938 6.269531 -6.1875 C 6.839844 -5.539063 7.125 -4.640625 7.128906 -3.492188 C 7.125 -2.34375 6.839844 -1.445313 6.269531 -0.796875 C 5.699219 -0.148438 4.914063 0.175781 3.917969 0.179688 C 2.910156 0.175781 2.125 -0.148438 1.558594 -0.796875 C 0.988281 -1.445313 0.703125 -2.34375 0.707031 -3.492188 C 0.703125 -4.640625 0.988281 -5.539063 1.558594 -6.1875 C 2.125 -6.835938 2.910156 -7.160156 3.917969 -7.164063 Z M 3.917969 -7.164063 "/>
</symbol>
<symbol overflow="visible" id="glyph0-27">
<path style="stroke:none;" d="M 2.515625 -8.289063 L 2.515625 -1.035156 L 4.042969 -1.035156 C 5.328125 -1.03125 6.269531 -1.324219 6.867188 -1.910156 C 7.464844 -2.492188 7.761719 -3.410156 7.765625 -4.671875 C 7.761719 -5.917969 7.464844 -6.835938 6.867188 -7.417969 C 6.269531 -7.996094 5.328125 -8.285156 4.042969 -8.289063 Z M 1.253906 -9.324219 L 3.847656 -9.324219 C 5.652344 -9.320313 6.976563 -8.945313 7.824219 -8.195313 C 8.667969 -7.441406 9.09375 -6.265625 9.09375 -4.671875 C 9.09375 -3.0625 8.667969 -1.882813 7.820313 -1.128906 C 6.96875 -0.375 5.644531 0 3.847656 0 L 1.253906 0 Z M 1.253906 -9.324219 "/>
</symbol>
<symbol overflow="visible" id="glyph0-28">
<path style="stroke:none;" d="M 0.382813 -6.996094 L 1.597656 -6.996094 L 3.785156 -1.125 L 5.972656 -6.996094 L 7.191406 -6.996094 L 4.566406 0 L 3.003906 0 Z M 0.382813 -6.996094 "/>
</symbol>
<symbol overflow="visible" id="glyph0-29">
<path style="stroke:none;" d="M -0.0234375 -9.324219 L 1.332031 -9.324219 L 3.917969 -5.492188 L 6.484375 -9.324219 L 7.839844 -9.324219 L 4.542969 -4.441406 L 4.542969 0 L 3.273438 0 L 3.273438 -4.441406 Z M -0.0234375 -9.324219 "/>
</symbol>
<symbol overflow="visible" id="glyph0-30">
<path style="stroke:none;" d="M 2.515625 -8.289063 L 2.515625 -4.785156 L 4.105469 -4.785156 C 4.6875 -4.78125 5.140625 -4.933594 5.464844 -5.238281 C 5.78125 -5.542969 5.941406 -5.976563 5.945313 -6.539063 C 5.941406 -7.097656 5.78125 -7.527344 5.464844 -7.832031 C 5.140625 -8.132813 4.6875 -8.285156 4.105469 -8.289063 Z M 1.253906 -9.324219 L 4.105469 -9.324219 C 5.148438 -9.320313 5.9375 -9.085938 6.472656 -8.613281 C 7.007813 -8.140625 7.277344 -7.449219 7.277344 -6.539063 C 7.277344 -5.617188 7.007813 -4.921875 6.472656 -4.453125 C 5.9375 -3.980469 5.148438 -3.742188 4.105469 -3.746094 L 2.515625 -3.746094 L 2.515625 0 L 1.253906 0 Z M 1.253906 -9.324219 "/>
</symbol>
<symbol overflow="visible" id="glyph0-31">
<path style="stroke:none;" d="M 5.808594 -3.578125 C 5.804688 -4.410156 5.632813 -5.054688 5.292969 -5.515625 C 4.945313 -5.96875 4.464844 -6.199219 3.847656 -6.203125 C 3.226563 -6.199219 2.746094 -5.96875 2.40625 -5.515625 C 2.0625 -5.054688 1.890625 -4.410156 1.894531 -3.578125 C 1.890625 -2.746094 2.0625 -2.101563 2.40625 -1.648438 C 2.746094 -1.1875 3.226563 -0.960938 3.847656 -0.960938 C 4.464844 -0.960938 4.945313 -1.1875 5.292969 -1.648438 C 5.632813 -2.101563 5.804688 -2.746094 5.808594 -3.578125 Z M 6.957031 -0.867188 C 6.953125 0.320313 6.691406 1.203125 6.164063 1.789063 C 5.632813 2.367188 4.820313 2.660156 3.734375 2.660156 C 3.328125 2.660156 2.949219 2.628906 2.589844 2.570313 C 2.230469 2.507813 1.882813 2.414063 1.550781 2.292969 L 1.550781 1.175781 C 1.882813 1.355469 2.214844 1.488281 2.546875 1.578125 C 2.875 1.664063 3.210938 1.710938 3.554688 1.710938 C 4.304688 1.710938 4.867188 1.511719 5.246094 1.121094 C 5.617188 0.722656 5.804688 0.132813 5.808594 -0.664063 L 5.808594 -1.230469 C 5.566406 -0.816406 5.261719 -0.507813 4.894531 -0.304688 C 4.519531 -0.101563 4.078125 0 3.566406 0 C 2.707031 0 2.015625 -0.324219 1.492188 -0.980469 C 0.964844 -1.628906 0.703125 -2.496094 0.707031 -3.578125 C 0.703125 -4.660156 0.964844 -5.527344 1.492188 -6.183594 C 2.015625 -6.832031 2.707031 -7.160156 3.566406 -7.164063 C 4.078125 -7.160156 4.519531 -7.058594 4.894531 -6.855469 C 5.261719 -6.652344 5.566406 -6.34375 5.808594 -5.933594 L 5.808594 -6.996094 L 6.957031 -6.996094 Z M 6.957031 -0.867188 "/>
</symbol>
<symbol overflow="visible" id="glyph0-32">
<path style="stroke:none;" d="M 6.652344 -5.652344 C 6.933594 -6.164063 7.277344 -6.542969 7.679688 -6.792969 C 8.078125 -7.035156 8.550781 -7.160156 9.09375 -7.164063 C 9.820313 -7.160156 10.382813 -6.90625 10.78125 -6.398438 C 11.175781 -5.886719 11.371094 -5.160156 11.375 -4.222656 L 11.375 0 L 10.21875 0 L 10.21875 -4.183594 C 10.21875 -4.855469 10.097656 -5.351563 9.863281 -5.675781 C 9.621094 -6 9.261719 -6.164063 8.777344 -6.164063 C 8.179688 -6.164063 7.707031 -5.964844 7.363281 -5.570313 C 7.015625 -5.171875 6.84375 -4.632813 6.847656 -3.953125 L 6.847656 0 L 5.691406 0 L 5.691406 -4.183594 C 5.6875 -4.855469 5.570313 -5.351563 5.332031 -5.679688 C 5.09375 -6 4.726563 -6.164063 4.234375 -6.164063 C 3.644531 -6.164063 3.179688 -5.964844 2.835938 -5.566406 C 2.488281 -5.167969 2.3125 -4.628906 2.316406 -3.953125 L 2.316406 0 L 1.160156 0 L 1.160156 -6.996094 L 2.316406 -6.996094 L 2.316406 -5.910156 C 2.574219 -6.335938 2.890625 -6.652344 3.257813 -6.855469 C 3.625 -7.058594 4.058594 -7.160156 4.566406 -7.164063 C 5.070313 -7.160156 5.503906 -7.03125 5.863281 -6.777344 C 6.21875 -6.515625 6.480469 -6.140625 6.652344 -5.652344 Z M 6.652344 -5.652344 "/>
</symbol>
<symbol overflow="visible" id="glyph0-33">
<path style="stroke:none;" d="M 1.253906 -9.324219 L 2.515625 -9.324219 L 2.515625 -1.0625 L 7.058594 -1.0625 L 7.058594 0 L 1.253906 0 Z M 1.253906 -9.324219 "/>
</symbol>
<symbol overflow="visible" id="glyph0-34">
<path style="stroke:none;" d="M 1.5 -1.585938 L 2.816406 -1.585938 L 2.816406 0 L 1.5 0 Z M 1.5 -6.613281 L 2.816406 -6.613281 L 2.816406 -5.027344 L 1.5 -5.027344 Z M 1.5 -6.613281 "/>
</symbol>
<symbol overflow="visible" id="glyph0-35">
<path style="stroke:none;" d="M 1.585938 -1.0625 L 3.648438 -1.0625 L 3.648438 -8.175781 L 1.40625 -7.726563 L 1.40625 -8.875 L 3.636719 -9.324219 L 4.898438 -9.324219 L 4.898438 -1.0625 L 6.957031 -1.0625 L 6.957031 0 L 1.585938 0 Z M 1.585938 -1.0625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-36">
<path style="stroke:none;" d="M 4.066406 -8.496094 C 3.417969 -8.496094 2.929688 -8.175781 2.601563 -7.535156 C 2.273438 -6.894531 2.109375 -5.933594 2.113281 -4.652344 C 2.109375 -3.371094 2.273438 -2.410156 2.601563 -1.773438 C 2.929688 -1.132813 3.417969 -0.8125 4.066406 -0.816406 C 4.71875 -0.8125 5.207031 -1.132813 5.535156 -1.773438 C 5.859375 -2.410156 6.023438 -3.371094 6.027344 -4.652344 C 6.023438 -5.933594 5.859375 -6.894531 5.535156 -7.535156 C 5.207031 -8.175781 4.71875 -8.496094 4.066406 -8.496094 Z M 4.066406 -9.496094 C 5.109375 -9.492188 5.90625 -9.078125 6.460938 -8.253906 C 7.011719 -7.425781 7.289063 -6.226563 7.289063 -4.652344 C 7.289063 -3.082031 7.011719 -1.882813 6.460938 -1.058594 C 5.90625 -0.234375 5.109375 0.175781 4.066406 0.179688 C 3.019531 0.175781 2.21875 -0.234375 1.671875 -1.058594 C 1.117188 -1.882813 0.84375 -3.082031 0.84375 -4.652344 C 0.84375 -6.226563 1.117188 -7.425781 1.671875 -8.253906 C 2.21875 -9.078125 3.019531 -9.492188 4.066406 -9.496094 Z M 4.066406 -9.496094 "/>
</symbol>
<symbol overflow="visible" id="glyph0-37">
<path style="stroke:none;" d="M 1.367188 -1.585938 L 2.6875 -1.585938 L 2.6875 0 L 1.367188 0 Z M 1.367188 -1.585938 "/>
</symbol>
<symbol overflow="visible" id="glyph0-38">
<path style="stroke:none;" d="M 4.222656 -5.164063 C 3.65625 -5.160156 3.207031 -4.96875 2.875 -4.582031 C 2.542969 -4.195313 2.378906 -3.664063 2.378906 -2.992188 C 2.378906 -2.320313 2.542969 -1.789063 2.875 -1.402344 C 3.207031 -1.007813 3.65625 -0.8125 4.222656 -0.816406 C 4.785156 -0.8125 5.234375 -1.007813 5.566406 -1.402344 C 5.898438 -1.789063 6.0625 -2.320313 6.066406 -2.992188 C 6.0625 -3.664063 5.898438 -4.195313 5.566406 -4.582031 C 5.234375 -4.96875 4.785156 -5.160156 4.222656 -5.164063 Z M 6.726563 -9.121094 L 6.726563 -7.96875 C 6.410156 -8.117188 6.089844 -8.230469 5.769531 -8.3125 C 5.445313 -8.390625 5.125 -8.429688 4.808594 -8.433594 C 3.972656 -8.429688 3.335938 -8.148438 2.898438 -7.589844 C 2.457031 -7.023438 2.207031 -6.175781 2.148438 -5.039063 C 2.390625 -5.402344 2.699219 -5.679688 3.070313 -5.875 C 3.441406 -6.066406 3.847656 -6.164063 4.296875 -6.164063 C 5.230469 -6.164063 5.972656 -5.878906 6.515625 -5.3125 C 7.058594 -4.742188 7.328125 -3.96875 7.332031 -2.992188 C 7.328125 -2.03125 7.046875 -1.261719 6.480469 -0.6875 C 5.914063 -0.109375 5.160156 0.175781 4.222656 0.179688 C 3.140625 0.175781 2.316406 -0.234375 1.75 -1.058594 C 1.175781 -1.882813 0.890625 -3.082031 0.894531 -4.652344 C 0.890625 -6.125 1.238281 -7.300781 1.941406 -8.179688 C 2.636719 -9.054688 3.578125 -9.492188 4.761719 -9.496094 C 5.074219 -9.492188 5.394531 -9.460938 5.71875 -9.402344 C 6.039063 -9.335938 6.375 -9.242188 6.726563 -9.121094 Z M 6.726563 -9.121094 "/>
</symbol>
<symbol overflow="visible" id="glyph0-39">
<path style="stroke:none;" d="M 1.5 -1.585938 L 2.816406 -1.585938 L 2.816406 -0.511719 L 1.792969 1.488281 L 0.988281 1.488281 L 1.5 -0.511719 Z M 1.5 -1.585938 "/>
</symbol>
<symbol overflow="visible" id="glyph0-40">
<path style="stroke:none;" d="M 2.453125 -1.0625 L 6.859375 -1.0625 L 6.859375 0 L 0.9375 0 L 0.9375 -1.0625 C 1.414063 -1.554688 2.066406 -2.21875 2.894531 -3.054688 C 3.722656 -3.886719 4.242188 -4.425781 4.453125 -4.671875 C 4.855469 -5.121094 5.136719 -5.507813 5.296875 -5.824219 C 5.457031 -6.136719 5.535156 -6.445313 5.539063 -6.753906 C 5.535156 -7.246094 5.363281 -7.648438 5.015625 -7.964844 C 4.667969 -8.273438 4.214844 -8.429688 3.660156 -8.433594 C 3.261719 -8.429688 2.84375 -8.363281 2.40625 -8.226563 C 1.964844 -8.085938 1.496094 -7.875 1 -7.601563 L 1 -8.875 C 1.503906 -9.078125 1.976563 -9.234375 2.421875 -9.339844 C 2.859375 -9.441406 3.265625 -9.492188 3.636719 -9.496094 C 4.601563 -9.492188 5.371094 -9.25 5.945313 -8.769531 C 6.519531 -8.285156 6.808594 -7.640625 6.808594 -6.832031 C 6.808594 -6.449219 6.734375 -6.085938 6.59375 -5.742188 C 6.445313 -5.398438 6.183594 -4.992188 5.808594 -4.527344 C 5.699219 -4.40625 5.371094 -4.058594 4.816406 -3.480469 C 4.257813 -2.902344 3.46875 -2.09375 2.453125 -1.0625 Z M 2.453125 -1.0625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-41">
<path style="stroke:none;" d="M 0.625 -4.015625 L 3.992188 -4.015625 L 3.992188 -2.992188 L 0.625 -2.992188 Z M 0.625 -4.015625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-42">
<path style="stroke:none;" d="M 4.371094 -8.082031 L 2.660156 -3.441406 L 6.089844 -3.441406 Z M 3.660156 -9.324219 L 5.089844 -9.324219 L 8.644531 0 L 7.332031 0 L 6.484375 -2.390625 L 2.28125 -2.390625 L 1.429688 0 L 0.101563 0 Z M 3.660156 -9.324219 "/>
</symbol>
</g>
</defs>
<g id="surface144239">
<rect x="0" y="0" width="1503" height="965" style="fill:rgb(100%,100%,100%);fill-opacity:1;stroke:none;"/>
<path style="fill-rule:evenodd;fill:rgb(89.803922%,89.803922%,89.803922%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 23.187567 0.685658 L 34.111005 0.685658 L 34.111005 7.312806 L 23.187567 7.312806 Z M 23.187567 0.685658 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 23.187567 0.685658 L 34.111005 0.685658 L 34.111005 7.312806 L 23.187567 7.312806 Z M 23.187567 0.685658 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-0" x="695.660156" y="430.292969"/>
<use xlink:href="#glyph0-1" x="704.585938" y="430.292969"/>
<use xlink:href="#glyph0-2" x="708.140625" y="430.292969"/>
<use xlink:href="#glyph0-3" x="716.246094" y="430.292969"/>
<use xlink:href="#glyph0-4" x="722.90625" y="430.292969"/>
<use xlink:href="#glyph0-5" x="727.925781" y="430.292969"/>
<use xlink:href="#glyph0-6" x="735.796875" y="430.292969"/>
</g>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M -5.991925 15.99679 L 4.931513 15.99679 L 4.931513 22.623939 L -5.991925 22.623939 Z M -5.991925 15.99679 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M -5.991925 15.99679 L 4.931513 15.99679 L 4.931513 22.623939 L -5.991925 22.623939 Z M -5.991925 15.99679 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 7.806317 15.859486 L 18.729755 15.859486 L 18.729755 22.486634 L 7.806317 22.486634 Z M 7.806317 15.859486 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 7.806317 15.859486 L 18.729755 15.859486 L 18.729755 22.486634 L 7.806317 22.486634 Z M 7.806317 15.859486 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 19.685028 15.951087 L 30.608466 15.951087 L 30.608466 22.578236 L 19.685028 22.578236 Z M 19.685028 15.951087 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 19.685028 15.951087 L 30.608466 15.951087 L 30.608466 22.578236 L 19.685028 22.578236 Z M 19.685028 15.951087 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-7" x="116.054688" y="736.515625"/>
<use xlink:href="#glyph0-8" x="124.179688" y="736.515625"/>
<use xlink:href="#glyph0-9" x="132.285156" y="736.515625"/>
<use xlink:href="#glyph0-6" x="140.117188" y="736.515625"/>
<use xlink:href="#glyph0-10" x="145.371094" y="736.515625"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-7" x="392.019531" y="733.773438"/>
<use xlink:href="#glyph0-8" x="400.144531" y="733.773438"/>
<use xlink:href="#glyph0-9" x="408.25" y="733.773438"/>
<use xlink:href="#glyph0-6" x="416.082031" y="733.773438"/>
<use xlink:href="#glyph0-10" x="421.335938" y="733.773438"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-7" x="629.59375" y="735.601563"/>
<use xlink:href="#glyph0-8" x="637.71875" y="735.601563"/>
<use xlink:href="#glyph0-9" x="645.824219" y="735.601563"/>
<use xlink:href="#glyph0-6" x="653.65625" y="735.601563"/>
<use xlink:href="#glyph0-10" x="658.910156" y="735.601563"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-11" x="959.320313" y="742.324219"/>
<use xlink:href="#glyph0-12" x="968.207031" y="742.324219"/>
<use xlink:href="#glyph0-7" x="976.292969" y="742.324219"/>
<use xlink:href="#glyph0-13" x="984.417969" y="742.324219"/>
<use xlink:href="#glyph0-14" x="992.230469" y="742.324219"/>
<use xlink:href="#glyph0-11" x="996.292969" y="742.324219"/>
<use xlink:href="#glyph0-5" x="1005.179688" y="742.324219"/>
<use xlink:href="#glyph0-15" x="1013.050781" y="742.324219"/>
<use xlink:href="#glyph0-2" x="1021.175781" y="742.324219"/>
<use xlink:href="#glyph0-5" x="1029.28125" y="742.324219"/>
<use xlink:href="#glyph0-3" x="1037.152344" y="742.324219"/>
<use xlink:href="#glyph0-4" x="1043.8125" y="742.324219"/>
<use xlink:href="#glyph0-14" x="1048.832031" y="742.324219"/>
<use xlink:href="#glyph0-16" x="1052.894531" y="742.324219"/>
<use xlink:href="#glyph0-2" x="1062.972656" y="742.324219"/>
<use xlink:href="#glyph0-5" x="1071.078125" y="742.324219"/>
<use xlink:href="#glyph0-2" x="1078.949219" y="742.324219"/>
<use xlink:href="#glyph0-5" x="1087.054688" y="742.324219"/>
</g>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 56.952802 17.127454 L 67.876239 17.127454 L 67.876239 23.754603 L 56.952802 23.754603 Z M 56.952802 17.127454 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 56.952802 17.127454 L 67.876239 17.127454 L 67.876239 23.754603 L 56.952802 23.754603 Z M 56.952802 17.127454 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-11" x="1334.128906" y="759.132813"/>
<use xlink:href="#glyph0-12" x="1343.015625" y="759.132813"/>
<use xlink:href="#glyph0-7" x="1351.101563" y="759.132813"/>
<use xlink:href="#glyph0-13" x="1359.226563" y="759.132813"/>
<use xlink:href="#glyph0-14" x="1367.039063" y="759.132813"/>
<use xlink:href="#glyph0-11" x="1371.101563" y="759.132813"/>
<use xlink:href="#glyph0-5" x="1379.988281" y="759.132813"/>
<use xlink:href="#glyph0-17" x="1387.859375" y="759.132813"/>
<use xlink:href="#glyph0-1" x="1395.984375" y="759.132813"/>
<use xlink:href="#glyph0-18" x="1399.539063" y="759.132813"/>
<use xlink:href="#glyph0-14" x="1407.117188" y="759.132813"/>
<use xlink:href="#glyph0-16" x="1411.179688" y="759.132813"/>
<use xlink:href="#glyph0-2" x="1421.257813" y="759.132813"/>
<use xlink:href="#glyph0-5" x="1429.363281" y="759.132813"/>
<use xlink:href="#glyph0-2" x="1437.234375" y="759.132813"/>
<use xlink:href="#glyph0-5" x="1445.339844" y="759.132813"/>
</g>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 56.818427 0.875697 L 67.741864 0.875697 L 67.741864 7.502845 L 56.818427 7.502845 Z M 56.818427 0.875697 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 56.818427 0.875697 L 67.741864 0.875697 L 67.741864 7.502845 L 56.818427 7.502845 Z M 56.818427 0.875697 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-19" x="1314.332031" y="434.097656"/>
<use xlink:href="#glyph0-3" x="1323.6875" y="434.097656"/>
<use xlink:href="#glyph0-5" x="1330.347656" y="434.097656"/>
<use xlink:href="#glyph0-6" x="1338.21875" y="434.097656"/>
<use xlink:href="#glyph0-14" x="1343.472656" y="434.097656"/>
<use xlink:href="#glyph0-0" x="1347.535156" y="434.097656"/>
<use xlink:href="#glyph0-9" x="1356.460938" y="434.097656"/>
<use xlink:href="#glyph0-1" x="1364.292969" y="434.097656"/>
<use xlink:href="#glyph0-1" x="1367.847656" y="434.097656"/>
<use xlink:href="#glyph0-20" x="1371.402344" y="434.097656"/>
<use xlink:href="#glyph0-9" x="1379.527344" y="434.097656"/>
<use xlink:href="#glyph0-21" x="1387.359375" y="434.097656"/>
<use xlink:href="#glyph0-22" x="1394.390625" y="434.097656"/>
<use xlink:href="#glyph0-14" x="1401.792969" y="434.097656"/>
<use xlink:href="#glyph0-23" x="1405.855469" y="434.097656"/>
<use xlink:href="#glyph0-2" x="1413.21875" y="434.097656"/>
<use xlink:href="#glyph0-24" x="1421.324219" y="434.097656"/>
<use xlink:href="#glyph0-21" x="1429.429688" y="434.097656"/>
<use xlink:href="#glyph0-4" x="1436.460938" y="434.097656"/>
<use xlink:href="#glyph0-25" x="1441.480469" y="434.097656"/>
<use xlink:href="#glyph0-26" x="1445.035156" y="434.097656"/>
<use xlink:href="#glyph0-24" x="1452.867188" y="434.097656"/>
<use xlink:href="#glyph0-3" x="1460.972656" y="434.097656"/>
</g>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1.407685 0.667494 L 12.331122 0.667494 L 12.331122 7.294642 L 1.407685 7.294642 Z M 1.407685 0.667494 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1.407685 0.667494 L 12.331122 0.667494 L 12.331122 7.294642 L 1.407685 7.294642 Z M 1.407685 0.667494 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-27" x="234.925781" y="429.933594"/>
<use xlink:href="#glyph0-25" x="244.769531" y="429.933594"/>
<use xlink:href="#glyph0-3" x="248.324219" y="429.933594"/>
<use xlink:href="#glyph0-21" x="254.984375" y="429.933594"/>
<use xlink:href="#glyph0-26" x="262.015625" y="429.933594"/>
<use xlink:href="#glyph0-6" x="269.847656" y="429.933594"/>
<use xlink:href="#glyph0-10" x="275.101563" y="429.933594"/>
<use xlink:href="#glyph0-14" x="283.226563" y="429.933594"/>
<use xlink:href="#glyph0-12" x="287.289063" y="429.933594"/>
<use xlink:href="#glyph0-28" x="295.375" y="429.933594"/>
<use xlink:href="#glyph0-5" x="302.953125" y="429.933594"/>
<use xlink:href="#glyph0-24" x="310.824219" y="429.933594"/>
<use xlink:href="#glyph0-4" x="318.929688" y="429.933594"/>
<use xlink:href="#glyph0-3" x="323.949219" y="429.933594"/>
</g>
<path style="fill-rule:evenodd;fill:rgb(89.803922%,89.803922%,89.803922%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 38.207489 0.750111 L 49.130927 0.750111 L 49.130927 7.377259 L 38.207489 7.377259 Z M 38.207489 0.750111 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 38.207489 0.750111 L 49.130927 0.750111 L 49.130927 7.377259 L 38.207489 7.377259 Z M 38.207489 0.750111 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 27.878583 7.358119 L 26.01706 15.471986 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 25.940302 15.806165 L 25.819794 15.309486 L 26.01706 15.471986 L 26.265302 15.411634 Z M 25.940302 15.806165 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 25.940302 15.806165 L 25.819794 15.309486 L 26.01706 15.471986 L 26.265302 15.411634 Z M 25.940302 15.806165 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 25.245185 7.357337 L 16.988935 15.502454 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 16.744989 15.743275 L 16.909833 15.259486 L 16.988935 15.502454 L 17.230731 15.584876 Z M 16.744989 15.743275 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 16.744989 15.743275 L 16.909833 15.259486 L 16.988935 15.502454 L 17.230731 15.584876 Z M 16.744989 15.743275 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 23.14206 6.888978 L 5.371161 16.213783 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 5.06745 16.372962 L 5.366083 15.958314 L 5.371161 16.213783 L 5.578388 16.363001 Z M 5.06745 16.372962 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 5.06745 16.372962 L 5.366083 15.958314 L 5.371161 16.213783 L 5.578388 16.363001 Z M 5.06745 16.372962 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(89.803922%,89.803922%,89.803922%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 28.706708 -17.233288 L 41.797724 -17.233288 L 44.416083 -10.948913 L 41.797724 -4.664538 L 28.706708 -4.664538 L 26.088349 -10.948913 L 28.706708 -17.233288 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 28.706708 -17.233288 L 41.797724 -17.233288 L 44.416083 -10.948913 L 41.797724 -4.664538 L 28.706708 -4.664538 L 26.088349 -10.948913 L 28.706708 -17.233288 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-29" x="806.644531" y="131.453125"/>
<use xlink:href="#glyph0-26" x="814.457031" y="131.453125"/>
<use xlink:href="#glyph0-2" x="822.289063" y="131.453125"/>
<use xlink:href="#glyph0-6" x="830.394531" y="131.453125"/>
<use xlink:href="#glyph0-14" x="835.648438" y="131.453125"/>
<use xlink:href="#glyph0-30" x="839.710938" y="131.453125"/>
<use xlink:href="#glyph0-6" x="847.425781" y="131.453125"/>
<use xlink:href="#glyph0-26" x="852.679688" y="131.453125"/>
<use xlink:href="#glyph0-31" x="860.511719" y="131.453125"/>
<use xlink:href="#glyph0-6" x="868.636719" y="131.453125"/>
<use xlink:href="#glyph0-9" x="873.890625" y="131.453125"/>
<use xlink:href="#glyph0-32" x="881.722656" y="131.453125"/>
</g>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 33.965302 -4.618444 L 30.955536 0.260658 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 30.775458 0.552454 L 30.820966 0.0434701 L 30.955536 0.260658 L 31.210028 0.283509 Z M 30.775458 0.552454 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 30.775458 0.552454 L 30.820966 0.0434701 L 30.955536 0.260658 L 31.210028 0.283509 Z M 30.775458 0.552454 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,100%,100%);stroke-opacity:1;stroke-miterlimit:10;" d="M -7.268487 23.674134 L 5.679755 23.674134 L 5.679755 24.793861 L -7.268487 24.793861 Z M -7.268487 23.674134 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-12" x="0" y="837.898438"/>
<use xlink:href="#glyph0-9" x="8.085938" y="837.898438"/>
<use xlink:href="#glyph0-21" x="15.917969" y="837.898438"/>
<use xlink:href="#glyph0-8" x="22.949219" y="837.898438"/>
<use xlink:href="#glyph0-14" x="31.054688" y="837.898438"/>
<use xlink:href="#glyph0-3" x="35.117188" y="837.898438"/>
<use xlink:href="#glyph0-8" x="41.777344" y="837.898438"/>
<use xlink:href="#glyph0-9" x="49.882813" y="837.898438"/>
<use xlink:href="#glyph0-6" x="57.714844" y="837.898438"/>
<use xlink:href="#glyph0-10" x="62.96875" y="837.898438"/>
<use xlink:href="#glyph0-14" x="71.09375" y="837.898438"/>
<use xlink:href="#glyph0-25" x="75.15625" y="837.898438"/>
<use xlink:href="#glyph0-3" x="78.710938" y="837.898438"/>
<use xlink:href="#glyph0-14" x="85.371094" y="837.898438"/>
<use xlink:href="#glyph0-9" x="89.433594" y="837.898438"/>
<use xlink:href="#glyph0-14" x="97.265625" y="837.898438"/>
<use xlink:href="#glyph0-3" x="101.328125" y="837.898438"/>
<use xlink:href="#glyph0-5" x="107.988281" y="837.898438"/>
<use xlink:href="#glyph0-17" x="115.859375" y="837.898438"/>
<use xlink:href="#glyph0-9" x="123.984375" y="837.898438"/>
<use xlink:href="#glyph0-6" x="131.816406" y="837.898438"/>
<use xlink:href="#glyph0-9" x="137.070313" y="837.898438"/>
<use xlink:href="#glyph0-4" x="144.902344" y="837.898438"/>
<use xlink:href="#glyph0-5" x="149.921875" y="837.898438"/>
<use xlink:href="#glyph0-14" x="157.792969" y="837.898438"/>
<use xlink:href="#glyph0-4" x="161.855469" y="837.898438"/>
<use xlink:href="#glyph0-8" x="166.875" y="837.898438"/>
<use xlink:href="#glyph0-6" x="174.980469" y="837.898438"/>
<use xlink:href="#glyph0-5" x="180.234375" y="837.898438"/>
<use xlink:href="#glyph0-9" x="188.105469" y="837.898438"/>
<use xlink:href="#glyph0-10" x="195.9375" y="837.898438"/>
</g>
<path style="fill:none;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,100%,100%);stroke-opacity:1;stroke-miterlimit:10;" d="M 8.450849 23.64972 L 31.41745 23.64972 L 31.41745 24.769447 L 8.450849 24.769447 Z M 8.450849 23.64972 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-29" x="314.386719" y="837.410156"/>
<use xlink:href="#glyph0-26" x="322.199219" y="837.410156"/>
<use xlink:href="#glyph0-2" x="330.03125" y="837.410156"/>
<use xlink:href="#glyph0-14" x="338.136719" y="837.410156"/>
<use xlink:href="#glyph0-32" x="342.199219" y="837.410156"/>
<use xlink:href="#glyph0-9" x="354.660156" y="837.410156"/>
<use xlink:href="#glyph0-18" x="362.492188" y="837.410156"/>
<use xlink:href="#glyph0-14" x="370.070313" y="837.410156"/>
<use xlink:href="#glyph0-21" x="374.132813" y="837.410156"/>
<use xlink:href="#glyph0-6" x="381.164063" y="837.410156"/>
<use xlink:href="#glyph0-5" x="386.417969" y="837.410156"/>
<use xlink:href="#glyph0-9" x="394.289063" y="837.410156"/>
<use xlink:href="#glyph0-4" x="402.121094" y="837.410156"/>
<use xlink:href="#glyph0-5" x="407.140625" y="837.410156"/>
<use xlink:href="#glyph0-14" x="415.011719" y="837.410156"/>
<use xlink:href="#glyph0-9" x="419.074219" y="837.410156"/>
<use xlink:href="#glyph0-3" x="426.90625" y="837.410156"/>
<use xlink:href="#glyph0-14" x="433.566406" y="837.410156"/>
<use xlink:href="#glyph0-32" x="437.628906" y="837.410156"/>
<use xlink:href="#glyph0-9" x="450.089844" y="837.410156"/>
<use xlink:href="#glyph0-24" x="457.921875" y="837.410156"/>
<use xlink:href="#glyph0-18" x="466.027344" y="837.410156"/>
<use xlink:href="#glyph0-14" x="473.605469" y="837.410156"/>
<use xlink:href="#glyph0-3" x="477.667969" y="837.410156"/>
<use xlink:href="#glyph0-8" x="484.328125" y="837.410156"/>
<use xlink:href="#glyph0-9" x="492.433594" y="837.410156"/>
<use xlink:href="#glyph0-6" x="500.265625" y="837.410156"/>
<use xlink:href="#glyph0-10" x="505.519531" y="837.410156"/>
<use xlink:href="#glyph0-3" x="513.644531" y="837.410156"/>
<use xlink:href="#glyph0-14" x="520.304688" y="837.410156"/>
<use xlink:href="#glyph0-17" x="524.367188" y="837.410156"/>
<use xlink:href="#glyph0-5" x="532.492188" y="837.410156"/>
<use xlink:href="#glyph0-6" x="540.363281" y="837.410156"/>
<use xlink:href="#glyph0-14" x="545.617188" y="837.410156"/>
<use xlink:href="#glyph0-21" x="549.679688" y="837.410156"/>
<use xlink:href="#glyph0-1" x="556.710938" y="837.410156"/>
<use xlink:href="#glyph0-2" x="560.265625" y="837.410156"/>
<use xlink:href="#glyph0-3" x="568.371094" y="837.410156"/>
<use xlink:href="#glyph0-4" x="575.03125" y="837.410156"/>
<use xlink:href="#glyph0-5" x="580.050781" y="837.410156"/>
<use xlink:href="#glyph0-6" x="587.921875" y="837.410156"/>
<use xlink:href="#glyph0-14" x="593.175781" y="837.410156"/>
<use xlink:href="#glyph0-9" x="597.238281" y="837.410156"/>
<use xlink:href="#glyph0-3" x="605.070313" y="837.410156"/>
<use xlink:href="#glyph0-14" x="611.730469" y="837.410156"/>
<use xlink:href="#glyph0-18" x="615.792969" y="837.410156"/>
<use xlink:href="#glyph0-26" x="623.371094" y="837.410156"/>
<use xlink:href="#glyph0-2" x="631.203125" y="837.410156"/>
<use xlink:href="#glyph0-14" x="639.308594" y="837.410156"/>
<use xlink:href="#glyph0-24" x="643.371094" y="837.410156"/>
<use xlink:href="#glyph0-5" x="651.476563" y="837.410156"/>
<use xlink:href="#glyph0-5" x="659.347656" y="837.410156"/>
<use xlink:href="#glyph0-10" x="667.21875" y="837.410156"/>
</g>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 43.203388 21.039564 L 54.126825 21.039564 L 54.126825 27.666712 L 43.203388 27.666712 Z M 43.203388 21.039564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 43.203388 21.039564 L 54.126825 21.039564 L 54.126825 27.666712 L 43.203388 27.666712 Z M 43.203388 21.039564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 41.753388 20.164564 L 52.676825 20.164564 L 52.676825 26.791712 L 41.753388 26.791712 Z M 41.753388 20.164564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 41.753388 20.164564 L 52.676825 20.164564 L 52.676825 26.791712 L 41.753388 26.791712 Z M 41.753388 20.164564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 40.403388 18.889564 L 51.326825 18.889564 L 51.326825 25.516712 L 40.403388 25.516712 Z M 40.403388 18.889564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 40.403388 18.889564 L 51.326825 18.889564 L 51.326825 25.516712 L 40.403388 25.516712 Z M 40.403388 18.889564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 39.203388 17.539564 L 50.126825 17.539564 L 50.126825 24.166712 L 39.203388 24.166712 Z M 39.203388 17.539564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 39.203388 17.539564 L 50.126825 17.539564 L 50.126825 24.166712 L 39.203388 24.166712 Z M 39.203388 17.539564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(67.843139%,84.705883%,90.196079%);fill-opacity:1;stroke-width:0.05;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(67.843139%,84.705883%,90.196079%);stroke-opacity:1;stroke-miterlimit:10;" d="M 38.008075 16.294056 L 48.931513 16.294056 L 48.931513 22.921204 L 38.008075 22.921204 Z M 38.008075 16.294056 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.0914097;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 38.008075 16.294056 L 48.931513 16.294056 L 48.931513 22.921204 L 38.008075 22.921204 Z M 38.008075 16.294056 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 48.931513 19.607533 L 56.466864 20.073353 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 56.841083 20.096595 L 56.32663 20.31515 L 56.466864 20.073353 L 56.357489 19.816126 Z M 56.841083 20.096595 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 43.62663 7.377064 L 43.518622 15.807337 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 43.513739 16.182337 L 43.270185 15.679212 L 43.518622 15.807337 L 43.770185 15.685658 Z M 43.513739 16.182337 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 62.386981 17.127454 L 62.311591 7.989564 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 62.308466 7.614759 L 62.562567 8.112611 L 62.311591 7.989564 L 62.062567 8.116712 Z M 62.308466 7.614759 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-11" x="943.632813" y="742.460938"/>
<use xlink:href="#glyph0-12" x="952.519531" y="742.460938"/>
<use xlink:href="#glyph0-7" x="960.605469" y="742.460938"/>
<use xlink:href="#glyph0-13" x="968.730469" y="742.460938"/>
<use xlink:href="#glyph0-14" x="976.542969" y="742.460938"/>
<use xlink:href="#glyph0-11" x="980.605469" y="742.460938"/>
<use xlink:href="#glyph0-5" x="989.492188" y="742.460938"/>
<use xlink:href="#glyph0-15" x="997.363281" y="742.460938"/>
<use xlink:href="#glyph0-2" x="1005.488281" y="742.460938"/>
<use xlink:href="#glyph0-5" x="1013.59375" y="742.460938"/>
<use xlink:href="#glyph0-3" x="1021.464844" y="742.460938"/>
<use xlink:href="#glyph0-4" x="1028.125" y="742.460938"/>
<use xlink:href="#glyph0-14" x="1033.144531" y="742.460938"/>
<use xlink:href="#glyph0-16" x="1037.207031" y="742.460938"/>
<use xlink:href="#glyph0-2" x="1047.285156" y="742.460938"/>
<use xlink:href="#glyph0-5" x="1055.390625" y="742.460938"/>
<use xlink:href="#glyph0-2" x="1063.261719" y="742.460938"/>
<use xlink:href="#glyph0-5" x="1071.367188" y="742.460938"/>
<use xlink:href="#glyph0-3" x="1079.238281" y="742.460938"/>
</g>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 56.818817 1.130579 L 43.107294 -6.549303 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 42.780145 -6.732506 L 43.338544 -6.706335 L 43.107294 -6.549303 L 43.094208 -6.270006 Z M 42.780145 -6.732506 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-33" x="88.859375" y="961.296875"/>
<use xlink:href="#glyph0-9" x="95.988281" y="961.296875"/>
<use xlink:href="#glyph0-3" x="103.820313" y="961.296875"/>
<use xlink:href="#glyph0-4" x="110.480469" y="961.296875"/>
<use xlink:href="#glyph0-14" x="115.5" y="961.296875"/>
<use xlink:href="#glyph0-2" x="119.5625" y="961.296875"/>
<use xlink:href="#glyph0-17" x="127.667969" y="961.296875"/>
<use xlink:href="#glyph0-10" x="135.792969" y="961.296875"/>
<use xlink:href="#glyph0-9" x="143.917969" y="961.296875"/>
<use xlink:href="#glyph0-4" x="151.75" y="961.296875"/>
<use xlink:href="#glyph0-5" x="156.769531" y="961.296875"/>
<use xlink:href="#glyph0-10" x="164.640625" y="961.296875"/>
<use xlink:href="#glyph0-34" x="172.765625" y="961.296875"/>
<use xlink:href="#glyph0-14" x="177.082031" y="961.296875"/>
<use xlink:href="#glyph0-35" x="181.144531" y="961.296875"/>
<use xlink:href="#glyph0-36" x="189.289063" y="961.296875"/>
<use xlink:href="#glyph0-37" x="197.433594" y="961.296875"/>
<use xlink:href="#glyph0-36" x="201.496094" y="961.296875"/>
<use xlink:href="#glyph0-37" x="209.640625" y="961.296875"/>
<use xlink:href="#glyph0-38" x="213.703125" y="961.296875"/>
<use xlink:href="#glyph0-39" x="221.847656" y="961.296875"/>
<use xlink:href="#glyph0-14" x="225.910156" y="961.296875"/>
<use xlink:href="#glyph0-35" x="229.972656" y="961.296875"/>
<use xlink:href="#glyph0-40" x="238.117188" y="961.296875"/>
<use xlink:href="#glyph0-41" x="246.261719" y="961.296875"/>
<use xlink:href="#glyph0-42" x="250.871094" y="961.296875"/>
<use xlink:href="#glyph0-17" x="259.621094" y="961.296875"/>
<use xlink:href="#glyph0-6" x="267.746094" y="961.296875"/>
<use xlink:href="#glyph0-41" x="273" y="961.296875"/>
<use xlink:href="#glyph0-40" x="277.609375" y="961.296875"/>
<use xlink:href="#glyph0-36" x="285.753906" y="961.296875"/>
<use xlink:href="#glyph0-40" x="293.898438" y="961.296875"/>
<use xlink:href="#glyph0-40" x="302.042969" y="961.296875"/>
</g>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 12.330731 1.108509 L 27.305536 -6.768835 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 27.637567 -6.943249 L 27.311395 -6.489342 L 27.305536 -6.768835 L 27.078583 -6.931725 Z M 27.637567 -6.943249 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1.069013 15.997376 L 5.058661 7.732533 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 5.221552 7.394837 L 5.229364 7.953822 L 5.058661 7.732533 L 4.779169 7.736439 Z M 5.221552 7.394837 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 11.872724 15.860072 L 8.453778 7.742884 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 8.30827 7.397181 L 8.732685 7.761048 L 8.453778 7.742884 L 8.271942 7.954994 Z M 8.30827 7.397181 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 21.183856 15.950697 L 11.205927 7.607337 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 10.918231 7.366712 L 11.462177 7.495619 L 11.205927 7.607337 L 11.141474 7.879212 Z M 10.918231 7.366712 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill:none;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 34.110224 4.022572 L 37.721356 4.038197 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<path style="fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 38.096356 4.039759 L 37.595185 4.287611 L 37.721356 4.038197 L 37.597333 3.787611 Z M 38.096356 4.039759 " transform="matrix(20,0,0,20,145.369747,345.861067)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-27" x="963.949219" y="423.585938"/>
<use xlink:href="#glyph0-25" x="973.792969" y="423.585938"/>
<use xlink:href="#glyph0-3" x="977.347656" y="423.585938"/>
<use xlink:href="#glyph0-21" x="984.007813" y="423.585938"/>
<use xlink:href="#glyph0-26" x="991.039063" y="423.585938"/>
<use xlink:href="#glyph0-6" x="998.871094" y="423.585938"/>
<use xlink:href="#glyph0-10" x="1004.125" y="423.585938"/>
<use xlink:href="#glyph0-14" x="1012.25" y="423.585938"/>
<use xlink:href="#glyph0-9" x="1016.3125" y="423.585938"/>
<use xlink:href="#glyph0-24" x="1024.144531" y="423.585938"/>
<use xlink:href="#glyph0-10" x="1032.25" y="423.585938"/>
<use xlink:href="#glyph0-14" x="1040.375" y="423.585938"/>
<use xlink:href="#glyph0-19" x="1044.4375" y="423.585938"/>
<use xlink:href="#glyph0-3" x="1053.792969" y="423.585938"/>
<use xlink:href="#glyph0-5" x="1060.453125" y="423.585938"/>
<use xlink:href="#glyph0-6" x="1068.324219" y="423.585938"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-11" x="970.667969" y="439.585938"/>
<use xlink:href="#glyph0-12" x="979.554688" y="439.585938"/>
<use xlink:href="#glyph0-7" x="987.640625" y="439.585938"/>
<use xlink:href="#glyph0-13" x="995.765625" y="439.585938"/>
<use xlink:href="#glyph0-14" x="1003.578125" y="439.585938"/>
<use xlink:href="#glyph0-11" x="1007.640625" y="439.585938"/>
<use xlink:href="#glyph0-5" x="1016.527344" y="439.585938"/>
<use xlink:href="#glyph0-15" x="1024.398438" y="439.585938"/>
<use xlink:href="#glyph0-2" x="1032.523438" y="439.585938"/>
<use xlink:href="#glyph0-5" x="1040.628906" y="439.585938"/>
<use xlink:href="#glyph0-3" x="1048.5" y="439.585938"/>
<use xlink:href="#glyph0-4" x="1055.160156" y="439.585938"/>
<use xlink:href="#glyph0-3" x="1060.179688" y="439.585938"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Some files were not shown because too many files have changed in this diff Show More