PKª©°8“×2EGG-INFO/dependency_links.txt PKª©°8â®,ÇŽŽEGG-INFO/entry_points.txt[trac.plugins] hackergotchi.providers = hackergotchi.providers hackergotchi.web_ui = hackergotchi.web_ui hackergotchi.api = hackergotchi.api PKª©°8½‰±Ô  EGG-INFO/PKG-INFOMetadata-Version: 1.0 Name: TracHackergotchi Version: 1.0 Summary: Adds user-specific icons to the Trac timeline view. Home-page: http://trac-hacks.org/wiki/HackergotchiPlugin Author: Noah Kantrowitz Author-email: noah@coderanger.net License: BSD Description: Notes ===== Adds user-specific icons to the timeline view. .. image:: http://trac-hacks.org/attachment/wiki/HackergotchiPlugin/hackergotchi.png?format=raw Providers ========= Identicon --------- Locally generate identicons__ using the author information. This provider requires PIL__ to be installed. __ http://www.docuverse.com/blog/donpark/2007/01/18/visual-security-9-block-ip-identification __ http://pythonware.com/products/pil/ Identicon implementation is courtesy of `Shin Adachi `_. Gravatar -------- Uses the Gravatar.com__ service to generate images. __ http://gravatar.com Configuration ============= All configuration options go in the ``[hackergotchi]`` section. ``providers`` : *optional, default: GravatarHackergotchiProvider, IdenticonHackergotchiProvider* Order to try providers when looking for an image. Any providers enabled but not listed will be tried in a pseudo-random order. ``gravatar_default`` : *optional, default: identicon* Value to pass along to Gravatar to use if the email doesn't match anything. Valid values are ``identicon``, ``monsterid``, ``wavatar``, or a URL to an image. In the first three cases, a icon will be procedurally generated. To enable the plugin:: [components] hackergotchi.* = enabled Example ======= To never use the Gravatar provider:: [hackergotchi] providers = IdenticonHackergotchiProvider [components] hackergotchi.* = enabled hackergotchi.providers.GravatarHackergotchiProvider = disabled Keywords: trac plugin timeline identicon hackergotchi Platform: UNKNOWN Classifier: Framework :: Trac Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: License :: OSI Approved :: BSD License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python PKª©°8Ó‘šEGG-INFO/requires.txtTracPKª©°8˜pc))EGG-INFO/SOURCES.txtREADME setup.py TracHackergotchi.egg-info/PKG-INFO TracHackergotchi.egg-info/SOURCES.txt TracHackergotchi.egg-info/dependency_links.txt TracHackergotchi.egg-info/entry_points.txt TracHackergotchi.egg-info/requires.txt TracHackergotchi.egg-info/top_level.txt hackergotchi/__init__.py hackergotchi/api.py hackergotchi/identicon.py hackergotchi/providers.py hackergotchi/web_ui.py hackergotchi/htdocs/default.png hackergotchi/htdocs/hackergotchi.css hackergotchi/htdocs/identicon/identicon_canvas.js hackergotchi/htdocs/identicon/identicon_canvas_test.htm PKª©°8@Ÿp EGG-INFO/top_level.txthackergotchi PKª©°8“×2EGG-INFO/zip-safe PK©°8hackergotchi/__init__.pyPKª©°8á°Ú‹‹hackergotchi/__init__.pyc;ò ó/.Hc@sdS(N((((s;build/bdist.darwin-8.11.1-i386/egg/hackergotchi/__init__.pys?sPK©°8e¨nnhackergotchi/api.py# Created by Noah Kantrowitz on 2008-05-16. # Copyright (c) 2008 Noah Kantrowitz. All rights reserved. from trac.core import * class IHackergotchiProvider(Interface): """An extension-point interface for exposing hackergotchi providers.""" def get_hackergotchi(href, user, name, email): """Return an href to an image corresponding the user information given. :param href: An Href object for the current Trac. :param user: The username or 'anonymous' :param name: The user's full name or None :param email: The user's email address or None """ PKª©°8•Œi00hackergotchi/api.pyc;ò ó/.Hc@s!dkTdefd„ƒYZdS((s*sIHackergotchiProvidercBstZdZd„ZRS(sAAn extension-point interface for exposing hackergotchi providers.cCsdS(s2Return an href to an image corresponding the user information given. :param href: An Href object for the current Trac. :param user: The username or 'anonymous' :param name: The user's full name or None :param email: The user's email address or None N((shrefsusersnamesemail((s6build/bdist.darwin-8.11.1-i386/egg/hackergotchi/api.pysget_hackergotchi s(s__name__s __module__s__doc__sget_hackergotchi(((s6build/bdist.darwin-8.11.1-i386/egg/hackergotchi/api.pysIHackergotchiProviders N(s trac.cores InterfacesIHackergotchiProvider(sIHackergotchiProvider((s6build/bdist.darwin-8.11.1-i386/egg/hackergotchi/api.pys?sPK©°8W»9@@hackergotchi/identicon.py#!/usr/bin/env python # -*- coding:utf-8 -*- """ identicon.py identicon python implementation. by Shin Adachi = usage = == commandline == >>> python identicon.py [code] == python == >>> import identicon >>> identicon.render_identicon(code, size) Return a PIL Image class instance which have generated identicon image. ```size``` specifies `patch size`. Generated image size is 3 * ```size```. """ # g # PIL Modules import Image, ImageDraw, ImagePath, ImageColor __all__ = ['render_identicon', 'IdenticonRendererBase'] class Matrix2D(list): """Matrix for Patch rotation""" def __init__(self, initial = [0.] * 9): assert isinstance(initial, list) and len(initial)==9 list.__init__(self, initial) def clear(self): for i in xrange(9): self[i] = 0. def set_identity(self): self.clear() for i in xrange(3): self[i] = 1. def __str__(self): return '[%s]' % ', '.join('%3.2f' % v for v in self) def __mul__(self, other): r = [] if isinstance(other, Matrix2D): for y in xrange(3): for x in xrange(3): v = 0.0 for i in xrange(3): v += (self[i * 3 + x] * other[y * 3 + i]) r.append(v) else: raise NotImplementedError return Matrix2D(r) def for_PIL(self): return self[0:6] @classmethod def translate(kls, x, y): return kls([1.0, 0.0, float(x), 0.0, 1.0, float(y), 0.0, 0.0, 1.0]) @classmethod def scale(kls, x, y): return kls([float(x), 0.0, 0.0, 0.0, float(y), 0.0, 0.0, 0.0, 1.0]) """ # need `import math` @classmethod def rotate(kls, theta, pivot=None): c = math.cos(theta) s = math.sin(theta) matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.]) if not pivot: return matR return kls.translate(-pivot[0], -pivot[1]) * matR * kls.translate(*pivot) """ @classmethod def rotateSquare(kls, theta, pivot=None): theta = theta % 4 c = [1., 0., -1., 0.][theta] s = [0., 1., 0., -1.][theta] matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.]) if not pivot: return matR return kls.translate(-pivot[0], -pivot[1]) * matR * kls.translate(*pivot) class IdenticonRendererBase(object): PATH_SET = [] def __init__(self, code): """ @param code code for icon """ if not isinstance(code, int): code = int(code) self.code = code def render(self, size): """ render identicon to PIL.Image @param size identicon patchsize. (image size is 3 * [size]) @return PIL.Image """ # decode the code middle, corner, side, foreColor, backColor = self.decode(self.code) # make image image = Image.new("RGB", (size * 3, size * 3)) draw = ImageDraw.Draw(image) # fill background draw.rectangle((0, 0, image.size[0], image.size[1]), fill=0) kwds = { 'draw': draw, 'size': size, 'foreColor': foreColor, 'backColor': backColor } # middle patch self.drawPatch((1, 1), middle[2], middle[1], middle[0], **kwds) # side patch kwds['type'] = side[0] for i in xrange(4): pos = [(1, 0), (2, 1), (1, 2), (0, 1)][i] self.drawPatch(pos, side[2] + 1 + i, side[1], **kwds) # corner patch kwds['type'] = corner[0] for i in xrange(4): pos = [(0, 0), (2, 0), (2, 2), (0, 2)][i] self.drawPatch(pos, corner[2] + 1 + i, corner[1], **kwds) return image def drawPatch(self, pos, turn, invert, type, draw, size, foreColor, backColor): """ @param size patch size """ path = self.PATH_SET[type] if not path: # blank patch invert = not invert path = [(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)] patch = ImagePath.Path(path) if invert: foreColor, backColor = backColor, foreColor mat = Matrix2D.rotateSquare(turn, pivot=(0.5, 0.5)) *\ Matrix2D.translate(*pos) *\ Matrix2D.scale(size, size) patch.transform(mat.for_PIL()) draw.rectangle((pos[0] * size, pos[1] * size, (pos[0]+1) * size, (pos[1]+1) * size), fill=backColor) draw.polygon(patch, fill=foreColor, outline=foreColor) ### virtual functions def decode(self, code): raise NotImplementedError class DonRenderer(IdenticonRendererBase): """ Don Park's implementation of identicon see : http://www.docuverse.com/blog/donpark/2007/01/19/identicon-updated-and-source-released """ PATH_SET = [ [(0, 0), (4, 0), (4, 4), (0, 4)], # 0 [(0, 0), (4, 0), (0, 4)], [(2, 0), (4, 4), (0, 4)], [(0, 0), (2, 0), (2, 4), (0, 4)], [(2, 0), (4, 2), (2, 4), (0, 2)], # 4 [(0, 0), (4, 2), (4, 4), (2, 4)], [(2, 0), (4, 4), (2, 4), (3, 2), (1, 2), (2, 4), (0, 4)], [(0, 0), (4, 2), (2, 4)], [(1, 1), (3, 1), (3, 3), (1, 3)], # 8 [(2, 0), (4, 0), (0, 4), (0, 2), (2, 2)], [(0, 0), (2, 0), (2, 2), (0, 2)], [(0, 2), (4, 2), (2, 4)], [(2, 2), (4, 4), (0, 4)], [(2, 0), (2, 2), (0, 2)], [(0, 0), (2, 0), (0, 2)], [] # 15 ] MIDDLE_PATCH_SET = [0, 4, 8, 15] # modify path set for idx in xrange(len(PATH_SET)): if PATH_SET[idx]: p = map(lambda vec: (vec[0] / 4.0, vec[1] / 4.0), PATH_SET[idx]) PATH_SET[idx] = p + p[:1] def decode(self, code): # decode the code middleType = self.MIDDLE_PATCH_SET[code & 0x03] middleInvert= (code >> 2) & 0x01 cornerType = (code >> 3) & 0x0F cornerInvert= (code >> 7) & 0x01 cornerTurn = (code >> 8) & 0x03 sideType = (code >> 10) & 0x0F sideInvert = (code >> 14) & 0x01 sideTurn = (code >> 15) & 0x03 blue = (code >> 16) & 0x1F green = (code >> 21) & 0x1F red = (code >> 27) & 0x1F foreColor = (red << 3, green << 3, blue << 3) return (middleType, middleInvert, 0),\ (cornerType, cornerInvert, cornerTurn),\ (sideType, sideInvert, sideTurn),\ foreColor, ImageColor.getrgb('white') def render_identicon(code, size, renderer=None): if not renderer: renderer = DonRenderer return renderer(code).render(size) if __name__=='__main__': import sys if len(sys.argv)<2: print 'usage: python identicon.py [CODE]....' raise SystemExit for code in sys.argv[1:]: if code.startswith('0x') or code.startswith('0X'): code = int(code[2:], 16) elif code.startswith('0'): code = int(code[1:], 8) else: code = int(code) icon = render_identicon(code, 24) icon.save('%08x.png' % code, 'PNG')PK©°8ŒÀ±ý ý hackergotchi/providers.py# Created by Noah Kantrowitz on 2008-05-16. # Copyright (c) 2008 Noah Kantrowitz. All rights reserved. import urllib from cStringIO import StringIO try: from hashlib import md5 except ImportError: from md5 import new as md5 from trac.core import * from trac.config import Option, BoolOption from trac.web.api import IRequestHandler, RequestDone from hackergotchi.api import IHackergotchiProvider try: from hackergotchi.identicon import render_identicon except ImportError: render_identicon = None # TODO: Add a client-side identicon implementation using canvas class GravatarHackergotchiProvider(Component): """Use gravatar.com to provide images.""" implements(IHackergotchiProvider) default = Option('hackergotchi', 'gravatar_default', default='identicon', doc='The default value to pass along to gravatar to use if the email address does not match.') # IHackergotchiProvider methods def get_hackergotchi(self, href, user, name, email): if email: return 'http://www.gravatar.com/avatar.php?' + urllib.urlencode({ 'gravatar_id': md5(email).hexdigest(), 'default': self.default, 'size': 20, }) class IdenticonHackergotchiProvider(Component): """Generate identicons locally to provide images.""" implements(IHackergotchiProvider, IRequestHandler) # IHackergotchiProvider methods def get_hackergotchi(self, href, user, name, email): if render_identicon is None: return None h = md5(user) h.update(name or '') h.update(email or '') code = h.hexdigest()[:8] return href.identicon(code+'.png') # IRequestHandler methods def match_request(self, req): return req.path_info.startswith('/identicon') def process_request(self, req): if render_identicon is None: raise TracError('PIL not installed, can not render identicon') # Render the identicon to a string code = int(req.path_info[11:-4], 16) icon = render_identicon(code, 20) out = StringIO() icon.save(out, 'png') data = out.getvalue() # Send response req.send_response(200) req.send_header('Content-Type', 'image/png') req.send_header('Content-Length', len(data)) req.send_header('Cache-Control', 'max-age=36000') if req.method != 'HEAD': req.write(data) raise RequestDonePKª©°8a‹Ú*TThackergotchi/providers.pyc;ò ó/.Hc@sádkZdklZydklZWn ej odklZnXdkTdkl Z l Z dk l Z l Z dklZydklZWnej o eZnXd efd „ƒYZd efd „ƒYZdS( N(sStringIO(smd5(snew(s*(sOptions BoolOption(sIRequestHandlers RequestDone(sIHackergotchiProvider(srender_identiconsGravatarHackergotchiProvidercBs<tZdZeeƒeddddddƒZd„ZRS(s#Use gravatar.com to provide images.s hackergotchisgravatar_defaultsdefaults identiconsdocsWThe default value to pass along to gravatar to use if the email address does not match.cCsJ|o?dtihdt|ƒiƒ<d|i<dd<ƒSndS(Ns#http://www.gravatar.com/avatar.php?s gravatar_idsdefaultssizei(semailsurllibs urlencodesmd5s hexdigestsselfsdefault(sselfshrefsusersnamesemail((s<build/bdist.darwin-8.11.1-i386/egg/hackergotchi/providers.pysget_hackergotchis(s__name__s __module__s__doc__s implementssIHackergotchiProvidersOptionsdefaultsget_hackergotchi(((s<build/bdist.darwin-8.11.1-i386/egg/hackergotchi/providers.pysGravatarHackergotchiProviders   sIdenticonHackergotchiProvidercBs6tZdZeeeƒd„Zd„Zd„ZRS(s.Generate identicons locally to provide images.cCsnttjotSnt|ƒ}|i|pdƒ|i|pdƒ|iƒd }|i |dƒSdS(Nsis.png( srender_identiconsNonesmd5susershsupdatesnamesemails hexdigestscodeshrefs identicon(sselfshrefsusersnamesemailscodesh((s<build/bdist.darwin-8.11.1-i386/egg/hackergotchi/providers.pysget_hackergotchi.s  cCs|iidƒSdS(Ns /identicon(sreqs path_infos startswith(sselfsreq((s<build/bdist.darwin-8.11.1-i386/egg/hackergotchi/providers.pys match_request9scCsØttjotdƒ‚nt|idd!dƒ}t|dƒ}tƒ}|i |dƒ|i ƒ}|i dƒ|idd ƒ|id t|ƒƒ|id d ƒ|id jo|i|ƒnt‚dS(Ns+PIL not installed, can not render identiconi iüÿÿÿiispngiÈs Content-Types image/pngsContent-Lengths Cache-Controls max-age=36000sHEAD(srender_identiconsNones TracErrorsintsreqs path_infoscodesiconsStringIOsoutssavesgetvaluesdatas send_responses send_headerslensmethodswrites RequestDone(sselfsreqscodesoutsdatasicon((s<build/bdist.darwin-8.11.1-i386/egg/hackergotchi/providers.pysprocess_request<s    ( s__name__s __module__s__doc__s implementssIHackergotchiProvidersIRequestHandlersget_hackergotchis match_requestsprocess_request(((s<build/bdist.darwin-8.11.1-i386/egg/hackergotchi/providers.pysIdenticonHackergotchiProvider(s   (surllibs cStringIOsStringIOshashlibsmd5s ImportErrorsnews trac.cores trac.configsOptions BoolOptions trac.web.apisIRequestHandlers RequestDoneshackergotchi.apisIHackergotchiProvidershackergotchi.identiconsrender_identiconsNones ComponentsGravatarHackergotchiProvidersIdenticonHackergotchiProvider( sIdenticonHackergotchiProviders RequestDonesOptionsStringIOsIRequestHandlersurllibsIHackergotchiProvidersrender_identicons BoolOptionsGravatarHackergotchiProvidersmd5((s<build/bdist.darwin-8.11.1-i386/egg/hackergotchi/providers.pys?s    PK©°8 ¤?‰ççhackergotchi/web_ui.py# Created by Noah Kantrowitz on 2008-05-16. # Copyright (c) 2008 Noah Kantrowitz. All rights reserved. import itertools import re from trac.core import * from trac.web.api import ITemplateStreamFilter from trac.web.chrome import ITemplateProvider, add_stylesheet from trac.config import Option, OrderedExtensionsOption from genshi.builder import tag from genshi.filters.transform import Transformer from pkg_resources import resource_filename from hackergotchi.api import IHackergotchiProvider class HackergotchiModule(Component): """A stream filter to add hackergotchi emblems to the timeline.""" providers = OrderedExtensionsOption('hackergotchi', 'providers', IHackergotchiProvider, default='GravatarHackergotchiProvider, IdenticonHackergotchiProvider') implements(ITemplateStreamFilter, ITemplateProvider) anon_re = re.compile('([^<]+?)\s+<([^>]+)>', re.U) # ITemplateStreamFilter methods def filter_stream(self, req, method, filename, stream, data): if req.path_info.startswith('/timeline'): closure_state = [0] db = self.env.get_db_cnx() cursor = db.cursor() cache = {} def f(stream): # Update the closed value n = closure_state[0] closure_state[0] += 1 # Extract the user information author = data['events'][n]['author'].strip() user_info = cache.get(author) if user_info is not None: author, name, email = user_info else: user_info = self._get_info(author, cursor) cache[author] = user_info author, name, email = user_info # Try to find a provider for provider in self.providers: href = provider.get_hackergotchi(req.href, author, name, email) if href is not None: break else: href = req.href.chrome('hackergotchi', 'default.png') # Build our element elm = tag.img(src=href, alt='Hackergotchi for %s'%author, class_='hackergotchi') # Output the combined stream return itertools.chain(elm.generate(), stream) stream |= Transformer('//div[@id="content"]/dl/dt/a/span[@class="time"]').filter(f) add_stylesheet(req, 'hackergotchi/hackergotchi.css') return stream # ITemplateProvider methods def get_htdocs_dirs(self): yield 'hackergotchi', resource_filename(__name__, 'htdocs') def get_templates_dirs(self): #return [resource_filename(__name__, 'templates')] return [] # Internal methods def _get_info(self, author, cursor): if author == 'anonymous': # Don't even bother trying for "anonymous" return author, None, None md = self.anon_re.match(author) if md: # name return 'anonymous', md.group(1), md.group(2) cursor.execute('SELECT name, value FROM session_attribute WHERE sid=%s AND authenticated=%s', (author, 1)) rows = cursor.fetchall() if rows: # Authenticated user, with session name = email = None for key, value in rows: if key == 'name': name = value elif key == 'email': email = value if name or email: return author, name, email else: return author, None, None # Assume anonymous user from this point on if '@' in author: # Likely an email address return 'anonymous', None, author # See if there is a default domain domain = self.config.get('notification', 'smtp_default_domain') if domain and ' ' not in author: return author, None, author+'@'+domain return 'anonymous', author, None PKª©°8áßßhackergotchi/web_ui.pyc;ò ó/.Hc@sšdkZdkZdkTdklZdklZlZdkl Z l Z dk l Z dk lZdklZdklZd efd „ƒYZdS( N(s*(sITemplateStreamFilter(sITemplateProvidersadd_stylesheet(sOptionsOrderedExtensionsOption(stag(s Transformer(sresource_filename(sIHackergotchiProvidersHackergotchiModulecBsltZdZeddeddƒZeeeƒe i de i ƒZ d„Z d„Zd„Zd „ZRS( s<A stream filter to add hackergotchi emblems to the timeline.s hackergotchis providerssdefaults;GravatarHackergotchiProvider, IdenticonHackergotchiProviders([^<]+?)\s+<([^>]+)>c sŠˆiidƒoodg‰ˆiiƒ} | iƒ‰h‰‡‡‡‡‡‡d†}|t dƒi |ƒO}tˆdƒn|SdS(Ns /timelineic sˆd}ˆdcd7<ˆd|diƒ}ˆi|ƒ}|tj o|\}}}n,ˆi |ˆƒ}|ˆ|<|\}}}xSˆiD]3}|iˆi|||ƒ}|tj oPq–q–Wˆiiddƒ}tid|dd |d dƒ}ti|iƒ|ƒSdS( Niiseventssauthors hackergotchis default.pngssrcsaltsHackergotchi for %ssclass_(s closure_statesnsdatasstripsauthorscachesgets user_infosNonesnamesemailsselfs _get_infoscursors providerssprovidersget_hackergotchisreqshrefschromestagsimgselms itertoolsschainsgeneratesstream( sstreamsnamesauthorsns user_infoselmshrefsprovidersemail(sreqs closure_statesselfscachescursorsdata(s9build/bdist.darwin-8.11.1-i386/egg/hackergotchi/web_ui.pysf#s$       s0//div[@id="content"]/dl/dt/a/span[@class="time"]shackergotchi/hackergotchi.css(sreqs path_infos startswiths closure_statesselfsenvs get_db_cnxsdbscursorscachesfsstreams Transformersfiltersadd_stylesheet( sselfsreqsmethodsfilenamesstreamsdatascachesfs closure_statesdbscursor((sselfsreqsdatas closure_statescachescursors9build/bdist.darwin-8.11.1-i386/egg/hackergotchi/web_ui.pys filter_streams  ccsdttdƒfVdS(Ns hackergotchishtdocs(sresource_filenames__name__(sself((s9build/bdist.darwin-8.11.1-i386/egg/hackergotchi/web_ui.pysget_htdocs_dirsFscCsgSdS(N((sself((s9build/bdist.darwin-8.11.1-i386/egg/hackergotchi/web_ui.pysget_templates_dirsIsc Css|djo|ttfSn|ii|ƒ}|o#d|idƒ|idƒfSn|id|dfƒ|i ƒ}|ot}} xB|D]:\}}|djo |}q”|djo |} q”q”W|p| o||| fSq|ttfSnd|jodt|fSn|iidd ƒ}|o d |jo|t|d|fSnd|tfSdS( Ns anonymousiisKSELECT name, value FROM session_attribute WHERE sid=%s AND authenticated=%ssnamesemails@s notificationssmtp_default_domains (sauthorsNonesselfsanon_resmatchsmdsgroupscursorsexecutesfetchallsrowssnamesemailskeysvaluesconfigsgetsdomain( sselfsauthorscursorsmdsrowssnamesdomainsvalueskeysemail((s9build/bdist.darwin-8.11.1-i386/egg/hackergotchi/web_ui.pys _get_infoNs2 #         (s__name__s __module__s__doc__sOrderedExtensionsOptionsIHackergotchiProviders providerss implementssITemplateStreamFiltersITemplateProvidersrescompilesUsanon_res filter_streamsget_htdocs_dirssget_templates_dirss _get_info(((s9build/bdist.darwin-8.11.1-i386/egg/hackergotchi/web_ui.pysHackergotchiModules     )  (s itertoolssres trac.cores trac.web.apisITemplateStreamFilterstrac.web.chromesITemplateProvidersadd_stylesheets trac.configsOptionsOrderedExtensionsOptionsgenshi.builderstagsgenshi.filters.transforms Transformers pkg_resourcessresource_filenameshackergotchi.apisIHackergotchiProviders ComponentsHackergotchiModule( s TransformersOptionsITemplateProvidersOrderedExtensionsOptionsadd_stylesheetsresource_filenamesres itertoolsstagsITemplateStreamFiltersIHackergotchiProvidersHackergotchiModule((s9build/bdist.darwin-8.11.1-i386/egg/hackergotchi/web_ui.pys?s       PK©°8®¼21bb$hackergotchi/htdocs/hackergotchi.cssimg.hackergotchi { width: 20px; height: 20px; display: block; float: left; clear: left }PKª©°8“×2¤EGG-INFO/dependency_links.txtPKª©°8â®,ÇŽŽ¤<EGG-INFO/entry_points.txtPKª©°8½‰±Ô  ¤EGG-INFO/PKG-INFOPKª©°8Ó‘š¤K EGG-INFO/requires.txtPKª©°8˜pc))¤‚ EGG-INFO/SOURCES.txtPKª©°8@Ÿp ¤Ý EGG-INFO/top_level.txtPKª©°8“×2¤EGG-INFO/zip-safePK©°8¤Nhackergotchi/__init__.pyPKª©°8á°Ú‹‹¤„hackergotchi/__init__.pycPK©°8e¨nn¤Fhackergotchi/api.pyPKª©°8•Œi00¤åhackergotchi/api.pycPK©°8W»9@@¤Ghackergotchi/identicon.pyPK©°8ŒÀ±ý ý ¤¾4hackergotchi/providers.pyPKª©°8a‹Ú*TT¤ò>hackergotchi/providers.pycPK©°8 ¤?‰çç¤~Ohackergotchi/web_ui.pyPKª©°8áßߤ™`hackergotchi/web_ui.pycPK©°8®¼21bb$¤­thackergotchi/htdocs/hackergotchi.cssPKšQu