yaboli/yaboli/controller.py
Joscha da84c6685e Improve bot command parsing
Further improvements:
 - add message.mid or session.uid or session.sid
 - use "messages" instead of "log" and "sessions" instead of
   "listing" where they're only lists
2017-09-08 11:16:44 +00:00

213 lines
6.2 KiB
Python

import asyncio
import logging
from .room import Room
logger = logging.getLogger(__name__)
__all__ = ["Controller"]
class Controller:
"""
Callback order:
on_start - self.room not available
while running:
<connects to room>
on_ping - always possible (until on_disconnected)
on_bounce - self.room only session
on_hello - self.room only session
<authenticated, access to room>
on_connected - self.room session and chat room (fully connected)
on_snapshot - self.room session and chat room
<other callbacks> - self.room session and chat room
<leaving room>
on_disconnected - self.room not connected to room any more
on_stop - self.room not available
"""
def __init__(self, nick, human=False, cookie=None, connect_timeout=10):
"""
roomname - name of room to connect to
human - whether the human flag should be set on connections
cookie - cookie to use in HTTP request, if any
connect_timeout - time for authentication to complete
"""
self.nick = nick
self.human = human
self.cookie = cookie
self.roomname = "test"
self.password = None
self.room = None
self.connect_timeout = connect_timeout # in seconds
self._connect_result = None
def _create_room(self, roomname):
return Room(roomname, self, human=self.human, cookie=self.cookie)
def _set_connect_result(self, result):
logger.debug(f"Attempting to set connect result to {result}")
if self._connect_result and not self._connect_result.done():
logger.debug(f"Setting connect result to {result}")
self._connect_result.set_result(result)
async def connect(self, roomname, password=None, timeout=10):
"""
task, reason = await connect(roomname, password=None, timeout=10)
Connect to a room and authenticate, if necessary.
roomname - name of the room to connect to
password - password for the room, if needed
timeout - wait this long for a reply from the server
Returns:
task - the task running the bot, or None on failure
reason - the reason for failure
"no room" = could not establish connection, room doesn't exist
"auth option" = can't authenticate with a password
"no password" = password needed to connect to room
"wrong password" = password given does not work
"disconnected" = connection closed before client could access the room
"timeout" = timed out while waiting for server
"success" = no failure
"""
logger.info(f"Attempting to connect to &{roomname}")
# make sure nothing is running any more
try:
await self.stop()
except asyncio.CancelledError:
logger.error("Calling connect from the controller itself.")
raise
self.password = password
self.room = self._create_room(roomname)
# prepare for if connect() is successful
self._connect_result = asyncio.Future()
# attempt to connect to the room
task = await self.room.connect()
if not task:
logger.warn(f"Could not connect to &{roomname}.")
self.room = None
return None, "no room"
# connection succeeded, now we need to know whether we can log in
# wait for success/authentication/disconnect
try:
await asyncio.wait_for(self._connect_result, self.connect_timeout)
except asyncio.TimeoutError:
result = "timeout"
else:
result = self._connect_result.result()
logger.debug(f"&{roomname}._connect_result: {result!r}")
# deal with result
if result == "success":
logger.info(f"Successfully connected to &{roomname}.")
return task, result
else: # not successful for some reason
logger.warn(f"Could not join &{roomname}: {result!r}")
await self.stop()
return None, result
async def stop(self):
if self.room:
logger.info(f"@{self.nick}: Stopping")
await self.room.stop()
logger.debug(f"@{self.nick}: Stopped. Deleting room")
self.room = None
async def set_nick(self, nick):
if nick != self.nick:
_, _, _, to_nick = await self.room.nick(nick)
self.nick = to_nick
if to_nick != nick:
logger.warn(f"&{self.room.roomname}: Could not set nick to {nick!r}, set to {to_nick!r} instead.")
async def on_connected(self):
"""
Client has successfully (re-)joined the room.
Use: Actions that are meant to happen upon (re-)connecting to a room,
such as resetting the message history.
"""
self._set_connect_result("success")
async def on_disconnected(self):
"""
Client has disconnected from the room.
This is the last time the old self.room can be accessed.
Use: Reconfigure self before next connection.
Need to store information from old room?
"""
logger.debug(f"on_disconnected: self.room is {self.room}")
self._set_connect_result("disconnected")
async def on_bounce(self, reason=None, auth_options=[], agent_id=None, ip=None):
if "passcode" not in auth_options:
self._set_connect_result("auth option")
elif self.password is None:
self._set_connect_result("no password")
else:
success, reason = await self.room.auth("passcode", passcode=self.password)
if not success:
self._set_connect_result("wrong password")
async def on_disconnect(self, reason):
pass
async def on_hello(self, user_id, session, room_is_private, version, account=None,
account_has_access=None, account_email_verified=None):
pass
async def on_join(self, session):
pass
async def on_login(self, account_id):
pass
async def on_logout(self):
pass
async def on_network(self, ntype, server_id, server_era):
pass
async def on_nick(self, session_id, user_id, from_nick, to_nick):
pass
async def on_edit_message(self, edit_id, message):
pass
async def on_part(self, session):
pass
async def on_ping(self, ptime, pnext):
"""
Default implementation, refer to api.euphoria.io
"""
logger.debug(f"&{self.room.roomname}: Pong!")
await self.room.ping_reply(ptime)
async def on_pm_initiate(self, from_id, from_nick, from_room, pm_id):
pass
async def on_send(self, message):
pass
async def on_snapshot(self, user_id, session_id, version, sessions, messages, nick=None,
pm_with_nick=None, pm_with_user_id=None):
if nick != self.nick:
await self.room.nick(self.nick)