mirror of
https://github.com/jlu5/PyLink.git
synced 2025-02-17 14:01:03 +01:00
relay: move iteration over all networks into a wrapper function
First part of #471.
This commit is contained in:
parent
a43076e815
commit
f203abdeb0
115
plugins/relay.py
115
plugins/relay.py
@ -611,9 +611,23 @@ def is_relay_client(irc, user):
|
|||||||
return False
|
return False
|
||||||
isRelayClient = is_relay_client
|
isRelayClient = is_relay_client
|
||||||
|
|
||||||
|
def iterate_all(origirc, func, extra_args=(), kwargs=None):
|
||||||
|
"""
|
||||||
|
Runs the given function 'func' on all connected networks. 'func' must take at least two arguments: the original IRCNetwork object and the remote IRCNetwork object.
|
||||||
|
"""
|
||||||
|
if kwargs is None:
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
for name, remoteirc in world.networkobjects.copy().items():
|
||||||
|
if name == origirc.name or not remoteirc.connected.is_set():
|
||||||
|
# Don't relay things to their source network...
|
||||||
|
continue
|
||||||
|
|
||||||
|
func(origirc, remoteirc, *extra_args, **kwargs)
|
||||||
|
|
||||||
### EVENT HANDLER INTERNALS
|
### EVENT HANDLER INTERNALS
|
||||||
|
|
||||||
def relay_joins(irc, channel, users, ts, burst=True):
|
def relay_joins(irc, channel, users, ts, **kwargs):
|
||||||
"""
|
"""
|
||||||
Relays one or more users' joins from a channel to its relay links.
|
Relays one or more users' joins from a channel to its relay links.
|
||||||
"""
|
"""
|
||||||
@ -623,17 +637,14 @@ def relay_joins(irc, channel, users, ts, burst=True):
|
|||||||
log.debug('(%s) relay: resetting too low TS value of %s on %s to %s', irc.name, ts, users, current_ts)
|
log.debug('(%s) relay: resetting too low TS value of %s on %s to %s', irc.name, ts, users, current_ts)
|
||||||
ts = current_ts
|
ts = current_ts
|
||||||
|
|
||||||
for name, remoteirc in world.networkobjects.copy().items():
|
def _relay_joins_loop(irc, remoteirc, channel, users, ts, burst=True):
|
||||||
queued_users = []
|
queued_users = []
|
||||||
if name == irc.name or not remoteirc.connected.is_set():
|
|
||||||
# Don't relay things to their source network...
|
|
||||||
continue
|
|
||||||
|
|
||||||
remotechan = get_remote_channel(irc, remoteirc, channel)
|
remotechan = get_remote_channel(irc, remoteirc, channel)
|
||||||
if remotechan is None:
|
if remotechan is None:
|
||||||
# If there is no link on the current network for the channel in question,
|
# If there is no link on the current network for the channel in question,
|
||||||
# just skip it
|
# just skip it.
|
||||||
continue
|
return
|
||||||
|
|
||||||
for user in users.copy():
|
for user in users.copy():
|
||||||
if is_relay_client(irc, user):
|
if is_relay_client(irc, user):
|
||||||
@ -680,15 +691,13 @@ def relay_joins(irc, channel, users, ts, burst=True):
|
|||||||
|
|
||||||
remoteirc.call_hooks([rsid, 'PYLINK_RELAY_JOIN', {'channel': remotechan, 'users': [u[-1] for u in queued_users]}])
|
remoteirc.call_hooks([rsid, 'PYLINK_RELAY_JOIN', {'channel': remotechan, 'users': [u[-1] for u in queued_users]}])
|
||||||
|
|
||||||
def relay_part(irc, channel, user):
|
iterate_all(irc, _relay_joins_loop, extra_args=(channel, users, ts), kwargs=kwargs)
|
||||||
|
|
||||||
|
def relay_part(irc, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Relays a user part from a channel to its relay links, as part of a channel delink.
|
Relays a user part from a channel to its relay links, as part of a channel delink.
|
||||||
"""
|
"""
|
||||||
for name, remoteirc in world.networkobjects.copy().items():
|
def _relay_part_loop(irc, remoteirc, channel, user):
|
||||||
if name == irc.name or not remoteirc.connected.is_set():
|
|
||||||
# Don't relay things to their source network...
|
|
||||||
continue
|
|
||||||
|
|
||||||
remotechan = get_remote_channel(irc, remoteirc, channel)
|
remotechan = get_remote_channel(irc, remoteirc, channel)
|
||||||
log.debug('(%s) relay.relay_part: looking for %s/%s on %s', irc.name, user, irc.name, remoteirc.name)
|
log.debug('(%s) relay.relay_part: looking for %s/%s on %s', irc.name, user, irc.name, remoteirc.name)
|
||||||
log.debug('(%s) relay.relay_part: remotechan found as %s', irc.name, remotechan)
|
log.debug('(%s) relay.relay_part: remotechan found as %s', irc.name, remotechan)
|
||||||
@ -699,7 +708,7 @@ def relay_part(irc, channel, user):
|
|||||||
if remotechan is None or remoteuser is None:
|
if remotechan is None or remoteuser is None:
|
||||||
# If there is no relay channel on the target network, or the relay
|
# If there is no relay channel on the target network, or the relay
|
||||||
# user doesn't exist, just do nothing.
|
# user doesn't exist, just do nothing.
|
||||||
continue
|
return
|
||||||
|
|
||||||
# Part the relay client with the channel delinked message.
|
# Part the relay client with the channel delinked message.
|
||||||
remoteirc.part(remoteuser, remotechan, 'Channel delinked.')
|
remoteirc.part(remoteuser, remotechan, 'Channel delinked.')
|
||||||
@ -709,6 +718,7 @@ def relay_part(irc, channel, user):
|
|||||||
remoteirc.quit(remoteuser, 'Left all shared channels.')
|
remoteirc.quit(remoteuser, 'Left all shared channels.')
|
||||||
del relayusers[(irc.name, user)][remoteirc.name]
|
del relayusers[(irc.name, user)][remoteirc.name]
|
||||||
|
|
||||||
|
iterate_all(irc, _relay_part_loop, extra_args=args, kwargs=kwargs)
|
||||||
|
|
||||||
whitelisted_cmodes = {'admin', 'allowinvite', 'autoop', 'ban', 'banexception',
|
whitelisted_cmodes = {'admin', 'allowinvite', 'autoop', 'ban', 'banexception',
|
||||||
'blockcolor', 'halfop', 'invex', 'inviteonly', 'key',
|
'blockcolor', 'halfop', 'invex', 'inviteonly', 'key',
|
||||||
@ -1069,14 +1079,13 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
target = '#' + target
|
target = '#' + target
|
||||||
|
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
for name, remoteirc in world.networkobjects.copy().items():
|
def _handle_messages_loop(irc, remoteirc, numeric, command, args, notice, target, text):
|
||||||
real_target = get_remote_channel(irc, remoteirc, target)
|
real_target = get_remote_channel(irc, remoteirc, target)
|
||||||
|
|
||||||
# Don't relay anything back to the source net, or to disconnected networks
|
# Don't relay anything back to the source net, or to disconnected networks
|
||||||
# and networks without a relay for this channel.
|
# and networks without a relay for this channel.
|
||||||
if irc.name == name or (not remoteirc.connected.is_set()) or (not real_target) \
|
if (not real_target) or (not irc.connected.is_set()):
|
||||||
or (not irc.connected.is_set()):
|
return
|
||||||
continue
|
|
||||||
|
|
||||||
user = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
|
user = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
|
||||||
|
|
||||||
@ -1085,7 +1094,7 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
conf.conf.get('relay', {}).get('accept_weird_senders', True))):
|
conf.conf.get('relay', {}).get('accept_weird_senders', True))):
|
||||||
log.debug("(%s) Dropping message for %s from user-less sender %s", irc.name,
|
log.debug("(%s) Dropping message for %s from user-less sender %s", irc.name,
|
||||||
real_target, numeric)
|
real_target, numeric)
|
||||||
continue
|
return
|
||||||
# No relay clone exists for the sender; route the message through our
|
# No relay clone exists for the sender; route the message through our
|
||||||
# main client (or SID for notices).
|
# main client (or SID for notices).
|
||||||
|
|
||||||
@ -1104,14 +1113,14 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
user = get_remote_sid(remoteirc, irc, spawn_if_missing=False) \
|
user = get_remote_sid(remoteirc, irc, spawn_if_missing=False) \
|
||||||
if notice else remoteirc.pseudoclient.uid
|
if notice else remoteirc.pseudoclient.uid
|
||||||
if not user:
|
if not user:
|
||||||
continue
|
return
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Remote main client hasn't spawned yet. Drop the message.
|
# Remote main client hasn't spawned yet. Drop the message.
|
||||||
continue
|
return
|
||||||
else:
|
else:
|
||||||
if remoteirc.pseudoclient.uid not in remoteirc.users:
|
if remoteirc.pseudoclient.uid not in remoteirc.users:
|
||||||
# Remote UID is ghosted, drop message.
|
# Remote UID is ghosted, drop message.
|
||||||
continue
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
real_text = text
|
real_text = text
|
||||||
@ -1131,7 +1140,8 @@ def handle_messages(irc, numeric, command, args):
|
|||||||
log.warning("(%s) relay: Relay client %s on %s was killed while "
|
log.warning("(%s) relay: Relay client %s on %s was killed while "
|
||||||
"trying to send a message through it!", irc.name,
|
"trying to send a message through it!", irc.name,
|
||||||
remoteirc.name, user)
|
remoteirc.name, user)
|
||||||
continue
|
return
|
||||||
|
iterate_all(irc, _handle_messages_loop, extra_args=(numeric, command, args, notice, target, text))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Get the real user that the PM was meant for
|
# Get the real user that the PM was meant for
|
||||||
@ -1196,17 +1206,21 @@ def handle_kick(irc, source, command, args):
|
|||||||
return
|
return
|
||||||
|
|
||||||
origuser = get_orig_user(irc, target)
|
origuser = get_orig_user(irc, target)
|
||||||
for name, remoteirc in world.networkobjects.copy().items():
|
|
||||||
if irc.name == name or not remoteirc.connected.is_set():
|
def _handle_kick_loop(irc, remoteirc, source, command, args):
|
||||||
continue
|
|
||||||
remotechan = get_remote_channel(irc, remoteirc, channel)
|
remotechan = get_remote_channel(irc, remoteirc, channel)
|
||||||
|
name = remoteirc.name
|
||||||
log.debug('(%s) relay.handle_kick: remotechan for %s on %s is %s', irc.name, channel, name, remotechan)
|
log.debug('(%s) relay.handle_kick: remotechan for %s on %s is %s', irc.name, channel, name, remotechan)
|
||||||
|
|
||||||
if remotechan is None:
|
if remotechan is None:
|
||||||
continue
|
return
|
||||||
|
|
||||||
real_kicker = get_remote_user(irc, remoteirc, kicker, spawn_if_missing=False)
|
real_kicker = get_remote_user(irc, remoteirc, kicker, spawn_if_missing=False)
|
||||||
log.debug('(%s) relay.handle_kick: real kicker for %s on %s is %s', irc.name, kicker, name, real_kicker)
|
log.debug('(%s) relay.handle_kick: real kicker for %s on %s is %s', irc.name, kicker, name, real_kicker)
|
||||||
|
|
||||||
if not is_relay_client(irc, target):
|
if not is_relay_client(irc, target):
|
||||||
log.debug('(%s) relay.handle_kick: target %s is NOT an internal client', irc.name, target)
|
log.debug('(%s) relay.handle_kick: target %s is NOT an internal client', irc.name, target)
|
||||||
|
|
||||||
# Both the target and kicker are external clients; i.e.
|
# Both the target and kicker are external clients; i.e.
|
||||||
# they originate from the same network. We won't have
|
# they originate from the same network. We won't have
|
||||||
# to filter this; the uplink IRCd will handle it appropriately,
|
# to filter this; the uplink IRCd will handle it appropriately,
|
||||||
@ -1216,6 +1230,7 @@ def handle_kick(irc, source, command, args):
|
|||||||
else:
|
else:
|
||||||
log.debug('(%s) relay.handle_kick: target %s is an internal client, going to look up the real user', irc.name, target)
|
log.debug('(%s) relay.handle_kick: target %s is an internal client, going to look up the real user', irc.name, target)
|
||||||
real_target = get_orig_user(irc, target, targetirc=remoteirc)
|
real_target = get_orig_user(irc, target, targetirc=remoteirc)
|
||||||
|
|
||||||
if not check_claim(irc, channel, kicker):
|
if not check_claim(irc, channel, kicker):
|
||||||
log.debug('(%s) relay.handle_kick: kicker %s is not opped... We should rejoin the target user %s', irc.name, kicker, real_target)
|
log.debug('(%s) relay.handle_kick: kicker %s is not opped... We should rejoin the target user %s', irc.name, kicker, real_target)
|
||||||
# Home network is not in the channel's claim AND the kicker is not
|
# Home network is not in the channel's claim AND the kicker is not
|
||||||
@ -1223,8 +1238,10 @@ def handle_kick(irc, source, command, args):
|
|||||||
# TODO: make the check slightly more advanced: i.e. halfops can't
|
# TODO: make the check slightly more advanced: i.e. halfops can't
|
||||||
# kick ops, admins can't kick owners, etc.
|
# kick ops, admins can't kick owners, etc.
|
||||||
modes = get_prefix_modes(remoteirc, irc, remotechan, real_target)
|
modes = get_prefix_modes(remoteirc, irc, remotechan, real_target)
|
||||||
|
|
||||||
# Join the kicked client back with its respective modes.
|
# Join the kicked client back with its respective modes.
|
||||||
irc.sjoin(irc.sid, channel, [(modes, target)])
|
irc.sjoin(irc.sid, channel, [(modes, target)])
|
||||||
|
|
||||||
if kicker in irc.users:
|
if kicker in irc.users:
|
||||||
log.info('(%s) relay: Blocked KICK (reason %r) from %s/%s to relay client %s on %s.',
|
log.info('(%s) relay: Blocked KICK (reason %r) from %s/%s to relay client %s on %s.',
|
||||||
irc.name, args['text'], irc.users[source].nick, irc.name,
|
irc.name, args['text'], irc.users[source].nick, irc.name,
|
||||||
@ -1239,7 +1256,8 @@ def handle_kick(irc, source, command, args):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if not real_target:
|
if not real_target:
|
||||||
continue
|
return
|
||||||
|
|
||||||
# Propogate the kick!
|
# Propogate the kick!
|
||||||
if real_kicker:
|
if real_kicker:
|
||||||
log.debug('(%s) relay.handle_kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan,real_kicker, kicker, irc.name)
|
log.debug('(%s) relay.handle_kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan,real_kicker, kicker, irc.name)
|
||||||
@ -1249,6 +1267,7 @@ def handle_kick(irc, source, command, args):
|
|||||||
# common channels with the target relay network.
|
# common channels with the target relay network.
|
||||||
rsid = get_remote_sid(remoteirc, irc)
|
rsid = get_remote_sid(remoteirc, irc)
|
||||||
log.debug('(%s) relay.handle_kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan, rsid, kicker, irc.name)
|
log.debug('(%s) relay.handle_kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan, rsid, kicker, irc.name)
|
||||||
|
|
||||||
if not irc.has_cap('can-spawn-clients'):
|
if not irc.has_cap('can-spawn-clients'):
|
||||||
# Special case for clientbot: no kick prefixes are needed.
|
# Special case for clientbot: no kick prefixes are needed.
|
||||||
text = args['text']
|
text = args['text']
|
||||||
@ -1261,6 +1280,7 @@ def handle_kick(irc, source, command, args):
|
|||||||
text = "(%s/%s) %s" % (kname, irc.name, args['text'])
|
text = "(%s/%s) %s" % (kname, irc.name, args['text'])
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
text = "(<unknown kicker>@%s) %s" % (irc.name, args['text'])
|
text = "(<unknown kicker>@%s) %s" % (irc.name, args['text'])
|
||||||
|
|
||||||
rsid = rsid or remoteirc.sid # Fall back to the main PyLink SID if get_remote_sid() fails
|
rsid = rsid or remoteirc.sid # Fall back to the main PyLink SID if get_remote_sid() fails
|
||||||
remoteirc.kick(rsid, remotechan, real_target, text)
|
remoteirc.kick(rsid, remotechan, real_target, text)
|
||||||
|
|
||||||
@ -1269,6 +1289,8 @@ def handle_kick(irc, source, command, args):
|
|||||||
del relayusers[(irc.name, target)][remoteirc.name]
|
del relayusers[(irc.name, target)][remoteirc.name]
|
||||||
remoteirc.quit(real_target, 'Left all shared channels.')
|
remoteirc.quit(real_target, 'Left all shared channels.')
|
||||||
|
|
||||||
|
iterate_all(irc, _handle_kick_loop, extra_args=(source, command, args))
|
||||||
|
|
||||||
if origuser and not irc.users[target].channels:
|
if origuser and not irc.users[target].channels:
|
||||||
del relayusers[origuser][irc.name]
|
del relayusers[origuser][irc.name]
|
||||||
irc.quit(target, 'Left all shared channels.')
|
irc.quit(target, 'Left all shared channels.')
|
||||||
@ -1305,13 +1327,9 @@ for c in ('CHGHOST', 'CHGNAME', 'CHGIDENT'):
|
|||||||
utils.add_hook(handle_chgclient, c)
|
utils.add_hook(handle_chgclient, c)
|
||||||
|
|
||||||
def handle_mode(irc, numeric, command, args):
|
def handle_mode(irc, numeric, command, args):
|
||||||
target = args['target']
|
def _handle_mode_loop(irc, remoteirc, numeric, command, args):
|
||||||
modes = args['modes']
|
target = args['target']
|
||||||
|
modes = args['modes']
|
||||||
for name, remoteirc in world.networkobjects.copy().items():
|
|
||||||
if irc.name == name or not remoteirc.connected.is_set():
|
|
||||||
continue
|
|
||||||
|
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
# Use the old state of the channel to check for CLAIM access.
|
# Use the old state of the channel to check for CLAIM access.
|
||||||
oldchan = args.get('channeldata')
|
oldchan = args.get('channeldata')
|
||||||
@ -1335,7 +1353,7 @@ def handle_mode(irc, numeric, command, args):
|
|||||||
irc.name, modes, reversed_modes)
|
irc.name, modes, reversed_modes)
|
||||||
if reversed_modes:
|
if reversed_modes:
|
||||||
irc.mode(irc.sid, target, reversed_modes)
|
irc.mode(irc.sid, target, reversed_modes)
|
||||||
break
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Set hideoper on remote opers, to prevent inflating
|
# Set hideoper on remote opers, to prevent inflating
|
||||||
@ -1353,6 +1371,7 @@ def handle_mode(irc, numeric, command, args):
|
|||||||
|
|
||||||
if remoteuser and modes:
|
if remoteuser and modes:
|
||||||
remoteirc.mode(remoteuser, remoteuser, modes)
|
remoteirc.mode(remoteuser, remoteuser, modes)
|
||||||
|
iterate_all(irc, _handle_mode_loop, extra_args=(numeric, command, args))
|
||||||
|
|
||||||
utils.add_hook(handle_mode, 'MODE')
|
utils.add_hook(handle_mode, 'MODE')
|
||||||
|
|
||||||
@ -1360,15 +1379,19 @@ def handle_topic(irc, numeric, command, args):
|
|||||||
channel = args['channel']
|
channel = args['channel']
|
||||||
oldtopic = args.get('oldtopic')
|
oldtopic = args.get('oldtopic')
|
||||||
topic = args['text']
|
topic = args['text']
|
||||||
|
|
||||||
if check_claim(irc, channel, numeric):
|
if check_claim(irc, channel, numeric):
|
||||||
for name, remoteirc in world.networkobjects.copy().items():
|
def _handle_topic_loop(irc, remoteirc, numeric, command, args):
|
||||||
if irc.name == name or not remoteirc.connected.is_set():
|
channel = args['channel']
|
||||||
continue
|
oldtopic = args.get('oldtopic')
|
||||||
|
topic = args['text']
|
||||||
|
|
||||||
remotechan = get_remote_channel(irc, remoteirc, channel)
|
remotechan = get_remote_channel(irc, remoteirc, channel)
|
||||||
|
|
||||||
# Don't send if the remote topic is the same as ours.
|
# Don't send if the remote topic is the same as ours.
|
||||||
if remotechan is None or topic == remoteirc.channels[remotechan].topic:
|
if remotechan is None or topic == remoteirc.channels[remotechan].topic:
|
||||||
continue
|
return
|
||||||
|
|
||||||
# This might originate from a server too.
|
# This might originate from a server too.
|
||||||
remoteuser = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
|
remoteuser = get_remote_user(irc, remoteirc, numeric, spawn_if_missing=False)
|
||||||
if remoteuser:
|
if remoteuser:
|
||||||
@ -1376,6 +1399,8 @@ def handle_topic(irc, numeric, command, args):
|
|||||||
else:
|
else:
|
||||||
rsid = get_remote_sid(remoteirc, irc)
|
rsid = get_remote_sid(remoteirc, irc)
|
||||||
remoteirc.topic_burst(rsid, remotechan, topic)
|
remoteirc.topic_burst(rsid, remotechan, topic)
|
||||||
|
iterate_all(irc, _handle_topic_loop, extra_args=(numeric, command, args))
|
||||||
|
|
||||||
elif oldtopic: # Topic change blocked by claim.
|
elif oldtopic: # Topic change blocked by claim.
|
||||||
irc.topic_burst(irc.sid, channel, oldtopic)
|
irc.topic_burst(irc.sid, channel, oldtopic)
|
||||||
|
|
||||||
@ -1493,18 +1518,22 @@ def handle_disconnect(irc, numeric, command, args):
|
|||||||
log.debug('(%s) Grabbing spawnlocks_servers[%s] from thread %r in function %r', irc.name, irc.name,
|
log.debug('(%s) Grabbing spawnlocks_servers[%s] from thread %r in function %r', irc.name, irc.name,
|
||||||
threading.current_thread().name, inspect.currentframe().f_code.co_name)
|
threading.current_thread().name, inspect.currentframe().f_code.co_name)
|
||||||
if spawnlocks_servers[irc.name].acquire(timeout=TCONDITION_TIMEOUT):
|
if spawnlocks_servers[irc.name].acquire(timeout=TCONDITION_TIMEOUT):
|
||||||
for name, ircobj in world.networkobjects.copy().items():
|
|
||||||
|
def _handle_disconnect_loop(irc, remoteirc):
|
||||||
|
name = remoteirc.name
|
||||||
if name != irc.name:
|
if name != irc.name:
|
||||||
try:
|
try:
|
||||||
rsid = relayservers[name][irc.name]
|
rsid = relayservers[name][irc.name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
return
|
||||||
else:
|
else:
|
||||||
ircobj.proto.squit(ircobj.sid, rsid, text='Relay network lost connection.')
|
remoteirc.proto.squit(remoteirc.sid, rsid, text='Relay network lost connection.')
|
||||||
|
|
||||||
if irc.name in relayservers[name]:
|
if irc.name in relayservers[name]:
|
||||||
del relayservers[name][irc.name]
|
del relayservers[name][irc.name]
|
||||||
|
|
||||||
|
iterate_all(irc, _handle_disconnect_loop)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
del relayservers[irc.name]
|
del relayservers[irc.name]
|
||||||
except KeyError: # Already removed; ignore.
|
except KeyError: # Already removed; ignore.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user