"""<message text> Sends out a Instance-wide notice. """ permissions.checkPermissions(irc, source, ["global.global"]) message = " ".join(args) global_conf = conf.conf.get('global') or {} template = string.Template(global_conf.get('format', DEFAULT_FORMAT)) for name, ircd in world.networkobjects.items(): if ircd.connected.is_set( ): # Only attempt to send to connected networks for channel in ircd.pseudoclient.channels: subst = { 'sender': irc.getFriendlyName(source), 'network': irc.name, 'fullnetwork': irc.getFullNetworkName(), 'current_channel': channel, 'current_network': ircd.name, 'current_fullnetwork': ircd.getFullNetworkName(), 'text': message } # Disable relaying or other plugins handling the global message. ircd.msg(channel, template.safe_substitute(subst), loopback=False) utils.add_cmd(g, "global", featured=True)
# foxy.py: some fun stuff for the foxy from pylinkirc import utils from pylinkirc.log import log import random def pet(irc, source, args): if irc.pseudoclient.nick == "py-foxy": # if <= 25, purrs # if 26-49, whines # if 50-74, scratches # if 75-100, kills result = random.randrange(1, 100) mynick = irc.pseudoclient.nick myuid = irc.nickToUid(mynick) if result in range(0, 25): irc.reply('\x01ACTION purrs\x01') elif result in range(26, 50): irc.reply('\x01ACTION whines\x01') elif result in range(51, 75): irc.reply('\x01ACTION scratches %s\x01' % (irc.getFriendlyName(source))) elif result in range(76, 100): irc.proto.kill(myuid, source, '* kills %s' % (irc.getFriendlyName(source))) utils.add_cmd(pet, "pet")
Admin-only. Executes <code> in the current PyLink instance. This command performs backslash escaping of characters, so things like \\n and \\ will work. \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02""" irc.checkAuthenticated(source, allowOper=False) # Allow using \n in the code, while escaping backslashes correctly otherwise. args = bytes(' '.join(args), 'utf-8').decode("unicode_escape") if not args.strip(): irc.reply('No code entered!') return log.info('(%s) Executing %r for %s', irc.name, args, irc.getHostmask(source)) exec(args, globals(), locals()) utils.add_cmd(_exec, 'exec') def _eval(irc, source, args): """<Python expression> Admin-only. Evaluates the given Python expression and returns the result. \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02""" irc.checkAuthenticated(source, allowOper=False) args = ' '.join(args) if not args.strip(): irc.reply('No code entered!') return log.info('(%s) Evaluating %r for %s', irc.name, args,
# join() doesn't support prefixes. if prefixes: irc.sjoin(irc.sid, real_channel, [(joinmodes, u)]) else: irc.join(u, real_channel) try: modes = irc.channels[real_channel].modes except KeyError: modes = [] # Signal the join to other plugins irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_JOIN', {'channel': real_channel, 'users': [u], 'modes': modes, 'parse_as': 'JOIN'}]) irc.reply("Done.") utils.add_cmd(joinclient, name='join') @utils.add_cmd def nick(irc, source, args): """[<target>] <newnick> Changes the nick of <target>, a PyLink client, to <newnick>. If <target> is not given, it defaults to the main PyLink client.""" permissions.check_permissions(irc, source, ['bots.nick']) try: nick = args[0] newnick = args[1] except IndexError: try: nick = irc.pseudoclient.nick
if irc.is_channel(irc.called_in): irc.reply('Error: This command must be sent in private. ' '(Would you really type a password inside a channel?)') return try: username, password = args[0], args[1] except IndexError: irc.reply('Error: Not enough arguments.') return # Process new-style accounts. if check_login(username, password): _irc_try_login(irc, source, username) return # Process legacy logins (login:user). if username.lower() == conf.conf['login'].get( 'user', '').lower() and password == conf.conf['login'].get('password'): realuser = conf.conf['login']['user'] _irc_try_login(irc, source, realuser, skip_checks=True) return # Username not found or password incorrect. log.warning("(%s) Failed login to %r from %s", irc.name, username, irc.get_hostmask(source)) raise utils.NotAuthorizedError('Bad username or password.') utils.add_cmd(identify, aliases=('login', 'id'))
else: irc.error("hash algorithm '%s' unavailable, see 'algorithms'" % digest, private=True) else: d = hashlib.new("sha256") d.update(b'%s' % password.encode('utf-8')) irc.reply(d.hexdigest(), private=True) except IndexError: irc.error( "Not enough arguments. Needs 1-2: password, digest (optional).", private=True) def algorithms(irc, source, args): """algorithms [query] When given '.' gives all available algorithms, or will filter the available when given a starting few characters.""" try: digest = args[0] pattern = re.compile(r"%s.*" % digest, re.I) avail_digests = filter(pattern.search, list(hashlib.algorithms_available)) avail_digests = " \xB7 ".join(avail_digests) irc.reply(avail_digests, private=True) except IndexError: irc.error("Not enough arguments. Needs 1: query.", private=True) utils.add_cmd(algorithms, "algorithms") utils.add_cmd(_hash, "hash")
config = bls._dict() if config.get("blacklists", {}): blacklist = args.blacklist if config.get('blacklists').get(blacklist, {}): record = args.record reply = " ".join(args.reply) config['blacklists'][blacklist]['records'][record] = reply bls.dump(config) irc.reply("Record added to %s. %d: %s" % (blacklist, record, reply)) else: irc.error("Blacklist %s is not defined" % blacklist) else: irc.error("Could not retrieve blacklists") utils.add_cmd(addrec, "radd", featured=True) dnsbl_remrec = utils.IRCParser() dnsbl_remrec.add_argument('blacklist') dnsbl_remrec.add_argument('record', type=int) def remrec(irc, source, args): """<blacklist> <record> Removes record from blacklist. """ permissions.checkPermissions(irc, source, ["dnsbl.rrem"]) args = dnsbl_remrec.parse_args(args) bls = Blacklists() config = bls._dict() try: del config['blacklists'][args.blacklist]['records'][args.record] bls.dump(config)
@utils.add_cmd # irc: The IRC object where the command was called. # source: The UID/numeric of the calling user. # args: A list of command args (excluding the command name) that the command was called with. def randint(irc, source, args): # The docstring here is used as command help by the 'help' command, and formatted using the # same line breaks as the raw string. You shouldn't make this text or any one line too long, # to prevent flooding users or getting long lines cut off. # The same applies to message replies in general: plugins sending long strings of text should # be wary that long messages can get cut off. Automatic word-wrap may be added in the future: # https://github.com/GLolol/PyLink/issues/153 """[<min> <max>] Returns a random number between <min> and <max>. <min> and <max> default to 1 and 10 respectively, if both aren't given.""" try: rmin = args[0] rmax = args[1] except IndexError: rmin, rmax = 1, 10 n = random.randint(rmin, rmax) # irc.reply() is intelligent and will automatically reply to the caller in # right context. If fantasy is loaded and you call the command via it, # it will send replies into the channel instead of in your PM. irc.reply(str(n)) # You can also bind a command function multiple times, and/or to different command names via a # second argument. utils.add_cmd(randint, "random")
remoteirc = world.networkobjects[relay_server] if remoteirc.proto.hasCap('can-track-servers'): # Only ever show relay subservers once - this prevents infinite loops. showall(remoteirc, remoteirc.sid, hops=hops, is_relay_server=True) else: # Afterwards, decrement the hopcount. hops -= 1 # Start the map at our PyLink server firstserver = hostsid showall(ircobj, firstserver) serverlist = irc.servers reply('Total %s users on %s local servers - average of %1.f per server' % (usercount, len(serverlist), usercount / len(serverlist))) utils.add_cmd(_map, 'map') @utils.add_cmd def localmap(irc, source, args): """[<network>] Shows the network map for the given network, or the current network if not specified. This command does not expand Relay subservers.""" _map(irc, source, args, show_relay=False)
options = parser.parse_args(args) else: irc.reply("Syntax: rr {ADD|SHOW|DEL} <args...>") irc.reply("See 'help rr' for more info.") if options.command == "cf_add": response = cf_add(options) if response: result = response["result"] irc.reply("Record Added. %(name)s as %(content)s" % result, private=False) irc.reply("Record ID: %(id)s" % result, private=False) elif options.command == "cf_show": response = cf_show(options) count = response.get("result_info", {}).get("count", 0) result = response.get("result", []) irc.reply("Found %d records" % count, private=False) for res in result: irc.reply("\x02Name\x02: %(name)s \x02Content\x02: %(content)s" % res, private=False) irc.reply("\x02ID\x02: %(id)s" % res, private=False) elif options.command == "cf_rem": response = cf_rem(options) result = response["result"] irc.reply("Record Removed. ID: %(id)s" % result, private=False) utils.add_cmd(rr, "rr", featured=True)
# ctcp.py: Handles basic CTCP requests. import random import datetime from pylinkirc import utils from pylinkirc.log import log def handle_ctcpversion(irc, source, args): """ Handles CTCP version requests. """ irc.msg(source, '\x01VERSION %s\x01' % irc.version(), notice=True) utils.add_cmd(handle_ctcpversion, '\x01version') utils.add_cmd(handle_ctcpversion, '\x01version\x01') def handle_ctcpping(irc, source, args): """ Handles CTCP ping requests. """ # CTCP PING 23152511 pingarg = ' '.join(args).strip('\x01') irc.msg(source, '\x01PING %s\x01' % pingarg, notice=True) utils.add_cmd(handle_ctcpping, '\x01ping')
def randint(irc, source, args): # The 'help' command uses command functions' docstrings as help text, and formats them # in the following manner: # - Any newlines immediately adjacent to text on both sides are replaced with a space. This # means that the first descriptive paragraph ("Returns a random...given.") shows up as one # line, even though it is physically written on two. # - Double line breaks are treated as breaks between two paragraphs, and will be shown # as distinct lines in IRC. # As of PyLink 2.0, long paragraphs are automatically word-wrapped by irc.reply(). """[<min> <max>] Returns a random number between <min> and <max>. <min> and <max> default to 1 and 10 respectively, if both aren't given. Example second paragraph here.""" try: rmin = args[0] rmax = args[1] except IndexError: rmin, rmax = 1, 10 n = random.randint(rmin, rmax) # irc.reply() is intelligent and will automatically reply to the caller in # right context. If fantasy is loaded and you call the command via it, # it will send replies into the channel instead of in your PM. irc.reply(str(n)) # You can bind a command function to multiple names using the 'aliases' option. utils.add_cmd(randint, "random", aliases=("randint", "getrandint"))
try: template = args[1] except IndexError: irc.error("no template given, using default") if lengtharg == 0 and template == "": irc.error("using default length and a default template") result = "" if isinstance(lengtharg, int): if lengtharg == "" and template == "": template = "[\c\\u\d]" template = template + "{%s}" % length result = sg("%s" % template).render() if lengtharg == 0 and template == "": template = "[\c\\u\d\p]" template = template + "{%s}" % length result = sg("%s" % template).render() elif lengtharg > 0 and template == "": template = "[\c\\u\d\p]" result = sg("%s{%s}" % (template, lengtharg)).render() elif lengtharg == 0 and template != "": result = sg("%s" % template).render() else: irc.error("part of your input was invalid") password = result irc.reply(password, private=True) utils.add_cmd(passgen, "passgen")
try: template = args[1] except IndexError: irc.error("no template given, using default") if lengtharg == 0 and template == "": irc.error("using default length and a default template") result = "" if isinstance(lengtharg, int): if lengtharg == "" and template == "": template = "[\c\\u\d]" template = template + "{%s}" % length result = sg("%s" % template).render() if lengtharg == 0 and template == "": template = "[\c\\u\d\p]" template = template + "{%s}" % length result = sg("%s" % template).render() elif lengtharg > 0 and template == "": template = "[\c\\u\d\p]" result = sg("%s{%s}" % (template, lengtharg)).render() elif lengtharg == 0 and template != "": result = sg("%s" % template).render() else: irc.error("part of your input was invalid") password = result irc.reply(password, private=True) utils.add_cmd(passgen, "passgen")
irc.reply("Error: Invalid channel name %r." % channel) return irc.proto.join(u, channel) # Call a join hook manually so other plugins like relay can understand it. irc.callHooks([ u, 'PYLINK_BOTSPLUGIN_JOIN', { 'channel': channel, 'users': [u], 'modes': irc.channels[channel].modes, 'parse_as': 'JOIN' } ]) utils.add_cmd(joinclient, name='join') @utils.add_cmd def nick(irc, source, args): """[<target>] <newnick> Admin-only. Changes the nick of <target>, a PyLink client, to <newnick>. If <target> is not given, it defaults to the main PyLink client.""" irc.checkAuthenticated(source, allowOper=False) try: nick = args[0] newnick = args[1] except IndexError: try: nick = irc.pseudoclient.nick
if digest: if digest in digests: d = hashlib.new("%s" % digest) d.update(password) irc.reply(d.hexdigest(), private=True) else: irc.error("hash algorithm '%s' unavailable, see 'algorithms'" % digest, private=True) else: d = hashlib.new("sha256") d.update(password) irc.reply(d.hexdigest(), private=True) except IndexError: irc.error("Not enough arguments. Needs 1-2: password, digest (optional).", private=True) def algorithms(irc, source, args): """algorithms [query] When given '.' gives all available algorithms, or will filter the available when given a starting few characters.""" try: digest = args[0] pattern = re.compile(r"%s.*" % digest, re.I) avail_digests = filter(pattern.search, list(hashlib.algorithms_available)) avail_digests = " \xB7 ".join(avail_digests) irc.reply(avail_digests, private=True) except IndexError: irc.error("Not enough arguments. Needs 1: query.", private=True) utils.add_cmd(algorithms, "algorithms", featured=True) utils.add_cmd(_hash, "hash", featured=True)
(min([results, args.maxresults]), results), private=True) else: irc.reply("No results found.", private=True) else: # Target can be both a nick (of an online user) or a hostmask. irc.match_host() handles this # automatically. if irc.match_host(args.banmask, args.target): irc.reply('Yes, \x02%s\x02 matches \x02%s\x02.' % (args.target, args.banmask)) else: irc.reply('No, \x02%s\x02 does not match \x02%s\x02.' % (args.target, args.banmask)) utils.add_cmd(checkban, aliases=('cban', 'trace')) def checkbanre(irc, source, args): """<regular expression> [<target nick or hostmask>] [--channel #channel] [--maxresults <num>] CHECKBANRE provides a ban checker command based on regular expressions matched against users' "nick!user@host [gecos]" mask. If a target nick or hostmask is given, this command returns whether the given banmask will match it. Otherwise, it will display a list of connected users matching the banmask. If the --channel argument is given without a target mask, the returned results will only include users in the given channel. The --maxresults option configures how many responses will be shown."""
return args = cf_add_parser.parse_args(args) body = { "type": args.type, "name": args.name, "content": args.content, "ttl": args.ttl } response = cf.zones.dns_records.post(zone, data=body) result = response.get("result", []) irc.reply("Record Added. %(name)s as %(content)s" % result, private=False) irc.reply("Record ID: %(id)s" % result, private=False) utils.add_cmd(cf_add, "cf-add", featured=True) cf_show_parser = utils.IRCParser() cf_show_parser.add_argument("-t", "--type", choices=["A", "AAAA", "CNAME"]) cf_show_parser.add_argument("subdomain", nargs='?') cf_show_parser.add_argument("-c", "--content") cf_show_parser.add_argument("-o", "--order", default="type") def cf_show(irc, source, args): """[<subdomain>] [--type <type>] [--content <record content>] [--order <sort order>] Searches CloudFlare DNS records. The following options are supported: -t / --type : Type of record -c / --content : Content of record / IP