777 lines
40 KiB
C++
777 lines
40 KiB
C++
#include <set>
|
|
#include <iomanip>
|
|
#include <netinet/in.h>
|
|
#include <log/LogUtils.h>
|
|
#include <misc/strobf.h>
|
|
|
|
#include "../InstanceHandler.h"
|
|
#include "../manager/ConversationManager.h"
|
|
#include "../music/MusicBotManager.h"
|
|
#include "../client/music/MusicClient.h"
|
|
#include "../client/voice/VoiceClient.h"
|
|
#include "./ConnectedClient.h"
|
|
|
|
using namespace ts;
|
|
using namespace ts::server;
|
|
using namespace std;
|
|
using namespace std::chrono;
|
|
|
|
extern InstanceHandler *serverInstance;
|
|
|
|
std::deque<std::string> split(std::string str, std::string sep) {
|
|
char *cstr = const_cast<char *>(str.c_str());
|
|
char *current;
|
|
std::deque<std::string> arr;
|
|
current = strtok(cstr, sep.c_str());
|
|
while (current != NULL) {
|
|
arr.push_back(current);
|
|
current = strtok(NULL, sep.c_str());
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
#define ERR(sender, msg) \
|
|
do { \
|
|
send_message(sender, msg); \
|
|
return true; \
|
|
} while(false)
|
|
|
|
#define TLEN(index) if(arguments.size() < index) ERR(serverInstance->musicRoot(), "Invalid argument count");
|
|
#define TARG(index, type) (arguments.size() > index && arguments[index] == type)
|
|
#define TMUSIC(bot) if(!bot->current_player()) ERR(serverInstance->musicRoot(), "Im not playing a song!");
|
|
#define GBOT(var, ignore_disabled) \
|
|
auto var = this->selectedBot.lock(); \
|
|
if(!var) var = dynamic_pointer_cast<MusicClient>(target); \
|
|
if(!var) { \
|
|
send_message(serverInstance->musicRoot(), "Please select a music bot! (" + ts::config::music::command_prefix + "mbot select <id>)"); \
|
|
return true; \
|
|
} \
|
|
if(!ignore_disabled && var->properties()[property::CLIENT_DISABLED].as<bool>()) { \
|
|
send_message(serverInstance->musicRoot(), strobf("This bot has been disabled. Upgrade your TeaSpeak license to use more bots.").string()); \
|
|
return true; \
|
|
}
|
|
|
|
inline string filterUrl(string in){
|
|
size_t index = 0;
|
|
while ((index = in.find("[URL")) != std::string::npos && index < in.length()) {
|
|
auto end = in.find(']', index);
|
|
in.replace(index, end - index + 1, "", 0);
|
|
}
|
|
|
|
while ((index = in.find("[/URL")) != std::string::npos && index < in.length()) {
|
|
auto end = in.find(']', index);
|
|
in.replace(index, end - index + 1, "", 0);
|
|
}
|
|
return in;
|
|
}
|
|
|
|
inline void permissionableCommand(ConnectedClient* client, stringstream& ss, const std::string& cmd, permission::PermissionType perm, permission::PermissionType sec = permission::unknown) {
|
|
auto permA = perm == permission::unknown || permission::v2::permission_granted(1, client->calculate_permission(perm, client->getChannelId()));
|
|
auto permB = permA || (sec != permission::unknown && permission::v2::permission_granted(1, client->calculate_permission(sec, client->getChannelId())));
|
|
if(!(permA || permB)) {
|
|
ss << "[color=red]" << cmd << "[/color]" << endl;
|
|
} else {
|
|
ss << "[color=green]" << cmd << "[/color]" << endl;
|
|
}
|
|
}
|
|
|
|
inline std::string bot_volume(float vol) {
|
|
auto volume = to_string(vol * 100);
|
|
auto idx = volume.find('.');
|
|
return volume.substr(0, idx + 2);
|
|
}
|
|
|
|
//FIXME add chat command for channel commander (within bot settings)
|
|
//Return true if blocked
|
|
bool ConnectedClient::handleTextMessage(ChatMessageMode mode, std::string text, const std::shared_ptr<ConnectedClient>& target) {
|
|
if (text.length() < ts::config::music::command_prefix.length()) return false;
|
|
if (text.find(ts::config::music::command_prefix) != 0) return false;
|
|
if(!this->currentChannel)
|
|
return false;
|
|
|
|
std::string command = text.substr(ts::config::music::command_prefix.length());
|
|
auto arguments = command.find(' ') != -1 ? split(command.substr(command.find(' ') + 1), " ") : deque<string>{};
|
|
command = command.substr(0, command.find(' '));
|
|
debugMessage(this->getServerId(), "Having command \"" + command + "\".");
|
|
for (const auto &arg : arguments)
|
|
debugMessage(this->getServerId(), " Argument: '" + arg + "'");
|
|
|
|
|
|
#define PERM_CHECK_BOT(perm, reqperm, err) \
|
|
if(bot->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId() && \
|
|
!permission::v2::permission_granted(bot->calculate_permission(permission::reqperm, bot->getChannelId()), this->calculate_permission(permission::perm, bot->getChannelId()))) { \
|
|
send_message(serverInstance->musicRoot(), err); \
|
|
return true; \
|
|
}
|
|
|
|
//TODO: Correct error message print!
|
|
#define HANDLE_CMD_ERROR(_message) \
|
|
send_message(serverInstance->musicRoot(), string(_message) + ": action failed");
|
|
/*
|
|
if(result.extraProperties.count("extra_msg") > 0) \
|
|
send_message(serverInstance->musicRoot(), string(_message) + ": " + result.extraProperties["extra_msg"]); \
|
|
else if(result.extraProperties.count("failed_permid") > 0) \
|
|
send_message(serverInstance->musicRoot(), string(_message) + ". (Missing permission " + permission::resolvePermissionData((permission::PermissionType) stoull(result.extraProperties["failed_permid"]))->name + ")"); \
|
|
else \
|
|
send_message(serverInstance->musicRoot(), string(_message) + ": " + result.error.message);
|
|
*/
|
|
|
|
#define JOIN_ARGS(variable, index) \
|
|
string variable; \
|
|
{ \
|
|
stringstream ss; \
|
|
for (auto it = arguments.begin() + index; it != arguments.end(); it++) \
|
|
ss << *it << (it + 1 == arguments.end() ? "" : " "); \
|
|
variable = ss.str(); \
|
|
}
|
|
|
|
handle_text_command_fn_t function;
|
|
if(mode == ChatMessageMode::TEXTMODE_SERVER) {
|
|
function = [&](const shared_ptr<ConnectedClient>& sender, const string& message) {
|
|
this->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, sender, 0, 0, system_clock::now(), message);
|
|
};
|
|
} else if(mode == ChatMessageMode::TEXTMODE_CHANNEL) {
|
|
function = [&](const shared_ptr<ConnectedClient>& sender, const string& message) {
|
|
this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, 0, 0, system_clock::now(), message);
|
|
};
|
|
} else if(mode == ChatMessageMode::TEXTMODE_PRIVATE) {
|
|
function = [&, target](const shared_ptr<ConnectedClient>& sender, const string& message) {
|
|
this->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, target, this->getClientId(), 0, system_clock::now(), message);
|
|
};
|
|
|
|
}
|
|
return handle_text_command(mode, command, arguments, function, target);
|
|
}
|
|
|
|
bool ConnectedClient::handle_text_command(
|
|
ChatMessageMode mode,
|
|
const string &command,
|
|
const deque<string> &arguments,
|
|
const function<void(const shared_ptr<ConnectedClient> &, const string &)> &send_message,
|
|
const shared_ptr<ConnectedClient>& target) {
|
|
|
|
if (command == "mbot") {
|
|
if(!config::music::enabled) {
|
|
send_message(serverInstance->musicRoot(), "Music bots are not enabled! Enable them via the server config!");
|
|
return true;
|
|
}
|
|
if (TARG(0, "create")) {
|
|
Command cmd("");
|
|
auto result = this->handleCommandMusicBotCreate(cmd);
|
|
if(result.has_error()) {
|
|
result.release_data();
|
|
HANDLE_CMD_ERROR("Failed to create music bot");
|
|
return true;
|
|
}
|
|
send_message(serverInstance->musicRoot(), "Bot created");
|
|
return true;
|
|
} else if (TARG(0, "list")) {
|
|
bool server = false;
|
|
if(TARG(1, "server"))
|
|
server = true;
|
|
|
|
string locationStr = server ? "on this server" : "in this channel";
|
|
if(!permission::v2::permission_granted(1, this->calculate_permission(server ? permission::b_client_music_server_list : permission::b_client_music_channel_list, this->getChannelId()))) {
|
|
send_message(serverInstance->musicRoot(), "You don't have the permission to list all music bots " + locationStr);
|
|
return true;
|
|
}
|
|
auto mbots = this->server->getClientsByChannel<MusicClient>(server ? nullptr : this->currentChannel);
|
|
if (mbots.empty())
|
|
send_message(serverInstance->musicRoot(), "There are no music bots " + locationStr);
|
|
else {
|
|
send_message(serverInstance->musicRoot(), "There are " + to_string(mbots.size()) + " music bots " + locationStr + ":");
|
|
for (const auto &mbot : mbots) {
|
|
if(mbot->properties()[property::CLIENT_DISABLED].as<bool>()) {
|
|
send_message(serverInstance->musicRoot(), " - [color=red]" + to_string(mbot->getClientDatabaseId()) + " | " + mbot->getDisplayName() + " [DISABLED][/color]");
|
|
} else {
|
|
send_message(serverInstance->musicRoot(), " - [color=green]" + to_string(mbot->getClientDatabaseId()) + " | " + mbot->getDisplayName() + "[/color]");
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
} else if (TARG(0, "select")) {
|
|
TLEN(2);
|
|
if(arguments[1].find_first_not_of("0123456789") != std::string::npos) {
|
|
send_message(serverInstance->musicRoot(), "Invalid bot id");
|
|
return true;
|
|
}
|
|
auto botId = static_cast<ClientDbId>(stoll(arguments[1]));
|
|
auto bot = this->server->music_manager_->findBotById(botId);
|
|
if (!bot) ERR(serverInstance->musicRoot(), "Could not find target bot");
|
|
if(bot->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId() &&
|
|
!permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_music_channel_list, this->getChannelId())) &&
|
|
!permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_music_server_list, this->getChannelId()))) { //No perms for listing
|
|
send_message(serverInstance->musicRoot(), "You don't have the permission to select a music bot");
|
|
return true;
|
|
}
|
|
|
|
this->selectedBot = bot;
|
|
send_message(serverInstance->musicRoot(), "You successfully select the bot " + to_string(botId) + " | " + bot->getDisplayName());
|
|
return true;
|
|
} else if (TARG(0, "rename")) {
|
|
TLEN(2);
|
|
GBOT(bot, true);
|
|
|
|
stringstream ss;
|
|
for (auto it = arguments.begin() + 1; it != arguments.end(); it++)
|
|
ss << *it << (it + 1 == arguments.end() ? "" : " ");
|
|
string name = ss.str();
|
|
|
|
Command cmd("");
|
|
cmd["client_nickname"] = ss.str();
|
|
auto result = this->handleCommandClientEdit(cmd, bot);
|
|
if(result.has_error()) {
|
|
HANDLE_CMD_ERROR("Failed to rename bot");
|
|
result.release_data();
|
|
return true;
|
|
}
|
|
send_message(serverInstance->musicRoot(), "Name successfully changed!");
|
|
return true;
|
|
} else if (TARG(0, "delete")) {
|
|
GBOT(bot, true);
|
|
PERM_CHECK_BOT(i_client_music_delete_power, i_client_music_needed_delete_power, "You don't have the permission to rename this music bot");
|
|
this->server->music_manager_->deleteBot(bot);
|
|
send_message(bot, "You successfully deleted this music bot!");
|
|
return true;
|
|
} else if(TARG(0, "yt") || TARG(0, "soundcloud") || TARG(0, "sc")){
|
|
TLEN(2);
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
|
|
auto playlist = bot->playlist();
|
|
if(!playlist) {
|
|
send_message(bot, "bot hasnt a playlist!");
|
|
return true;
|
|
}
|
|
|
|
JOIN_ARGS(url, 1);
|
|
send_message(bot, "Queueing video " + url);
|
|
playlist->add_song(this->ref(), filterUrl(url), "YouTube");
|
|
return true;
|
|
} else if(TARG(0, "stream")){
|
|
TLEN(2);
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
|
|
auto playlist = bot->playlist();
|
|
if(!playlist) {
|
|
send_message(bot, "bot hasnt a playlist!");
|
|
return true;
|
|
}
|
|
|
|
JOIN_ARGS(url, 1);
|
|
send_message(bot, "Queueing video " + url);
|
|
playlist->add_song(this->ref(), filterUrl(url), "FFMpeg");
|
|
return true;
|
|
} else if(TARG(0, "player")) {
|
|
TLEN(2);
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
|
|
auto playlist = bot->playlist();
|
|
if(!playlist) {
|
|
send_message(bot, "bot hasnt a playlist!");
|
|
return true;
|
|
}
|
|
|
|
JOIN_ARGS(url, 2);
|
|
send_message(bot, "Queueing video " + url);
|
|
playlist->add_song(this->ref(), filterUrl(url), arguments[1]);
|
|
return true;
|
|
} else if(TARG(0, "forward")){
|
|
TLEN(2);
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
TMUSIC(bot);
|
|
|
|
if(arguments[1].find_first_not_of("0123456789") != std::string::npos) {
|
|
send_message(bot, "Invalid number of seconds!");
|
|
return true;
|
|
}
|
|
threads::Thread([bot, arguments]() {
|
|
bot->current_player()->forward(seconds(stoll(arguments[1])));
|
|
}).detach();
|
|
send_message(bot, "Skipped " + to_string(stoll(arguments[1])) + " seconds!");
|
|
return true;
|
|
} else if(TARG(0, "rewind")){
|
|
TLEN(2);
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
TMUSIC(bot);
|
|
|
|
if(arguments[1].find_first_not_of("0123456789") != std::string::npos) {
|
|
send_message(bot, "Invalid number of seconds!");
|
|
return true;
|
|
}
|
|
threads::Thread([bot, arguments]() {
|
|
bot->current_player()->rewind(seconds(stoll(arguments[1])));
|
|
}).detach();
|
|
send_message(bot, "Rewind " + to_string(stoll(arguments[1])) + " seconds!");
|
|
return true;
|
|
} else if(TARG(0, "stop")){
|
|
GBOT(bot, true);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
TMUSIC(bot);
|
|
bot->current_player()->stop();
|
|
send_message(bot, "Music stopped!");
|
|
return true;
|
|
} else if(TARG(0, "pause")){
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
TMUSIC(bot);
|
|
bot->current_player()->pause();
|
|
send_message(bot, "Music paused!");
|
|
return true;
|
|
} else if(TARG(0, "play")) {
|
|
if(arguments.size() >= 2) {
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
|
|
auto playlist = bot->playlist();
|
|
if(!playlist) {
|
|
send_message(bot, "bot hasnt a playlist!");
|
|
return true;
|
|
}
|
|
|
|
send_message(bot, "Queueing video " + arguments[1]);
|
|
playlist->add_song(this->ref(), filterUrl(arguments[1]), "");
|
|
return true;
|
|
} else {
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
TMUSIC(bot);
|
|
bot->current_player()->play();
|
|
send_message(bot, "Music started!");
|
|
return true;
|
|
}
|
|
} else if(TARG(0, "info")){
|
|
GBOT(bot, true);
|
|
PERM_CHECK_BOT(i_client_music_info, i_client_music_needed_info, "You don't have the permission to display the music bot information");
|
|
send_message(bot, "Music bot info:");
|
|
send_message(bot, " Bot id : " + to_string(bot->getClientDatabaseId()));
|
|
send_message(bot, " Bot name: " + bot->getDisplayName());
|
|
send_message(bot, " Bot volume: " + bot_volume(bot->volumeModifier()));
|
|
send_message(bot, " State: " + to_string(bot->player_state()));
|
|
if(bot->current_player()){
|
|
auto player = bot->current_player();
|
|
send_message(bot, " Play state: player open");
|
|
send_message(bot, " State : " + string(::music::stateNames[player->state()]));
|
|
send_message(bot, " Title : " + player->songTitle());
|
|
send_message(bot, " Description : " + player->songDescription());
|
|
send_message(bot, " Timeline : " + to_string(duration_cast<seconds>(player->currentIndex()).count()) + "/" + to_string(duration_cast<seconds>(player->length()).count()));
|
|
send_message(bot, " Buffered : " + to_string(duration_cast<seconds>(player->bufferedUntil() - player->currentIndex()).count()) + " seconds");
|
|
} else {
|
|
send_message(bot, " Play state: not playing");
|
|
}
|
|
|
|
auto bot_playlist = bot->playlist();
|
|
if(bot_playlist) {
|
|
send_message(bot, " Playlist ID : " + to_string(bot_playlist->playlist_id()));
|
|
send_message(bot, " Playlist size : " + to_string(bot_playlist->list_songs().size()));
|
|
} else {
|
|
send_message(bot, " Playlist ID : No playlist assigned");
|
|
}
|
|
return true;
|
|
} else if(TARG(0, "queue") || TARG(0, "playlist") || TARG(0, "pl")) {
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_info, i_client_music_needed_info, "You don't have the permission to display the music bot information");
|
|
|
|
auto bot_playlist = bot->playlist();
|
|
if(bot_playlist) {
|
|
send_message(bot, "Playlist ID : " + to_string(bot_playlist->playlist_id()));
|
|
send_message(bot, "Playlist entries (" + to_string(bot_playlist->list_songs().size()) + "): [color=orange]orange[/color] = currently index");
|
|
|
|
set<ClientDbId> dbids;
|
|
for(const auto& song : bot_playlist->list_songs())
|
|
dbids.insert(song->invoker);
|
|
|
|
auto dbinfo = serverInstance->databaseHelper()->queryDatabaseInfo(this->getServer(), deque<ClientDbId>(dbids.begin(), dbids.end()));
|
|
|
|
auto current_song = bot_playlist->currently_playing();
|
|
for(const auto& song : bot_playlist->list_songs()) {
|
|
string invoker = "unknown";
|
|
for(const auto& e : dbinfo) {
|
|
if(e->client_database_id == song->invoker) {
|
|
invoker = "[URL=client://0/" + e->client_unique_id + "~" + e->client_nickname + "]" + e->client_nickname + "[/URL]";
|
|
break;
|
|
}
|
|
}
|
|
|
|
string data = "\"" + song->original_url + "\" added by " + invoker;
|
|
if(song->song_id == current_song)
|
|
data = "[color=orange]" + data + "[/color]";
|
|
send_message(bot, " - " + data);
|
|
}
|
|
|
|
} else {
|
|
send_message(bot, "The bot hasn't a playlist");
|
|
}
|
|
return true;
|
|
} else if(TARG(0, "next")) {
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
bot->forwardSong();
|
|
auto song = bot->current_song();
|
|
if(song)
|
|
send_message(bot, "Replaying next song (" + song->getUrl() + ")");
|
|
else send_message(bot, "Queue is empty! Could not forward!");
|
|
return true;
|
|
} else if(TARG(0, "volume")) {
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to play something this music bot");
|
|
if(arguments.size() < 2) {
|
|
send_message(bot, "Current volume: " + bot_volume(bot->volumeModifier()));
|
|
return true;
|
|
}
|
|
if(arguments[1].find_first_not_of(".-0123456789") != std::string::npos) {
|
|
send_message(bot, "Invalid volume!");
|
|
return true;
|
|
}
|
|
auto volume = stof(arguments[1]);
|
|
if(volume < 0 || volume > 100) {
|
|
send_message(bot, "Invalid volume! Volume must be greater or equal to zero and less or equal then one!");
|
|
return true;
|
|
}
|
|
|
|
auto max_volume = this->calculate_permission(permission::i_client_music_create_modify_max_volume, 0);
|
|
if(max_volume.has_value && !permission::v2::permission_granted(volume, max_volume)) {
|
|
send_message(bot, "You don't have the permission to use higher volumes that " + to_string(max_volume.value) + "%");
|
|
return true;
|
|
}
|
|
|
|
bot->volume_modifier(volume / 100);
|
|
send_message(bot, "Volume successfully changed to " + bot_volume(bot->volumeModifier()));
|
|
return true;
|
|
} else if(TARG(0, "formats")) {
|
|
auto providers = ::music::manager::registeredTypes();
|
|
stringstream ss;
|
|
ss << "Available providers:" << endl;
|
|
for(const auto& prov : providers) {
|
|
ss << " " << prov->providerName << ":" << endl;
|
|
ss << " Description: " << prov->providerDescription << endl;
|
|
|
|
auto fmts = prov->availableFormats();
|
|
ss << " Supported formats: (" << fmts.size() << ")" << endl;
|
|
for(const auto& fmt : fmts)
|
|
ss << " - " << fmt << endl;
|
|
|
|
auto prots = prov->availableProtocols();
|
|
ss << " Supported protocols: (" << prots.size() << ")" << endl;
|
|
for(const auto& fmt : prots)
|
|
ss << " - " << fmt << endl;
|
|
}
|
|
send_message(serverInstance->musicRoot(), ss.str());
|
|
return true;
|
|
} else if(TARG(0, "settings")) {
|
|
GBOT(bot, false);
|
|
PERM_CHECK_BOT(i_client_music_play_power, i_client_music_needed_play_power, "You don't have the permission to change anything on the bot"); //TODO FIXME!
|
|
|
|
if(TARG(1, "bot")) {
|
|
|
|
const static vector<string> editable_properties = {
|
|
"client_nickname",
|
|
"client_player_volume",
|
|
"client_is_channel_commander",
|
|
"client_version",
|
|
"client_country",
|
|
"client_platform",
|
|
"client_bot_type",
|
|
"client_uptime_mode",
|
|
"client_is_priority_speaker",
|
|
"client_flag_notify_song_change"
|
|
};
|
|
|
|
if(arguments.size() < 3) {
|
|
send_message(bot, "Bot properties:");
|
|
for(const auto& property : bot->properties()->list_properties(~0)) {
|
|
if(find(editable_properties.begin(), editable_properties.end(), property.type().name) == editable_properties.end()) continue;
|
|
|
|
send_message(bot, " - " + std::string{property.type().name} + " = " + property.value() + " " + (property.default_value() == property.value() ? "(default)" : ""));
|
|
}
|
|
} else if(arguments.size() < 4) {
|
|
if(find(editable_properties.begin(), editable_properties.end(), arguments[2]) == editable_properties.end()) {
|
|
send_message(bot, "Unknown property or property is not editable.");
|
|
return true;
|
|
}
|
|
|
|
const auto &property_info = property::find<property::ClientProperties>(arguments[2]);
|
|
if(property_info.is_undefined()) {
|
|
send_message(bot, "Unknown property " + arguments[2] + ".");
|
|
return true;
|
|
}
|
|
|
|
auto prop = bot->properties()[(property::ClientProperties) property_info.property_index];
|
|
send_message(bot, "Bot property " + std::string{property_info.name} + " = " + prop.value() + " " + (property_info.default_value == prop.value() ? "(default)" : ""));
|
|
return true;
|
|
} else {
|
|
Command cmd("");
|
|
JOIN_ARGS(value, 3);
|
|
cmd[arguments[2]] = value;
|
|
auto result = this->handleCommandClientEdit(cmd, bot);
|
|
if(result.has_error()) {
|
|
HANDLE_CMD_ERROR("Failed to change bot property");
|
|
result.release_data();
|
|
return true;
|
|
}
|
|
send_message(serverInstance->musicRoot(), "Property successfully changed!");
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
} else if(TARG(1, "playlist")) {
|
|
auto playlist = bot->playlist();
|
|
if(!playlist) {
|
|
send_message(bot, "Bot hasn't a playlist!");
|
|
return true;
|
|
}
|
|
if(arguments.size() < 3) {
|
|
send_message(bot, "Playlist properties:");
|
|
for(const auto& property : playlist->properties().list_properties(property::FLAG_PLAYLIST_VARIABLE)) {
|
|
send_message(bot, " - " + std::string{property.type().name} + " = " + property.value() + " " + (property.default_value() == property.value() ? "(default)" : ""));
|
|
}
|
|
} else if(arguments.size() < 4) {
|
|
const auto &property_info = property::find<property::PlaylistProperties>(arguments[2]);
|
|
if(property_info.is_undefined()) {
|
|
send_message(bot, "Unknown property " + arguments[2] + ".");
|
|
return true;
|
|
}
|
|
|
|
auto prop = playlist->properties()[(property::PlaylistProperties) property_info.property_index];
|
|
send_message(bot, "Bot property " + std::string{property_info.name} + " = " + prop.value() + " " + (property_info.default_value == prop.value() ? "(default)" : ""));
|
|
} else {
|
|
const auto &property_info = property::find<property::PlaylistProperties>(arguments[2]);
|
|
if(property_info.is_undefined()) {
|
|
send_message(bot, "Unknown property " + arguments[2] + ".");
|
|
return true;
|
|
}
|
|
|
|
JOIN_ARGS(value, 3);
|
|
if(!property_info.validate_input(value)) {
|
|
send_message(bot, "Please enter a valid value!");
|
|
return true;
|
|
}
|
|
|
|
if((property_info.flags & property::FLAG_USER_EDITABLE) == 0) {
|
|
send_message(bot, "This property isnt changeable!");
|
|
return true;
|
|
}
|
|
|
|
playlist->properties()[(property::PlaylistProperties) property_info.property_index] = value;
|
|
send_message(bot, "Property successfully changed");
|
|
return true;
|
|
}
|
|
} else {
|
|
send_message(bot, "Please enter a valid setting mode");
|
|
}
|
|
return true;
|
|
}
|
|
} else if (command == "help") {
|
|
//send_message(serverInstance->musicRoot(), " ̶.̶̶m̶̶b̶̶o̶̶t̶̶ ̶̶f̶̶o̶̶r̶̶m̶̶a̶̶t̶̶s (Not supported yet)");
|
|
stringstream ss;
|
|
ss << "Available music bot commands: ([color=green]green[/color] = permission granted | [color=red]red[/color] = insufficient permissions)" << endl;
|
|
|
|
bool has_list_server = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_music_server_list, this->getChannelId()));
|
|
bool has_list_channel = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_music_channel_list, this->getChannelId()));
|
|
permissionableCommand(this, ss, string() + " .mbot list [<[color=" + (has_list_server ? "green" : "red") + "]server[/color]|[color=" + (has_list_channel ? "green" : "red") + "]channel[/color]>]", permission::b_client_music_channel_list, permission::b_client_music_server_list);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot select <id>", permission::b_client_music_channel_list, permission::b_client_music_server_list);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot formats", permission::unknown);
|
|
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot create", permission::b_client_music_create_temporary); // [<type>]
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot info", permission::i_client_music_info);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot rename", permission::i_client_music_rename_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot delete", permission::i_client_music_delete_power);
|
|
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot yt <video url>", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot soundcloud <video url>", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot stream <stream url> (For supported protocols|formats see '" + ts::config::music::command_prefix + "mbot formats')", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot player <provider> <data>", permission::i_client_music_play_power);
|
|
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot play", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot play <url>", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot volume <0-100>", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot forward <seconds>", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot rewind <seconds>", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot pause", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot stop", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot next", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot <playlist|pl>", permission::i_client_music_play_power);
|
|
permissionableCommand(this, ss, " " + ts::config::music::command_prefix + "mbot settings <playlist|bot> [name] [value]", permission::i_client_music_modify_power);
|
|
|
|
send_message(serverInstance->musicRoot(), ss.str());
|
|
return true;
|
|
} else if (command == "dummy") {
|
|
if(TARG(0, "timerevent")) {
|
|
send_message(_this.lock(), "Sending command dummy_timerevent");
|
|
this->sendCommand(Command("dummy_timerevent"));
|
|
return true;
|
|
} else if(TARG(0, "rd")) {
|
|
send_message(_this.lock(), "Sending command rd");
|
|
Command cmd("rd");
|
|
cmd["msg"] = "Hello world";
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
} else if(TARG(0, "connectfailed")) {
|
|
send_message(_this.lock(), "Sending command dummy_connectfailed");
|
|
Command cmd("dummy_connectfailed");
|
|
cmd["status"] = 1797;
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
send_message(_this.lock(), "Invalid dummy command!");
|
|
send_message(_this.lock(), "- timerevent");
|
|
send_message(_this.lock(), "- connectfailed");
|
|
send_message(_this.lock(), "- rd");
|
|
return true;
|
|
} else if(command == "protocol") {
|
|
if(TARG(0, "generation")) {
|
|
auto vc = dynamic_pointer_cast<VoiceClient>(_this.lock());
|
|
if(!vc) return false;
|
|
|
|
send_message(_this.lock(), "Packet generations:");
|
|
for(const auto& type : {
|
|
protocol::PacketTypeInfo::Command,
|
|
protocol::PacketTypeInfo::CommandLow,
|
|
protocol::PacketTypeInfo::Ack,
|
|
protocol::PacketTypeInfo::AckLow,
|
|
protocol::PacketTypeInfo::Voice,
|
|
protocol::PacketTypeInfo::VoiceWhisper,
|
|
protocol::PacketTypeInfo::Ping,
|
|
protocol::PacketTypeInfo::Pong}) {
|
|
|
|
//auto id = vc->getConnection()->getPacketIdManager().currentPacketId(type);
|
|
//auto gen = vc->getConnection()->getPacketIdManager().generationId(type);
|
|
//auto& genestis = vc->getConnection()->get_incoming_generation_estimators();
|
|
|
|
//send_message(_this.lock(), " OUT " + type.name() + " => generation: " + to_string(gen) + " id: " + to_string(id));
|
|
//send_message(_this.lock(), " IN " + type.name() + " => generation: " + to_string(genestis[type.type()].generation()) + " id: " + to_string(genestis[type.type()].current_packet_id()));
|
|
}
|
|
return true;
|
|
} else if(TARG(0, "rtt")) {
|
|
auto vc = dynamic_pointer_cast<VoiceClient>(_this.lock());
|
|
if(!vc) return false;
|
|
|
|
auto& ack = vc->connection->packet_encoder().acknowledge_manager();
|
|
send_message(_this.lock(), "Command retransmission values:");
|
|
send_message(_this.lock(), " RTO : " + std::to_string(ack.current_rto()));
|
|
send_message(_this.lock(), " RTTVAR: " + std::to_string(ack.current_rttvar()));
|
|
send_message(_this.lock(), " SRTT : " + std::to_string(ack.current_srtt()));
|
|
return true;
|
|
} else if(TARG(0, "sgeneration")) {
|
|
TLEN(4);
|
|
|
|
try {
|
|
auto type = stol(arguments[1]);
|
|
auto generation = stol(arguments[2]);
|
|
auto pid = stol(arguments[3]);
|
|
|
|
auto vc = dynamic_pointer_cast<VoiceClient>(_this.lock());
|
|
if(!vc) return false;
|
|
|
|
/*
|
|
auto& genestis = vc->getConnection()->get_incoming_generation_estimators();
|
|
if(type >= genestis.size()) {
|
|
send_message(_this.lock(), "Invalid type");
|
|
return true;
|
|
}
|
|
genestis[type].set_last_state(pid, generation);
|
|
*/
|
|
} catch(std::exception& ex) {
|
|
send_message(_this.lock(), "Failed to parse argument");
|
|
return true;
|
|
}
|
|
return true;
|
|
} else if(TARG(0, "ping")) {
|
|
auto vc = dynamic_pointer_cast<VoiceClient>(_this.lock());
|
|
if(!vc) return false;
|
|
|
|
auto ping_handler = vc->getConnection()->ping_handler();
|
|
send_message(this->ref(), "Ping: " + std::to_string(ping_handler.current_ping().count()));
|
|
|
|
auto last_response_ms = std::chrono::ceil<std::chrono::milliseconds>(std::chrono::system_clock::now() - ping_handler.last_ping_response()).count();
|
|
send_message(this->ref(), "Last ping response: " + std::to_string(last_response_ms));
|
|
return true;
|
|
} else if(TARG(0, "disconnect")) {
|
|
auto vc = dynamic_pointer_cast<VoiceClient>(_this.lock());
|
|
if(!vc) return false;
|
|
|
|
send_message(_this.lock(), "You'll timeout");
|
|
Command cmd("notifyclientupdated");
|
|
vc->sendCommand(cmd);
|
|
return true;
|
|
} else if(TARG(0, "resetip")) {
|
|
auto vc = dynamic_pointer_cast<VoiceClient>(_this.lock());
|
|
if(!vc) return false;
|
|
|
|
send_message(_this.lock(), "I lost your IP address. I'm so dump :)");
|
|
vc->connection->reset_remote_address();
|
|
memset(&vc->remote_address, 0, sizeof(vc->remote_address));
|
|
send_message(_this.lock(), "Hey, we got the address back");
|
|
return true;
|
|
} else if(TARG(0, "fb")) {
|
|
this->increaseFloodPoints(0xFF8F);
|
|
send_message(_this.lock(), "Done :)");
|
|
return true;
|
|
} else if(TARG(0, "binary")) {
|
|
send_message(_this.lock(), "Send binary message");
|
|
this->sendCommand(Command{"\02\03\04 \22"});
|
|
return true;
|
|
}
|
|
send_message(_this.lock(), "Invalid protocol command!");
|
|
send_message(_this.lock(), "- generation");
|
|
send_message(_this.lock(), "- disconnect");
|
|
send_message(_this.lock(), "- resetip");
|
|
send_message(_this.lock(), "- fb");
|
|
return true;
|
|
} else if (command == "sleep") {
|
|
if(arguments.empty() || arguments[0].find_first_not_of("0123456789") != string::npos) {
|
|
send_message(_this.lock(), "Invalid argument! Requires a number in ms.");
|
|
return true;
|
|
}
|
|
|
|
send_message(_this.lock(), "Sleeping for " + to_string(stoll(arguments[0])) + "!");
|
|
auto end = system_clock::now() + milliseconds(stoll(arguments[0]));
|
|
threads::self::sleep_until(end);
|
|
send_message(_this.lock(), "Done!");
|
|
return true;
|
|
} else if(command == "conversation") {
|
|
if(TARG(0, "history")) {
|
|
system_clock::time_point timestamp_begin = system_clock::now();
|
|
system_clock::time_point timestamp_end;
|
|
size_t message_count = 100;
|
|
|
|
if(arguments.size() > 1) {
|
|
timestamp_begin -= seconds(stoll(arguments[1]));
|
|
}
|
|
if(arguments.size() > 2) {
|
|
timestamp_end = system_clock::now() - seconds(stoll(arguments[2]));
|
|
}
|
|
if(arguments.size() > 3) {
|
|
message_count = stoll(arguments[3]);
|
|
}
|
|
|
|
auto time_str = [](const system_clock::time_point& tp) {
|
|
using system_clock_duration = std::chrono::system_clock::duration;
|
|
auto converted_timep = std::chrono::time_point_cast<system_clock_duration>(tp);
|
|
auto seconds_since_epoch = std::chrono::system_clock::to_time_t(tp);
|
|
|
|
ostringstream os;
|
|
os << std::put_time(std::localtime(&seconds_since_epoch), "%Y %b %d %H:%M:%S");
|
|
return os.str();
|
|
};
|
|
send_message(_this.lock(), "Looking up history from " + time_str(timestamp_end) + " to " + time_str(timestamp_begin) + ". Max messages: " + to_string(message_count));
|
|
auto conversation = this->server->conversation_manager()->get_or_create(this->currentChannel->channelId());
|
|
auto data = conversation->message_history(timestamp_begin, message_count, timestamp_end);
|
|
send_message(_this.lock(), "Entries: " + to_string(data.size()));
|
|
for(auto& entry : data) {
|
|
send_message(_this.lock(), "<" + time_str(entry->message_timestamp) + ">" + entry->sender_name + ": " + entry->message);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
send_message(serverInstance->musicRoot(), "Invalid channel command.");
|
|
send_message(serverInstance->musicRoot(), "Type " + ts::config::music::command_prefix + "help for a command overview");
|
|
|
|
return false;
|
|
} |