Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3 #134

Open
wants to merge 102 commits into
base: main
Choose a base branch
from
Open

v3 #134

wants to merge 102 commits into from

Conversation

Log1x
Copy link
Member

@Log1x Log1x commented Feb 6, 2025

What started off as a weekend hobby project has grown significantly in features and had become a slight burden to maintain and contribute to due to minimal modularization and excessive logic residing in a single Laracord.php god class.

Today we right those wrongs. Laracord v3 is a near complete refactor of the architecture of the framework with (hopefully) minimal breaking changes. It boasts plugin support, a full custom console implementation utilizing proper ReactPHP streams (props to @QWp6t), proper logging support, improved error rendering/handling, command/interaction middleware using pipelines, full DI support in component handlers, improved bot configuration, basic sharding support, proper command cooldown support, and more.

Prior to v3, the Laracord skeleton shipped with a Bot.php that extended the parent Laracord class allowing you to override methods. This has been removed and all configuration is now done within' the BotServiceProvider. You can still technically override the Laracord class, but you'd need to do it within the container and a service provider and I'd generally recommend against it.

This PR closes #131 #121 #119 #71 #67 #65 #64 #18 #124 #129 #132 #53 so far.

❤️ If you're able, please consider supporting my development on Laracord: https://github.com/sponsors/log1x

Try It Out

$ composer create-project laracord/laracord:dev-next

Configuration

<?php

namespace App\Providers;

use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Routing\Router;
use Laracord\Bot\Hook;
use Laracord\Laracord;
use Laracord\LaracordServiceProvider;
use Example\ExamplePlugin;

class BotServiceProvider extends LaracordServiceProvider
{
    /**
     * Configure the bot instance.
     */
    public function bot(Laracord $bot): Laracord
    {
        return $bot
            ->plugins([
                new ExamplePlugin,
            ])
            ->registerHook(Hook::AFTER_BOOT, function (Laracord $bot) {
                $bot->logger->info('Laracord is ready!');
            })
            ->registerCommands([
                //
            ])
            ->registerSlashCommands([
                //
            ])
            ->registerContextMenus([
                //
            ])
            ->registerEvents([
                //
            ])
            ->registerServices([
                //
            ])
            ->registerCommandMiddlewares([
                //
            ])
            ->registerPrompts([
                //
            ])
            ->withRoutes(function (Router $router) {
                //
            })
            ->withMiddleware(function (Middleware $middleware) {
                //
            });
    }
}

Plugins

I find the introduction of plugins exciting as it gives Laracord the possibility of having its own little ecosystem. Plugin's have the same capabilities as you would in the bot provider but using the new Plugin interface:

<?php

namespace Example\ExamplePlugin;

use Laracord\Contracts\Plugin;
use Laracord\Laracord;

class ExamplePlugin implements Plugin
{
    /**
     * Register the plugin with the bot.
     */
    public function plugin(Laracord $bot): Laracord
    {
        return $bot
            ->registerCommands([
                //
            ])
            ...
            ->registerSlashCommands([
                //
            ]);
    }
}

You would then register the plugin in your BotServiceProvider.php.

Components are also able to be discovered via directory/namespace with Laracord registering the following defaults:

$bot
    ->discoverCommands(in: app_path('Commands'), for: 'App\\Commands')
    ->discoverSlashCommands(in: app_path('SlashCommands'), for: 'App\\SlashCommands')
    ->discoverContextMenus(in: app_path('Menus'), for: 'App\\Menus')
    ->discoverEvents(in: app_path('Events'), for: 'App\\Events')
    ->discoverServices(in: app_path('Services'), for: 'App\\Services');

The above component discovery (and a lot of other changes) are heavily inspired by the Filament team. Huge shoutout to them for easing some of my pain in figuring out where I wanted to go with the structure of the project.

Console

Already existing in Laracord were (undocumented) built-in commands you could execute in the bot's console while it was running such as status. For v3, this will now be a documented and you can now make your own bot console commands called Prompts using laracord make:prompt and registering them in your BotServiceProvider.

Note

Sorry Windows users, you will have to use WSL.

<?php

namespace App\Console\Prompts;

use Laracord\Console\Console;
use Laracord\Console\Prompts\Prompt;
use Laracord\Laracord;

class ExamplePrompt extends Prompt
{
    /**
     * The name of the prompt.
     *
     * @var string
     */
    protected $name = 'example';

    /**
     * Handle the prompt.
     */
    public function handle(Console $console, Laracord $bot): void
    {
        $console->info('Hello world!');
    }
}

Logging

With the new console implementation, I was able to implement proper logging support rendering logs as you'd expect them in both the console as well as the configured Laravel logger (defaults to laracord.log).

With this change, you will likely want to move any console logging to the logger() instead of console():

- $this->console()->log('Hello world');
+ $this->logger()->info('Hello world');

When rendering in the console, logging is now a little more colorful with it properly distinguishing debug and other logs with a unique color:

Screenshot

Error Handling

With the above, I was able to get rid of hack-y solutions such as Laracord::handleSafe() and now depend on Laravel's native rescue() helper when handling exceptions throughout the framework. This will then properly pass the exception through a try/catch and to our logger which now renders in console. This is great because we can now take advantage of Collision for error rendering and promise rejection while inside the loop while also logging the stack trace to the configured logger:

Screenshot 2

Command Middleware

Commands can now have individual or global middleware. This allows for implementations such as global rate limiting, etc. without overriding parent classes or resorting to less than desirable methods.

Take a simple example where if the command is ping– we short-circuit it's handler and give a different message instead using the new Message facade:

<?php

namespace App\Commands\Middleware;

use Closure;
use Laracord\Commands\Middleware\Context;
use Laracord\Commands\Middleware\Middleware;
use Laracord\Discord\Facades\Message;

class ExampleMiddleware implements Middleware
{
    /**
     * Handle the command.
     *
     * @return mixed
     */
    public function handle(Context $context, Closure $next)
    {
        if ($context->isCommand() && $context->command->getName() === 'ping') {
            Message::content('Not this time!')->reply($context->source);

            return;
        }

        return $next($context);
    }
}

Basic Sharding Support

I'm talkin' real basic here. In the future I'd like to implement a ShardManager that allows for IPC through child processes but today is not that day. For now, you can now pass your shard id/count during boot:

$ laracord bot:boot --shard-id=0 --shard-count=3
$ laracord bot:boot --shard-id=1 --shard-count=3
$ laracord bot:boot --shard-id=2 --shard-count=3

You can pass a custom token using --token= now too.

Remaining Tasks

  • Implement proper hook registration.
  • Improve User model handling.
  • Implement a non-blocking file logging channel.
  • Add file log rotation.
  • Add ability to set the denied handler
  • Add ability to customize the default !help command
  • Do a PR for Laracord skeleton. (v3 laracord#16)
  • Create a plugin skeleton.
  • Write a migration guide.
  • Update documentation.
  • ???

Log1x added 30 commits February 5, 2025 23:11
🔧 Add missing DiscordPHP options to the `discord.php` config
🎨 Utilize the `Laracord` facade when fetching token while resolving a user
🔊 Utilize the logger instead of console for logs
🧑‍💻 Improve HTTP server middleware implementation
🧑‍💻 Implement command component discovery
🎨 Improve command registration
🧑‍💻 Implement context menu component discovery
🎨 Improve context menu registration
🧑‍💻 Implement event component discovery
🎨 Improve event registration
🧑‍💻 Implement service component discovery
🎨 Improve service registration
🧑‍💻 Implement slash command component discovery
🎨 Improve slash command registration
🏗️ Move interaction logic to a trait
🧑‍💻 Improve command cooldown support (Fixes #18)
🎨 Improve abstract command structure/types
🏗️ Move Discord logic to a trait
✨ Add configuration methods for routing/middleware
🏗️ Move HTTP server boot/configuration logic to a trait
✨ Implement `Prompts` for interacting with the bot console while running
🏗️ Move console logic to a trait
@Log1x Log1x linked an issue Feb 8, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment