* * This file is subject to the MIT license that is bundled * with this source code in the LICENSE file. */ namespace Discord\Http; class Endpoint { // GET public const GATEWAY = 'gateway'; // GET public const GATEWAY_BOT = self::GATEWAY.'/bot'; // GET, POST, PUT public const GLOBAL_APPLICATION_COMMANDS = 'applications/:application_id/commands'; // GET, PATCH, DELETE public const GLOBAL_APPLICATION_COMMAND = 'applications/:application_id/commands/:command_id'; // GET, POST, PUT public const GUILD_APPLICATION_COMMANDS = 'applications/:application_id/guilds/:guild_id/commands'; // GET, PUT public const GUILD_APPLICATION_COMMANDS_PERMISSIONS = 'applications/:application_id/guilds/:guild_id/commands/permissions'; // GET, PATCH, DELETE public const GUILD_APPLICATION_COMMAND = 'applications/:application_id/guilds/:guild_id/commands/:command_id'; // GET, PUT public const GUILD_APPLICATION_COMMAND_PERMISSIONS = 'applications/:application_id/guilds/:guild_id/commands/:command_id/permissions'; // POST public const INTERACTION_RESPONSE = 'interactions/:interaction_id/:interaction_token/callback'; // PATCH, DELETE public const ORIGINAL_INTERACTION_RESPONSE = 'webhooks/:application_id/:interaction_token/messages/@original'; // POST public const CREATE_INTERACTION_FOLLOW_UP = 'webhooks/:application_id/:interaction_token'; // PATCH, DELETE public const INTERACTION_FOLLOW_UP = 'webhooks/:application_id/:interaction_token/messages/:message_id'; // GET public const AUDIT_LOG = 'guilds/:guild_id/audit-logs'; // GET, PATCH, DELETE public const CHANNEL = 'channels/:channel_id'; // GET, POST public const CHANNEL_MESSAGES = self::CHANNEL.'/messages'; // GET, PATCH, DELETE public const CHANNEL_MESSAGE = self::CHANNEL.'/messages/:message_id'; // POST public const CHANNEL_CROSSPOST_MESSAGE = self::CHANNEL.'/messages/:message_id/crosspost'; // POST public const CHANNEL_MESSAGES_BULK_DELETE = self::CHANNEL.'/messages/bulk-delete'; // PUT, DELETE public const CHANNEL_PERMISSIONS = self::CHANNEL.'/permissions/:overwrite_id'; // GET, POST public const CHANNEL_INVITES = self::CHANNEL.'/invites'; // POST public const CHANNEL_FOLLOW = self::CHANNEL.'/followers'; // POST public const CHANNEL_TYPING = self::CHANNEL.'/typing'; // GET public const CHANNEL_PINS = self::CHANNEL.'/pins'; // PUT, DELETE public const CHANNEL_PIN = self::CHANNEL.'/pins/:message_id'; // POST public const CHANNEL_THREADS = self::CHANNEL.'/threads'; // POST public const CHANNEL_MESSAGE_THREADS = self::CHANNEL_MESSAGE.'/threads'; // GET public const CHANNEL_THREADS_ACTIVE = self::CHANNEL_THREADS.'/active'; // GET public const CHANNEL_THREADS_ARCHIVED_PUBLIC = self::CHANNEL_THREADS.'/archived/public'; // GET public const CHANNEL_THREADS_ARCHIVED_PRIVATE = self::CHANNEL_THREADS.'/archived/private'; // GET public const CHANNEL_THREADS_ARCHIVED_PRIVATE_ME = self::CHANNEL.'/users/@me/threads/archived/private'; // GET, PATCH, DELETE public const THREAD = 'channels/:thread_id'; // GET public const THREAD_MEMBERS = self::THREAD.'/thread-members'; // GET, PUT, DELETE public const THREAD_MEMBER = self::THREAD_MEMBERS.'/:user_id'; // PUT, DELETE public const THREAD_MEMBER_ME = self::THREAD_MEMBERS.'/@me'; // GET, DELETE public const MESSAGE_REACTION_ALL = self::CHANNEL.'/messages/:message_id/reactions'; // GET, DELETE public const MESSAGE_REACTION_EMOJI = self::CHANNEL.'/messages/:message_id/reactions/:emoji'; // PUT, DELETE public const OWN_MESSAGE_REACTION = self::CHANNEL.'/messages/:message_id/reactions/:emoji/@me'; // DELETE public const USER_MESSAGE_REACTION = self::CHANNEL.'/messages/:message_id/reactions/:emoji/:user_id'; // GET, POST public const CHANNEL_WEBHOOKS = self::CHANNEL.'/webhooks'; // POST public const GUILDS = 'guilds'; // GET, PATCH, DELETE public const GUILD = 'guilds/:guild_id'; // GET, POST, PATCH public const GUILD_CHANNELS = self::GUILD.'/channels'; // GET public const GUILD_MEMBERS = self::GUILD.'/members'; // GET public const GUILD_MEMBERS_SEARCH = self::GUILD.'/members/search'; // GET, PATCH, PUT, DELETE public const GUILD_MEMBER = self::GUILD.'/members/:user_id'; // PATCH public const GUILD_MEMBER_SELF = self::GUILD.'/members/@me'; /** @deprecated 9.0.9 Use `GUILD_MEMBER_SELF` */ public const GUILD_MEMBER_SELF_NICK = self::GUILD.'/members/@me/nick'; // PUT, DELETE public const GUILD_MEMBER_ROLE = self::GUILD.'/members/:user_id/roles/:role_id'; // GET public const GUILD_BANS = self::GUILD.'/bans'; // GET, PUT, DELETE public const GUILD_BAN = self::GUILD.'/bans/:user_id'; // GET, PATCH public const GUILD_ROLES = self::GUILD.'/roles'; // GET, POST, PATCH, DELETE public const GUILD_ROLE = self::GUILD.'/roles/:role_id'; // POST public const GUILD_MFA = self::GUILD.'/mfa'; // GET, POST public const GUILD_INVITES = self::GUILD.'/invites'; // GET, POST public const GUILD_INTEGRATIONS = self::GUILD.'/integrations'; // PATCH, DELETE public const GUILD_INTEGRATION = self::GUILD.'/integrations/:integration_id'; // POST public const GUILD_INTEGRATION_SYNC = self::GUILD.'/integrations/:integration_id/sync'; // GET, POST public const GUILD_EMOJIS = self::GUILD.'/emojis'; // GET, PATCH, DELETE public const GUILD_EMOJI = self::GUILD.'/emojis/:emoji_id'; // GET public const GUILD_PREVIEW = self::GUILD.'/preview'; // GET, POST public const GUILD_PRUNE = self::GUILD.'/prune'; // GET public const GUILD_REGIONS = self::GUILD.'/regions'; // GET, PATCH public const GUILD_WIDGET_SETTINGS = self::GUILD.'/widget'; // GET public const GUILD_WIDGET = self::GUILD.'/widget.json'; // GET public const GUILD_WIDGET_IMAGE = self::GUILD.'/widget.png'; // GET, PATCH public const GUILD_WELCOME_SCREEN = self::GUILD.'/welcome-screen'; // PATCH public const GUILD_USER_CURRENT_VOICE_STATE = self::GUILD.'/voice-states/@me'; // PATCH public const GUILD_USER_VOICE_STATE = self::GUILD.'/voice-states/:user_id'; // GET public const GUILD_VANITY_URL = self::GUILD.'/vanity-url'; // GET, PATCH public const GUILD_MEMBERSHIP_SCREENING = self::GUILD.'/member-verification'; // GET public const GUILD_WEBHOOKS = self::GUILD.'/webhooks'; // GET, POST public const GUILD_STICKERS = self::GUILD.'/stickers'; // GET, PATCH, DELETE public const GUILD_STICKER = self::GUILD.'/stickers/:sticker_id'; // GET public const STICKER = 'stickers/:sticker_id'; // GET public const STICKER_PACKS = 'sticker-packs'; // GET, POST public const GUILD_SCHEDULED_EVENTS = self::GUILD.'/scheduled-events'; // GET, PATCH, DELETE public const GUILD_SCHEDULED_EVENT = self::GUILD.'/scheduled-events/:guild_scheduled_event_id'; // GET public const GUILD_SCHEDULED_EVENT_USERS = self::GUILD.'/scheduled-events/:guild_scheduled_event_id/users'; // GET, DELETE public const INVITE = 'invites/:code'; // POST public const STAGE_INSTANCES = 'stage-instances'; // GET, PATCH, DELETE public const STAGE_INSTANCE = 'stage-instances/:channel_id'; // GET, POST public const GUILDS_TEMPLATE = self::GUILDS.'/templates/:template_code'; // GET, POST public const GUILD_TEMPLATES = self::GUILD.'/templates'; // PUT, PATCH, DELETE public const GUILD_TEMPLATE = self::GUILD.'/templates/:template_code'; // GET, POST public const GUILD_AUTO_MODERATION_RULES = self::GUILD.'/auto-moderation/rules'; // GET, PATCH, DELETE public const GUILD_AUTO_MODERATION_RULE = self::GUILD.'/auto-moderation/rules/:auto_moderation_rule_id'; // GET, PATCH public const USER_CURRENT = 'users/@me'; // GET public const USER = 'users/:user_id'; // GET public const USER_CURRENT_GUILDS = self::USER_CURRENT.'/guilds'; // DELETE public const USER_CURRENT_GUILD = self::USER_CURRENT.'/guilds/:guild_id'; // GET public const USER_CURRENT_MEMBER = self::USER_CURRENT.'/guilds/:guild_id/member'; // GET, POST public const USER_CURRENT_CHANNELS = self::USER_CURRENT.'/channels'; // GET public const USER_CURRENT_CONNECTIONS = self::USER_CURRENT.'/connections'; // GET, PUT public const USER_CURRENT_APPLICATION_ROLE_CONNECTION = self::USER_CURRENT.'/applications/:application_id/role-connection'; // GET public const APPLICATION_CURRENT = 'oauth2/applications/@me'; // GET, PATCH, DELETE public const WEBHOOK = 'webhooks/:webhook_id'; // GET, PATCH, DELETE public const WEBHOOK_TOKEN = 'webhooks/:webhook_id/:webhook_token'; // POST public const WEBHOOK_EXECUTE = self::WEBHOOK_TOKEN; // POST public const WEBHOOK_EXECUTE_SLACK = self::WEBHOOK_EXECUTE.'/slack'; // POST public const WEBHOOK_EXECUTE_GITHUB = self::WEBHOOK_EXECUTE.'/github'; // PATCH, DELETE public const WEBHOOK_MESSAGE = self::WEBHOOK_TOKEN.'/messages/:message_id'; // GET, PUT public const APPLICATION_ROLE_CONNECTION_METADATA = 'applications/:application_id/role-connections/metadata'; /** * Regex to identify parameters in endpoints. * * @var string */ public const REGEX = '/:([^\/]*)/'; /** * A list of parameters considered 'major' by Discord. * * @see https://discord.com/developers/docs/topics/rate-limits * @var string[] */ public const MAJOR_PARAMETERS = ['channel_id', 'guild_id', 'webhook_id', 'thread_id']; /** * The string version of the endpoint, including all parameters. * * @var string */ protected $endpoint; /** * Array of placeholders to be replaced in the endpoint. * * @var string[] */ protected $vars = []; /** * Array of arguments to substitute into the endpoint. * * @var string[] */ protected $args = []; /** * Array of query data to be appended * to the end of the endpoint with `http_build_query`. * * @var array */ protected $query = []; /** * Creates an endpoint class. * * @param string $endpoint */ public function __construct(string $endpoint) { $this->endpoint = $endpoint; if (preg_match_all(self::REGEX, $endpoint, $vars)) { $this->vars = $vars[1] ?? []; } } /** * Binds a list of arguments to the endpoint. * * @param string[] ...$args * @return this */ public function bindArgs(...$args): self { for ($i = 0; $i < count($this->vars) && $i < count($args); $i++) { $this->args[$this->vars[$i]] = $args[$i]; } return $this; } /** * Binds an associative array to the endpoint. * * @param string[] $args * @return this */ public function bindAssoc(array $args): self { $this->args = array_merge($this->args, $args); return $this; } /** * Adds a key-value query pair to the endpoint. * * @param string $key * @param string|bool $value */ public function addQuery(string $key, $value): void { if (! is_bool($value)) { $value = (string) $value; } $this->query[$key] = $value; } /** * Converts the endpoint into the absolute endpoint with * placeholders replaced. * * Passing a true boolean in will only replace the major parameters. * Used for rate limit buckets. * * @param bool $onlyMajorParameters * @return string */ public function toAbsoluteEndpoint(bool $onlyMajorParameters = false): string { $endpoint = $this->endpoint; foreach ($this->vars as $var) { if (! isset($this->args[$var]) || ($onlyMajorParameters && ! $this->isMajorParameter($var))) { continue; } $endpoint = str_replace(":{$var}", $this->args[$var], $endpoint); } if (! $onlyMajorParameters && count($this->query) > 0) { $endpoint .= '?'.http_build_query($this->query); } return $endpoint; } /** * Converts the endpoint to a string. * Alias of ->toAbsoluteEndpoint();. * * @return string */ public function __toString(): string { return $this->toAbsoluteEndpoint(); } /** * Creates an endpoint class and binds arguments to * the newly created instance. * * @param string $endpoint * @param string[] $args * @return Endpoint */ public static function bind(string $endpoint, ...$args) { $endpoint = new Endpoint($endpoint); $endpoint->bindArgs(...$args); return $endpoint; } /** * Checks if a parameter is a major parameter. * * @param string $param * @return bool */ private static function isMajorParameter(string $param): bool { return in_array($param, self::MAJOR_PARAMETERS); } }