Merge pull request #3028 from InspectorCaracal/discord-bugs

Rework Discord reconnection logic
This commit is contained in:
Griatch 2022-12-14 22:28:52 +01:00 committed by GitHub
commit e15aebdf86
2 changed files with 31 additions and 49 deletions

View file

@ -1925,6 +1925,7 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
/delete - alias to remove /delete - alias to remove
/guild - toggle the Discord server tag on/off /guild - toggle the Discord server tag on/off
/channel - toggle the Evennia/Discord channel tags on/off /channel - toggle the Evennia/Discord channel tags on/off
/start - tell the bot to start, in case it lost its connection
Example: Example:
discord2chan mydiscord = 555555555555555 discord2chan mydiscord = 555555555555555
@ -1943,6 +1944,7 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
"guild", "guild",
"list", "list",
"remove", "remove",
"start",
) )
locks = "cmd:serversetting(DISCORD_ENABLED) and pperm(Developer)" locks = "cmd:serversetting(DISCORD_ENABLED) and pperm(Developer)"
help_category = "Comms" help_category = "Comms"
@ -1973,6 +1975,13 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
f"WARNING: The Discord bot's typeclass is '{discord_bot.typeclass_path}'. This does not match {settings.DISCORD_BOT_CLASS} in settings!" f"WARNING: The Discord bot's typeclass is '{discord_bot.typeclass_path}'. This does not match {settings.DISCORD_BOT_CLASS} in settings!"
) )
if "start" in self.switches:
if discord_bot.sessions.all():
self.msg("The Discord bot is already running.")
else:
discord_bot.start()
return
if "guild" in self.switches: if "guild" in self.switches:
discord_bot.db.tag_guild = not discord_bot.db.tag_guild discord_bot.db.tag_guild = not discord_bot.db.tag_guild
self.msg( self.msg(

View file

@ -84,7 +84,7 @@ def should_retry(status_code):
class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.ReconnectingClientFactory): class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.ReconnectingClientFactory):
""" """
A variant of the websocket-factory that auto-reconnects. A customized websocket client factory that navigates the Discord gateway process.
""" """
@ -94,7 +94,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
noisy = False noisy = False
gateway = None gateway = None
resume_url = None resume_url = None
do_retry = True is_connecting = False
def __init__(self, sessionhandler, *args, **kwargs): def __init__(self, sessionhandler, *args, **kwargs):
self.uid = kwargs.get("uid") self.uid = kwargs.get("uid")
@ -122,8 +122,8 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
d = readBody(response) d = readBody(response)
d.addCallback(self.websocket_init, *args, **kwargs) d.addCallback(self.websocket_init, *args, **kwargs)
return d return d
elif should_retry(response.code): else:
delay(300, self.get_gateway_url, *args, **kwargs) logger.log_warn("Discord gateway request failed.")
d.addCallback(cbResponse) d.addCallback(cbResponse)
@ -132,6 +132,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
callback for when the URL is gotten callback for when the URL is gotten
""" """
data = json.loads(str(payload, "utf-8")) data = json.loads(str(payload, "utf-8"))
self.is_connecting = False
if url := data.get("url"): if url := data.get("url"):
self.gateway = f"{url}/?v={DISCORD_API_VERSION}&encoding=json".encode("utf-8") self.gateway = f"{url}/?v={DISCORD_API_VERSION}&encoding=json".encode("utf-8")
useragent = kwargs.pop("useragent", DISCORD_USER_AGENT) useragent = kwargs.pop("useragent", DISCORD_USER_AGENT)
@ -179,30 +180,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
connector (Connector): Represents the connection. connector (Connector): Represents the connection.
""" """
logger.log_info("Attempting connection to Discord...") logger.log_info("Connecting to Discord...")
def clientConnectionFailed(self, connector, reason):
"""
Called when Client failed to connect.
Args:
connector (Connection): Represents the connection.
reason (str): The reason for the failure.
"""
protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
def clientConnectionLost(self, connector, reason):
"""
Called when Client loses connection.
Args:
connector (Connection): Represents the connection.
reason (str): The reason for the failure.
"""
if self.do_retry and self.bot:
self.retry(connector)
def reconnect(self): def reconnect(self):
""" """
@ -210,33 +188,30 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
de-registering the session and then reattaching a new one. de-registering the session and then reattaching a new one.
""" """
# set the retry flag to False so it doesn't attempt an automatic retry
# and duplicate the connection
self.do_retry = False
# disconnect everything
self.bot.transport.loseConnection()
self.sessionhandler.server_disconnect(self.bot)
# set up the reconnection # set up the reconnection
if self.resume_url: if self.resume_url:
self.url = self.resume_url self.url = self.resume_url
elif self.gateway: elif self.gateway:
self.url = self.gateway self.url = self.gateway
else: else:
# we don't know where to reconnect to! start from the beginning # we don't know where to reconnect to! we'll start from the beginning
self.get_gateway_url() self.url = None
return # reset the internal delay, since this is a deliberate disconnect
self.start() self.delay = self.initialDelay
# disconnect to allow the reconnection process to kick in
self.bot.sendClose()
self.sessionhandler.server_disconnect(self.bot)
def start(self): def start(self):
"Connect protocol to remote server" "Connect protocol to remote server"
if not self.gateway: if not self.gateway:
# we can't actually start yet # we don't know where to connect to
# get the gateway URL from Discord # get the gateway URL from Discord
self.is_connecting = True
self.get_gateway_url() self.get_gateway_url()
else: elif not self.is_connecting:
# set the retry flag so we maintain this connection # everything is good, connect
self.do_retry = True
connectWS(self) connectWS(self)
@ -255,7 +230,6 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
def __init__(self): def __init__(self):
WebSocketClientProtocol.__init__(self) WebSocketClientProtocol.__init__(self)
_BASE_SESSION_CLASS.__init__(self) _BASE_SESSION_CLASS.__init__(self)
self.restart_downtime = None
def at_login(self): def at_login(self):
pass pass
@ -265,8 +239,7 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
Called when connection is established. Called when connection is established.
""" """
self.restart_downtime = None logger.log_msg("Discord connection established.")
self.restart_task = None
self.factory.bot = self self.factory.bot = self
self.init_session("discord", "discord.gg", self.factory.sessionhandler) self.init_session("discord", "discord.gg", self.factory.sessionhandler)
@ -352,11 +325,11 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
""" """
if self.nextHeartbeatCall: if self.nextHeartbeatCall:
self.nextHeartbeatCall.cancel() self.nextHeartbeatCall.cancel()
self.disconnect(reason) self.nextHeartbeatCall = None
if code >= 4000: if wasClean:
logger.log_err(f"Discord connection closed: {reason}") logger.log_info(f"Discord connection closed ({code}) reason: {reason}")
else: else:
logger.log_info(f"Discord disconnected: {reason}") logger.log_info(f"Discord connection lost.")
def _send_json(self, data): def _send_json(self, data):
""" """