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);