Skip to content

Commit

Permalink
✨ Add input/output stream support (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
Log1x authored Feb 21, 2024
2 parents 1e5987a + 3fac63f commit ae534df
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 31 deletions.
55 changes: 52 additions & 3 deletions src/Discord/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@

class Message
{
/**
* The bot instance.
*/
protected ?Laracord $bot = null;

/**
* The message channel.
*/
Expand Down Expand Up @@ -115,6 +120,11 @@ class Message
*/
protected array $buttons = [];

/**
* The message files.
*/
protected array $files = [];

/**
* The default embed colors.
*/
Expand Down Expand Up @@ -167,6 +177,12 @@ public function build(): MessageBuilder
$message->addComponent($this->getButtons());
}

if ($this->files) {
foreach ($this->files as $file) {
$message->addFileFromContent($file['filename'], $file['content']);
}
}

return $message;
}

Expand All @@ -188,10 +204,10 @@ public function send(mixed $destination = null): void
public function sendTo(mixed $user): void
{
if (is_numeric($user)) {
$member = app('bot')->discord()->users->get('id', $user);
$member = $this->bot->discord()->users->get('id', $user);

if (! $member) {
app('bot')->console()->error("Could not find user <fg=red>{$user}</> to send message");
$this->bot->console()->error("Could not find user <fg=red>{$user}</> to send message");

return;
}
Expand Down Expand Up @@ -284,7 +300,7 @@ public function getChannel(): Channel
public function channel(mixed $channel): self
{
if (is_numeric($channel)) {
$channel = app('bot')->discord()->getChannel($channel);
$channel = $this->bot->discord()->getChannel($channel);
}

if ($channel instanceof ChannelMessage) {
Expand Down Expand Up @@ -360,6 +376,39 @@ public function body(string $body = ''): self
return $this;
}

/**
* Add a file to the message.
*/
public function file(string $path, string $filename = ''): self
{
if (! file_exists($path)) {
$this->bot->console()->error("File <fg=red>{$path}</> does not exist");

return $this;
}

$filename = $filename ?? basename($path);

$this->rawFile(file_get_contents($path), $filename);

return $this;
}

/**
* Add a file from content to the message.
*/
public function rawFile(string $content = '', string $filename = ''): self
{
$filename = $filename ?? 'file.txt';

$this->files[] = [
'content' => $content,
'filename' => $filename,
];

return $this;
}

/**
* Set the message color.
*/
Expand Down
24 changes: 24 additions & 0 deletions src/Http/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@

class Server
{
/**
* The Laracord instance.
*
* @var \Laracord\Laracord
*/
protected $bot;

/**
* The HTTP server instance.
*
Expand Down Expand Up @@ -74,6 +81,23 @@ public function boot(): self
return $this;
}

/**
* Shutdown the HTTP server.
*/
public function shutdown(): void
{
if (! $this->isBooted()) {
return;
}

$this->getServer()->removeAllListeners();
$this->getSocket()->close();

$this->booted = false;

$this->bot->console()->log('The HTTP server has been shutdown');
}

/**
* Retrieve the HTTP server instance.
*
Expand Down
169 changes: 141 additions & 28 deletions src/Laracord.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
use Laracord\Logging\Logger;
use Laracord\Services\Service;
use React\EventLoop\Loop;
use React\Stream\ReadableResourceStream;
use React\Stream\WritableResourceStream;
use ReflectionClass;

use function Laravel\Prompts\table;
Expand Down Expand Up @@ -102,10 +104,24 @@ class Laracord
*/
protected array $services = [];

/**
* The console input stream.
*
* @var \React\Stream\ReadableResourceStream
*/
protected $inputStream;

/**
* The console output stream.
*
* @var \React\Stream\WritableResourceStream
*/
protected $outputStream;

/**
* The bot HTTP server.
*
* @var \React\Http\HttpServer
* @var \Laracord\Http\Server
*/
protected $httpServer;

Expand Down Expand Up @@ -160,6 +176,8 @@ public function boot(): void

$this->bootDiscord();

$this->registerStream();

$this->registerCommands();

$this->discord()->on('ready', function () {
Expand All @@ -173,6 +191,7 @@ public function boot(): void
'command' => count($this->registeredCommands),
'event' => count($this->registeredEvents),
'service' => count($this->registeredServices),
'routes' => count(Route::getRoutes()->getRoutes()),
])
->filter()
->map(function ($count, $type) {
Expand All @@ -189,6 +208,8 @@ public function boot(): void
->showCommands()
->showInvite()
->afterBoot();

$this->outputStream->write('> ');
});

$this->discord()->run();
Expand All @@ -208,6 +229,88 @@ public function bootDiscord(): void
]);
}

/**
* Register the input and output streams.
*/
public function registerStream(): self
{
if ($this->inputStream && $this->outputStream) {
return $this;
}

$this->inputStream = new ReadableResourceStream(STDIN, $this->getLoop());
$this->outputStream = new WritableResourceStream(STDOUT, $this->getLoop());

$this->inputStream->on('data', fn ($data) => $this->handleStream($data));

return $this;
}

/**
* Handle the input stream.
*/
public function handleStream(string $data): void
{
$command = trim($data);

if (! $command) {
$this->outputStream->write('> ');

return;
}

$this->console()->newLine();

match ($command) {
'shutdown', 'exit', 'quit', 'stop' => $this->shutdown(),
'restart' => $this->restart(),
'invite' => $this->showInvite(force: true),
'commands' => $this->showCommands(),
'?' => table(['<fg=blue>Command</>', '<fg=blue>Description</>'], [
['shutdown', 'Shutdown the bot.'],
['restart', 'Restart the bot.'],
['invite', 'Show the invite link.'],
['commands', 'Show the registered commands.'],
]),
default => $this->console()->error("Unknown command: <fg=red>{$command}</>"),
};

$this->outputStream->write('> ');
}

/**
* Shutdown the bot.
*/
public function shutdown(int $code = 0): void
{
$this->console()->log("Shutting down <fg=blue>{$this->getName()}</>.");

$this->httpServer()->shutdown();
$this->discord()->close();

exit($code);
}

/**
* Restart the bot.
*/
public function restart(): void
{
$this->console()->log("<fg=blue>{$this->getName()}</> is restarting.");

$this->httpServer()->shutdown();
$this->discord()->close();

$this->httpServer = null;
$this->discord = null;

$this->registeredCommands = [];
$this->registeredEvents = [];
$this->registeredServices = [];

$this->boot();
}

/**
* Actions to run before booting the bot.
*/
Expand Down Expand Up @@ -543,13 +646,15 @@ public function showCommands(): self
/**
* Show the invite link if the bot is not in any guilds.
*/
public function showInvite(): self
public function showInvite(bool $force = false): self
{
if (! $this->showInvite || $this->discord()->guilds->count() > 0) {
if (! $force && (! $this->showInvite || $this->discord()->guilds->count() > 0)) {
return $this;
}

$this->console()->warn("{$this->getName()} is currently not in any guilds.");
if (! $force) {
$this->console()->warn("{$this->getName()} is currently not in any guilds.");
}

$query = Arr::query([
'client_id' => $this->discord()->id,
Expand Down Expand Up @@ -793,6 +898,34 @@ public function getServicePath(): string
return app_path('Services');
}

/**
* Retrieve the prefixes.
*/
public function getPrefixes(): Collection
{
if ($this->prefixes) {
return $this->prefixes;
}

$prefixes = collect(config('discord.prefix', '!'))
->filter()
->reject(fn ($prefix) => Str::startsWith($prefix, '/'));

if ($prefixes->isEmpty()) {
throw new Exception('You must provide a valid command prefix.');
}

return $this->prefixes = $prefixes;
}

/**
* Retrieve the primary prefix.
*/
public function getPrefix(): string
{
return $this->getPrefixes()->first();
}

/**
* Get the event loop.
*/
Expand All @@ -808,7 +941,7 @@ public function getLoop()
/**
* Get the Discord instance.
*/
public function discord(): Discord
public function discord(): ?Discord
{
return $this->discord;
}
Expand All @@ -822,31 +955,11 @@ public function console(): ConsoleCommand
}

/**
* Retrieve the prefixes.
* Get the HTTP server instance.
*/
public function getPrefixes(): Collection
public function httpServer(): ?Server
{
if ($this->prefixes) {
return $this->prefixes;
}

$prefixes = collect(config('discord.prefix', '!'))
->filter()
->reject(fn ($prefix) => Str::startsWith($prefix, '/'));

if ($prefixes->isEmpty()) {
throw new Exception('You must provide a valid command prefix.');
}

return $this->prefixes = $prefixes;
}

/**
* Retrieve the primary prefix.
*/
public function getPrefix(): string
{
return $this->getPrefixes()->first();
return $this->httpServer;
}

/**
Expand Down

0 comments on commit ae534df

Please sign in to comment.