]> git.localhorst.tv Git - alttp.git/commitdiff
chat bot protocol ui
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 9 Apr 2024 13:34:06 +0000 (15:34 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 9 Apr 2024 13:34:06 +0000 (15:34 +0200)
app/Http/Controllers/ChannelController.php
app/Models/Channel.php
resources/js/components/chat-bot-logs/ChatBotLog.js [new file with mode: 0644]
resources/js/components/chat-bot-logs/Dialog.js [new file with mode: 0644]
resources/js/components/chat-bot-logs/Item.js [new file with mode: 0644]
resources/js/components/chat-bot-logs/List.js [new file with mode: 0644]
resources/js/components/twitch-bot/Controls.js
resources/js/i18n/de.js
resources/js/i18n/en.js
routes/api.php

index 883ca65eff2360d93bcd20d9fb2d2bf93640caad..15213b412f675481ca86d854a4ecbf1579f0dfc6 100644 (file)
@@ -68,6 +68,16 @@ class ChannelController extends Controller {
                return $this->sendChannel($channel);
        }
 
+       public function chatBotLog(Request $request, Channel $channel) {
+               $this->authorize('editRestream', $channel);
+               $log = $channel->chat_bot_logs()
+                       ->with('origin')
+                       ->orderBy('created_at', 'DESC')
+                       ->limit(150)
+                       ->get();
+               return $log->values()->toJson();
+       }
+
        public function chatSettings(Request $request, Channel $channel) {
                if (!$channel->twitch_chat) {
                        throw new \Exception('channel has no twitch chat set');
index 46155cda710d1c5d0de970a692d84a8fce889924..8af6a7d43e201e191651a4ab9b28fc7c11642ec5 100644 (file)
@@ -240,6 +240,10 @@ class Channel extends Model {
                return Arr::join($entries, ', ', ' and ');
        }
 
+       public function chat_bot_logs() {
+               return $this->hasMany(ChatBotLog::class);
+       }
+
        public function crews() {
                return $this->hasMany(ChannelCrew::class);
        }
diff --git a/resources/js/components/chat-bot-logs/ChatBotLog.js b/resources/js/components/chat-bot-logs/ChatBotLog.js
new file mode 100644 (file)
index 0000000..45c8027
--- /dev/null
@@ -0,0 +1,51 @@
+import axios from 'axios';
+import PropTypes from 'prop-types';
+import React, { useEffect, useState } from 'react';
+import { Button } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+
+import Dialog from './Dialog';
+import Icon from '../common/Icon';
+
+const ChatBotLog = ({ id }) => {
+       const [showDialog, setShowDialog] = useState(false);
+       const [log, setLog] = useState([]);
+
+       const { t } = useTranslation();
+
+       useEffect(() => {
+               if (!showDialog) return;
+               const ctrl = new AbortController();
+               axios
+                       .get(`/api/channels/${id}/chat-bot-log`, { signal: ctrl.signal })
+                       .then(response => {
+                               setLog(response.data);
+                       });
+               return () => {
+                       ctrl.abort();
+               };
+       }, [id, showDialog]);
+
+       return (
+               <>
+                       <Button
+                               onClick={() => setShowDialog(true)}
+                               title={t('button.protocol')}
+                               variant="outline-info"
+                       >
+                               <Icon.PROTOCOL title="" />
+                       </Button>
+                       <Dialog
+                               log={log}
+                               onHide={() => setShowDialog(false)}
+                               show={showDialog}
+                       />
+               </>
+       );
+};
+
+ChatBotLog.propTypes = {
+       id: PropTypes.number,
+};
+
+export default ChatBotLog;
diff --git a/resources/js/components/chat-bot-logs/Dialog.js b/resources/js/components/chat-bot-logs/Dialog.js
new file mode 100644 (file)
index 0000000..ffe457c
--- /dev/null
@@ -0,0 +1,65 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Alert, Button, Modal } from 'react-bootstrap';
+import { withTranslation } from 'react-i18next';
+
+import List from './List';
+import i18n from '../../i18n';
+
+class Dialog extends React.Component {
+
+       componentDidMount() {
+               this.timer = setInterval(() => {
+                       this.forceUpdate();
+               }, 30000);
+       }
+
+       componentWillUnmount() {
+               clearInterval(this.timer);
+       }
+
+       render() {
+               const {
+                       log,
+                       onHide,
+                       show,
+               } = this.props;
+               return <Modal className="chat-bot-log-dialog" onHide={onHide} show={show} size="lg">
+                       <Modal.Header closeButton>
+                               <Modal.Title>
+                                       {i18n.t('chatBotLog.heading')}
+                               </Modal.Title>
+                       </Modal.Header>
+                       {log && log.length ?
+                               <List log={log} />
+                       :
+                               <Modal.Body>
+                                       <Alert variant="info">
+                                               {i18n.t('chatBotLog.empty')}
+                                       </Alert>
+                               </Modal.Body>
+                       }
+                       <Modal.Footer>
+                               <Button onClick={onHide} variant="secondary">
+                                       {i18n.t('button.close')}
+                               </Button>
+                       </Modal.Footer>
+               </Modal>;
+       }
+
+}
+
+Dialog.propTypes = {
+       log: PropTypes.arrayOf(PropTypes.shape({
+       })),
+       onHide: PropTypes.func,
+       show: PropTypes.bool,
+};
+
+Dialog.defaultProps = {
+       log: null,
+       onHide: null,
+       show: false,
+};
+
+export default withTranslation()(Dialog);
diff --git a/resources/js/components/chat-bot-logs/Item.js b/resources/js/components/chat-bot-logs/Item.js
new file mode 100644 (file)
index 0000000..8340e4e
--- /dev/null
@@ -0,0 +1,59 @@
+import moment from 'moment';
+import PropTypes from 'prop-types';
+import React from 'react';
+import { ListGroup } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+
+const getEntryDate = entry => {
+       const dateStr = moment(entry.created_at).fromNow();
+       return entry.user
+               ? `${entry.user.username} ${dateStr}`
+               : dateStr;
+};
+
+const getEntryOrigin = (entry, t) => {
+       return t('chatBotLog.origin.chatLog', {
+               channel: entry.origin.params[0],
+               date: new Date(entry.origin.created_at),
+               nick: entry.origin.nick,
+       });
+};
+
+const Item = ({ entry }) => {
+       const { t } = useTranslation();
+
+       return <ListGroup.Item>
+               <div>
+                       <div>
+                               {entry.text}
+                       </div>
+                       {entry.origin ?
+                               <div
+                                       className="text-muted"
+                               >
+                                       {getEntryOrigin(entry, t)}
+                               </div>
+                       : null}
+                       <div
+                               className="text-muted"
+                               title={moment(entry.created_at).format('LLLL')}
+                       >
+                               {getEntryDate(entry)}
+                       </div>
+               </div>
+       </ListGroup.Item>;
+};
+
+Item.propTypes = {
+       entry: PropTypes.shape({
+               created_at: PropTypes.string,
+               origin: PropTypes.shape({}),
+               text: PropTypes.string,
+       }),
+};
+
+Item.defaultProps = {
+       entry: {},
+};
+
+export default Item;
diff --git a/resources/js/components/chat-bot-logs/List.js b/resources/js/components/chat-bot-logs/List.js
new file mode 100644 (file)
index 0000000..9838b8e
--- /dev/null
@@ -0,0 +1,23 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { ListGroup } from 'react-bootstrap';
+
+import Item from './Item';
+
+const List = ({ log }) =>
+       <ListGroup variant="flush">
+               {log ? log.map(entry =>
+                       <Item key={entry.id} entry={entry} />
+               ) : null}
+       </ListGroup>;
+
+List.propTypes = {
+       log: PropTypes.arrayOf(PropTypes.shape({
+       })),
+};
+
+List.defaultProps = {
+       log: [],
+};
+
+export default List;
index 496ff11dd9dae4f977eecb793e25e0f0f2349256..51f0633a226bdc1f5626a47ee9778ba77d160ac2 100644 (file)
@@ -8,6 +8,7 @@ import ChatSettingsForm from './ChatSettingsForm';
 import CommandDialog from './CommandDialog';
 import Commands from './Commands';
 import GuessingSettingsForm from './GuessingSettingsForm';
+import ChatBotLog from '../chat-bot-logs/ChatBotLog';
 import ChannelSelect from '../common/ChannelSelect';
 import Icon from '../common/Icon';
 import ToggleSwitch from '../common/ToggleSwitch';
@@ -228,7 +229,12 @@ const Controls = () => {
                                        </div>
                                </Col>
                                <Col className="mt-5" md={6}>
-                                       <h3>{t('twitchBot.chatSettings')}</h3>
+                                       <div className="d-flex justify-content-between">
+                                               <h3>{t('twitchBot.chatSettings')}</h3>
+                                               <div className="button-bar">
+                                                       <ChatBotLog id={channel.id} />
+                                               </div>
+                                       </div>
                                        <ChatSettingsForm channel={channel} onSubmit={saveChatSettings} />
                                </Col>
                                <Col className="mt-5" md={12}>
index c663747c87c3b36d66c2486140327e2f5b5aa5f7..8a249913a34d220c6571273280800fed5fc8c906 100644 (file)
@@ -97,6 +97,13 @@ export default {
                        unconfirm: 'Zurückziehen',
                        unset: 'Zurücksetzen',
                },
+               chatBotLog: {
+                       empty: 'Noch keine Nachrichten erfasst',
+                       heading: 'Chat Bot Protokoll',
+                       origin: {
+                               chatLog: '{{ nick }} in {{ channel }} am {{ date, L LT }}',
+                       },
+               },
                content: {
                        attribution: 'Attribution',
                        description: 'Beschreibung',
index 0bc85e8e989bcc2c625fdca2c6dd196e00ba58db..f33d4f023cb37e0cb83c136b344d9927285e8cdd 100644 (file)
@@ -97,6 +97,13 @@ export default {
                        unconfirm: 'Retract',
                        unset: 'Unset',
                },
+               chatBotLog: {
+                       empty: 'No messages on protocol yet',
+                       heading: 'Chat Bot Log',
+                       origin: {
+                               chatLog: '{{ nick }} in {{ channel }} on {{ date, L LT }}',
+                       },
+               },
                content: {
                        attribution: 'Attribution',
                        description: 'Description',
index 0c897af2f23f463e0cfdaac57bb8b698ee2cba03..69a4bbfdc02f9159c52aeaa7bc0aca367011d944 100644 (file)
@@ -27,6 +27,7 @@ Route::post('application/{application}/reject', 'App\Http\Controllers\Applicatio
 Route::get('channels', 'App\Http\Controllers\ChannelController@search');
 Route::get('channels/{channel}', 'App\Http\Controllers\ChannelController@single');
 Route::post('channels/{channel}/chat', 'App\Http\Controllers\ChannelController@chat');
+Route::get('channels/{channel}/chat-bot-log', 'App\Http\Controllers\ChannelController@chatBotLog');
 Route::post('channels/{channel}/chat-settings', 'App\Http\Controllers\ChannelController@chatSettings');
 Route::post('channels/{channel}/join', 'App\Http\Controllers\ChannelController@join');
 Route::post('channels/{channel}/part', 'App\Http\Controllers\ChannelController@part');