Update snek core bot.
This commit is contained in:
parent
7c688c1c9b
commit
a91d4e72ec
22
README.md
22
README.md
@ -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())
|
||||
```
|
||||
|
||||
|
22
example.py
22
example.py
@ -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())
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user