generated from retoor/boeh
Compare commits
2
Commits
main
..
64d898c31b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64d898c31b | ||
|
|
91ac6a17e8 |
@@ -1,7 +1,7 @@
|
|||||||
BIN = ./.venv/bin/
|
BIN = ./.venv/bin/
|
||||||
PYTHON = ./.venv/bin/python
|
PYTHON = ./.venv/bin/python
|
||||||
PIP = ./.venv/bin/pip
|
PIP = ./.venv/bin/pip
|
||||||
APP_NAME=rwebgui
|
APP_NAME=boeh
|
||||||
|
|
||||||
all: install build
|
all: install build
|
||||||
|
|
||||||
@@ -24,5 +24,5 @@ build:
|
|||||||
$(PYTHON) -m build
|
$(PYTHON) -m build
|
||||||
|
|
||||||
run:
|
run:
|
||||||
$(BIN)$(APP_NAME).serve --port=3080
|
$(BIN)$(APP_NAME) --port=3028
|
||||||
|
|
||||||
|
|||||||
@@ -1,110 +1,4 @@
|
|||||||
# Backend Web Services
|
# Boeh
|
||||||
|
|
||||||
|
## Description
|
||||||
## Example programs
|
Matrix bot written in Python that says boeh everytime that Joe talks. He knows why.
|
||||||
This is all you need to write an application that does post to GPT and returns the result in the textarea. No javascript needed! Only HTML and equal naming of field names. The prompt-id of the html element should match the field name server side.
|
|
||||||
### Server side sourc
|
|
||||||
```python
|
|
||||||
class GPT(Component):
|
|
||||||
|
|
||||||
class Children:
|
|
||||||
prompt = Component
|
|
||||||
answer = Component
|
|
||||||
|
|
||||||
class submit(Component):
|
|
||||||
async def trigger(self, id_, event, data):
|
|
||||||
print("GOGOG", event, data)
|
|
||||||
return await super().trigger(id_, event, data)
|
|
||||||
|
|
||||||
async def on_click(self, data):
|
|
||||||
from xmlrpc.client import ServerProxy
|
|
||||||
|
|
||||||
client = ServerProxy("https://api.molodetz.nl/rpc")
|
|
||||||
prompt = await self.app.prompt.get_attr("value")
|
|
||||||
print(prompt)
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
await self.answer.set_attr("value", client.gpt4o(prompt))
|
|
||||||
```
|
|
||||||
### HTML Source
|
|
||||||
```html
|
|
||||||
<div>
|
|
||||||
<textarea id="prompt" type="text" value=""></textarea>
|
|
||||||
<textarea id="answer" type="text" value=""></textarea>
|
|
||||||
<input id="submit" type="button" value="Submit" />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
## Component using it's own service that frequently repeats.
|
|
||||||
Every method prefixed with `task_` will have it's own thread. Background tasks!
|
|
||||||
In this example, this counter (a text field, defined by HTML) has two background workers.
|
|
||||||
An task_increment that does +1 every second using pure python. Another task that only does something
|
|
||||||
every ten seconds.
|
|
||||||
### Server side source
|
|
||||||
```python
|
|
||||||
class Counter(Component):
|
|
||||||
|
|
||||||
async def task_test(self):
|
|
||||||
while True:
|
|
||||||
await asyncio.sleep(10)
|
|
||||||
print("Slow task")
|
|
||||||
|
|
||||||
async def task_increment(self):
|
|
||||||
if not self.value:
|
|
||||||
self.value = 0
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
self.value = int(self.value)
|
|
||||||
except:
|
|
||||||
self.value = 0
|
|
||||||
self.value += 1
|
|
||||||
await self.set_attr("value", self.value)
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
```
|
|
||||||
### HTML page
|
|
||||||
```
|
|
||||||
<input id="teller1" type="text" value="[some text]" />
|
|
||||||
```
|
|
||||||
## Interactive
|
|
||||||
Here's an example of an interactive calculator that calculates your expression after typing f. No javascript need and no polling. It' a textbox, but if you would change it to a textarea, it doesn't matter. As long if it has the right javascript events.
|
|
||||||
|
|
||||||
### Eval server side execution
|
|
||||||
```python
|
|
||||||
class EvalBox(Component):
|
|
||||||
|
|
||||||
async def on_change(self, value):
|
|
||||||
|
|
||||||
try:
|
|
||||||
if value and value.strip().endswith("="):
|
|
||||||
value = value.strip()[:-1]
|
|
||||||
try:
|
|
||||||
result = eval(value)
|
|
||||||
value = value + "= " + str(result)
|
|
||||||
await self.set_attr("value", value)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
except AttributeError:
|
|
||||||
print(value)
|
|
||||||
return value
|
|
||||||
```
|
|
||||||
### HTML
|
|
||||||
```html
|
|
||||||
<input id="eval_box" type="text" value="wiii" />
|
|
||||||
```
|
|
||||||
|
|
||||||
## Random string stream from the Demo
|
|
||||||
This is the code of that random ascii banner.
|
|
||||||
```python
|
|
||||||
class RandomString(Component):
|
|
||||||
|
|
||||||
async def task_random(self):
|
|
||||||
import random
|
|
||||||
|
|
||||||
rand_bytes = [random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(15)]
|
|
||||||
random_data = "".join(rand_bytes)
|
|
||||||
while True:
|
|
||||||
remember = random_data[0]
|
|
||||||
random_data = random_data[1:] + remember
|
|
||||||
await self.set_attr("innerHTML", random_data)
|
|
||||||
await asyncio.sleep(0.01)
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
from nio import AsyncClient, RoomMessageText
|
||||||
|
import random
|
||||||
|
|
||||||
|
class BooeehBot:
|
||||||
|
|
||||||
|
def generate_boeh(self):
|
||||||
|
boeh = "b"
|
||||||
|
for _ in range(random.randint(1, 10)):
|
||||||
|
boeh += "o"
|
||||||
|
for _ in range(random.randint(1, 5)):
|
||||||
|
boeh += "e"
|
||||||
|
for _ in range(random.randint(1, 3)):
|
||||||
|
boeh += "e"
|
||||||
|
return boeh
|
||||||
|
|
||||||
|
def __init__(self, url, username, password):
|
||||||
|
self.url = url
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.client = AsyncClient(url, username)
|
||||||
|
|
||||||
|
async def login(self):
|
||||||
|
try:
|
||||||
|
response = await self.client.login(self.password)
|
||||||
|
print(f"Logged in. Serving {self.username}.")
|
||||||
|
return response
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Login error: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def handle_message(self, room, event):
|
||||||
|
specific_user_id = "@joewilliams007:matrix.org"
|
||||||
|
|
||||||
|
if isinstance(event, RoomMessageText):
|
||||||
|
if event.sender == specific_user_id:
|
||||||
|
response_text = self.generate_boeh()
|
||||||
|
try:
|
||||||
|
await self.client.room_send(
|
||||||
|
room.room_id,
|
||||||
|
message_type="m.room.message",
|
||||||
|
content={"msgtype": "m.text", "body": response_text},
|
||||||
|
)
|
||||||
|
print(f"Response to {event.sender}: " + response_text)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to send message: {e}")
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
login_response = await self.login()
|
||||||
|
if not login_response:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.client.add_event_callback(self.handle_message, RoomMessageText)
|
||||||
|
|
||||||
|
await self.client.sync_forever(timeout=30000)
|
||||||
|
|
||||||
|
async def stop(self):
|
||||||
|
await self.client.close()
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
from boeh import BooeehBot, env
|
||||||
|
|
||||||
|
|
||||||
|
async def main_async():
|
||||||
|
url = "https://matrix.org"
|
||||||
|
username = "@retoor2:matrix.org"
|
||||||
|
password = env.secret4
|
||||||
|
bot = BooeehBot(url, username, password)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await bot.start()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
await bot.stop()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
asyncio.run(main_async())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import base64
|
||||||
|
import os
|
||||||
|
|
||||||
|
secret = None
|
||||||
|
secret2 = None
|
||||||
|
secret3 = None
|
||||||
|
secret4 = None
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
secret = input("Type secret: ")
|
||||||
|
print(base64.b64encode(secret.encode()).decode())
|
||||||
|
else:
|
||||||
|
|
||||||
|
try:
|
||||||
|
secret = base64.b64decode(os.getenv("SECRET", "").encode()).decode()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
secret2 = base64.b64decode(os.getenv("SECRET2", "").encode()).decode()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
secret3 = base64.b64decode(os.getenv("SECRET3", "").encode()).decode()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
secret4 = base64.b64decode(os.getenv("SECRET4", "").encode()).decode()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
+5
-34
@@ -1,37 +1,8 @@
|
|||||||
# Written by retoor@molodetz.nl
|
|
||||||
|
|
||||||
# This script initializes a web application using aiohttp and runs it asynchronously with a thread pool executor.
|
|
||||||
|
|
||||||
# Imports aiohttp for web server functionality and concurrent.futures for handling asynchronous execution.
|
|
||||||
|
|
||||||
#
|
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Copyright (c) 2023 Future Contributor
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in all
|
|
||||||
# copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from concurrent.futures import ThreadPoolExecutor as Executor
|
|
||||||
from aiohttp import web
|
|
||||||
from rwebgui.app import Application
|
from rwebgui.app import Application
|
||||||
|
from aiohttp import web
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from concurrent.futures import ThreadPoolExecutor as Executor
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -39,7 +10,7 @@ def main():
|
|||||||
executor = Executor(max_workers=20)
|
executor = Executor(max_workers=20)
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
loop.set_default_executor(executor)
|
loop.set_default_executor(executor)
|
||||||
web.run_app(app, loop=loop, port=3080)
|
web.run_app(app, loop=loop)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+33
-34
@@ -1,14 +1,12 @@
|
|||||||
|
import pathlib
|
||||||
|
from aiohttp import web
|
||||||
|
import uuid
|
||||||
|
from app.app import Application as BaseApplication
|
||||||
|
from rwebgui.component import Component
|
||||||
|
import traceback
|
||||||
|
import time
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import pathlib
|
|
||||||
import time
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from aiohttp import web
|
|
||||||
from app.app import Application as BaseApplication
|
|
||||||
|
|
||||||
from rwebgui.component import Component
|
|
||||||
|
|
||||||
|
|
||||||
class EvalBox(Component):
|
class EvalBox(Component):
|
||||||
|
|
||||||
@@ -20,44 +18,42 @@ class EvalBox(Component):
|
|||||||
try:
|
try:
|
||||||
result = eval(value)
|
result = eval(value)
|
||||||
value = value + "= " + str(result)
|
value = value + "= " + str(result)
|
||||||
await self.set_attr("value", value)
|
await self.set_attr("value",value)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
except AttributeError:
|
except AttributeError as ex:
|
||||||
print(value)
|
print(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Button(Component):
|
class Button(Component):
|
||||||
|
|
||||||
async def on_click(self, event):
|
async def on_click(self, event):
|
||||||
component = self.app.search
|
component = self.app.search
|
||||||
await component.set_attr("value", "Woeiii")
|
await component.set_attr("value","Woeiii")
|
||||||
|
|
||||||
|
|
||||||
class Button1(Component):
|
class Button1(Component):
|
||||||
|
|
||||||
async def on_click(self, event):
|
async def on_click(self,event):
|
||||||
field = self.app.search
|
field = self.app.search
|
||||||
await field.toggle()
|
await field.toggle()
|
||||||
value = await field.get_style("display", "block")
|
value = await field.get_style("display","block")
|
||||||
await self.set_attr("innerText", value)
|
await self.set_attr("innerText", value)
|
||||||
|
|
||||||
|
|
||||||
class RandomString(Component):
|
class RandomString(Component):
|
||||||
|
|
||||||
|
|
||||||
async def task_random(self):
|
async def task_random(self):
|
||||||
import random
|
import random
|
||||||
|
|
||||||
rand_bytes = [random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(15)]
|
rand_bytes = [random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(15)]
|
||||||
random_data = "".join(rand_bytes)
|
random_data = "".join(rand_bytes)
|
||||||
while True:
|
while True:
|
||||||
remember = random_data[0]
|
remember = random_data[0]
|
||||||
random_data = random_data[1:] + remember
|
random_data = random_data[1:] + remember
|
||||||
await self.set_attr("innerHTML", random_data)
|
await self.set_attr("innerHTML",random_data)
|
||||||
await asyncio.sleep(0.01)
|
await asyncio.sleep(0.01)
|
||||||
|
|
||||||
|
|
||||||
class Counter(Component):
|
class Counter(Component):
|
||||||
|
|
||||||
async def task_test(self):
|
async def task_test(self):
|
||||||
@@ -65,6 +61,8 @@ class Counter(Component):
|
|||||||
await asyncio.sleep(10)
|
await asyncio.sleep(10)
|
||||||
print("Slow task")
|
print("Slow task")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def task_increment(self):
|
async def task_increment(self):
|
||||||
if not self.value:
|
if not self.value:
|
||||||
self.value = 0
|
self.value = 0
|
||||||
@@ -74,36 +72,31 @@ class Counter(Component):
|
|||||||
except:
|
except:
|
||||||
self.value = 0
|
self.value = 0
|
||||||
self.value += 1
|
self.value += 1
|
||||||
await self.set_attr("value", self.value)
|
await self.set_attr("value",self.value)
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
class GPT(Component):
|
class GPT(Component):
|
||||||
|
|
||||||
class Children:
|
class Children:
|
||||||
prompt = Component
|
prompt = Component
|
||||||
answer = Component
|
answer = Component
|
||||||
|
|
||||||
class submit(Component):
|
class submit(Component):
|
||||||
async def trigger(self, id_, event, data):
|
async def trigger(self, id_, event, data):
|
||||||
print("GOGOG", event, data)
|
print("GOGOG",event,data)
|
||||||
return await super().trigger(id_, event, data)
|
return await super().trigger(id_, event, data)
|
||||||
|
async def on_click(self,data):
|
||||||
async def on_click(self, data):
|
|
||||||
from xmlrpc.client import ServerProxy
|
from xmlrpc.client import ServerProxy
|
||||||
|
|
||||||
client = ServerProxy("https://api.molodetz.nl/rpc")
|
client = ServerProxy("https://api.molodetz.nl/rpc")
|
||||||
prompt = await self.app.prompt.get_attr("value")
|
prompt = await self.app.prompt.get_attr("value")
|
||||||
print(prompt)
|
print(prompt)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
await self.answer.set_attr("value", client.gpt4o(prompt))
|
await self.answer.set_attr("value",client.gpt4o(prompt))
|
||||||
return {"event_id": data["event_id"], "success": True}
|
return {"event_id":data['event_id'],"success":True}
|
||||||
|
|
||||||
|
|
||||||
class SpeedMeter(Component):
|
class SpeedMeter(Component):
|
||||||
|
|
||||||
def __init__(self, app, id_, description=None, ws=None):
|
def __init__(self, app, id_, description=None, ws = None):
|
||||||
self.time_start = time.time()
|
self.time_start = time.time()
|
||||||
self.bytes_received = 0
|
self.bytes_received = 0
|
||||||
super().__init__(app, id_, description, ws)
|
super().__init__(app, id_, description, ws)
|
||||||
@@ -113,7 +106,7 @@ class SpeedMeter(Component):
|
|||||||
bytes_received = self.bytes_received
|
bytes_received = self.bytes_received
|
||||||
self.bytes_received = 0
|
self.bytes_received = 0
|
||||||
|
|
||||||
await self.set_attr("value", f"{bytes_received / 1000} kb/s")
|
await self.set_attr("value","{} kb/s".format(bytes_received / 1000))
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
async def trigger(self, id_, event, data):
|
async def trigger(self, id_, event, data):
|
||||||
@@ -149,25 +142,31 @@ class Application(BaseApplication):
|
|||||||
self.location_static = self.location.joinpath("static")
|
self.location_static = self.location.joinpath("static")
|
||||||
self.template_path = self.location.joinpath("templates")
|
self.template_path = self.location.joinpath("templates")
|
||||||
super().__init__(template_path=self.template_path)
|
super().__init__(template_path=self.template_path)
|
||||||
self.router.add_static("/static", self.location_static)
|
self.router.add_static('/static', self.location_static)
|
||||||
self.router.add_get("/", self.index_handler)
|
self.router.add_get("/", self.index_handler)
|
||||||
self.router.add_get("/ws/{uuid}", self.websocket_handler)
|
self.router.add_get("/ws/{uuid}", self.websocket_handler)
|
||||||
|
|
||||||
async def websocket_handler(self, request):
|
async def websocket_handler(self, request):
|
||||||
uuid_value = request.match_info["uuid"]
|
# Extract the UUID from the route
|
||||||
|
uuid_value = request.match_info['uuid']
|
||||||
|
|
||||||
|
# Validate if it's a valid UUID
|
||||||
try:
|
try:
|
||||||
uuid_obj = uuid.UUID(uuid_value)
|
uuid_obj = uuid.UUID(uuid_value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return web.Response(text="Invalid UUID", status=400)
|
return web.Response(text="Invalid UUID", status=400)
|
||||||
|
|
||||||
|
# Upgrade the connection to WebSocket
|
||||||
ws = web.WebSocketResponse()
|
ws = web.WebSocketResponse()
|
||||||
await ws.prepare(request)
|
await ws.prepare(request)
|
||||||
|
|
||||||
|
print(f"WebSocket connection established with UUID: {uuid_obj}")
|
||||||
component = App(self, "app", ws=ws)
|
component = App(self, "app", ws=ws)
|
||||||
await component.service()
|
await component.service()
|
||||||
|
|
||||||
return ws
|
return ws
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def index_handler(self, request):
|
async def index_handler(self, request):
|
||||||
return await self.render_template("index.html", request, {})
|
return await self.render_template("index.html",request,{})
|
||||||
|
|||||||
+77
-82
@@ -1,41 +1,18 @@
|
|||||||
# Written by retoor@molodetz.nl
|
|
||||||
|
|
||||||
# This module defines a Component class that facilitates WebSocket communication and management of various tasks in an asynchronous environment. It allows dynamic creation of child components and interaction through callbacks and events.
|
|
||||||
|
|
||||||
# Imports used: aiohttp (external library for client-server communication)
|
|
||||||
|
|
||||||
# MIT License
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import time
|
|
||||||
import uuid
|
import uuid
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import aiohttp
|
||||||
|
import asyncio
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
|
||||||
class Component:
|
class Component:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def define(cls):
|
def define(cls):
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
def __init__(self, app, id_, description=None, ws: web.WebSocketResponse = None):
|
def __init__(self,app, id_, description=None,ws: web.WebSocketResponse=None):
|
||||||
|
|
||||||
self.id = id_
|
self.id = id_
|
||||||
self.ws = ws
|
self.ws = ws
|
||||||
self.app = app
|
self.app = app
|
||||||
@@ -44,19 +21,19 @@ class Component:
|
|||||||
self._callbacks = {}
|
self._callbacks = {}
|
||||||
self.value = None
|
self.value = None
|
||||||
self._running = False
|
self._running = False
|
||||||
if not hasattr(self, "Children"):
|
if not hasattr(self,"Children"):
|
||||||
return
|
return
|
||||||
|
|
||||||
for name in dir(self.Children):
|
for name in dir(self.Children):
|
||||||
if name.startswith("__"):
|
if name.startswith("__"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
obj = getattr(self.Children, name)
|
obj = getattr(self.Children, name)
|
||||||
instance = obj(app=self.app, id_=name, ws=ws)
|
|
||||||
|
instance = obj(app=self.app,id_=name,ws=ws )
|
||||||
self.add_child(instance)
|
self.add_child(instance)
|
||||||
instance.app = self
|
instance.app = self
|
||||||
instance.ws = self.ws
|
instance.ws = self.ws
|
||||||
setattr(self, name, instance)
|
setattr(self, name, instance)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, json):
|
def from_json(cls, json):
|
||||||
obj = cls(None, None)
|
obj = cls(None, None)
|
||||||
@@ -65,7 +42,7 @@ class Component:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_json(cls):
|
def to_json(cls):
|
||||||
obj = cls.__dict__.copy()
|
obj = cls.__dict__ .copy()
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -80,21 +57,21 @@ class Component:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def tasks(self):
|
def tasks(self):
|
||||||
tasks_ = [
|
tasks_ = [getattr(self, name) for name in dir(self) if name.startswith("task_") and hasattr(self, name)]
|
||||||
getattr(self, name)
|
|
||||||
for name in dir(self)
|
|
||||||
if name.startswith("task_") and hasattr(self, name)
|
|
||||||
]
|
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
tasks_ += child.tasks
|
tasks_ += child.tasks#.extend(await child.get_tasks())
|
||||||
return tasks_
|
return tasks_
|
||||||
|
|
||||||
async def communicate(self, event_id=None):
|
async def communicate(self, event_id=None):
|
||||||
|
|
||||||
async for msg in self.ws:
|
async for msg in self.ws:
|
||||||
if msg.type == web.WSMsgType.TEXT:
|
if msg.type == web.WSMsgType.TEXT:
|
||||||
|
# Echo the message back to the client
|
||||||
|
#print(f"Received message: {msg.data}")
|
||||||
data = msg.json()
|
data = msg.json()
|
||||||
if not event_id:
|
if not event_id:
|
||||||
pass
|
pass
|
||||||
|
#return data
|
||||||
else:
|
else:
|
||||||
if data.get("event_id") == event_id:
|
if data.get("event_id") == event_id:
|
||||||
return data
|
return data
|
||||||
@@ -103,50 +80,54 @@ class Component:
|
|||||||
def callbacks(self):
|
def callbacks(self):
|
||||||
return hasattr(self.app, "callbacks") and self.app.callbacks or self._callbacks
|
return hasattr(self.app, "callbacks") and self.app.callbacks or self._callbacks
|
||||||
|
|
||||||
async def trigger(self, id_, event, data):
|
|
||||||
|
async def trigger(self,id_, event,data):
|
||||||
if self.id == id_:
|
if self.id == id_:
|
||||||
method_name = "on_" + event
|
method_name = "on_"+event
|
||||||
if hasattr(self, method_name):
|
if hasattr(self, method_name):
|
||||||
method = getattr(self, method_name)
|
method = getattr(self, method_name)
|
||||||
await method(data)
|
await method(data)
|
||||||
|
print("JAAJ")
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
await child.trigger(id_, event, data)
|
await child.trigger(id_,event,data)
|
||||||
|
|
||||||
async def register_callback(self, event_id, callback):
|
async def register_callback(self, event_id, callback):
|
||||||
self.callbacks[event_id] = callback
|
self.callbacks[event_id] = callback
|
||||||
|
|
||||||
async def call(self, method, args=None, id_=None, callback=True):
|
async def call(self, method, args=None,id_=None, callback=True):
|
||||||
while not self.running:
|
while not self.running:
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
if not args:
|
if not args:
|
||||||
args = []
|
args= []
|
||||||
event_id = str(uuid.uuid4())
|
event_id = str(uuid.uuid4())
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
future = loop.create_future()
|
future = loop.create_future()
|
||||||
|
|
||||||
self.callbacks[event_id] = lambda data: future.set_result(data)
|
self.callbacks[event_id] = lambda data: future.set_result(data)
|
||||||
await self.ws.send_json(
|
await self.ws.send_json({
|
||||||
{
|
"event_id": event_id,
|
||||||
"event_id": event_id,
|
"event": "call",
|
||||||
"event": "call",
|
"id": id_ and id_ or self.id,
|
||||||
"id": id_ and id_ or self.id,
|
"method": method,
|
||||||
"method": method,
|
"args": args,
|
||||||
"args": args,
|
"callback": callback
|
||||||
"callback": callback,
|
})
|
||||||
}
|
|
||||||
)
|
|
||||||
if callback:
|
if callback:
|
||||||
response = await self.communicate(event_id=event_id)
|
response = await self.communicate(event_id=event_id)
|
||||||
return response["result"]
|
return response['result']
|
||||||
|
#print("GLUKT")
|
||||||
|
#return response['result']
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
#return await future
|
||||||
|
|
||||||
|
|
||||||
async def get_attr(self, key, default=None):
|
async def get_attr(self, key, default=None):
|
||||||
result = await self.call("getAttr", [self.id, key], True)
|
result = await self.call("getAttr", [self.id, key],True)
|
||||||
return result or default
|
return result or default
|
||||||
|
|
||||||
async def set_attr(self, key, value):
|
async def set_attr(self, key, value):
|
||||||
result = await self.call("setAttr", [self.id, key, value], callback=False)
|
result = await self.call("setAttr", [self.id,key,value],callback=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def get(self, id_):
|
async def get(self, id_):
|
||||||
@@ -158,19 +139,20 @@ class Component:
|
|||||||
return child
|
return child
|
||||||
|
|
||||||
async def set_data(self, key, value):
|
async def set_data(self, key, value):
|
||||||
result = await self.call("setData", [self.id, key, value], callback=False)
|
result = await self.call("setData", [self.id, key,value], callback=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def get_data(self, key, default=None):
|
async def get_data(self, key, default=None):
|
||||||
result = await self.call("getData", [self.id, key], default, True)
|
result = await self.call("getData", [self.id,key], default,True)
|
||||||
return result or default
|
return result or default
|
||||||
|
|
||||||
async def set_style(self, key, value):
|
async def set_style(self, key, value):
|
||||||
result = await self.call("setStyle", [self.id, key, value], callback=False)
|
result = await self.call("setStyle", [self.id, key,value], callback=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def toggle(self):
|
async def toggle(self):
|
||||||
value = await self.get_style("display", "block")
|
value = await self.get_style("display", "block")
|
||||||
|
|
||||||
if value == "none":
|
if value == "none":
|
||||||
value = ""
|
value = ""
|
||||||
else:
|
else:
|
||||||
@@ -178,10 +160,13 @@ class Component:
|
|||||||
await self.set_style("display", value)
|
await self.set_style("display", value)
|
||||||
|
|
||||||
async def get_style(self, key, default=None):
|
async def get_style(self, key, default=None):
|
||||||
result = await self.call("getStyle", [self.id, key], default)
|
result = await self.call("getStyle", [self.id,key], default)
|
||||||
return result or default
|
return result or default
|
||||||
|
|
||||||
async def on_keyup(self, event):
|
|
||||||
|
|
||||||
|
|
||||||
|
async def on_keyup(self,event):
|
||||||
value = await self.get_attr("value")
|
value = await self.get_attr("value")
|
||||||
if self.value != value:
|
if self.value != value:
|
||||||
if hasattr(self, "on_change"):
|
if hasattr(self, "on_change"):
|
||||||
@@ -189,10 +174,11 @@ class Component:
|
|||||||
self.value = value
|
self.value = value
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
async def get_tasks(self):
|
async def get_tasks(self):
|
||||||
tasks = self.tasks
|
tasks = self.tasks
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
tasks += child.tasks
|
tasks += child.tasks#.extend(await child.get_tasks())
|
||||||
return tasks
|
return tasks
|
||||||
|
|
||||||
async def set_running(self):
|
async def set_running(self):
|
||||||
@@ -210,33 +196,42 @@ class Component:
|
|||||||
try:
|
try:
|
||||||
async for msg in self.ws:
|
async for msg in self.ws:
|
||||||
if msg.type == web.WSMsgType.TEXT:
|
if msg.type == web.WSMsgType.TEXT:
|
||||||
|
# Echo the message back to the client
|
||||||
|
#print(f"Received message: {msg.data}")
|
||||||
data = msg.json()
|
data = msg.json()
|
||||||
response = {"event_id": data["event_id"], "success": True}
|
response = {"event_id":data['event_id'],"success":True}
|
||||||
response["time_start"] = time.time()
|
response['time_start'] = time.time()
|
||||||
if self.callbacks.get(data["event_id"]):
|
if self.callbacks.get(data['event_id']):
|
||||||
self.callbacks[data["event_id"]](data["result"])
|
self.callbacks[data['event_id']](data['result'])
|
||||||
elif data.get("data") and not data["data"].get("id"):
|
elif data.get('data') and not data['data'].get('id'):
|
||||||
response["handled"] = False
|
response['handled'] = False
|
||||||
elif data.get("data"):
|
elif data.get('data'):
|
||||||
response["handled"] = True
|
response['handled'] = True
|
||||||
response["data"] = await self.trigger(
|
response['data'] = await self.trigger(data['data']['id'], data['event'],data['data'])
|
||||||
data["data"]["id"], data["event"], data["data"]
|
response['cancel'] = True
|
||||||
)
|
|
||||||
response["cancel"] = True
|
|
||||||
|
|
||||||
response["time_end"] = time.time()
|
response['time_end'] = time.time()
|
||||||
response["time_duration"] = (
|
response['time_duration'] = response['time_end'] - response['time_start']
|
||||||
response["time_end"] - response["time_start"]
|
|
||||||
)
|
|
||||||
await self.ws.send_json(response)
|
await self.ws.send_json(response)
|
||||||
|
|
||||||
|
#await ws.send_str(f"Echo: {msg.data}")
|
||||||
elif msg.type == web.WSMsgType.ERROR:
|
elif msg.type == web.WSMsgType.ERROR:
|
||||||
print(f"WebSocket error: {self.ws.exception()}")
|
print(f"WebSocket error: {self.ws.exception()}")
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print(ex)
|
print(ex)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#async def the_task():
|
||||||
|
# while True:
|
||||||
|
# time.sleep(1)
|
||||||
|
#while True:
|
||||||
tasks.append(events)
|
tasks.append(events)
|
||||||
await asyncio.gather(*[task() for task in tasks])
|
await asyncio.gather(*[task() for task in tasks])
|
||||||
|
#await asyncio.create_task(asyncio.gather(*[task() for task in tasks]))
|
||||||
|
#await tasks()
|
||||||
|
print("AFTERR")
|
||||||
|
|
||||||
|
|
||||||
def add_child(self, child):
|
def add_child(self, child):
|
||||||
child.app = self.app
|
child.app = self.app
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const allEvents = [
|
|||||||
'dragenter', 'dragleave', 'touchstart', 'touchmove', 'touchend',
|
'dragenter', 'dragleave', 'touchstart', 'touchmove', 'touchend',
|
||||||
'touchcancel', 'pointerdown', 'pointerup', 'pointermove', 'pointerover',
|
'touchcancel', 'pointerdown', 'pointerup', 'pointermove', 'pointerover',
|
||||||
'pointerout', 'pointerenter', 'pointerleave', 'wheel'/*'scroll',*/
|
'pointerout', 'pointerenter', 'pointerleave', 'wheel'/*'scroll',*/
|
||||||
|
// Add more as needed
|
||||||
];
|
];
|
||||||
const props = [
|
const props = [
|
||||||
'data',
|
'data',
|
||||||
@@ -103,15 +104,20 @@ const callback = (mutationList, observer) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Create an observer instance linked to the callback function
|
||||||
const observer = new MutationObserver(callback);
|
const observer = new MutationObserver(callback);
|
||||||
|
|
||||||
|
// Start observing the target node for configured mutations
|
||||||
observer.observe(rWebGui, config);
|
observer.observe(rWebGui, config);
|
||||||
|
|
||||||
|
// Later, you can stop observing
|
||||||
|
//observer.disconnect();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RWebGuiApp extends HTMLElement {
|
class MyCustomElement extends HTMLElement {
|
||||||
|
static observedAttributes = ["color", "size"];
|
||||||
_ready = false
|
_ready = false
|
||||||
_uuid = null
|
_uuid = null
|
||||||
ws = null
|
ws = null
|
||||||
@@ -128,16 +134,11 @@ class RWebGuiApp extends HTMLElement {
|
|||||||
return this.app._ready && this.app.connected
|
return this.app._ready && this.app.connected
|
||||||
}
|
}
|
||||||
|
|
||||||
get url() {
|
|
||||||
const protocol = window.location.protocol === "https:" ? "wss" : "ws";
|
|
||||||
return `${protocol}://${window.location.host}/ws/${this.uuid}`
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Always call super first in constructor
|
// Always call super first in constructor
|
||||||
super();
|
super();
|
||||||
if(!this.parent || !this.parent.app){
|
if(!this.parent || !this.parent.app){
|
||||||
this.ws = new WebSocket(this.url)
|
this.ws = new WebSocket(`ws://${window.location.host}/ws/${this.uuid}`)
|
||||||
const me = this
|
const me = this
|
||||||
this.ws.onopen = ()=>{
|
this.ws.onopen = ()=>{
|
||||||
me.connected = true;
|
me.connected = true;
|
||||||
@@ -251,6 +252,7 @@ class RWebGuiApp extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
|
console.log("Custom element added to page.");
|
||||||
|
|
||||||
|
|
||||||
this.rWebGui()
|
this.rWebGui()
|
||||||
@@ -268,15 +270,25 @@ class RWebGuiApp extends HTMLElement {
|
|||||||
this._ready = true
|
this._ready = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
console.log("Custom element removed from page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
adoptedCallback() {
|
||||||
|
console.log("Custom element moved to new page.");
|
||||||
|
}
|
||||||
|
|
||||||
attributeChangedCallback(name, oldValue, newValue) {
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
this.emit("attributeChanged", {aa:123})
|
this.emit("attributeChanged", {aa:123})
|
||||||
|
console.log(`Attribute ${name} has changed.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("rwebgui-app",RWebGuiApp);
|
customElements.define("rwebgui-app", MyCustomElement);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
console.log("DOM fully loaded and parsed");
|
||||||
document.querySelectorAll("*").forEach(child => {
|
document.querySelectorAll("*").forEach(child => {
|
||||||
child.rWebGui()
|
child.rWebGui()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user