Compare commits
1 commit
main
...
wip/modque
Author | SHA1 | Date | |
---|---|---|---|
6b57153d5e |
3 changed files with 179 additions and 0 deletions
1
src/ovtk_audiencekit/plugins/ModQueue/__init__.py
Normal file
1
src/ovtk_audiencekit/plugins/ModQueue/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .mod import ModPlugin as Plugin
|
103
src/ovtk_audiencekit/plugins/ModQueue/mod.py
Normal file
103
src/ovtk_audiencekit/plugins/ModQueue/mod.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
from dataclasses import dataclass, field, asdict
|
||||
from enum import Enum
|
||||
import asyncio
|
||||
from typing import Callable
|
||||
import quart
|
||||
import json
|
||||
import random
|
||||
|
||||
import kdl
|
||||
|
||||
from ovtk_audiencekit.core import PluginBase
|
||||
from ovtk_audiencekit.utils import format_exception
|
||||
from ovtk_audiencekit.events.Message import SysMessage
|
||||
|
||||
|
||||
class Judgement(Enum):
|
||||
cringe = 0
|
||||
based = 1
|
||||
|
||||
@dataclass
|
||||
class Item:
|
||||
id: int = field(init=False)
|
||||
links: list[str]
|
||||
files: list[str]
|
||||
kdl: list[str]
|
||||
ctx: dict
|
||||
user: str = None
|
||||
judgement: Judgement = None
|
||||
|
||||
def __post_init__(self):
|
||||
self.id = random.getrandbits(32)
|
||||
|
||||
def describe(self):
|
||||
return asdict(self)
|
||||
|
||||
|
||||
class ModPlugin(PluginBase):
|
||||
def setup(self):
|
||||
self.requests = {}
|
||||
self._queue_change_ev = asyncio.Event()
|
||||
|
||||
self.blueprint.add_url_rule('/', 'ctrlpanel', self.ui_ctrlpanel)
|
||||
self.blueprint.add_url_rule('/<id>/<is_based>', 'api-review', self.ui_review)
|
||||
self.blueprint.add_url_rule('/monitor', 'monitor', self.ui_monitor_ws, is_websocket=True)
|
||||
|
||||
async def run(self):
|
||||
pass
|
||||
|
||||
async def send(self, _children=None, _ctx={}):
|
||||
user = None
|
||||
if source_ev := _ctx.get('event'):
|
||||
if 'user_name' in source_ev.__dict__:
|
||||
user = source_ev.user_name
|
||||
|
||||
item = Item(_children, _ctx, user)
|
||||
self.requests[item.id] = item
|
||||
self._queue_change_ev.set()
|
||||
|
||||
async def review(self, selector, judgement, _ctx={}):
|
||||
if selector == 'last':
|
||||
item_id = self.requests.keys()[-1]
|
||||
item = self.requests[item_id]
|
||||
else:
|
||||
item = self.requests.get(selector)
|
||||
|
||||
if item is None:
|
||||
if source := _ctx.get('event'):
|
||||
msg = SysMessage(self._name, f"No item found", replies_to=source)
|
||||
self.chats[source.via].send(msg)
|
||||
return
|
||||
|
||||
item.judgement = Judgement(int(is_based))
|
||||
self._queue_change_ev.set()
|
||||
|
||||
async def _runner_loop(self):
|
||||
while True:
|
||||
await self._queue_change_ev.wait()
|
||||
for id, item in requests.items():
|
||||
if item.judgement is not None:
|
||||
await self.execute_kdl(item.kdl, _ctx=item.ctx)
|
||||
|
||||
async def ui_ctrlpanel(self):
|
||||
groups = self._ui_get_state()
|
||||
return await self.blueprint.render('index.html', init_state=json.dumps(groups))
|
||||
|
||||
async def ui_review(self, id=None):
|
||||
data = await request.get_data()
|
||||
self.judge(id, is_based)
|
||||
return quart.Response(status=200)
|
||||
|
||||
async def ui_monitor_ws(self):
|
||||
await quart.websocket.accept()
|
||||
while True:
|
||||
groups = self._ui_get_state()
|
||||
await quart.websocket.send(json.dumps(groups))
|
||||
await self._queue_change_ev.wait()
|
||||
|
||||
def _ui_get_state(self):
|
||||
response = []
|
||||
for id, item in self.requests.items():
|
||||
if item.judgement is not None:
|
||||
response.append((id, item.describe()))
|
||||
return response
|
75
src/ovtk_audiencekit/plugins/ModQueue/templates/index.html
Normal file
75
src/ovtk_audiencekit/plugins/ModQueue/templates/index.html
Normal file
|
@ -0,0 +1,75 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Moderatuin qyeye</title>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": { "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js" }
|
||||
}
|
||||
</script>
|
||||
<script id="initial_state" type='text/kdl'>
|
||||
<< init_state|safe >>
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import { createApp, ref, onMounted } from 'vue'
|
||||
|
||||
createApp({
|
||||
setup() {
|
||||
const scriptEl = document.getElementById("initial_state");
|
||||
const groups = ref(JSON.parse(scriptEl.innerHTML));
|
||||
onMounted(() => {
|
||||
const websock = new WebSocket('<<url_for(".monitor")>>');
|
||||
websock.addEventListener('message', (msg) => {
|
||||
groups.value = JSON.parse(msg.data);
|
||||
})
|
||||
})
|
||||
return { groups }
|
||||
},
|
||||
}).mount('#app')
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body id="root">
|
||||
<div id="app">
|
||||
<div v-for="(id, data) in groups" class="group">
|
||||
<h3>{{ id }}</h3>
|
||||
<p>{{ data }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
font-family: sans-serif;
|
||||
gap: 8px;
|
||||
}
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
h3 {
|
||||
margin-right: 1em;
|
||||
}
|
||||
.group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.scene {
|
||||
padding: 12px 24px;
|
||||
user-select: none;
|
||||
background-color: lightgray;
|
||||
flex: 1;
|
||||
}
|
||||
.scene.pending {
|
||||
background-color: lightgoldenrodyellow;
|
||||
}
|
||||
.scene.active {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue