The unofficial DiscordPHP Bot boilerplate
DiscordPHP Discord Server Only ask questions relevant to using DiscordPHP's own wrapper, not on how to use this.
- Copy
.env.example
to.env
. - Open
.env
and add your bot token, you can also add additional environment variables that pertain to your bot. Such as mysql credentials.
Env::get()->variableName;
Env::get()->variableName = "value";
use function Common\env;
Env::get() === env(); // these do the same thing
- Copy
Events/Example.php
toEvents/NameOfYourEvent.php
- Replace
Event::MESSAGE_CREATE
with the name of your event - Insert the code you'd like to run when that event is triggered into the handler method. When defining arguments that are passed into the event handler make sure to set their default value to null. This is due to how extending abstract classes work as you cannot have required arguments that aren't defined in the parent class.
- If you want your event handler to only run once then change the
$runOnce
property totrue
After following the steps above you should be left with something that looks like this.
<?php
namespace Events;
use Discord\Discord;
use Discord\Parts\Channel\Message;
use Discord\WebSockets\Event;
class MESSAGE_CREATE extends Template {
protected static string $event = Event::MESSAGE_CREATE;
protected static bool $runOnce = false;
public static function handler(Message $message = null, Discord $discord = null): void
{
if ($message->author->bot) {
return;
}
$message->reply("Well I can't read what you said but I'm glad you said something :)");
}
}
Inside index.php
, add the class name to the $env->events
array like so...
// ...
$env->events = [
Events\Ready::class, // DO NOT REMOVE THIS EVENT!
Events\MESSAGE_CREATE::class
];
// ...
- Copy
Commands/Example.php
toCommands/NameOfYourCommand.php
. - Replace
Example
with your command name (for subcommands check Additional notes for subcommands) andExample Command
with the description of your command. - Add the code that will be invoked inside your
handler
method and add any additional command configuration required into thegetConfig
method. Advance users can also return an array rather than using the CommandBuilder. - If your command has autocomplete enabled then put the code relevant to that inside the autocomplete method (if not you can remove the autocomplete method)
- If your command is guild specific then you can change the static property
$guild
to the id of the guild. After completing the steps above you should be left with something similar to...
<?php
namespace Commands;
use Commands\Template;
use Discord\Builders\CommandBuilder;
use Discord\Builders\MessageBuilder;
use Discord\Parts\Interactions\Interaction;
class Ping extends Template {
protected static string|array $name = "ping";
public static function handler(Interaction $interaction): void
{
$interaction->respondWithMessage(MessageBuilder::new()->setContent('pong :ping_pong:'), true);
}
public static function getConfig(): CommandBuilder|array
{
return (new CommandBuilder)
->setName(self::$name)
->setDescription("Ping the bot")
;
}
}
If you're command has subcommands change your getName()
method to look more like...
// ...
public static function getName(): array
{
return ["baseCommandName", ["subCommandName"], ["secondSubCommandName"]]
}
// ...
For subcommand groups your array would look like this
["baseCommandName", ["subCommandGroupName", "subCommandName"], ["subCommandGroupName", "secondSubCommandName"]]
You will also need to modify your getConfig()
method to look like...
// ...
public static function getConfig(): CommandBuilder
{
return (new CommandBuilder)
->setName(self::getName()[0]) // <-- gets base command name
// rest of the config can stay the same
Do not include the subcommand name in the base command class instead create another command class like you would for any other command and the array return in the getName()
method will be...
["baseCommandName", ["subCommandName"]]
You can also change the getConfig()
method to...
public static function getConfig(): array
{
return [];
}
As this method will never be called
Note you will see a warning in the console like this...
Warning caught: The command `{insert your baseCommandName here}` already exists.
If this is about a command already existing for a command you're listening for that has a separate subcommand handler you can safely ignore this :)
You can use the PHP script below to add a command to your application or update an existing one.
php bot sc save commandName
Similar to adding and updating commands to your application you can just swap save with delete.
php bot sc delete commandName
php bot sc deleteall
If your command's path, for example, is Commands/Admin/Ban.php
instead of doing php bot sc action Ban
you would do php bot sc action Admin\\Ban
Note the namespace in Ban.php would have to be Commands\Admin
to work!
In index.php
there is environment variable commands
that's an array. Add the class name of your command like so...
// ...
$env->commands = [
Commands\Ping::class,
Commands\Profile::class,
// ...
];
SPECIAL NOTE: If you have a subcommand that has a different handler than the base command make sure you add it's listener second, for example
public function handler(Discord $discord = null): void
{
$env->commands = [
Commands\Ping::class,
Commands\Profile::class,
Commands\BaseCommand::class,
Commands\SubCommand::class
// ...
];
}
If you do not do this the SubCommand's handler will be used for the baseCommand!!
By default you would have to create an event listener for the INTERACTION_CREATE event and then most likely do a bunch of if-else statements to define your custom handlers. For organization I created an easier way to listen for interactions namespace just like you would regular events.
- Copy
Interactions/Example.php
toInteraction/NameOfYourInteraction.php
- Change the static property
$id
to the name custom_id of your interaction - If you want your Interaction to run more than once you can change the runOnce property to true (you can also remove this property definition if not)
- Added the code you want executed whenever this interaction is triggered within the static handler method. Afterwards you should be left with something like
<?php
namespace Interactions;
use Discord\Builders\MessageBuilder;
use Discord\Parts\Interactions\Interaction;
use Discord\Discord;
class Pong extends Template {
protected static string $id = "Pong";
public static function handler(Interaction $interaction, Discord $discord)
{
$interaction->respondWithMessage(MessageBuilder::new()->setContent('Ping :ping_pong:')->addComponent(\Commands\Ping::getActionRow()), true);
}
}
If you need to store data in the id, separate the id and the data e.g. Ping|5
make sure the id comes first. You can then access these by adding optional args after Discord $discord in the handler method. Also, args are passed in the same order as defined in the id.
// ...
public static function handler(Interaction $interaction, Discord $discord, int $timesPinged)
// ...
You can have multiple columns of data if needed as well. e.g. Ping|5|232224992908017664
// ...
public static function handler(Interaction $interaction, Discord $discord, int $timesPinged, string $userid)
// ...
On index.php
add the interaction classname to $env->interactions
like so...
$env->interactions = [
Interactions\Ping::class,
Interactions\Pong::class
];
composer install
php bot s
(s is an alias for setup)
Enter token
php bot sc save Ping
(sc is an alias for slashcommands)
php index.php
Inside Common/Helpers.php
there's a bunch of utility commands that can make some repetitive tasks easier
function newSlashCommandOption(string $name, string $description, int $type, bool $required = false): Option
Type | Name |
---|---|
string | $name |
string | $description |
int | $type |
bool | $required |
Option
Create a new Option used for building slash commands
function newSlashCommandChoice(string $name, float|int|string $value): Choice
Type | Name |
---|---|
string | $name |
float | int |
Choice
Create a new Choice used for building slash commands
function newDiscordPart(string $class, mixed ...$args): mixed
Type | Name |
---|---|
string | $class |
mixed | ...$args |
mixed
Create a new instance of an object that requires \Discord\Discord
as the first argument
$embed = newDiscordPart("\Discord\Parts\Embed\Embed);
function messageWithContent(string $content): MessageBuilder
Type | Name |
---|---|
string | $content |
MessageBuilder
Create a new MessageBuilder object with the content define for creating simple MessageBuilders quickly
$message = messageWithContent("Hello World");
function buildActionRowWithButtons(Button ...$buttons): ActionRow
Type | Name |
---|---|
Button | ...$buttons |
ActionRow
Quickly build an action row with multiple buttons
$banButton = (new Button(Button::STYLE_DANGER))->setLabel("Ban User");
$kickButton = (new Button(Button::STYLE_DANGER))->setLabel("Kick User");
$actionRow = buildActionRowWithButtons($banButton, $kickButton);
This can also be paired with newButton
$actionRow = buildActionWithButtons(
newButton(Button::STYLE_DANGER, "Ban User")
newButton(Button::STYLE_DANGER, "Kick User")
);
function newButton(int $style, string $label, ?string $custom_id = null): Button
Type | Name |
---|---|
int | $style |
string | $label |
?string | $custom_id |
Button
Quickly create button objects
$button = newButton(Button::STYLE_DANGER, "Kick User", "Kick|Command_String");
function getOptionFromInteraction(Collection|Interaction $options, string ...$names): Option|null
Type | Name |
---|---|
Collection | Interaction |
string | ...$names |
Option|null
Get an option from an Interaction/Interaction Repository by specifying the option(s) name
For regular slash commands
/ban :user
$user = getOptionFromInteraction($interaction, "user");
For sub commands / sub command groups you can stack the names
/admin ban :user
$user = getOptionFromInteraction($interaction->data->options, "ban", "user");
function emptyEmbedField(?Embed $embed = null): array|Embed
Type | Name |
---|---|
?Embed | $embed |
array|Embed
Append to grab and empty array field. You can supply an embed to have the empty field added or
if you leave the $embed
option null
then an array containing the empty field will be returned
$embed = newDiscordPart("\Discord\Parts\Embed\Embed");
emptyEmbedField($embed);
or
$embed = newDiscordPart("\Discord\Parts\Embed\Embed");
$emptyField = emptyEmbedField();
function getDiscord(): Discord
Discord
Retrieve the \Discord\Discord
instance from Environment