925 lines
38 KiB
C++
Raw Normal View History

//
// Created by wolverindev on 26.01.20.
//
#include <memory>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>
#include <bitset>
#include <algorithm>
#include <openssl/sha.h>
#include "../../build.h"
#include "../ConnectedClient.h"
#include "../InternalClient.h"
#include "../../server/file/FileServer.h"
#include "../../server/VoiceServer.h"
#include "../voice/VoiceClient.h"
#include "PermissionManager.h"
#include "../../InstanceHandler.h"
#include "../../server/QueryServer.h"
#include "../file/FileClient.h"
#include "../music/MusicClient.h"
#include "../query/QueryClient.h"
#include "../../weblist/WebListManager.h"
#include "../../manager/ConversationManager.h"
#include "../../manager/PermissionNameMapper.h"
#include <experimental/filesystem>
#include <cstdint>
#include <StringVariable.h>
#include "helpers.h"
#include <Properties.h>
#include <log/LogUtils.h>
#include <misc/sassert.h>
#include <misc/base64.h>
#include <misc/hex.h>
#include <misc/digest.h>
#include <misc/rnd.h>
#include <misc/timer.h>
#include <misc/strobf.h>
#include <misc/scope_guard.h>
#include <bbcode/bbcodes.h>
namespace fs = std::experimental::filesystem;
using namespace std::chrono;
using namespace std;
using namespace ts;
using namespace ts::server;
using namespace ts::token;
command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) {
if(!config::music::enabled) return command_result{error::music_disabled};
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
if(this->server->musicManager->max_bots() != -1 && this->server->musicManager->max_bots() <= this->server->musicManager->current_bot_count()){
if(config::license->isPremium())
return command_result{error::music_limit_reached};
else
return command_result{error::music_limit_reached, strobf("You reached the server music bot limit. You could increase this limit by extend your server with a premium license.").string()};
}
auto permissions_list = this->calculate_permissions({
permission::i_client_music_limit,
permission::b_client_music_create_permanent,
permission::b_client_music_create_semi_permanent,
permission::b_client_music_create_temporary,
permission::i_channel_join_power,
permission::i_client_music_delete_power,
permission::i_client_music_create_modify_max_volume
}, this->getChannelId());
auto permissions = map<permission::PermissionType, permission::v2::PermissionFlaggedValue>(permissions_list.begin(), permissions_list.end());
auto max_bots = permissions[permission::i_client_music_limit];
if(max_bots.has_value) {
auto ownBots = this->server->musicManager->listBots(this->getClientDatabaseId());
if(!permission::v2::permission_granted(ownBots.size() + 1, max_bots))
return command_result{error::music_client_limit_reached};
}
MusicClient::Type::value create_type;
if(cmd[0].has("type")) {
create_type = cmd["type"].as<MusicClient::Type::value>();
switch(create_type) {
case MusicClient::Type::PERMANENT:
if(!permission::v2::permission_granted(1, permissions[permission::b_client_music_create_permanent]))
return command_result{permission::b_client_music_create_permanent};
break;
case MusicClient::Type::SEMI_PERMANENT:
if(!permission::v2::permission_granted(1, permissions[permission::b_client_music_create_semi_permanent]))
return command_result{permission::b_client_music_create_semi_permanent};
break;
case MusicClient::Type::TEMPORARY:
if(!permission::v2::permission_granted(1, permissions[permission::b_client_music_create_temporary]))
return command_result{permission::b_client_music_create_temporary};
break;
default:
return command_result{error::vs_critical};
}
} else {
if(permission::v2::permission_granted(1, permissions[permission::b_client_music_create_permanent]))
create_type = MusicClient::Type::PERMANENT;
else if(permission::v2::permission_granted(1, permissions[permission::b_client_music_create_semi_permanent]))
create_type = MusicClient::Type::SEMI_PERMANENT;
else if(permission::v2::permission_granted(1, permissions[permission::b_client_music_create_temporary]))
create_type = MusicClient::Type::TEMPORARY;
else
return command_result{permission::b_client_music_create_temporary};
}
shared_lock server_channel_lock(this->server->channel_tree_lock);
auto channel = cmd[0].has("cid") ? this->server->channelTree->findChannel(cmd["cid"]) : this->currentChannel;
if(!channel) {
if(cmd[0].has("cid")) return command_result{error::channel_invalid_id};
} else {
if(!this->calculate_and_get_join_state(channel))
channel = nullptr;
}
if(!channel)
channel = this->server->channelTree->getDefaultChannel();
auto bot = this->server->musicManager->createBot(this->getClientDatabaseId());
if(!bot) return command_result{error::vs_critical};
bot->set_bot_type(create_type);
if(permissions[permission::i_client_music_create_modify_max_volume].has_value) {
auto max_volume = min(100, max(0, permissions[permission::i_client_music_create_modify_max_volume].value));
if(max_volume >= 0)
bot->volume_modifier(max_volume / 100.f);
}
this->selectedBot = bot;
{
server_channel_lock.unlock();
unique_lock server_channel_w_lock(this->server->channel_tree_lock);
this->server->client_move(
bot,
channel,
nullptr,
"music bot created",
ViewReasonId::VREASON_USER_ACTION,
false,
server_channel_w_lock
);
}
bot->properties()[property::CLIENT_LAST_CHANNEL] = channel ? channel->channelId() : 0;
bot->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag;
if(permissions[permission::i_client_music_delete_power].has_value && permissions[permission::i_client_music_delete_power].value >= 0) {
bot->clientPermissions->set_permission(permission::i_client_music_needed_delete_power, {permissions[permission::i_client_music_delete_power].value,0}, permission::v2::set_value, permission::v2::do_nothing);
}
Command notify(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifymusiccreated" : "");
notify["bot_id"] = bot->getClientDatabaseId();
this->sendCommand(notify);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandMusicBotDelete(Command& cmd) {
if(!config::music::enabled) return command_result{error::music_disabled};
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
if(bot->getOwner() != this->getClientDatabaseId()) {
ACTION_REQUIRES_PERMISSION(permission::i_client_music_delete_power, bot->calculate_permission(permission::i_client_music_needed_delete_power, bot->getChannelId()), this->getChannelId());
}
this->server->musicManager->deleteBot(bot);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandMusicBotSetSubscription(ts::Command &cmd) {
if(!config::music::enabled) return command_result{error::music_disabled};
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
if(!bot && cmd["bot_id"].as<ClientDbId>() != 0) return command_result{error::music_invalid_id};
{
auto old_bot = this->subscribed_bot.lock();
if(old_bot)
old_bot->remove_subscriber(_this.lock());
}
if(bot) {
bot->add_subscriber(_this.lock());
this->subscribed_bot = bot;
}
return command_result{error::ok};
}
void apply_song(Command& command, const std::shared_ptr<ts::music::SongInfo>& element, int index = 0) {
if(!element) return;
command[index]["song_id"] = element ? element->getSongId() : 0;
command[index]["song_url"] = element ? element->getUrl() : "";
command[index]["song_invoker"] = element ? element->getInvoker() : 0;
command[index]["song_loaded"] = false;
auto entry = dynamic_pointer_cast<ts::music::PlayableSong>(element);
if(entry) {
auto data = entry->song_loaded_data();
command[index]["song_loaded"] = entry->song_loaded() && data;
if(entry->song_loaded() && data) {
command[index]["song_title"] = data->title;
command[index]["song_description"] = data->description;
command[index]["song_thumbnail"] = data->thumbnail;
command[index]["song_length"] = data->length.count();
}
}
}
command_result ConnectedClient::handleCommandMusicBotPlayerInfo(Command& cmd) {
if(!config::music::enabled) return command_result{error::music_disabled};
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymusicplayerinfo" : "");
result["bot_id"] = bot->getClientDatabaseId();
result["player_state"] =(int) bot->player_state();
if(bot->current_player()) {
result["player_buffered_index"] = bot->current_player()->bufferedUntil().count();
result["player_replay_index"] = bot->current_player()->currentIndex().count();
result["player_max_index"] = bot->current_player()->length().count();
result["player_seekable"] = bot->current_player()->seek_supported();
result["player_title"] = bot->current_player()->songTitle();
result["player_description"] = bot->current_player()->songDescription();
} else {
result["player_buffered_index"] = 0;
result["player_replay_index"] = 0;
result["player_max_index"] = 0;
result["player_seekable"] = 0;
result["player_title"] = "";
result["player_description"] = "";
}
apply_song(result, bot->current_song());
this->sendCommand(result);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandMusicBotPlayerAction(Command& cmd) {
if(!config::music::enabled) return command_result{error::music_disabled};
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
ACTION_REQUIRES_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, bot->getChannelId()), this->getChannelId());
if(cmd["action"] == 0) {
bot->stopMusic();
} else if(cmd["action"] == 1) {
bot->playMusic();
} else if(cmd["action"] == 2) {
bot->player_pause();
} else if(cmd["action"] == 3) {
bot->forwardSong();
} else if(cmd["action"] == 4) {
bot->rewindSong();
} else if(cmd["action"] == 5) {
if(!bot->current_player()) return command_result{error::music_no_player};
bot->current_player()->forward(::music::PlayerUnits(cmd["units"].as<int64_t>()));
} else if(cmd["action"] == 6) {
if(!bot->current_player()) return command_result{error::music_no_player};
bot->current_player()->rewind(::music::PlayerUnits(cmd["units"].as<int64_t>()));
} else return command_result{error::music_invalid_action};
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistList(ts::Command &cmd) {
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto self_dbid = this->getClientDatabaseId();
auto playlist_view_power = this->calculate_permission(permission::i_playlist_view_power, 0);
auto playlists = this->server->musicManager->playlists();
playlists.erase(find_if(playlists.begin(), playlists.end(), [&](const shared_ptr<music::PlayablePlaylist>& playlist) {
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] == self_dbid)
return false;
auto needed_view_power = playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power);
return !permission::v2::permission_granted(needed_view_power, playlist_view_power, false);;
}), playlists.end());
if(playlists.empty())
return command_result{error::database_empty_result};
Command notify(this->notify_response_command("notifyplaylistlist"));
size_t index = 0;
for(const auto& entry : playlists) {
notify[index]["playlist_id"] = entry->playlist_id();
auto bot = entry->current_bot();
notify[index]["playlist_bot_id"] = bot ? bot->getClientDatabaseId() : 0;
notify[index]["playlist_title"] = entry->properties()[property::PLAYLIST_TITLE].value();
notify[index]["playlist_type"] = entry->properties()[property::PLAYLIST_TYPE].value();
notify[index]["playlist_owner_dbid"] = entry->properties()[property::PLAYLIST_OWNER_DBID].value();
notify[index]["playlist_owner_name"] = entry->properties()[property::PLAYLIST_OWNER_NAME].value();
notify[index]["needed_power_modify"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power);
notify[index]["needed_power_permission_modify"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power);
notify[index]["needed_power_delete"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_delete_power);
notify[index]["needed_power_song_add"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_add_power);
notify[index]["needed_power_song_move"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_move_power);
notify[index]["needed_power_song_remove"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_remove_power);
index++;
}
this->sendCommand(notify);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistCreate(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_playlist_create, 1);
{
auto max_playlists = this->calculate_permission(permission::i_max_playlists, 0);
if(max_playlists.has_value) {
auto playlists = ref_server->musicManager->find_playlists_by_client(this->getClientDatabaseId());
if(!permission::v2::permission_granted(playlists.size(), max_playlists))
return command_result{permission::i_max_playlists};
}
}
auto playlist = ref_server->musicManager->create_playlist(this->getClientDatabaseId(), this->getDisplayName());
if(!playlist) return command_result{error::vs_critical};
playlist->properties()[property::PLAYLIST_TYPE] = music::Playlist::Type::GENERAL;
{
auto max_songs = this->calculate_permission(permission::i_max_playlist_size, 0);
if(max_songs.has_value && max_songs.value >= 0)
playlist->properties()[property::PLAYLIST_MAX_SONGS] = max_songs.value;
}
auto power = this->calculate_permission(permission::i_playlist_song_remove_power, 0);
if(power.has_value && power.value >= 0)
playlist->permissions()->setPermission(permission::i_playlist_song_needed_remove_power, power.value, nullptr);
power = this->calculate_permission(permission::i_playlist_delete_power, 0);
if(power.has_value && power.value >= 0)
playlist->permissions()->setPermission(permission::i_playlist_needed_delete_power, power.value, nullptr);
power = this->calculate_permission(permission::i_playlist_modify_power, 0);
if(power.has_value && power.value >= 0)
playlist->permissions()->setPermission(permission::i_playlist_needed_modify_power, power.value, nullptr);
power = this->calculate_permission(permission::i_playlist_permission_modify_power, 0);
if(power.has_value && power.value >= 0)
playlist->permissions()->setPermission(permission::i_playlist_needed_permission_modify_power, power.value, nullptr);
Command notify(this->notify_response_command("notifyplaylistcreated"));
notify["playlist_id"] = playlist->playlist_id();
this->sendCommand(notify);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistDelete(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_delete_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_delete_power));
string error;
if(!ref_server->musicManager->delete_playlist(playlist->playlist_id(), error)) {
logError(this->getServerId(), "Failed to delete playlist with id {}. Error: {}", playlist->playlist_id(), error);
return command_result{error::vs_critical};
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistInfo(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power));
Command notify(this->notify_response_command("notifyplaylistinfo"));
for(const auto& property : playlist->properties().list_properties(property::FLAG_PLAYLIST_VARIABLE)) {
notify[property.type().name] = property.value();
}
this->sendCommand(notify);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistEdit(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power));
deque<pair<shared_ptr<property::PropertyDescription>, string>> properties;
for(const auto& key : cmd[0].keys()) {
if(key == "playlist_id") continue;
if(key == "return_code") continue;
auto property = property::info<property::PlaylistProperties>(key);
if(*property == property::PLAYLIST_UNDEFINED) {
logError(this->getServerId(), R"([{}] Tried to edit a not existing playlist property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string());
continue;
}
if((property->flags & property::FLAG_USER_EDITABLE) == 0) {
logError(this->getServerId(), "[{}] Tried to change a playlist property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string());
continue;
}
if(!property->validate_input(cmd[key].as<string>())) {
logError(this->getServerId(), "[{}] Tried to change a playlist property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string());
continue;
}
if(*property == property::PLAYLIST_CURRENT_SONG_ID) {
auto song_id = cmd[key].as<SongId>();
auto song = song_id > 0 ? playlist->find_song(song_id) : nullptr;
if(song_id != 0 && !song)
return command_result{error::playlist_invalid_song_id};
} else if(*property == property::PLAYLIST_MAX_SONGS) {
auto value = cmd[key].as<int32_t>();
auto max_value = this->calculate_permission(permission::i_max_playlist_size, this->getChannelId());
if(max_value.has_value && !permission::v2::permission_granted(value, max_value))
return command_result{permission::i_max_playlist_size};
}
properties.emplace_back(property, key);
}
for(const auto& property : properties) {
if(*property.first == property::PLAYLIST_CURRENT_SONG_ID) {
playlist->set_current_song(cmd[property.second]);
continue;
}
playlist->properties()[property.first] = cmd[property.second].string();
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistPermList(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_playlist_permission_list, 1);
auto permissions = playlist->permissions()->listPermissions(PERM_FLAG_PUBLIC);
if(permissions.empty())
return command_result{error::vs_critical};
Command result(this->notify_response_command("notifyplaylistpermlist"));
int index = 0;
result["playlist_id"] = playlist->playlist_id();
for (const auto &elm : permissions) {
if(elm->hasValue()) {
result[index]["permid"] = elm->type->type;
result[index]["permvalue"] = elm->value;
result[index]["permnegated"] = elm->flag_negate;
result[index]["permskip"] = elm->flag_skip;
index++;
}
if(elm->hasGrant()) {
result[index]["permid"] = (uint16_t) (elm->type->type | PERM_ID_GRANT);
result[index]["permvalue"] = elm->granted;
result[index]["permnegated"] = 0;
result[index]["permskip"] = 0;
index++;
}
}
this->sendCommand(result);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistAddPerm(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power));
auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, true);
if(!max_value.has_value) return command_result{permission::i_permission_modify_power};
auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0));
bool conOnError = cmd[0].has("continueonerror");
for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd);
auto val = cmd[index]["permvalue"].as<permission::PermissionValue>();
if(permission_require_granted_value(permType) && !permission::v2::permission_granted(val, max_value)) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
if(!ignore_granted_values && !permission::v2::permission_granted(val, this->calculate_permission(permType, 0, true))) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
if (grant) {
playlist->permissions()->setPermissionGranted(permType, cmd[index]["permvalue"], nullptr);
} else {
playlist->permissions()->setPermission(permType, cmd[index]["permvalue"], nullptr, cmd[index]["permskip"], cmd[index]["permnegated"]);
}
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistDelPerm(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power));
auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0));
bool conOnError = cmd[0].has("continueonerror");
for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd);
if(!ignore_granted_values && !permission::v2::permission_granted(0, this->calculate_permission(permType, 0, true))) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
if (grant) {
playlist->permissions()->setPermissionGranted(permType, permNotGranted, nullptr);
} else {
playlist->permissions()->deletePermission(permType, nullptr);
}
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistSongList(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power));
auto songs = playlist->list_songs();
if(songs.empty())
return command_result{error::database_empty_result};
Command notify(this->notify_response_command("notifyplaylistsonglist"));
notify["playlist_id"] = playlist->playlist_id();
size_t index = 0;
for(const auto& song : songs) {
notify[index]["song_id"] = song->id;
notify[index]["song_invoker"] = song->invoker;
notify[index]["song_previous_song_id"] = song->previous_song_id;
notify[index]["song_url"] = song->url;
notify[index]["song_url_loader"] = song->url_loader;
notify[index]["song_loaded"] = song->loaded;
notify[index]["song_metadata"] = song->metadata;
index++;
}
this->sendCommand(notify);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistSongAdd(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_song_add_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_add_power));
if(!cmd[0].has("invoker"))
cmd["invoker"] = "";
if(!cmd[0].has("previous")) {
auto songs = playlist->list_songs();
if(songs.empty())
cmd["previous"] = "0";
else
cmd["previous"] = songs.back()->id;
}
auto song = playlist->add_song(_this.lock(), cmd["url"], cmd["invoker"], cmd["previous"]);
if(!song) return command_result{error::vs_critical};
Command notify(this->notify_response_command("notifyplaylistsongadd"));
notify["song_id"] = song->id;
this->sendCommand(notify);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistSongReorder(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_song_move_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_move_power));
SongId song_id = cmd["song_id"];
SongId previous_id = cmd["song_previous_song_id"];
auto song = playlist->find_song(song_id);
if(!song) return command_result{error::playlist_invalid_song_id};
if(!playlist->reorder_song(song_id, previous_id))
return command_result{error::vs_critical};
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistSongRemove(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_song_remove_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_remove_power));
SongId song_id = cmd["song_id"];
auto song = playlist->find_song(song_id);
if(!song) return command_result{error::playlist_invalid_song_id};
if(!playlist->delete_song(song_id))
return command_result{error::vs_critical};
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandMusicBotQueueList(Command& cmd) {
return command_result{error::not_implemented}; //FIXME
/*
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
PERM_CHECK_CHANNELR(permission::i_client_music_info, bot->permissionValue(permission::i_client_music_needed_info, bot->currentChannel), this->currentChannel, true);
bool bulked = cmd.hasParm("bulk") || cmd.hasParm("balk") || cmd.hasParm("pipe") || cmd.hasParm("bar") || cmd.hasParm("paypal");
int command_index = 0;
Command notify(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : "");
{
auto history = bot->queue()->history();
for(int index = history.size(); index > 0; index--) {
if(!bulked)
notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : "");
apply_song(notify, history[index - 1], command_index);
notify[command_index]["queue_index"] = -index;
if(!bulked)
this->sendCommand(notify);
else
command_index++;
}
}
{
if(!bulked)
notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : "");
auto song = bot->queue()->currentSong();
apply_song(notify, song, command_index);
notify[command_index]["queue_index"] = 0;
if(!bulked)
this->sendCommand(notify);
else if(song)
command_index++;
}
{
auto queue = bot->queue()->queueEntries();
for(int index = 0; index < queue.size(); index++) {
if(!bulked)
notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : "");
apply_song(notify, queue[index], command_index);
notify[command_index]["queue_index"] = index + 1;
if(!bulked)
this->sendCommand(notify);
else
command_index++;
}
}
debugMessage(this->getServerId(),"Send: {}",notify.build());
if(bulked) {
if(command_index > 0) {
this->sendCommand(notify);
} else return { ErrorType::DBEmpty };
}
if(this->getExternalType() == CLIENT_VOICE) {
Command notify("notifymusicqueuefinish");
notify["bot_id"] = bot->getClientDatabaseId();
this->sendCommand(notify);
}
return command_result{error::ok};
*/
}
command_result ConnectedClient::handleCommandMusicBotQueueAdd(Command& cmd) {
return command_result{error::not_implemented}; //FIXME
/*
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true);
MusicClient::loader_t loader;
auto& type = cmd[0]["type"];
if((type.castable<int>() && type.as<int>() == 0) || type.as<string>() == "yt") {
loader = bot->ytLoader(this->getServer());
} else if((type.castable<int>() && type.as<int>() == 1) || type.as<string>() == "ffmpeg") {
loader = bot->ffmpegLoader(this->getServer());
} else if((type.castable<int>() && type.as<int>() == 2) || type.as<string>() == "channel") {
loader = bot->channelLoader(this->getServer());
} else if((type.castable<int>() && type.as<int>() == -1) || type.as<string>() == "any") {
loader = bot->providerLoader(this->getServer(), "");
}
if(!loader) return command_result{error::music_invalid_action};
auto entry = bot->queue()->insertEntry(cmd["url"], _this.lock(), loader);
if(!entry) return command_result{error::vs_critical};
this->server->forEachClient([&](shared_ptr<ConnectedClient> client) {
client->notifyMusicQueueAdd(bot, entry, bot->queue()->queueEntries().size() - 1, _this.lock());
});
return command_result{error::ok};
*/
}
command_result ConnectedClient::handleCommandMusicBotQueueRemove(Command& cmd) {
return command_result{error::not_implemented}; //FIXME
/*
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true);
std::deque<std::shared_ptr<music::SongInfo>> songs;
for(int index = 0; index < cmd.bulkCount(); index++) {
auto entry = bot->queue()->find_queue(cmd["song_id"]);
if(!entry) {
if(cmd.hasParm("skip_error")) continue;
return command_result{error::database_empty_result};
}
songs.push_back(move(entry));
}
for(const auto& entry : songs)
bot->queue()->deleteEntry(dynamic_pointer_cast<music::PlayableSong>(entry));
this->server->forEachClient([&](shared_ptr<ConnectedClient> client) {
client->notifyMusicQueueRemove(bot, songs, _this.lock());
});
return command_result{error::ok};
*/
}
command_result ConnectedClient::handleCommandMusicBotQueueReorder(Command& cmd) {
return command_result{error::not_implemented}; //FIXME
/*
CMD_REQ_SERVER;
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true);
auto entry = bot->queue()->find_queue(cmd["song_id"]);
if(!entry) return command_result{error::database_empty_result};
auto order = bot->queue()->changeOrder(entry, cmd["index"]);
if(order < 0) return command_result{error::vs_critical};
this->server->forEachClient([&](shared_ptr<ConnectedClient> client) {
client->notifyMusicQueueOrderChange(bot, entry, order, _this.lock());
});
return command_result{error::ok};
*/
}
command_result ConnectedClient::handleCommandMusicBotPlaylistAssign(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = ref_server->musicManager->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
if(bot->getOwner() != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, 0));
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id};
if(ref_server->musicManager->find_bot_by_playlist(playlist))
return command_result{error::playlist_already_in_use};
if(playlist && playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power));
if(!ref_server->musicManager->assign_playlist(bot, playlist))
return command_result{error::vs_critical};
return command_result{error::ok};
}