]> git.localhorst.tv Git - alttp.git/commitdiff
add context bot chatbot log
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 6 Sep 2024 14:58:41 +0000 (16:58 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 6 Sep 2024 14:58:41 +0000 (16:58 +0200)
app/Http/Controllers/ChatBotLogController.php
resources/js/components/chat-bot-logs/Item.js
resources/js/components/chat-logs/Item.js [new file with mode: 0644]
resources/js/components/chat-logs/List.js [new file with mode: 0644]
resources/js/i18n/de.js
resources/js/i18n/en.js
resources/sass/app.scss
resources/sass/chatlog.scss [new file with mode: 0644]
routes/api.php

index 86bb69082278f1c42df8f7f65e8d09cf3e573265..62c28855ab3bb4af816cb1141b23af832cc9aed9 100644 (file)
@@ -3,6 +3,8 @@
 namespace App\Http\Controllers;
 
 use App\Models\ChatBotLog;
+use App\Models\ChatLog;
+use Carbon\Carbon;
 use Illuminate\Http\Request;
 
 class ChatBotLogController extends Controller {
@@ -12,4 +14,34 @@ class ChatBotLogController extends Controller {
                return $logs->get()->toJson();
        }
 
+       public function getContext(ChatBotLog $entry) {
+               $log = ChatLog::where('command', '=', 'PRIVMSG')
+                       ->where('channel_id', '=', $entry->channel_id)
+                       ->where('created_at', '<=', $entry->created_at)
+                       ->orderBy('created_at', 'DESC')
+                       ->limit(10)
+                       ->get()
+                       ->reverse()
+                       ->values();
+               $original = null;
+               if ($entry->origin_id) {
+                       try {
+                               $original = ChatLog::where('command', '=', 'PRIVMSG')
+                                       ->where('channel_id', '=', $entry->origin->channel_id)
+                                       ->where('id', '<', $entry->origin_id)
+                                       ->orderBy('created_at', 'DESC')
+                                       ->limit(10)
+                                       ->get()
+                                       ->reverse()
+                                       ->values();
+                       } catch (\Exception $e) {
+                               // original was deleted perhaps
+                       }
+               }
+               return [
+                       'current' => $log,
+                       'original' => $original,
+               ];
+       }
+
 }
index 9b4cfcb7a27a18015f75e50acee8c162da18af78..395ad333551b583d0f553c30dd1ba5bdcde9316f 100644 (file)
@@ -1,10 +1,14 @@
+import axios from 'axios';
 import moment from 'moment';
 import PropTypes from 'prop-types';
 import React from 'react';
-import { ListGroup } from 'react-bootstrap';
+import { Button, Col, ListGroup, Row } from 'react-bootstrap';
 import { useTranslation } from 'react-i18next';
 
 import ChannelLink from '../channel/Link';
+import List from '../chat-logs/List';
+import Icon from '../common/Icon';
+import Loading from '../common/Loading';
 import { getUserName } from '../../helpers/User';
 
 const getEntryDate = entry => moment(entry.created_at).fromNow();
@@ -41,30 +45,87 @@ const getEntryInfo = (entry, t) => {
 };
 
 const Item = ({ entry = {} }) => {
+       const [context, setContext] = React.useState(null);
+       const [contextLoading, setContextLoading] = React.useState(true);
+       const [showContext, setShowContext] = React.useState(false);
+
        const { t } = useTranslation();
 
-       return <ListGroup.Item className="d-flex justify-content-between">
-               <div>
+       React.useEffect(() => {
+               if (context || !showContext) return;
+               const ctrl = new AbortController();
+               axios
+                       .get(`/api/chatbotlogs/${entry.id}/context`, {
+                               signal: ctrl.signal
+                       })
+                       .then(response => {
+                               setContextLoading(false);
+                               setContext(response.data);
+                       })
+                       .catch(error => {
+                               if (!axios.isCancel(error)) {
+                                       setContextLoading(false);
+                                       setContext(null);
+                               }
+                       });
+               return () => {
+                       ctrl.abort();
+               };
+       }, [context, showContext]);
+
+       return <ListGroup.Item>
+               <div className="d-flex justify-content-between">
                        <div>
-                               {entry.text}
-                       </div>
-                       {entry.origin ?
+                               <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')}
                                >
-                                       {getEntryOrigin(entry, t)}
+                                       {getEntryInfo(entry, t)}
                                </div>
-                       : null}
-                       <div
-                               className="text-muted"
-                               title={moment(entry.created_at).format('LLLL')}
-                       >
-                               {getEntryInfo(entry, t)}
                        </div>
-               </div>
-               {entry.channel ?
                        <div>
-                               <ChannelLink channel={entry.channel} />
+                               {entry.channel ?
+                                       <ChannelLink channel={entry.channel} />
+                               : null}
+                               <Button
+                                       className="ms-2"
+                                       onClick={() => { setShowContext(c => !c); }}
+                                       title={t('chatBotLog.showContext')}
+                                       variant={showContext ? 'secondary' : 'outline-secondary'}
+                               >
+                                       <Icon.PROTOCOL title="" />
+                               </Button>
+                       </div>
+               </div>
+               {showContext ?
+                       <div className="chat-bot-log-context mt-2">
+                               {contextLoading ?
+                                       <Loading />
+                               : null}
+                               {context ?
+                                       <Row>
+                                               <Col sm={6}>
+                                                       <h3 className="fs-6">{t('chatBotLog.context')}</h3>
+                                                       <List log={context.current} />
+                                               </Col>
+                                               {context.original ?
+                                                       <Col sm={6}>
+                                                               <h3 className="fs-6">{t('chatBotLog.originalContext')}</h3>
+                                                               <List log={context.original} />
+                                                       </Col>
+                                               : null}
+                                       </Row>
+                               : null}
                        </div>
                : null}
        </ListGroup.Item>;
diff --git a/resources/js/components/chat-logs/Item.js b/resources/js/components/chat-logs/Item.js
new file mode 100644 (file)
index 0000000..52110e3
--- /dev/null
@@ -0,0 +1,53 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+const getChatterColor = entry => {
+       if (entry.tags && entry.tags['color']) {
+               return entry.tags['color'];
+       }
+       return 'inherit';
+};
+
+const getChatterNick = entry => {
+       if (entry.tags && entry.tags['display-name']) {
+               return entry.tags['display-name'];
+       }
+       return entry.nick;
+};
+
+const getTextContent = entry => {
+       if (entry.params && entry.params.length >= 2) {
+               return entry.params[1];
+       }
+       return entry.text_content;
+};
+
+const getTimestamp = entry => {
+       if (entry.tags && entry.tags['tmi-sent-ts']) {
+               return new Date(parseInt(entry.tags['tmi-sent-ts'], 10));
+       }
+       return new Date(entry.created_at);
+};
+
+const Item = ({ entry }) => {
+       const { t } = useTranslation();
+
+       return <div className="chat-log-item">
+               <div>
+                       <span className="text-muted me-2">
+                               {t('chatBotLog.shortTimestamp', { date: getTimestamp(entry) })}
+                       </span>
+                       <strong style={{ color: getChatterColor(entry) }}>{getChatterNick(entry)}</strong>
+               </div>
+               <div>{getTextContent(entry)}</div>
+       </div>;
+};
+
+Item.propTypes = {
+       entry: PropTypes.shape({
+               text_content: PropTypes.string,
+       }).isRequired,
+};
+
+export default Item;
diff --git a/resources/js/components/chat-logs/List.js b/resources/js/components/chat-logs/List.js
new file mode 100644 (file)
index 0000000..8da17e2
--- /dev/null
@@ -0,0 +1,19 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+
+import Item from './Item';
+
+const List = ({ log = [] }) => {
+       return <div className="chat-log-list">
+               {log.map(entry =>
+                       <Item key={entry.id} entry={entry} />
+               )}
+       </div>;
+};
+
+List.propTypes = {
+       log: PropTypes.arrayOf(PropTypes.shape({
+       })),
+};
+
+export default List;
index ce3faa67a0b0fc2cbe6d86b88073e8ce8456da74..3e32cee81f22ffe69ae8d4fdfa417320fe098a44 100644 (file)
@@ -98,6 +98,7 @@ export default {
                        unset: 'Zurücksetzen',
                },
                chatBotLog: {
+                       context: 'Kontext',
                        empty: 'Noch keine Nachrichten erfasst',
                        heading: 'Chat Bot Protokoll',
                        info: {
@@ -108,6 +109,9 @@ export default {
                        origin: {
                                chatLog: 'Quelle: {{ nick }} in {{ channel }} am {{ date, L LT }}',
                        },
+                       originalContext: 'Ursprünglicher Kontext',
+                       shortTimestamp: '{{ date, HH:mm:ss }}',
+                       showContext: 'Kontext zeigen',
                },
                content: {
                        attribution: 'Attribution',
index afd1aa17af18d953491ba249646d09262c3e0e54..b8d87215d25f9137a70e315835f3c60ec1842336 100644 (file)
@@ -98,6 +98,7 @@ export default {
                        unset: 'Unset',
                },
                chatBotLog: {
+                       context: 'Context',
                        empty: 'No messages on protocol yet',
                        heading: 'Chat Bot Log',
                        info: {
@@ -108,6 +109,9 @@ export default {
                        origin: {
                                chatLog: 'Source: {{ nick }} in {{ channel }} on {{ date, L LT }}',
                        },
+                       originalContext: 'Original context',
+                       shortTimestamp: '{{ date, hh:mm:ss }}',
+                       showContext: 'Show context',
                },
                content: {
                        attribution: 'Attribution',
index 7554a9e74dbc0769ca31f85bbb41bfc8fe83fee2..910d3826eb05c6188c356afe90b199ba28d222a2 100644 (file)
@@ -7,6 +7,7 @@
 // Custom
 @import 'common';
 @import 'channels';
+@import 'chatlog';
 @import 'discord';
 @import 'doors';
 @import 'episodes';
diff --git a/resources/sass/chatlog.scss b/resources/sass/chatlog.scss
new file mode 100644 (file)
index 0000000..63f9a00
--- /dev/null
@@ -0,0 +1,4 @@
+.chat-log-list {
+       padding: 0.5ex 1ex;
+       background: $dark;
+}
index e3ced1d98fb1f2222368228fdf0108ed1c446a84..28184147aa65296fdad747c08f1f7d36de268f1e 100644 (file)
@@ -40,6 +40,7 @@ Route::put('channels/{channel}/guessing-game/{name}', 'App\Http\Controllers\Chan
 Route::get('guessing-game-monitor/{key}', 'App\Http\Controllers\ChannelController@getGuessingGameMonitor');
 
 Route::get('chatbotlogs', 'App\Http\Controllers\ChatBotLogController@search');
+Route::get('chatbotlogs/{entry}/context', 'App\Http\Controllers\ChatBotLogController@getContext');
 
 Route::get('content', 'App\Http\Controllers\TechniqueController@search');
 Route::get('content/{tech:name}', 'App\Http\Controllers\TechniqueController@single');