no more plugins/util

This commit is contained in:
Luke Rogers 2013-10-02 12:51:52 +13:00
parent 26bc076e7f
commit c6fd9e1f55
12 changed files with 0 additions and 908 deletions

View file

@ -1,35 +0,0 @@
# Colors. Plugin by blha303, color/control id info from http://stackoverflow.com/a/13382032
colors = {'white': '0', 'black': '1', 'darkblue': '2', 'darkgreen': '3',
'red': '4', 'darkred': '5', 'darkviolet': '6', 'orange': '7',
'yellow': '8', 'lightgreen': '9', 'cyan': '10', 'lightcyan': '11',
'blue': '12', 'violet': '13', 'darkgray': '14', 'lightgray': '15'}
control = {'bold': '\x02', 'color': '\x03', 'italic': '\x09',
'strikethrough': '\x13', 'reset': '\x0f', 'underline': '\x15',
'underline2': '\x1f', 'reverse': '\x16'}
def color(color):
return control['color'] + colors[color]
def bold():
return control['bold']
def italic():
return control['italic']
def strike():
return control['strikethrough']
def reset():
return control['reset']
def underline(other=False):
if other:
return control['underline2']
else:
return control['underline']
def reverse():
return control['reverse']

View file

@ -1,30 +0,0 @@
import http, web
def eval_py(code, paste_multiline=True):
attempts = 0
while True:
try:
output = http.get("http://eval.appspot.com/eval", statement=code).rstrip('\n')
# sometimes the API returns a blank string on first attempt, lets try again
# and make sure it is actually supposed to be a blank string. ._.
if output == "":
output = http.get("http://eval.appspot.com/eval", statement=code).rstrip('\n')
break
except http.HTTPError:
if attempts > 2:
return "Failed to execute code."
else:
attempts += 1
continue
if "Traceback (most recent call last):" in output:
status = "Python error: "
else:
status = "Code executed sucessfully: "
if "\n" in output and paste_multiline:
return status + web.haste(output)
else:
return output

View file

@ -1,34 +0,0 @@
def raw(format_string):
"""Replace based irc formatting"""
stuff = {}
stuff['col'] = {'[white]':'\x030',
'[black]':'\x031',
'[dblue]':'\x032',
'[dgreen]':'\x033',
'[dred]':'\x034',
'[brown]':'\x035',
'[purple]':'\x036',
'[gold]':'\x037',
'[yellow]':'\x038',
'[green]':'\x039',
'[cyan]':'\x0310',
'[lblue]':'\x0311',
'[blue]':'\x0312',
'[pink]':'\x0313',
'[gray]':'\x0314',
'[lgray]':'\x0315',
'[err]':'\x034\x02'
'[/err]':'\x030\x02'}
stuff['style'] = {'[b]':'\x02',
'[clear]':'\x0f'}
stuff['sym'] = {'[point]':'\x07'}
stuff['text'] = {'[url]':'http://'}
final = {}
for x in stuff:
final.update(stuff[x])
for x in final:
format_string = format_string.replace(x,final[x])
return format_string
def err(format_string):
"""Format the string with standard error styling"""
return "\x034\x02{}\x0f".format(format_string)

View file

@ -1,101 +0,0 @@
import inspect
import re
def _hook_add(func, add, name=''):
if not hasattr(func, '_hook'):
func._hook = []
func._hook.append(add)
if not hasattr(func, '_filename'):
func._filename = func.func_code.co_filename
if not hasattr(func, '_args'):
argspec = inspect.getargspec(func)
if name:
n_args = len(argspec.args)
if argspec.defaults:
n_args -= len(argspec.defaults)
if argspec.keywords:
n_args -= 1
if argspec.varargs:
n_args -= 1
if n_args != 1:
err = '%ss must take 1 non-keyword argument (%s)' % (name,
func.__name__)
raise ValueError(err)
args = []
if argspec.defaults:
end = bool(argspec.keywords) + bool(argspec.varargs)
args.extend(argspec.args[-len(argspec.defaults):
end if end else None])
if argspec.keywords:
args.append(0) # means kwargs present
func._args = args
if not hasattr(func, '_thread'): # does function run in its own thread?
func._thread = False
def sieve(func):
if func.func_code.co_argcount != 5:
raise ValueError(
'sieves must take 5 arguments: (bot, input, func, type, args)')
_hook_add(func, ['sieve', (func,)])
return func
def command(arg=None, **kwargs):
args = {}
def command_wrapper(func):
args.setdefault('name', func.func_name)
_hook_add(func, ['command', (func, args)], 'command')
return func
if kwargs or not inspect.isfunction(arg):
if arg is not None:
args['name'] = arg
args.update(kwargs)
return command_wrapper
else:
return command_wrapper(arg)
def event(arg=None, **kwargs):
args = kwargs
def event_wrapper(func):
args['name'] = func.func_name
args.setdefault('events', ['*'])
_hook_add(func, ['event', (func, args)], 'event')
return func
if inspect.isfunction(arg):
return event_wrapper(arg, kwargs)
else:
if arg is not None:
args['events'] = arg.split()
return event_wrapper
def singlethread(func):
func._thread = True
return func
def regex(regex, flags=0, **kwargs):
args = kwargs
def regex_wrapper(func):
args['name'] = func.func_name
args['regex'] = regex
args['re'] = re.compile(regex, flags)
_hook_add(func, ['regex', (func, args)], 'regex')
return func
if inspect.isfunction(regex):
raise ValueError("regex decorators require a regex to match against")
else:
return regex_wrapper

View file

@ -1,115 +0,0 @@
# convenience wrapper for urllib2 & friends
import cookielib
import json
import urllib
import urllib2
import urlparse
from urllib import quote, quote_plus as _quote_plus
from lxml import etree, html
from bs4 import BeautifulSoup
# used in plugins that import this
from urllib2 import URLError, HTTPError
ua_cloudbot = 'Cloudbot/DEV http://github.com/CloudDev/CloudBot'
ua_firefox = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/17.0' \
' Firefox/17.0'
ua_old_firefox = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; ' \
'rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6'
ua_internetexplorer = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'
ua_chrome = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.4 (KHTML, ' \
'like Gecko) Chrome/22.0.1229.79 Safari/537.4'
jar = cookielib.CookieJar()
def get(*args, **kwargs):
return open(*args, **kwargs).read()
def get_url(*args, **kwargs):
return open(*args, **kwargs).geturl()
def get_html(*args, **kwargs):
return html.fromstring(get(*args, **kwargs))
def get_soup(*args, **kwargs):
return BeautifulSoup(get(*args, **kwargs), 'lxml')
def get_xml(*args, **kwargs):
return etree.fromstring(get(*args, **kwargs))
def get_json(*args, **kwargs):
return json.loads(get(*args, **kwargs))
def open(url, query_params=None, user_agent=None, post_data=None,
referer=None, get_method=None, cookies=False, timeout=None, **kwargs):
if query_params is None:
query_params = {}
if user_agent is None:
user_agent = ua_cloudbot
query_params.update(kwargs)
url = prepare_url(url, query_params)
request = urllib2.Request(url, post_data)
if get_method is not None:
request.get_method = lambda: get_method
request.add_header('User-Agent', user_agent)
if referer is not None:
request.add_header('Referer', referer)
if cookies:
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar))
else:
opener = urllib2.build_opener()
if timeout:
return opener.open(request, timeout=timeout)
else:
return opener.open(request)
def prepare_url(url, queries):
if queries:
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
query = dict(urlparse.parse_qsl(query))
query.update(queries)
query = urllib.urlencode(dict((to_utf8(key), to_utf8(value))
for key, value in query.iteritems()))
url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
return url
def to_utf8(s):
if isinstance(s, unicode):
return s.encode('utf8', 'ignore')
else:
return str(s)
def quote_plus(s):
return _quote_plus(to_utf8(s))
def unescape(s):
if not s.strip():
return s
return html.fromstring(s).text_content()

View file

@ -1,230 +0,0 @@
# -*- coding: utf-8 -*-
""" formatting.py - handy functions for formatting text
this file contains code from the following URL:
<http://code.djangoproject.com/svn/django/trunk/django/utils/text.py>
"""
import re
from HTMLParser import HTMLParser
import htmlentitydefs
class HTMLTextExtractor(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.result = []
def handle_data(self, d):
self.result.append(d)
def handle_charref(self, number):
codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
self.result.append(unichr(codepoint))
def handle_entityref(self, name):
codepoint = htmlentitydefs.name2codepoint[name]
self.result.append(unichr(codepoint))
def get_text(self):
return u''.join(self.result)
def strip_html(html):
s = HTMLTextExtractor()
s.feed(html)
return s.get_text()
def munge(text, munge_count=0):
"""munges up text."""
reps = 0
for n in xrange(len(text)):
rep = character_replacements.get(text[n])
if rep:
text = text[:n] + rep.decode('utf8') + text[n + 1:]
reps += 1
if reps == munge_count:
break
return text
character_replacements = {
'a': 'ä',
'b': 'Б',
'c': 'ċ',
'd': 'đ',
'e': 'ë',
'f': 'ƒ',
'g': 'ġ',
'h': 'ħ',
'i': 'í',
'j': 'ĵ',
'k': 'ķ',
'l': 'ĺ',
'm': '',
'n': 'ñ',
'o': 'ö',
'p': 'ρ',
'q': 'ʠ',
'r': 'ŗ',
's': 'š',
't': 'ţ',
'u': 'ü',
'v': '',
'w': 'ω',
'x': 'χ',
'y': 'ÿ',
'z': 'ź',
'A': 'Å',
'B': 'Β',
'C': 'Ç',
'D': 'Ď',
'E': 'Ē',
'F': '',
'G': 'Ġ',
'H': 'Ħ',
'I': 'Í',
'J': 'Ĵ',
'K': 'Ķ',
'L': 'Ĺ',
'M': 'Μ',
'N': 'Ν',
'O': 'Ö',
'P': 'Р',
'Q': '',
'R': 'Ŗ',
'S': 'Š',
'T': 'Ţ',
'U': 'Ů',
'V': '',
'W': 'Ŵ',
'X': 'Χ',
'Y': '',
'Z': 'Ż'}
def capitalize_first(line):
"""
capitalises the first letter of words
(keeps other letters intact)
"""
return ' '.join([s[0].upper() + s[1:] for s in line.split(' ')])
def multiword_replace(text, wordDic):
"""
take a text and replace words that match a key in a dictionary with
the associated value, return the changed text
"""
rc = re.compile('|'.join(map(re.escape, wordDic)))
def translate(match):
return wordDic[match.group(0)]
return rc.sub(translate, text)
def truncate_words(content, length=10, suffix='...'):
"""Truncates a string after a certain number of words."""
nmsg = content.split(" ")
out = None
x = 0
for i in nmsg:
if x <= length:
if out:
out = out + " " + nmsg[x]
else:
out = nmsg[x]
x += 1
if x <= length:
return out
else:
return out + suffix
# from <http://stackoverflow.com/questions/250357/smart-truncate-in-python>
def truncate_str(content, length=100, suffix='...'):
"""Truncates a string after a certain number of chars."""
if len(content) <= length:
return content
else:
return content[:length].rsplit(' ', 1)[0] + suffix
# ALL CODE BELOW THIS LINE IS COVERED BY THE FOLLOWING AGREEMENT:
# Copyright (c) Django Software Foundation and individual contributors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of Django nor the names of its contributors may be used
# to endorse or promote products derived from this software without
# specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
#ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Expression to match some_token and some_token="with spaces" (and similarly
# for single-quoted strings).
split_re = re.compile(r"""((?:[^\s'"]*(?:(?:"(?:[^"\\]|\\.)*" | '(?:[""" \
r"""^'\\]|\\.)*')[^\s'"]*)+) | \S+)""", re.VERBOSE)
def smart_split(text):
r"""
Generator that splits a string by spaces, leaving quoted phrases together.
Supports both single and double quotes, and supports escaping quotes with
backslashes. In the output, strings will keep their initial and trailing
quote marks and escaped quotes will remain escaped (the results can then
be further processed with unescape_string_literal()).
>>> list(smart_split(r'This is "a person\'s" test.'))
[u'This', u'is', u'"a person\\\'s"', u'test.']
>>> list(smart_split(r"Another 'person\'s' test."))
[u'Another', u"'person\\'s'", u'test.']
>>> list(smart_split(r'A "\"funky\" style" test.'))
[u'A', u'"\\"funky\\" style"', u'test.']
"""
for bit in split_re.finditer(text):
yield bit.group(0)
def get_text_list(list_, last_word='or'):
"""
>>> get_text_list(['a', 'b', 'c', 'd'])
u'a, b, c or d'
>>> get_text_list(['a', 'b', 'c'], 'and')
u'a, b and c'
>>> get_text_list(['a', 'b'], 'and')
u'a and b'
>>> get_text_list(['a'])
u'a'
>>> get_text_list([])
u''
"""
if len(list_) == 0:
return ''
if len(list_) == 1:
return list_[0]
return '%s %s %s' % (
# Translators: This string is used as a separator between list elements
', '.join([i for i in list_][:-1]),
last_word, list_[-1])

View file

@ -1,51 +0,0 @@
import re
import random
TEMPLATE_RE = re.compile(r"\{(.+?)\}")
class TextGenerator(object):
def __init__(self, templates, parts, default_templates=None, variables=None):
self.templates = templates
self.default_templates = default_templates
self.parts = parts
self.variables = variables
def generate_string(self, template=None):
"""
Generates one string using the specified templates.
If no templates are specified, use a random template from the default_templates list.
"""
# this is bad
if self.default_templates:
text = self.templates[template or random.choice(self.default_templates)]
else:
text = random.choice(self.templates)
# replace static variables in the template with provided values
if self.variables:
for key, value in self.variables.items():
text = text.replace("{%s}" % key, value)
# get a list of all text parts we need
required_parts = TEMPLATE_RE.findall(text)
for required_part in required_parts:
ppart = self.parts[required_part]
# check if the part is a single string or a list
if not isinstance(ppart, basestring):
part = random.choice(self.parts[required_part])
else:
part = self.parts[required_part]
text = text.replace("{%s}" % required_part, part)
return text
def generate_strings(self, amount, template=None):
strings = []
for i in xrange(amount):
strings.append(self.generate_string())
return strings
def get_template(self, template):
return self.templates[template]

View file

@ -1,14 +0,0 @@
def timeformat(seconds):
days = seconds / 86400
seconds -= 86400 * days
hours = seconds / 3600
seconds -= 3600 * hours
minutes = seconds / 60
seconds -= 60 * minutes
if days != 0:
return "%sd %sh %sm %ss" % (days, hours, minutes, seconds)
elif hours == 0 and minutes != 0:
return "%sm %ss" % (minutes, seconds)
elif hours == 0 and minutes == 0:
return "%ss" % seconds
return "%sh %sm %ss" % (hours, minutes, seconds)

View file

@ -1,105 +0,0 @@
# Copyright (c) Django Software Foundation and individual contributors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of Django nor the names of its contributors may be used
# to endorse or promote products derived from this software without
# specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
#ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import datetime
def timesince(d, now=None):
"""
Takes two datetime objects and returns the time between d and now
as a nicely formatted string, e.g. "10 minutes". If d occurs after now,
then "0 minutes" is returned.
Units used are years, months, weeks, days, hours, and minutes.
Seconds and microseconds are ignored. Up to two adjacent units will be
displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are
possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not.
Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since
"""
chunks = (
(60 * 60 * 24 * 365, ('year', 'years')),
(60 * 60 * 24 * 30, ('month', 'months')),
(60 * 60 * 24 * 7, ('week', 'weeks')),
(60 * 60 * 24, ('day', 'days')),
(60 * 60, ('hour', 'hours')),
(60, ('minute', 'minutes'))
)
# Convert int or float (unix epoch) to datetime.datetime for comparison
if isinstance(d, int) or isinstance(d, float):
d = datetime.datetime.fromtimestamp(d)
if isinstance(now, int) or isinstance(now, float):
now = datetime.datetime.fromtimestamp(now)
# Convert datetime.date to datetime.datetime for comparison.
if not isinstance(d, datetime.datetime):
d = datetime.datetime(d.year, d.month, d.day)
if now and not isinstance(now, datetime.datetime):
now = datetime.datetime(now.year, now.month, now.day)
if not now:
now = datetime.datetime.now()
# ignore microsecond part of 'd' since we removed it from 'now'
delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
since = delta.days * 24 * 60 * 60 + delta.seconds
if since <= 0:
# d is in the future compared to now, stop processing.
return u'0 ' + 'minutes'
for i, (seconds, name) in enumerate(chunks):
count = since // seconds
if count != 0:
break
if count == 1:
s = '%(number)d %(type)s' % {'number': count, 'type': name[0]}
else:
s = '%(number)d %(type)s' % {'number': count, 'type': name[1]}
if i + 1 < len(chunks):
# Now get the second item
seconds2, name2 = chunks[i + 1]
count2 = (since - (seconds * count)) // seconds2
if count2 != 0:
if count2 == 1:
s += ', %d %s' % (count2, name2[0])
else:
s += ', %d %s' % (count2, name2[1])
return s
def timeuntil(d, now=None):
"""
Like timesince, but returns a string measuring the time until
the given time.
"""
if not now:
now = datetime.datetime.now()
return timesince(now, d)

View file

@ -1,139 +0,0 @@
"""
URI Normalization function:
* Always provide the URI scheme in lowercase characters.
* Always provide the host, if any, in lowercase characters.
* Only perform percent-encoding where it is essential.
* Always use uppercase A-through-F characters when percent-encoding.
* Prevent dot-segments appearing in non-relative URI paths.
* For schemes that define a default authority, use an empty authority if the
default is desired.
* For schemes that define an empty path to be equivalent to a path of "/",
use "/".
* For schemes that define a port, use an empty port if the default is desired
* All portions of the URI must be utf-8 encoded NFC from Unicode strings
implements:
http://gbiv.com/protocols/uri/rev-2002/rfc2396bis.html#canonical-form
http://www.intertwingly.net/wiki/pie/PaceCanonicalIds
inspired by:
Tony J. Ibbs, http://starship.python.net/crew/tibs/python/tji_url.py
Mark Nottingham, http://www.mnot.net/python/urlnorm.py
"""
__license__ = "Python"
import re
import unicodedata
import urlparse
from urllib import quote, unquote
default_port = {
'http': 80,
}
class Normalizer(object):
def __init__(self, regex, normalize_func):
self.regex = regex
self.normalize = normalize_func
normalizers = (Normalizer(re.compile(
r'(?:https?://)?(?:[a-zA-Z0-9\-]+\.)?(?:amazon|amzn){1}\.(?P<tld>[a-zA-Z\.]{2,})\/(gp/(?:product|offer-listing|customer-media/product-gallery)/|exec/obidos/tg/detail/-/|o/ASIN/|dp/|(?:[A-Za-z0-9\-]+)/dp/)?(?P<ASIN>[0-9A-Za-z]{10})'),
lambda m: r'http://amazon.%s/dp/%s' % (m.group('tld'), m.group('ASIN'))),
Normalizer(re.compile(r'.*waffleimages\.com.*/([0-9a-fA-F]{40})'),
lambda m: r'http://img.waffleimages.com/%s' % m.group(1)),
Normalizer(re.compile(r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-zA-Z0-9]+)'),
lambda m: r'http://youtube.com/watch?v=%s' % m.group(1)),
)
def normalize(url, assume_scheme=False):
"""Normalize a URL."""
scheme, auth, path, query, fragment = urlparse.urlsplit(url.strip())
userinfo, host, port = re.search('([^@]*@)?([^:]*):?(.*)', auth).groups()
# Always provide the URI scheme in lowercase characters.
scheme = scheme.lower()
# Always provide the host, if any, in lowercase characters.
host = host.lower()
if host and host[-1] == '.':
host = host[:-1]
if host and host.startswith("www."):
if not scheme:
scheme = "http"
host = host[4:]
elif path and path.startswith("www."):
if not scheme:
scheme = "http"
path = path[4:]
if assume_scheme and not scheme:
scheme = assume_scheme.lower()
# Only perform percent-encoding where it is essential.
# Always use uppercase A-through-F characters when percent-encoding.
# All portions of the URI must be utf-8 encoded NFC from Unicode strings
def clean(string):
string = unicode(unquote(string), 'utf-8', 'replace')
return unicodedata.normalize('NFC', string).encode('utf-8')
path = quote(clean(path), "~:/?#[]@!$&'()*+,;=")
fragment = quote(clean(fragment), "~")
# note care must be taken to only encode & and = characters as values
query = "&".join(["=".join([quote(clean(t), "~:/?#[]@!$'()*+,;=")
for t in q.split("=", 1)]) for q in query.split("&")])
# Prevent dot-segments appearing in non-relative URI paths.
if scheme in ["", "http", "https", "ftp", "file"]:
output = []
for input in path.split('/'):
if input == "":
if not output:
output.append(input)
elif input == ".":
pass
elif input == "..":
if len(output) > 1:
output.pop()
else:
output.append(input)
if input in ["", ".", ".."]:
output.append("")
path = '/'.join(output)
# For schemes that define a default authority, use an empty authority if
# the default is desired.
if userinfo in ["@", ":@"]:
userinfo = ""
# For schemes that define an empty path to be equivalent to a path of "/",
# use "/".
if path == "" and scheme in ["http", "https", "ftp", "file"]:
path = "/"
# For schemes that define a port, use an empty port if the default is
# desired
if port and scheme in default_port.keys():
if port.isdigit():
port = str(int(port))
if int(port) == default_port[scheme]:
port = ''
# Put it all back together again
auth = (userinfo or "") + host
if port:
auth += ":" + port
if url.endswith("#") and query == "" and fragment == "":
path += "#"
normal_url = urlparse.urlunsplit((scheme, auth, path, query,
fragment)).replace("http:///", "http://")
for norm in normalizers:
m = norm.regex.match(normal_url)
if m:
return norm.normalize(m)
return normal_url

View file

@ -1,54 +0,0 @@
""" web.py - handy functions for web services """
import http
import urlnorm
import json
import urllib
import yql
short_url = "http://is.gd/create.php"
paste_url = "http://hastebin.com"
yql_env = "http://datatables.org/alltables.env"
YQL = yql.Public()
class ShortenError(Exception):
def __init__(self, code, text):
self.code = code
self.text = text
def __str__(self):
return self.text
def isgd(url):
""" shortens a URL with the is.gd API """
url = urlnorm.normalize(url.encode('utf-8'), assume_scheme='http')
params = urllib.urlencode({'format': 'json', 'url': url})
request = http.get_json("http://is.gd/create.php?%s" % params)
if "errorcode" in request:
raise ShortenError(request["errorcode"], request["errormessage"])
else:
return request["shorturl"]
def try_isgd(url):
try:
out = isgd(url)
except (ShortenError, http.HTTPError):
out = url
return out
def haste(text, ext='txt'):
""" pastes text to a hastebin server """
page = http.get(paste_url + "/documents", post_data=text)
data = json.loads(page)
return ("%s/%s.%s" % (paste_url, data['key'], ext))
def query(query, params={}):
""" runs a YQL query and returns the results """
return YQL.execute(query, params, env=yql_env)