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 {
 
                parent::__construct($discord, $cmd);
        }
 
-       public function execute() {
+       public function execute(): PromiseInterface {
                if (!$this->hasParameter('text')) {
                        throw new \Exception('missing text parameter');
                }
 
 use Discord\Discord;
 use Discord\Parts\User\Activity;
 use React\Promise\Promise;
+use React\Promise\PromiseInterface;
 
 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';
 
 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 {
 
                parent::__construct($discord, $cmd);
        }
 
-       public function execute() {
+       public function execute(): PromiseInterface {
                if (!$this->hasRoundChannels()) {
                        return \React\Promise\resolve();
                }
 
 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 {
 
                parent::__construct($discord, $cmd);
        }
 
-       public function execute() {
+       public function execute(): PromiseInterface {
                return $this->fetchUser()
                        ->then(function (DiscordUser $discordUser) {
                                $user = User::find($discordUser->id);
 
 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([
 
 
 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;
 
        }, [channel, guild]);
 
        return <section className="mt-5">
-               <h3>{t('discordBot.channelControls')}</h3>
+               <h2>{t('discordBot.channelControls')}</h2>
                <Row>
                        <Col md={6}>
                                <Form.Group>
 
 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);
                        </Form.Group>
                </Row>
                {guild && channel ?
-                       <ChannelControls channel={channel} guild={guild} />
+                       <ErrorBoundary>
+                               <ChannelControls channel={channel} guild={guild} />
+                       </ErrorBoundary>
+               : null}
+               {guild ?
+                       <ErrorBoundary>
+                               <GuildControls guild={guild} />
+                               <GuildProtocol guild={guild} />
+                       </ErrorBoundary>
                : null}
        </>;
 };
 
--- /dev/null
+import PropTypes from 'prop-types';
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+const GuildControls = ({ guild }) => {
+       const { t } = useTranslation();
+
+       return <section className="mt-5">
+               <h2>{t('discordBot.guildControls')}</h2>
+       </section>;
+};
+
+GuildControls.propTypes = {
+       guild: PropTypes.shape({
+       }),
+};
+
+export default GuildControls;
 
--- /dev/null
+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 <section className="mt-5">
+               <h2>{t('discordBot.guildProtocol')}</h2>
+               {loading ?
+                       <Loading />
+               :
+                       protocol.map((entry) =>
+                               <div className="discord-bot-protocol border-top" key={entry.id}>
+                                       <div className="d-flex justify-content-between">
+                                               <span>{t(`discordBot.commandType.${entry.command}`)}</span>
+                                               <span>{t(`discordBot.commandStatus.${entry.status}`)}</span>
+                                       </div>
+                                       <div className="d-flex justify-content-between">
+                                               <span className="text-muted">
+                                                       {t('discordBot.commandTime', { time: new Date(entry.created_at) })}
+                                               </span>
+                                               <span className="text-muted">
+                                                       {entry.executed_at
+                                                               ? t('discordBot.commandTime', { time: new Date(entry.executed_at) })
+                                                               : t('discordBot.commandPending')
+                                                       }
+                                               </span>
+                                       </div>
+                               </div>
+                       )
+               }
+       </section>;
+};
+
+GuildProtocol.propTypes = {
+       guild: PropTypes.shape({
+               id: PropTypes.number,
+       }).isRequired,
+};
+
+export default GuildProtocol;
 
                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',
 
                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',
 
 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';
 
                                </Button>
                        </span>
                </p>
-               <h2>{t('discordBot.controls')}</h2>
-               <Controls />
+               <ErrorBoundary>
+                       <Controls />
+               </ErrorBoundary>
        </Container>;
 };
 
 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');
 
 <?php
 
 use App\Models\Channel;
+use App\Models\DiscordGuild;
 use App\Models\Tournament;
 use Illuminate\Support\Facades\Broadcast;
 
        return $user->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);