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');
return Arr::join($entries, ', ', ' and ');
}
+ public function chat_bot_logs() {
+ return $this->hasMany(ChatBotLog::class);
+ }
+
public function crews() {
return $this->hasMany(ChannelCrew::class);
}
--- /dev/null
+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;
--- /dev/null
+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);
--- /dev/null
+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;
--- /dev/null
+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;
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';
</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}>
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',
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',
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');