--- /dev/null
+<?php
+
+namespace App\DiscordBotCommands;
+
+use App\Models\DiscordBotCommand;
+use App\Models\Round;
+use App\Models\User;
+use Discord\Discord;
+use Discord\Parts\Channel\Channel;
+use Discord\Parts\Guild\Guild;
+use Discord\Parts\User\Member;
+use Illuminate\Support\Facades\App;
+
+abstract class BaseCommand {
+
+ public static function resolve(Discord $discord, DiscordBotCommand $cmd) {
+ switch ($cmd->command) {
+ case 'presence':
+ return new PresenceCommand($discord, $cmd);
+ case 'result':
+ return new ResultCommand($discord, $cmd);
+ default:
+ throw new Exception('unrecognized command');
+ }
+ }
+
+ public abstract function execute();
+
+
+ protected function __construct(Discord $discord, DiscordBotCommand $cmd) {
+ $this->discord = $discord;
+ $this->command = $cmd;
+ }
+
+ protected function fetchGuild() {
+ if (isset($this->guild)) {
+ return \React\Promise\resolve($this->guild);
+ }
+ return $this->discord->guilds
+ ->fetch($this->command->tournament->discord)
+ ->then(function (Guild $guild) {
+ $this->guild = $guild;
+ if ($guild->preferred_locale) {
+ App::setLocale($guild->preferred_locale);
+ }
+ return $guild;
+ });
+ }
+
+ protected function fetchMember() {
+ return $this->fetchGuild()->then(function (Guild $guild) {
+ return $guild->members
+ ->fetch($this->getParameter('user'))
+ ->then(function (Member $member) {
+ $this->member = $member;
+ return $member;
+ });
+ });
+ }
+
+ protected function fetchRoundChannel() {
+ if (isset($this->roundChannel)) {
+ return \React\Promise\resolve($this->roundChannel);
+ }
+ return $this->fetchGuild()
+ ->then(function (Guild $guild) {
+ $channel = $guild->channels->find(function (Channel $c) {
+ return $c->name == $this->getRoundChannelName();
+ });
+ if ($channel) {
+ return $channel;
+ }
+ $channel = $guild->channels->create([
+ 'name' => $this->getRoundChannelName(),
+ 'is_private' => true,
+ 'parent_id' => $this->command->tournament->discord_round_category,
+ ]);
+ return $guild->channels->save($channel);
+ })
+ ->then(function (Channel $channel) {
+ $this->roundChannel = $channel;
+ return $channel;
+ });
+ }
+
+
+ protected function getParameter($name) {
+ return $this->command->parameters[$name];
+ }
+
+ protected function getRound() {
+ if (!$this->hasParameter('round')) {
+ throw new \Exception('no rounds in parameters');
+ }
+ return Round::findOrFail($this->getParameter('round'));
+ }
+
+ protected function getRoundChannelName() {
+ $round = $this->getRound();
+ return sprintf($this->command->tournament->discord_round_template, $round->number);
+ }
+
+ protected function getUser() {
+ if (!$this->hasParameter('user')) {
+ throw new \Exception('no user in parameters');
+ }
+ return User::findOrFail($this->getParameter('user'));
+ }
+
+ protected function hasParameter($name) {
+ return array_key_exists($name, $this->command->parameters);
+ }
+
+ protected function hasRoundChannels() {
+ return !empty($this->command->tournament->discord_round_template);
+ }
+
+
+ protected $command;
+ protected $discord;
+
+ protected $guild = null;
+ protected $member = null;
+ protected $roundChannel = null;
+
+}
--- /dev/null
+<?php
+
+namespace App\DiscordBotCommands;
+
+use App\Models\DiscordBotCommand;
+use Discord\Discord;
+use Discord\Parts\User\Activity;
+use React\Promise\Promise;
+
+class PresenceCommand extends BaseCommand {
+
+ public function __construct(Discord $discord, DiscordBotCommand $cmd) {
+ parent::__construct($discord, $cmd);
+ }
+
+ public function execute() {
+ return new Promise(function($resolve) {
+ $activity = null;
+ $idle = false;
+ $status = 'online';
+ $afk = false;
+ if ($this->hasParameter('activity')) {
+ $activity = new Activity($this->discord);
+ $activity->type = $this->getParameter('activity');
+ if ($this->hasParameter('name')) {
+ $activity->name = $this->getParameter('name');
+ }
+ if ($this->hasParameter('url')) {
+ $activity->url = $this->getParameter('url');
+ }
+ }
+ if ($this->hasParameter('idle')) {
+ $idle = $this->parameters['idle'];
+ }
+ if ($this->hasParameter('status')) {
+ $status = $this->getParameter('status');
+ }
+ if ($this->hasParameter('afk')) {
+ $afk = $this->getParameter('afk');
+ }
+ $this->discord->updatePresence($activity, $idle, $status, $afk);
+ $resolve();
+ });
+ }
+
+}
--- /dev/null
+<?php
+
+namespace App\DiscordBotCommands;
+
+use App\Models\DiscordBotCommand;
+use Discord\Discord;
+use Discord\Parts\Channel\Channel;
+use Discord\Parts\Channel\Message;
+use Discord\Parts\User\Member;
+
+class ResultCommand extends BaseCommand {
+
+ public function __construct(Discord $discord, DiscordBotCommand $cmd) {
+ parent::__construct($discord, $cmd);
+ }
+
+ public function execute() {
+ if (!$this->hasRoundChannels()) {
+ return \React\Promise\resolve();
+ }
+ return $this->fetchRoundChannel()
+ ->then(function (Channel $channel) {
+ return $this->fetchMember();
+ })
+ ->then(function (Member $member) {
+ return $this->roundChannel->setPermissions($member, [
+ 'view_channel',
+ ]);
+ })
+ ->then(function () {
+ $user = $this->getUser();
+ $round = $this->getRound();
+ $result = $user->findResult($round);
+ $msg = '';
+ if (!$result || $result->forfeit) {
+ $msg = __('discord_commands.result.forfeit', ['name' => $user->getName()]);
+ } else {
+ $msg = __('discord_commands.result.finish', ['name' => $user->getName(), 'time' => $result->formatTime()]);
+ }
+ return $this->roundChannel->sendMessage($msg);
+ });
+ }
+
+}
namespace App\Models;
+use App\DiscordBotCommands\BaseCommand;
use Discord\Discord;
-use Discord\Parts\User\Activity;
+use Discord\Parts\Channel\Channel;
+use Discord\Parts\Guild\Guild;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
{
use HasFactory;
+ public function tournament() {
+ return $this->belongsTo(Tournament::class);
+ }
+
+ public function user() {
+ return $this->belongsTo(User::class);
+ }
+
public function execute(Discord $discord) {
- $this->status = 'executing';
- $this->executed_at = now();
- $this->save();
+ $this->setExecuting();
try {
- switch ($this->command) {
- case 'presence':
- $this->executePresence($discord);
- break;
- default:
- throw new Exception('unrecognized command');
- }
- $this->status = 'done';
- $this->save();
+ BaseCommand::resolve($discord, $this)
+ ->execute()
+ ->otherwise(function (\Throwable $e) {
+ $this->setException($e);
+ })
+ ->done(function($v = null) {
+ $this->setDone();
+ });
} catch (\Exception $e) {
- $this->status = 'exception';
- $this->result = [
- 'type' => get_class($e),
- 'message' => $e->getMessage(),
- ];
- $this->save();
+ $this->setException($e);
}
}
- protected function executePresence(Discord $discord) {
- $activity = null;
- $idle = false;
- $status = 'online';
- $afk = false;
- if (isset($this->parameters['activity'])) {
- $activity = new Activity($discord);
- $activity->type = $this->parameters['activity'];
- if (isset($this->parameters['name'])) {
- $activity->name = $this->parameters['name'];
- }
- if (isset($this->parameters['url'])) {
- $activity->url = $this->parameters['url'];
- }
- }
- if (isset($this->parameters['idle'])) {
- $idle = $this->parameters['idle'];
- }
- if (isset($this->parameters['status'])) {
- $status = $this->parameters['status'];
- }
- if (isset($this->parameters['afk'])) {
- $afk = $this->parameters['afk'];
- }
- $discord->updatePresence($activity, $idle, $status, $afk);
+
+ private function setDone() {
+ $this->status = 'done';
+ $this->save();
}
+ private function setExecuting() {
+ $this->status = 'executing';
+ $this->executed_at = now();
+ $this->save();
+ }
+
+ private function setException(\Throwable $e) {
+ $this->status = 'exception';
+ $this->result = [
+ 'type' => get_class($e),
+ 'file' => $e->getFile(),
+ 'line' => $e->getLine(),
+ 'message' => $e->getMessage(),
+ ];
+ $this->save();
+ }
+
+
protected $casts = [
'parameters' => 'array',
'result' => 'array',
use HasFactory;
+ public function formatTime() {
+ $hours = floor($this->time / 60 / 60);
+ $minutes = floor(($this->time / 60) % 60);
+ $seconds = floor($this->time % 60);
+ return sprintf('%d:%02d:%02d', $hours, $minutes, $seconds);
+ }
+
public function updateResult($time, $forfeit) {
$this->time = $time;
$this->forfeit = $forfeit;
{
use HasApiTokens, HasFactory, Notifiable;
+ public function findResult(Round $round) {
+ foreach ($round->results as $result) {
+ if ($this->id == $result->user_id) {
+ return $result;
+ }
+ }
+ return null;
+ }
+
+ public function getName() {
+ if (!empty($this->nickname)) {
+ return $this->nickname;
+ }
+ return $this->username;
+ }
+
+
public function isAdmin() {
return $this->role === 'admin';
}
--- /dev/null
+<?php
+
+return [
+
+ 'result' => [
+ 'finish' => ':name hat mit einer Zeit von :time abgeschlossen.',
+ 'forfeit' => ':name hat aufgegeben.',
+ ],
+
+];
--- /dev/null
+<?php
+
+return [
+
+ 'result' => [
+ 'finish' => ':name has finished with a time of :time.',
+ 'forfeit' => ':name has forfeited.',
+ ],
+
+];