From 94f1aa5cffa7eee77a5cb3a478b1bb99b168a170 Mon Sep 17 00:00:00 2001 From: trigg Date: Tue, 12 Apr 2022 00:21:07 +0000 Subject: [PATCH] - Huge overhaul of connector - Removed almost all uses of GET_CHANNEL - Rate limiting no longer in use - Probably don't need server limit any more - Unsubscribe from events when changing text/voice room - find_user now has a direct method call which is miles better than previous - hooked 'GUILD_CREATE" but currently not tested --- discover_overlay/discord_connector.py | 217 ++++++++++++++------------ discover_overlay/text_settings.py | 17 +- 2 files changed, 132 insertions(+), 102 deletions(-) diff --git a/discover_overlay/discord_connector.py b/discover_overlay/discord_connector.py index 4be39cb..3eae085 100644 --- a/discover_overlay/discord_connector.py +++ b/discover_overlay/discord_connector.py @@ -64,13 +64,6 @@ class DiscordConnector: self.last_connection = "" self.text = [] self.authed = False - self.last_text_channel = None - - self.request_text_rooms = None - self.request_text_rooms_response = None - self.request_text_rooms_awaiting = 0 - self.last_requested_guild = 0 - self.needs_guild_rerequest = -1 self.rate_limited_channels = [] self.reconnect_delay = 0 @@ -124,6 +117,8 @@ class DiscordConnector: "Joined room: %s", channel_name) else: log.info("Joining private room") + if self.current_voice!="0": + self.unsub_voice_channel(self.current_voice) self.sub_voice_channel(channel) self.current_voice = channel if need_req: @@ -137,9 +132,7 @@ class DiscordConnector: self.current_text = "0" return if channel != self.current_text: - self.current_text = channel - log.info( - "Changing text room: %s", channel) + self.start_listening_text(channel) if need_req: self.req_channel_details(channel) @@ -293,8 +286,7 @@ class DiscordConnector: nick = j["data"]["nick"] thisuser["nick"] = nick self.update_user(thisuser) - # If someone joins any voice room grab it fresh from server - self.req_channel_details(self.current_voice) + # We've joined a room... but where? if j["data"]["user"]["id"] == self.user["id"]: self.find_user() elif j["evt"] == "VOICE_STATE_DELETE": @@ -302,7 +294,6 @@ class DiscordConnector: self.set_in_room(j["data"]["user"]["id"], False) if j["data"]["user"]["id"] == self.user["id"]: self.in_room = [] - # self.sub_all_voice() elif j["evt"] == "SPEAKING_START": self.list_altered = True # It's only possible to get alerts for the room you're in @@ -332,8 +323,11 @@ class DiscordConnector: elif j["evt"] == "MESSAGE_DELETE": if self.current_text == j["data"]["channel_id"]: self.delete_text(j["data"]["message"]) + elif j["evt"] == "CHANNEL_CREATE": + # We haven't been told what guild this is in + self.req_channel_details(j["data"]["id"], 'new') else: - log.info(j) + log.warning(j) return elif j["cmd"] == "AUTHENTICATE": if j["evt"] == "ERROR": @@ -354,6 +348,13 @@ class DiscordConnector: if len(self.voice_settings.guild_ids) == 0 or guild["id"] in self.voice_settings.guild_ids: self.req_channels(guild["id"]) return + elif j["cmd"] == "GET_GUILD": + # We currently only get here because of a "CHANNEL_CREATE" event. Stupidly long winded way around + if j["data"]: + guild = j["data"] + if len(self.voice_settings.guild_ids) == 0 or guild["id"] in self.voice_settings.guild_ids: + self.req_channels(guild["id"]) + return elif j["cmd"] == "GET_CHANNELS": self.guilds[j['nonce']]["channels"] = j["data"]["channels"] for channel in j["data"]["channels"]: @@ -362,47 +363,48 @@ class DiscordConnector: self.channels[channel["id"]] = channel if channel["type"] == 2: self.req_channel_details(channel["id"]) + if j["nonce"] == self.text_settings.get_guild(): + self.text_settings.set_channels(j["data"]["channels"]) self.check_guilds() return elif j["cmd"] == "SUBSCRIBE": + # Only log errors + if j['evt']: + log.warning(j) + return + elif j["cmd"] == "UNSUBSCRIBE": + return + elif j["cmd"] == "GET_SELECTED_VOICE_CHANNEL": + if 'data' in j and j['data'] and 'id' in j['data']: + self.set_channel(j['data']['id']) + self.list_altered = True + + for u in j['data']['voice_states']: + thisuser = u["user"] + nick = u["nick"] + thisuser["nick"] = nick + mute = (u["voice_state"]["mute"] or + u["voice_state"]["self_mute"] or + u["voice_state"]["suppress"]) + deaf = u["voice_state"]["deaf"] or u["voice_state"]["self_deaf"] + thisuser["mute"] = mute + thisuser["deaf"] = deaf + self.update_user(thisuser) + self.set_in_room(thisuser["id"], True) return elif j["cmd"] == "GET_CHANNEL": if j["evt"] == "ERROR": log.info( "Could not get room") return - if j["data"]["type"] == 2: # Voice channel - for voice in j["data"]["voice_states"]: - if voice["user"]["id"] == self.user["id"]: - self.set_channel(j["data"]["id"], False) - if j["data"]["id"] == self.current_voice: - self.list_altered = True - self.in_room = [] - for voice in j["data"]["voice_states"]: - thisuser = voice["user"] - if "nick" in j["data"]: - thisuser["nick"] = j["data"]["nick"] - self.update_user(thisuser) - self.set_in_room(thisuser["id"], True) - elif j["data"]["type"] == 0: # Text channel - if self.request_text_rooms_response is not None: - count = len(self.request_text_rooms_response) - if count < self.request_text_rooms_awaiting: - self.request_text_rooms_response.append(j['data']) - if count == self.request_text_rooms_awaiting-1: # Last one - self.text_settings.set_channels( - self.request_text_rooms_response) - else: - self.request_text_rooms_awaiting = 0 - self.request_text_rooms_response = None - + if j["data"]["type"] == 0: # Text channel if self.current_text == j["data"]["id"]: self.text = [] for message in j["data"]["messages"]: self.add_text(message) - return - log.info(j) + return + log.warning(j) def check_guilds(self): """ @@ -424,8 +426,8 @@ class DiscordConnector: self.voice_overlay.set_enabled(True) if self.text_overlay: self.text_overlay.set_enabled(self.text_settings.enabled) - if self.last_text_channel: - self.sub_text_channel(self.last_text_channel) + if self.current_text: + self.start_listening_text(self.current_text) def on_error(self, error): """ @@ -437,7 +439,7 @@ class DiscordConnector: """ Called when connection is closed """ - log.info("Connection closed") + log.warning("Connection closed") self.voice_overlay.hide() if self.text_overlay: self.text_overlay.hide() @@ -457,6 +459,17 @@ class DiscordConnector: } self.websocket.send(json.dumps(cmd)) + def req_guild(self, guild_id,nonce): + """ + Request info on one guild + """ + cmd = { + "cmd": "GET_GUILD", + "args": {"guild_id": guild_id}, + "nonce": nonce + } + self.websocket.send(json.dumps(cmd)) + def req_guilds(self): """ Request all guilds information for logged in user @@ -477,20 +490,14 @@ class DiscordConnector: if guild in self.guilds: self.rate_limited_channels.append(guild) else: - log.info(f"Didn't find guild with id {guild}") - # cmd = { - # "cmd": "GET_CHANNELS", - # "args": { - # "guild_id": guild - # }, - # "nonce": guild - # } - # self.websocket.send(json.dumps(cmd)) + log.warning(f"Didn't find guild with id {guild}") def req_channel_details(self, channel): """message Request information about a specific channel """ + if not self.websocket: + return cmd = { "cmd": "GET_CHANNEL", "args": { @@ -500,31 +507,19 @@ class DiscordConnector: } self.websocket.send(json.dumps(cmd)) - def req_all_channel_details(self, guild): - """ - Ask for information on all channels in a guild - """ - for channel in self.guilds[guild]["channels"]: - self.req_channel_details(channel["id"]) - def find_user(self): """ - ***Potential overload issue*** - - Asks the server for information about every single voice channel (type==2) - in the hope that one of them will say the user is present - - because if asks about every single one without waiting for reply it is heavy even - if the user is relatively simple to find - - It might be worth limiting the usage of this + Find the user """ - count = 0 - for channel in self.channels: - if self.channels[channel]["type"] == 2: - self.req_channel_details(channel) - count += 1 - log.warning("Getting %s rooms", count) + + cmd = { + "cmd": "GET_SELECTED_VOICE_CHANNEL", + "args": { + + }, + "nonce": "test" + } + self.websocket.send(json.dumps(cmd)) def sub_raw(self, event, args, nonce): """ @@ -538,6 +533,18 @@ class DiscordConnector: } self.websocket.send(json.dumps(cmd)) + def unsub_raw(self, event, args, nonce): + """ + Subscribe to event helper function + """ + cmd = { + "cmd": "UNSUBSCRIBE", + "args": args, + "evt": event, + "nonce": nonce + } + self.websocket.send(json.dumps(cmd)) + def sub_server(self): """ Subscribe to helpful events that report connectivity issues & @@ -548,6 +555,8 @@ class DiscordConnector: """ self.sub_raw("VOICE_CHANNEL_SELECT", {}, "VOICE_CHANNEL_SELECT") self.sub_raw("VOICE_CONNECTION_STATUS", {}, "VOICE_CONNECTION_STATUS") + self.sub_raw("GUILD_CREATE", {}, "GUILD_CREATE") + self.sub_raw("CHANNEL_CREATE", {}, "CHANNEL_CREATE") def sub_channel(self, event, channel): """ @@ -555,6 +564,12 @@ class DiscordConnector: """ self.sub_raw(event, {"channel_id": channel}, channel) + def unsub_channel(self, event, channel): + """ + Subscribe to event on channel + """ + self.unsub_raw(event, {"channel_id": channel}, channel) + def sub_text_channel(self, channel): """ Subscribe to text-based events. @@ -563,6 +578,14 @@ class DiscordConnector: self.sub_channel("MESSAGE_UPDATE", channel) self.sub_channel("MESSAGE_DELETE", channel) + def unsub_text_channel(self, channel): + """ + Unsubscribe to text-based events. + """ + self.unsub_channel("MESSAGE_CREATE", channel) + self.unsub_channel("MESSAGE_UPDATE", channel) + self.unsub_channel("MESSAGE_DELETE", channel) + def sub_voice_channel(self, channel): """ Subscribe to voice-based events @@ -573,6 +596,17 @@ class DiscordConnector: self.sub_channel("SPEAKING_START", channel) self.sub_channel("SPEAKING_STOP", channel) + def unsub_voice_channel(self, channel): + """ + Remove subscription to voice-based events + """ + self.unsub_channel("VOICE_STATE_CREATE", channel) + self.unsub_channel("VOICE_STATE_UPDATE", channel) + self.unsub_channel("VOICE_STATE_DELETE", channel) + self.unsub_channel("SPEAKING_START", channel) + self.unsub_channel("SPEAKING_STOP", channel) + + def do_read(self): """ Poorly named logic center. @@ -592,7 +626,7 @@ class DiscordConnector: # No timeout left, connect to discord again self.connect() if self.warn_connection: - log.info( + log.warning( "Unable to connect to Discord client") self.warn_connection = False return True @@ -600,11 +634,6 @@ class DiscordConnector: # Timeout requested, wait it out self.reconnect_delay -= 1 return True - if self.needs_guild_rerequest == 0: - log.error("Re-requesting guild list") - self.request_text_rooms_for_guild(self.last_requested_guild) - if self.needs_guild_rerequest >= 0: - self.needs_guild_rerequest -= 1 # Recreate a list of users in current room newlist = [] for userid in self.in_room: @@ -658,9 +687,12 @@ class DiscordConnector: Helper function to avoid race conditions of reading config vs connecting to websocket """ if self.websocket: - self.sub_text_channel(channel) - else: - self.last_text_channel = channel + if self.current_text != "0": + self.unsub_text_channel(self.current_text) + if channel != "0": + self.sub_text_channel(channel) + self.req_channel_details(channel) + self.current_text = channel def request_text_rooms_for_guild(self, guild_id): """ @@ -670,23 +702,8 @@ class DiscordConnector: """ if(guild_id == 0): return - self.last_requested_guild = guild_id if guild_id in self.guilds: - guild = self.guilds[guild_id] - if "channels" in guild: - self.request_text_rooms = guild_id - self.request_text_rooms_response = [] - self.request_text_rooms_awaiting = 0 - for channel in guild["channels"]: - if channel["type"] == 0: - self.request_text_rooms_awaiting += 1 - self.req_all_channel_details(guild_id) - else: - log.warning( - f"Trying to request channel details for guild without " - f"cached channels. Retrying in 1 second" - ) - self.needs_guild_rerequest = 60 + self.req_guild(guild_id, "refresh") def connect(self): """ diff --git a/discover_overlay/text_settings.py b/discover_overlay/text_settings.py index 0e2769d..8c3bfea 100644 --- a/discover_overlay/text_settings.py +++ b/discover_overlay/text_settings.py @@ -110,7 +110,7 @@ class TextSettingsWindow(SettingsWindow): """ self.connector = conn if self.channel: - self.connector.start_listening_text(self.channel) + self.connector.set_text_channel(self.channel) def present_settings(self): """ @@ -479,7 +479,7 @@ class TextSettingsWindow(SettingsWindow): return channel = self.channel_lookup[button.get_active()] - self.connector.start_listening_text(channel) + self.connector.set_text_channel(channel) self.channel = channel self.save_config() @@ -536,6 +536,19 @@ class TextSettingsWindow(SettingsWindow): """ return self.channel + def set_channel(self, channel="0"): + """ + Change the stored channel + """ + self.channel = channel + self.save_config() + + def get_guild(self): + """ + Return selected guild + """ + return self.guild + def change_bg(self, button): """ Background colour changed