mirror of
https://github.com/jlu5/PyLink.git
synced 2024-12-30 22:52:34 +01:00
8135f3a735
This is an API breaking change. I really don't know why I made these keys plural to start, since it only made things more complicated. Prefix modes are not pluralized in the list of supported modes of each IRCd.
242 lines
8.9 KiB
Python
242 lines
8.9 KiB
Python
# commands.py: base PyLink commands
|
|
import sys
|
|
import os
|
|
from time import ctime
|
|
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
import utils
|
|
import conf
|
|
from log import log
|
|
import world
|
|
import classes
|
|
|
|
@utils.add_cmd
|
|
def status(irc, source, args):
|
|
"""takes no arguments.
|
|
|
|
Returns your current PyLink login status."""
|
|
identified = irc.users[source].identified
|
|
if identified:
|
|
irc.reply('You are identified as \x02%s\x02.' % identified)
|
|
else:
|
|
irc.reply('You are not identified as anyone.')
|
|
irc.reply('Operator access: \x02%s\x02' % bool(utils.isOper(irc, source)))
|
|
|
|
def listcommands(irc, source, args):
|
|
"""takes no arguments.
|
|
|
|
Returns a list of available commands PyLink has to offer."""
|
|
cmds = list(world.commands.keys())
|
|
cmds.sort()
|
|
for idx, cmd in enumerate(cmds):
|
|
nfuncs = len(world.commands[cmd])
|
|
if nfuncs > 1:
|
|
cmds[idx] = '%s(x%s)' % (cmd, nfuncs)
|
|
irc.reply('Available commands include: %s' % ', '.join(cmds))
|
|
irc.reply('To see help on a specific command, type \x02help <command>\x02.')
|
|
utils.add_cmd(listcommands, 'list')
|
|
|
|
@utils.add_cmd
|
|
def help(irc, source, args):
|
|
"""<command>
|
|
|
|
Gives help for <command>, if it is available."""
|
|
try:
|
|
command = args[0].lower()
|
|
except IndexError: # No argument given, just return 'list' output
|
|
listcommands(irc, source, args)
|
|
return
|
|
if command not in world.commands:
|
|
irc.msg(source, 'Error: Unknown command %r.' % command)
|
|
return
|
|
else:
|
|
funcs = world.commands[command]
|
|
if len(funcs) > 1:
|
|
irc.reply('The following \x02%s\x02 plugins bind to the \x02%s\x02 command: %s'
|
|
% (len(funcs), command, ', '.join([func.__module__ for func in funcs])))
|
|
for func in funcs:
|
|
doc = func.__doc__
|
|
mod = func.__module__
|
|
if doc:
|
|
lines = doc.split('\n')
|
|
# Bold the first line, which usually just tells you what
|
|
# arguments the command takes.
|
|
lines[0] = '\x02%s %s\x02 (plugin: %r)' % (command, lines[0], mod)
|
|
for line in lines:
|
|
irc.reply(line.strip())
|
|
else:
|
|
irc.msg(source, "Error: Command %r (from plugin %r) "
|
|
"doesn't offer any help." % (command, mod))
|
|
return
|
|
|
|
_none = '\x1D(none)\x1D'
|
|
@utils.add_cmd
|
|
def showuser(irc, source, args):
|
|
"""<user>
|
|
|
|
Shows information about <user>."""
|
|
try:
|
|
target = args[0]
|
|
except IndexError:
|
|
irc.reply("Error: Not enough arguments. Needs 1: nick.")
|
|
return
|
|
u = irc.nickToUid(target) or target
|
|
# Only show private info if the person is calling 'showuser' on themselves,
|
|
# or is an oper.
|
|
verbose = utils.isOper(irc, source) or u == source
|
|
if u not in irc.users:
|
|
irc.reply('Error: Unknown user %r.' % target)
|
|
return
|
|
|
|
f = lambda s: irc.msg(source, s)
|
|
userobj = irc.users[u]
|
|
f('Information on user \x02%s\x02 (%s@%s): %s' % (userobj.nick, userobj.ident,
|
|
userobj.host, userobj.realname))
|
|
|
|
sid = irc.getServer(u)
|
|
serverobj = irc.servers[sid]
|
|
ts = userobj.ts
|
|
|
|
# Show connected server & signon time
|
|
f('\x02Home server\x02: %s (%s); \x02Signon time:\x02 %s (%s)' % \
|
|
(serverobj.name, sid, ctime(float(ts)), ts))
|
|
|
|
if verbose: # Oper only data: user modes, channels on, account info, etc.
|
|
|
|
f('\x02User modes\x02: %s' % utils.joinModes(userobj.modes))
|
|
f('\x02Protocol UID\x02: %s; \x02Real host\x02: %s; \x02IP\x02: %s' % \
|
|
(u, userobj.realhost, userobj.ip))
|
|
channels = sorted(userobj.channels)
|
|
f('\x02Channels\x02: %s' % (' '.join(channels) or _none))
|
|
f('\x02PyLink identification\x02: %s; \x02Services account\x02: %s; \x02Away status\x02: %s' % \
|
|
((userobj.identified or _none), (userobj.services_account or _none), userobj.away or _none))
|
|
|
|
|
|
@utils.add_cmd
|
|
def showchan(irc, source, args):
|
|
"""<channel>
|
|
|
|
Shows information about <channel>."""
|
|
try:
|
|
channel = utils.toLower(irc, args[0])
|
|
except IndexError:
|
|
irc.reply("Error: Not enough arguments. Needs 1: channel.")
|
|
return
|
|
if channel not in irc.channels:
|
|
irc.reply('Error: Unknown channel %r.' % channel)
|
|
return
|
|
|
|
f = lambda s: irc.msg(source, s)
|
|
c = irc.channels[channel]
|
|
# Only show verbose info if caller is oper or is in the target channel.
|
|
verbose = source in c.users or utils.isOper(irc, source)
|
|
secret = ('s', None) in c.modes
|
|
if secret and not verbose:
|
|
# Hide secret channels from normal users.
|
|
irc.msg(source, 'Error: Unknown channel %r.' % channel)
|
|
return
|
|
|
|
nicks = [irc.users[u].nick for u in c.users]
|
|
pmodes = ('owner', 'admin', 'op', 'halfop', 'voice')
|
|
|
|
f('Information on channel \x02%s\x02:' % channel)
|
|
f('\x02Channel topic\x02: %s' % c.topic)
|
|
f('\x02Channel creation time\x02: %s (%s)' % (ctime(c.ts), c.ts))
|
|
# Show only modes that aren't list-style modes.
|
|
modes = utils.joinModes([m for m in c.modes if m[0] not in irc.cmodes['*A']])
|
|
f('\x02Channel modes\x02: %s' % modes)
|
|
if verbose:
|
|
nicklist = []
|
|
# Iterate over the user list, sorted by nick.
|
|
for user, nick in sorted(zip(c.users, nicks),
|
|
key=lambda userpair: userpair[1].lower()):
|
|
prefixmodes = [irc.prefixmodes.get(irc.cmodes.get(pmode, ''), '')
|
|
for pmode in pmodes if user in c.prefixmodes[pmode]]
|
|
nicklist.append(''.join(prefixmodes) + nick)
|
|
|
|
while nicklist[:20]: # 20 nicks per line to prevent message cutoff.
|
|
f('\x02User list\x02: %s' % ' '.join(nicklist[:20]))
|
|
nicklist = nicklist[20:]
|
|
|
|
@utils.add_cmd
|
|
def version(irc, source, args):
|
|
"""takes no arguments.
|
|
|
|
Returns the version of the currently running PyLink instance."""
|
|
irc.reply("PyLink version \x02%s\x02, released under the Mozilla Public License version 2.0." % world.version)
|
|
irc.reply("The source of this program is available at \x02%s\x02." % world.source)
|
|
|
|
@utils.add_cmd
|
|
def echo(irc, source, args):
|
|
"""<text>
|
|
|
|
Echoes the text given."""
|
|
irc.reply(' '.join(args))
|
|
|
|
@utils.add_cmd
|
|
def rehash(irc, source, args):
|
|
"""takes no arguments.
|
|
|
|
Reloads the configuration file for PyLink, (dis)connecting added/removed networks.
|
|
Plugins must be manually reloaded."""
|
|
utils.checkAuthenticated(irc, source, allowOper=False)
|
|
old_conf = conf.conf.copy()
|
|
fname = conf.fname
|
|
try:
|
|
new_conf = conf.loadConf(fname, errors_fatal=False)
|
|
except Exception as e: # Something went wrong, abort.
|
|
log.exception("Error REHASH'ing config: ")
|
|
irc.reply("Error loading configuration file: %s: %s" % (type(e).__name__, e))
|
|
return
|
|
else:
|
|
new_conf = conf.validateConf(new_conf)
|
|
conf.conf = new_conf
|
|
for network, ircobj in world.networkobjects.copy().items():
|
|
# Server was removed from the config file, disconnect them.
|
|
log.debug('(%s) rehash: checking if %r is in new conf still.', irc.name, network)
|
|
if network not in new_conf['servers']:
|
|
log.debug('(%s) rehash: removing connection to %r (removed from config).', irc.name, network)
|
|
# Disable autoconnect first.
|
|
ircobj.serverdata['autoconnect'] = -1
|
|
ircobj.disconnect()
|
|
del world.networkobjects[network]
|
|
else:
|
|
ircobj.conf = new_conf
|
|
ircobj.serverdata = new_conf['servers'][network]
|
|
ircobj.botdata = new_conf['bot']
|
|
|
|
# Clear the IRC object's channel loggers and replace them with
|
|
# new ones by re-running logSetup().
|
|
while ircobj.loghandlers:
|
|
log.removeHandler(ircobj.loghandlers.pop())
|
|
|
|
ircobj.logSetup()
|
|
|
|
for network, sdata in new_conf['servers'].items():
|
|
# New server was added. Connect them if not already connected.
|
|
if network not in world.networkobjects:
|
|
proto = utils.getProtocolModule(sdata['protocol'])
|
|
world.networkobjects[network] = classes.Irc(network, proto, new_conf)
|
|
irc.reply("Done.")
|
|
|
|
loglevels = {'DEBUG': 10, 'INFO': 20, 'WARNING': 30, 'ERROR': 40, 'CRITICAL': 50}
|
|
@utils.add_cmd
|
|
def loglevel(irc, source, args):
|
|
"""<level>
|
|
|
|
Sets the log level to the given <level>. <level> must be either DEBUG, INFO, WARNING, ERROR, or CRITICAL.
|
|
If no log level is given, shows the current one."""
|
|
utils.checkAuthenticated(irc, source, allowOper=False)
|
|
try:
|
|
level = args[0].upper()
|
|
try:
|
|
loglevel = loglevels[level]
|
|
except KeyError:
|
|
irc.reply('Error: Unknown log level "%s".' % level)
|
|
return
|
|
else:
|
|
log.setLevel(loglevel)
|
|
irc.reply("Done.")
|
|
except IndexError:
|
|
irc.reply(log.getEffectiveLevel())
|