Skip to content

Commit

Permalink
Rewrite Discord group sync to use v5 Nameless-Link API (#3222)
Browse files Browse the repository at this point in the history
* Rewrite Discord group sync

* fix param

* Create new endpoint for plugin to use

* wip

* wip (will work after NamelessMC/Nameless-Java-API#61)

* wip

* Undo hardcoded version

* Style

* Style

* Style

* Fix nickname sync

* UNDO UNDO UNDO
  • Loading branch information
tadhgboyle authored Jan 28, 2023
1 parent 1715723 commit 60a7cef
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 159 deletions.
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

0 comments on commit 60a7cef

Please sign in to comment.