mirror of
https://github.com/jlu5/PyLink.git
synced 2024-11-27 21:19:31 +01:00
commit
8b77956ab9
@ -186,7 +186,7 @@ def showchan(irc, source, args):
|
||||
def mode(irc, source, args):
|
||||
"""<source> <target> <modes>
|
||||
|
||||
Admin-only. Sets modes <modes> on <target> from <source>, where <source> is the nick of a PyLink client."""
|
||||
Admin-only. Sets modes <modes> on <target> from <source>, where <source> is either the nick of a PyLink client, or the SID of a PyLink server."""
|
||||
checkauthenticated(irc, source)
|
||||
try:
|
||||
modesource, target, modes = args[0], args[1], args[2:]
|
||||
@ -208,3 +208,30 @@ def mode(irc, source, args):
|
||||
else:
|
||||
sourceuid = utils.nickToUid(irc, modesource)
|
||||
irc.proto.modeClient(irc, sourceuid, target, parsedmodes)
|
||||
|
||||
@utils.add_cmd
|
||||
def msg(irc, source, args):
|
||||
"""<source> <target> <text>
|
||||
|
||||
Admin-only. Sends message <text> from <source>, where <source> is the nick of a PyLink client."""
|
||||
checkauthenticated(irc, source)
|
||||
try:
|
||||
msgsource, target, text = args[0], args[1], ' '.join(args[2:])
|
||||
except IndexError:
|
||||
utils.msg(irc, source, 'Error: not enough arguments. Needs 3: source nick, target, text.')
|
||||
return
|
||||
sourceuid = utils.nickToUid(irc, msgsource)
|
||||
if not sourceuid:
|
||||
utils.msg(irc, source, 'Error: unknown user %r' % msgsource)
|
||||
return
|
||||
if not utils.isChannel(target):
|
||||
real_target = utils.nickToUid(irc, target)
|
||||
if real_target is None:
|
||||
utils.msg(irc, source, 'Error: unknown user %r' % target)
|
||||
return
|
||||
else:
|
||||
real_target = target
|
||||
if not text:
|
||||
utils.msg(irc, source, 'Error: no text given.')
|
||||
return
|
||||
irc.proto.messageClient(irc, sourceuid, real_target, text)
|
||||
|
118
plugins/relay.py
118
plugins/relay.py
@ -16,7 +16,9 @@ dbname = "pylinkrelay"
|
||||
if confname != 'pylink':
|
||||
dbname += '-%s' % confname
|
||||
dbname += '.db'
|
||||
|
||||
relayusers = defaultdict(dict)
|
||||
spawnlocks = defaultdict(threading.Lock)
|
||||
|
||||
def relayWhoisHandlers(irc, target):
|
||||
user = irc.users[target]
|
||||
@ -124,6 +126,7 @@ def getRemoteUser(irc, remoteirc, user, spawnIfMissing=True):
|
||||
return remoteirc.pseudoclient.uid
|
||||
except AttributeError: # Network hasn't been initialized yet?
|
||||
pass
|
||||
with spawnlocks[irc.name]:
|
||||
try:
|
||||
u = relayusers[(irc.name, user)][remoteirc.name]
|
||||
except KeyError:
|
||||
@ -142,7 +145,7 @@ def getRemoteUser(irc, remoteirc, user, spawnIfMissing=True):
|
||||
u = remoteirc.proto.spawnClient(remoteirc, nick, ident=ident,
|
||||
host=host, realname=realname,
|
||||
modes=modes, ts=userobj.ts).uid
|
||||
remoteirc.users[u].remote = irc.name
|
||||
remoteirc.users[u].remote = (irc.name, user)
|
||||
away = userobj.away
|
||||
if away:
|
||||
remoteirc.proto.awayClient(remoteirc, u, away)
|
||||
@ -160,21 +163,10 @@ def getLocalUser(irc, user, targetirc=None):
|
||||
representing the original user on the target network, similar to what
|
||||
getRemoteUser() does."""
|
||||
# First, iterate over everyone!
|
||||
try:
|
||||
remoteuser = irc.users[user].remote
|
||||
except (AttributeError, KeyError):
|
||||
remoteuser = None
|
||||
for k, v in relayusers.items():
|
||||
if k[0] == irc.name:
|
||||
# We don't need to do anything if the target users is on
|
||||
# the same network as us.
|
||||
continue
|
||||
if v.get(irc.name) == user:
|
||||
# If the stored pseudoclient UID for the kicked user on
|
||||
# this network matches the target we have, set that user
|
||||
# as the one we're kicking! It's a handful, but remember
|
||||
# we're mapping (home network, UID) pairs to their
|
||||
# respective relay pseudoclients on other networks.
|
||||
remoteuser = k
|
||||
log.debug('(%s) getLocalUser: found %s to correspond to %s.', irc.name, v, k)
|
||||
break
|
||||
log.debug('(%s) getLocalUser: remoteuser set to %r (looking up %s/%s).', irc.name, remoteuser, user, irc.name)
|
||||
if remoteuser:
|
||||
# If targetirc is given, we'll return simply the UID of the user on the
|
||||
@ -223,6 +215,11 @@ def initializeChannel(irc, channel):
|
||||
log.debug('(%s) initializeChannel: relay pair found to be %s', irc.name, relay)
|
||||
queued_users = []
|
||||
if relay:
|
||||
# Send our users and channel modes to the other nets
|
||||
log.debug('(%s) initializeChannel: joining our users: %s', irc.name, c.users)
|
||||
relayJoins(irc, channel, c.users, c.ts)
|
||||
irc.proto.joinClient(irc, irc.pseudoclient.uid, channel)
|
||||
|
||||
all_links = db[relay]['links'].copy()
|
||||
all_links.update((relay,))
|
||||
log.debug('(%s) initializeChannel: all_links: %s', irc.name, all_links)
|
||||
@ -239,29 +236,23 @@ def initializeChannel(irc, channel):
|
||||
if not (remoteirc.connected.is_set() and findRemoteChan(remoteirc, irc, remotechan)):
|
||||
continue # They aren't connected, don't bother!
|
||||
# Join their (remote) users and set their modes.
|
||||
relayJoins(remoteirc, remotechan, rc.users,
|
||||
rc.ts, rc.modes)
|
||||
relayModes(irc, remoteirc, irc.sid, channel)
|
||||
topic = remoteirc.channels[relay[1]].topic
|
||||
relayJoins(remoteirc, remotechan, rc.users, rc.ts)
|
||||
relayModes(remoteirc, irc, remoteirc.sid, remotechan, rc.modes)
|
||||
relayModes(irc, remoteirc, irc.sid, channel, modes)
|
||||
topic = remoteirc.channels[remotechan].topic
|
||||
# Only update the topic if it's different from what we already have,
|
||||
# and topic bursting is complete.
|
||||
if remoteirc.channels[channel].topicset and topic != irc.channels[channel].topic:
|
||||
if remoteirc.channels[remotechan].topicset and topic != irc.channels[channel].topic:
|
||||
irc.proto.topicServer(irc, irc.sid, channel, topic)
|
||||
|
||||
log.debug('(%s) initializeChannel: joining our users: %s', irc.name, c.users)
|
||||
# After that's done, we'll send our users to them.
|
||||
relayJoins(irc, channel, c.users, c.ts, c.modes)
|
||||
irc.proto.joinClient(irc, irc.pseudoclient.uid, channel)
|
||||
|
||||
def handle_join(irc, numeric, command, args):
|
||||
channel = args['channel']
|
||||
if not findRelay((irc.name, channel)):
|
||||
# No relay here, return.
|
||||
return
|
||||
modes = args['modes']
|
||||
ts = args['ts']
|
||||
users = set(args['users'])
|
||||
relayJoins(irc, channel, users, ts, modes)
|
||||
relayJoins(irc, channel, users, ts)
|
||||
utils.add_hook(handle_join, 'JOIN')
|
||||
|
||||
def handle_quit(irc, numeric, command, args):
|
||||
@ -308,20 +299,8 @@ def handle_privmsg(irc, numeric, command, args):
|
||||
text = args['text']
|
||||
if target == irc.pseudoclient.uid:
|
||||
return
|
||||
sent = 0
|
||||
relay = findRelay((irc.name, target))
|
||||
# Don't send any "you must be in common channels" if we're not part
|
||||
# of a relay, or we are but there are no links!
|
||||
remoteusers = relayusers[(irc.name, numeric)].items()
|
||||
'''
|
||||
if utils.isChannel(target) and ((relay and not db[relay]['links']) or \
|
||||
relay is None):
|
||||
return
|
||||
'''
|
||||
if not remoteusers:
|
||||
return
|
||||
for netname, user in relayusers[(irc.name, numeric)].items():
|
||||
remoteirc = utils.networkobjects[netname]
|
||||
remoteusers = relayusers[(irc.name, numeric)]
|
||||
# HACK: Don't break on sending to @#channel or similar.
|
||||
try:
|
||||
prefix, target = target.split('#', 1)
|
||||
@ -329,36 +308,46 @@ def handle_privmsg(irc, numeric, command, args):
|
||||
prefix = ''
|
||||
else:
|
||||
target = '#' + target
|
||||
if utils.isChannel(target):
|
||||
log.debug('(%s) relay privmsg: prefix is %r, target is %r', irc.name, prefix, target)
|
||||
if utils.isChannel(target) and relay and numeric not in irc.channels[target].users:
|
||||
# The sender must be in the target channel to send messages over the relay;
|
||||
# it's the only way we can make sure they have a spawned client on ALL
|
||||
# of the linked networks. This affects -n channels too; see
|
||||
# https://github.com/GLolol/PyLink/issues/91 for an explanation of why.
|
||||
utils.msg(irc, numeric, 'Error: You must be in %r in order to send '
|
||||
'messages over the relay.' % target, notice=True)
|
||||
return
|
||||
if utils.isChannel(target):
|
||||
for netname, user in relayusers[(irc.name, numeric)].items():
|
||||
remoteirc = utils.networkobjects[netname]
|
||||
real_target = findRemoteChan(irc, remoteirc, target)
|
||||
if not real_target:
|
||||
continue
|
||||
real_target = prefix + real_target
|
||||
else:
|
||||
remoteuser = getLocalUser(irc, target)
|
||||
if remoteuser is None:
|
||||
continue
|
||||
real_target = remoteuser[1]
|
||||
if notice:
|
||||
remoteirc.proto.noticeClient(remoteirc, user, real_target, text)
|
||||
else:
|
||||
remoteirc.proto.messageClient(remoteirc, user, real_target, text)
|
||||
sent += 1
|
||||
'''
|
||||
if not sent:
|
||||
# We must be on a common channel with the target. Otherwise, the sender
|
||||
# doesn't have a client representing them on the remote network,
|
||||
# and we won't have anywhere to send our messages from.
|
||||
# In this case, we've iterated over all networks where the sender
|
||||
# has pseudoclients, and found no suitable targets to send to.
|
||||
if target in irc.users:
|
||||
target_s = 'a common channel with %r' % irc.users[target].nick
|
||||
else:
|
||||
target_s = repr(target)
|
||||
utils.msg(irc, numeric, 'Error: You must be in %s in order to send messages.' % \
|
||||
target_s, notice=True)
|
||||
'''
|
||||
remoteuser = getLocalUser(irc, target)
|
||||
if remoteuser is None:
|
||||
return
|
||||
homenet, real_target = remoteuser
|
||||
# For PMs, we must be on a common channel with the target.
|
||||
# Otherwise, the sender doesn't have a client representing them
|
||||
# on the remote network, and we won't have anything to send our
|
||||
# messages from.
|
||||
if homenet not in remoteusers.keys():
|
||||
utils.msg(irc, numeric, 'Error: you must be in a common channel '
|
||||
'with %r in order to send messages.' % \
|
||||
irc.users[target].nick, notice=True)
|
||||
return
|
||||
remoteirc = utils.networkobjects[homenet]
|
||||
user = getRemoteUser(irc, remoteirc, numeric, spawnIfMissing=False)
|
||||
if notice:
|
||||
remoteirc.proto.noticeClient(remoteirc, user, real_target, text)
|
||||
else:
|
||||
remoteirc.proto.messageClient(remoteirc, user, real_target, text)
|
||||
utils.add_hook(handle_privmsg, 'PRIVMSG')
|
||||
utils.add_hook(handle_privmsg, 'NOTICE')
|
||||
|
||||
@ -386,7 +375,7 @@ def handle_kick(irc, source, command, args):
|
||||
# they originate from the same network. We won't have
|
||||
# to filter this; the uplink IRCd will handle it appropriately,
|
||||
# and we'll just follow.
|
||||
real_target = getRemoteUser(irc, remoteirc, target)
|
||||
real_target = getRemoteUser(irc, remoteirc, target, spawnIfMissing=False)
|
||||
log.debug('(%s) Relay kick: real target for %s is %s', irc.name, target, real_target)
|
||||
else:
|
||||
log.debug('(%s) Relay kick: target %s is an internal client, going to look up the real user', irc.name, target)
|
||||
@ -429,9 +418,9 @@ def handle_kick(irc, source, command, args):
|
||||
remotechan, real_target, text)
|
||||
|
||||
if target != irc.pseudoclient.uid and not irc.users[target].channels:
|
||||
irc.proto.quitClient(irc, target, 'Left all shared channels.')
|
||||
remoteuser = getLocalUser(irc, target)
|
||||
del relayusers[remoteuser][irc.name]
|
||||
irc.proto.quitClient(irc, target, 'Left all shared channels.')
|
||||
|
||||
utils.add_hook(handle_kick, 'KICK')
|
||||
|
||||
@ -643,7 +632,7 @@ def handle_kill(irc, numeric, command, args):
|
||||
|
||||
utils.add_hook(handle_kill, 'KILL')
|
||||
|
||||
def relayJoins(irc, channel, users, ts, modes):
|
||||
def relayJoins(irc, channel, users, ts):
|
||||
for name, remoteirc in utils.networkobjects.items():
|
||||
queued_users = []
|
||||
if name == irc.name or not remoteirc.connected.is_set():
|
||||
@ -672,7 +661,7 @@ def relayJoins(irc, channel, users, ts, modes):
|
||||
u = getRemoteUser(irc, remoteirc, user)
|
||||
# Only join users if they aren't already joined. This prevents op floods
|
||||
# on charybdis from all the SJOINing.
|
||||
if u not in remoteirc.channels[channel].users:
|
||||
if u not in remoteirc.channels[remotechan].users:
|
||||
ts = irc.channels[channel].ts
|
||||
prefixes = getPrefixModes(irc, remoteirc, channel, user)
|
||||
userpair = (prefixes, u)
|
||||
@ -683,7 +672,6 @@ def relayJoins(irc, channel, users, ts, modes):
|
||||
u, remoteirc.name, remotechan)
|
||||
if queued_users:
|
||||
remoteirc.proto.sjoinServer(remoteirc, remoteirc.sid, remotechan, queued_users, ts=ts)
|
||||
relayModes(irc, remoteirc, irc.sid, channel, modes)
|
||||
|
||||
def relayPart(irc, channel, user):
|
||||
for name, remoteirc in utils.networkobjects.items():
|
||||
|
@ -56,10 +56,11 @@ def joinClient(irc, client, channel):
|
||||
if not server:
|
||||
log.error('(%s) Error trying to join client %r to %r (no such pseudoclient exists)', irc.name, client, channel)
|
||||
raise LookupError('No such PyLink PseudoClient exists.')
|
||||
# One channel per line here!
|
||||
# Strip out list-modes, they shouldn't be ever sent in FJOIN.
|
||||
modes = [m for m in irc.channels[channel].modes if m[0] not in irc.cmodes['*A']]
|
||||
_send(irc, server, "FJOIN {channel} {ts} {modes} :,{uid}".format(
|
||||
ts=irc.channels[channel].ts, uid=client, channel=channel,
|
||||
modes=utils.joinModes(irc.channels[channel].modes)))
|
||||
modes=utils.joinModes(modes)))
|
||||
irc.channels[channel].users.add(client)
|
||||
irc.users[client].channels.add(channel)
|
||||
|
||||
@ -70,16 +71,17 @@ def sjoinServer(irc, server, channel, users, ts=None):
|
||||
log.debug('(%s) sjoinServer: got %r for users', irc.name, users)
|
||||
if not server:
|
||||
raise LookupError('No such PyLink PseudoClient exists.')
|
||||
if ts is None:
|
||||
ts = irc.channels[channel].ts
|
||||
orig_ts = irc.channels[channel].ts
|
||||
ts = ts or orig_ts
|
||||
if ts < orig_ts:
|
||||
log.debug('(%s) sjoinServer: resetting TS of %r from %s to %s (clearing modes)',
|
||||
irc.name, channel, orig_ts, ts)
|
||||
irc.channels[channel].ts = ts
|
||||
irc.channels[channel].modes.clear()
|
||||
for p in irc.channels[channel].prefixmodes.values():
|
||||
p.clear()
|
||||
log.debug("sending SJOIN to %s%s with ts %s (that's %r)", channel, irc.name, ts,
|
||||
time.strftime("%c", time.localtime(ts)))
|
||||
''' TODO: handle this properly!
|
||||
if modes is None:
|
||||
modes = irc.channels[channel].modes
|
||||
else:
|
||||
utils.applyModes(irc, channel, modes)
|
||||
'''
|
||||
# Strip out list-modes, they shouldn't be ever sent in FJOIN.
|
||||
modes = [m for m in irc.channels[channel].modes if m[0] not in irc.cmodes['*A']]
|
||||
uids = []
|
||||
@ -120,8 +122,12 @@ def removeClient(irc, numeric):
|
||||
|
||||
Removes a client from our internal databases, regardless
|
||||
of whether it's one of our pseudoclients or not."""
|
||||
for v in irc.channels.values():
|
||||
for c, v in irc.channels.copy().items():
|
||||
v.removeuser(numeric)
|
||||
# Clear empty non-permanent channels.
|
||||
if not (irc.channels[c].users or ((irc.cmodes.get('permanent'), None) in irc.channels[c].modes)):
|
||||
del irc.channels[c]
|
||||
|
||||
sid = numeric[:3]
|
||||
log.debug('Removing client %s from irc.users', numeric)
|
||||
del irc.users[numeric]
|
||||
@ -245,6 +251,8 @@ def topicClient(irc, numeric, target, text):
|
||||
if not utils.isInternalClient(irc, numeric):
|
||||
raise LookupError('No such PyLink PseudoClient exists.')
|
||||
_send(irc, numeric, 'TOPIC %s :%s' % (target, text))
|
||||
irc.channels[target].topic = text
|
||||
irc.channels[target].topicset = True
|
||||
|
||||
def topicServer(irc, numeric, target, text):
|
||||
if not utils.isInternalServer(irc, numeric):
|
||||
@ -252,6 +260,8 @@ def topicServer(irc, numeric, target, text):
|
||||
ts = int(time.time())
|
||||
servername = irc.servers[numeric].name
|
||||
_send(irc, numeric, 'FTOPIC %s %s %s :%s' % (target, ts, servername, text))
|
||||
irc.channels[target].topic = text
|
||||
irc.channels[target].topicset = True
|
||||
|
||||
def inviteClient(irc, numeric, target, channel):
|
||||
"""<irc object> <client numeric> <text>
|
||||
@ -360,6 +370,9 @@ def handle_part(irc, source, command, args):
|
||||
reason = args[1]
|
||||
except IndexError:
|
||||
reason = ''
|
||||
# Clear empty non-permanent channels.
|
||||
if not (irc.channels[channel].users or ((irc.cmodes.get('permanent'), None) in irc.channels[channel].modes)):
|
||||
del irc.channels[channel]
|
||||
return {'channels': channels, 'text': reason}
|
||||
|
||||
def handle_error(irc, numeric, command, args):
|
||||
@ -378,6 +391,9 @@ def handle_fjoin(irc, servernumeric, command, args):
|
||||
log.debug('(%s) Setting channel TS of %s to %s from %s',
|
||||
irc.name, channel, their_ts, our_ts)
|
||||
irc.channels[channel].ts = their_ts
|
||||
irc.channels[channel].modes.clear()
|
||||
for p in irc.channels[channel].prefixmodes.values():
|
||||
p.clear()
|
||||
modestring = args[2:-1] or args[2]
|
||||
parsedmodes = utils.parseModes(irc, channel, modestring)
|
||||
utils.applyModes(irc, channel, parsedmodes)
|
||||
|
@ -77,8 +77,15 @@ def sjoinServer(irc, server, channel, users, ts=None):
|
||||
log.debug('(%s) sjoinServer: got %r for users', irc.name, users)
|
||||
if not server:
|
||||
raise LookupError('No such PyLink PseudoClient exists.')
|
||||
if ts is None:
|
||||
ts = irc.channels[channel].ts
|
||||
orig_ts = irc.channels[channel].ts
|
||||
ts = ts or orig_ts
|
||||
if ts < orig_ts:
|
||||
log.debug('(%s) sjoinServer: resetting TS of %r from %s to %s (clearing modes)',
|
||||
irc.name, channel, orig_ts, ts)
|
||||
irc.channels[channel].ts = ts
|
||||
irc.channels[channel].modes.clear()
|
||||
for p in irc.channels[channel].prefixmodes.values():
|
||||
p.clear()
|
||||
log.debug("sending SJOIN to %s%s with ts %s (that's %r)", channel, irc.name, ts,
|
||||
time.strftime("%c", time.localtime(ts)))
|
||||
modes = [m for m in irc.channels[channel].modes if m[0] not in irc.cmodes['*A']]
|
||||
@ -95,6 +102,7 @@ def sjoinServer(irc, server, channel, users, ts=None):
|
||||
pr = irc.prefixmodes.get(prefix)
|
||||
if pr:
|
||||
prefixchars += pr
|
||||
changedmodes.append(('+%s' % prefix, user))
|
||||
namelist.append(prefixchars+user)
|
||||
uids.append(user)
|
||||
try:
|
||||
@ -186,6 +194,8 @@ def topicServer(irc, numeric, target, text):
|
||||
ts = irc.channels[target].ts
|
||||
servername = irc.servers[numeric].name
|
||||
_send(irc, numeric, 'TB %s %s %s :%s' % (target, ts, servername, text))
|
||||
irc.channels[target].topic = text
|
||||
irc.channels[target].topicset = True
|
||||
|
||||
def inviteClient(irc, numeric, target, channel):
|
||||
"""<irc object> <client numeric> <text>
|
||||
@ -368,9 +378,10 @@ def handle_part(irc, source, command, args):
|
||||
reason = args[1]
|
||||
except IndexError:
|
||||
reason = ''
|
||||
if not (irc.channels[channel].users or ((irc.cmodes.get('permanent'), None) in irc.channels[channel].modes)):
|
||||
del irc.channels[channel]
|
||||
return {'channels': channels, 'text': reason}
|
||||
|
||||
|
||||
def handle_sjoin(irc, servernumeric, command, args):
|
||||
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
||||
channel = args[1].lower()
|
||||
@ -382,6 +393,9 @@ def handle_sjoin(irc, servernumeric, command, args):
|
||||
log.debug('(%s) Setting channel TS of %s to %s from %s',
|
||||
irc.name, channel, their_ts, our_ts)
|
||||
irc.channels[channel].ts = their_ts
|
||||
irc.channels[channel].modes.clear()
|
||||
for p in irc.channels[channel].prefixmodes.values():
|
||||
p.clear()
|
||||
modestring = args[2:-1] or args[2]
|
||||
parsedmodes = utils.parseModes(irc, channel, modestring)
|
||||
utils.applyModes(irc, channel, parsedmodes)
|
||||
|
3
utils.py
3
utils.py
@ -241,7 +241,10 @@ def applyModes(irc, target, changedmodes):
|
||||
log.debug('(%s) Applying modes %r on %s (initial modelist: %s)', irc.name, changedmodes, target, modelist)
|
||||
for mode in changedmodes:
|
||||
# Chop off the +/- part that parseModes gives; it's meaningless for a mode list.
|
||||
try:
|
||||
real_mode = (mode[0][1], mode[1])
|
||||
except IndexError:
|
||||
real_mode = mode
|
||||
if not usermodes:
|
||||
pmode = ''
|
||||
for m in ('owner', 'admin', 'op', 'halfop', 'voice'):
|
||||
|
Loading…
Reference in New Issue
Block a user