Files
pymetaverse/examples/bots/complexBot/main.py

177 lines
5.8 KiB
Python
Raw Normal View History

2025-06-28 07:33:08 -04:00
import logging
logging.basicConfig(
level=logging.INFO, # Or INFO, WARNING, etc.
format="%(asctime)s [%(levelname)s] %(message)s"
)
import asyncio
import json
import time
import datetime
import traceback
import uuid
import re
import importlib
from aiohttp import web
2025-08-19 08:56:20 -04:00
from metaverse import login
from metaverse.bot import SimpleBot
from metaverse.viewer import messages
from metaverse.const import *
2025-06-28 07:33:08 -04:00
def loadDictIntoMessage(msg, data):
for name, block in data.items():
if type(data[name]) == list:
for i, varblock in enumerate(data[name]):
for key, value in data[name][i].items():
if type(value) == dict:
if value["type"] == "bytestring":
value = value["data"].encode() + b"\0"
elif value["type"] == "bytes":
value = bytes(value["data"])
msg.blocks[name][i].values[key] = value
msg.blocks[name].count = len(data[name])
elif type(data[name]) == dict:
for key, value in data[name].items():
if type(value) == dict:
if value["type"] == "bytestring":
value = value["data"].encode() + b"\0"
elif value["type"] == "bytes":
value = bytes(value["data"])
msg.blocks[name].values[key] = value
class BotInstance:
def __init__(self, username, password, features = None):
self.username = username
self.password = password
self.features = features or []
self.routes = []
self.bot = None
def route(self, pattern):
def decorator(func):
regex = re.compile(f"^{pattern}$")
self.routes.append((regex, func))
return func
return decorator
async def handle_request(self, request, path):
for regex, handler in self.routes:
match = regex.match(path)
if match:
return await handler(request, **match.groupdict())
logging.warning("[{}] No handler for path: {}".format(
".".join(self.bot.agent.username),
path
))
return web.Response(status=404, text="No handler for path")
async def run(self):
while True:
try:
bot = SimpleBot()
self.bot = bot
self.routes = []
for feature in self.features:
feature(self, bot)
await bot.login(self.username, self.password)
logging.info("[{}] Logged in.".format(
".".join(bot.agent.username)
))
await bot.run()
logging.info("[{}] Logged out.".format(
".".join(bot.agent.username)
))
self.bot = None
except asyncio.exceptions.CancelledError as e:
break
def load_callable(path: str):
"""Loads a function or object from a string like 'package.module:func'."""
if ':' not in path:
raise ValueError(f"Invalid path '{path}', expected format 'module.submodule:function'")
module_path, func_name = path.split(':', 1)
module = importlib.import_module(module_path)
try:
return getattr(module, func_name)
except AttributeError:
raise ImportError(f"Function '{func_name}' not found in module '{module_path}'")
async def main():
with open("bots.json", "r") as f:
bots = json.load(f)
instances = []
for bot in bots:
if bot.get("disabled", False) == True:
continue
functions = []
for function_path in bot.get("functions", []):
functions.append(load_callable(function_path))
instance = BotInstance(bot["username"], bot["password"], functions)
instances.append(instance)
async def handle_bot_request(request):
uuid_str = request.match_info['uuid']
path = request.match_info.get('path', "")
bot = None
try:
bot_uuid = str(uuid.UUID(uuid_str))
for instance in instances:
try:
if instance.bot.agent.agentId == uuid_str:
bot = instance
break
except Exception as e:
raise e
except ValueError:
for instance in instances:
try:
if ".".join(instance.bot.agent.username).lower() == uuid_str.lower():
bot = instance
break
except Exception as e:
raise e
if bot:
return await bot.handle_request(request, path)
logging.warning("[WEB] No bot found: {}".format(
uuid_str
))
return web.Response(status=404, text="Bot not found")
async def handle_bot_index(request):
response = {}
for instance in instances:
response[instance.bot.agent.agentId] = {
"username": list(instance.bot.agent.username) if instance.bot.agent.username != (None, None) else []
}
return web.Response(status=200, text=json.dumps(response))
async def run_web_server():
app = web.Application()
app.router.add_get('/bot/{uuid}/{path:.*}', handle_bot_request)
app.router.add_get('/bot/{uuid}', handle_bot_request)
app.router.add_get('/bot', handle_bot_index)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, 'localhost', 26875)
await site.start()
# Launch both the web server and bots
await asyncio.gather(
run_web_server(),
*[instance.run() for instance in instances]
)
# Run everything
asyncio.run(main())