diff --git a/SupyMisc/plugin.py b/SupyMisc/plugin.py index 3cba03c..ebc8063 100644 --- a/SupyMisc/plugin.py +++ b/SupyMisc/plugin.py @@ -35,6 +35,11 @@ try: from itertools import izip except ImportError: izip = zip +try: + from bs4 import BeautifulSoup +except ImportError: + BeautifulSoup = None +from codecs import unicode_escape_decode as u import supybot.conf as conf import supybot.utils as utils @@ -69,10 +74,10 @@ class SupyMisc(callbacks.Plugin): L.append(word) irc.reply(' '.join(L)) scramble = wrap(scramble, ['text']) - + def repeat(self, irc, msg, args, num, text): """ - Returns repeated times. must be a positive integer. + Returns repeated times. must be a positive integer. To keep leading and trailing spaces, it is recommended to quote the argument " like this ". """ maxN = self.registryValue("maxLen") @@ -82,10 +87,10 @@ class SupyMisc(callbacks.Plugin): irc.error("The value given is too large. Current " "maximum: {}".format(maxN), Raise=True) repeat = wrap(repeat, ['positiveInt', 'text']) - + def uniform(self, irc, msg, args, a, b): """ - Return a random floating point number N such that a <= N <= b for a <= b and b <= N + Return a random floating point number N such that a <= N <= b for a <= b and b <= N <= a for b < a. A frontend to Python's random.uniform() command.""" irc.reply(random.uniform(a,b)) uniform = wrap(uniform, ['float', 'float']) @@ -198,7 +203,7 @@ class SupyMisc(callbacks.Plugin): Counts the amount of networks the bot is on. """ irc.reply(len(world.ircs)) netcount = wrap(netcount) - + def supyplugins(self, irc, msg, args, text): """[] Returns a URL for the source of this repository. If @@ -243,7 +248,7 @@ class SupyMisc(callbacks.Plugin): else: irc.reply(None) getchan = wrap(getchan) - + def me(self, irc, msg, args): """takes no arguments. Returns the nick of the person who called the command. @@ -278,6 +283,59 @@ class SupyMisc(callbacks.Plugin): irc.reply(ircutils.hostFromHostmask(irc.state.nickToHostmask(nick))) gethost = wrap(gethost, [(additional('nick'))]) + @wrap(['positiveInt']) + def port(self, irc, msg, args, port): + """ + + Looks up in Wikipedia's list of ports at + https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers. + """ + if port > 65535: + irc.error('Port numbers cannot be greater than 65535.', Raise=True) + if BeautifulSoup is None: + irc.error("Beautiful Soup 4 is required for this plugin: get it" + " at http://www.crummy.com/software/BeautifulSoup/bs4/" + "doc/#installing-beautiful-soup", Raise=True) + url = "https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers" + fd = utils.web.getUrlFd(url) + soup = BeautifulSoup(fd) + if port >= 49152: + results = ['The range 49152–65535 (2^15+2^14 to 2^16−1)—above the ' + 'registered ports—contains dynamic or private ports that ' + 'cannot be registered with IANA. This range is used for ' + 'custom or temporary purposes and for automatic ' + 'allocation of ephemeral ports.'] + else: + results = [] + for tr in soup.find_all('tr'): + tds = tr.find_all('td') + if not tds: + continue + portnum = tds[0].text + if '–' in portnum: + startport, endport = map(int, portnum.split('–')) + p = range(startport, endport+1) + else: + try: + p = [int(portnum)] + except ValueError: + continue + if port in p: + text = tds[3].text + # Remove inline citations (text[1][2][3]), citation needed tags, etc. + text = re.sub('\[.*?]', '', text) + tcp = tds[1].text + udp = tds[2].text + official = tds[4].text + if tcp and udp: + porttype = '/'.join((tcp, udp)) + else: + porttype = tcp or udp + results.append('%s [%s; %s]' % (ircutils.bold(text), official, porttype)) + if results: + irc.reply(format('%s: %L', ircutils.bold(ircutils.underline(port)), results)) + else: + irc.error(_('No results found.')) Class = SupyMisc # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/SupyMisc/test.py b/SupyMisc/test.py index 088f15f..53f2468 100644 --- a/SupyMisc/test.py +++ b/SupyMisc/test.py @@ -79,4 +79,14 @@ class SupyMiscTestCase(PluginTestCase): def testServerlist(self): self.assertNotError('serverlist') + def testPort(self): + self.assertRegexp('port 22', 'SSH.*?Official.*?TCP') + # Some entries in the list are defined as port ranges (i.e. + # 6665-6669); we have to split them manually. + self.assertRegexp('port 6667', 'Internet Relay Chat') + self.assertError('port 77777') # Port numbers can't be >65535 + # Wikipedia notes that ports 49152-65535 cannot be registered + # with IANA, so we'll leave a note here too. + self.assertRegexp('port 65300', 'cannot be registered') + # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: