diff --git a/README.md b/README.md index d372951..17f6513 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,242 @@ -# TopPHP -A Top.gg API wrapper written in PHP 8. +# Top.gg PHP SDK -## Documentation -See [here](https://docs.top.gg/libraries/php/) for the official information. +The community-maintained PHP library for Top.gg. + +## Chapters + +- [Installation](#installation) +- [Setting up](#setting-up) +- [Usage](#usage) + - [API v1](#api-v1-1) + - [Getting your project's vote information of a user](#getting-your-projects-vote-information-of-a-user) + - [Posting your bot's application commands list](#posting-your-bots-application-commands-list) + - [API v0](#api-v0-1) + - [Getting a bot](#getting-a-bot) + - [Getting several bots](#getting-several-bots) + - [Getting your project's voters](#getting-your-projects-voters) + - [Check if a user has voted for your project](#check-if-a-user-has-voted-for-your-project) + - [Getting your bot's statistics](#getting-your-bots-statistics) + - [Posting your bot's statistics](#posting-your-bots-statistics) + - [Automatically posting your bot's statistics every few minutes](#automatically-posting-your-bots-statistics-every-few-minutes) + - [Checking if the weekend vote multiplier is active](#checking-if-the-weekend-vote-multiplier-is-active) + - [Generating widget URLs](#generating-widget-urls) + - [Webhooks](#webhooks) + - [Being notified whenever someone voted for your project](#being-notified-whenever-someone-voted-for-your-project) ## Installation -TopPHP uses composer to download. In order to install the library, use the following line: -`composer require top-gg/php-sdk` +```sh +$ composer require top-gg/php-sdk +``` + +## Setting up + +### API v1 + +> **NOTE**: API v1 also includes API v0. + +```php +include_once __DIR__ . "/vendor/autoload.php"; + +use DBL\V1DBL; + +$client = new V1DBL([ + "token" => "Top.gg API token" +]); +``` + +### API v0 + +```php +include_once __DIR__ . "/vendor/autoload.php"; + +use DBL\DBL; + +$client = new DBL([ + "token" => "Top.gg API token" +]); +``` + +## Usage + +### API v1 + +#### Getting your project's vote information of a user + +```php +$vote = $client->get_vote(661200758510977084); +``` + +#### Posting your bot's application commands list + +```php +$client->post_commands([ + [ + "options" => [], + "name" => "test", + "name_localizations" => null, + "description" => "command description", + "description_localizations" => null, + "contexts" => [], + "default_permission" => null, + "default_member_permissions" => null, + "dm_permission" => false, + "integration_types" => [], + "nsfw" => false + ] +]); +``` + +### API v0 + +#### Getting a bot + +```php +$bot = $client->get_bot(1026525568344264724); +``` + +#### Getting several bots + +```php +// Limit Offset Sort by +$bots = $client->get_bots(50, 0, "monthlyPoints"); +``` + +#### Getting your project's voters + +##### First page + +```php +$voters = $client->get_votes(); +``` + +##### Subsequent pages + +```php +// Bot ID (unused) Page number +$voters = $client->get_votes(0, 2); +``` + +#### Check if a user has voted for your project + +```php +// Bot ID (unused) User ID +$has_voted = $client->get_user_vote(0, 661200758510977084); +``` + +#### Getting your bot's statistics + +```php +$stats = $client->get_stats(); +``` + +#### Posting your bot's statistics + +```php +$client->post_stats(0, [ + "server_count" => $bot->get_server_count() +]); +``` + +#### Automatically posting your bot's statistics every few minutes + +> **NOTE**: Considering PHP's shortcomings, this is only possible via a URL. + +In your original client declaration: + +```php +$client = new DBL([ + "token" => "Insert your Top.gg API token here.", + "auto_stats" => [ + "url": "Your URL", + "callback": => function () use ($bot) { + return [ + "server_count" => $bot->get_server_count() + ]; + } + ] +]); +``` + +#### Checking if the weekend vote multiplier is active + +```php +$is_weekend = $client->is_weekend(); +``` + +#### Generating widget URLs + +##### Large + +```php +use DBL\Widget; +use DBL\WidgetType; + +$widget_url = Widget::large(WidgetType::DiscordBot, 574652751745777665); +``` + +##### Votes + +```php +use DBL\Widget; +use DBL\WidgetType; + +$widget_url = Widget::votes(WidgetType::DiscordBot, 574652751745777665); +``` + +##### Owner + +```php +use DBL\Widget; +use DBL\WidgetType; + +$widget_url = Widget::owner(WidgetType::DiscordBot, 574652751745777665); +``` + +##### Social + +```php +use DBL\Widget; +use DBL\WidgetType; + +$widget_url = Widget::social(WidgetType::DiscordBot, 574652751745777665); +``` + +### Webhooks + +#### Being notified whenever someone voted for your project + +**With Laravel:** + +In your `config/logging.php`: + +```php +"channels" => [ + "stdout" => [ + "driver" => "single", + "path" => "php://stdout", + "level" => "debug" + ] +] +``` + +In your `routes/api.php`: + +```php +use DBL\Webhook; -## Features +use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Log; -* Working GET and POST requests. -* Enumerable constants for HTTP search lookups. -* Flexible namespaces and interfaces for easily editing. -* Up-to-standard package following the latest PSR. -* Automatic bot statistics POST requests. (Shards, guilds) +class MyVoteListener extends Webhook { + public function getAuthorization() { + return getenv("MY_TOPGG_WEBHOOK_SECRET"); + } -## Coming Soon + public function callback(array $vote) { + Log::channel("stdout")->info("A user with the ID of " . $vote["user"] . " has voted us on Top.gg!"); + } +} -* Webhooks -* Forced library global rate-limiting. +Route::post('/votes', [MyVoteListener::class, "main"]); +``` diff --git a/composer.json b/composer.json index 9a9ac10..930aed9 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,9 @@ "description": "The official Top.gg PHP API wrapper.", "license": "MIT", "require": { - "php": "^7.3|^8.0" + "php": "^7.3|^8.0", + "illuminate/routing": "^12.19", + "illuminate/http": "^12.19" }, "authors": [ { diff --git a/main.php b/main.php index ab78619..395f293 100644 --- a/main.php +++ b/main.php @@ -18,12 +18,61 @@ */ include_once __DIR__ . "/vendor/autoload.php"; -use DBL\DBL; -use DBL\API\Http; +use DBL\V1DBL; $token = @file_get_contents(".TOKEN"); -$api = new DBL([ +$client = new V1DBL([ "token" => $token ]); +echo "\nrunning get bot:\n"; +$bot = $client->get_bot(1026525568344264724); +print_r($bot); + +echo "\nrunning get bots:\n"; +$bots = $client->get_bots(); +print_r($bots); + +echo "\nrunning get votes:\n"; +$voters = $client->get_votes(); +print_r($voters); + +echo "\nrunning get user vote:\n"; +$has_voted = $client->get_user_vote(0, 661200758510977084); +print_r($has_voted); + +echo "\nrunning get stats:\n"; +$stats = $client->get_stats(); +print_r($stats); + +echo "\nrunning post stats:\n"; +$client->post_stats(0, [ + "server_count" => 2 +]); + +echo "\nrunning is weekend:\n"; +$is_weekend = $client->is_weekend(); +print_r($is_weekend); + +echo "\nrunning post commands:\n"; +$client->post_commands([ + [ + "options" => [], + "name" => "test", + "name_localizations" => null, + "description" => "command description", + "description_localizations" => null, + "contexts" => [], + "default_permission" => null, + "default_member_permissions" => null, + "dm_permission" => false, + "integration_types" => [], + "nsfw" => false + ] +]); + +echo "\nrunning get vote:\n"; +$vote = $client->get_vote(661200758510977084); +print_r($vote); + ?> diff --git a/src/TopPHP/API/Exceptions/GlobalRatelimitException.php b/src/TopPHP/API/Exceptions/GlobalRatelimitException.php deleted file mode 100644 index b22e98b..0000000 --- a/src/TopPHP/API/Exceptions/GlobalRatelimitException.php +++ /dev/null @@ -1,51 +0,0 @@ -message = "You have encountered a global ratelimit. Please refer to the JSON contents for your remaining time."; - - switch($type) - { - case self::THROW_NONE: - break; - - default: - die($this->message); - break; - } - } -} - -?> diff --git a/src/TopPHP/API/Exceptions/MissingStatsException.php b/src/TopPHP/API/Exceptions/MissingStatsException.php deleted file mode 100644 index 58d6237..0000000 --- a/src/TopPHP/API/Exceptions/MissingStatsException.php +++ /dev/null @@ -1,52 +0,0 @@ -message = "The API could not make a successful connection due to missing value(s). (Check for valid value?)"; - - switch($type) - { - case self::THROW_NONE: - break; - - default: - die($this->message); - break; - } - } -} - -?> diff --git a/src/TopPHP/API/Exceptions/MissingTokenException.php b/src/TopPHP/API/Exceptions/MissingTokenException.php index 194f7ac..7cbbade 100644 --- a/src/TopPHP/API/Exceptions/MissingTokenException.php +++ b/src/TopPHP/API/Exceptions/MissingTokenException.php @@ -17,34 +17,19 @@ * This allows for an exception to be made when * the DBL class does not detect a token. */ -class MissingTokenException +class MissingTokenException extends \Exception { /** @var mixed */ public $message; - /** Special throwing rules. */ - public const THROW_NONE = 0; - public const THROW_DEFAULT = 1; - /** * Creates a MissingTokenException class. - * - * @param const|null $type The throwing type. - * @return void */ - public function __construct($type = self::THROW_DEFAULT) + public function __construct() { $this->message = "The API could not make a successful connection due to a missing token. (Do you have a token path specified in the DBL class?)"; - - switch($type) - { - case self::THROW_NONE: - break; - - default: - die($this->message); - break; - } + + parent::__construct($this->message); } } diff --git a/src/TopPHP/API/Exceptions/ResourceRatelimitException.php b/src/TopPHP/API/Exceptions/ResourceRatelimitException.php index 7bde38d..1465c6a 100644 --- a/src/TopPHP/API/Exceptions/ResourceRatelimitException.php +++ b/src/TopPHP/API/Exceptions/ResourceRatelimitException.php @@ -17,35 +17,21 @@ * This allows for exceptions to be made when * the HTTP hits a rate limit for a specific request. */ -class ResourceRatelimitException +class ResourceRatelimitException extends \Exception { /** @var mixed */ public $message; - /** Special throwing rules. */ - public const THROW_NONE = 0; - public const THROW_DEFAULT = 1; - /** * Creates a ResourceRatelimitException class. * * @param string $message The error message. - * @param const|null $type The throwing type. - * @return void */ - public function __construct(string $message, $type = self::THROW_DEFAULT) + public function __construct(string $message) { - $this->message = $message; + parent::__construct($message); - switch($type) - { - case self::THROW_NONE: - break; - - default: - die($this->message); - break; - } + $this->message = $message; } } diff --git a/src/TopPHP/API/Http.php b/src/TopPHP/API/Http.php index 19f7be2..3cf1b23 100644 --- a/src/TopPHP/API/Http.php +++ b/src/TopPHP/API/Http.php @@ -11,7 +11,9 @@ */ namespace DBL\API; + use DBL\Structs\HttpStruct; +use DBL\API\Exceptions\ResourceRatelimitException; /** * Represents the HTTP class for Top.gg. @@ -51,8 +53,8 @@ public function __construct(string $http, int $port = 80) * Has to meet with the constants of currently * accepted HTTP requests compatible with the API. * - * @param string $type The HTTP request you're using. - * @param string $path The HTTP path you're calling. + * @param string $type The HTTP request you're using. + * @param string $path The HTTP path you're calling. * @param array $payload Additional information you want to pass on as JSON. * @return array */ @@ -91,8 +93,8 @@ public function call(string $type, string $path, array $payload = []): array $_headers = ($_type) ? get_headers($path) : "400"; $_response = substr($_headers[0], 9, 3); - if($_response === "401") $_response = "200"; // This is because the token is not being enforced. - if($_response === "429") + if ($_response === "401") $_response = "200"; // This is because the token is not being enforced. + if ($_response === "429") { /** * We have two specific ratelimit exceptions here. @@ -103,12 +105,11 @@ public function call(string $type, string $path, array $payload = []): array * will be updated in the future. */ - die("You have encountered a rate limit. Please refer to the JSON contents for the remaining time."); + throw new ResourceRatelimitException("You have encountered a rate limit. Please refer to the JSON contents for the remaining time."); } /** Now provide the information for the structure. */ $_path = ($_response === "200") ? $path : null; - $_payload = (!$payload) ? $payload : null; /** * All returned information will be formatted this way. @@ -120,7 +121,7 @@ public function call(string $type, string $path, array $payload = []): array "type" => $_type, "path" => $_path, "status" => $_response, - "json" => $_payload + "json" => $payload ]; } diff --git a/src/TopPHP/API/Request.php b/src/TopPHP/API/Request.php index 3b8b5c6..7aef83e 100644 --- a/src/TopPHP/API/Request.php +++ b/src/TopPHP/API/Request.php @@ -11,6 +11,7 @@ */ namespace DBL\API; + use DBL\API\Http; use DBL\Structs\RequestStruct; @@ -70,64 +71,55 @@ public function __construct(string $token, string $http = self::SERVER_ADDR) * @param array $json Additional information you want to pass on as JSON. * @return array */ - public function req(string $type, string $path = null, array $json = [], int $port = self::SERVER_PORT): array + public function req(string $type, ?string $path = null, array $json = [], int $port = self::SERVER_PORT): array { $_http = new Http($this->http, $port); - $_path = ($path) ? $_http->getHttp(), $path : null; + $_path = ($path) ? $_http->getHttp() . $path : ""; $_error = false; $_request = null; $_response = null; - $_json = (![]) ? http_build_query($json) : null; + $_json = ($json && $type === "GET") ? "?" . http_build_query($json) : ""; try { - /** Ensure headers are restored. */ - // header_remove("Content-Type"); - /** * Set up the HTTP request structure. * Will contextualize and create how we will interact. */ - $_path = $_path, $_json; + $_path = $_path . $_json; $_struct = [ "http" => [ "method" => $type, "header" => "Content-Type: application/json" . "\r\n" . - "Authorization: {$this->token}" . "\r\n" + "Authorization: Bearer {$this->token}" . "\r\n" ] ]; - $_struct = @stream_context_create($_struct); - /** - * Here is where the official request is made - * to receive information. - */ + if ($json && $type !== "GET") { + $_struct["http"]["content"] = json_encode($json); + } + + $_struct = @stream_context_create($_struct); $_request = @file_get_contents($_path, true, $_struct); - if(!$_request) $_error = true; } - catch (Exception $error) { return $error; } + catch (\Exception $error) { return $error; } finally { - if(!$_error) + if (!$_error) { - // header("Content-Type: application/json"); - // @http_response_code(intval($this->response) + 0); - $_struct = $_http->call( $type, $_path, - json_decode($_request, true) + $_request ? json_decode($_request, true) : [] ); $this->cache = $_struct; return $_struct; } - else { - error_reporting(E_ALL); $_error = error_get_last(); /** @@ -142,7 +134,7 @@ public function req(string $type, string $path = null, array $json = [], int $po "type" => $type, "path" => $_path, "status" => $_response, - "json" => ["message" => $_error["message"]] + "json" => ["message" => $_error["message"] ?? $_error["detail"]] ]; return $this->content; diff --git a/src/TopPHP/DBL.php b/src/TopPHP/DBL.php index 2d5bfa7..dd0bbf2 100644 --- a/src/TopPHP/DBL.php +++ b/src/TopPHP/DBL.php @@ -11,20 +11,18 @@ */ namespace DBL; + use DBL\API\Http; use DBL\API\Request; use DBL\API\Exceptions\MissingTokenException; -use DBL\API\Exceptions\MissingStatsException; use DBL\Structs\BaseStruct; +use DBL\Webhook; +use DBL\Widget; /** - * Represents the TopPHP/Top.gg base class. - * This class handles all of the specified - * GET and POST requests that the API allows - * to be called on, and has methods declared - * for each specific/particular usage. + * Top.gg API v0 client */ -final class DBL implements BaseStruct +class DBL implements BaseStruct { /** * @var string @@ -58,10 +56,10 @@ final class DBL implements BaseStruct private $features; /** - * @var bool - * @access public + * @var int + * @access private */ - public $connected; + private $id; /** * Creates a DBL instance. @@ -83,8 +81,6 @@ public function __construct(array $parameters) * KEKW. */ - error_reporting(0); - /** * Begin scanning through all of the possible accepted parameters. * PHP Warnings are thrown in the event that they're found to be as invalid keys @@ -97,26 +93,53 @@ public function __construct(array $parameters) "webhook" => [] ]; - if($parameters["auto_stats"]) $this->features["auto_stats"][0] = true; - if($parameters["safety"]) $this->features["safety"] = true; - if($parameters["webhook"]) $this->features["webhook"] = true; - if($parameters["token"]) $this->token = $parameters["token"]; + if (array_key_exists("auto_stats", $parameters) && $parameters["auto_stats"]) $this->features["auto_stats"][0] = true; + if (array_key_exists("safety", $parameters) && $parameters["safety"]) $this->features["safety"] = true; + if (array_key_exists("webhook", $parameters) && $parameters["webhook"]) $this->features["webhook"] = true; + if (array_key_exists("token", $parameters) && $parameters["token"]) $this->token = $parameters["token"]; else throw new MissingTokenException(); - $this->http = (!$parameters["webhook"]["url"]) ? Request::SERVER_ADDR : $parameters["webhook"]["url"]; - $this->port = (!$parameters["webhook"]["port"]) ? Request::SERVER_PORT : $parameters["webhook"]["port"]; + $this->http = (!array_key_exists("webhook", $parameters) || !array_key_exists("url", $parameters["webhook"]) || !$parameters["webhook"]["url"]) ? Request::SERVER_ADDR : $parameters["webhook"]["url"]; + $this->port = (!array_key_exists("webhook", $parameters) || !array_key_exists("port", $parameters["webhook"]) || !$parameters["webhook"]["port"]) ? Request::SERVER_PORT : $parameters["webhook"]["port"]; $this->api = new Request($this->token, $this->http); - /** Proxy an HTTP request to see if it works. */ - $_response = $this->api->req("GET", "/users/140862798832861184")["status"]; - if($_response === "200") $this->connected = true; + try { + $parts = explode('.', $this->token); + + if (count($parts) < 2) { + throw new \Exception(); + } + + $encoded_json = $parts[1]; + $padding = 4 - (strlen($encoded_json) % 4); + + if ($padding < 4) { + $encoded_json .= str_repeat('=', $padding); + } + + $decoded_json = base64_decode($encoded_json, true); + + if ($decoded_json === false) { + throw new \Exception(); + } + + $token_data = json_decode($decoded_json, true); + + if (!isset($token_data['id']) || !is_numeric($token_data['id'])) { + throw new \Exception(); + } + + $this->id = intval($token_data['id']); + } catch (\Exception $e) { + throw new MissingTokenException(); + } /** Finally do our feature checks from the parameters list. */ - if($parameters["auto_stats"]) + if (array_key_exists("auto_stats", $parameters) && $parameters["auto_stats"]) { $this->check_auto_stats( $parameters["auto_stats"]["url"], - $parameters["auto_stats"]["id"] + $parameters["auto_stats"]["callback"] ); } @@ -127,33 +150,21 @@ public function __construct(array $parameters) * Checks if stats should be posted to the website automatically. * This can only be done for a website URL. * - * @param string $path The HTTP path you're using. - * @param int $id The bot ID. - * @param array $values A list of values to be automatically posted. + * @param string $url The HTTP path you're using. + * @param callable $callback The callback function that returns the bot's statistics. * @return void */ - protected function check_auto_stats(string $path, int $id, array $values) + private function check_auto_stats(string $url, callable $callback) { try { - if($values["shards"]) $_json["shards"] = $values["shards"]; - if($values["shard_id"]) $_json["shard_id"] = $values["shard_id"]; - if($values["shard_count"]) $_json["shard_count"] = $values["shard_count"]; - if($values["server_count"]) $_json["server_count"] = $values["server_count"]; - else throw new MissingStatsException(); - - $_id = ($id) ? $id : throw new MissingStatsException(); - $_url = ($path) ? $path : throw new MissingStatsException(); - $_type = Http::BOT; - $_request = $this->api->req("POST", "/{$_type}/{$_id}/stats", $_json)["json"]; + $this->post_stats(0, $callback()); } - - catch(Exception $error) { echo $error; } - + catch (\Exception $error) { echo $error; } finally { header("Content-Type: application/json"); - echo ""; + echo ""; } } @@ -167,10 +178,12 @@ protected function check_auto_stats(string $path, int $id, array $values) protected function check_safety() { /** One last time to check. */ - if($this->features["safety"]) die(); + if ($this->features["safety"]) die(); } /** + * @deprecated Use get_bots() instead. + * * Shows the information from the specified type through a query search. * * @param string $type The search type. @@ -179,27 +192,25 @@ protected function check_safety() */ public function show_info(string $type, array $json = []): array { + trigger_error('show_info() is deprecated, use get_bots() instead', E_USER_DEPRECATED); + switch($type) { - case Http::USER: - $_path = "users"; - break; - case Http::BOT: - $_path = "bots"; break; default: - die("Invalid search parameter: {$type}"); + throw new \Exception("Invalid search parameter: {$type}"); break; } - return $this->api->req("GET", "/{$type}", $json)["json"]; + return $this->api->req("GET", "/bots", $json)["json"]; } /** - * Displays the general information about something - * given through the search type. + * @deprecated Use get_bot() instead. + * + * Gets the general information about something given through the search type. * * @param string $type The search type. * @param int $id The bot/user ID. @@ -207,68 +218,119 @@ public function show_info(string $type, array $json = []): array */ public function find_info(string $type, int $id): array { + trigger_error('find_info() is deprecated, use get_bot() instead', E_USER_DEPRECATED); + switch($type) { - case Http::USER: - $_path = "users"; - break; - case Http::BOT: - $_path = "bots"; break; default: - die("Invalid search parameter: {$type}"); + throw new \Exception("Invalid search parameter: {$type}"); break; } - return $this->api->req("GET", "/{$type}/{$id}")["json"]; + return $this->api->req("GET", "/bots/{$id}")["json"]; } /** - * Returns the total votes of the bot. + * Returns the unique voters of your project. * - * @param int $id The bot ID. + * @param int $id The project ID. Unused, no longer has an effect. + * @param int $page The page counter. Defaults to 1. * @return array */ - public function get_votes(int $id) + public function get_votes(int $id = 0, int $page = 1): array { - return $this->api->req("GET", "/bots/{$id}/votes")["json"]; + return $this->api->req("GET", "/bots/{$this->id}/votes", ["page" => $page])["json"]; } /** - * Returns a boolean check for if a user voted for your bot. + * Returns true if a user has voted for your project. * - * @param int $id The bot user ID. - * @param int $user The user Snowflake ID. - * @return array + * @param int $id The project ID. Unused, no longer has an effect. + * @param int $user The user ID. + * @return bool */ - public function get_user_vote(int $id, int $user): array + public function get_user_vote(int $id, int $user): bool { - return $this->api->req("GET", "/bots/{$id}/check", ["userId" => $user])["json"]["voted"]; + return $this->api->req("GET", "/bots/check", ["userId" => $user])["json"]["voted"] != 0; } /** - * Returns the statistics of the bot. + * Returns your bot's posted statistics. * - * @param int $id The bot ID. + * @param int $id The bot ID. Unused, no longer has an effect. + * @return array + */ + public function get_stats(int $id = 0): array + { + return $this->api->req("GET", "/bots/stats")["json"]; + } + + /** + * Posts your Discord bot's statistics to the API. This will update the statistics in your Discord bot's Top.gg page. + * + * @param int $id The bot ID. Unused, no longer has an effect. + * @param array $json Your bot's new statistics. + */ + public function post_stats(int $id, array $json) + { + $this->api->req("POST", "/bots/stats", $json); + } + + /** + * Gets the general information of several bots. + * + * @param int $limit The maximum amount of bots to be queried. + * @param int $offset The amount of bots to be skipped. + * @param string $sort_by Sorts results based on a specific criteria. Results will always be descending. * @return array */ - public function get_stats(int $id): array + public function get_bots(int $limit = 50, int $offset = 0, string $sort_by = "monthlyPoints"): array { - return $this->api->req("GET", "/bots/{$id}/stats")["json"]; + if ($limit <= 0) { + $limit .= 1; + } else if ($limit > 500) { + $limit .= 500; + } + + if ($offset < 0) { + $offset .= 0; + } else if ($offset > 499) { + $offset .= 499; + } + + if ($sort_by !== "monthlyPoints" && $sort_by !== "date" && $sort_by !== "id") { + throw new \Exception("sort_by argument must be \"monthlyPoints\", \"date\", or \"id\"."); + } + + return $this->api->req("GET", "/bots", [ + "limit" => $limit, + "offset" => $offset, + "sort" => $sort_by, + ])["json"]; } /** - * Posts statistics to the bot's Top.gg page. + * Gets the general information about a bot. * - * @param int $id The bot ID. - * @param array $json The JSON query fields. + * @param int $id The bot ID. * @return array */ - public function post_stats(int $id, array $json): array + public function get_bot(int $id): array + { + return $this->api->req("GET", "/bots/{$id}")["json"]; + } + + /** + * Returns true if the weekend multiplier is active, where a single vote counts as two. + * + * @return bool + */ + public function is_weekend(): bool { - return $this->api->req("POST", "/bots/{$id}/stats", $json)["json"]; + return $this->api->req("GET", "/weekend")["json"]["is_weekend"]; } /** diff --git a/src/TopPHP/Structs/BaseStruct.php b/src/TopPHP/Structs/BaseStruct.php index 25bef85..c5dca64 100644 --- a/src/TopPHP/Structs/BaseStruct.php +++ b/src/TopPHP/Structs/BaseStruct.php @@ -25,9 +25,13 @@ public function __construct(array $parameters); public function show_info(string $type, array $json = []); public function find_info(string $type, int $id); - /** Get information on the votes, vote check; and stats. */ - public function get_votes(int $id); + public function get_bots(int $limit, int $offset, string $sort_by); + public function get_bot(int $id); + + /** Get information on your voters, vote check, weekend multiplier, and stats. */ + public function get_votes(int $id, int $page); public function get_user_vote(int $id, int $user); + public function is_weekend(); public function get_stats(int $id); /** diff --git a/src/TopPHP/Structs/RequestStruct.php b/src/TopPHP/Structs/RequestStruct.php index dee2023..a3e9b0b 100644 --- a/src/TopPHP/Structs/RequestStruct.php +++ b/src/TopPHP/Structs/RequestStruct.php @@ -15,7 +15,7 @@ interface RequestStruct { public function __construct(string $token, string $http); - public function req(string $type, string $path = null, array $json = [], int $port = 80); + public function req(string $type, ?string $path = null, array $json = [], int $port = 80); /** Accessor methods for getting private instances. */ public function getContents(); diff --git a/src/TopPHP/V1DBL.php b/src/TopPHP/V1DBL.php new file mode 100644 index 0000000..9cdba6b --- /dev/null +++ b/src/TopPHP/V1DBL.php @@ -0,0 +1,56 @@ +api->req("POST", "/v1/projects/@me/commands", $commands); + } + + /** + * Gets the latest vote information of a Top.gg user on your project. + * + * @param int $id The user's ID. + * @param string $source The ID type to use. Defaults to "discord". + * @return array|null + */ + public function get_vote(int $id, string $source = "discord"): array|null + { + if ($source !== "topgg" && $source !== "discord") + { + throw new \Exception("source argument must be \"topgg\" or \"discord\"."); + } + + $result = $this->api->req("GET", "/v1/projects/@me/votes/{$id}", ["source" => $source]); + + if ($result["status"] === "404") { + return null; + } + + return $result["json"]; + } +} + +?> diff --git a/src/TopPHP/Webhook.php b/src/TopPHP/Webhook.php new file mode 100644 index 0000000..fc35408 --- /dev/null +++ b/src/TopPHP/Webhook.php @@ -0,0 +1,51 @@ +header("Authorization") !== $this->getAuthorization()) { + return response("Unauthorized", 401); + } + + $data = $request->json()->all(); + + if (!empty($data)) { + $this->callback($data); + + return response("", 204); + } + + return response("Bad request", 400); + } + + /** + * Retrieves the authorization token used to authorize incoming webhook requests. + * + * @return string + */ + public abstract function getAuthorization(); + + /** + * Receives the JSON body sent from Top.gg containing webhook event information. + * + * @param array $data The JSON body sent from Top.gg containing webhook event information. + * @return void + */ + public abstract function callback(array $data); +} + +?> \ No newline at end of file diff --git a/src/TopPHP/Widget.php b/src/TopPHP/Widget.php new file mode 100644 index 0000000..1582983 --- /dev/null +++ b/src/TopPHP/Widget.php @@ -0,0 +1,68 @@ +value . "/$id"; + } + + /** + * Generates a small widget URL for displaying votes. + * + * @param WidgetType $ty The widget type. + * @param int $id The ID. + * @return string + */ + public static function votes(WidgetType $ty, int $id): string { + return self::BASE_URL . "/widgets/small/votes/" . $ty->value . "/$id"; + } + + /** + * Generates a small widget URL for displaying a project's owner. + * + * @param WidgetType $ty The widget type. + * @param int $id The ID. + * @return string + */ + public static function owner(WidgetType $ty, int $id): string { + return self::BASE_URL . "/widgets/small/owner/" . $ty->value . "/$id"; + } + + /** + * Generates a small widget URL for displaying social stats. + * + * @param WidgetType $ty The widget type. + * @param int $id The ID. + * @return string + */ + public static function social(WidgetType $ty, int $id): string { + return self::BASE_URL . "/widgets/small/social/" . $ty->value . "/$id"; + } +} + +?> \ No newline at end of file