Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite Discord group sync to use v5 Nameless-Link API #3222

Merged
merged 12 commits into from
Jan 28, 2023
2 changes: 1 addition & 1 deletion core/classes/Core/Log.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ class Log extends Instanceable {
// TODO
],
'discord' => [
'bot_request_failed' => 'discord_bot_request_failed',
'role_set' => 'discord_role_set',
'upon_validation_error' => 'upon_validation_error'
],
'mc_group_sync' => [
'role_set' => 'mc_group_sync_set'
Expand Down
30 changes: 30 additions & 0 deletions core/classes/Group_Sync/BatchableGroupSyncInjector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/**
* Represents a GroupSyncInjector which can add multiple groups to a {@see User} at once
*
* @package NamelessMC\Group_Sync
* @author Aberdeener
* @version 2.0.3
* @license MIT
*/
interface BatchableGroupSyncInjector {

/**
* Add multiple groups to a user at once.
*
* @param User $user The user to add groups to
* @param array $group_ids Array of native group IDs to remove
* @return false|array Array of group IDs and the status of the operation for each, or false if error
*/
public function batchAddGroups(User $user, array $group_ids);

/**
* Remove multiple groups from a user at once.
*
* @param User $user The user to remove groups from
* @param array $group_ids Array of native group IDs to remove
* @return false|array Array of group IDs and the status of the operation for each, or false if error
*/
public function batchRemoveGroups(User $user, array $group_ids);

}
55 changes: 50 additions & 5 deletions core/classes/Group_Sync/GroupSyncManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ public function broadcastChange(User $user, string $sending_injector_class, arra
}
}

$batched_changes = [];
foreach ($rules as $rule) {

foreach ($this->getEnabledInjectors() as $injector) {
Expand All @@ -181,6 +182,16 @@ public function broadcastChange(User $user, string $sending_injector_class, arra
continue;
}

$injector_class = get_class($injector);

$batchable = $injector instanceof BatchableGroupSyncInjector;
if ($batchable && !array_key_exists($injector_class, $batched_changes)) {
$batched_changes[$injector_class] = [
'add' => [],
'remove' => [],
];
}

$injector_column = $injector->getColumnName();
$injector_group_id = $rule->{$injector_column};
$sending_group_id = $rule->{$sending_injector->getColumnName()};
Expand All @@ -201,11 +212,12 @@ public function broadcastChange(User $user, string $sending_injector_class, arra
}

if (in_array($sending_group_id, $group_ids)) {
// TODO: add bot status of "nochange" @ https://canary.discord.com/channels/246705793066467328/434751012428054530/995503906350174290
// Attempt to add group if this group id was sent in the broadcastChange() method
// and if they don't have the namelessmc equivilant of it
if ($injector->addGroup($user, $injector_group_id)) {
$modified[$injector_column][] = $injector_group_id;
$modified[$injector_column][] = $injector_group_id;
if ($batchable) {
$batched_changes[$injector_class]['add'][] = $injector_group_id;
} elseif ($injector->addGroup($user, $injector_group_id)) {
$logs['added'][] = "{$injector_column} -> {$injector_group_id}";
}
} else {
Expand All @@ -219,14 +231,47 @@ public function broadcastChange(User $user, string $sending_injector_class, arra

// Attempt to remove this group if it doesn't have multiple rules, or if the group ids
// list sent to broadcastChange() was empty - NOT both
if ($injector->removeGroup($user, $injector_group_id)) {
$modified[$injector_column][] = $injector_group_id;
$modified[$injector_column][] = $injector_group_id;
if ($batchable) {
$batched_changes[$injector_class]['remove'][] = $injector_group_id;
} elseif ($injector->removeGroup($user, $injector_group_id)) {
$logs['removed'][] = "{$injector_column} -> {$injector_group_id}";
}
}
}
}

foreach ($batched_changes as $injector_class => $data) {
/** @var GroupSyncInjector&BatchableGroupSyncInjector $injector */
$injector = $this->getInjectorByClass($injector_class);
$injector_column = $injector->getColumnName();

$add = $data['add'];
$remove = $data['remove'];

if (count($add)) {
$result = $injector->batchAddGroups($user, $add);
if (is_array($result)) {
foreach ($result as $res) {
if ($res['status'] === 'added') {
$logs['added'][] = "{$injector_column} -> {$res['group_id']}";
}
}
}
}

if (count($remove)) {
$result = $injector->batchRemoveGroups($user, $remove);
if (is_array($result)) {
foreach ($result as $res) {
if ($res['status'] === 'removed') {
$logs['removed'][] = "{$injector_column} -> {$res['group_id']}";
}
}
}
}
}

return $logs;
}

Expand Down
2 changes: 2 additions & 0 deletions custom/languages/en_UK.json
Original file line number Diff line number Diff line change
Expand Up @@ -702,8 +702,10 @@
"api/finish_registration_email": "Please check your emails to complete registration.",
"api/finish_registration_link": "Please click on the following link to complete registration:",
"api/group_updated": "Group updated successfully",
"api/groups_updates_successfully": "Groups updated successfully",
"api/integration_identifier_already_linked": "{{integration}} identifier is already linked to another user.",
"api/integration_username_already_linked": "{{integration}} username is already linked to another user.",
"api/invalid_group_sync_server_id": "Invalid group sync server ID",
"api/report_created": "Report created successfully",
"api/server_info_updated": "Server info updated successfully",
"api/unable_to_update_server_info": "Unable to update server info",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function getColumnType(): string {
}

public function shouldEnable(): bool {
return Util::getSetting('group_sync_mc_server') != 0 && count($this->getSelectionOptions()) > 0;
return count($this->getSelectionOptions()) > 0;
}

public function getSelectionOptions(): array {
Expand Down
1 change: 1 addition & 0 deletions modules/Core/classes/Misc/CoreApiErrors.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class CoreApiErrors {
public const ERROR_USER_ALREADY_ACTIVE = 'core:user_already_active';

public const ERROR_UNABLE_TO_UPDATE_USERNAME = 'core:unable_to_update_username';
public const ERROR_UNABLE_TO_UPDATE_GROUPS = 'core:unable_to_update_groups';

public const ERROR_INTEGRATION_IDENTIFIER_ERRORS = 'core:integration_identifier_errors';
public const ERROR_INTEGRATION_USERNAME_ERRORS = 'core:integration_username_errors';
Expand Down
46 changes: 4 additions & 42 deletions modules/Core/includes/endpoints/ServerInfoEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public function execute(Nameless2API $api): void {
$api->throwError(Nameless2API::ERROR_INVALID_POST_CONTENTS, 'players');
}

$serverId = $_POST['server-id'];
$server_id = $_POST['server-id'];
// Ensure server exists
$server_query = $api->getDb()->get('mc_servers', ['id', $serverId]);
$server_query = $api->getDb()->get('mc_servers', ['id', $server_id]);

if (!$server_query->count() || $server_query->first()->bedrock) {
$api->throwError(CoreApiErrors::ERROR_INVALID_SERVER_ID, $serverId);
$api->throwError(CoreApiErrors::ERROR_INVALID_SERVER_ID, $server_id);
}

if (isset($_POST['verify_command'])) {
Expand Down Expand Up @@ -64,9 +64,6 @@ public function execute(Nameless2API $api): void {
$api->throwError(CoreApiErrors::ERROR_UNABLE_TO_UPDATE_SERVER_INFO, $e->getMessage(), 500);
}

$group_sync_log = [];
$should_group_sync = $serverId == Util::getSetting('group_sync_mc_server');

try {
$integration = Integrations::getInstance()->getIntegration('Minecraft');

Expand All @@ -75,13 +72,6 @@ public function execute(Nameless2API $api): void {
if ($integrationUser->exists()) {
$this->updateUsername($integrationUser, $player);

if ($should_group_sync) {
$log = $this->updateGroups($integrationUser, $player);
if (count($log)) {
$group_sync_log[] = $log;
}
}

if (isset($player['placeholders']) && count($player['placeholders'])) {
$this->updatePlaceholders($integrationUser->getUser(), $player);
}
Expand All @@ -91,7 +81,7 @@ public function execute(Nameless2API $api): void {
$api->throwError(CoreApiErrors::ERROR_UNABLE_TO_UPDATE_SERVER_INFO, $e->getMessage(), 500);
}

$api->returnArray(array_merge(['message' => $api->getLanguage()->get('api', 'server_info_updated')], ['log' => $group_sync_log]));
$api->returnArray(array_merge(['message' => $api->getLanguage()->get('api', 'server_info_updated')]));
}

private function updateUsername(IntegrationUser $integrationUser, array $player): void {
Expand Down Expand Up @@ -121,37 +111,9 @@ private function updateUsername(IntegrationUser $integrationUser, array $player)
}
}

private function updateGroups(IntegrationUser $integrationUser, array $player): array {
if (!$integrationUser->isVerified()) {
return [];
}

$user = $integrationUser->getUser();
if (!$user->exists()) {
return [];
}

if (!$user->isValidated()) {
return [];
}

$log = GroupSyncManager::getInstance()->broadcastChange(
$user,
MinecraftGroupSyncInjector::class,
$player['groups'] ?? []
);

if (count($log)) {
Log::getInstance()->log(Log::Action('mc_group_sync/role_set'), json_encode($log), $user->data()->id);
}

return $log;
}

private function updatePlaceholders(User $user, $player): void {
if ($user->exists()) {
$user->savePlaceholders($_POST['server-id'], $player['placeholders']);
}
}

}
67 changes: 67 additions & 0 deletions modules/Core/includes/endpoints/UpdateGroupsEndpoint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

class UpdateGroupsEndpoint extends KeyAuthEndpoint {

public function __construct() {
$this->_route = 'minecraft/update-groups';
$this->_module = 'Core';
$this->_description = 'Update a users groups based on their groups from the Minecraft server';
$this->_method = 'POST';
}

public function execute(Nameless2API $api): void {
$api->validateParams($_POST, ['server_id', 'player_groups']);

$server_id = $_POST['server_id'];
$group_sync_log = [];

if ($server_id == Util::getSetting('group_sync_mc_server')) {
try {
$integration = Integrations::getInstance()->getIntegration('Minecraft');

foreach ($_POST['player_groups'] as $uuid => $groups) {
$integrationUser = new IntegrationUser($integration, str_replace('-', '', $uuid), 'identifier');
if ($integrationUser->exists()) {
$log = $this->updateGroups($integrationUser, $groups['groups']);
if (count($log)) {
$group_sync_log[] = $log;
}
}
}
} catch (Exception $e) {
$api->throwError(CoreApiErrors::ERROR_UNABLE_TO_UPDATE_GROUPS, $e->getMessage(), 500);
}

$api->returnArray(array_merge(['message' => $api->getLanguage()->get('api', 'groups_updates_successfully')], ['log' => $group_sync_log]));
}

$api->throwError(CoreApiErrors::ERROR_INVALID_SERVER_ID, $server_id);
}

private function updateGroups(IntegrationUser $integrationUser, array $groups): array {
if (!$integrationUser->isVerified()) {
return [];
}

$user = $integrationUser->getUser();
if (!$user->exists()) {
return [];
}

if (!$user->isValidated()) {
return [];
}

$log = GroupSyncManager::getInstance()->broadcastChange(
$user,
MinecraftGroupSyncInjector::class,
$groups,
);

if (count($log)) {
Log::getInstance()->log(Log::Action('mc_group_sync/role_set'), json_encode($log), $user->data()->id);
}

return $log;
}
}
Loading