// // Created by wolverindev on 26.01.20. // #include #include #include #include #include #include #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 #include #include #include "helpers.h" #include #include #include #include #include #include #include #include #include #include #include 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(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(); 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() != 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& 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(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())); } 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())); } 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& 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, string>> properties; for(const auto& key : cmd[0].keys()) { if(key == "playlist_id") continue; if(key == "return_code") continue; auto property = property::info(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())) { 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(); 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(); 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(); 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() && type.as() == 0) || type.as() == "yt") { loader = bot->ytLoader(this->getServer()); } else if((type.castable() && type.as() == 1) || type.as() == "ffmpeg") { loader = bot->ffmpegLoader(this->getServer()); } else if((type.castable() && type.as() == 2) || type.as() == "channel") { loader = bot->channelLoader(this->getServer()); } else if((type.castable() && type.as() == -1) || type.as() == "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 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> 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(entry)); this->server->forEachClient([&](shared_ptr 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 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}; }