home *** CD-ROM | disk | FTP | other *** search
- # Source Generated with Decompyle++
- # File: in.pyo (Python 2.6)
-
- from __future__ import with_statement
- import sys
- import re
- import os.path as os
- from operator import itemgetter
- import config
- from common import profile
- from datetime import datetime
- from util import soupify, Storage as S, tail, boolify
- from util.primitives.misc import fromutc
- from path import path
- from digsby import iswidget
- from traceback import print_exc
- from logging import getLogger
- log = getLogger('logger')
- log_info = log.info
- from common.message import Message
- from protocolmeta import SERVICE_MAP
- import lxml.html as lxml
- from util.htmlutils import render_contents
- if config.platform == 'win':
- import blist
- fastFind = blist.findFiles
- else:
- fastFind = None
-
- def get_default_logging_dir():
- lp = localprefs
- import prefs
- localprefs = lp()
- return path(localprefs['chatlogdir']) / DEFAULT_LOG_DIR_NAME / profile.username
-
- DEFAULT_LOG_DIR_NAME = u'Digsby Logs'
- LOGSIZE_PARSE_LIMIT = 1024 * 15
-
- def buddy_path(account, buddy):
- return path(account.name).joinpath(account.username, buddy.name + '_' + buddy.service)
-
- GROUP_CHAT_DIRNAME = 'Group Chats'
-
- def chat_path(account, convo):
- return path(account.name).joinpath(account.username, GROUP_CHAT_DIRNAME)
-
-
- def message_timestamp_id(dt):
- return datetime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
-
- message_timestamp_fmt = '%Y-%m-%d %H:%M:%S'
- message_timestamp_fmt_OLD = '%Y-%m-%d %H:%M'
- filename_format_re = re.compile('\\d{4}-\\d{2}-\\d{2}\\..*')
- message_shorttime_fmt = '%H:%M:%S %p'
- html_header = u'<?xml version="1.0" encoding="UTF-8"?>\n <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\n "http://www.w3.org/TR/html4/strict.dtd">\n<HTML>\n <HEAD>\n <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />\n <TITLE>%(title)s</TITLE>\n <style>\n .buddy { font-weight: bold; }\n .buddy:after { content: ":" }\n\n .time {\n color: #a0a0a0;\n font-family: monaco, courier new, monospace;\n font-size: 75%%;\n }\n .time:hover { color: black; }\n\n .outgoing { background-color: #efefef; }\n .incoming { background-color: #ffffff; }\n </style>\n <script type="text/javascript">\n//<![CDATA[\n function convert_time(datetime){\n var dt = datetime.split(" ");\n var date = dt[0].split("-");\n var time = dt[1].split(":");\n var t = new Date;\n t.setUTCFullYear(date[0],date[1]-1,date[2]);\n t.setUTCHours(time[0],time[1],time[2]);\n return t.toLocaleTimeString();\n }\n\n function utc_to_local(){\n var node;\n for (var i=0; i<document.body.childNodes.length; i++){\n node = document.body.childNodes[i];\n if(node.nodeType == 1 && node.className.match("message")){\n var showtime = convert_time(node.getAttribute(\'timestamp\'));\n var newspan = \'<span class="time">(\' + showtime + \') </span>\';\n var msgnode = node;\n msgnode.innerHTML = newspan + msgnode.innerHTML;\n }\n }\n }\n//]]>\n </script>\n </HEAD>\n <BODY onload="utc_to_local()">\n'
- html_log_entry = '<div class="%(type)s message" auto="%(auto)s" timestamp="%(timestamp)s"><span class="buddy">%(buddy)s</span> <span class="msgcontent">%(message)s</span></div>\n'
-
- class Logger(object):
-
- def __init__(self, output_dir = None, log_ims = True, log_chats = True, log_widgets = False):
- if output_dir is None:
- output_dir = get_default_logging_dir()
-
- self.OutputDir = output_dir
- self.OutputType = 'html'
- self.LogChats = log_chats
- self.LogIMs = log_ims
- self.LogWidgets = log_widgets
-
-
- def calculate_log_sizes(self):
- log_sizes = blist.getLogSizes(self.OutputDir)
- return sorted((lambda .0: for name, size in .0:
- (name, size))(log_sizes.iteritems()), key = itemgetter(1), reverse = True)
-
-
- def on_message(self, messageobj = None, convo = None, **opts):
- if not self.should_log_message(messageobj):
- return None
- messageobj = modify_message(messageobj)
- output = self.generate_output(messageobj)
- written_size = self.write_output(output, messageobj)
-
- try:
- buddy = messageobj.conversation.buddy
- except AttributeError:
- self.should_log_message(messageobj)
- self.should_log_message(messageobj)
- except:
- self.should_log_message(messageobj)
-
-
- try:
- buddy.increase_log_size(written_size)
- except AttributeError:
- self.should_log_message(messageobj)
- self.should_log_message(messageobj)
- import traceback
- traceback.print_exc()
- except:
- self.should_log_message(messageobj)
-
-
-
- def should_log_message(self, messageobj):
- if messageobj is None:
- return False
- convo = messageobj.conversation
- if convo.ischat and not (self.LogChats):
- return False
- if not (convo.ischat) and not (self.LogIMs):
- return False
- if not (self.LogWidgets) and iswidget(convo.buddy):
- return False
- if messageobj.buddy is None:
- return False
- if not messageobj.buddy.protocol.should_log(messageobj):
- return False
- return True
-
-
- def history_for(self, account, buddy):
- log.debug('history_for(%r, %r)', account, buddy)
- files = self.logfiles_for(account, buddy)
- log.debug('%d %s log files found', len(files), self.OutputType)
- if not files:
- return iter([])
- if fastFind is None:
- files.sort(reverse = True)
-
- return history_from_files(files, 'html')
-
-
- def history_for_safe(self, account, buddy):
-
- try:
- hist = self.history_for(account, buddy)
- except Exception:
- print_exc()
- hist = iter([])
-
- return hist
-
-
- def logsize(self, account, buddy):
- return sum((lambda .0: for f in .0:
- f.size)(self.logfiles_for(account, buddy)))
-
-
- def logsize_for_nameservice(self, name, service):
- glob_str = ''.join(('*/', name, '_', service, '/*.html'))
- outpath = self.OutputDir
- types = SERVICE_MAP.get(service, [
- service])
- total = 0
- for accttype in types:
- logpath = outpath / accttype
- total += sum((lambda .0: for f in .0:
- f.size)(logpath.glob(glob_str)))
-
- return total
-
-
- def logfiles_for(self, account, buddy):
- global fastFind
- logdir = self.pathfor(account, buddy)
- if not logdir.isdir():
- return []
- if fastFind is not None:
- pathjoin = os.path.join
-
- try:
- wildcard = pathjoin(logdir, '*-*-*.html')
- return [ pathjoin(logdir, p) for p in fastFind(wildcard) ]
- except Exception:
- logdir.isdir()
- logdir.isdir()
- print_exc()
- fastFind = None
- except:
- logdir.isdir()<EXCEPTION MATCH>Exception
-
-
- logdir.isdir()
- return list((lambda .0: for f in .0:
- if filename_format_re.match(f.name):
- fcontinue)(logdir.files('*.' + self.OutputType)))
-
-
- def pathfor(self, account, buddy):
- return self.OutputDir.joinpath(buddy_path(account, buddy))
-
-
- def set_outputdir(self, val):
- self.output_dir = path(val)
-
-
- def get_outputdir(self):
- return self.output_dir
-
- OutputDir = property(get_outputdir, set_outputdir, doc = 'where to write logs')
-
- def walk_group_chats(self):
- for service in path(self.OutputDir).dirs():
- for account in service.dirs():
- group_chat_dir = account / GROUP_CHAT_DIRNAME
- if group_chat_dir.isdir():
- for chat_file in group_chat_dir.files():
- filename = chat_file.namebase
-
- try:
- if filename.count('-') == 2:
- time = datetime.strptime(filename, chat_time_format)
- roomname = None
- else:
- (time_part, roomname) = filename.split(' - ', 1)
- time = datetime.strptime(time_part, chat_time_format)
- if isinstance(roomname, str):
- roomname = roomname.decode('filesys')
- except ValueError:
- continue
- except Exception:
- print_exc()
- continue
- else:
- yield dict(time = time, service = service.name, file = chat_file, roomname = roomname)
- continue
- continue
- continue
- return None
-
-
-
- def get_path_for_chat(self, chat):
- pathdir = path(self.OutputDir) / chat_path(chat.protocol, chat)
- if chat.chat_room_name:
- for f in pathdir.files('*.html'):
- name = f.namebase
- if 'T' in name:
- day_part = name.split('T')[0]
-
- try:
- dt = datetime.strptime(day_part, chat_time_category)
- except ValueError:
- pass
-
- if fromutc(chat.start_time_utc).date() == dt.date():
-
- try:
- (time_part, roomname) = name.split(' - ', 1)
- except ValueError:
- pass
-
- if roomname == chat.chat_room_name:
- return f
-
- fromutc(chat.start_time_utc).date() == dt.date()
-
-
- return pathdir / (convo_time_filename(chat) + '.' + self.OutputType)
-
-
- def write_output(self, output, messageobj):
- convo = messageobj.conversation
- proto = convo.protocol
- if convo.ischat:
- p = self.get_path_for_chat(convo)
- else:
- datefilename = fromutc(messageobj.timestamp).date().isoformat()
- pathelems = (buddy_path(proto, convo.buddy), datefilename)
- p = path(path(self.OutputDir).joinpath(*pathelems) + '.' + self.OutputType)
- if not p.parent.isdir():
-
- try:
- p.parent.makedirs()
- except WindowsError:
- e = None
- if e.winerror == 183:
- pass
- else:
- raise
- e.winerror == 183
-
-
- None<EXCEPTION MATCH>WindowsError
- written_size = 0
- if not p.isfile():
- header = globals()['generate_header_' + self.OutputType](messageobj, self.output_encoding)
- written_size += len(header)
- p.write_bytes(header)
-
- written_size += len(output)
- p.write_bytes(output, append = p.isfile())
- return written_size
-
-
- def generate_output(self, messageobj):
- return globals()['generate_output_' + self.OutputType](messageobj, self.output_encoding)
-
- output_encoding = 'utf-8'
-
-
- def generate_header_html(messageobj, encoding):
- c = messageobj.conversation
- datefmt = messageobj.timestamp.date().isoformat()
- if c.ischat:
- title = 'Chat in %s on %s' % (c.name, datefmt)
- else:
- title = 'IM Logs with %s on %s' % (c.buddy.name, datefmt)
- return (html_header % dict(title = title.encode('xml'))).encode(encoding, 'replace')
-
-
- def generate_output_html(m, encoding = 'utf-8'):
- return (None % html_log_entry(dict = 'buddy' if m.buddy is not None else '', timestamp = m.timestamp.strftime(message_timestamp_fmt), message = m.message, type = m.type, auto = getattr(m, 'auto', False))).encode(encoding, 'replace')
-
- class_buddy = {
- 'class': 'buddy' }
- class_message = {
- 'class': 'message' }
- class_msgcontent = {
- 'class': 'msgcontent' }
-
- def parse_html_lxml(html):
- messages = []
- doc = lxml.html.document_fromstring(html, parser = lxmlparser())
- for div in doc.xpath('//html/body/div'):
-
- try:
- message_type = div.attrib.get('class', '')
- if 'message' not in message_type:
- continue
-
- message_type = message_type.replace('message', '').strip()
- if message_type not in ('incoming', 'outgoing'):
- continue
-
- buddyname = div.find_class('buddy')[0].text
- timestamp = div.attrib.get('timestamp')
- if timestamp is not None:
- timestamp = parse_timestamp(timestamp)
-
- message = render_contents(div.find_class('msgcontent')[0])
- auto = boolify(div.attrib.get('auto', 'false'))
- except Exception:
- print_exc()
- continue
-
- messages.append(Message(buddy = S(name = buddyname), timestamp = timestamp, message = message, type = message_type, auto = auto, has_autotext = auto))
-
- return messages
-
- _lxmlparser = None
-
- def lxmlparser():
- global _lxmlparser
- if _lxmlparser is None:
- _lxmlparser = lxml.html.HTMLParser(encoding = 'utf-8')
-
- return _lxmlparser
-
-
- def parse_html_slow(html):
- html = html.decode('utf-8', 'ignore')
- soup = soupify(html, markupMassage = ((br_re, (lambda m: '<br />')),))
- messages = []
- strptime = datetime.strptime
- for div in soup.findAll(message_divs):
-
- try:
- buddyname = div.findAll('span', class_buddy)[0].renderContents(None)
- timestamp = parse_timestamp(div['timestamp'])
- message = div.findAll('span', class_msgcontent)[0].renderContents(None)
- type = div['class'].replace('message', '').strip()
- auto = boolify(div.get('auto', 'false'))
- except Exception:
- print_exc()
- continue
-
- messages.append(Message(buddy = S(name = buddyname), timestamp = timestamp, message = message, type = type, auto = auto))
-
- log_info('parse_html_slow with %d bytes returning %d messages', len(html), len(messages))
- return messages
-
-
- def message_divs(tag):
- if tag.name == 'div':
- pass
- return 'message' in dict(tag.attrs).get('class', '')
-
- show_logparse_tracebacks = True
-
- def parse_html(html):
- global show_logparse_tracebacks
- if sys.platform == 'darwin':
- messages = parse_html_slow(html)
- else:
-
- try:
- messages = parse_html_lxml(html)
- except Exception:
- if __debug__ or show_logparse_tracebacks:
- print_exc()
- show_logparse_tracebacks = False
-
- messages = parse_html_slow(html)
- log_info('parsed slow: got %d messages', len(messages))
-
- return messages
-
-
- def parse_timestamp(timestamp):
-
- try:
- return datetime.strptime(timestamp, message_timestamp_fmt)
- except Exception:
- return datetime.strptime(timestamp, message_timestamp_fmt_OLD)
-
-
-
- def history_from_files(files, logtype = 'html'):
- parse = globals()['parse_' + logtype]
- for logfile in files:
-
- try:
- bytes = tail(logfile, LOGSIZE_PARSE_LIMIT)
- except Exception:
- print_exc()
- continue
-
- for msg in reversed(parse(bytes)):
- yield msg
-
- if len(bytes) < logfile.size:
- break
- continue
-
-
- chat_time_format = '%Y-%m-%dT%H.%M.%S'
- chat_time_category = '%Y-%m-%d'
-
- def convo_time_filename(convo):
- time_part = fromutc(convo.start_time_utc).replace(microsecond = 0).strftime(chat_time_format)
- room_name = convo.chat_room_name
- if room_name:
- return '%s - %s' % (time_part, room_name)
- return time_part
-
- import config
- USE_LXML = config.platform == 'win'
- if USE_LXML:
- from util.htmlutils import to_xhtml
- else:
-
- to_xhtml = lambda s: s
-
- def modify_message(msgobj):
- msg = getattr(msgobj, 'message', None)
- if msg is not None:
- msgobj.message = to_xhtml(msg)
-
- return msgobj
-
- import re
- real_br = '<br />'
- br_re = re.compile('<br\\s*/?>', re.IGNORECASE)
-
- brfix = lambda s: br_re.sub(real_br, s)
-