2015-06-07 22:40:18 +02:00
|
|
|
# admin.py: PyLink administrative commands
|
2015-07-19 23:59:51 +02:00
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import inspect
|
2015-06-07 22:40:18 +02:00
|
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
2015-07-19 23:59:51 +02:00
|
|
|
|
2015-06-07 22:40:18 +02:00
|
|
|
import utils
|
2015-07-19 23:59:51 +02:00
|
|
|
from log import log
|
2015-06-07 22:40:18 +02:00
|
|
|
|
|
|
|
class NotAuthenticatedError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def checkauthenticated(irc, source):
|
2015-07-19 23:59:51 +02:00
|
|
|
lastfunc = inspect.stack()[1][3]
|
2015-06-07 22:40:18 +02:00
|
|
|
if not irc.users[source].identified:
|
2015-07-19 23:59:51 +02:00
|
|
|
log.warning('(%s) Access denied for %s calling %r', irc.name,
|
|
|
|
utils.getHostmask(irc, source), lastfunc)
|
2015-06-07 22:40:18 +02:00
|
|
|
raise NotAuthenticatedError("You are not authenticated!")
|
|
|
|
|
2015-06-19 19:44:25 +02:00
|
|
|
def _exec(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<code>
|
|
|
|
|
|
|
|
Admin-only. Executes <code> in the current PyLink instance.
|
|
|
|
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
|
2015-06-07 22:40:18 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
args = ' '.join(args)
|
|
|
|
if not args.strip():
|
|
|
|
utils.msg(irc, source, 'No code entered!')
|
|
|
|
return
|
2015-07-19 23:59:51 +02:00
|
|
|
log.info('(%s) Executing %r for %s', irc.name, args, utils.getHostmask(irc, source))
|
2015-06-19 19:44:25 +02:00
|
|
|
exec(args, globals(), locals())
|
|
|
|
utils.add_cmd(_exec, 'exec')
|
2015-06-07 22:40:18 +02:00
|
|
|
|
|
|
|
@utils.add_cmd
|
|
|
|
def spawnclient(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<nick> <ident> <host>
|
|
|
|
|
|
|
|
Admin-only. Spawns the specified PseudoClient on the PyLink server.
|
|
|
|
Note: this doesn't check the validity of any fields you give it!"""
|
2015-06-07 22:40:18 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
|
|
|
nick, ident, host = args[:3]
|
|
|
|
except ValueError:
|
|
|
|
utils.msg(irc, source, "Error: not enough arguments. Needs 3: nick, user, host.")
|
|
|
|
return
|
2015-06-17 05:05:41 +02:00
|
|
|
irc.proto.spawnClient(irc, nick, ident, host)
|
2015-06-07 22:40:18 +02:00
|
|
|
|
|
|
|
@utils.add_cmd
|
2015-07-11 01:43:03 +02:00
|
|
|
def quit(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<target> [<reason>]
|
|
|
|
|
2015-07-22 22:18:11 +02:00
|
|
|
Admin-only. Quits the PyLink client with nick <target>, if one exists."""
|
2015-06-07 22:40:18 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
2015-06-08 04:31:56 +02:00
|
|
|
nick = args[0]
|
2015-06-07 22:40:18 +02:00
|
|
|
except IndexError:
|
2015-07-09 07:50:19 +02:00
|
|
|
utils.msg(irc, source, "Error: not enough arguments. Needs 1-2: nick, reason (optional).")
|
2015-06-07 22:40:18 +02:00
|
|
|
return
|
2015-06-08 04:36:21 +02:00
|
|
|
if irc.pseudoclient.uid == utils.nickToUid(irc, nick):
|
2015-06-08 04:31:56 +02:00
|
|
|
utils.msg(irc, source, "Error: cannot quit the main PyLink PseudoClient!")
|
2015-06-07 22:40:18 +02:00
|
|
|
return
|
2015-06-08 04:36:21 +02:00
|
|
|
u = utils.nickToUid(irc, nick)
|
2015-06-08 04:31:56 +02:00
|
|
|
quitmsg = ' '.join(args[1:]) or 'Client quit'
|
2015-06-17 05:05:41 +02:00
|
|
|
irc.proto.quitClient(irc, u, quitmsg)
|
2015-08-15 14:12:49 +02:00
|
|
|
irc.callHooks([u, 'PYLINK_ADMIN_QUIT', {'text': quitmsg, 'parse_as': 'QUIT'}])
|
2015-06-07 22:40:18 +02:00
|
|
|
|
|
|
|
def joinclient(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<target> <channel1>,[<channel2>], etc.
|
|
|
|
|
2015-07-22 22:18:11 +02:00
|
|
|
Admin-only. Joins <target>, the nick of a PyLink client, to a comma-separated list of channels."""
|
2015-06-07 22:40:18 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
|
|
|
nick = args[0]
|
|
|
|
clist = args[1].split(',')
|
|
|
|
if not clist:
|
|
|
|
raise IndexError
|
|
|
|
except IndexError:
|
|
|
|
utils.msg(irc, source, "Error: not enough arguments. Needs 2: nick, comma separated list of channels.")
|
|
|
|
return
|
2015-06-08 04:36:21 +02:00
|
|
|
u = utils.nickToUid(irc, nick)
|
2015-06-08 04:31:56 +02:00
|
|
|
for channel in clist:
|
2015-07-09 07:50:19 +02:00
|
|
|
if not utils.isChannel(channel):
|
|
|
|
utils.msg(irc, source, "Error: Invalid channel name %r." % channel)
|
2015-06-08 04:31:56 +02:00
|
|
|
return
|
2015-06-17 05:05:41 +02:00
|
|
|
irc.proto.joinClient(irc, u, channel)
|
2015-08-15 14:12:49 +02:00
|
|
|
irc.callHooks([u, 'PYLINK_ADMIN_JOIN', {'channel': channel, 'users': [u],
|
|
|
|
'modes': irc.channels[channel].modes,
|
|
|
|
'parse_as': 'JOIN'}])
|
2015-07-09 07:50:19 +02:00
|
|
|
utils.add_cmd(joinclient, name='join')
|
2015-06-08 04:31:56 +02:00
|
|
|
|
|
|
|
@utils.add_cmd
|
2015-07-09 07:50:19 +02:00
|
|
|
def nick(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<target> <newnick>
|
|
|
|
|
|
|
|
Admin-only. Changes the nick of <target>, a PyLink client, to <newnick>."""
|
2015-06-08 04:31:56 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
|
|
|
nick = args[0]
|
|
|
|
newnick = args[1]
|
|
|
|
except IndexError:
|
|
|
|
utils.msg(irc, source, "Error: not enough arguments. Needs 2: nick, newnick.")
|
|
|
|
return
|
2015-06-08 04:36:21 +02:00
|
|
|
u = utils.nickToUid(irc, nick)
|
2015-07-09 07:50:19 +02:00
|
|
|
if newnick in ('0', u):
|
|
|
|
newnick = u
|
|
|
|
elif not utils.isNick(newnick):
|
|
|
|
utils.msg(irc, source, 'Error: Invalid nickname %r.' % newnick)
|
|
|
|
return
|
2015-06-17 05:05:41 +02:00
|
|
|
irc.proto.nickClient(irc, u, newnick)
|
2015-08-15 14:12:49 +02:00
|
|
|
irc.callHooks([u, 'PYLINK_ADMIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}])
|
2015-06-08 04:31:56 +02:00
|
|
|
|
|
|
|
@utils.add_cmd
|
2015-07-09 07:50:19 +02:00
|
|
|
def part(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<target> <channel1>,[<channel2>],... [<reason>]
|
|
|
|
|
2015-07-22 22:18:11 +02:00
|
|
|
Admin-only. Parts <target>, the nick of a PyLink client, from a comma-separated list of channels."""
|
2015-06-08 04:31:56 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
|
|
|
nick = args[0]
|
|
|
|
clist = args[1].split(',')
|
|
|
|
reason = ' '.join(args[2:])
|
|
|
|
except IndexError:
|
|
|
|
utils.msg(irc, source, "Error: not enough arguments. Needs 2: nick, comma separated list of channels.")
|
2015-06-07 22:40:18 +02:00
|
|
|
return
|
2015-06-08 04:36:21 +02:00
|
|
|
u = utils.nickToUid(irc, nick)
|
2015-06-07 22:40:18 +02:00
|
|
|
for channel in clist:
|
2015-07-09 07:50:19 +02:00
|
|
|
if not utils.isChannel(channel):
|
|
|
|
utils.msg(irc, source, "Error: Invalid channel name %r." % channel)
|
2015-06-07 22:40:18 +02:00
|
|
|
return
|
2015-06-17 05:05:41 +02:00
|
|
|
irc.proto.partClient(irc, u, channel, reason)
|
2015-08-15 14:12:49 +02:00
|
|
|
irc.callHooks([u, 'PYLINK_ADMIN_PART', {'channels': clist, 'text': reason, 'parse_as': 'PART'}])
|
2015-06-08 04:31:56 +02:00
|
|
|
|
|
|
|
@utils.add_cmd
|
2015-07-09 07:50:19 +02:00
|
|
|
def kick(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<source> <channel> <user> [<reason>]
|
|
|
|
|
2015-07-22 22:18:11 +02:00
|
|
|
Admin-only. Kicks <user> from <channel> via <source>, where <source> is the nick of a PyLink client."""
|
2015-06-08 04:31:56 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
|
|
|
nick = args[0]
|
|
|
|
channel = args[1]
|
|
|
|
target = args[2]
|
|
|
|
reason = ' '.join(args[3:])
|
|
|
|
except IndexError:
|
2015-07-09 07:50:19 +02:00
|
|
|
utils.msg(irc, source, "Error: not enough arguments. Needs 3-4: source nick, channel, target, reason (optional).")
|
2015-06-08 04:31:56 +02:00
|
|
|
return
|
2015-06-08 04:36:21 +02:00
|
|
|
u = utils.nickToUid(irc, nick)
|
|
|
|
targetu = utils.nickToUid(irc, target)
|
2015-07-09 07:50:19 +02:00
|
|
|
if not utils.isChannel(channel):
|
|
|
|
utils.msg(irc, source, "Error: Invalid channel name %r." % channel)
|
2015-06-08 04:31:56 +02:00
|
|
|
return
|
2015-06-17 05:05:41 +02:00
|
|
|
irc.proto.kickClient(irc, u, channel, targetu, reason)
|
2015-08-15 14:12:49 +02:00
|
|
|
irc.callHooks([u, 'PYLINK_ADMIN_KICK', {'channel': channel, 'target': targetu, 'text': reason, 'parse_as': 'KICK'}])
|
2015-06-17 05:05:41 +02:00
|
|
|
|
2015-06-19 22:00:23 +02:00
|
|
|
@utils.add_cmd
|
|
|
|
def showuser(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<user>
|
|
|
|
|
|
|
|
Admin-only. Shows information about <user>."""
|
2015-06-19 22:00:23 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
|
|
|
target = args[0]
|
|
|
|
except IndexError:
|
|
|
|
utils.msg(irc, source, "Error: not enough arguments. Needs 1: nick.")
|
|
|
|
return
|
|
|
|
u = utils.nickToUid(irc, target)
|
|
|
|
if u is None:
|
|
|
|
utils.msg(irc, source, 'Error: unknown user %r' % target)
|
|
|
|
return
|
2015-07-10 02:05:23 +02:00
|
|
|
s = ['\x02%s\x02: %s' % (k, v) for k, v in sorted(irc.users[u].__dict__.items())]
|
2015-07-09 08:01:09 +02:00
|
|
|
s = 'Information on user \x02%s\x02: %s' % (target, '; '.join(s))
|
|
|
|
utils.msg(irc, source, s)
|
|
|
|
|
|
|
|
@utils.add_cmd
|
|
|
|
def showchan(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<channel>
|
|
|
|
|
|
|
|
Admin-only. Shows information about <channel>."""
|
2015-07-09 08:01:09 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
|
|
|
channel = args[0].lower()
|
|
|
|
except IndexError:
|
|
|
|
utils.msg(irc, source, "Error: not enough arguments. Needs 1: channel.")
|
|
|
|
return
|
2015-07-10 02:05:23 +02:00
|
|
|
if channel not in irc.channels:
|
|
|
|
utils.msg(irc, source, 'Error: unknown channel %r' % channel)
|
|
|
|
return
|
|
|
|
s = ['\x02%s\x02: %s' % (k, v) for k, v in sorted(irc.channels[channel].__dict__.items())]
|
2015-07-09 08:01:09 +02:00
|
|
|
s = 'Information on channel \x02%s\x02: %s' % (channel, '; '.join(s))
|
2015-06-19 22:00:23 +02:00
|
|
|
utils.msg(irc, source, s)
|
|
|
|
|
2015-07-09 08:01:09 +02:00
|
|
|
@utils.add_cmd
|
|
|
|
def mode(irc, source, args):
|
2015-07-18 07:35:34 +02:00
|
|
|
"""<source> <target> <modes>
|
|
|
|
|
2015-08-15 14:12:20 +02:00
|
|
|
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."""
|
2015-07-09 08:01:09 +02:00
|
|
|
checkauthenticated(irc, source)
|
|
|
|
try:
|
|
|
|
modesource, target, modes = args[0], args[1], args[2:]
|
|
|
|
except IndexError:
|
|
|
|
utils.msg(irc, source, 'Error: not enough arguments. Needs 3: source nick, target, modes to set.')
|
|
|
|
return
|
|
|
|
if not modes:
|
|
|
|
utils.msg(irc, source, "Error: no modes given to set!")
|
|
|
|
return
|
|
|
|
parsedmodes = utils.parseModes(irc, target, modes)
|
|
|
|
targetuid = utils.nickToUid(irc, target)
|
|
|
|
if targetuid:
|
|
|
|
target = targetuid
|
|
|
|
elif not utils.isChannel(target):
|
|
|
|
utils.msg(irc, source, "Error: Invalid channel or nick %r." % target)
|
|
|
|
return
|
|
|
|
if utils.isInternalServer(irc, modesource):
|
|
|
|
irc.proto.modeServer(irc, modesource, target, parsedmodes)
|
2015-08-15 14:12:49 +02:00
|
|
|
irc.callHooks([modesource, 'PYLINK_ADMIN_MODE', {'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}])
|
2015-07-09 08:01:09 +02:00
|
|
|
else:
|
|
|
|
sourceuid = utils.nickToUid(irc, modesource)
|
|
|
|
irc.proto.modeClient(irc, sourceuid, target, parsedmodes)
|
2015-08-15 14:12:49 +02:00
|
|
|
irc.callHooks([sourceuid, 'PYLINK_ADMIN_MODE', {'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}])
|
2015-08-15 13:51:32 +02:00
|
|
|
|
|
|
|
@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)
|
2015-08-15 14:12:49 +02:00
|
|
|
irc.callHooks([sourceuid, 'PYLINK_ADMIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])
|