From 66e57699b8055ef8a65a1be55b05301c811090bb Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Sun, 1 May 2022 15:04:30 +0200 Subject: [PATCH] discord result command --- app/DiscordBotCommands/BaseCommand.php | 126 +++++++++++++++++++++ app/DiscordBotCommands/PresenceCommand.php | 46 ++++++++ app/DiscordBotCommands/ResultCommand.php | 44 +++++++ app/Models/DiscordBotCommand.php | 87 +++++++------- app/Models/Result.php | 7 ++ app/Models/User.php | 17 +++ lang/de/discord_commands.php | 10 ++ lang/en/discord_commands.php | 10 ++ 8 files changed, 303 insertions(+), 44 deletions(-) create mode 100644 app/DiscordBotCommands/BaseCommand.php create mode 100644 app/DiscordBotCommands/PresenceCommand.php create mode 100644 app/DiscordBotCommands/ResultCommand.php create mode 100644 lang/de/discord_commands.php create mode 100644 lang/en/discord_commands.php diff --git a/app/DiscordBotCommands/BaseCommand.php b/app/DiscordBotCommands/BaseCommand.php new file mode 100644 index 0000000..eb3a012 --- /dev/null +++ b/app/DiscordBotCommands/BaseCommand.php @@ -0,0 +1,126 @@ +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; + +} diff --git a/app/DiscordBotCommands/PresenceCommand.php b/app/DiscordBotCommands/PresenceCommand.php new file mode 100644 index 0000000..c466b1c --- /dev/null +++ b/app/DiscordBotCommands/PresenceCommand.php @@ -0,0 +1,46 @@ +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(); + }); + } + +} diff --git a/app/DiscordBotCommands/ResultCommand.php b/app/DiscordBotCommands/ResultCommand.php new file mode 100644 index 0000000..bfe35bd --- /dev/null +++ b/app/DiscordBotCommands/ResultCommand.php @@ -0,0 +1,44 @@ +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); + }); + } + +} diff --git a/app/Models/DiscordBotCommand.php b/app/Models/DiscordBotCommand.php index aec2f56..8a74d43 100644 --- a/app/Models/DiscordBotCommand.php +++ b/app/Models/DiscordBotCommand.php @@ -2,8 +2,10 @@ 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; @@ -11,58 +13,55 @@ class DiscordBotCommand extends 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', diff --git a/app/Models/Result.php b/app/Models/Result.php index 308b9e9..99f3791 100644 --- a/app/Models/Result.php +++ b/app/Models/Result.php @@ -11,6 +11,13 @@ class Result extends Model 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; diff --git a/app/Models/User.php b/app/Models/User.php index ff45c61..164fb02 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -12,6 +12,23 @@ class User extends Authenticatable { 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'; } diff --git a/lang/de/discord_commands.php b/lang/de/discord_commands.php new file mode 100644 index 0000000..1c7880b --- /dev/null +++ b/lang/de/discord_commands.php @@ -0,0 +1,10 @@ + [ + 'finish' => ':name hat mit einer Zeit von :time abgeschlossen.', + 'forfeit' => ':name hat aufgegeben.', + ], + +]; diff --git a/lang/en/discord_commands.php b/lang/en/discord_commands.php new file mode 100644 index 0000000..9003899 --- /dev/null +++ b/lang/en/discord_commands.php @@ -0,0 +1,10 @@ + [ + 'finish' => ':name has finished with a time of :time.', + 'forfeit' => ':name has forfeited.', + ], + +]; -- 2.39.2