#!/usr/bin/env python3
# -*- coding: ascii -*-

# A script dumping Instant message logs in a format similar to the copy-paste
# pretty-printer.

import sys, time
import operator
import instabot, id2time, scribe

# Provide a fancy __name__ for help display.
class msgid(id2time.MessageID): pass

# Return msglist sorted in the order it would be put into by the Web client.
# NOTE that messages' immediate parent might be missing.
def sort_threads(msglist):
    def dump(k):
        if k in index:
            ret.append(index[k])
        ids = [m['id'] for m in children.get(k, ())]
        ids.sort()
        for k in ids:
            dump(k)
    # Put messages into indexes
    index, children = {}, {}
    for m in msglist:
        index[m['id']] = m
        children.setdefault(m.get('parent'), []).append(m)
    roots = [k for k in children if k is not None and
             (k not in index or index[k].get('parent') is None)]
    roots.sort()
    # Extract messages again
    ret = []
    for k in roots: dump(k)
    return ret

class LogFormatter:
    def __init__(self, detail=0, mono=False):
        self.detail = detail
        self.mono = mono

    # Return a plain-text representation of msg.
    # NOTE that there is no trailing newline.
    def format_message(self, msg, indent='', first_indent=None):
        if first_indent is None: first_indent = indent
        prefix, lines = '<%s> ' % msg['nick'], msg['text'].split('\n')
        align = ' ' * len(prefix) if self.mono else '  '
        return '\n'.join((first_indent + prefix if n == 0 else
                          indent + align) + l for n, l in enumerate(lines))

    # Return a plain-text representation of the messages in msglist.
    # NOTE that msglist is used in the order given, and there is no trailing
    #      newline.
    def format_logs(self, msglist):
        def prefix(msgid):
            if not self.detail:
                return ''
            elif msgid is None:
                return '[---------- --------  ] '
            else:
                return time.strftime('[%Y-%m-%d %H:%M:%S Z] ',
                                     time.gmtime(int(msgid, 16) // 1024000))
        stack, res = [], []
        for m in msglist:
            while stack and stack[-1] != m.get('parent'): stack.pop()
            if m.get('parent') is not None and not stack:
                res.append(prefix(None) + '| ' * len(stack) + '...')
                stack.append(m['parent'])
            p, ind = prefix(m['id']), '| ' * len(stack)
            res.append(self.format_message(m, ' ' * len(p) + ind, p + ind))
            stack.append(m['id'])
        return '\n'.join(res)

def main():
    # Parse command line
    p = instabot.OptionParser(sys.argv[0])
    p.help_action()
    p.option('from', short='f', type=msgid,
             help='Minimal (earliest) message ID to output')
    p.option('to', short='t', type=msgid,
             help='Maximal (latest) message ID to output')
    p.option('length', short='l', type=int,
             help='Maximal amount of messages to output')
    p.flag('detail', short='d',
           help='Prepend date-times to messages')
    p.flag('mono', short='m',
           help='Optimize indents for monospaced display')
    p.argument('msgdb', help='Database file to dump logs from')
    p.parse(sys.argv[1:])
    # Retrieve messages
    lfrom, lto, amount = p.get('from', 'to', 'length')
    if lfrom is not None: lfrom = lfrom.format_id()
    if lto is not None: lto = lto.format_id()
    db = scribe.LogDBSQLite(p.get('msgdb'))
    db.init()
    try:
        messages = db.query(lfrom, lto, amount)
    finally:
        db.close()
    # Format messages
    fmt = LogFormatter(detail=p.get('detail'), mono=p.get('mono'))
    print (fmt.format_logs(sort_threads(messages)))

if __name__ == '__main__': main()

@@ -1875,20 +1875,16 @@
      if 
-not 
 self.det
@@ -1890,36 +1890,19 @@
 tail
+ == 1
 :%0A    
-            return ''%0A
     
@@ -1909,18 +1909,16 @@
         
-el
 if msgid
@@ -1927,16 +1927,20 @@
 s None:%0A
+    
         
@@ -1993,16 +1993,20 @@
         
+    
 else:%0A  
@@ -2015,24 +2015,28 @@
             
+ 
+   
 return time.
@@ -2094,29 +2094,16 @@
         
-             
 time.gmt
@@ -2134,16 +2134,292 @@
 24000))%0A
+            elif self.detail == 2:%0A                if msgid is None:%0A                    return '%5B---------- ------------    -----%5D '%0A                else:%0A                    return '%5B%25s%5D ' %25 id2time.MessageID(msgid).format_time()%0A            else:%0A                return ''%0A
         
@@ -3318,16 +3318,19 @@
   p.flag
+_ex
 ('detail
@@ -3342,17 +3342,52 @@
 ort='d',
-%0A
+ default=0, value=1, accum=True,%0A   
         

Allow full message ID-s in log dumps