athenahr/app/Helpers/Discord.php
miguel456 b89d71b371
Revert "Revert "merge 1""
This reverts commit 0c463d1f10145bf99dd63fd7128f992ab2371ffb.
2022-10-24 01:04:22 +01:00

237 lines
7.3 KiB
PHP
Executable File

<?php
/*
* Copyright © 2020 Miguel Nogueira
*
* This file is part of Raspberry Staff Manager.
*
* Raspberry Staff Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Raspberry Staff Manager is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Raspberry Staff Manager. If not, see <https://www.gnu.org/licenses/>.
*/
namespace App\Helpers;
use App\Exceptions\AccountNotLinkedException;
use App\User;
use Carbon\Carbon;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Http;
// Small wrapper for the necessary sections of the Discord API; A library is overkill here
class Discord
{
/**
* The current working guild. Default is Home guild from app config
* @var string
*/
protected string $workingGuild;
/**
* Current user.
*
* @var User The user all methods will affect.
*/
protected User $user;
public function __construct() {
if (isset($this->workingGuild)) {
$this->setWorkingGuild(config('services.discord.home_guild'));
}
}
/**
* Sets the working guild
*
* @param string $workingGuild
* @return Discord
*/
public function setWorkingGuild(string $workingGuild): Discord
{
$this->workingGuild = $workingGuild;
return $this;
}
/**
* Sets the current user, upon validation
*
* @param User $user
* @return Discord
* @throws AccountNotLinkedException
*/
public function setUser(User $user): Discord
{
if ($user->hasDiscordConnection()) {
$this->user = $user;
return $this;
}
throw new AccountNotLinkedException('Specified website user has not linked their Discord account yet.');
}
/**
* Obtains the current user's authentication info. Caches the data for the access_token's TTL, thus
* preventing unnecessary API requests.
*
* @return object Current user's authentication info (has no sensitive fields)
* @throws RequestException
*/
public function getAuthorizationInfo(): object {
if (Cache::has($this->user->discord_user_id)) {
return unserialize(Cache::get($this->user->discord_user_id));
}
else {
$authInfo = (object) Http::withToken($this->user->discord_token)
->get(config('services.discord.base_url') . '/oauth2/@me')
->throw()
->json();
Cache::put($this->user->discord_user_id, serialize($authInfo), Carbon::parse($authInfo->expires));
return $authInfo;
}
}
/**
* Checks if the user's token is close to expiring.
* Tokens should be refreshed the day before they expire.
*
* @return bool Whether the user's token needs to be refreshed
* @throws RequestException
*/
public function needsRefresh(): bool {
return Carbon::parse($this->getAuthorizationInfo()->expires)->diffInDays() == 1;
}
public function exchangeRefreshToken() {
}
/**
* Adds current working user to current working guild. Bot must be member of target guild, and account must be linked
*
* @return object|bool A GuildMember object; false if member is already in guild
* @throws RequestException Any client and server errors
* @see https://discord.com/developers/docs/resources/guild#guild-member-object
*/
public function addGuildMember(): object|bool
{
$params = [
'access_token' => $this->user->discord_token
];
$member = Http::withBody(json_encode($params), 'application/json')
->withHeaders([
'Authorization' => 'Bot ' . config('services.discord.token')
])->put(config('services.discord.base_url') . "/guilds/{$this->workingGuild}/members/{$this->user->discord_user_id}")
->throw();
if ($member->successful() && $member->status() == 204) {
return false;
} else {
return (object) $member->json();
}
}
/**
* Bans a specified user from the guild.
* May be called from the suspension service optionally by the banning user
*
* @param string $reason The reason to supply Discord with
* @return void Nothing on success
* @throws RequestException
* @throws AccountNotLinkedException
*/
public function addGuildBan(string $reason): void {
Http::withHeaders([
'Authorization' => 'Bot ' . config('services.discord.token'),
'X-Audit-Log-Reason' => $reason
])->put(config('services.discord.base_url') . "/guilds/{$this->workingGuild}/bans/{$this->user->discord_user_id}")
->throw();
throw new AccountNotLinkedException('Specified website user has not linked their Discord account yet.');
}
/**
* @param string $reason The removal reason to provide Discord with (e.g. ban expired)
* @return null|bool Null on unnan, false if user is not banned
* @throws RequestException
*/
public function removeGuildBan(string $reason): null|bool {
if ($this->getGuildBan($this->user)) {
Http::withHeaders([
'Authorization' => 'Bot ' . config('services.discord.token'),
'X-Audit-Log-Reason' => $reason
])->delete(config('services.discord.base_url') . "/guilds/{$this->workingGuild}/bans/{$this->user->discord_user_id}")
->throw();
return null;
}
return false;
}
/**
* Gets (possible) ban for current user.
*
* @return object|bool Ban object if user is banned. Null
* @throws RequestException
* @see https://discord.com/developers/docs/resources/guild#ban-object
*/
public function getGuildBan(): object|bool
{
$ban = Http::withHeaders([
'Authorization' => 'Bot ' . config('services.discord.token')
])->get(config('services.discord.base_url') . "/guilds/{$this->workingGuild}/bans/{$this->user->discord_user_id}");
if ($ban->status() == 404) {
return false;
}
return ($ban->successful()) ? (object) $ban->json() : $ban->throwIf($ban->status() !== 404);
}
/**
* Retrieves list of Role objects
* @see https://discord.com/developers/docs/topics/permissions#role-object
* @return array List of role objects
*
* @throws RequestException
*/
public function getGuildRoles(): array {
return Http::withHeaders([
'Authorization' => 'Bot ' . config('services.discord.token')
])->get(config('services.discord.base_url') . "/guilds/{$this->workingGuild}/roles")
->throw()
->json();
}
}