From: Daniel Karbach Date: Sat, 5 Jul 2025 16:31:05 +0000 (+0200) Subject: discord bot log X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=2cb04d6a86079b0dac91bce09e551004492fa601;p=alttp.git discord bot log --- diff --git a/app/DiscordBotCommands/MessageCommand.php b/app/DiscordBotCommands/MessageCommand.php index 746bd5f..f147375 100644 --- a/app/DiscordBotCommands/MessageCommand.php +++ b/app/DiscordBotCommands/MessageCommand.php @@ -5,8 +5,7 @@ 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; +use React\Promise\PromiseInterface; class MessageCommand extends BaseCommand { @@ -14,7 +13,7 @@ class MessageCommand extends BaseCommand { parent::__construct($discord, $cmd); } - public function execute() { + public function execute(): PromiseInterface { if (!$this->hasParameter('text')) { throw new \Exception('missing text parameter'); } diff --git a/app/DiscordBotCommands/PresenceCommand.php b/app/DiscordBotCommands/PresenceCommand.php index 47c15a5..8fa249b 100644 --- a/app/DiscordBotCommands/PresenceCommand.php +++ b/app/DiscordBotCommands/PresenceCommand.php @@ -6,6 +6,7 @@ use App\Models\DiscordBotCommand; use Discord\Discord; use Discord\Parts\User\Activity; use React\Promise\Promise; +use React\Promise\PromiseInterface; class PresenceCommand extends BaseCommand { @@ -13,8 +14,8 @@ class PresenceCommand extends BaseCommand { parent::__construct($discord, $cmd); } - public function execute() { - return new Promise(function($resolve) { + public function execute(): PromiseInterface { + return new Promise(function ($resolve) { $activity = null; $idle = false; $status = 'online'; diff --git a/app/DiscordBotCommands/ResultCommand.php b/app/DiscordBotCommands/ResultCommand.php index bfe35bd..3f2030e 100644 --- a/app/DiscordBotCommands/ResultCommand.php +++ b/app/DiscordBotCommands/ResultCommand.php @@ -5,8 +5,8 @@ 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; +use React\Promise\PromiseInterface; class ResultCommand extends BaseCommand { @@ -14,7 +14,7 @@ class ResultCommand extends BaseCommand { parent::__construct($discord, $cmd); } - public function execute() { + public function execute(): PromiseInterface { if (!$this->hasRoundChannels()) { return \React\Promise\resolve(); } diff --git a/app/DiscordBotCommands/SyncUserCommand.php b/app/DiscordBotCommands/SyncUserCommand.php index 642c8c7..dc2f74e 100644 --- a/app/DiscordBotCommands/SyncUserCommand.php +++ b/app/DiscordBotCommands/SyncUserCommand.php @@ -8,6 +8,7 @@ use Discord\Discord; use Discord\Parts\User\User as DiscordUser; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Storage; +use React\Promise\PromiseInterface; class SyncUserCommand extends BaseCommand { @@ -15,7 +16,7 @@ class SyncUserCommand extends BaseCommand { parent::__construct($discord, $cmd); } - public function execute() { + public function execute(): PromiseInterface { return $this->fetchUser() ->then(function (DiscordUser $discordUser) { $user = User::find($discordUser->id); diff --git a/app/Http/Controllers/DiscordBotController.php b/app/Http/Controllers/DiscordBotController.php index 99a1b4c..4e415a4 100644 --- a/app/Http/Controllers/DiscordBotController.php +++ b/app/Http/Controllers/DiscordBotController.php @@ -10,6 +10,11 @@ use Illuminate\Http\Request; class DiscordBotController extends Controller { + public function recentCommands(DiscordGuild $guild) { + $this->authorize('manage', $guild); + return $guild->bot_commands()->limit(10)->get(); + } + public function sendMessage(Request $request, DiscordGuild $guild) { $this->authorize('manage', $guild); $validatedData = $request->validate([ diff --git a/app/Models/DiscordBotCommand.php b/app/Models/DiscordBotCommand.php index 9397e5b..d67ee9f 100644 --- a/app/Models/DiscordBotCommand.php +++ b/app/Models/DiscordBotCommand.php @@ -4,15 +4,24 @@ namespace App\Models; use App\DiscordBotCommands\BaseCommand; use Discord\Discord; -use Discord\Parts\Channel\Channel; -use Discord\Parts\Guild\Guild; +use Illuminate\Broadcasting\PrivateChannel; +use Illuminate\Database\Eloquent\BroadcastsEvents; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -class DiscordBotCommand extends Model -{ +class DiscordBotCommand extends Model { + + use BroadcastsEvents; use HasFactory; + public function broadcastOn(string $event): array { + $channels = []; + if ($this->discord_guild_id) { + $channels[] = new PrivateChannel('DiscordGuild.'.$this->discord_guild_id); + } + return $channels; + } + public static function queueResult(Result $result) { $cmd = new DiscordBotCommand(); $cmd->tournament_id = $result->round->tournament_id; diff --git a/resources/js/components/discord-bot/ChannelControls.jsx b/resources/js/components/discord-bot/ChannelControls.jsx index ee06e66..66840d0 100644 --- a/resources/js/components/discord-bot/ChannelControls.jsx +++ b/resources/js/components/discord-bot/ChannelControls.jsx @@ -23,7 +23,7 @@ const ChannelControls = ({ channel, guild }) => { }, [channel, guild]); return
-

{t('discordBot.channelControls')}

+

{t('discordBot.channelControls')}

diff --git a/resources/js/components/discord-bot/Controls.jsx b/resources/js/components/discord-bot/Controls.jsx index 12c2fb6..f001345 100644 --- a/resources/js/components/discord-bot/Controls.jsx +++ b/resources/js/components/discord-bot/Controls.jsx @@ -3,8 +3,11 @@ import { Col, Form, Row } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import ChannelControls from './ChannelControls'; +import GuildControls from './GuildControls'; +import GuildProtocol from './GuildProtocol'; import DiscordChannelSelect from '../common/DiscordChannelSelect'; import DiscordSelect from '../common/DiscordSelect'; +import ErrorBoundary from '../common/ErrorBoundary'; const Controls = () => { const [channel, setChannel] = React.useState(null); @@ -38,7 +41,15 @@ const Controls = () => { {guild && channel ? - + + + + : null} + {guild ? + + + + : null} ; }; diff --git a/resources/js/components/discord-bot/GuildControls.jsx b/resources/js/components/discord-bot/GuildControls.jsx new file mode 100644 index 0000000..545c5ee --- /dev/null +++ b/resources/js/components/discord-bot/GuildControls.jsx @@ -0,0 +1,18 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +const GuildControls = ({ guild }) => { + const { t } = useTranslation(); + + return
+

{t('discordBot.guildControls')}

+
; +}; + +GuildControls.propTypes = { + guild: PropTypes.shape({ + }), +}; + +export default GuildControls; diff --git a/resources/js/components/discord-bot/GuildProtocol.jsx b/resources/js/components/discord-bot/GuildProtocol.jsx new file mode 100644 index 0000000..1121720 --- /dev/null +++ b/resources/js/components/discord-bot/GuildProtocol.jsx @@ -0,0 +1,73 @@ +import axios from 'axios'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import Loading from '../common/Loading'; + +const GuildProtocol = ({ guild }) => { + const [loading, setLoading] = React.useState(true); + const [protocol, setProtocol] = React.useState([]); + + const { t } = useTranslation(); + + React.useEffect(() => { + const ctrl = new AbortController(); + axios + .get(`/api/discord-bot/${guild.id}/commands`, { signal: ctrl.signal }) + .then(response => { + setLoading(false); + setProtocol(response.data); + }); + window.Echo.private(`DiscordGuild.${guild.id}`) + .listen('.DiscordBotCommandCreated', e => { + if (e.model) { + setProtocol(protocol => [e.model, ...protocol]); + } + }) + .listen('.DiscordBotCommandUpdated', e => { + if (e.model) { + setProtocol(protocol => protocol.map(p => p.id === e.model.id ? { ...p, ...e.mode } : p)); + } + }); + return () => { + ctrl.abort(); + window.Echo.leave(`DiscordGuild.${guild.id}`); + }; + }, [guild.id]); + + return
+

{t('discordBot.guildProtocol')}

+ {loading ? + + : + protocol.map((entry) => +
+
+ {t(`discordBot.commandType.${entry.command}`)} + {t(`discordBot.commandStatus.${entry.status}`)} +
+
+ + {t('discordBot.commandTime', { time: new Date(entry.created_at) })} + + + {entry.executed_at + ? t('discordBot.commandTime', { time: new Date(entry.executed_at) }) + : t('discordBot.commandPending') + } + +
+
+ ) + } +
; +}; + +GuildProtocol.propTypes = { + guild: PropTypes.shape({ + id: PropTypes.number, + }).isRequired, +}; + +export default GuildProtocol; diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index fc4b295..6c206be 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -140,8 +140,23 @@ export default { discordBot: { channel: 'Kanal', channelControls: 'Kanal-Steuerung', + commandStatus: { + done: 'Abgeschlossen', + exception: 'Fehler', + executing: 'Ausführen', + hold: 'Pausiert', + pending: 'Ausstehend', + }, + commandTime: '{{ time, L HH:mm:ss }}', + commandType: { + 'episode-event': 'Episoden-Event Synchronisation', + message: 'Nachricht', + result: 'Ergebnis', + }, controls: 'Steuerung', guild: 'Server', + guildControls: 'Server-Steuerung', + guildProtocol: 'Command Protokoll', heading: 'Discord Bot', invite: 'Bot einladen', message: 'Nachricht', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index b2175c5..7ed99a3 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -140,8 +140,23 @@ export default { discordBot: { channel: 'Channel', channelControls: 'Channel controls', + commandStatus: { + done: 'Done', + exception: 'Error', + executing: 'Executing', + hold: 'Hold', + pending: 'Pending', + }, + commandTime: '{{ time, L HH:mm:ss }}', + commandType: { + 'episode-event': 'Episode event synchronization', + message: 'Message', + result: 'Result', + }, controls: 'Controls', guild: 'Server', + guildControls: 'Server controls', + guildProtocol: 'Command protocol', heading: 'Discord Bot', invite: 'Invite bot', message: 'Message', diff --git a/resources/js/pages/DiscordBot.jsx b/resources/js/pages/DiscordBot.jsx index 111cbbe..0f22ed2 100644 --- a/resources/js/pages/DiscordBot.jsx +++ b/resources/js/pages/DiscordBot.jsx @@ -3,6 +3,7 @@ import { Button, Container } from 'react-bootstrap'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; +import ErrorBoundary from '../components/common/ErrorBoundary'; import Icon from '../components/common/Icon'; import Controls from '../components/discord-bot/Controls'; @@ -30,7 +31,8 @@ export const Component = () => {

-

{t('discordBot.controls')}

- + + + ; }; diff --git a/routes/api.php b/routes/api.php index 9428065..81f35b8 100644 --- a/routes/api.php +++ b/routes/api.php @@ -46,6 +46,7 @@ Route::get('content', 'App\Http\Controllers\TechniqueController@search'); Route::get('content/{tech:name}', 'App\Http\Controllers\TechniqueController@single'); Route::put('content/{content}', 'App\Http\Controllers\TechniqueController@update'); +Route::get('discord-bot/{guild}/commands', 'App\Http\Controllers\DiscordBotController@recentCommands'); Route::post('discord-bot/{guild}/send-message', 'App\Http\Controllers\DiscordBotController@sendMessage'); Route::get('discord-channels/{channel_id}', 'App\Http\Controllers\DiscordChannelController@single'); diff --git a/routes/channels.php b/routes/channels.php index b0a2552..e04f537 100644 --- a/routes/channels.php +++ b/routes/channels.php @@ -1,6 +1,7 @@ can('editRestream', $channel); }); +Broadcast::channel('DiscordGuild.{id}', function ($user, $id) { + $guild = DiscordGuild::findOrFail($id); + return $user->can('manage', $guild); +}); + Broadcast::channel('Protocol.{id}', function ($user, $id) { $tournament = Tournament::findOrFail($id); return $user->can('viewProtocol', $tournament);