Merge remote-tracking branch 'upstream/master'
commit
545b6690ed
|
@ -2,9 +2,10 @@ language: python
|
||||||
sudo: false
|
sudo: false
|
||||||
cache: pip
|
cache: pip
|
||||||
python:
|
python:
|
||||||
- 3.4
|
|
||||||
- 3.5
|
- 3.5
|
||||||
- 3.6
|
- 3.6
|
||||||
|
- 3.7
|
||||||
install:
|
install:
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
script: nosetests
|
script:
|
||||||
|
- LANG=en_US.UTF-8 nosetests
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
FROM python:3
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY requirements.txt /usr/src/app/
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . /usr/src/app/
|
||||||
|
|
||||||
|
RUN groupadd -r -g 200 bot \
|
||||||
|
&& useradd -mr -g bot -u 200 bot
|
||||||
|
USER bot
|
||||||
|
|
||||||
|
VOLUME ["/home/bot/.phenny"]
|
||||||
|
|
||||||
|
CMD ["/usr/src/app/phenny"]
|
64
irc.py
64
irc.py
|
@ -9,11 +9,16 @@ http://inamidst.com/phenny/
|
||||||
|
|
||||||
import asynchat
|
import asynchat
|
||||||
import asyncore
|
import asyncore
|
||||||
|
import functools
|
||||||
|
import proto
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import ssl
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
|
import threading
|
||||||
|
from tools import decorate
|
||||||
|
|
||||||
|
|
||||||
class Origin(object):
|
class Origin(object):
|
||||||
|
@ -49,9 +54,12 @@ class Bot(asynchat.async_chat):
|
||||||
self.channels = channels or []
|
self.channels = channels or []
|
||||||
self.stack = []
|
self.stack = []
|
||||||
|
|
||||||
import threading
|
|
||||||
self.sending = threading.RLock()
|
self.sending = threading.RLock()
|
||||||
|
|
||||||
|
proto_func = lambda attr: functools.partial(proto.commands[attr], self)
|
||||||
|
proto_map = {attr: proto_func(attr) for attr in proto.commands}
|
||||||
|
self.proto = decorate(object(), proto_map)
|
||||||
|
|
||||||
def initiate_send(self):
|
def initiate_send(self):
|
||||||
self.sending.acquire()
|
self.sending.acquire()
|
||||||
asynchat.async_chat.initiate_send(self)
|
asynchat.async_chat.initiate_send(self)
|
||||||
|
@ -61,24 +69,22 @@ class Bot(asynchat.async_chat):
|
||||||
# asynchat.async_chat.push(self, *args, **kargs)
|
# asynchat.async_chat.push(self, *args, **kargs)
|
||||||
|
|
||||||
def __write(self, args, text=None):
|
def __write(self, args, text=None):
|
||||||
# print 'PUSH: %r %r %r' % (self, args, text)
|
line = b' '.join(args)
|
||||||
try:
|
|
||||||
if text is not None:
|
if text is not None:
|
||||||
# 510 because CR and LF count too, as nyuszika7h points out
|
line += b' :' + text
|
||||||
self.push((b' '.join(args) + b' :' + text)[:510] + b'\r\n')
|
|
||||||
else:
|
# 510 because CR and LF count too
|
||||||
self.push(b' '.join(args)[:512] + b'\r\n')
|
self.push(line[:510] + b'\r\n')
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def write(self, args, text=None):
|
def write(self, args, text=None):
|
||||||
"""This is a safe version of __write"""
|
"""This is a safe version of __write"""
|
||||||
def safe(input):
|
def safe(input):
|
||||||
if type(input) == str:
|
if type(input) == str:
|
||||||
input = input.replace('\n', '')
|
input = re.sub(' ?(\r|\n)+', ' ', input)
|
||||||
input = input.replace('\r', '')
|
|
||||||
return input.encode('utf-8')
|
return input.encode('utf-8')
|
||||||
else:
|
else:
|
||||||
|
input = re.sub(b' ?(\r|\n)+', b' ', input)
|
||||||
return input
|
return input
|
||||||
try:
|
try:
|
||||||
args = [safe(arg) for arg in args]
|
args = [safe(arg) for arg in args]
|
||||||
|
@ -127,10 +133,12 @@ class Bot(asynchat.async_chat):
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print('connected!', file=sys.stderr)
|
print('connected!', file=sys.stderr)
|
||||||
|
|
||||||
if self.password:
|
if self.password:
|
||||||
self.write(('PASS', self.password))
|
self.proto.pass_(self.password)
|
||||||
self.write(('NICK', self.nick))
|
|
||||||
self.write(('USER', self.user, '+iw', self.nick), self.name)
|
self.proto.nick(self.nick)
|
||||||
|
self.proto.user(self.user, '+iw', self.name)
|
||||||
|
|
||||||
def handle_close(self):
|
def handle_close(self):
|
||||||
self.close()
|
self.close()
|
||||||
|
@ -165,7 +173,7 @@ class Bot(asynchat.async_chat):
|
||||||
self.dispatch(origin, tuple([text] + args))
|
self.dispatch(origin, tuple([text] + args))
|
||||||
|
|
||||||
if args[0] == 'PING':
|
if args[0] == 'PING':
|
||||||
self.write(('PONG', text))
|
self.proto.pong(text)
|
||||||
|
|
||||||
def dispatch(self, origin, args):
|
def dispatch(self, origin, args):
|
||||||
pass
|
pass
|
||||||
|
@ -185,19 +193,6 @@ class Bot(asynchat.async_chat):
|
||||||
except UnicodeEncodeError as e:
|
except UnicodeEncodeError as e:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Split long messages
|
|
||||||
maxlength = 430
|
|
||||||
if len(text) > maxlength:
|
|
||||||
first_message = text[0:maxlength].decode('utf-8','ignore')
|
|
||||||
line_break = len(first_message)
|
|
||||||
for i in range(len(first_message)-1,-1,-1):
|
|
||||||
if first_message[i] == " ":
|
|
||||||
line_break = i
|
|
||||||
break
|
|
||||||
self.msg(recipient, text.decode('utf-8','ignore')[0:line_break])
|
|
||||||
self.msg(recipient, text.decode('utf-8','ignore')[line_break+1:])
|
|
||||||
return
|
|
||||||
|
|
||||||
# No messages within the last 3 seconds? Go ahead!
|
# No messages within the last 3 seconds? Go ahead!
|
||||||
# Otherwise, wait so it's been at least 0.8 seconds + penalty
|
# Otherwise, wait so it's been at least 0.8 seconds + penalty
|
||||||
if self.stack:
|
if self.stack:
|
||||||
|
@ -216,12 +211,7 @@ class Bot(asynchat.async_chat):
|
||||||
self.sending.release()
|
self.sending.release()
|
||||||
return
|
return
|
||||||
|
|
||||||
def safe(input):
|
self.proto.privmsg(recipient, text)
|
||||||
if type(input) == str:
|
|
||||||
input = input.encode('utf-8')
|
|
||||||
input = input.replace(b'\n', b'')
|
|
||||||
return input.replace(b'\r', b'')
|
|
||||||
self.__write((b'PRIVMSG', safe(recipient)), safe(text))
|
|
||||||
self.stack.append((time.time(), text))
|
self.stack.append((time.time(), text))
|
||||||
self.stack = self.stack[-10:]
|
self.stack = self.stack[-10:]
|
||||||
|
|
||||||
|
@ -231,12 +221,8 @@ class Bot(asynchat.async_chat):
|
||||||
text = "\x01ACTION {0}\x01".format(text)
|
text = "\x01ACTION {0}\x01".format(text)
|
||||||
return self.msg(recipient, text)
|
return self.msg(recipient, text)
|
||||||
|
|
||||||
def notice(self, dest, text):
|
|
||||||
self.write(('NOTICE', dest), text)
|
|
||||||
|
|
||||||
def error(self, origin):
|
def error(self, origin):
|
||||||
try:
|
try:
|
||||||
import traceback
|
|
||||||
trace = traceback.format_exc()
|
trace = traceback.format_exc()
|
||||||
print(trace)
|
print(trace)
|
||||||
lines = list(reversed(trace.splitlines()))
|
lines = list(reversed(trace.splitlines()))
|
||||||
|
|
|
@ -13,9 +13,7 @@ def join(phenny, input):
|
||||||
if input.sender.startswith('#'): return
|
if input.sender.startswith('#'): return
|
||||||
if input.admin:
|
if input.admin:
|
||||||
channel, key = input.group(1), input.group(2)
|
channel, key = input.group(1), input.group(2)
|
||||||
if not key:
|
phenny.proto.join(channel, key)
|
||||||
phenny.write(['JOIN'], channel)
|
|
||||||
else: phenny.write(['JOIN', channel, key])
|
|
||||||
join.rule = r'\.join (#\S+)(?: *(\S+))?'
|
join.rule = r'\.join (#\S+)(?: *(\S+))?'
|
||||||
join.priority = 'low'
|
join.priority = 'low'
|
||||||
join.example = '.join #example or .join #example key'
|
join.example = '.join #example or .join #example key'
|
||||||
|
@ -24,7 +22,7 @@ def autojoin(phenny, input):
|
||||||
"""Join the specified channel when invited by an admin."""
|
"""Join the specified channel when invited by an admin."""
|
||||||
if input.admin:
|
if input.admin:
|
||||||
channel = input.group(1)
|
channel = input.group(1)
|
||||||
phenny.write(['JOIN'], channel)
|
phenny.proto.join(channel)
|
||||||
autojoin.event = 'INVITE'
|
autojoin.event = 'INVITE'
|
||||||
autojoin.rule = r'(.*)'
|
autojoin.rule = r'(.*)'
|
||||||
|
|
||||||
|
@ -33,7 +31,7 @@ def part(phenny, input):
|
||||||
# Can only be done in privmsg by an admin
|
# Can only be done in privmsg by an admin
|
||||||
if input.sender.startswith('#'): return
|
if input.sender.startswith('#'): return
|
||||||
if input.admin:
|
if input.admin:
|
||||||
phenny.write(['PART'], input.group(2))
|
phenny.proto.part(input.group(2))
|
||||||
part.rule = (['part'], r'(#\S+)')
|
part.rule = (['part'], r'(#\S+)')
|
||||||
part.priority = 'low'
|
part.priority = 'low'
|
||||||
part.example = '.part #example'
|
part.example = '.part #example'
|
||||||
|
@ -43,7 +41,7 @@ def quit(phenny, input):
|
||||||
# Can only be done in privmsg by the owner
|
# Can only be done in privmsg by the owner
|
||||||
if input.sender.startswith('#'): return
|
if input.sender.startswith('#'): return
|
||||||
if input.owner:
|
if input.owner:
|
||||||
phenny.write(['QUIT'])
|
phenny.proto.quit()
|
||||||
__import__('os')._exit(0)
|
__import__('os')._exit(0)
|
||||||
quit.commands = ['quit']
|
quit.commands = ['quit']
|
||||||
quit.priority = 'low'
|
quit.priority = 'low'
|
||||||
|
|
|
@ -10,36 +10,33 @@ modified from Wikipedia module
|
||||||
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
|
||||||
import web
|
|
||||||
import wiki
|
import wiki
|
||||||
|
|
||||||
wikiapi = 'https://wiki.archlinux.org/api.php?action=query&list=search&srsearch={0}&limit=1&prop=snippet&format=json'
|
endpoints = {
|
||||||
wikiuri = 'https://wiki.archlinux.org/index.php/{0}'
|
'api': 'https://wiki.archlinux.org/api.php?action=query&list=search&srsearch={0}&limit=1&format=json',
|
||||||
wikisearch = 'https://wiki.archlinux.org/index.php/Special:Search?' \
|
'url': 'https://wiki.archlinux.org/index.php/{0}',
|
||||||
+ 'search={0}&fulltext=Search'
|
'search': 'https://wiki.archlinux.org/index.php/Special:Search?search={0}&fulltext=Search',
|
||||||
|
}
|
||||||
|
|
||||||
def awik(phenny, input):
|
def awik(phenny, input):
|
||||||
origterm = input.groups()[1]
|
""".awik <term> - Look up something on the ArchWiki."""
|
||||||
if not origterm:
|
|
||||||
|
origterm = input.group(1)
|
||||||
|
if not origterm:
|
||||||
return phenny.say('Perhaps you meant ".awik dwm"?')
|
return phenny.say('Perhaps you meant ".awik dwm"?')
|
||||||
|
|
||||||
term = web.unquote(origterm)
|
term, section = wiki.parse_term(origterm)
|
||||||
term = term[0].upper() + term[1:]
|
|
||||||
term = term.replace(' ', '_')
|
|
||||||
|
|
||||||
w = wiki.Wiki(wikiapi, wikiuri, wikisearch)
|
w = wiki.Wiki(endpoints)
|
||||||
|
match = w.search(term)
|
||||||
|
|
||||||
try:
|
if not match:
|
||||||
result = w.search(term)
|
phenny.say('Can\'t find anything in the ArchWiki for "{0}".'.format(term))
|
||||||
except web.ConnectionError:
|
return
|
||||||
error = "Can't connect to wiki.archlinux.org ({0})".format(wikiuri.format(term))
|
|
||||||
return phenny.say(error)
|
|
||||||
|
|
||||||
if result is not None:
|
snippet, url = wiki.extract_snippet(match, section)
|
||||||
phenny.say(result)
|
|
||||||
else:
|
phenny.say('"{0}" - {1}'.format(snippet, url))
|
||||||
phenny.say('Can\'t find anything in the ArchWiki for "{0}".'.format(origterm))
|
|
||||||
|
|
||||||
awik.commands = ['awik']
|
awik.commands = ['awik']
|
||||||
awik.priority = 'high'
|
awik.priority = 'high'
|
||||||
|
|
|
@ -27,13 +27,11 @@ def setup(phenny):
|
||||||
timer = threading.Timer(refresh_delay, close, ())
|
timer = threading.Timer(refresh_delay, close, ())
|
||||||
phenny.data['startup.setup.timer'] = timer
|
phenny.data['startup.setup.timer'] = timer
|
||||||
phenny.data['startup.setup.timer'].start()
|
phenny.data['startup.setup.timer'].start()
|
||||||
# print "PING!"
|
phenny.proto.ping(phenny.config.host)
|
||||||
phenny.write(('PING', phenny.config.host))
|
|
||||||
phenny.data['startup.setup.pingloop'] = pingloop
|
phenny.data['startup.setup.pingloop'] = pingloop
|
||||||
|
|
||||||
def pong(phenny, input):
|
def pong(phenny, input):
|
||||||
try:
|
try:
|
||||||
# print "PONG!"
|
|
||||||
phenny.data['startup.setup.timer'].cancel()
|
phenny.data['startup.setup.timer'].cancel()
|
||||||
time.sleep(refresh_delay + 60.0)
|
time.sleep(refresh_delay + 60.0)
|
||||||
pingloop()
|
pingloop()
|
||||||
|
@ -50,16 +48,16 @@ def startup(phenny, input):
|
||||||
if phenny.data.get('startup.setup.pingloop'):
|
if phenny.data.get('startup.setup.pingloop'):
|
||||||
phenny.data['startup.setup.pingloop']()
|
phenny.data['startup.setup.pingloop']()
|
||||||
|
|
||||||
if hasattr(phenny.config, 'serverpass'):
|
if hasattr(phenny.config, 'serverpass'):
|
||||||
phenny.write(('PASS', phenny.config.serverpass))
|
phenny.proto.pass_(phenny.config.serverpass)
|
||||||
|
|
||||||
if hasattr(phenny.config, 'password'):
|
if hasattr(phenny.config, 'password'):
|
||||||
phenny.msg('NickServ', 'IDENTIFY %s' % phenny.config.password)
|
phenny.msg('NickServ', 'IDENTIFY %s' % phenny.config.password)
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
# Cf. http://swhack.com/logs/2005-12-05#T19-32-36
|
# Cf. http://swhack.com/logs/2005-12-05#T19-32-36
|
||||||
for channel in phenny.channels:
|
for channel in phenny.channels:
|
||||||
phenny.write(('JOIN', channel))
|
phenny.proto.join(channel)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
startup.rule = r'(.*)'
|
startup.rule = r'(.*)'
|
||||||
startup.event = '251'
|
startup.event = '251'
|
||||||
|
|
|
@ -2,38 +2,76 @@
|
||||||
test_archwiki.py - tests for the arch wiki module
|
test_archwiki.py - tests for the arch wiki module
|
||||||
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
|
||||||
import unittest
|
import unittest
|
||||||
from mock import MagicMock, Mock
|
from mock import MagicMock
|
||||||
from modules import archwiki
|
from modules import archwiki
|
||||||
|
import wiki
|
||||||
|
|
||||||
|
|
||||||
class TestArchwiki(unittest.TestCase):
|
class TestArchwiki(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.phenny = MagicMock()
|
self.phenny = MagicMock()
|
||||||
|
self.input = MagicMock()
|
||||||
|
|
||||||
|
self.term = None
|
||||||
|
self.section = None
|
||||||
|
|
||||||
|
def prepare(self):
|
||||||
|
if self.section:
|
||||||
|
self.text = self.term + '#' + self.section
|
||||||
|
url_text = wiki.format_term(self.term) +\
|
||||||
|
'#' + wiki.format_section(self.section)
|
||||||
|
else:
|
||||||
|
self.text = self.term
|
||||||
|
url_text = wiki.format_term(self.term)
|
||||||
|
|
||||||
|
self.input.group = lambda x: [None, self.text][x]
|
||||||
|
self.url = 'https://wiki.archlinux.org/index.php/{0}'.format(url_text)
|
||||||
|
|
||||||
|
def check_snippet(self, output):
|
||||||
|
self.assertIn(self.url, output)
|
||||||
|
|
||||||
|
for keyword in self.keywords:
|
||||||
|
self.assertIn(keyword, output)
|
||||||
|
|
||||||
def test_awik(self):
|
def test_awik(self):
|
||||||
input = Mock(groups=lambda: ['', "KVM"])
|
self.term = "OpenDMARC"
|
||||||
archwiki.awik(self.phenny, input)
|
self.prepare()
|
||||||
|
|
||||||
|
archwiki.awik(self.phenny, self.input)
|
||||||
out = self.phenny.say.call_args[0][0]
|
out = self.phenny.say.call_args[0][0]
|
||||||
m = re.match('^.* - https:\/\/wiki\.archlinux\.org\/index\.php\/KVM$',
|
|
||||||
out, flags=re.UNICODE)
|
self.keywords = ['DMARC', 'implementation', 'specification']
|
||||||
self.assertTrue(m)
|
self.check_snippet(out)
|
||||||
|
|
||||||
|
def test_awik_fragment(self):
|
||||||
|
self.term = "KVM"
|
||||||
|
self.section = "Kernel support"
|
||||||
|
self.prepare()
|
||||||
|
|
||||||
|
archwiki.awik(self.phenny, self.input)
|
||||||
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
self.keywords = ['kernel', 'modules', 'KVM', 'VIRTIO']
|
||||||
|
self.check_snippet(out)
|
||||||
|
|
||||||
def test_awik_invalid(self):
|
def test_awik_invalid(self):
|
||||||
term = "KVM#Enabling_KSM"
|
self.term = "KVM"
|
||||||
input = Mock(groups=lambda: ['', term])
|
self.section = "Enabling KSM"
|
||||||
archwiki.awik(self.phenny, input)
|
self.prepare()
|
||||||
|
|
||||||
self.phenny.say.assert_called_once_with( "Can't find anything in "\
|
archwiki.awik(self.phenny, self.input)
|
||||||
"the ArchWiki for \"{0}\".".format(term))
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
message = "No '{0}' section found.".format(self.section)
|
||||||
|
self.assertEqual(out, '"{0}" - {1}'.format(message, self.url))
|
||||||
|
|
||||||
def test_awik_none(self):
|
def test_awik_none(self):
|
||||||
term = "Ajgoajh"
|
self.term = "Ajgoajh"
|
||||||
input = Mock(groups=lambda: ['', term])
|
self.prepare()
|
||||||
archwiki.awik(self.phenny, input)
|
|
||||||
|
|
||||||
self.phenny.say.assert_called_once_with( "Can't find anything in "\
|
archwiki.awik(self.phenny, self.input)
|
||||||
"the ArchWiki for \"{0}\".".format(term))
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
expected = "Can't find anything in the ArchWiki for \"{0}\"."
|
||||||
|
self.assertEqual(out, expected.format(self.text))
|
||||||
|
|
|
@ -20,7 +20,7 @@ class TestTfw(unittest.TestCase):
|
||||||
tfw.tfw(self.phenny, input)
|
tfw.tfw(self.phenny, input)
|
||||||
|
|
||||||
self.phenny.say.assert_called_once_with(
|
self.phenny.say.assert_called_once_with(
|
||||||
"WHERE THE FUCK IS THAT? Try another location.")
|
"WHERE THE FUCK IS THAT? I guess you might think it's a place, but no one else does. Try again.")
|
||||||
|
|
||||||
def test_celsius(self):
|
def test_celsius(self):
|
||||||
input = Mock(group=lambda x: '24060')
|
input = Mock(group=lambda x: '24060')
|
||||||
|
|
|
@ -2,38 +2,77 @@
|
||||||
test_vtluugwiki.py - tests for the VTLUUG wiki module
|
test_vtluugwiki.py - tests for the VTLUUG wiki module
|
||||||
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
|
||||||
import unittest
|
import unittest
|
||||||
from mock import MagicMock, Mock
|
from mock import MagicMock
|
||||||
from modules import vtluugwiki
|
from modules import vtluugwiki
|
||||||
|
import wiki
|
||||||
|
|
||||||
|
|
||||||
class TestVtluugwiki(unittest.TestCase):
|
class TestVtluugwiki(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.phenny = MagicMock()
|
self.phenny = MagicMock()
|
||||||
|
self.input = MagicMock()
|
||||||
|
|
||||||
|
self.term = None
|
||||||
|
self.section = None
|
||||||
|
|
||||||
|
def prepare(self):
|
||||||
|
if self.section:
|
||||||
|
self.text = self.term + '#' + self.section
|
||||||
|
url_text = wiki.format_term(self.term) +\
|
||||||
|
'#' + wiki.format_section(self.section)
|
||||||
|
else:
|
||||||
|
self.text = self.term
|
||||||
|
url_text = wiki.format_term(self.term)
|
||||||
|
|
||||||
|
self.input.groups.return_value = [None, self.text]
|
||||||
|
self.url = 'https://vtluug.org/wiki/{0}'.format(url_text)
|
||||||
|
|
||||||
|
def check_snippet(self, output):
|
||||||
|
self.assertIn(self.url, output)
|
||||||
|
|
||||||
|
for keyword in self.keywords:
|
||||||
|
self.assertIn(keyword, output)
|
||||||
|
|
||||||
def test_vtluug(self):
|
def test_vtluug(self):
|
||||||
input = Mock(groups=lambda: ['', "VT-Wireless"])
|
self.term = "VT-Wireless"
|
||||||
vtluugwiki.vtluug(self.phenny, input)
|
self.prepare()
|
||||||
|
|
||||||
|
vtluugwiki.vtluug(self.phenny, self.input)
|
||||||
out = self.phenny.say.call_args[0][0]
|
out = self.phenny.say.call_args[0][0]
|
||||||
m = re.match('^.* - https:\/\/vtluug\.org\/wiki\/VT-Wireless$',
|
|
||||||
out, flags=re.UNICODE)
|
self.keywords = ['campus', 'wireless', 'networks']
|
||||||
self.assertTrue(m)
|
self.check_snippet(out)
|
||||||
|
|
||||||
|
def test_vtluug_fragment(self):
|
||||||
|
self.term = "EAP-TLS"
|
||||||
|
self.section = "netctl"
|
||||||
|
self.prepare()
|
||||||
|
|
||||||
|
vtluugwiki.vtluug(self.phenny, self.input)
|
||||||
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
self.keywords = ['Arch', 'Linux', 'netctl']
|
||||||
|
self.check_snippet(out)
|
||||||
|
|
||||||
def test_vtluug_invalid(self):
|
def test_vtluug_invalid(self):
|
||||||
term = "EAP-TLS#netcfg"
|
self.term = "EAP-TLS"
|
||||||
input = Mock(groups=lambda: ['', term])
|
self.section = "netcfg"
|
||||||
vtluugwiki.vtluug(self.phenny, input)
|
self.prepare()
|
||||||
|
|
||||||
self.phenny.say.assert_called_once_with( "Can't find anything in "\
|
vtluugwiki.vtluug(self.phenny, self.input)
|
||||||
"the VTLUUG Wiki for \"{0}\".".format(term))
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
message = "No '{0}' section found.".format(self.section)
|
||||||
|
self.assertEqual(out, '"{0}" - {1}'.format(message, self.url))
|
||||||
|
|
||||||
def test_vtluug_none(self):
|
def test_vtluug_none(self):
|
||||||
term = "Ajgoajh"
|
self.term = "Ajgoajh"
|
||||||
input = Mock(groups=lambda: ['', term])
|
self.prepare()
|
||||||
vtluugwiki.vtluug(self.phenny, input)
|
|
||||||
|
vtluugwiki.vtluug(self.phenny, self.input)
|
||||||
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
expected = "Can't find anything in the VTLUUG Wiki for \"{0}\"."
|
||||||
|
self.assertEqual(out, expected.format(self.text))
|
||||||
|
|
||||||
self.phenny.say.assert_called_once_with( "Can't find anything in "\
|
|
||||||
"the VTLUUG Wiki for \"{0}\".".format(term))
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
||||||
import re
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
from mock import MagicMock, Mock, patch
|
from mock import MagicMock, Mock, patch
|
||||||
from modules.weather import location, local, code, f_weather
|
from modules import weather
|
||||||
|
|
||||||
|
|
||||||
class TestWeather(unittest.TestCase):
|
class TestWeather(unittest.TestCase):
|
||||||
|
@ -14,58 +14,55 @@ class TestWeather(unittest.TestCase):
|
||||||
self.phenny = MagicMock()
|
self.phenny = MagicMock()
|
||||||
|
|
||||||
def test_locations(self):
|
def test_locations(self):
|
||||||
def check_places(*args):
|
def check_location(result, expected):
|
||||||
def validate(actual_name, actual_lat, actual_lon):
|
self.assertAlmostEqual(result[0], expected[0], places=1)
|
||||||
names = [n.strip() for n in actual_name.split(',')]
|
self.assertAlmostEqual(result[1], expected[1], places=1)
|
||||||
for arg in args:
|
|
||||||
self.assertIn(arg, names)
|
|
||||||
return validate
|
|
||||||
|
|
||||||
locations = [
|
locations = [
|
||||||
('92121', check_places("San Diego", "California")),
|
('92121', (32.9, -117.2)),
|
||||||
('94110', check_places("SF", "California")),
|
('94110', (37.8, -122.4)),
|
||||||
('94041', check_places("Mountain View", "California")),
|
('94041', (37.4, -122.1)),
|
||||||
('27959', check_places("Dare County", "North Carolina")),
|
('27959', (36.0, -75.6)),
|
||||||
('48067', check_places("Royal Oak", "Michigan")),
|
('48067', (42.5, -83.1)),
|
||||||
('23606', check_places("Newport News", "Virginia")),
|
('23606', (37.1, -76.5)),
|
||||||
('23113', check_places("Chesterfield County", "Virginia")),
|
('23113', (37.5, -77.6)),
|
||||||
('27517', check_places("Chapel Hill", "North Carolina")),
|
('27517', (42.6, -7.8)),
|
||||||
('15213', check_places("Allegheny County", "Pennsylvania")),
|
('15213', (40.4, -80.0)),
|
||||||
('90210', check_places("Los Angeles County", "California")),
|
('90210', (34.1, -118.3)),
|
||||||
('33109', check_places("Miami-Dade County", "Florida")),
|
('33109', (25.8, -80.1)),
|
||||||
('80201', check_places("Denver", "Colorado")),
|
('80201', (22.6, 120.3)),
|
||||||
|
|
||||||
("Berlin", check_places("Berlin", "Deutschland")),
|
("Berlin", (52.5, 13.4)),
|
||||||
("Paris", check_places("Paris", "France")),
|
("Paris", (48.9, 2.4)),
|
||||||
("Vilnius", check_places("Vilnius", "Lietuva")),
|
("Vilnius", (54.7, 25.3)),
|
||||||
|
|
||||||
('Blacksburg, VA', check_places("Blacksburg", "Virginia")),
|
('Blacksburg, VA', (37.2, -80.4)),
|
||||||
('Granger, IN', check_places("Granger", "Indiana")),
|
('Granger, IN', (41.8, -86.1)),
|
||||||
]
|
]
|
||||||
|
|
||||||
for loc, validator in locations:
|
for query, expected in locations:
|
||||||
names, lat, lon = location(loc)
|
result = weather.location(query)
|
||||||
validator(names, lat, lon)
|
check_location(result, expected)
|
||||||
|
|
||||||
def test_code_94110(self):
|
def test_code_94110(self):
|
||||||
icao = code(self.phenny, '94110')
|
icao = weather.code(self.phenny, '94110')
|
||||||
self.assertEqual(icao, 'KSFO')
|
self.assertEqual(icao, 'KSFO')
|
||||||
|
|
||||||
def test_airport(self):
|
def test_airport(self):
|
||||||
input = Mock(group=lambda x: 'KIAD')
|
input = Mock(group=lambda x: 'KIAD')
|
||||||
f_weather(self.phenny, input)
|
weather.f_weather(self.phenny, input)
|
||||||
|
|
||||||
assert self.phenny.say.called is True
|
assert self.phenny.say.called is True
|
||||||
|
|
||||||
def test_place(self):
|
def test_place(self):
|
||||||
input = Mock(group=lambda x: 'Blacksburg')
|
input = Mock(group=lambda x: 'Blacksburg')
|
||||||
f_weather(self.phenny, input)
|
weather.f_weather(self.phenny, input)
|
||||||
|
|
||||||
assert self.phenny.say.called is True
|
assert self.phenny.say.called is True
|
||||||
|
|
||||||
def test_notfound(self):
|
def test_notfound(self):
|
||||||
input = Mock(group=lambda x: 'Hell')
|
input = Mock(group=lambda x: 'Hell')
|
||||||
f_weather(self.phenny, input)
|
weather.f_weather(self.phenny, input)
|
||||||
|
|
||||||
self.phenny.say.called_once_with('#phenny',
|
self.phenny.say.called_once_with('#phenny',
|
||||||
"No NOAA data available for that location.")
|
"No NOAA data available for that location.")
|
||||||
|
|
|
@ -2,38 +2,76 @@
|
||||||
test_wikipedia.py - tests for the wikipedia module
|
test_wikipedia.py - tests for the wikipedia module
|
||||||
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
|
||||||
import unittest
|
import unittest
|
||||||
from mock import MagicMock, Mock
|
from mock import MagicMock
|
||||||
from modules import wikipedia
|
from modules import wikipedia
|
||||||
|
import wiki
|
||||||
|
|
||||||
|
|
||||||
class TestWikipedia(unittest.TestCase):
|
class TestWikipedia(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.phenny = MagicMock()
|
self.phenny = MagicMock()
|
||||||
|
self.input = MagicMock()
|
||||||
|
|
||||||
|
self.term = None
|
||||||
|
self.section = None
|
||||||
|
|
||||||
|
def prepare(self):
|
||||||
|
if self.section:
|
||||||
|
self.text = self.term + '#' + self.section
|
||||||
|
url_text = wiki.format_term(self.term) +\
|
||||||
|
'#' + wiki.format_section(self.section)
|
||||||
|
else:
|
||||||
|
self.text = self.term
|
||||||
|
url_text = wiki.format_term(self.term)
|
||||||
|
|
||||||
|
self.input.groups.return_value = [None, self.text]
|
||||||
|
self.url = 'https://en.wikipedia.org/wiki/{0}'.format(url_text)
|
||||||
|
|
||||||
|
def check_snippet(self, output):
|
||||||
|
self.assertIn(self.url, output)
|
||||||
|
|
||||||
|
for keyword in self.keywords:
|
||||||
|
self.assertIn(keyword, output)
|
||||||
|
|
||||||
def test_wik(self):
|
def test_wik(self):
|
||||||
input = Mock(groups=lambda: ['', "Human back"])
|
self.term = "Human back"
|
||||||
wikipedia.wik(self.phenny, input)
|
self.prepare()
|
||||||
|
|
||||||
|
wikipedia.wik(self.phenny, self.input)
|
||||||
out = self.phenny.say.call_args[0][0]
|
out = self.phenny.say.call_args[0][0]
|
||||||
m = re.match('^.* - https:\/\/en\.wikipedia\.org\/wiki\/Human_back$',
|
|
||||||
out, flags=re.UNICODE)
|
self.keywords = ['human', 'back', 'body', 'buttocks', 'neck']
|
||||||
self.assertTrue(m)
|
self.check_snippet(out)
|
||||||
|
|
||||||
|
def test_wik_fragment(self):
|
||||||
|
self.term = "New York City"
|
||||||
|
self.section = "Climate"
|
||||||
|
self.prepare()
|
||||||
|
|
||||||
|
wikipedia.wik(self.phenny, self.input)
|
||||||
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
self.keywords = ['New York', 'climate', 'humid', 'subtropical']
|
||||||
|
self.check_snippet(out)
|
||||||
|
|
||||||
def test_wik_invalid(self):
|
def test_wik_invalid(self):
|
||||||
term = "New York City#Climate"
|
self.term = "New York City"
|
||||||
input = Mock(groups=lambda: ['', term])
|
self.section = "Physics"
|
||||||
wikipedia.wik(self.phenny, input)
|
self.prepare()
|
||||||
|
|
||||||
self.phenny.say.assert_called_once_with( "Can't find anything in "\
|
wikipedia.wik(self.phenny, self.input)
|
||||||
"Wikipedia for \"{0}\".".format(term))
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
message = "No '{0}' section found.".format(self.section)
|
||||||
|
self.assertEqual(out, '"{0}" - {1}'.format(message, self.url))
|
||||||
|
|
||||||
def test_wik_none(self):
|
def test_wik_none(self):
|
||||||
term = "Ajgoajh"
|
self.term = "Ajgoajh"
|
||||||
input = Mock(groups=lambda: ['', term])
|
self.prepare()
|
||||||
wikipedia.wik(self.phenny, input)
|
|
||||||
|
|
||||||
self.phenny.say.assert_called_once_with( "Can't find anything in "\
|
wikipedia.wik(self.phenny, self.input)
|
||||||
"Wikipedia for \"{0}\".".format(term))
|
out = self.phenny.say.call_args[0][0]
|
||||||
|
|
||||||
|
expected = "Can't find anything in Wikipedia for \"{0}\"."
|
||||||
|
self.assertEqual(out, expected.format(self.text))
|
||||||
|
|
132
modules/tfw.py
132
modules/tfw.py
|
@ -26,7 +26,7 @@ def tfw(phenny, input, fahrenheit=False, celsius=False, mev=False):
|
||||||
icao_code = weather.code(phenny, where)
|
icao_code = weather.code(phenny, where)
|
||||||
|
|
||||||
if not icao_code:
|
if not icao_code:
|
||||||
phenny.say("WHERE THE FUCK IS THAT? Try another location.")
|
phenny.say("WHERE THE FUCK IS THAT? I guess you might think it's a place, but no one else does. Try again.")
|
||||||
return
|
return
|
||||||
|
|
||||||
uri = 'http://tgftp.nws.noaa.gov/data/observations/metar/stations/%s.TXT'
|
uri = 'http://tgftp.nws.noaa.gov/data/observations/metar/stations/%s.TXT'
|
||||||
|
@ -35,11 +35,11 @@ def tfw(phenny, input, fahrenheit=False, celsius=False, mev=False):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise GrumbleError("THE INTERNET IS FUCKING BROKEN. Please try again later.")
|
raise GrumbleError("THE INTERNET IS FUCKING BROKEN. Please try again later.")
|
||||||
except web.HTTPError:
|
except web.HTTPError:
|
||||||
phenny.say("WHERE THE FUCK IS THAT? Try another location.")
|
phenny.say("WHERE THE FUCK IS THAT? I guess you might think it's a place, but no one else does. Try again.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'Not Found' in bytes:
|
if 'Not Found' in bytes:
|
||||||
phenny.say("WHERE THE FUCK IS THAT? Try another location.")
|
phenny.say("WHERE THE FUCK IS THAT? I guess you might think it's a place, but no one else does. Try again.")
|
||||||
return
|
return
|
||||||
|
|
||||||
w = metar.parse(bytes)
|
w = metar.parse(bytes)
|
||||||
|
@ -64,33 +64,44 @@ def tfw(phenny, input, fahrenheit=False, celsius=False, mev=False):
|
||||||
"Nothing a few shots couldn't fix",
|
"Nothing a few shots couldn't fix",
|
||||||
"Should have gone south",
|
"Should have gone south",
|
||||||
"You think this is cold? Have you been to upstate New York?",
|
"You think this is cold? Have you been to upstate New York?",
|
||||||
"Why do I live here?", "wang icicles.",
|
"Why do I live here?",
|
||||||
"Freezing my balls off out here", "Fuck this place.",
|
"wang icicles.",
|
||||||
"GREAT! If you're a penguin.", "Fresh off the tap.",
|
"Freezing my balls off out here",
|
||||||
|
"Fuck this place.",
|
||||||
|
"GREAT! If you're a penguin.",
|
||||||
|
"Fresh off the tap.",
|
||||||
"Fantastic do-nothing weather.",
|
"Fantastic do-nothing weather.",
|
||||||
"Put on some fucking socks.", "Blue balls x 2",
|
"Put on some fucking socks.",
|
||||||
|
"Blue balls x 2",
|
||||||
"Good news, food won't spoil nearly as fast outside. Bad news, who cares?",
|
"Good news, food won't spoil nearly as fast outside. Bad news, who cares?",
|
||||||
"Really?", "Wear a fucking jacket.",
|
"Really?",
|
||||||
|
"Wear a fucking jacket.",
|
||||||
"I hear Siberia is the same this time of year.",
|
"I hear Siberia is the same this time of year.",
|
||||||
"NOT FUCKING JOGGING WEATHER", "Shrinkage's best friend.",
|
"NOT FUCKING JOGGING WEATHER",
|
||||||
"Warmer than Hoth.", "Good baby making weather.",
|
"Shrinkage's best friend.",
|
||||||
|
"Warmer than Hoth.",
|
||||||
|
"Good baby making weather.",
|
||||||
"Where's a Tauntaun when you need one?",
|
"Where's a Tauntaun when you need one?",
|
||||||
"My nipples could cut glass", "Global Warming? Bullshit.",
|
"My nipples could cut glass",
|
||||||
|
"Global Warming? Bullshit.",
|
||||||
"Call your local travel agency and ask them if they're serious.",
|
"Call your local travel agency and ask them if they're serious.",
|
||||||
"Freezing my balls off IN here",
|
"Freezing my balls off IN here",
|
||||||
"I'm not sure how you can stand it", "I'm sorry.",
|
"I'm not sure how you can stand it",
|
||||||
|
"I'm sorry.",
|
||||||
"Even penguins are wearing jackets.",
|
"Even penguins are wearing jackets.",
|
||||||
"Keep track of your local old people.",
|
"Keep track of your local old people.",
|
||||||
"WHAT THE FUCK DO YOU MEAN IT'S NICER IN ALASKA?",
|
"WHAT THE FUCK DO YOU MEAN IT'S NICER IN ALASKA?",
|
||||||
"Sock warmers are go. Everywhere.",
|
"Sock warmers are go. Everywhere.",
|
||||||
"Why does my car feel like a pair of ice skates?",
|
"Why does my car feel like a pair of ice skates?",
|
||||||
"Actually, a sharp-stick in the eye might not all be that bad right now.",
|
"Actually, a sharp-stick in the eye might not all be that bad right now.",
|
||||||
"THO Season.", "It's a tit-bit nipplie.",
|
"THO Season.",
|
||||||
|
"It's a tit-bit nipplie.",
|
||||||
"Anything wooden will make a good fireplace. Thank us later.",
|
"Anything wooden will make a good fireplace. Thank us later.",
|
||||||
"MOVE THE FUCK ON GOLDILOCKS",
|
"MOVE THE FUCK ON GOLDILOCKS",
|
||||||
"I'm defrosting inside of my freezer.",
|
"I'm defrosting inside of my freezer.",
|
||||||
"It's time for a vacation.",
|
"It's time for a vacation.",
|
||||||
"It's bone chilling cold out. Sorry ladies."]
|
"It's bone chilling cold out. Sorry ladies."
|
||||||
|
]
|
||||||
elif w.temperature < 20:
|
elif w.temperature < 20:
|
||||||
remark = "IT'S FUCKING...ALRIGHT"
|
remark = "IT'S FUCKING...ALRIGHT"
|
||||||
flavors = [
|
flavors = [
|
||||||
|
@ -98,7 +109,8 @@ def tfw(phenny, input, fahrenheit=False, celsius=False, mev=False):
|
||||||
"Better than a sharp stick in the eye.",
|
"Better than a sharp stick in the eye.",
|
||||||
"Everything's nice butter weather!",
|
"Everything's nice butter weather!",
|
||||||
"At least you aren't living in a small town in Alaska",
|
"At least you aren't living in a small town in Alaska",
|
||||||
"It could be worse.", "FUCKING NOTHING TO SEE HERE",
|
"It could be worse.",
|
||||||
|
"FUCKING NOTHING TO SEE HERE",
|
||||||
"Listen, weather. We need to have a talk.",
|
"Listen, weather. We need to have a talk.",
|
||||||
"OH NO. THE WEATHER MACHINE IS BROKEN.",
|
"OH NO. THE WEATHER MACHINE IS BROKEN.",
|
||||||
"An Eskimo would beat your ass to be here",
|
"An Eskimo would beat your ass to be here",
|
||||||
|
@ -120,31 +132,52 @@ def tfw(phenny, input, fahrenheit=False, celsius=False, mev=False):
|
||||||
"Well, at least we're not in prison.",
|
"Well, at least we're not in prison.",
|
||||||
"Slap me around and call me Sally. It'd be an improvement.",
|
"Slap me around and call me Sally. It'd be an improvement.",
|
||||||
"Today is the perfect size, really honey.",
|
"Today is the perfect size, really honey.",
|
||||||
"Maybe Jersey Shore is on tonight."]
|
"It's that kind of day where you want zip off pants, until you realize how much of a jackass you look like in them.",
|
||||||
|
"Maybe Jersey Shore is on tonight.",
|
||||||
|
"Praise \"Bob\"!",
|
||||||
|
"Or kill me.",
|
||||||
|
"This statement is false.",
|
||||||
|
"Lies and slander, sire!"
|
||||||
|
]
|
||||||
elif w.temperature < 27:
|
elif w.temperature < 27:
|
||||||
remark = "IT'S FUCKING NICE"
|
remark = "IT'S FUCKING NICE"
|
||||||
flavors = [
|
flavors = [
|
||||||
"I made today breakfast in bed.", "FUCKING SWEET",
|
"I made today breakfast in bed.",
|
||||||
"Quit your bitching", "Enjoy.", "IT'S ABOUT FUCKING TIME",
|
"FUCKING SWEET",
|
||||||
"READ A FUCKIN' BOOK", "LETS HAVE A FUCKING PICNIC",
|
"Quit your bitching",
|
||||||
"It is safe to take your ball-mittens off.", "More please.",
|
"Enjoy.",
|
||||||
"uh, can we trade?", "WOO, Spring Break!",
|
"IT'S ABOUT FUCKING TIME",
|
||||||
"I can't believe it's not porn!", "I approve of this message!",
|
"READ A FUCKIN' BOOK",
|
||||||
"Operation beach volleyball is go.", "Plucky ducky kinda day.",
|
"LETS HAVE A FUCKING PICNIC",
|
||||||
|
"It is safe to take your ball-mittens off.",
|
||||||
|
"More please.",
|
||||||
|
"uh, can we trade?",
|
||||||
|
"I approve of this message!",
|
||||||
|
"WE WERE BEGINNING TO THINK YOU LOST YOUR MIND",
|
||||||
|
"WOO, Spring Break!",
|
||||||
|
"I can't believe it's not porn!",
|
||||||
|
"I approve of this message!",
|
||||||
|
"Operation beach volleyball is go.",
|
||||||
|
"Plucky ducky kinda day.",
|
||||||
"Today called just to say \"Hi.\"",
|
"Today called just to say \"Hi.\"",
|
||||||
"STOP AND SMELL THE FUCKING ROSES",
|
"STOP AND SMELL THE FUCKING ROSES",
|
||||||
"FUCKING NOTHING WRONG WITH TODAY", "LETS HAVE A FUCKING SOIREE",
|
"FUCKING NOTHING WRONG WITH TODAY",
|
||||||
|
"LETS HAVE A FUCKING SOIREE",
|
||||||
"What would you do for a holyshititsniceout bar?",
|
"What would you do for a holyshititsniceout bar?",
|
||||||
"There are no rules today, blow shit up!",
|
"There are no rules today, blow shit up!",
|
||||||
"Celebrate Today's Day and buy your Today a present so it knows you care.",
|
"Celebrate Today's Day and buy your Today a present so it knows you care.",
|
||||||
"I feel bad about playing on my computer all day.",
|
"I feel bad about playing on my computer all day.",
|
||||||
"Party in the woods.", "It is now safe to leave your home.",
|
"Party in the woods.",
|
||||||
|
"It is now safe to leave your home.",
|
||||||
"PUT A FUCKING CAPE ON TODAY, BECAUSE IT'S SUPER",
|
"PUT A FUCKING CAPE ON TODAY, BECAUSE IT'S SUPER",
|
||||||
"Today is like \"ice\" if it started with an \"n\". Fuck you, we don't mean nce.",
|
"Today is like \"ice\" if it started with an \"n\". Fuck you, we don't mean nce.",
|
||||||
"Water park! Water drive! Just get wet!",
|
"Water park! Water drive! Just get wet!",
|
||||||
"The geese are on their way back! Unless you live where they migrate to for the winter.",
|
"The geese are on their way back! Unless you live where they migrate to for the winter.",
|
||||||
"FUCKING AFFABLE AS SHIT", "Give the sun a raise!",
|
"FUCKING AFFABLE AS SHIT",
|
||||||
"Today is better than an original holographic Charizard. Loser!"]
|
"Give the sun a raise!",
|
||||||
|
"Go outside and go cycling or some shit, you fitness nerd!",
|
||||||
|
"Today is better than an original holographic Charizard. Loser!"
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
remark = "IT'S FUCKING HOT"
|
remark = "IT'S FUCKING HOT"
|
||||||
flavors = [
|
flavors = [
|
||||||
|
@ -161,24 +194,60 @@ def tfw(phenny, input, fahrenheit=False, celsius=False, mev=False):
|
||||||
"Isn't the desert nice this time of year?",
|
"Isn't the desert nice this time of year?",
|
||||||
"Why, oh why did we decide to live in an oven?",
|
"Why, oh why did we decide to live in an oven?",
|
||||||
"It's hotter outside than my fever.",
|
"It's hotter outside than my fever.",
|
||||||
"I recommend staying away from fat people.",
|
|
||||||
"TAKE IT OFF!",
|
"TAKE IT OFF!",
|
||||||
|
"TAKE FUCKING EVERYTHING OFF!",
|
||||||
|
"EVEN THAT NEEDS TO COME OFF!",
|
||||||
"Even your frigid girlfriend can't save you from today.",
|
"Even your frigid girlfriend can't save you from today.",
|
||||||
"I need gloves to touch the steering wheel.",
|
"I need gloves to touch the steering wheel.",
|
||||||
|
"I can hear that power bill running up right now!",
|
||||||
"Lock up yo' ice cream trucks, lock up yo' wife.",
|
"Lock up yo' ice cream trucks, lock up yo' wife.",
|
||||||
"FUCKING SUNBURNED, AND I WAS INSIDE ALL DAY.",
|
"FUCKING SUNBURNED, AND I WAS INSIDE ALL DAY.",
|
||||||
"Fuck this shit, I'm moving back to Alaska."]
|
"Fuck this shit, I'm moving back to Alaska."
|
||||||
|
]
|
||||||
|
|
||||||
if w.descriptor == "thunderstorm":
|
if w.descriptor == "thunderstorm":
|
||||||
remark += " AND THUNDERING"
|
remark += " AND THUNDERING"
|
||||||
|
flavors += [
|
||||||
|
"Are you sure you want to go out in that? I'm not",
|
||||||
|
"Fuck my ears!",
|
||||||
|
"Don't go flying a kite. Unless you're Ben Franklin",
|
||||||
|
"Did you think Eris would smile upon your failings?"
|
||||||
|
]
|
||||||
elif w.precipitation in ("snow", "snow grains"):
|
elif w.precipitation in ("snow", "snow grains"):
|
||||||
remark += " AND SNOWING"
|
remark += " AND SNOWING"
|
||||||
|
flavors += [
|
||||||
|
"What's this white stuff that's sticking to everything?",
|
||||||
|
"At least that stuff doesn't glow in the dark!",
|
||||||
|
"How the fuck am I supposed to get around now?",
|
||||||
|
"And you thought four-wheel-drive would help you!",
|
||||||
|
"Go fight those cadets with snowballs",
|
||||||
|
"Where does the white go when the snow melts?",
|
||||||
|
"Just sNOw"
|
||||||
|
]
|
||||||
elif w.precipitation in ("drizzle", "rain", "unknown precipitation"):
|
elif w.precipitation in ("drizzle", "rain", "unknown precipitation"):
|
||||||
remark += " AND WET"
|
remark += " AND WET"
|
||||||
|
flavors += [
|
||||||
|
"Just like your mom!",
|
||||||
|
"I guess it can't get much worse",
|
||||||
|
"Hope you have a rain coat",
|
||||||
|
"Shower outside?",
|
||||||
|
"If only more buildings had gargoyles..."
|
||||||
|
]
|
||||||
elif w.precipitation in ("ice crystals", "ice pellets"):
|
elif w.precipitation in ("ice crystals", "ice pellets"):
|
||||||
remark += " AND ICY"
|
remark += " AND ICY"
|
||||||
|
flavors += [
|
||||||
|
"Nice, but without the N!",
|
||||||
|
"Where's some NaCl when you need it?",
|
||||||
|
"I hope your skates are nearby.",
|
||||||
|
"Studded tyres? What're those?"
|
||||||
|
]
|
||||||
elif w.precipitation in ("hail", "small hail"):
|
elif w.precipitation in ("hail", "small hail"):
|
||||||
remark += " AND HAILING"
|
remark += " AND HAILING"
|
||||||
|
flavors += [
|
||||||
|
"Windshield damage!",
|
||||||
|
"Car alarms!",
|
||||||
|
"Lie face-down outside: free massage!"
|
||||||
|
]
|
||||||
|
|
||||||
if int(tempf) == 69:
|
if int(tempf) == 69:
|
||||||
remark = "IT'S FUCKING SEXY TIME"
|
remark = "IT'S FUCKING SEXY TIME"
|
||||||
|
@ -187,7 +256,8 @@ def tfw(phenny, input, fahrenheit=False, celsius=False, mev=False):
|
||||||
"What comes after 69? Mouthwash.",
|
"What comes after 69? Mouthwash.",
|
||||||
"If you are given two contradictory orders, obey them both.",
|
"If you are given two contradictory orders, obey them both.",
|
||||||
"a good fuckin' time! ;)",
|
"a good fuckin' time! ;)",
|
||||||
"What's the square root of 69? Eight something."]
|
"What's the square root of 69? Eight something."
|
||||||
|
]
|
||||||
|
|
||||||
flavor = random.choice(flavors)
|
flavor = random.choice(flavors)
|
||||||
|
|
||||||
|
@ -210,7 +280,7 @@ def tfwc(phenny, input):
|
||||||
tfwc.rule = (['tfwc'], r'(.*)')
|
tfwc.rule = (['tfwc'], r'(.*)')
|
||||||
|
|
||||||
def tfwev(phenny, input):
|
def tfwev(phenny, input):
|
||||||
""".tfwc <city/zip> - The fucking weather, in fucking degrees celsius."""
|
""".tfwev <city/zip> - The fucking weather, in fucking electron volts."""
|
||||||
return tfw(phenny, input, mev=True)
|
return tfw(phenny, input, mev=True)
|
||||||
tfwev.rule = (['tfwev'], r'(.*)')
|
tfwev.rule = (['tfwev'], r'(.*)')
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,6 @@ def urbandict(phenny, input):
|
||||||
phenny.say(urbandict.__doc__.strip())
|
phenny.say(urbandict.__doc__.strip())
|
||||||
return
|
return
|
||||||
|
|
||||||
# create opener
|
|
||||||
#opener = urllib.request.build_opener()
|
|
||||||
#opener.addheaders = [
|
|
||||||
# ('User-agent', web.Grab().version),
|
|
||||||
# ('Referer', "http://m.urbandictionary.com"),
|
|
||||||
#]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = web.get(
|
data = web.get(
|
||||||
"http://api.urbandictionary.com/v0/define?term={0}".format(
|
"http://api.urbandictionary.com/v0/define?term={0}".format(
|
||||||
|
@ -33,11 +26,13 @@ def urbandict(phenny, input):
|
||||||
raise GrumbleError(
|
raise GrumbleError(
|
||||||
"Urban Dictionary slemped out on me. Try again in a minute.")
|
"Urban Dictionary slemped out on me. Try again in a minute.")
|
||||||
|
|
||||||
if data['result_type'] == 'no_results':
|
results = data['list']
|
||||||
|
|
||||||
|
if not results:
|
||||||
phenny.say("No results found for {0}".format(word))
|
phenny.say("No results found for {0}".format(word))
|
||||||
return
|
return
|
||||||
|
|
||||||
result = data['list'][0]
|
result = results[0]
|
||||||
url = 'http://www.urbandictionary.com/define.php?term={0}'.format(
|
url = 'http://www.urbandictionary.com/define.php?term={0}'.format(
|
||||||
web.quote(word))
|
web.quote(word))
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,13 @@ modified from Wikipedia module
|
||||||
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
author: mutantmonkey <mutantmonkey@mutantmonkey.in>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
|
||||||
import web
|
|
||||||
import wiki
|
import wiki
|
||||||
|
|
||||||
wikiapi = 'https://vtluug.org/w/api.php?action=query&list=search&srsearch={0}&limit=1&prop=snippet&format=json'
|
endpoints = {
|
||||||
wikiuri = 'https://vtluug.org/wiki/{0}'
|
'api': 'https://vtluug.org/w/api.php?action=query&list=search&srsearch={0}&limit=1&prop=snippet&format=json',
|
||||||
wikisearch = 'https://vtluug.org/wiki/Special:Search?' \
|
'url': 'https://vtluug.org/wiki/{0}',
|
||||||
+ 'search={0}&fulltext=Search'
|
'search': 'https://vtluug.org/wiki/Special:Search?search={0}&fulltext=Search',
|
||||||
|
}
|
||||||
|
|
||||||
def vtluug(phenny, input):
|
def vtluug(phenny, input):
|
||||||
""".vtluug <term> - Look up something on the VTLUUG wiki."""
|
""".vtluug <term> - Look up something on the VTLUUG wiki."""
|
||||||
|
@ -26,22 +25,19 @@ def vtluug(phenny, input):
|
||||||
if not origterm:
|
if not origterm:
|
||||||
return phenny.say('Perhaps you meant ".vtluug VT-Wireless"?')
|
return phenny.say('Perhaps you meant ".vtluug VT-Wireless"?')
|
||||||
|
|
||||||
term = web.unquote(origterm)
|
term, section = wiki.parse_term(origterm)
|
||||||
term = term[0].upper() + term[1:]
|
|
||||||
term = term.replace(' ', '_')
|
|
||||||
|
|
||||||
w = wiki.Wiki(wikiapi, wikiuri, wikisearch)
|
w = wiki.Wiki(endpoints)
|
||||||
|
match = w.search(term)
|
||||||
|
|
||||||
try:
|
if not match:
|
||||||
result = w.search(term)
|
phenny.say('Can\'t find anything in the VTLUUG Wiki for "{0}".'.format(term))
|
||||||
except web.ConnectionError:
|
return
|
||||||
error = "Can't connect to vtluug.org ({0})".format(wikiuri.format(term))
|
|
||||||
return phenny.say(error)
|
snippet, url = wiki.extract_snippet(match, section)
|
||||||
|
|
||||||
|
phenny.say('"{0}" - {1}'.format(snippet, url))
|
||||||
|
|
||||||
if result is not None:
|
|
||||||
phenny.say(result)
|
|
||||||
else:
|
|
||||||
phenny.say('Can\'t find anything in the VTLUUG Wiki for "{0}".'.format(origterm))
|
|
||||||
vtluug.commands = ['vtluug']
|
vtluug.commands = ['vtluug']
|
||||||
vtluug.priority = 'high'
|
vtluug.priority = 'high'
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,13 @@ def location(q):
|
||||||
results = web.get(uri)
|
results = web.get(uri)
|
||||||
data = json.loads(results)
|
data = json.loads(results)
|
||||||
|
|
||||||
if len(data) < 1:
|
if not data:
|
||||||
return '?', None, None
|
return None, None
|
||||||
|
|
||||||
display_name = data[0]['display_name']
|
latitude = float(data[0]['lat'])
|
||||||
lat = float(data[0]['lat'])
|
longitude = float(data[0]['lon'])
|
||||||
lon = float(data[0]['lon'])
|
|
||||||
return display_name, lat, lon
|
return latitude, longitude
|
||||||
|
|
||||||
|
|
||||||
def local(icao, hour, minute):
|
def local(icao, hour, minute):
|
||||||
|
@ -58,7 +58,7 @@ def code(phenny, search):
|
||||||
if search.upper() in [loc[0] for loc in data]:
|
if search.upper() in [loc[0] for loc in data]:
|
||||||
return search.upper()
|
return search.upper()
|
||||||
else:
|
else:
|
||||||
display_name, latitude, longitude = location(search)
|
latitude, longitude = location(search)
|
||||||
if not latitude or not longitude:
|
if not latitude or not longitude:
|
||||||
return False
|
return False
|
||||||
sumOfSquares = (99999999999999999999999999999, 'ICAO')
|
sumOfSquares = (99999999999999999999999999999, 'ICAO')
|
||||||
|
|
|
@ -7,14 +7,13 @@ Licensed under the Eiffel Forum License 2.
|
||||||
http://inamidst.com/phenny/
|
http://inamidst.com/phenny/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
|
||||||
import web
|
|
||||||
import wiki
|
import wiki
|
||||||
|
|
||||||
wikiapi = 'https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch={0}&limit=1&prop=snippet&format=json'
|
endpoints = {
|
||||||
wikiuri = 'https://en.wikipedia.org/wiki/{0}'
|
'api': 'https://en.wikipedia.org/w/api.php?format=json&action=query&list=search&srsearch={0}&prop=snippet&limit=1',
|
||||||
wikisearch = 'https://en.wikipedia.org/wiki/Special:Search?' \
|
'url': 'https://en.wikipedia.org/wiki/{0}',
|
||||||
+ 'search={0}&fulltext=Search'
|
'search': 'https://en.wikipedia.org/wiki/Special:Search?search={0}&fulltext=Search',
|
||||||
|
}
|
||||||
|
|
||||||
def wik(phenny, input):
|
def wik(phenny, input):
|
||||||
""".wik <term> - Look up something on Wikipedia."""
|
""".wik <term> - Look up something on Wikipedia."""
|
||||||
|
@ -23,22 +22,19 @@ def wik(phenny, input):
|
||||||
if not origterm:
|
if not origterm:
|
||||||
return phenny.say('Perhaps you meant ".wik Zen"?')
|
return phenny.say('Perhaps you meant ".wik Zen"?')
|
||||||
|
|
||||||
term = web.unquote(origterm)
|
origterm = origterm.strip()
|
||||||
term = term[0].upper() + term[1:]
|
term, section = wiki.parse_term(origterm)
|
||||||
term = term.replace(' ', '_')
|
|
||||||
|
|
||||||
w = wiki.Wiki(wikiapi, wikiuri, wikisearch)
|
w = wiki.Wiki(endpoints)
|
||||||
|
match = w.search(term)
|
||||||
|
|
||||||
try:
|
if not match:
|
||||||
result = w.search(term)
|
|
||||||
except web.ConnectionError:
|
|
||||||
error = "Can't connect to en.wikipedia.org ({0})".format(wikiuri.format(term))
|
|
||||||
return phenny.say(error)
|
|
||||||
|
|
||||||
if result is not None:
|
|
||||||
phenny.say(result)
|
|
||||||
else:
|
|
||||||
phenny.say('Can\'t find anything in Wikipedia for "{0}".'.format(origterm))
|
phenny.say('Can\'t find anything in Wikipedia for "{0}".'.format(origterm))
|
||||||
|
return
|
||||||
|
|
||||||
|
snippet, url = wiki.extract_snippet(match, section)
|
||||||
|
|
||||||
|
phenny.say('"{0}" - {1}'.format(snippet, url))
|
||||||
|
|
||||||
wik.commands = ['wik']
|
wik.commands = ['wik']
|
||||||
wik.priority = 'high'
|
wik.priority = 'high'
|
||||||
|
|
|
@ -17,7 +17,7 @@ def wuvt(phenny, input):
|
||||||
except:
|
except:
|
||||||
raise GrumbleError("Failed to fetch current track from WUVT")
|
raise GrumbleError("Failed to fetch current track from WUVT")
|
||||||
|
|
||||||
if 'listeners' in trackinfo:
|
if 'listeners' in trackinfo and trackinfo['listeners'] is not None:
|
||||||
phenny.say(
|
phenny.say(
|
||||||
"{dj} is currently playing \"{title}\" by {artist} with "
|
"{dj} is currently playing \"{title}\" by {artist} with "
|
||||||
"{listeners:d} online listeners".format(
|
"{listeners:d} online listeners".format(
|
||||||
|
|
43
phenny
43
phenny
|
@ -12,9 +12,9 @@ Then run ./phenny again
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import imp
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from importlib.machinery import SourceFileLoader
|
||||||
from textwrap import dedent as trim
|
from textwrap import dedent as trim
|
||||||
|
|
||||||
dotdir = os.path.expanduser('~/.phenny')
|
dotdir = os.path.expanduser('~/.phenny')
|
||||||
|
@ -152,35 +152,24 @@ def main(argv=None):
|
||||||
config_modules = []
|
config_modules = []
|
||||||
for config_name in config_names(args.config):
|
for config_name in config_names(args.config):
|
||||||
name = os.path.basename(config_name).split('.')[0] + '_config'
|
name = os.path.basename(config_name).split('.')[0] + '_config'
|
||||||
module = imp.load_source(name, config_name)
|
module = SourceFileLoader(name, config_name).load_module()
|
||||||
module.filename = config_name
|
module.filename = config_name
|
||||||
|
|
||||||
if not hasattr(module, 'prefix'):
|
defaults = {
|
||||||
module.prefix = r'\.'
|
'prefix': r'\.',
|
||||||
|
'name': 'Phenny Palmersbot, http://inamidst.com/phenny/',
|
||||||
|
'port': 6667,
|
||||||
|
'ssl': False,
|
||||||
|
'ca_certs': None,
|
||||||
|
'ssl_cert': None,
|
||||||
|
'ssl_key': None,
|
||||||
|
'ipv6': False,
|
||||||
|
'password': None,
|
||||||
|
}
|
||||||
|
|
||||||
if not hasattr(module, 'name'):
|
for key, value in defaults.items():
|
||||||
module.name = 'Phenny Palmersbot, http://inamidst.com/phenny/'
|
if not hasattr(module, key):
|
||||||
|
setattr(module, key, value)
|
||||||
if not hasattr(module, 'port'):
|
|
||||||
module.port = 6667
|
|
||||||
|
|
||||||
if not hasattr(module, 'ssl'):
|
|
||||||
module.ssl = False
|
|
||||||
|
|
||||||
if not hasattr(module, 'ca_certs'):
|
|
||||||
module.ca_certs = None
|
|
||||||
|
|
||||||
if not hasattr(module, 'ssl_cert'):
|
|
||||||
module.ssl_cert = None
|
|
||||||
|
|
||||||
if not hasattr(module, 'ssl_key'):
|
|
||||||
module.ssl_key = None
|
|
||||||
|
|
||||||
if not hasattr(module, 'ipv6'):
|
|
||||||
module.ipv6 = False
|
|
||||||
|
|
||||||
if not hasattr(module, 'password'):
|
|
||||||
module.password = None
|
|
||||||
|
|
||||||
if module.host == 'irc.example.net':
|
if module.host == 'irc.example.net':
|
||||||
error = ('Error: you must edit the config file first!\n' +
|
error = ('Error: you must edit the config file first!\n' +
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
proto.py - IRC protocol messages
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
def _comma(arg):
|
||||||
|
if type(arg) is list:
|
||||||
|
arg = ','.join(arg)
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
def join(self, channels, keys=None):
|
||||||
|
channels = _comma(channels)
|
||||||
|
|
||||||
|
if keys:
|
||||||
|
keys = _comma(keys)
|
||||||
|
self.write(('JOIN', channels, keys))
|
||||||
|
else:
|
||||||
|
self.write(('JOIN', channels))
|
||||||
|
|
||||||
|
def nick(self, nickname):
|
||||||
|
self.write(('NICK', nickname))
|
||||||
|
|
||||||
|
def notice(self, msgtarget, message):
|
||||||
|
self.write(('NOTICE', msgtarget), message)
|
||||||
|
|
||||||
|
def part(self, channels, message=None):
|
||||||
|
channels = _comma(channels)
|
||||||
|
self.write(('PART', channels), message)
|
||||||
|
|
||||||
|
def pass_(self, password):
|
||||||
|
self.write(('PASS', password))
|
||||||
|
|
||||||
|
def ping(self, server1, server2=None):
|
||||||
|
self.write(('PING', server1), server2)
|
||||||
|
|
||||||
|
def pong(self, server1, server2=None):
|
||||||
|
self.write(('PONG', server1), server2)
|
||||||
|
|
||||||
|
def privmsg(self, msgtarget, message):
|
||||||
|
self.write(('PRIVMSG', msgtarget), message)
|
||||||
|
|
||||||
|
def quit(self, message=None):
|
||||||
|
self.write(('QUIT'), message)
|
||||||
|
|
||||||
|
def user(self, user, mode, realname):
|
||||||
|
self.write(('USER', user, mode, '_'), realname)
|
||||||
|
|
||||||
|
|
||||||
|
module_dict = sys.modules[__name__].__dict__
|
||||||
|
command_filter = lambda k, v: callable(v) and not k.startswith('_')
|
||||||
|
commands = {k: v for k, v in module_dict.items() if command_filter(k, v)}
|
|
@ -42,7 +42,7 @@ class BotTest(unittest.TestCase):
|
||||||
|
|
||||||
mock_write.assert_has_calls([
|
mock_write.assert_has_calls([
|
||||||
call(('NICK', self.nick)),
|
call(('NICK', self.nick)),
|
||||||
call(('USER', self.nick, '+iw', self.nick), self.name)
|
call(('USER', self.nick, '+iw', '_'), self.name)
|
||||||
])
|
])
|
||||||
|
|
||||||
@patch('irc.Bot.write')
|
@patch('irc.Bot.write')
|
||||||
|
@ -50,7 +50,7 @@ class BotTest(unittest.TestCase):
|
||||||
self.bot.buffer = b"PING"
|
self.bot.buffer = b"PING"
|
||||||
self.bot.found_terminator()
|
self.bot.found_terminator()
|
||||||
|
|
||||||
mock_write.assert_called_once_with(('PONG', ''))
|
mock_write.assert_called_once_with(('PONG', ''), None)
|
||||||
|
|
||||||
@patch('irc.Bot.push')
|
@patch('irc.Bot.push')
|
||||||
def test_msg(self, mock_push):
|
def test_msg(self, mock_push):
|
||||||
|
@ -80,6 +80,6 @@ class BotTest(unittest.TestCase):
|
||||||
@patch('irc.Bot.write')
|
@patch('irc.Bot.write')
|
||||||
def test_notice(self, mock_write):
|
def test_notice(self, mock_write):
|
||||||
notice = "This is a notice!"
|
notice = "This is a notice!"
|
||||||
self.bot.notice('jqh', notice)
|
self.bot.proto.notice('jqh', notice)
|
||||||
|
|
||||||
mock_write.assert_called_once_with(('NOTICE', 'jqh'), notice)
|
mock_write.assert_called_once_with(('NOTICE', 'jqh'), notice)
|
||||||
|
|
13
tools.py
13
tools.py
|
@ -8,6 +8,19 @@ http://inamidst.com/phenny/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def decorate(obj, delegate):
|
||||||
|
class Decorator(object):
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
if attr in delegate:
|
||||||
|
return delegate[attr]
|
||||||
|
|
||||||
|
return getattr(obj, attr)
|
||||||
|
|
||||||
|
def __setattr__(self, attr, value):
|
||||||
|
return setattr(obj, attr, value)
|
||||||
|
|
||||||
|
return Decorator()
|
||||||
|
|
||||||
class GrumbleError(Exception):
|
class GrumbleError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
142
wiki.py
142
wiki.py
|
@ -1,5 +1,8 @@
|
||||||
import json
|
import json
|
||||||
|
import lxml.html
|
||||||
import re
|
import re
|
||||||
|
from requests.exceptions import HTTPError
|
||||||
|
from urllib.parse import quote, unquote
|
||||||
import web
|
import web
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,15 +19,104 @@ abbrs = ['etc', 'ca', 'cf', 'Co', 'Ltd', 'Inc', 'Mt', 'Mr', 'Mrs',
|
||||||
'syn', 'transl', 'sess', 'fl', 'Op', 'Dec', 'Brig', 'Gen'] \
|
'syn', 'transl', 'sess', 'fl', 'Op', 'Dec', 'Brig', 'Gen'] \
|
||||||
+ list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') \
|
+ list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') \
|
||||||
+ list('abcdefghijklmnopqrstuvwxyz')
|
+ list('abcdefghijklmnopqrstuvwxyz')
|
||||||
t_sentence = r'^.{5,}?(?<!\b%s)(?:\.(?=[\[ ][A-Z0-9]|\Z)|\Z)'
|
no_abbr = ''.join('(?<! ' + abbr + ')' for abbr in abbrs)
|
||||||
r_sentence = re.compile(t_sentence % r')(?<!\b'.join(abbrs))
|
breaks = re.compile('({})+'.format('|'.join([
|
||||||
|
no_abbr + '[.!?](?:[ \n]|\[[0-9]+\]|$)',
|
||||||
|
'。', '。', '.', '!', '?',
|
||||||
|
])))
|
||||||
|
|
||||||
|
def format_term(term):
|
||||||
|
term = term.replace(' ', '_')
|
||||||
|
term = term[0].upper() + term[1:]
|
||||||
|
return term
|
||||||
|
|
||||||
|
def deformat_term(term):
|
||||||
|
term = term.replace('_', ' ')
|
||||||
|
return term
|
||||||
|
|
||||||
|
def format_section(section):
|
||||||
|
section = section.replace(' ', '_')
|
||||||
|
section = quote(section)
|
||||||
|
section = section.replace('%', '.')
|
||||||
|
section = section.replace(".3A", ":")
|
||||||
|
return section
|
||||||
|
|
||||||
|
def parse_term(origterm):
|
||||||
|
if "#" in origterm:
|
||||||
|
term, section = origterm.split("#")[:2]
|
||||||
|
term, section = term.strip(), section.strip()
|
||||||
|
else:
|
||||||
|
term = origterm.strip()
|
||||||
|
section = None
|
||||||
|
|
||||||
|
return (term, section)
|
||||||
|
|
||||||
|
def good_content(text, content):
|
||||||
|
if text.tag not in ['p', 'ul', 'ol']:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not content.strip():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not breaks.search(content):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if text.find(".//span[@id='coordinates']") is not None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def search_content(text):
|
||||||
|
if text is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
content = text.text_content()
|
||||||
|
|
||||||
|
while not good_content(text, content):
|
||||||
|
text = text.getnext()
|
||||||
|
|
||||||
|
if text is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
content = text.text_content()
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def extract_snippet(match, origsection=None):
|
||||||
|
html, url = match
|
||||||
|
page = lxml.html.fromstring(html)
|
||||||
|
article = page.get_element_by_id('mw-content-text')
|
||||||
|
|
||||||
|
if origsection:
|
||||||
|
section = format_section(origsection)
|
||||||
|
text = article.find(".//span[@id='{0}']".format(section))
|
||||||
|
url += "#" + unquote(section)
|
||||||
|
|
||||||
|
if text is None:
|
||||||
|
return ("No '{0}' section found.".format(origsection), url)
|
||||||
|
|
||||||
|
text = text.getparent().getnext()
|
||||||
|
content = search_content(text)
|
||||||
|
|
||||||
|
if text is None:
|
||||||
|
return ("No section text found.", url)
|
||||||
|
else:
|
||||||
|
text = article.find('./p')
|
||||||
|
|
||||||
|
if text is None:
|
||||||
|
text = article.find('./div/p')
|
||||||
|
|
||||||
|
content = search_content(text)
|
||||||
|
|
||||||
|
if text is None:
|
||||||
|
return ("No introduction text found.", url)
|
||||||
|
|
||||||
|
sentences = [x.strip() for x in breaks.split(content)]
|
||||||
|
return (sentences[0], url)
|
||||||
|
|
||||||
class Wiki(object):
|
class Wiki(object):
|
||||||
def __init__(self, api, url, searchurl=""):
|
def __init__(self, endpoints):
|
||||||
self.api = api
|
self.endpoints = endpoints
|
||||||
self.url = url
|
|
||||||
self.searchurl = searchurl
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unescape(s):
|
def unescape(s):
|
||||||
|
@ -41,18 +133,34 @@ class Wiki(object):
|
||||||
html = r_whitespace.sub(' ', html)
|
html = r_whitespace.sub(' ', html)
|
||||||
return Wiki.unescape(html).strip()
|
return Wiki.unescape(html).strip()
|
||||||
|
|
||||||
def search(self, term, last=False):
|
def search(self, term):
|
||||||
url = self.api.format(term)
|
|
||||||
bytes = web.get(url)
|
|
||||||
try:
|
try:
|
||||||
result = json.loads(bytes)
|
exactterm = format_term(term)
|
||||||
result = result['query']['search']
|
exactterm = quote(exactterm)
|
||||||
if len(result) <= 0:
|
exacturl = self.endpoints['url'].format(exactterm)
|
||||||
return None
|
html = web.get(exacturl)
|
||||||
|
return (html, exacturl)
|
||||||
|
except HTTPError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
term = deformat_term(term)
|
||||||
|
term = quote(term)
|
||||||
|
apiurl = self.endpoints['api'].format(term)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = json.loads(web.get(apiurl))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
term = result[0]['title']
|
|
||||||
term = term.replace(' ', '_')
|
|
||||||
snippet = self.text(result[0]['snippet'])
|
|
||||||
return "{0} - {1}".format(snippet, self.url.format(term))
|
|
||||||
|
|
||||||
|
result = result['query']['search']
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
|
||||||
|
term = result[0]['title']
|
||||||
|
term = format_term(term)
|
||||||
|
term = quote(term)
|
||||||
|
|
||||||
|
url = self.endpoints['url'].format(term)
|
||||||
|
html = web.get(url)
|
||||||
|
return (html, url)
|
||||||
|
|
Loading…
Reference in New Issue