diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..42f7c7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build/ +out/ +.vs/ +.vscode/ +.idea/ +tmp/ +config.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index e69de29..0000000 diff --git a/.vscode/launch.json b/.vscode/launch.json index 5c7247b..07565c9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,7 +1,29 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", - "configurations": [] + "configurations": [ + { + "name": "Debug", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/BumbleCee", + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "${defaultBuildTask}" + } + ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 7695765..e238adf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,19 +1,56 @@ { "files.associations": { "iostream": "cpp", + "any": "cpp", + "chrono": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "condition_variable": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "list": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "optional": "cpp", + "string_view": "cpp", + "random": "cpp", + "*.tcc": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "istream": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "ranges": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp", "array": "cpp", "atomic": "cpp", "bit": "cpp", - "*.tcc": "cpp", - "bitset": "cpp", "cctype": "cpp", - "chrono": "cpp", + "charconv": "cpp", "clocale": "cpp", "cmath": "cpp", - "compare": "cpp", - "complex": "cpp", "concepts": "cpp", - "condition_variable": "cpp", + "coroutine": "cpp", "csignal": "cpp", "cstdarg": "cpp", "cstddef": "cpp", @@ -24,48 +61,14 @@ "ctime": "cpp", "cwchar": "cpp", "cwctype": "cpp", - "deque": "cpp", - "list": "cpp", "map": "cpp", - "set": "cpp", - "string": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "vector": "cpp", - "exception": "cpp", "algorithm": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "memory_resource": "cpp", "numeric": "cpp", - "random": "cpp", "ratio": "cpp", - "regex": "cpp", - "source_location": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", "utility": "cpp", - "fstream": "cpp", - "future": "cpp", - "initializer_list": "cpp", "iomanip": "cpp", "iosfwd": "cpp", - "istream": "cpp", "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "numbers": "cpp", - "ostream": "cpp", - "semaphore": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "stop_token": "cpp", - "streambuf": "cpp", - "thread": "cpp", - "cinttypes": "cpp", - "typeinfo": "cpp" + "numbers": "cpp" } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e30858d..1b14456 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,31 +1,35 @@ { + "version": "2.0.0", "tasks": [ - { - "type": "cppbuild", - "label": "C/C++: g++ build active file", - "command": "/usr/bin/g++", - "args": [ - "-fdiagnostics-color=always", - "-g", - "${file}", - "-o", - "${fileDirname}/${fileBasenameNoExtension}", - "-I", - "${fileDirname}/include", - "-ldpp" - ], - "options": { - "cwd": "${fileDirname}" + { + "type": "cppbuild", + "label": "make", + "command": "make", + "args": [], + "options": { + "cwd": "${fileDirname}/../build" + }, + "group": { + "kind": "build", + "isDefault": true + }, + "dependsOn": ["cmake"] }, - "problemMatcher": [ - "$gcc" - ], - "group": { - "kind": "build", - "isDefault": true + { + "type": "shell", + "label": "cmake", + "command": "cmake", + "args": [ + ".." + ], + "options": { + "cwd": "${fileDirname}/../build" + }, + "group": { + "kind": "build", + "isDefault": false + } }, - "detail": "compiler: /usr/bin/g++" - } ] - } \ No newline at end of file +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cf49b53 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required (VERSION 3.6) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(BOT_NAME "BumbleCee") + +project(${BOT_NAME}) +aux_source_directory("src" coresrc) +add_executable(${BOT_NAME} ${coresrc}) + +string(ASCII 27 Esc) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +set_target_properties(${BOT_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON +) + +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads REQUIRED) +find_package(DPP) +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) +else() + find_package(OpenSSL REQUIRED) +endif() + +target_include_directories(${BOT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${OPENSSL_INCLUDE_DIR} + /usr/local/include/opus +) + +target_link_libraries(${BOT_NAME} + dl + oggz + opus + opusfile + ogg + ${CMAKE_THREAD_LIBS_INIT} + ${OPENSSL_CRYPTO_LIBRARY} + ${OPENSSL_SSL_LIBRARY} +) + +if (DPP_FOUND) + target_link_libraries(${BOT_NAME} ${DPP_LIBRARIES}) + target_include_directories(${BOT_NAME} PUBLIC ${DPP_INCLUDE_DIR}) +else() + message(WARNING "Could not find DPP install. Building from source instead.") + option(DPP_BUILD_TEST "" OFF) + include(FetchContent) + + FetchContent_Declare( + libdpp + GIT_REPOSITORY https://github.com/brainboxdotcc/DPP.git + GIT_TAG master) + + FetchContent_GetProperties(libdpp) + if(NOT libdpp_POPULATED) + FetchContent_Populate(libdpp) + target_include_directories(${BOT_NAME} PUBLIC + ${libdpp_SOURCE_DIR}/include + ) + add_subdirectory( + ${libdpp_SOURCE_DIR} + ${libdpp_BINARY_DIR} + EXCLUDE_FROM_ALL) + endif() + + target_link_libraries(${BOT_NAME} dpp) +endif() diff --git a/audioplayback.ogg b/audioplayback.ogg new file mode 100644 index 0000000..1abef2c Binary files /dev/null and b/audioplayback.ogg differ diff --git a/cmake/FindDPP.cmake b/cmake/FindDPP.cmake new file mode 100644 index 0000000..ffa8ea0 --- /dev/null +++ b/cmake/FindDPP.cmake @@ -0,0 +1,7 @@ +find_path(DPP_INCLUDE_DIR NAMES dpp/dpp.h HINTS ${DPP_ROOT_DIR}) + +find_library(DPP_LIBRARIES NAMES dpp "libdpp.a" HINTS ${DPP_ROOT_DIR}) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(DPP DEFAULT_MSG DPP_LIBRARIES DPP_INCLUDE_DIR) diff --git a/include/Bot.cpp b/include/Bot.cpp deleted file mode 100644 index dfea483..0000000 --- a/include/Bot.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include -#include - -void IBot::test() { - std::cout << "작동함"; -} diff --git a/include/Bot.hpp b/include/Bot.hpp index ce6c8eb..7474fbf 100644 --- a/include/Bot.hpp +++ b/include/Bot.hpp @@ -1,9 +1,19 @@ #pragma once +#include "CommandType.hpp" #include +#include -class IBot{ +class IBot { private: public: - void test(); + IBot(std::string Token); + void AddCommand(ICommand &Command); + void Start(); + + std::shared_ptr bot; protected: + virtual void OnReady(const dpp::ready_t& event); + virtual void OnCommand(const dpp::slashcommand_t& event); + + std::vector CommandsArray; }; \ No newline at end of file diff --git a/include/BumbleCee.cpp b/include/BumbleCee.cpp deleted file mode 100644 index d468c4e..0000000 --- a/include/BumbleCee.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include -#include \ No newline at end of file diff --git a/include/BumbleCee.hpp b/include/BumbleCee.hpp deleted file mode 100644 index 9ff9f24..0000000 --- a/include/BumbleCee.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include - -class BumbleCee : public IBot { -public: - static BumbleCee* getInstance() { - static BumbleCee instance; - return &instance; - } -protected: -private: - BumbleCee() {} - BumbleCee(const BumbleCee& ref) {} - ~BumbleCee() {} -}; \ No newline at end of file diff --git a/include/BumbleCeepp.hpp b/include/BumbleCeepp.hpp new file mode 100644 index 0000000..96521f4 --- /dev/null +++ b/include/BumbleCeepp.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "Bot.hpp" +#include +#include + +class BumbleCeepp : public IBot { +public: + static BumbleCeepp* GetInstance(std::string Token) { + static BumbleCeepp Instance(Token); + return &Instance; + } + void enqueue(std::string); + std::string dequeue(); +protected: + std::list MusicQueue; +private: + BumbleCeepp(std::string Token); + + void OnCommand(const dpp::slashcommand_t& Event); +}; \ No newline at end of file diff --git a/include/CommandType.hpp b/include/CommandType.hpp new file mode 100644 index 0000000..b75a5c6 --- /dev/null +++ b/include/CommandType.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include + +class ICommand { +public: + virtual void operator() (const dpp::slashcommand_t& Event) = 0; + virtual void operator() (std::list& MusicQueue, const dpp::slashcommand_t& Event) = 0; + + std::vector CommandObjectVector; +}; \ No newline at end of file diff --git a/include/Commands.hpp b/include/Commands.hpp new file mode 100644 index 0000000..519dd09 --- /dev/null +++ b/include/Commands.hpp @@ -0,0 +1,4 @@ +#pragma once +#include "Commands/Play.hpp" +#include "Commands/Queue.hpp" +#include "Commands/Join.hpp" \ No newline at end of file diff --git a/include/Commands/Join.hpp b/include/Commands/Join.hpp new file mode 100644 index 0000000..2fbbf37 --- /dev/null +++ b/include/Commands/Join.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "../CommandType.hpp" + +class Join : public ICommand { +public: + Join(dpp::snowflake Id); + + void operator()(const dpp::slashcommand_t& Event) {} + void operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event); +}; \ No newline at end of file diff --git a/include/Commands/Play.hpp b/include/Commands/Play.hpp new file mode 100644 index 0000000..83dc5a4 --- /dev/null +++ b/include/Commands/Play.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "../CommandType.hpp" + +class Play : public ICommand { +public: + Play(dpp::snowflake Id); + + void operator()(const dpp::slashcommand_t& Event) {} + void operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event); +}; \ No newline at end of file diff --git a/include/Commands/Queue.hpp b/include/Commands/Queue.hpp new file mode 100644 index 0000000..f1725e1 --- /dev/null +++ b/include/Commands/Queue.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "../CommandType.hpp" + +class Queue : public ICommand { +public: + Queue(dpp::snowflake Id); + + void operator()(const dpp::slashcommand_t& Event) {} + void operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event); +}; \ No newline at end of file diff --git a/main b/main deleted file mode 100755 index eb54d94..0000000 Binary files a/main and /dev/null differ diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 419f1fa..0000000 --- a/main.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "BumbleCee.hpp" -#include - -std::string Token = "NzE1OTE2NDg2MTIzOTc4ODEy.GtT2ek.5ckzjEbg73QDS_FEZY_BS-UVpf-ZSEpR98pn80"; - -int main() { - BumbleCee* my_bot = BumbleCee::getInstance(); - IBot bot; - - bot.test(); - - return 0; -} \ No newline at end of file diff --git a/src/Bot.cpp b/src/Bot.cpp new file mode 100644 index 0000000..cac3389 --- /dev/null +++ b/src/Bot.cpp @@ -0,0 +1,40 @@ +#include "Bot.hpp" + +IBot::IBot(std::string Token) { + bot = std::make_shared(Token); + bot->on_log(dpp::utility::cout_logger()); + + bot->on_slashcommand([this](const dpp::slashcommand_t& Event) {OnCommand(Event);}); + bot->on_ready([this](const dpp::ready_t& Event) {OnReady(Event);}); +} + +void IBot::AddCommand(ICommand &Command) { + CommandsArray.push_back(&Command); +} + +void IBot::Start() { + bot->start(dpp::st_wait); +} + +void IBot::OnReady(const dpp::ready_t& Event) { + if (!dpp::run_once()) + return; + + bot->global_bulk_command_delete(); + + for (auto command : CommandsArray) { + for (auto Alias : command->CommandObjectVector) { + bot->global_command_create(Alias); + } + } +} + +void IBot::OnCommand(const dpp::slashcommand_t& Event) { + for (auto Command : CommandsArray) { + for (auto Alias : Command->CommandObjectVector) { + if (Event.command.get_command_name() == Alias.name) { + (*Command)(Event); + } + } + } +} \ No newline at end of file diff --git a/src/BumbleCeepp.cpp b/src/BumbleCeepp.cpp new file mode 100644 index 0000000..a70b201 --- /dev/null +++ b/src/BumbleCeepp.cpp @@ -0,0 +1,15 @@ +#include "BumbleCeepp.hpp" +#include + +BumbleCeepp::BumbleCeepp(std::string Token) : IBot(Token) { +} + +void BumbleCeepp::OnCommand(const dpp::slashcommand_t& Event) { + for (auto Command : CommandsArray) { + for (auto Alias : Command->CommandObjectVector) { + if (Event.command.get_command_name() == Alias.name) { + (*Command)(MusicQueue, Event); + } + } + } +} \ No newline at end of file diff --git a/src/Join.cpp b/src/Join.cpp new file mode 100644 index 0000000..15669dd --- /dev/null +++ b/src/Join.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +Join::Join(dpp::snowflake Id) { + dpp::slashcommand Command = dpp::slashcommand("join", "asdf", Id); + + dpp::slashcommand Alias = dpp::slashcommand("j", "asdf", Id); + + CommandObjectVector.push_back(Command); + CommandObjectVector.push_back(Alias); +} + +void Join::operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event) { + dpp::guild* g = dpp::find_guild(Event.command.guild_id); + + /* Attempt to connect to a voice channel, returns false if we fail to connect. */ + if (!g->connect_member_voice(Event.command.get_issuing_user().id)) { + Event.reply("You don't seem to be in a voice channel!"); + return; + } + + /* Tell the user we joined their channel. */ + Event.reply("Joined your channel!"); +} \ No newline at end of file diff --git a/src/Play.cpp b/src/Play.cpp new file mode 100644 index 0000000..214eef3 --- /dev/null +++ b/src/Play.cpp @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include + +Play::Play(dpp::snowflake Id) { + dpp::slashcommand Command = dpp::slashcommand("play", "노래 재생", Id); + + Command.add_option( + dpp::command_option(dpp::co_string, "query", "링크 또는 검색어", Id) + ); + + dpp::slashcommand Alias = dpp::slashcommand("p", "노래 재생", Id); + + Alias.add_option( + dpp::command_option(dpp::co_string, "query", "링크 또는 검색어", Id) + ); + + CommandObjectVector.push_back(Command); + CommandObjectVector.push_back(Alias); +} + +void Play::operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event) { + if (std::holds_alternative(Event.get_parameter("query"))) { + /* Get the voice channel the bot is in, in this current guild. */ + dpp::voiceconn* v = Event.from->get_voice(Event.command.guild_id); + + /* If the voice channel was invalid, or there is an issue with it, then tell the user. */ + if (!v || !v->voiceclient || !v->voiceclient->is_ready()) { + Event.reply("There was an issue with getting the voice channel. Make sure I'm in a voice channel!"); + return; + } + + ogg_sync_state oy; + ogg_stream_state os; + ogg_page og; + ogg_packet op; + OpusHead header; + char *buffer; + + FILE *fd; + + fd = fopen("../audioplayback.ogg", "rb"); + + fseek(fd, 0L, SEEK_END); + size_t sz = ftell(fd); + rewind(fd); + + ogg_sync_init(&oy); + + buffer = ogg_sync_buffer(&oy, sz); + fread(buffer, 1, sz, fd); + + ogg_sync_wrote(&oy, sz); + + 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); + + Event.reply("Finished playing the audio file!"); + } +} \ No newline at end of file diff --git a/src/Queue.cpp b/src/Queue.cpp new file mode 100644 index 0000000..87a9ae0 --- /dev/null +++ b/src/Queue.cpp @@ -0,0 +1,14 @@ +#include +#include + +Queue::Queue(dpp::snowflake Id) { + dpp::slashcommand Command = dpp::slashcommand("queue", "노래 예약 큐 확인", Id); + dpp::slashcommand Alias = dpp::slashcommand("q", "노래 예약 큐 확인", Id); + + CommandObjectVector.push_back(Command); + CommandObjectVector.push_back(Alias); +} + +void Queue::operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event) { + +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..96828cf --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +int main() { + json configdocument; + std::ifstream configfile("../config.json"); + configfile >> configdocument; + + std::unique_ptr BumbleBee(BumbleCeepp::GetInstance(configdocument["token"])); + + Play Command1(BumbleBee->bot->me.id); + Queue Command2(BumbleBee->bot->me.id); + Join Command3(BumbleBee->bot->me.id); + + BumbleBee->AddCommand(Command1); + BumbleBee->AddCommand(Command2); + BumbleBee->AddCommand(Command3); + + BumbleBee->Start(); + + return 0; +} \ No newline at end of file