Update snek core bot.

This commit is contained in:
retoor 2025-04-24 21:03:12 +02:00
parent 7c688c1c9b
commit a91d4e72ec
4 changed files with 143 additions and 96 deletions

View File

@ -42,23 +42,19 @@ class ExampleBot(Bot):
async def on_join(self, channel_uid):
print(f"I joined!")
await self.send_message(
channel_uid,
f"Hello, i'm actively part of the conversation in channel {channel_uid} now, you don't have to mention me anymore. "
channel_uid,
f"Hello, i'm actively part of the conversation in channel {channel_uid} now, you don't have to mention me anymore. ",
)
async def on_leave(self, channel_uid):
print(f"I left!!")
await self.send_message(
channel_uid,
"I stop actively being part of the conversation now. Bye!"
channel_uid, "I stop actively being part of the conversation now. Bye!"
)
async def on_ping(self,username, user_nick, channel_uid, message):
async def on_ping(self, username, user_nick, channel_uid, message):
print(f"Ping from {user_nick} in channel {channel_uid}: {message}")
await self.send_message(
channel_uid,
"pong " + message
)
await self.send_message(channel_uid, "pong " + message)
async def on_own_message(self, data):
print(f"Received my own message: {data.message}")
@ -80,7 +76,9 @@ class ExampleBot(Bot):
)
await self.send_message(channel_uid, result)
else:
await self.send_message(channel_uid, f'Hey {user_nick}, Thanks for mentioning me "{message}".')
await self.send_message(
channel_uid, f'Hey {user_nick}, Thanks for mentioning me "{message}".'
)
async def on_message(self, sender_username, sender_nick, channel_uid, message):
print(f"Message from {sender_nick}: {message}")
@ -98,7 +96,9 @@ class ExampleBot(Bot):
await self.send_message(channel_uid, result)
bot = ExampleBot(url="ws://snek.molodetz.nl/rpc.ws", username="example", password="example")
bot = ExampleBot(
url="ws://snek.molodetz.nl/rpc.ws", username="example", password="example"
)
asyncio.run(bot.run())
```

View File

@ -11,23 +11,19 @@ class ExampleBot(Bot):
async def on_join(self, channel_uid):
print(f"I joined!")
await self.send_message(
channel_uid,
f"Hello, i'm actively part of the conversation in channel {channel_uid} now, you don't have to mention me anymore. "
channel_uid,
f"Hello, i'm actively part of the conversation in channel {channel_uid} now, you don't have to mention me anymore. ",
)
async def on_leave(self, channel_uid):
print(f"I left!!")
await self.send_message(
channel_uid,
"I stop actively being part of the conversation now. Bye!"
channel_uid, "I stop actively being part of the conversation now. Bye!"
)
async def on_ping(self,username, user_nick, channel_uid, message):
async def on_ping(self, username, user_nick, channel_uid, message):
print(f"Ping from {user_nick} in channel {channel_uid}: {message}")
await self.send_message(
channel_uid,
"pong " + message
)
await self.send_message(channel_uid, "pong " + message)
async def on_own_message(self, data):
print(f"Received my own message: {data.message}")
@ -49,7 +45,9 @@ class ExampleBot(Bot):
)
await self.send_message(channel_uid, result)
else:
await self.send_message(channel_uid, f'Hey {user_nick}, Thanks for mentioning me "{message}".')
await self.send_message(
channel_uid, f'Hey {user_nick}, Thanks for mentioning me "{message}".'
)
async def on_message(self, sender_username, sender_nick, channel_uid, message):
print(f"Message from {sender_nick}: {message}")
@ -67,5 +65,7 @@ class ExampleBot(Bot):
await self.send_message(channel_uid, result)
bot = ExampleBot(url="ws://snek.molodetz.nl/rpc.ws", username="example", password="xxxxxx")
bot = ExampleBot(
url="ws://snek.molodetz.nl/rpc.ws", username="example", password="xxxxxx"
)
asyncio.run(bot.run())

View File

@ -12,15 +12,16 @@
# MIT License
import asyncio
import logging
import traceback
import aiohttp
from snekbot.rpc import RPC
import logging
logger = logging.getLogger("snekbot")
class Bot:
def __init__(self, username, password, url="wss://snek.molodetz.nl/rpc.ws"):
@ -33,20 +34,41 @@ class Bot:
self.ws = None
self.joined = set()
async def on_join(self, channel_uid):
self.joined.add(channel_uid)
logger.debug("Joined channel: " + channel_uid)
async def on_leave(self, channel_uid):
self.joined.remove(channel_uid)
logger.debug("Left channel: " + channel_uid)
async def on_mention(self, username, user_nick, channel_uid, message):
logger.debug("Received mention from " + username + ": " + message)
async def on_idle(self):
logger.debug("Bot is idle.")
async def on_ping(self, username, user_nick, channel_uid, message):
logger.debug("Received ping from " + username + ": " + message)
async def on_own_message(self, channel_uid, message):
logger.debug("Received own message: " + message)
async def on_message(self, username, user_nick, channel_uid, message):
logger.debug("Received message from " + username + ": " + message)
async def run(self, reconnect=True):
while True:
try:
await self.run_once()
except Exception as ex:
print(ex)
except:
traceback.print_exc()
await asyncio.sleep(1)
if not reconnect:
break
def has_joined(self, channel_uid):
return channel_uid in self.joined
@ -55,57 +77,71 @@ class Bot:
await self.rpc.send_message(channel_uid, message)
return True
async def get_channels(self):
return await (await self.rpc.get_channels())()
async def run_once(self):
async with aiohttp.ClientSession() as session:
async with session.ws_connect(self.url) as ws:
self.ws = ws
self.rpc = RPC(ws)
rpc = self.rpc
success = await (await rpc.login(self.username, self.password))()
print(success)
self.channels = await (await rpc.get_channels())()
rpc = RPC(self.ws)
self.rpc = rpc
await (await rpc.login(self.username, self.password))()
self.channels = await self.get_channels()
for channel in self.channels:
logger.debug("Found channel: " + channel["name"])
self.user = (await (await rpc.get_user(None))())
self.user = await (await rpc.get_user(None))()
logger.debug("Logged in as: " + self.user["username"])
while True:
await self.on_idle()
data = await rpc.receive()
if data is None:
break
try:
try:
await self.on_idle()
except AttributeError:
pass
message = data.message.strip()
except AttributeError:
continue
data = await rpc.receive()
if data is None:
break
try:
message = data.message.strip()
except AttributeError:
continue
if data.username == self.user['username']:
await self.on_own_message(data.channel_uid, message)
elif message.startswith("ping"):
await self.on_ping(data.username, data.user_nick, data.channel_uid, data.message.lstrip("ping ").strip())
elif any([
"@" + self.user['nick'] + " join" in data.message,
"@" + self.user['username'] + " join" in data.message]):
self.joined.add(data.channel_uid)
await self.on_join(data.channel_uid)
elif any([
"@" + self.user['nick'] + " leave" in data.message,
"@" + self.user['username'] + " leave" in data.message]):
self.joined.remove(data.channel_uid)
await self.on_leave(data.channel_uid)
elif "@" + self.user['nick'] in data.message or "@" + self.user['username'] in data.message:
await self.on_mention(data.username, data.user_nick, data.channel_uid, data.message)
else:
await self.on_message(data.username, data.user_nick, data.channel_uid, data.message)
except AttributeError as ex:
logger.exception(ex)
raise
except Exception as ex:
raise ex
if data.username == self.user["username"]:
await self.on_own_message(data.channel_uid, message)
elif message.startswith("ping"):
await self.on_ping(
data.username,
data.user_nick,
data.channel_uid,
data.message.lstrip("ping ").strip(),
)
elif any(
[
"@" + self.user["nick"] + " join" in data.message,
"@" + self.user["username"] + " join" in data.message,
]
):
await self.on_join(data.channel_uid)
elif any(
[
"@" + self.user["nick"] + " leave" in data.message,
"@" + self.user["username"] + " leave" in data.message,
]
):
await self.on_leave(data.channel_uid)
elif (
"@" + self.user["nick"] in data.message
or "@" + self.user["username"] in data.message
):
await self.on_mention(
data.username,
data.user_nick,
data.channel_uid,
data.message,
)
else:
await self.on_message(
data.username,
data.user_nick,
data.channel_uid,
data.message,
)

View File

@ -1,6 +1,6 @@
# Written by retoor@molodetz.nl
# This code defines an RPC class that allows asynchronous communication over websockets,
# This code defines an RPC class that allows asynchronous communication over websockets,
# including command execution and handling of asynchronous responses using Python's asyncio and websockets library.
# Uses aiohttp for asynchronous HTTP network communication.
@ -9,22 +9,23 @@
import json
import subprocess
import logging
import pathlib
import aiohttp
import uuid
import subprocess
import uuid
import logging
import aiohttp
logger = logging.getLogger("snekbot.rpc")
class RPC:
class Response:
def __init__(self, msg):
if isinstance(msg, list):
self.list = msg
self.list = msg
self.__dict__.update(msg)
def __iter__(self):
for k in self.__dict__.get("data", []):
yield k
@ -38,40 +39,47 @@ class RPC:
return self.__dict__[name]
except:
pass
return self.__dict__.get('data',{})[name]
return self.__dict__.get("data", {})[name]
def __setitem__(self, name, value):
if not name in self.__dict__.get('data',{}):
if name not in self.__dict__.get("data", {}):
self.__dict__[name] = value
else:
self.__dict__['data'][name] = value
self.__dict__["data"][name] = value
def __str__(self):
return json.dumps(self.__dict__, default=str, indent=2)
def __init__(self, ws):
self.ws = ws
self.current_call_id = None
self.current_call_id = None
async def echo(self, data):
logger.debug("Schedule for retry: " + str(data))
await self.ws.send_json(dict(method="echo",args=[data]))
await self.ws.send_json({"method": "echo", "args": [data]})
def __getattr__(self, name):
async def method(*args, **kwargs):
self.current_call_id = str(uuid.uuid4())
payload = dict(method=name, args=args, kwargs=kwargs, callId=self.current_call_id)
payload = {
"method": name,
"args": args,
"kwargs": kwargs,
"callId": self.current_call_id,
}
await self.ws.send_json(payload)
async def returner():
while True:
while True:
response = await self.ws.receive()
data = response.json()
if not data.get("callId") == self.current_call_id:
await self.echo(data)
continue
return self.Response(data)
return returner
return returner
return method
async def system(self, command):
@ -108,15 +116,18 @@ class RPC:
break
if msg.type == aiohttp.WSMsgType.CLOSED:
logger.exception("WebSocket closed.")
break
break
elif msg.type == aiohttp.WSMsgType.ERROR:
logger.exception("WebSocket error.")
break
elif msg.type == aiohttp.WSMsgType.TEXT:
if self.current_call_id and not msg.json().get('callId') != self.current_call_id:
if (
self.current_call_id
and not msg.json().get("callId") != self.current_call_id
):
await self.echo(msg.json())
continue
continue
try:
response = self.Response(msg.json())
self.current_call_id = None
@ -126,5 +137,5 @@ class RPC:
break
else:
logger.exception("Unexpected message type.")
break
break
return None