Handle connecting to rooms

This commit is contained in:
Joscha 2017-03-29 17:24:29 +00:00
parent 04f7c9c781
commit 14bae17104
3 changed files with 131 additions and 69 deletions

View file

@ -26,22 +26,25 @@ class SessionView():
self.manager = is_manager self.manager = is_manager
@classmethod @classmethod
def from_data(self, data): def from_data(cls, data):
""" """
Creates and returns a session created from the data. Creates and returns a session created from the data.
data - a euphoria SessionView data - a euphoria SessionView
""" """
return self( view = cls(None, None, None, None, None)
data.get("id"), view.read_data(data)
data.get("name"), return view
data.get("server_id"),
data.get("server_era"), def read_data(self, data):
data.get("session_id"), if "id" in data: self.id = data.get("id")
data.get("is_staff"), if "name" in data: self.name = data.get("name")
data.get("is_manager") if "server_id" in data: self.server_id = data.get("server_id")
) if "server_era" in data: self.server_era = data.get("server_era")
if "session_id" in data: self.session_id = data.get("session_id")
if "is_staff" in data: self.is_staff = data.get("is_staff")
if "is_manager" in data: self.is_manager = data.get("is_manager")
def session_type(self): def session_type(self):
""" """

View file

@ -8,7 +8,9 @@ from websocket import WebSocketException as WSException
from .callbacks import Callbacks from .callbacks import Callbacks
SSLOPT_CA_CERTS = {'ca_certs': ssl.get_default_verify_paths().cafile} SSLOPT = {"ca_certs": ssl.get_default_verify_paths().cafile}
#SSLOPT = {"cert_reqs": ssl.CERT_NONE}
ROOM_FORMAT = "wss://euphoria.io/room/{}/ws"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Connection(): class Connection():
@ -25,8 +27,6 @@ class Connection():
- "stop" - "stop"
""" """
ROOM_FORMAT = "wss://euphoria.io/room/{}/ws"
def __init__(self, room, url_format=None): def __init__(self, room, url_format=None):
""" """
room - name of the room to connect to room - name of the room to connect to
@ -35,7 +35,7 @@ class Connection():
self.room = room self.room = room
self._url_format = url_format or Connection.ROOM_FORMAT self._url_format = url_format or ROOM_FORMAT
self._stopping = False self._stopping = False
@ -44,7 +44,7 @@ class Connection():
self._send_id = 0 self._send_id = 0
self._callbacks = Callbacks() self._callbacks = Callbacks()
self._id_callbacks = Callbacks() self._id_callbacks = Callbacks()
self._lock = threading.Lock() self._lock = threading.RLock()
def __enter__(self): def __enter__(self):
self._lock.acquire() self._lock.acquire()
@ -77,7 +77,7 @@ class Connection():
self._ws = websocket.create_connection( self._ws = websocket.create_connection(
url, url,
enable_multithread=True, enable_multithread=True,
sslopt=SSLOPT_CA_CERTS sslopt=SSLOPT
) )
except WSException: except WSException:
@ -107,6 +107,8 @@ class Connection():
""" """
if self._ws: if self._ws:
logger.debug("Closing connection!")
self._ws.abort()
self._ws.close() self._ws.close()
self._ws = None self._ws = None
@ -120,8 +122,11 @@ class Connection():
Connect to the room and spawn a new thread. Connect to the room and spawn a new thread.
""" """
self._thread = threading.Thread(target=self._run, self._stopping = False
name="{}-{}".format(int(time.time()), self.room)) self._thread = threading.Thread(
target=self._run,
name="{}-{}".format(int(time.time()), self.room)
)
logger.debug("Launching new thread: {}".format(self._thread.name)) logger.debug("Launching new thread: {}".format(self._thread.name))
self._thread.start() self._thread.start()
return self._thread return self._thread
@ -134,17 +139,21 @@ class Connection():
""" """
logger.debug("Running") logger.debug("Running")
if not self.switch_to(self.room): if not self.switch_to(self.room):
return return
while not self._stopping: while not self._stopping:
try: try:
self._handle_json(self._ws.recv()) j = self._ws.recv()
self._handle_json(j)
except (WSException, ConnectionResetError): except (WSException, ConnectionResetError):
if not self._stopping: if not self._stopping:
self.disconnect() self.disconnect()
self._connect() self._connect()
logger.debug("Finished running")
def stop(self): def stop(self):
""" """
stop() -> None stop() -> None
@ -248,15 +257,14 @@ class Connection():
logger.debug("Handling packet of type {}.".format(ptype)) logger.debug("Handling packet of type {}.".format(ptype))
data = packet.get("data") data = packet.get("data")
error = packet.get("error") if "error" in packet:
if error:
logger.debug("Error in packet: {!r}".format(error)) logger.debug("Error in packet: {!r}".format(error))
if "id" in packet: if "id" in packet:
self._id_callbacks.call(packet["id"], data, error) self._id_callbacks.call(packet["id"], data, packet)
self._id_callbacks.remove(packet["id"]) self._id_callbacks.remove(packet["id"])
self._callbacks.call(packet["type"], data, error) self._callbacks.call(packet["type"], data, packet)
def _send_json(self, data): def _send_json(self, data):
""" """

View file

@ -2,6 +2,7 @@ import logging
from .callbacks import Callbacks from .callbacks import Callbacks
from .connection import Connection from .connection import Connection
from .basic_types import Message, SessionView
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -13,64 +14,84 @@ class Session():
- seeing other clients - seeing other clients
- sending and receiving messages - sending and receiving messages
Things to wait for before being properly connected: Events:
hello-event enter - can view the room
snapshot-event ready - can view the room and post messages (has a nick)
-> connected
Things to wait for before being ready:
hello-event
snapshot-event
nick-reply
-> ready
When connecting to a new room:
_hello_event_completed = False
_snapshot_event_completed = False
""" """
def __init__(self, room, password=None): def __init__(self, room, password=None, name=None):
self._connection = Connection(room) self._connection = Connection(room)
self._connection.subscribe("disconnect", self._on_disconnect) self._connection.subscribe("disconnect", self._reset_variables)
self._connection.subscribe("bounce-event", self.handle_bounce_event) self._connection.subscribe("bounce-event", self.handle_bounce_event)
self._connection.subscribe("disconnect-event", self.handle_disconnect_event)
self._connection.subscribe("hello-event", self.handle_hello_event)
self._connection.subscribe("ping-event", self.handle_ping_event) self._connection.subscribe("ping-event", self.handle_ping_event)
self._connection.subscribe("snapshot-event", self.handle_snapshot_event)
self._callbacks = Callbacks() self._callbacks = Callbacks()
self._hello_event_completed = False self.subscribe("enter", self._on_enter)
self._snapshot_event_completed = False
self.password = password self.password = password
self._wish_name = name
self.my_session = None #self._hello_event_completed = False
self.sessions = {} # sessions in the room #self._snapshot_event_completed = False
#self._ready = False
#self.my_session = SessionView(None, None, None, None, None)
#self.sessions = {} # sessions in the room
#self.room_is_private = None
#self.server_version = None
self._reset_variables()
def _reset_variables(self):
logger.debug("Resetting room-related variables")
self.my_session = SessionView(None, None, None, None, None)
self.sessions = {}
self._hello_event_completed = False
self._snapshot_event_completed = False
self._ready = False
self.room_is_private = None
self.server_version = None
def _set_name(self, new_name):
with self._connection as conn:
logger.debug("setting name to {!r}".format(new_name))
conn.subscribe_to_next(self.handle_nick_reply)
conn.send_packet("nick", name=new_name)
def _on_enter(self):
logger.info("Connected and authenticated.")
if self._wish_name:
self._set_name(self._wish_name)
def launch(self): def launch(self):
return self._connection.launch() return self._connection.launch()
def stop(self): def stop(self):
logger.info("Stopping") logger.info("Stopping")
self._connection.stop() with self._connection as conn:
conn.stop()
def subscribe(self, event, callback, *args, **kwargs):
logger.debug("Adding callback {} to {}".format(callback, event))
self._callbacks.add(event, callback, *args, **kwargs)
@property @property
def name(self): def name(self):
if self.my_session: return self.my_session.name
return self.my_session.name
@name.setter @name.setter
def name(self, new_name): def name(self, new_name):
with self._connection as conn: self._wish_name = new_name
logger.debug("setting name to {!r}".format(new_name))
conn.subscribe_to_next(self.handle_nick_reply)
conn.send_packet("nick", name=new_name)
def _on_disconnect(self): if not self._ready:
logger.debug("Disconnected. Resetting related variables") self._set_name(new_name)
self.my_session = None
self.sessions = {}
self._hello_event_completed = False
self._snapshot_event_completed = False
def handle_bounce_event(self, data, error): def handle_bounce_event(self, data, packet):
if data.get("reason") == "authentication required": if data.get("reason") == "authentication required":
if self.password: if self.password:
with self._connection as conn: with self._connection as conn:
@ -80,30 +101,60 @@ class Session():
logger.warn("Could not access &{}: No password.".format(self._connection.room)) logger.warn("Could not access &{}: No password.".format(self._connection.room))
self.stop() self.stop()
def handle_disconnect_event(self, data, error): def handle_disconnect_event(self, data, packet):
self._connection.disconnect() # should reconnect self._connection.disconnect() # should reconnect
def handle_ping_event(self, data, error): def handle_hello_event(self, data, packet):
self.my_session.read_data(data.get("session"))
self.room_is_private = data.get("room_is_private")
self.server_version = data.get("version")
self._hello_event_completed = True
if self._snapshot_event_completed:
self._callbacks.call("enter")
def handle_ping_event(self, data, packet):
with self._connection as conn: with self._connection as conn:
logger.debug("playing ping pong") logger.debug("playing ping pong")
conn.send_packet("ping-reply", time=data.get("time")) conn.send_packet("ping-reply", time=data.get("time"))
def handle_auth_reply(self, data, error): def handle_snapshot_event(self, data, packet):
if data.get("success"): # deal with connected sessions
logger.debug("Authetication complete, password was correct.") for item in data.get("listing"):
else: view = SessionView.from_data(item)
self.sessions[view.session_id] = view
# deal with messages
# TODO: this
# deal with other info
self.server_version = data.get("version")
if "nick" in data:
self.my_session.name = data.get("nick")
self._snapshot_event_completed = True
if self._hello_event_completed:
self._callbacks.call("enter")
def handle_auth_reply(self, data, packet):
if not data.get("success"):
logger.warn("Could not authenticate, reason: {!r}".format(data.get("reason"))) logger.warn("Could not authenticate, reason: {!r}".format(data.get("reason")))
self.stop() self.stop()
else:
logger.debug("Authetication complete, password was correct.")
def handle_nick_reply(self, data, error): def handle_nick_reply(self, data, packet):
if error:
logger.error("nick-reply error: {!r}".format(error))
return
first_name = not self.name first_name = not self.name
logger.info("Changed name fro {!r} to {!r}.".format(data.get("from"), data.get("to"))) if first_name:
logger.info("Changed name to {!r}.".format(data.get("to")))
else:
logger.info("Changed name from {!r} to {!r}.".format(data.get("from"), data.get("to")))
self.my_session.name = data.get("to") self.my_session.name = data.get("to")
if first_name: if first_name:
self._ready = True
self._callbacks.call("ready") self._callbacks.call("ready")