diff --git a/README.md b/README.md
index abeb651..6b49c7d 100644
--- a/README.md
+++ b/README.md
@@ -153,7 +153,7 @@ class UserCommand
{
#[Subcommand(name: 'info', description: 'Get user information')]
public function info(
- #[Option(name: 'user', description: 'Target user', required: true)]
+ #[Option(description: 'Target user')]
User $user
): void {
// Handle user info command
@@ -161,6 +161,17 @@ class UserCommand
}
```
+Whether an option is required comes from whether its parameter has a default, so `$user`
+above is required without saying so.
+
+## Documentation
+
+See [docs/](docs/README.md) — guides for getting started, commands, autocomplete, events and
+translations, plus a reference generated from the source.
+
+Run `composer docs` after changing the public API; the test suite fails if the committed
+documentation no longer matches the code.
+
## Contributing
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Tempcord.
diff --git a/composer.json b/composer.json
index 4a469a6..fd2f5c0 100644
--- a/composer.json
+++ b/composer.json
@@ -5,9 +5,11 @@
"type": "library",
"require": {
"php": "^8.5",
- "tempest/console": "^3.18",
"ragnarok/fenrir": "^1.0",
- "tempest/core": "^3.18"
+ "tempest/console": "^3.18",
+ "tempest/core": "^3.18",
+ "tempest/event-bus": "^3.18",
+ "tempest/log": "^3.18"
},
"license": "MIT",
"authors": [
@@ -28,7 +30,8 @@
"scripts": {
"test": "phpunit",
"analyse": "phpstan analyse --memory-limit=1G",
- "cs-fix": "php-cs-fixer fix"
+ "cs-fix": "php-cs-fixer fix",
+ "docs": "php tools/generate-docs.php"
},
"config": {
"sort-packages": true,
@@ -47,7 +50,11 @@
},
"autoload-dev": {
"psr-4": {
- "Tempcord\\Tests\\": "tests/"
+ "Tempcord\\Tests\\": "tests/",
+ "Tempcord\\Tools\\": "tools/src/"
}
+ },
+ "suggest": {
+ "tempest/intl": "Enables name and description translations for commands via #[Command(translationKey: ...)]. Requires ext-intl."
}
}
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..dd24d74
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,29 @@
+
+
+# Tempcord documentation
+
+Build Discord bots with PHP, on top of [Tempest](https://tempestphp.com).
+
+## Guides
+
+- [Getting started](guides/01-getting-started.md)
+- [Commands and options](guides/02-commands.md)
+- [Autocomplete](guides/03-autocomplete.md)
+- [Events](guides/04-events.md)
+- [Translations](guides/05-translations.md)
+- [Plugins](guides/06-plugins.md)
+
+## Reference
+
+Generated from the source, so it describes what the framework actually does.
+
+**Attributes** — [Command](reference/attributes/command.md), [SubcommandGroup](reference/attributes/subcommand-group.md), [Subcommand](reference/attributes/subcommand.md), [Option](reference/attributes/option.md), [Event](reference/attributes/event.md)
+
+**Autocomplete** — [Autocomplete](reference/autocomplete/autocomplete.md), [ArrayAutocomplete](reference/autocomplete/array-autocomplete.md)
+
+**Configuration** — [TempcordConfig](reference/configuration/tempcord-config.md)
+
+**Enums** — [DiscordLocale](reference/enums/discord-locale.md)
+
+**Plugins** — [Plugin](reference/plugins/plugin.md)
+
diff --git a/docs/api-reference.md b/docs/api-reference.md
deleted file mode 100644
index 776c045..0000000
--- a/docs/api-reference.md
+++ /dev/null
@@ -1,833 +0,0 @@
-# API Reference
-
-This document provides a comprehensive reference for Tempcord's API classes, methods, and interfaces.
-
-## Core Classes
-
-### Tempcord\Discord\Interaction
-
-Represents a Discord interaction (slash command, button click, etc.).
-
-#### Properties
-
-```php
-public readonly string $id; // Interaction ID
-public readonly string $applicationId; // Application ID
-public readonly int $type; // Interaction type
-public readonly ?string $token; // Interaction token
-public readonly int $version; // Version
-public readonly ?string $commandName; // Command name
-public readonly ?string $commandId; // Command ID
-public readonly array $options; // Command options
-public readonly User $user; // User who triggered
-public readonly ?Member $member; // Guild member (if in guild)
-public readonly ?Guild $guild; // Guild (if in guild)
-public readonly Channel $channel; // Channel
-public readonly ?Message $message; // Message (for components)
-public readonly string $locale; // User locale
-public readonly ?string $guildLocale; // Guild locale
-```
-
-#### Methods
-
-```php
-// Response methods
-public function reply(string $content, bool $ephemeral = false): void
-public function editReply(string $content): void
-public function deleteReply(): void
-public function followUp(string $content, bool $ephemeral = false): void
-
-// Deferred responses
-public function defer(bool $ephemeral = false): void
-public function deferUpdate(): void
-
-// Modal responses
-public function showModal(Modal $modal): void
-
-// Component responses
-public function updateMessage(string $content, array $components = []): void
-
-// Option helpers
-public function getOption(string $name, mixed $default = null): mixed
-public function getStringOption(string $name, ?string $default = null): ?string
-public function getIntegerOption(string $name, ?int $default = null): ?int
-public function getBooleanOption(string $name, ?bool $default = null): ?bool
-public function getUserOption(string $name): ?User
-public function getChannelOption(string $name): ?Channel
-public function getRoleOption(string $name): ?Role
-public function getMentionableOption(string $name): User|Member|Role|null
-public function getAttachmentOption(string $name): ?Attachment
-
-// Context methods
-public function setContext(string $key, mixed $value): void
-public function getContext(string $key, mixed $default = null): mixed
-public function hasContext(string $key): bool
-
-// Permission checks
-public function hasPermission(Permission $permission): bool
-public function hasRole(string $roleId): bool
-public function isOwner(): bool
-public function isAdmin(): bool
-```
-
-### Tempcord\Discord\User
-
-Represents a Discord user.
-
-#### Properties
-
-```php
-public readonly string $id; // User ID
-public readonly string $username; // Username
-public readonly string $discriminator; // Discriminator (legacy)
-public readonly ?string $globalName; // Global display name
-public readonly ?string $avatar; // Avatar hash
-public readonly ?bool $bot; // Is bot
-public readonly ?bool $system; // Is system user
-public readonly ?bool $mfaEnabled; // MFA enabled
-public readonly ?string $banner; // Banner hash
-public readonly ?int $accentColor; // Accent color
-public readonly ?string $locale; // User locale
-public readonly ?bool $verified; // Email verified
-public readonly ?string $email; // Email address
-public readonly ?int $flags; // User flags
-public readonly ?int $premiumType; // Nitro type
-public readonly ?int $publicFlags; // Public flags
-```
-
-#### Methods
-
-```php
-// Display methods
-public function getDisplayName(): string // Get display name
-public function getTag(): string // Get user#discriminator
-public function getMention(): string // Get <@id> mention
-public function getAvatarUrl(?int $size = null): ?string // Get avatar URL
-public function getBannerUrl(?int $size = null): ?string // Get banner URL
-
-// Utility methods
-public function isBot(): bool // Check if bot
-public function hasNitro(): bool // Check if has Nitro
-public function getCreatedAt(): DateTime // Get account creation date
-```
-
-### Tempcord\Discord\Guild
-
-Represents a Discord guild (server).
-
-#### Properties
-
-```php
-public readonly string $id; // Guild ID
-public readonly string $name; // Guild name
-public readonly ?string $icon; // Icon hash
-public readonly ?string $iconHash; // Icon hash (legacy)
-public readonly ?string $splash; // Splash hash
-public readonly ?string $discoverySplash; // Discovery splash
-public readonly ?bool $owner; // Is owner
-public readonly string $ownerId; // Owner ID
-public readonly ?string $permissions; // Permissions
-public readonly ?string $region; // Voice region (deprecated)
-public readonly ?string $afkChannelId; // AFK channel ID
-public readonly int $afkTimeout; // AFK timeout
-public readonly ?bool $widgetEnabled; // Widget enabled
-public readonly ?string $widgetChannelId; // Widget channel ID
-public readonly int $verificationLevel; // Verification level
-public readonly int $defaultMessageNotifications; // Default notifications
-public readonly int $explicitContentFilter; // Content filter level
-public readonly array $roles; // Guild roles
-public readonly array $emojis; // Guild emojis
-public readonly array $features; // Guild features
-public readonly int $mfaLevel; // MFA level
-public readonly ?string $applicationId; // Application ID
-public readonly ?string $systemChannelId; // System channel ID
-public readonly int $systemChannelFlags; // System channel flags
-public readonly ?string $rulesChannelId; // Rules channel ID
-public readonly ?int $maxPresences; // Max presences
-public readonly ?int $maxMembers; // Max members
-public readonly ?string $vanityUrlCode; // Vanity URL
-public readonly ?string $description; // Description
-public readonly ?string $banner; // Banner hash
-public readonly int $premiumTier; // Boost tier
-public readonly ?int $premiumSubscriptionCount; // Boost count
-public readonly string $preferredLocale; // Preferred locale
-public readonly ?string $publicUpdatesChannelId; // Updates channel
-public readonly ?int $maxVideoChannelUsers; // Max video users
-public readonly ?int $approximateMemberCount; // Approx members
-public readonly ?int $approximatePresenceCount; // Approx online
-```
-
-#### Methods
-
-```php
-// Display methods
-public function getIconUrl(?int $size = null): ?string // Get icon URL
-public function getSplashUrl(?int $size = null): ?string // Get splash URL
-public function getBannerUrl(?int $size = null): ?string // Get banner URL
-
-// Member methods
-public function getMember(string $userId): ?Member // Get member
-public function getMembers(): array // Get all members
-public function getMemberCount(): int // Get member count
-
-// Channel methods
-public function getChannel(string $channelId): ?Channel // Get channel
-public function getChannels(): array // Get all channels
-public function getTextChannels(): array // Get text channels
-public function getVoiceChannels(): array // Get voice channels
-
-// Role methods
-public function getRole(string $roleId): ?Role // Get role
-public function getRoles(): array // Get all roles
-public function getEveryoneRole(): Role // Get @everyone role
-
-// Permission methods
-public function getMemberPermissions(string $userId): array // Get permissions
-public function hasFeature(string $feature): bool // Check feature
-
-// Utility methods
-public function getCreatedAt(): DateTime // Get creation date
-public function isOwner(string $userId): bool // Check if user is owner
-```
-
-### Tempcord\Discord\Channel
-
-Represents a Discord channel.
-
-#### Properties
-
-```php
-public readonly string $id; // Channel ID
-public readonly int $type; // Channel type
-public readonly ?string $guildId; // Guild ID
-public readonly ?int $position; // Position
-public readonly ?array $permissionOverwrites; // Permission overwrites
-public readonly ?string $name; // Channel name
-public readonly ?string $topic; // Channel topic
-public readonly ?bool $nsfw; // Is NSFW
-public readonly ?string $lastMessageId; // Last message ID
-public readonly ?int $bitrate; // Voice bitrate
-public readonly ?int $userLimit; // Voice user limit
-public readonly ?int $rateLimitPerUser; // Rate limit
-public readonly ?array $recipients; // DM recipients
-public readonly ?string $icon; // Channel icon
-public readonly ?string $ownerId; // Channel owner
-public readonly ?string $applicationId; // Application ID
-public readonly ?string $parentId; // Parent category ID
-public readonly ?DateTime $lastPinTimestamp; // Last pin timestamp
-public readonly ?string $rtcRegion; // RTC region
-public readonly ?int $videoQualityMode; // Video quality
-public readonly ?int $messageCount; // Message count
-public readonly ?int $memberCount; // Member count
-public readonly ?int $defaultAutoArchiveDuration; // Auto archive
-public readonly ?string $permissions; // Permissions
-public readonly ?int $flags; // Channel flags
-```
-
-#### Methods
-
-```php
-// Message methods
-public function sendMessage(string $content, array $options = []): Message
-public function getMessage(string $messageId): ?Message
-public function getMessages(int $limit = 50): array
-public function deleteMessage(string $messageId): void
-public function editMessage(string $messageId, string $content): Message
-
-// Permission methods
-public function hasPermission(string $userId, Permission $permission): bool
-public function getPermissions(string $userId): array
-
-// Utility methods
-public function getMention(): string // Get <#id> mention
-public function isText(): bool // Is text channel
-public function isVoice(): bool // Is voice channel
-public function isCategory(): bool // Is category
-public function isDM(): bool // Is DM channel
-public function isThread(): bool // Is thread
-public function getCreatedAt(): DateTime // Get creation date
-```
-
-## Attribute Classes
-
-### Tempcord\Attributes\Command
-
-Defines a slash command.
-
-```php
-#[Command(
- name: 'hello',
- description: 'Say hello to someone',
- defaultMemberPermissions: Permission::SEND_MESSAGES,
- dmPermission: true,
- nsfw: false
-)]
-```
-
-#### Parameters
-
-- `name` (string): Command name (required)
-- `description` (string): Command description (required)
-- `defaultMemberPermissions` (Permission|null): Default permissions
-- `dmPermission` (bool): Allow in DMs
-- `nsfw` (bool): NSFW command
-
-### Tempcord\Attributes\Option
-
-Defines a command option.
-
-```php
-#[Option(
- name: 'user',
- description: 'The user to greet',
- type: OptionType::USER,
- required: true,
- autocomplete: false
-)]
-```
-
-#### Parameters
-
-- `name` (string): Option name (required)
-- `description` (string): Option description (required)
-- `type` (OptionType): Option type (required)
-- `required` (bool): Is required
-- `autocomplete` (bool): Enable autocomplete
-- `choices` (array): Predefined choices
-- `minValue` (int|float): Minimum value
-- `maxValue` (int|float): Maximum value
-- `minLength` (int): Minimum string length
-- `maxLength` (int): Maximum string length
-- `channelTypes` (array): Allowed channel types
-
-### Tempcord\Attributes\EventListener
-
-Defines an event listener.
-
-```php
-#[EventListener(
- event: MessageCreateEvent::class,
- priority: 100,
- once: false
-)]
-```
-
-#### Parameters
-
-- `event` (string): Event class (required)
-- `priority` (int): Listener priority
-- `once` (bool): Run only once
-
-### Tempcord\Attributes\Middleware
-
-Defines middleware.
-
-```php
-#[Middleware(
- priority: 100
-)]
-```
-
-#### Parameters
-
-- `priority` (int): Middleware priority
-
-### Tempcord\Attributes\UseMiddleware
-
-Applies middleware to commands.
-
-```php
-#[UseMiddleware([
- AuthMiddleware::class,
- RateLimitMiddleware::class
-])]
-```
-
-#### Parameters
-
-- `middleware` (array): Array of middleware classes
-
-## Enums
-
-### Tempcord\Enums\OptionType
-
-Command option types.
-
-```php
-OptionType::SUB_COMMAND // 1
-OptionType::SUB_COMMAND_GROUP // 2
-OptionType::STRING // 3
-OptionType::INTEGER // 4
-OptionType::BOOLEAN // 5
-OptionType::USER // 6
-OptionType::CHANNEL // 7
-OptionType::ROLE // 8
-OptionType::MENTIONABLE // 9
-OptionType::NUMBER // 10
-OptionType::ATTACHMENT // 11
-```
-
-### Tempcord\Enums\Permission
-
-Discord permissions.
-
-```php
-Permission::CREATE_INSTANT_INVITE // 1 << 0
-Permission::KICK_MEMBERS // 1 << 1
-Permission::BAN_MEMBERS // 1 << 2
-Permission::ADMINISTRATOR // 1 << 3
-Permission::MANAGE_CHANNELS // 1 << 4
-Permission::MANAGE_GUILD // 1 << 5
-Permission::ADD_REACTIONS // 1 << 6
-Permission::VIEW_AUDIT_LOG // 1 << 7
-Permission::PRIORITY_SPEAKER // 1 << 8
-Permission::STREAM // 1 << 9
-Permission::VIEW_CHANNEL // 1 << 10
-Permission::SEND_MESSAGES // 1 << 11
-Permission::SEND_TTS_MESSAGES // 1 << 12
-Permission::MANAGE_MESSAGES // 1 << 13
-Permission::EMBED_LINKS // 1 << 14
-Permission::ATTACH_FILES // 1 << 15
-Permission::READ_MESSAGE_HISTORY // 1 << 16
-Permission::MENTION_EVERYONE // 1 << 17
-Permission::USE_EXTERNAL_EMOJIS // 1 << 18
-Permission::VIEW_GUILD_INSIGHTS // 1 << 19
-Permission::CONNECT // 1 << 20
-Permission::SPEAK // 1 << 21
-Permission::MUTE_MEMBERS // 1 << 22
-Permission::DEAFEN_MEMBERS // 1 << 23
-Permission::MOVE_MEMBERS // 1 << 24
-Permission::USE_VAD // 1 << 25
-Permission::CHANGE_NICKNAME // 1 << 26
-Permission::MANAGE_NICKNAMES // 1 << 27
-Permission::MANAGE_ROLES // 1 << 28
-Permission::MANAGE_WEBHOOKS // 1 << 29
-Permission::MANAGE_EMOJIS_AND_STICKERS // 1 << 30
-Permission::USE_APPLICATION_COMMANDS // 1 << 31
-Permission::REQUEST_TO_SPEAK // 1 << 32
-Permission::MANAGE_EVENTS // 1 << 33
-Permission::MANAGE_THREADS // 1 << 34
-Permission::CREATE_PUBLIC_THREADS // 1 << 35
-Permission::CREATE_PRIVATE_THREADS // 1 << 36
-Permission::USE_EXTERNAL_STICKERS // 1 << 37
-Permission::SEND_MESSAGES_IN_THREADS // 1 << 38
-Permission::USE_EMBEDDED_ACTIVITIES // 1 << 39
-Permission::MODERATE_MEMBERS // 1 << 40
-```
-
-### Tempcord\Enums\InteractionType
-
-Interaction types.
-
-```php
-InteractionType::PING // 1
-InteractionType::APPLICATION_COMMAND // 2
-InteractionType::MESSAGE_COMPONENT // 3
-InteractionType::APPLICATION_COMMAND_AUTOCOMPLETE // 4
-InteractionType::MODAL_SUBMIT // 5
-```
-
-### Tempcord\Enums\ChannelType
-
-Channel types.
-
-```php
-ChannelType::GUILD_TEXT // 0
-ChannelType::DM // 1
-ChannelType::GUILD_VOICE // 2
-ChannelType::GROUP_DM // 3
-ChannelType::GUILD_CATEGORY // 4
-ChannelType::GUILD_NEWS // 5
-ChannelType::GUILD_STORE // 6
-ChannelType::GUILD_NEWS_THREAD // 10
-ChannelType::GUILD_PUBLIC_THREAD // 11
-ChannelType::GUILD_PRIVATE_THREAD // 12
-ChannelType::GUILD_STAGE_VOICE // 13
-ChannelType::GUILD_DIRECTORY // 14
-ChannelType::GUILD_FORUM // 15
-```
-
-## Event Classes
-
-### Tempcord\Events\MessageCreateEvent
-
-Fired when a message is created.
-
-```php
-public readonly Message $message;
-public readonly ?Guild $guild;
-public readonly Channel $channel;
-public readonly User $author;
-```
-
-### Tempcord\Events\MessageUpdateEvent
-
-Fired when a message is updated.
-
-```php
-public readonly Message $message;
-public readonly ?Message $oldMessage;
-public readonly ?Guild $guild;
-public readonly Channel $channel;
-```
-
-### Tempcord\Events\MessageDeleteEvent
-
-Fired when a message is deleted.
-
-```php
-public readonly string $messageId;
-public readonly string $channelId;
-public readonly ?string $guildId;
-```
-
-### Tempcord\Events\GuildCreateEvent
-
-Fired when the bot joins a guild.
-
-```php
-public readonly Guild $guild;
-public readonly bool $unavailable;
-```
-
-### Tempcord\Events\GuildUpdateEvent
-
-Fired when a guild is updated.
-
-```php
-public readonly Guild $guild;
-public readonly ?Guild $oldGuild;
-```
-
-### Tempcord\Events\GuildDeleteEvent
-
-Fired when the bot leaves a guild.
-
-```php
-public readonly string $guildId;
-public readonly bool $unavailable;
-```
-
-### Tempcord\Events\GuildMemberAddEvent
-
-Fired when a member joins a guild.
-
-```php
-public readonly Member $member;
-public readonly Guild $guild;
-```
-
-### Tempcord\Events\GuildMemberUpdateEvent
-
-Fired when a member is updated.
-
-```php
-public readonly Member $member;
-public readonly ?Member $oldMember;
-public readonly Guild $guild;
-```
-
-### Tempcord\Events\GuildMemberRemoveEvent
-
-Fired when a member leaves a guild.
-
-```php
-public readonly User $user;
-public readonly string $guildId;
-```
-
-## Interface Classes
-
-### Tempcord\Middleware\MiddlewareInterface
-
-Interface for middleware classes.
-
-```php
-interface MiddlewareInterface
-{
- public function handle(Interaction $interaction, callable $next): mixed;
-}
-```
-
-### Tempcord\Events\EventListenerInterface
-
-Interface for event listeners.
-
-```php
-interface EventListenerInterface
-{
- public function handle(object $event): void;
-}
-```
-
-## Utility Classes
-
-### Tempcord\Utils\EmbedBuilder
-
-Builder for Discord embeds.
-
-```php
-$embed = EmbedBuilder::create()
- ->setTitle('Hello World')
- ->setDescription('This is an embed')
- ->setColor(0x00ff00)
- ->addField('Field 1', 'Value 1', true)
- ->addField('Field 2', 'Value 2', true)
- ->setFooter('Footer text', 'https://example.com/icon.png')
- ->setTimestamp()
- ->build();
-```
-
-#### Methods
-
-```php
-public static function create(): self
-public function setTitle(?string $title): self
-public function setDescription(?string $description): self
-public function setUrl(?string $url): self
-public function setTimestamp(?DateTime $timestamp = null): self
-public function setColor(?int $color): self
-public function setFooter(?string $text, ?string $iconUrl = null): self
-public function setImage(?string $url): self
-public function setThumbnail(?string $url): self
-public function setAuthor(?string $name, ?string $url = null, ?string $iconUrl = null): self
-public function addField(string $name, string $value, bool $inline = false): self
-public function setFields(array $fields): self
-public function build(): array
-```
-
-### Tempcord\Utils\ComponentBuilder
-
-Builder for Discord components.
-
-```php
-$components = ComponentBuilder::create()
- ->addButton('primary', 'click_me', 'Click Me!')
- ->addButton('secondary', 'cancel', 'Cancel')
- ->addSelectMenu('select_option', 'Choose an option', [
- ['label' => 'Option 1', 'value' => 'opt1'],
- ['label' => 'Option 2', 'value' => 'opt2'],
- ])
- ->build();
-```
-
-#### Methods
-
-```php
-public static function create(): self
-public function addButton(string $style, string $customId, string $label, ?string $emoji = null, bool $disabled = false): self
-public function addLinkButton(string $url, string $label, ?string $emoji = null, bool $disabled = false): self
-public function addSelectMenu(string $customId, string $placeholder, array $options, int $minValues = 1, int $maxValues = 1): self
-public function addTextInput(string $customId, string $label, string $style = 'short', bool $required = true): self
-public function newRow(): self
-public function build(): array
-```
-
-### Tempcord\Utils\PermissionCalculator
-
-Utility for permission calculations.
-
-```php
-$calculator = new PermissionCalculator();
-
-// Check if user has permission
-$hasPermission = $calculator->hasPermission($member, Permission::MANAGE_MESSAGES);
-
-// Calculate effective permissions
-$permissions = $calculator->calculatePermissions($member, $channel);
-
-// Check multiple permissions
-$hasAll = $calculator->hasAllPermissions($member, [
- Permission::SEND_MESSAGES,
- Permission::EMBED_LINKS,
-]);
-```
-
-#### Methods
-
-```php
-public function hasPermission(Member $member, Permission $permission, ?Channel $channel = null): bool
-public function hasAllPermissions(Member $member, array $permissions, ?Channel $channel = null): bool
-public function hasAnyPermission(Member $member, array $permissions, ?Channel $channel = null): bool
-public function calculatePermissions(Member $member, ?Channel $channel = null): array
-public function isAdmin(Member $member): bool
-```
-
-## Helper Functions
-
-### Global Helpers
-
-```php
-// Configuration
-config(string $key, mixed $default = null): mixed
-
-// Logging
-logger(): LoggerInterface
-
-// Cache
-cache(): CacheInterface
-
-// Queue
-queue(): QueueInterface
-
-// Application paths
-app_path(string $path = ''): string
-base_path(string $path = ''): string
-config_path(string $path = ''): string
-storage_path(string $path = ''): string
-database_path(string $path = ''): string
-
-// Environment
-env(string $key, mixed $default = null): mixed
-
-// Time
-now(): DateTime
-today(): DateTime
-
-// Strings
-str_random(int $length = 16): string
-str_slug(string $title, string $separator = '-'): string
-
-// Arrays
-array_get(array $array, string $key, mixed $default = null): mixed
-array_set(array &$array, string $key, mixed $value): void
-array_forget(array &$array, string $key): void
-
-// Validation
-validate(array $data, array $rules): array
-
-// Response helpers
-response(): ResponseFactory
-redirect(string $to = null): RedirectResponse
-
-// Discord helpers
-discord(): DiscordClient
-bot(): BotInstance
-```
-
-## Error Classes
-
-### Tempcord\Exceptions\TempcordException
-
-Base exception class.
-
-### Tempcord\Exceptions\CommandException
-
-Thrown when command execution fails.
-
-### Tempcord\Exceptions\ValidationException
-
-Thrown when validation fails.
-
-### Tempcord\Exceptions\PermissionException
-
-Thrown when permission check fails.
-
-### Tempcord\Exceptions\RateLimitException
-
-Thrown when rate limit is exceeded.
-
-### Tempcord\Exceptions\ConfigurationException
-
-Thrown when configuration is invalid.
-
-## Constants
-
-### Version Information
-
-```php
-Tempcord::VERSION // Current version
-Tempcord::DISCORD_API_VERSION // Discord API version
-Tempcord::USER_AGENT // HTTP User-Agent
-```
-
-### Limits
-
-```php
-Tempcord::MAX_EMBED_TITLE_LENGTH // 256
-Tempcord::MAX_EMBED_DESCRIPTION_LENGTH // 4096
-Tempcord::MAX_EMBED_FIELD_NAME_LENGTH // 256
-Tempcord::MAX_EMBED_FIELD_VALUE_LENGTH // 1024
-Tempcord::MAX_EMBED_FOOTER_LENGTH // 2048
-Tempcord::MAX_EMBED_AUTHOR_LENGTH // 256
-Tempcord::MAX_EMBED_FIELDS // 25
-Tempcord::MAX_EMBED_TOTAL_LENGTH // 6000
-
-Tempcord::MAX_MESSAGE_LENGTH // 2000
-Tempcord::MAX_COMMAND_NAME_LENGTH // 32
-Tempcord::MAX_COMMAND_DESCRIPTION_LENGTH // 100
-Tempcord::MAX_OPTION_NAME_LENGTH // 32
-Tempcord::MAX_OPTION_DESCRIPTION_LENGTH // 100
-Tempcord::MAX_CHOICE_NAME_LENGTH // 100
-Tempcord::MAX_CHOICE_VALUE_LENGTH // 100
-
-Tempcord::MAX_COMPONENTS_PER_ROW // 5
-Tempcord::MAX_ROWS_PER_MESSAGE // 5
-Tempcord::MAX_SELECT_OPTIONS // 25
-Tempcord::MAX_BUTTON_LABEL_LENGTH // 80
-Tempcord::MAX_SELECT_PLACEHOLDER_LENGTH // 150
-```
-
-## Type Definitions
-
-### Command Handler
-
-```php
-type CommandHandler = callable(Interaction): void
-```
-
-### Event Handler
-
-```php
-type EventHandler = callable(object): void
-```
-
-### Middleware Handler
-
-```php
-type MiddlewareHandler = callable(Interaction, callable): mixed
-```
-
-### Option Choice
-
-```php
-type OptionChoice = [
- 'name' => string,
- 'value' => string|int|float,
- 'name_localizations' => ?array,
-]
-```
-
-### Embed Field
-
-```php
-type EmbedField = [
- 'name' => string,
- 'value' => string,
- 'inline' => ?bool,
-]
-```
-
-### Component
-
-```php
-type Component = [
- 'type' => int,
- 'custom_id' => ?string,
- 'disabled' => ?bool,
- 'style' => ?int,
- 'label' => ?string,
- 'emoji' => ?array,
- 'url' => ?string,
- 'options' => ?array,
- 'placeholder' => ?string,
- 'min_values' => ?int,
- 'max_values' => ?int,
- 'min_length' => ?int,
- 'max_length' => ?int,
- 'required' => ?bool,
- 'value' => ?string,
-]
-```
-
-This API reference provides comprehensive documentation for all public classes, methods, and interfaces in Tempcord. For more detailed examples and usage patterns, refer to the other documentation files and the examples directory.
\ No newline at end of file
diff --git a/docs/commands.md b/docs/commands.md
deleted file mode 100644
index f8c9e6d..0000000
--- a/docs/commands.md
+++ /dev/null
@@ -1,427 +0,0 @@
-# Commands
-
-Commands are the primary way users interact with your Discord bot. Tempcord provides a powerful and flexible command system that supports both slash commands and traditional text commands.
-
-## Basic Commands
-
-### Creating a Command
-
-Create a command by defining a class with the `#[Command]` attribute:
-
-```php
-reply('Hello there! 👋');
- }
-}
-```
-
-### Command Attributes
-
-- `#[Command('name')]` - Defines the command name
-- `#[Description('text')]` - Provides a description for the command
-- `#[Option()]` - Defines command options/parameters
-
-## Command Options
-
-Add parameters to your commands using the `#[Option]` attribute:
-
-```php
-getOption('user');
- $message = $interaction->getOption('message') ?? 'Hello';
-
- $interaction->reply("{$message}, {$user->mention()}!");
- }
-}
-```
-
-### Option Types
-
-- `OptionType::STRING` - Text input
-- `OptionType::INTEGER` - Whole numbers
-- `OptionType::NUMBER` - Decimal numbers
-- `OptionType::BOOLEAN` - True/false values
-- `OptionType::USER` - Discord user
-- `OptionType::CHANNEL` - Discord channel
-- `OptionType::ROLE` - Discord role
-- `OptionType::MENTIONABLE` - User, role, or channel
-- `OptionType::ATTACHMENT` - File attachment
-
-### Option Parameters
-
-```php
-#[Option(
- name: 'option_name',
- type: OptionType::STRING,
- description: 'Option description',
- required: true,
- choices: ['choice1', 'choice2'],
- minValue: 1,
- maxValue: 100,
- minLength: 1,
- maxLength: 50
-)]
-```
-
-## Command Groups
-
-Organize related commands into groups:
-
-```php
-getOption('key');
- $value = $interaction->getOption('value');
-
- // Set configuration logic
- $interaction->reply("Set {$key} to {$value}");
- }
-
- #[Subcommand('get')]
- #[Description('Get a configuration value')]
- #[Option('key', OptionType::STRING, 'Configuration key', required: true)]
- public function get(Interaction $interaction): void
- {
- $key = $interaction->getOption('key');
-
- // Get configuration logic
- $interaction->reply("Value for {$key}: ...");
- }
-}
-```
-
-## Response Types
-
-### Basic Responses
-
-```php
-// Simple text response
-$interaction->reply('Hello!');
-
-// Ephemeral response (only visible to the user)
-$interaction->reply('Secret message', ephemeral: true);
-
-// Deferred response (for long-running operations)
-$interaction->defer();
-// ... do work ...
-$interaction->followUp('Operation completed!');
-```
-
-### Rich Responses
-
-```php
-use Tempcord\Discord\Embed;
-use Tempcord\Discord\Button;
-use Tempcord\Discord\ActionRow;
-
-// Embed response
-$embed = new Embed(
- title: 'Command Result',
- description: 'This is an embedded response',
- color: 0x00ff00
-);
-
-$interaction->reply(embeds: [$embed]);
-
-// Response with buttons
-$button = new Button(
- style: ButtonStyle::PRIMARY,
- label: 'Click me!',
- customId: 'my_button'
-);
-
-$actionRow = new ActionRow([$button]);
-
-$interaction->reply(
- content: 'Choose an option:',
- components: [$actionRow]
-);
-```
-
-## Command Validation
-
-### Built-in Validation
-
-Tempcord automatically validates:
-- Required options
-- Option types
-- Min/max values and lengths
-- Choice constraints
-
-### Custom Validation
-
-```php
-getOption('age');
-
- if ($age < 13) {
- throw new ValidationException('You must be at least 13 years old.');
- }
-
- if ($age > 120) {
- throw new ValidationException('Please enter a realistic age.');
- }
-
- $interaction->reply("Your age has been set to {$age}.");
- }
-}
-```
-
-## Error Handling
-
-```php
-performRiskyOperation();
- $interaction->reply('Operation successful!');
- } catch (Exception $e) {
- $interaction->reply(
- 'An error occurred: ' . $e->getMessage(),
- ephemeral: true
- );
- }
- }
-
- private function performRiskyOperation(): void
- {
- // Implementation
- }
-}
-```
-
-## Command Permissions
-
-### Discord Permissions
-
-```php
-isAdmin($interaction->user)) {
- $interaction->reply('You do not have permission to use this command.', ephemeral: true);
- return;
- }
-
- // Admin-only logic
- }
-
- private function isAdmin($user): bool
- {
- // Custom admin check logic
- return in_array($user->id, config('admin_users'));
- }
-}
-```
-
-## Command Registration
-
-### Automatic Registration
-
-By default, Tempcord automatically discovers and registers commands in the `App\Commands` namespace.
-
-### Manual Registration
-
-```php
-// In your bootstrap file
-use Tempcord\CommandRegistry;
-use App\Commands\MyCommand;
-
-$registry = new CommandRegistry();
-$registry->register(MyCommand::class);
-```
-
-### Conditional Registration
-
-```php
- [
- AdminOnlyMiddleware::class,
- AuditLogMiddleware::class,
- ],
- ],
-
- // Bot owners (can bypass certain restrictions)
- owners: explode(',', env('BOT_OWNERS', '')),
-
- // Guild-specific settings
- guilds: [
- 'default' => [
- 'prefix' => '!',
- 'features' => ['commands', 'events'],
- ],
- ],
-
- // Cache configuration
- cache: [
- 'driver' => env('CACHE_DRIVER', 'file'),
- 'ttl' => 3600,
- ],
-
- // Logging configuration
- logging: [
- 'level' => env('LOG_LEVEL', 'info'),
- 'channels' => ['single', 'discord'],
- ],
-);
-```
-
-## Environment Variables
-
-Create a `.env` file in your project root:
-
-```env
-# Discord Configuration
-DISCORD_TOKEN=your_bot_token_here
-DISCORD_APPLICATION_ID=your_application_id_here
-DISCORD_PUBLIC_KEY=your_public_key_here
-
-# Bot Configuration
-BOT_OWNERS=123456789012345678,987654321098765432
-BOT_PREFIX=!
-
-# Environment
-APP_ENV=local
-APP_DEBUG=true
-APP_URL=http://localhost
-
-# Database
-DB_CONNECTION=mysql
-DB_HOST=127.0.0.1
-DB_PORT=3306
-DB_DATABASE=tempcord
-DB_USERNAME=root
-DB_PASSWORD=
-
-# Cache
-CACHE_DRIVER=redis
-REDIS_HOST=127.0.0.1
-REDIS_PASSWORD=null
-REDIS_PORT=6379
-
-# Logging
-LOG_CHANNEL=stack
-LOG_LEVEL=debug
-
-# Queue
-QUEUE_CONNECTION=redis
-
-# Session
-SESSION_DRIVER=redis
-SESSION_LIFETIME=120
-```
-
-## Discord Application Setup
-
-### 1. Create Discord Application
-
-1. Go to [Discord Developer Portal](https://discord.com/developers/applications)
-2. Click "New Application"
-3. Give your application a name
-4. Navigate to the "Bot" section
-5. Click "Add Bot"
-6. Copy the bot token to your `.env` file
-
-### 2. Configure Bot Permissions
-
-In the "Bot" section, configure:
-
-- **Privileged Gateway Intents** (if needed):
- - Presence Intent
- - Server Members Intent
- - Message Content Intent
-
-- **Bot Permissions**:
- - Send Messages
- - Use Slash Commands
- - Read Message History
- - Add Reactions
- - Embed Links
- - Attach Files
-
-### 3. OAuth2 Setup
-
-In the "OAuth2" section:
-
-1. Select "bot" and "applications.commands" scopes
-2. Select required permissions
-3. Use the generated URL to invite your bot
-
-## Configuration Options
-
-### Core Settings
-
-```php
-return new TempcordConfig(
- // Required Discord credentials
- token: env('DISCORD_TOKEN'),
- applicationId: env('DISCORD_APPLICATION_ID'),
- publicKey: env('DISCORD_PUBLIC_KEY'),
-
- // Optional settings
- debug: env('APP_DEBUG', false),
- environment: env('APP_ENV', 'production'),
- timezone: env('APP_TIMEZONE', 'UTC'),
-);
-```
-
-### Command Configuration
-
-```php
-return new TempcordConfig(
- // Command discovery paths
- commandPaths: [
- app_path('Commands'),
- app_path('Modules/*/Commands'),
- ],
-
- // Command registration settings
- commandRegistration: [
- 'auto_register' => true,
- 'global_commands' => true,
- 'guild_commands' => [],
- 'delete_missing' => false,
- ],
-
- // Command defaults
- commandDefaults: [
- 'ephemeral' => false,
- 'defer' => false,
- 'timeout' => 30,
- ],
-);
-```
-
-### Event Configuration
-
-```php
-return new TempcordConfig(
- // Event listener discovery paths
- eventPaths: [
- app_path('Events'),
- app_path('Modules/*/Events'),
- ],
-
- // Event settings
- events: [
- 'auto_discover' => true,
- 'async_processing' => true,
- 'max_listeners' => 100,
- ],
-
- // Gateway intents
- intents: [
- 'guilds',
- 'guild_messages',
- 'guild_message_reactions',
- 'direct_messages',
- ],
-);
-```
-
-### Middleware Configuration
-
-```php
-return new TempcordConfig(
- // Global middleware (applied to all commands)
- globalMiddleware: [
- LoggingMiddleware::class,
- RateLimitMiddleware::class,
- MaintenanceModeMiddleware::class,
- ],
-
- // Middleware groups
- middlewareGroups: [
- 'admin' => [
- AdminOnlyMiddleware::class,
- AuditLogMiddleware::class,
- ],
- 'public' => [
- CooldownMiddleware::class,
- ValidationMiddleware::class,
- ],
- ],
-
- // Middleware settings
- middleware: [
- 'auto_discover' => true,
- 'priority_sorting' => true,
- ],
-);
-```
-
-### Database Configuration
-
-```php
-return new TempcordConfig(
- // Database settings
- database: [
- 'connection' => env('DB_CONNECTION', 'mysql'),
- 'migrations_path' => database_path('migrations'),
- 'auto_migrate' => env('AUTO_MIGRATE', false),
- ],
-
- // Model settings
- models: [
- 'auto_discover' => true,
- 'paths' => [
- app_path('Models'),
- ],
- ],
-);
-```
-
-### Cache Configuration
-
-```php
-return new TempcordConfig(
- // Cache settings
- cache: [
- 'driver' => env('CACHE_DRIVER', 'file'),
- 'prefix' => env('CACHE_PREFIX', 'tempcord'),
- 'ttl' => 3600,
-
- // Driver-specific settings
- 'redis' => [
- 'host' => env('REDIS_HOST', '127.0.0.1'),
- 'port' => env('REDIS_PORT', 6379),
- 'password' => env('REDIS_PASSWORD'),
- 'database' => env('REDIS_DB', 0),
- ],
-
- 'file' => [
- 'path' => storage_path('cache'),
- ],
- ],
-);
-```
-
-### Logging Configuration
-
-```php
-return new TempcordConfig(
- // Logging settings
- logging: [
- 'default' => env('LOG_CHANNEL', 'stack'),
- 'level' => env('LOG_LEVEL', 'info'),
-
- 'channels' => [
- 'stack' => [
- 'driver' => 'stack',
- 'channels' => ['single', 'discord'],
- ],
-
- 'single' => [
- 'driver' => 'single',
- 'path' => storage_path('logs/tempcord.log'),
- 'level' => 'debug',
- ],
-
- 'discord' => [
- 'driver' => 'discord',
- 'webhook_url' => env('DISCORD_LOG_WEBHOOK'),
- 'level' => 'error',
- ],
- ],
- ],
-);
-```
-
-### Queue Configuration
-
-```php
-return new TempcordConfig(
- // Queue settings
- queue: [
- 'default' => env('QUEUE_CONNECTION', 'sync'),
-
- 'connections' => [
- 'sync' => [
- 'driver' => 'sync',
- ],
-
- 'redis' => [
- 'driver' => 'redis',
- 'connection' => 'default',
- 'queue' => 'default',
- 'retry_after' => 90,
- ],
- ],
- ],
-);
-```
-
-## Guild-Specific Configuration
-
-```php
-return new TempcordConfig(
- // Guild-specific settings
- guilds: [
- // Default settings for all guilds
- 'default' => [
- 'prefix' => '!',
- 'features' => ['commands', 'events', 'moderation'],
- 'permissions' => [
- 'admin_roles' => [],
- 'mod_roles' => [],
- 'banned_users' => [],
- ],
- ],
-
- // Specific guild overrides
- '123456789012345678' => [
- 'prefix' => '?',
- 'features' => ['commands', 'events'],
- 'disabled_commands' => ['admin', 'moderation'],
- ],
-
- '987654321098765432' => [
- 'prefix' => '!',
- 'features' => ['commands'],
- 'custom_settings' => [
- 'welcome_channel' => '123456789012345678',
- 'log_channel' => '987654321098765432',
- ],
- ],
- ],
-);
-```
-
-## Feature Flags
-
-```php
-return new TempcordConfig(
- // Feature flags
- features: [
- 'slash_commands' => true,
- 'context_menus' => true,
- 'message_commands' => false,
- 'auto_complete' => true,
- 'modals' => true,
- 'select_menus' => true,
- 'buttons' => true,
-
- // Experimental features
- 'experimental' => [
- 'voice_support' => false,
- 'ai_integration' => false,
- 'advanced_permissions' => true,
- ],
- ],
-);
-```
-
-## Performance Configuration
-
-```php
-return new TempcordConfig(
- // Performance settings
- performance: [
- 'command_cache' => true,
- 'event_cache' => true,
- 'middleware_cache' => true,
-
- 'limits' => [
- 'max_commands' => 100,
- 'max_events' => 50,
- 'max_middleware' => 20,
- 'command_timeout' => 30,
- 'event_timeout' => 10,
- ],
-
- 'optimization' => [
- 'lazy_loading' => true,
- 'preload_commands' => false,
- 'compress_responses' => true,
- ],
- ],
-);
-```
-
-## Security Configuration
-
-```php
-return new TempcordConfig(
- // Security settings
- security: [
- 'verify_signatures' => true,
- 'rate_limiting' => [
- 'enabled' => true,
- 'max_requests' => 60,
- 'window_seconds' => 60,
- ],
-
- 'permissions' => [
- 'strict_mode' => true,
- 'require_permissions' => true,
- 'check_bot_permissions' => true,
- ],
-
- 'validation' => [
- 'sanitize_input' => true,
- 'validate_options' => true,
- 'max_input_length' => 2000,
- ],
- ],
-);
-```
-
-## Development Configuration
-
-```php
-return new TempcordConfig(
- // Development settings
- development: [
- 'hot_reload' => env('APP_DEBUG', false),
- 'debug_mode' => env('APP_DEBUG', false),
- 'profiling' => env('APP_DEBUG', false),
-
- 'testing' => [
- 'mock_discord' => env('MOCK_DISCORD', false),
- 'fake_interactions' => env('FAKE_INTERACTIONS', false),
- ],
-
- 'debugging' => [
- 'log_all_interactions' => false,
- 'dump_payloads' => false,
- 'trace_middleware' => false,
- ],
- ],
-);
-```
-
-## Configuration Validation
-
-Tempcord automatically validates your configuration on startup:
-
-```php
-// Custom validation rules
-return new TempcordConfig(
- // ... your config
-
- validation: [
- 'strict' => true,
- 'rules' => [
- 'token' => 'required|string|min:50',
- 'applicationId' => 'required|string|size:18',
- 'owners' => 'array',
- 'owners.*' => 'string|size:18',
- ],
- ],
-);
-```
-
-## Environment-Specific Configuration
-
-### Local Development
-
-```php
-// config/tempcord.local.php
-return [
- 'debug' => true,
- 'logging' => [
- 'level' => 'debug',
- ],
- 'cache' => [
- 'driver' => 'file',
- ],
- 'development' => [
- 'hot_reload' => true,
- 'profiling' => true,
- ],
-];
-```
-
-### Production
-
-```php
-// config/tempcord.production.php
-return [
- 'debug' => false,
- 'logging' => [
- 'level' => 'warning',
- ],
- 'cache' => [
- 'driver' => 'redis',
- ],
- 'performance' => [
- 'command_cache' => true,
- 'optimization' => [
- 'lazy_loading' => true,
- 'preload_commands' => true,
- ],
- ],
-];
-```
-
-## Configuration Helpers
-
-### Accessing Configuration
-
-```php
-// Get configuration value
-$token = config('tempcord.token');
-$debug = config('tempcord.debug', false);
-
-// Get nested configuration
-$cacheDriver = config('tempcord.cache.driver');
-$logLevel = config('tempcord.logging.level');
-
-// Check if feature is enabled
-if (config('tempcord.features.slash_commands')) {
- // Slash commands are enabled
-}
-```
-
-### Dynamic Configuration
-
-```php
-// Set configuration at runtime
-config(['tempcord.debug' => true]);
-
-// Merge configuration
-config()->merge('tempcord', [
- 'custom_setting' => 'value',
-]);
-```
-
-## Best Practices
-
-### Security
-- Never commit sensitive values to version control
-- Use environment variables for secrets
-- Validate all configuration values
-- Use secure defaults
-
-### Performance
-- Cache configuration in production
-- Use lazy loading for large configurations
-- Minimize configuration file size
-- Profile configuration loading
-
-### Maintainability
-- Use descriptive configuration keys
-- Group related settings
-- Document configuration options
-- Use type hints and validation
-
-### Environment Management
-- Use different configurations per environment
-- Validate environment-specific settings
-- Use feature flags for gradual rollouts
-- Monitor configuration changes
-
-## Troubleshooting
-
-### Common Issues
-
-1. **Invalid Discord Token**
- ```
- Error: Invalid token provided
- Solution: Check your DISCORD_TOKEN in .env
- ```
-
-2. **Missing Permissions**
- ```
- Error: Missing Access
- Solution: Check bot permissions in Discord
- ```
-
-3. **Configuration Not Found**
- ```
- Error: Configuration file not found
- Solution: Ensure config/tempcord.php exists
- ```
-
-### Debug Configuration
-
-```php
-// Enable debug mode
-config(['tempcord.debug' => true]);
-
-// Dump configuration
-dd(config('tempcord'));
-
-// Validate configuration
-Tempcord::validateConfig();
-```
-
-## Examples
-
-Check out the `examples/configuration/` directory for complete configuration examples and common setups.
\ No newline at end of file
diff --git a/docs/events.md b/docs/events.md
deleted file mode 100644
index 15e9a76..0000000
--- a/docs/events.md
+++ /dev/null
@@ -1,544 +0,0 @@
-# Events
-
-Events allow your bot to respond to various activities that happen in Discord, such as messages being sent, users joining servers, or reactions being added to messages.
-
-## Event Listeners
-
-### Creating an Event Listener
-
-Create event listeners using the `#[EventListener]` attribute:
-
-```php
-message;
-
- // Ignore bot messages
- if ($message->author->bot) {
- return;
- }
-
- // Respond to specific content
- if ($message->content === 'hello') {
- $message->reply('Hello there!');
- }
- }
-}
-```
-
-### Event Method Naming
-
-Event listener methods should follow the pattern `on{EventName}`:
-
-- `onMessageCreate` for `MessageCreate` events
-- `onGuildMemberAdd` for `GuildMemberAdd` events
-- `onReactionAdd` for `ReactionAdd` events
-
-## Available Events
-
-### Message Events
-
-#### MessageCreate
-Triggered when a message is sent:
-
-```php
-use Tempcord\Events\MessageCreate;
-
-#[EventListener]
-public function onMessageCreate(MessageCreate $event): void
-{
- $message = $event->message;
- $author = $message->author;
- $content = $message->content;
- $channel = $message->channel;
- $guild = $message->guild;
-}
-```
-
-#### MessageUpdate
-Triggered when a message is edited:
-
-```php
-use Tempcord\Events\MessageUpdate;
-
-#[EventListener]
-public function onMessageUpdate(MessageUpdate $event): void
-{
- $oldMessage = $event->oldMessage; // May be null if not cached
- $newMessage = $event->newMessage;
-}
-```
-
-#### MessageDelete
-Triggered when a message is deleted:
-
-```php
-use Tempcord\Events\MessageDelete;
-
-#[EventListener]
-public function onMessageDelete(MessageDelete $event): void
-{
- $message = $event->message; // May be null if not cached
- $messageId = $event->messageId;
- $channelId = $event->channelId;
- $guildId = $event->guildId;
-}
-```
-
-### Guild Events
-
-#### GuildMemberAdd
-Triggered when a user joins a server:
-
-```php
-use Tempcord\Events\GuildMemberAdd;
-
-#[EventListener]
-public function onGuildMemberAdd(GuildMemberAdd $event): void
-{
- $member = $event->member;
- $guild = $member->guild;
- $user = $member->user;
-
- // Send welcome message
- $welcomeChannel = $guild->getChannel('welcome-channel-id');
- $welcomeChannel->send("Welcome {$user->mention()} to {$guild->name}!");
-}
-```
-
-#### GuildMemberRemove
-Triggered when a user leaves a server:
-
-```php
-use Tempcord\Events\GuildMemberRemove;
-
-#[EventListener]
-public function onGuildMemberRemove(GuildMemberRemove $event): void
-{
- $user = $event->user;
- $guild = $event->guild;
-
- // Log the departure
- $logChannel = $guild->getChannel('log-channel-id');
- $logChannel->send("{$user->tag} has left the server.");
-}
-```
-
-#### GuildCreate
-Triggered when the bot joins a new server:
-
-```php
-use Tempcord\Events\GuildCreate;
-
-#[EventListener]
-public function onGuildCreate(GuildCreate $event): void
-{
- $guild = $event->guild;
-
- // Setup default configuration for new guild
- $this->setupGuildDefaults($guild);
-}
-```
-
-### Reaction Events
-
-#### ReactionAdd
-Triggered when a reaction is added to a message:
-
-```php
-use Tempcord\Events\ReactionAdd;
-
-#[EventListener]
-public function onReactionAdd(ReactionAdd $event): void
-{
- $reaction = $event->reaction;
- $user = $event->user;
- $message = $reaction->message;
- $emoji = $reaction->emoji;
-
- // Role reactions
- if ($message->id === 'role-message-id' && $emoji->name === '🎮') {
- $role = $message->guild->getRole('gamer-role-id');
- $member = $message->guild->getMember($user->id);
- $member->addRole($role);
- }
-}
-```
-
-#### ReactionRemove
-Triggered when a reaction is removed from a message:
-
-```php
-use Tempcord\Events\ReactionRemove;
-
-#[EventListener]
-public function onReactionRemove(ReactionRemove $event): void
-{
- $reaction = $event->reaction;
- $user = $event->user;
- $emoji = $reaction->emoji;
-
- // Remove role when reaction is removed
- if ($emoji->name === '🎮') {
- $role = $reaction->message->guild->getRole('gamer-role-id');
- $member = $reaction->message->guild->getMember($user->id);
- $member->removeRole($role);
- }
-}
-```
-
-### Voice Events
-
-#### VoiceStateUpdate
-Triggered when a user's voice state changes:
-
-```php
-use Tempcord\Events\VoiceStateUpdate;
-
-#[EventListener]
-public function onVoiceStateUpdate(VoiceStateUpdate $event): void
-{
- $oldState = $event->oldState;
- $newState = $event->newState;
- $member = $newState->member;
-
- // User joined a voice channel
- if (!$oldState->channel && $newState->channel) {
- // Handle voice channel join
- }
-
- // User left a voice channel
- if ($oldState->channel && !$newState->channel) {
- // Handle voice channel leave
- }
-
- // User moved between channels
- if ($oldState->channel && $newState->channel && $oldState->channel->id !== $newState->channel->id) {
- // Handle voice channel move
- }
-}
-```
-
-## Event Filtering
-
-### Guild-Specific Listeners
-
-Listen to events only from specific guilds:
-
-```php
-use Tempcord\Attributes\EventListener;
-use Tempcord\Attributes\GuildOnly;
-use Tempcord\Events\MessageCreate;
-
-class GuildMessageListener
-{
- #[EventListener]
- #[GuildOnly(['guild-id-1', 'guild-id-2'])]
- public function onMessageCreate(MessageCreate $event): void
- {
- // Only processes messages from specified guilds
- }
-}
-```
-
-### Channel-Specific Listeners
-
-Listen to events only from specific channels:
-
-```php
-use Tempcord\Attributes\EventListener;
-use Tempcord\Attributes\ChannelOnly;
-use Tempcord\Events\MessageCreate;
-
-class ChannelMessageListener
-{
- #[EventListener]
- #[ChannelOnly(['channel-id-1', 'channel-id-2'])]
- public function onMessageCreate(MessageCreate $event): void
- {
- // Only processes messages from specified channels
- }
-}
-```
-
-### Conditional Listeners
-
-Use custom conditions to filter events:
-
-```php
-use Tempcord\Attributes\EventListener;
-use Tempcord\Events\MessageCreate;
-
-class ConditionalListener
-{
- #[EventListener]
- public function onMessageCreate(MessageCreate $event): void
- {
- $message = $event->message;
-
- // Only process messages with attachments
- if (empty($message->attachments)) {
- return;
- }
-
- // Only process messages from non-bots
- if ($message->author->bot) {
- return;
- }
-
- // Process the message
- $this->processMessageWithAttachment($message);
- }
-}
-```
-
-## Event Priority
-
-Control the order in which event listeners are executed:
-
-```php
-use Tempcord\Attributes\EventListener;
-use Tempcord\Attributes\Priority;
-use Tempcord\Events\MessageCreate;
-
-class HighPriorityListener
-{
- #[EventListener]
- #[Priority(100)] // Higher numbers = higher priority
- public function onMessageCreate(MessageCreate $event): void
- {
- // This runs before lower priority listeners
- }
-}
-
-class LowPriorityListener
-{
- #[EventListener]
- #[Priority(10)] // Lower numbers = lower priority
- public function onMessageCreate(MessageCreate $event): void
- {
- // This runs after higher priority listeners
- }
-}
-```
-
-## Async Event Handling
-
-For long-running event handlers, use async processing:
-
-```php
-use Tempcord\Attributes\EventListener;
-use Tempcord\Attributes\Async;
-use Tempcord\Events\MessageCreate;
-
-class AsyncListener
-{
- #[EventListener]
- #[Async]
- public function onMessageCreate(MessageCreate $event): void
- {
- // This runs asynchronously and won't block other events
- $this->performLongRunningTask($event->message);
- }
-
- private function performLongRunningTask($message): void
- {
- // Long-running operation like API calls, file processing, etc.
- sleep(5); // This won't block other events
- }
-}
-```
-
-## Error Handling
-
-### Try-Catch in Listeners
-
-```php
-use Tempcord\Attributes\EventListener;
-use Tempcord\Events\MessageCreate;
-use Exception;
-
-class SafeListener
-{
- #[EventListener]
- public function onMessageCreate(MessageCreate $event): void
- {
- try {
- $this->processMessage($event->message);
- } catch (Exception $e) {
- // Log the error
- logger()->error('Error processing message', [
- 'message_id' => $event->message->id,
- 'error' => $e->getMessage(),
- ]);
-
- // Optionally notify administrators
- $this->notifyAdmins($e, $event->message);
- }
- }
-}
-```
-
-### Global Error Handling
-
-Configure global error handling for events:
-
-```php
-// In your configuration
-use Tempcord\Config\TempcordConfig;
-
-return new TempcordConfig(
- // ... other config
- eventErrorHandler: function (Exception $e, $event) {
- logger()->error('Event handler error', [
- 'event' => get_class($event),
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString(),
- ]);
- }
-);
-```
-
-## Event Data Access
-
-### Accessing Discord Objects
-
-```php
-use Tempcord\Events\MessageCreate;
-
-#[EventListener]
-public function onMessageCreate(MessageCreate $event): void
-{
- $message = $event->message;
-
- // Message properties
- $content = $message->content;
- $id = $message->id;
- $timestamp = $message->timestamp;
- $editedTimestamp = $message->editedTimestamp;
- $attachments = $message->attachments;
- $embeds = $message->embeds;
- $mentions = $message->mentions;
- $reactions = $message->reactions;
-
- // Author information
- $author = $message->author;
- $authorId = $author->id;
- $authorTag = $author->tag;
- $authorAvatar = $author->avatar;
-
- // Channel information
- $channel = $message->channel;
- $channelId = $channel->id;
- $channelName = $channel->name;
- $channelType = $channel->type;
-
- // Guild information (if in a guild)
- if ($message->guild) {
- $guild = $message->guild;
- $guildId = $guild->id;
- $guildName = $guild->name;
- $member = $message->member; // Guild member object
- }
-}
-```
-
-## Custom Events
-
-Create your own custom events:
-
-```php
-getGuild($event->guildId);
- $user = discord()->getUser($event->userId);
-
- $channel = $guild->getChannel('general');
- $channel->send("🎉 Congratulations {$user->mention()}! You reached level {$event->newLevel}!");
- }
-}
-```
-
-## Best Practices
-
-### Performance
-- Keep event handlers lightweight
-- Use async processing for heavy operations
-- Cache frequently accessed data
-- Avoid blocking operations in event handlers
-
-### Error Handling
-- Always wrap risky operations in try-catch blocks
-- Log errors with sufficient context
-- Don't let one event handler failure affect others
-- Implement graceful degradation
-
-### Security
-- Validate event data before processing
-- Check permissions before performing actions
-- Rate limit event-triggered actions
-- Sanitize user input from events
-
-### Organization
-- Group related listeners in the same class
-- Use descriptive method names
-- Keep listeners focused on single responsibilities
-- Document complex event handling logic
-
-## Examples
-
-Check out the `examples/events/` directory for more event handling examples and patterns.
\ No newline at end of file
diff --git a/docs/getting-started.md b/docs/getting-started.md
deleted file mode 100644
index 4d1f44e..0000000
--- a/docs/getting-started.md
+++ /dev/null
@@ -1,158 +0,0 @@
-# Getting Started with Tempcord
-
-Welcome to Tempcord! This guide will help you get up and running with your first Discord bot using the Tempcord framework.
-
-## Prerequisites
-
-Before you begin, make sure you have:
-
-- PHP 8.2 or higher
-- Composer installed
-- A Discord application and bot token
-- Basic knowledge of PHP and Discord bots
-
-## Installation
-
-### Creating a New Project
-
-The easiest way to get started is by creating a new project using Composer:
-
-```bash
-composer create-project tempcord/tempcord my-discord-bot
-cd my-discord-bot
-```
-
-### Adding to Existing Project
-
-If you want to add Tempcord to an existing project:
-
-```bash
-composer require tempcord/tempcord
-```
-
-## Discord Application Setup
-
-1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
-2. Click "New Application" and give it a name
-3. Go to the "Bot" section and click "Add Bot"
-4. Copy the bot token (you'll need this later)
-5. Under "Privileged Gateway Intents", enable the intents your bot needs
-
-## Configuration
-
-### Environment Setup
-
-Copy the example environment file:
-
-```bash
-cp .env.example .env
-```
-
-Edit the `.env` file with your Discord bot credentials:
-
-```env
-DISCORD_TOKEN=your_bot_token_here
-DISCORD_CLIENT_ID=your_client_id_here
-```
-
-### Basic Configuration
-
-Create or edit `app/config/tempcord.config.php`:
-
-```php
-reply('🏓 Pong!');
- }
-}
-```
-
-## Running Your Bot
-
-Start your bot with:
-
-```bash
-php tempcord start
-```
-
-You should see output indicating that your bot is connecting to Discord.
-
-## Testing Your Bot
-
-1. Invite your bot to a Discord server using the OAuth2 URL generator in the Discord Developer Portal
-2. In a channel where your bot has permissions, type `/ping`
-3. Your bot should respond with "🏓 Pong!"
-
-## Next Steps
-
-Now that you have a basic bot running, you can:
-
-- [Learn about Commands](commands.md)
-- [Explore Event Handling](events.md)
-- [Add Middleware](middleware.md)
-- [Configure Advanced Settings](configuration.md)
-
-## Troubleshooting
-
-### Common Issues
-
-**Bot doesn't respond to commands:**
-- Check that your bot token is correct
-- Ensure the bot has the necessary permissions in your Discord server
-- Verify that the required intents are enabled
-
-**"Invalid token" error:**
-- Double-check your bot token in the `.env` file
-- Make sure there are no extra spaces or characters
-
-**Commands not registering:**
-- Ensure `autoRegisterCommands` is set to `true` in your config
-- Check that your command classes are in the correct namespace
-- Verify that your command methods have the proper attributes
-
-### Getting Help
-
-If you're still having issues:
-
-1. Check the [API Reference](api-reference.md)
-2. Look through existing [GitHub Issues](https://github.com/tempcord/tempcord/issues)
-3. Join our Discord community for support
-4. Create a new issue if you've found a bug
-
-## What's Next?
-
-Congratulations! You've successfully created your first Tempcord bot. Continue reading the documentation to learn about more advanced features and best practices.
\ No newline at end of file
diff --git a/docs/guides/01-getting-started.md b/docs/guides/01-getting-started.md
new file mode 100644
index 0000000..81971ca
--- /dev/null
+++ b/docs/guides/01-getting-started.md
@@ -0,0 +1,83 @@
+# Getting started
+
+Tempcord builds Discord bots on top of [Tempest](https://tempestphp.com). A command is a
+class, its options are typed parameters, and the framework works out the rest at boot.
+
+## Requirements
+
+- PHP 8.5 or newer
+- A Discord application with a bot token
+- The intents your bot needs, enabled in the Discord developer portal
+
+## Installing
+
+```bash
+composer create-project tempcord/tempcord my-bot
+cd my-bot
+```
+
+Copy the environment file and add your token:
+
+```bash
+cp .env.example .env
+```
+
+```env
+DISCORD_TOKEN=your_bot_token_here
+```
+
+## Your first command
+
+A command is a class carrying `#[Command]`. If it declares an `__invoke` method, that method
+handles it, and each parameter marked `#[Option]` becomes an option Discord shows the user.
+
+```php
+use Ragnarok\Fenrir\Interaction\CommandInteraction;
+use Tempcord\Attributes\Command;
+use Tempcord\Attributes\Option;
+
+#[Command(description: 'Replies with pong')]
+final class PingCommand
+{
+ public function __invoke(
+ CommandInteraction $interaction,
+ #[Option(description: 'Who to greet')] string $name,
+ #[Option(description: 'How many times')] int $times = 1,
+ ): string {
+ return $name . ':' . $times;
+ }
+}
+```
+
+From [`tests/Fixtures/PingCommand.php`](../../tests/Fixtures/PingCommand.php) — compiled and exercised by the test suite.
+
+The name comes from the class — `PingCommand` becomes `/ping` — with a `Command` prefix or
+suffix stripped and the rest snake_cased. Pass `name:` to choose it yourself.
+
+Whether an option is required comes from whether its parameter has a default. Here `name` is
+required and `times` is not.
+
+## Registering and running
+
+Registration replaces the whole set of commands each time, so a command you delete from your
+code disappears from Discord rather than lingering there:
+
+```bash
+./tempcord boot --register
+```
+
+Once registered, boot without the flag:
+
+```bash
+./tempcord boot
+```
+
+Registration needs a request to Discord, so run it when your commands change rather than on
+every start.
+
+## Where to go next
+
+- [Commands and options](02-commands.md) — subcommands, groups, and every option constraint
+- [Autocomplete](03-autocomplete.md) — suggesting values as the user types
+- [Events](04-events.md) — reacting to what happens on the gateway
+- [Translations](05-translations.md) — showing commands in the user's own language
diff --git a/docs/guides/02-commands.md b/docs/guides/02-commands.md
new file mode 100644
index 0000000..0736036
--- /dev/null
+++ b/docs/guides/02-commands.md
@@ -0,0 +1,163 @@
+# Commands and options
+
+## Subcommands
+
+A class whose methods carry `#[Subcommand]` exposes each one as a subcommand. There is no
+`__invoke` in this case — the methods are the handlers.
+
+```php
+use Tempcord\Attributes\Command;
+use Tempcord\Attributes\Option;
+use Tempcord\Attributes\Subcommand;
+
+#[Command(description: 'Moderation tools')]
+final class ModerationCommand
+{
+ #[Subcommand(name: 'kick', description: 'Kick a member')]
+ public function kick(
+ #[Option(description: 'Reason for the kick')] string $reason,
+ ): string {
+ return 'kicked: ' . $reason;
+ }
+}
+```
+
+From [`tests/Fixtures/ModerationCommand.php`](../../tests/Fixtures/ModerationCommand.php) — compiled and exercised by the test suite.
+
+That registers `/moderation kick`.
+
+## Grouping subcommands
+
+Adding `#[SubcommandGroup]` to the class nests everything one level deeper, which is how
+Discord models `/command group subcommand`.
+
+```php
+use Tempcord\Attributes\Command;
+use Tempcord\Attributes\Option;
+use Tempcord\Attributes\Subcommand;
+use Tempcord\Attributes\SubcommandGroup;
+
+#[Command(description: 'Music controls')]
+#[SubcommandGroup(name: 'playlist', description: 'Playlist controls')]
+final class MusicCommand
+{
+ #[Subcommand(name: 'play', description: 'Play a track')]
+ public function play(
+ #[Option(description: 'Track title')] string $title,
+ ): string {
+ return 'playing ' . $title;
+ }
+
+ #[Subcommand(name: 'stop', description: 'Stop playback')]
+ public function stop(): string
+ {
+ return 'stopped';
+ }
+
+ public function notASubcommand(): void {}
+}
+```
+
+From [`tests/Fixtures/MusicCommand.php`](../../tests/Fixtures/MusicCommand.php) — compiled and exercised by the test suite.
+
+That registers `/music playlist play` and `/music playlist stop`. A method without
+`#[Subcommand]` is ignored, so helpers can sit alongside handlers.
+
+## Option types
+
+The Discord option type is read from the parameter's PHP type:
+
+| PHP type | Discord option type |
+| --- | --- |
+| `string` | STRING |
+| `int` | INTEGER |
+| `float` | NUMBER |
+| `bool` | BOOLEAN |
+| `Ragnarok\Fenrir\Parts\User` | USER |
+| `Ragnarok\Fenrir\Parts\Channel` | CHANNEL |
+| `Ragnarok\Fenrir\Parts\Role` | ROLE |
+
+A parameter typed `User`, `Channel` or `Role` is fetched from Discord before your handler
+runs, so you receive the entity rather than an id.
+
+An unsupported type fails at boot rather than on the first interaction that reaches it.
+
+## Receiving the interaction
+
+A parameter named `$interaction` receives the `CommandInteraction`, which is how you reply.
+It needs no attribute.
+
+## Constraining what users may send
+
+Discord can enforce constraints before your handler is ever called, which is cheaper and
+gives the user immediate feedback.
+
+```php
+use Ragnarok\Fenrir\Enums\ChannelType;
+use Ragnarok\Fenrir\Parts\Channel;
+use Tempcord\Attributes\Command;
+use Tempcord\Attributes\Option;
+
+#[Command(description: 'Every option constraint Discord accepts')]
+final class ConstrainedCommand
+{
+ public function __invoke(
+ #[Option(description: 'A labelled set', choices: ['Small' => 's', 'Large' => 'l'])]
+ string $size,
+ #[Option(description: 'A bare list', choices: ['red', 'green'])]
+ string $colour,
+ #[Option(description: 'Bounded number', minValue: 1, maxValue: 10)]
+ int $count,
+ #[Option(description: 'Bounded text', minLength: 2, maxLength: 32)]
+ string $note,
+ #[Option(description: 'Text channels only', channelTypes: [ChannelType::GUILD_TEXT])]
+ Channel $channel,
+ ): void {}
+}
+```
+
+From [`tests/Fixtures/ConstrainedCommand.php`](../../tests/Fixtures/ConstrainedCommand.php) — compiled and exercised by the test suite.
+
+Choices accept either shape. A map uses its keys as the labels users see; a list has no
+labels of its own, so each value stands in as its own.
+
+## Restricting who may use a command
+
+`permissions` sets the default a member needs. An empty list leaves the command unrestricted.
+
+```php
+use Ragnarok\Fenrir\Enums\Permission;
+use Tempcord\Attributes\Command;
+
+#[Command(
+ description: 'Only for moderators',
+ permissions: [Permission::KICK_MEMBERS, Permission::BAN_MEMBERS],
+)]
+final class RestrictedCommand
+{
+ public function __invoke(): void {}
+}
+```
+
+From [`tests/Fixtures/RestrictedCommand.php`](../../tests/Fixtures/RestrictedCommand.php) — compiled and exercised by the test suite.
+
+Server administrators can override this per guild, so treat it as a default rather than a
+security boundary.
+
+## Scoping a command to one guild
+
+`guildId` registers a command in a single guild instead of globally. Guild commands appear
+immediately, which makes them useful while developing; global commands can take up to an
+hour to propagate.
+
+```php
+use Tempcord\Attributes\Command;
+
+#[Command(name: 'alpha', description: 'Alpha, scoped to guild 111', guildId: 111)]
+final class GuildAlphaCommand
+{
+ public function __invoke(): void {}
+}
+```
+
+From [`tests/Fixtures/GuildAlphaCommand.php`](../../tests/Fixtures/GuildAlphaCommand.php) — compiled and exercised by the test suite.
diff --git a/docs/guides/03-autocomplete.md b/docs/guides/03-autocomplete.md
new file mode 100644
index 0000000..88fca59
--- /dev/null
+++ b/docs/guides/03-autocomplete.md
@@ -0,0 +1,69 @@
+# Autocomplete
+
+Autocomplete suggests values while the user is still typing, before they submit the command.
+
+## The built-in
+
+`ArrayAutocomplete` filters a fixed list by what has been typed so far.
+
+```php
+use Ragnarok\Fenrir\Interaction\CommandInteraction;
+use Tempcord\Attributes\Command;
+use Tempcord\Attributes\Option;
+use Tempcord\AutoCompletes\ArrayAutocomplete;
+
+#[Command(description: 'Suggests as you type')]
+final class SearchCommand
+{
+ public function __invoke(
+ CommandInteraction $interaction,
+ #[Option(description: 'What to look for', autocomplete: new ArrayAutocomplete(['alpha', 'beta', 'gamma']))]
+ string $query,
+ #[Option(description: 'Plain option with no suggestions')] string $note = '',
+ ): void {}
+}
+```
+
+From [`tests/Fixtures/SearchCommand.php`](../../tests/Fixtures/SearchCommand.php) — compiled and exercised by the test suite.
+
+## Writing your own
+
+Anything implementing `Autocomplete` can supply suggestions, which is what you want when
+they come from a database or an API.
+
+```php
+use Ragnarok\Fenrir\Interaction\CommandInteraction;
+use Tempcord\Interfaces\Autocomplete;
+
+final readonly class TrackAutocomplete implements Autocomplete
+{
+ public function __construct(
+ private TrackRepository $tracks,
+ ) {}
+
+ public function handle(CommandInteraction $interaction, mixed $value): array
+ {
+ return $this->tracks->matching((string) $value);
+ }
+}
+```
+
+## What you may return
+
+| Return | Result |
+| --- | --- |
+| A list | Each value is shown as its own label |
+| A map | Keys are the labels users see, values are what your handler receives |
+| A single scalar | One suggestion |
+| `ApplicationCommandOptionChoice` objects | Passed through untouched |
+
+Discord accepts at most 25 choices and rejects a response carrying more, so anything beyond
+that is dropped before sending.
+
+## Timing
+
+Discord expects an autocomplete response within about three seconds. Keep the work small,
+and cache rather than querying on every keystroke.
+
+Note that an autocomplete implementation is constructed as part of the attribute, so it is
+rebuilt whenever the command tree is read. Hold configuration in it, not a warm cache.
diff --git a/docs/guides/04-events.md b/docs/guides/04-events.md
new file mode 100644
index 0000000..386c626
--- /dev/null
+++ b/docs/guides/04-events.md
@@ -0,0 +1,57 @@
+# Events
+
+Events let a bot react to what happens on the gateway rather than to a command.
+
+## Listening
+
+An invokable class carrying `#[Event]` becomes a listener. The event name is the gateway
+event you want; the payload arrives as the single argument.
+
+```php
+use Tempcord\Attributes\Event;
+
+#[Event(name: 'READY')]
+final class ReadyListener
+{
+ /** @var list