Change switching rooms
This commit is contained in:
parent
75b2108b47
commit
c4fdb2942e
2 changed files with 182 additions and 134 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import socket
|
||||||
import ssl
|
import ssl
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
|
@ -29,17 +30,19 @@ class Connection():
|
||||||
- "stop"
|
- "stop"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, room, url_format=None):
|
def __init__(self, url_format=ROOM_FORMAT, tries=10, delay=30):
|
||||||
"""
|
"""
|
||||||
room - name of the room to connect to
|
|
||||||
url_format - url the bot will connect to, where the room name is represented by {}
|
url_format - url the bot will connect to, where the room name is represented by {}
|
||||||
|
tries - how often to try to reconnect when connection is lost (-1 - try forever)
|
||||||
|
delay - time (in seconds) to wait between tries
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.room = room
|
self.room = None
|
||||||
|
self.tries = tries
|
||||||
|
self.delay = delay
|
||||||
|
self.url_format = url_format
|
||||||
|
|
||||||
self._url_format = url_format or ROOM_FORMAT
|
self._stopping = True
|
||||||
|
|
||||||
self._stopping = False
|
|
||||||
|
|
||||||
self._ws = None
|
self._ws = None
|
||||||
self._thread = None
|
self._thread = None
|
||||||
|
|
@ -55,7 +58,7 @@ class Connection():
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
self._lock.release()
|
self._lock.release()
|
||||||
|
|
||||||
def _connect(self, tries=20, delay=10):
|
def _connect(self, tries=10, delay=30):
|
||||||
"""
|
"""
|
||||||
_connect(tries, delay) -> bool
|
_connect(tries, delay) -> bool
|
||||||
|
|
||||||
|
|
@ -70,7 +73,7 @@ class Connection():
|
||||||
|
|
||||||
while tries != 0:
|
while tries != 0:
|
||||||
try:
|
try:
|
||||||
url = self._url_format.format(self.room)
|
url = self.url_format.format(self.room)
|
||||||
logger.info("Connecting to url: {!r} ({} {} left)".format(
|
logger.info("Connecting to url: {!r} ({} {} left)".format(
|
||||||
url,
|
url,
|
||||||
tries-1 if tries > 0 else "infinite",
|
tries-1 if tries > 0 else "infinite",
|
||||||
|
|
@ -82,7 +85,7 @@ class Connection():
|
||||||
sslopt=SSLOPT
|
sslopt=SSLOPT
|
||||||
)
|
)
|
||||||
|
|
||||||
except WSException:
|
except (WSException, socket.gaierror, TimeoutError):
|
||||||
if tries > 0:
|
if tries > 0:
|
||||||
tries -= 1
|
tries -= 1
|
||||||
if tries != 0:
|
if tries != 0:
|
||||||
|
|
@ -99,27 +102,9 @@ class Connection():
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def disconnect(self):
|
def _launch(self):
|
||||||
"""
|
"""
|
||||||
disconnect() -> None
|
_launch() -> Thread
|
||||||
|
|
||||||
Disconnect from the room.
|
|
||||||
This will cause the connection to reconnect.
|
|
||||||
To completely disconnect, use stop().
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self._ws:
|
|
||||||
logger.debug("Closing connection!")
|
|
||||||
self._ws.abort()
|
|
||||||
self._ws.close()
|
|
||||||
self._ws = None
|
|
||||||
|
|
||||||
logger.debug("Disconnected")
|
|
||||||
self._callbacks.call("disconnect")
|
|
||||||
|
|
||||||
def launch(self):
|
|
||||||
"""
|
|
||||||
launch() -> Thread
|
|
||||||
|
|
||||||
Connect to the room and spawn a new thread.
|
Connect to the room and spawn a new thread.
|
||||||
"""
|
"""
|
||||||
|
|
@ -142,9 +127,6 @@ class Connection():
|
||||||
|
|
||||||
logger.debug("Running")
|
logger.debug("Running")
|
||||||
|
|
||||||
if not self.switch_to(self.room):
|
|
||||||
return
|
|
||||||
|
|
||||||
while not self._stopping:
|
while not self._stopping:
|
||||||
try:
|
try:
|
||||||
j = self._ws.recv()
|
j = self._ws.recv()
|
||||||
|
|
@ -152,95 +134,14 @@ class Connection():
|
||||||
except (WSException, ConnectionResetError):
|
except (WSException, ConnectionResetError):
|
||||||
if not self._stopping:
|
if not self._stopping:
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
self._connect()
|
self._connect(self.tries, self.delay)
|
||||||
|
|
||||||
logger.debug("Finished running")
|
logger.debug("Finished running")
|
||||||
|
self._thread = None
|
||||||
def stop(self):
|
|
||||||
"""
|
|
||||||
stop() -> None
|
|
||||||
|
|
||||||
Close the connection to the room.
|
|
||||||
Joins the thread launched by self.launch().
|
|
||||||
"""
|
|
||||||
|
|
||||||
logger.debug("Stopping")
|
|
||||||
self._stopping = True
|
|
||||||
self.disconnect()
|
|
||||||
|
|
||||||
self._callbacks.call("stop")
|
|
||||||
|
|
||||||
if self._thread and self._thread != threading.current_thread():
|
|
||||||
self._thread.join()
|
|
||||||
|
|
||||||
def switch_to(self, new_room):
|
|
||||||
"""
|
|
||||||
switch_to(new_room) -> bool
|
|
||||||
|
|
||||||
Returns True on success, False on failure.
|
|
||||||
|
|
||||||
Attempts to connect to new_room.
|
|
||||||
"""
|
|
||||||
|
|
||||||
old_room = self.room if self._ws else None
|
|
||||||
self.room = new_room
|
|
||||||
self.disconnect()
|
|
||||||
if old_room:
|
|
||||||
logger.info("Switching to &{} from &{}.".format(old_room, new_room))
|
|
||||||
else:
|
|
||||||
logger.info("Switching to &{}.".format(new_room))
|
|
||||||
|
|
||||||
if not self._connect(tries=1):
|
|
||||||
if old_room:
|
|
||||||
logger.info("Could not connect to &{}: Connecting to ${} again.".format(new_room, old_room))
|
|
||||||
self.room = old_room
|
|
||||||
self._connect()
|
|
||||||
else:
|
|
||||||
logger.info("Could not connect to &{}.".format(new_room))
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def next_id(self):
|
|
||||||
"""
|
|
||||||
next_id() -> id
|
|
||||||
|
|
||||||
Returns the id that will be used for the next package.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return str(self._send_id)
|
|
||||||
|
|
||||||
def subscribe(self, ptype, callback, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
subscribe(ptype, callback, *args, **kwargs) -> None
|
|
||||||
|
|
||||||
Add a function to be called when a packet of type ptype is received.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._callbacks.add(ptype, callback, *args, **kwargs)
|
|
||||||
|
|
||||||
def subscribe_to_id(self, pid, callback, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
subscribe_to_id(pid, callback, *args, **kwargs) -> None
|
|
||||||
|
|
||||||
Add a function to be called when a packet with id pid is received.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._id_callbacks.add(pid, callback, *args, **kwargs)
|
|
||||||
|
|
||||||
def subscribe_to_next(self, callback, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
subscribe_to_next(callback, *args, **kwargs) -> None
|
|
||||||
|
|
||||||
Add a function to be called when the answer to the next message sent is received.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._id_callbacks.add(self.next_id(), callback, *args, **kwargs)
|
|
||||||
|
|
||||||
def _handle_json(self, data):
|
def _handle_json(self, data):
|
||||||
"""
|
"""
|
||||||
handle_json(data) -> None
|
_handle_json(data) -> None
|
||||||
|
|
||||||
Handle incoming 'raw' data.
|
Handle incoming 'raw' data.
|
||||||
"""
|
"""
|
||||||
|
|
@ -281,6 +182,100 @@ class Connection():
|
||||||
except WSException:
|
except WSException:
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
|
|
||||||
|
def connected(self):
|
||||||
|
return self._ws
|
||||||
|
|
||||||
|
def connect_to(self, room):
|
||||||
|
"""
|
||||||
|
connect_to(room) -> bool
|
||||||
|
|
||||||
|
Attempts to connect to the room and spawns a new thread if it's successful.
|
||||||
|
Returns True if connection was sucessful, else False.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
logger.debug("Connecting to &{}.".format(room))
|
||||||
|
self.room = room
|
||||||
|
if self._connect(1):
|
||||||
|
self._launch()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warn("Could not connect to &{}.".format(room))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
"""
|
||||||
|
disconnect() -> None
|
||||||
|
|
||||||
|
Disconnect from the room.
|
||||||
|
This will cause the connection to reconnect.
|
||||||
|
To completely disconnect, use stop().
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._ws:
|
||||||
|
logger.debug("Closing connection!")
|
||||||
|
self._ws.abort()
|
||||||
|
self._ws.close()
|
||||||
|
self._ws = None
|
||||||
|
|
||||||
|
logger.debug("Disconnected")
|
||||||
|
self._id_callbacks = Callbacks() # we don't need the old id callbacks any more
|
||||||
|
self._callbacks.call("disconnect")
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
stop() -> None
|
||||||
|
|
||||||
|
Close the connection to the room.
|
||||||
|
Joins the thread launched by self.launch().
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug("Stopping")
|
||||||
|
self._stopping = True
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
self._callbacks.call("stop")
|
||||||
|
|
||||||
|
if self._thread and self._thread != threading.current_thread():
|
||||||
|
self._thread.join()
|
||||||
|
|
||||||
|
def next_id(self):
|
||||||
|
"""
|
||||||
|
next_id() -> id
|
||||||
|
|
||||||
|
Returns the id that will be used for the next package.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return str(self._send_id)
|
||||||
|
|
||||||
|
def subscribe(self, ptype, callback, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
subscribe(ptype, callback, *args, **kwargs) -> None
|
||||||
|
|
||||||
|
Add a function to be called when a packet of type ptype is received.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._callbacks.add(ptype, callback, *args, **kwargs)
|
||||||
|
|
||||||
|
def subscribe_to_id(self, pid, callback, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
subscribe_to_id(pid, callback, *args, **kwargs) -> None
|
||||||
|
|
||||||
|
Add a function to be called when a packet with id pid is received.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._id_callbacks.add(pid, callback, *args, **kwargs)
|
||||||
|
|
||||||
|
def subscribe_to_next(self, callback, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
subscribe_to_next(callback, *args, **kwargs) -> None
|
||||||
|
|
||||||
|
Add a function to be called when the answer to the next message sent is received.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._id_callbacks.add(self.next_id(), callback, *args, **kwargs)
|
||||||
|
|
||||||
def send_packet(self, ptype, **kwargs):
|
def send_packet(self, ptype, **kwargs):
|
||||||
"""
|
"""
|
||||||
send_packet(ptype, **kwargs) -> None
|
send_packet(ptype, **kwargs) -> None
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
|
|
||||||
from .callbacks import Callbacks
|
from .callbacks import Callbacks
|
||||||
from .connection import Connection
|
from .connection import Connection
|
||||||
|
|
@ -15,7 +16,9 @@ class Session():
|
||||||
- sending and receiving messages
|
- sending and receiving messages
|
||||||
|
|
||||||
event (args) | meaning
|
event (args) | meaning
|
||||||
-------------------|-------------------------------------------------
|
--------------------|-------------------------------------------------
|
||||||
|
join (bool) | joining the room was successful/not successful
|
||||||
|
| Callbacks for this event are cleared whenever it is called.
|
||||||
enter | can view the room
|
enter | can view the room
|
||||||
ready | can view the room and post messages (has a nick)
|
ready | can view the room and post messages (has a nick)
|
||||||
sessions-update | self.sessions has changed
|
sessions-update | self.sessions has changed
|
||||||
|
|
@ -25,8 +28,12 @@ class Session():
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, room, password=None, name=None):
|
def __init__(self, name=None):
|
||||||
self._connection = Connection(room)
|
self._room_accessible = False
|
||||||
|
self._room_accessible_event = threading.Event()
|
||||||
|
self._room_accessible_timeout = None
|
||||||
|
|
||||||
|
self._connection = Connection()
|
||||||
self._connection.subscribe("disconnect", self._reset_variables)
|
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)
|
||||||
|
|
@ -46,8 +53,8 @@ class Session():
|
||||||
self._callbacks = Callbacks()
|
self._callbacks = Callbacks()
|
||||||
self.subscribe("enter", self._on_enter)
|
self.subscribe("enter", self._on_enter)
|
||||||
|
|
||||||
self.password = password
|
self.password = None
|
||||||
self._wish_name = name
|
self.real_name = name
|
||||||
|
|
||||||
#self._hello_event_completed = False
|
#self._hello_event_completed = False
|
||||||
#self._snapshot_event_completed = False
|
#self._snapshot_event_completed = False
|
||||||
|
|
@ -59,10 +66,31 @@ class Session():
|
||||||
|
|
||||||
self._reset_variables()
|
self._reset_variables()
|
||||||
|
|
||||||
|
def switch_to(self, room, password=None, timeout=10):
|
||||||
|
logger.info("Switching to &{}.".format(room))
|
||||||
|
self.password = password
|
||||||
|
|
||||||
|
if self._room_accessible_timeout:
|
||||||
|
self._room_accessible_timeout.cancel()
|
||||||
|
self._room_accessible_timeout = threading.Timer(timeout, self.stop)
|
||||||
|
self._room_accessible_timeout.start()
|
||||||
|
|
||||||
|
if self._connection.connect_to(room):
|
||||||
|
logger.debug("Connection established. Waiting for correct events")
|
||||||
|
self._room_accessible_event.wait()
|
||||||
|
return self._room_accessible
|
||||||
|
else:
|
||||||
|
logger.warn("Could not connect to room url.")
|
||||||
|
return False
|
||||||
|
|
||||||
def _reset_variables(self):
|
def _reset_variables(self):
|
||||||
logger.debug("Resetting room-related variables")
|
logger.debug("Resetting room-related variables")
|
||||||
|
self._room_accessible = False
|
||||||
|
|
||||||
self.my_session = SessionView(None, None, None, None, None)
|
self.my_session = SessionView(None, None, None, None, None)
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
|
self._room_accessible_event.clear()
|
||||||
|
|
||||||
self._hello_event_completed = False
|
self._hello_event_completed = False
|
||||||
self._snapshot_event_completed = False
|
self._snapshot_event_completed = False
|
||||||
self._ready = False
|
self._ready = False
|
||||||
|
|
@ -78,15 +106,24 @@ class Session():
|
||||||
|
|
||||||
def _on_enter(self):
|
def _on_enter(self):
|
||||||
logger.info("Connected and authenticated.")
|
logger.info("Connected and authenticated.")
|
||||||
|
self._room_accessible_timeout.cancel()
|
||||||
|
self._room_accessible = True
|
||||||
|
self._room_accessible_event.set()
|
||||||
|
self._room_accessible_event.clear()
|
||||||
|
|
||||||
if self._wish_name:
|
if self.real_name:
|
||||||
self._set_name(self._wish_name)
|
self._set_name(self.real_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._room_accessible_timeout.cancel()
|
||||||
|
self._room_accessible = False
|
||||||
|
self._room_accessible_event.set()
|
||||||
|
self._room_accessible_event.clear()
|
||||||
|
|
||||||
with self._connection as conn:
|
with self._connection as conn:
|
||||||
conn.stop()
|
conn.stop()
|
||||||
|
|
||||||
|
|
@ -107,16 +144,20 @@ class Session():
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, new_name):
|
def name(self, new_name):
|
||||||
self._wish_name = new_name
|
self.real_name = new_name
|
||||||
|
|
||||||
if not self._ready:
|
if not self._ready:
|
||||||
self._set_name(new_name)
|
self._set_name(new_name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def room(self):
|
||||||
|
return self._connection.room
|
||||||
|
|
||||||
def refresh_sessions(self):
|
def refresh_sessions(self):
|
||||||
logger.debug("Refreshing sessions")
|
logger.debug("Refreshing sessions")
|
||||||
self._connection.send_packet("who")
|
self._connection.send_packet("who")
|
||||||
|
|
||||||
def _listing_to_sessions(self, listing):
|
def _set_sessions_from_listing(self, listing):
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
|
|
||||||
for item in listing:
|
for item in listing:
|
||||||
|
|
@ -125,6 +166,18 @@ class Session():
|
||||||
|
|
||||||
self._callbacks.call("sessions-update")
|
self._callbacks.call("sessions-update")
|
||||||
|
|
||||||
|
def _revert_to_revious_room(self):
|
||||||
|
self._callbacks.call("join", False)
|
||||||
|
|
||||||
|
if self._prev_room:
|
||||||
|
self.password = self._prev_password
|
||||||
|
self.room = self._prev_room # shouldn't do this
|
||||||
|
|
||||||
|
self._prev_room = None
|
||||||
|
self._prev_password = None
|
||||||
|
else:
|
||||||
|
self.stop()
|
||||||
|
|
||||||
def _handle_bounce_event(self, data, packet):
|
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:
|
||||||
|
|
@ -237,7 +290,7 @@ class Session():
|
||||||
|
|
||||||
def _handle_snapshot_event(self, data, packet):
|
def _handle_snapshot_event(self, data, packet):
|
||||||
# deal with connected sessions
|
# deal with connected sessions
|
||||||
self._listing_to_sessions(data.get("listing"))
|
self._set_sessions_from_listing(data.get("listing"))
|
||||||
|
|
||||||
# deal with messages
|
# deal with messages
|
||||||
# TODO: implement
|
# TODO: implement
|
||||||
|
|
@ -289,4 +342,4 @@ class Session():
|
||||||
self._callbacks.call("own-message", msg)
|
self._callbacks.call("own-message", msg)
|
||||||
|
|
||||||
def _handle_who_reply(self, data, packet):
|
def _handle_who_reply(self, data, packet):
|
||||||
self._listing_to_sessions(data.get("listing"))
|
self._set_sessions_from_listing(data.get("listing"))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue