diff --git a/tbot/twitch_bot/functions/quotes.py b/tbot/twitch_bot/functions/quotes.py
index cecaf48..0dc2107 100644
--- a/tbot/twitch_bot/functions/quotes.py
+++ b/tbot/twitch_bot/functions/quotes.py
@@ -1,5 +1,4 @@
from tbot.twitch_bot.var_filler import fills_vars, Send_error
-from tbot import config
from datetime import datetime
@fills_vars('quote.add')
@@ -40,7 +39,7 @@ async def quote_edit(bot, channel_id, cmd, args, **kwargs):
'WHERE channel_id=%s and number=%s',
(' '.join(args), datetime.utcnow(), channel_id, n)
)
- raise Send_error(f'Quote updated')
+ raise Send_error('Quote updated')
@fills_vars('quote.delete')
async def quote_delete(bot, channel_id, cmd, args, **kwargs):
diff --git a/tbot/web/app.py b/tbot/web/app.py
index 8733653..eb5da83 100644
--- a/tbot/web/app.py
+++ b/tbot/web/app.py
@@ -41,6 +41,8 @@ def App():
(r'/api/twitch/channels/([0-9]+)/commands/([0-9]+)', handlers.api.twitch.commands.Handler),
(r'/api/twitch/channels/([0-9]+)/commands-public', handlers.api.twitch.commands.Public_collection),
(r'/api/twitch/template-commands', handlers.api.twitch.commands.Template_collection),
+ (r'/api/twitch/channels/([0-9]+)/quotes', handlers.api.twitch.quotes.Collection_handler),
+ (r'/api/twitch/channels/([0-9]+)/quotes/([0-9]+)', handlers.api.twitch.quotes.Handler),
(r'/api/twitch/channels/([0-9]+)/admins', handlers.api.twitch.admin_of.Channel_admins),
(r'/api/twitch/channels/([0-9]+)/admins/([0-9]+)', handlers.api.twitch.admin_of.Channel_admins),
(r'/api/twitch/channels/([0-9]+)/filters', handlers.api.twitch.filters.Filters),
diff --git a/tbot/web/handlers/api/twitch/quotes.py b/tbot/web/handlers/api/twitch/quotes.py
new file mode 100644
index 0000000..9a38b46
--- /dev/null
+++ b/tbot/web/handlers/api/twitch/quotes.py
@@ -0,0 +1,89 @@
+import good
+from datetime import datetime, time
+from ..base import Api_handler, Level
+
+_schema = {
+ 'message': good.All(str, good.Length(min=1, max=400)),
+ 'date': good.Date('%Y-%m-%d'),
+}
+
+class Collection_handler(Api_handler):
+
+ __schema__ = good.Schema(_schema, default_keys=good.Required)
+
+ @Level(1)
+ async def get(self, channel_id):
+ cmds = await self.db.fetchall('''
+ SELECT *
+ FROM twitch_quotes
+ WHERE channel_id=%s AND enabled=1
+ ORDER BY number DESC
+ ''', (channel_id,))
+ self.write_object(cmds)
+
+ @Level(1)
+ async def post(self, channel_id):
+ data = self.validate()
+ data['channel_id'] = channel_id
+ data['created_at'] = datetime.combine(data['date'], time(0, 0, 0))
+ data['updated_at'] = datetime.utcnow()
+ raise Exception('Not implemented')
+ # TODO: get user name
+ #date['created_by'] = self.current_user.
+ fields = ','.join([f for f in data])
+ vfields = ','.join(['%s' for _ in data])
+ values = list(data.values())
+ c = await self.db.execute(
+ 'INSERT INTO twitch_quotes ({}) VALUES ({})'.format(fields, vfields),
+ values
+ )
+ cmd = await get_quote(self, channel_id, c.lastrowid)
+ self.set_status(201)
+ self.write_object(cmd)
+
+class Handler(Api_handler):
+
+ __schema__ = good.Schema(_schema, default_keys=good.Optional)
+
+ @Level(1)
+ async def get(self, channel_id, number):
+ cmd = await get_quote(self, channel_id, number)
+ if not cmd:
+ self.set_status(404)
+ self.write_object({'error': 'Unknown quote'})
+ else:
+ self.write_object(cmd)
+
+ @Level(1)
+ async def put(self, channel_id, number):
+ data = self.validate()
+ data['updated_at'] = datetime.utcnow()
+ if 'date' in data:
+ if data['date']:
+ data['created_at'] = datetime.combine(data['date'], time(0, 0, 0))
+ del data['date']
+ fields = ','.join(['{}=%s'.format(k) for k in data])
+ values = list(data.values())
+ values.append(channel_id)
+ values.append(number)
+ await self.db.execute(
+ 'UPDATE twitch_quotes SET {} WHERE channel_id=%s AND number=%s'.format(fields),
+ values
+ )
+ cmd = await get_quote(self, channel_id, number)
+ self.write_object(cmd)
+
+ @Level(1)
+ async def delete(self, channel_id, number):
+ await self.db.execute('''
+ UPDATE twitch_quotes SET enabled=0 WHERE channel_id=%s and number=%s
+ ''', (channel_id, number,))
+ self.set_status(204)
+
+async def get_quote(self, channel_id, number):
+ cmd = await self.db.fetchone('''
+ SELECT *
+ FROM twitch_quotes
+ WHERE channel_id=%s and number=%s
+ ''', (channel_id, number,))
+ return cmd
\ No newline at end of file
diff --git a/tbot/web/ui/twitch/dashboard/components/sidebar.jsx b/tbot/web/ui/twitch/dashboard/components/sidebar.jsx
index bc6374d..8874693 100644
--- a/tbot/web/ui/twitch/dashboard/components/sidebar.jsx
+++ b/tbot/web/ui/twitch/dashboard/components/sidebar.jsx
@@ -67,6 +67,7 @@ class Sidebar extends React.Component {
Filters
Banned words
Chat alerts
+ Quotes
{managedUser.level >= 3 ?
Admins
diff --git a/tbot/web/ui/twitch/dashboard/index.jsx b/tbot/web/ui/twitch/dashboard/index.jsx
index cede524..36b111c 100644
--- a/tbot/web/ui/twitch/dashboard/index.jsx
+++ b/tbot/web/ui/twitch/dashboard/index.jsx
@@ -8,6 +8,8 @@ import Sidebar from './components/sidebar'
import Topbar from './components/topbar'
import Dashboard from './dashboard'
import Commands from './commands'
+import Quotes from './quotes'
+import Quote from './quote'
import Command from './command'
import Spotify from './spotify'
import Discord from './discord'
@@ -52,6 +54,8 @@ class Main extends React.Component {
+
+
diff --git a/tbot/web/ui/twitch/dashboard/quote.jsx b/tbot/web/ui/twitch/dashboard/quote.jsx
new file mode 100644
index 0000000..af495cc
--- /dev/null
+++ b/tbot/web/ui/twitch/dashboard/quote.jsx
@@ -0,0 +1,134 @@
+import React from 'react'
+import {Redirect} from 'react-router'
+import api from 'tbot/twitch/api'
+import Loading from 'tbot/components/loading'
+import {setHeader, renderError} from 'tbot/utils'
+import {userLevelName, enabledWhenName} from 'tbot/twitch/utils'
+
+class Quote extends React.Component {
+
+ constructor(props) {
+ super(props)
+ this.state = {
+ cmd: {
+ message: '',
+ date: '',
+ },
+ loading: true,
+ error: null,
+ saving: false,
+ success: false,
+ }
+ }
+
+ componentDidMount() {
+ if (this.props.match.params.number) {
+ setHeader('Edit quote')
+ this.getQuote()
+ } else {
+ setHeader('New quote')
+ this.setState({loading: false})
+ }
+ }
+
+ getQuote() {
+ const number = this.props.match.params.number
+ api.get(`/api/twitch/channels/${managedUser.id}/quotes/${number}`).then(r => {
+ for (let key in r.data) {
+ if (!(key in this.state.cmd))
+ delete r.data[key]
+ }
+ this.setState({
+ quote: r.data,
+ loading: false
+ })
+ setHeader(`Edit quote`)
+ })
+ }
+
+ handleEvent = (e) => {
+ let val = e.target.type === 'checkbox' ? e.target.checked : e.target.value
+ this.state.quote[e.target.name] = val
+ this.setState({quote: this.state.quote})
+ }
+
+ handleSubmit = (e) => {
+ e.preventDefault()
+ this.setState({saving: true, error: null})
+ const number = this.props.match.params.number
+ if (number)
+ api.put(`/api/twitch/channels/${managedUser.id}/quotes/${number}`, this.state.quote).then(r => {
+ this.setState({success: true})
+ }).catch(e => {
+ this.setState({error: e.response.data, saving: false})
+ })
+ else
+ api.post(`/api/twitch/channels/${managedUser.id}/quotes`, this.state.quote).then(r => {
+ this.setState({success: true})
+ }).catch(e => {
+ this.setState({error: e.response.data, saving: false})
+ })
+ }
+
+ handleDelete = () => {
+ if (!confirm(`Delete quote?`))
+ return
+ this.setState({deleting: true, error: null})
+ let number = this.props.match.params.number
+ api.delete(`/api/twitch/channels/${managedUser.id}/quotes/${number}`).then(r => {
+ this.setState({
+ cmd: r.data,
+ success: true,
+ })
+ }).catch(e => {
+ this.setState({error: e.response.data, deleting: false})
+ })
+ }
+
+ renderButton() {
+ if (this.state.saving)
+ return
+ return
+ }
+
+ renderDeleteButton() {
+ if (!this.props.match.params.number)
+ return null
+ if (this.state.deleting)
+ return
+ return
+ }
+
+ render() {
+ if (this.state.loading)
+ return
+ if (this.state.success)
+ return
+
+ return
+ }
+}
+
+export default Quote
\ No newline at end of file
diff --git a/tbot/web/ui/twitch/dashboard/quotes.jsx b/tbot/web/ui/twitch/dashboard/quotes.jsx
new file mode 100644
index 0000000..9a84e43
--- /dev/null
+++ b/tbot/web/ui/twitch/dashboard/quotes.jsx
@@ -0,0 +1,56 @@
+import React from 'react'
+import {Link} from 'react-router-dom'
+import api from 'tbot/twitch/api'
+import Loading from 'tbot/components/loading'
+import {setHeader} from 'tbot/utils'
+
+class Quotes extends React.Component {
+
+ constructor(props) {
+ super(props)
+ this.state = {
+ quotes: [],
+ loading: true,
+ }
+ }
+
+ componentDidMount() {
+ setHeader('Quotes')
+ this.getQuotes()
+ }
+
+ getQuotes() {
+ api.get(`/api/twitch/channels/${managedUser.id}/quotes`).then(r => {
+ this.setState({
+ quotes: r.data,
+ loading: false
+ })
+ })
+ }
+
+ render() {
+ if (this.state.loading)
+ return
+ return
+
+
+
+ Number |
+ Quote |
+
+
+
+ {this.state.quotes.length>0?this.state.quotes.map(quote =>
+
+ {quote.number} |
+ {quote.message} |
+ Edit |
+
+ ): No quotes. |
}
+
+
+
+ }
+}
+
+export default Quotes
\ No newline at end of file