Bitmessage TUI

#!/usr/bin/env python2
# -*- coding: UTF-8 -*-



''' This Bitmessage messenger TUI can run from any subdir unlike crappy  https://github.com/Bitmessage/PyBitmessage/blob/v0.6/src/bitmessagecli.py and    pybitmessage --curses '''

apistring = "http://user:password@localhost:8442/"      # put your real API user:password (look them up in keys.dat) in here and you are ready to go!

myEditor = '/usr/bin/mcedit' # or whatever

                                                        # that's it! all user settings above.




#### TODO: add self-hosted s–posting capability

import xmlrpclib
import json
import time
import datetime
import os
import subprocess
import readchar

def senddd(recepient, subject, message):
        subject = subject.encode('base64')
        message = message.encode('base64')
        ackData = api.sendMessage(recepient, recepient, subject, message)
        time.sleep(1)
        print 'The ackData is:', ackData


def AddressesList():
    addresesListJsn = json.loads(api.listAddresses())
    NumberAllAddrs = len(addresesListJsn['addresses'])
    for i in range(NumberAllAddrs):
        addrsLabel = addresesListJsn['addresses'][i]['label']
        addrsAddress = addresesListJsn['addresses'][i]['address']
        print addrsLabel.ljust(30), ' : ', addrsAddress


def clearScr():
    print '\x1b[2J' # popen clear


def getNameChan(addressChn):
    myAddreessList = api.listAddresses()
    jsnmyAddreessList = json.loads(myAddreessList)['addresses']
    for addressChan in jsnmyAddreessList:
        if addressChan['address'] == addressChn:
            result = addressChan['label']
            break
        else:
            result = addressChn
    return result


def todate(unixtime):
    return datetime.datetime.fromtimestamp(float(unixtime)).strftime('%Y-%m-%d %H:%M:%S')


def status():
    status = api.clientStatus()
    jsnStatus = json.loads(status)
    for key in jsnStatus.keys():
        print "%s -> %s" % (key, jsnStatus[key])


def countMsg():
    inboxMessages = json.loads(api.getAllInboxMessages())
    NumberAllMsgs = len(inboxMessages['inboxMessages'])
    NmbLstMsg     = NumberAllMsgs
    return NmbLstMsg


def reply(numMsg):
    inboxMessages = json.loads(api.getAllInboxMessages())
    clearScr()
    if numMsg >= countMsg():
        numMsg -= 1   #= numMsg - 1
    print 'REPLY MODE', str(numMsg)
    text = inboxMessages['inboxMessages'][numMsg]['message'].decode('base64')
    print type(text)
    print text
    strings = text.split('\n')
    with open('viewer.txt' , 'w') as textfile:
        for string in strings:
            textfile.write('> ' + string + '\n')
    subprocess.call([myEditor, 'viewer.txt'])
    clearScr()
    print 'REPLY MODE'
    with open('viewer.txt' , 'r') as textfile:
        newlines = textfile.read()
    print newlines
    while True:
        print '\033[1;35;40m'
        print '(S)end or    ANY KEY  for Cancel'
        print '\033[0;37;40m'
        myinput =  readchar.readchar()
        if myinput == 's':
            recipient = inboxMessages['inboxMessages'][numMsg]['toAddress']
            subject   = inboxMessages['inboxMessages'][numMsg]['subject'].decode('base64')
            if subject.find('Re:') != 0:
                subject = 'Re: ' + subject
            print 'recipient: ', recipient
            print 'subject', subject
            print newlines
            senddd(recipient, subject, newlines)
            break
        else:
            break
    readMsgs(numMsg)
    os.remove('viewer.txt')


def readMsgs(numMsg):
    inboxMessages = json.loads(api.getAllInboxMessages())
    NumberAllMsgs = len(inboxMessages['inboxMessages'])
    NmbLstMsg = numMsg
    if countMsg() > 0:
        if NmbLstMsg + 1 > countMsg():
            NmbLstMsg = countMsg() - 1
            print '\033[5;31;40m', 'The LAST message!', '\033[0;37;40m'
        print '\033[1;31;40m'
        print 'message', str(NmbLstMsg + 1), 'from', str(countMsg())
        print '\033[0;37;40m'
        idLastMsg = inboxMessages['inboxMessages'][NmbLstMsg]['msgid']
        ToAddress = inboxMessages['inboxMessages'][NmbLstMsg]['toAddress']
        Subject = inboxMessages['inboxMessages'][NmbLstMsg]['subject'].decode('base64')
        print 'msgid: ', inboxMessages['inboxMessages'][NmbLstMsg]['msgid']
        print 'fromAddress: ', '\033[1;37;40m', getNameChan(inboxMessages['inboxMessages'][NmbLstMsg]['fromAddress']), '\033[0;37;40m'
        print 'toAddress: ', '\033[1;37;40m', getNameChan(inboxMessages['inboxMessages'][NmbLstMsg]['toAddress']), '\033[0;37;40m'
        print 'receivedTime: ', todate(inboxMessages['inboxMessages'][NmbLstMsg]['receivedTime'])
        print 'read: ', inboxMessages['inboxMessages'][NmbLstMsg]['read']
        print 'subject: ', '\033[1;37;40m', inboxMessages['inboxMessages'][NmbLstMsg]['subject'].decode('base64'), '\033[0;37;40m'
        print 'message:\n',inboxMessages['inboxMessages'][NmbLstMsg]['message'].decode('base64')
        return [NmbLstMsg + 1, inboxMessages['inboxMessages'][NmbLstMsg]['msgid']]
    else:
        print '\033[5;31;40m'
        print 'NO messages'
        print '\033[0;37;40m'


def listMsgs():
    inboxMessages = json.loads(api.getAllInboxMessages())
    i = 0
    for message in inboxMessages['inboxMessages']:
        i += 1
        print str(i).rjust(3) + '.', todate(message['receivedTime']), getNameChan(message['toAddress']).ljust(20), message['subject'].decode('base64')


def printMenu(cnt):
    if countMsg() > cnt:
        print '\033[1;32;40m Total: ', str(countMsg()), ' messages', '(' + str(countMsg() - cnt) + ' new)'
    else:
        print '\033[1;32;40m Total: ', str(countMsg()), ' messages'
    print (" ~~  MAIN MENU  ~~ ")
    print '(A)dressesList   (S)tatus   (L)ist_messages   (R)ead & Write Bitmessages     (T)est   (V)acuum   (Q)uit \033[0;37;40m '
    return countMsg()


def printReadMenu():
    print '\033[1;33;40m    ~~    BM Read & Write  menu   ~~ '
    print '(P)rev (N)ext (R)eply (D)elete (Q)uit \033[0;37;40m '


def readMode(numMessage):
    if len(numMessage) > 1:
        try:
            needPosition = int(numMessage.split()[1]) - 1
        except:
            needPosition = 0
    else:
        needPosition = 0
    position = readMsgs(needPosition)
    printReadMenu()
    while True:
        myinput = readchar.readchar() # getch()  # raw_input()
        clearScr()
        if position == None:
            print 'No messages'
            break
        if myinput == 'q':
            break
        if myinput == 'n':
            if position[0] < countMsg():
                position = readMsgs(position[0])
            else:
                position = countMsg() - 1
        if myinput == '':
            if position[0] == countMsg():
                position = readMsgs(position[0])
            else:
                position = readMsgs(position[0])
        if myinput == 'r':
            position = readMsgs(position[0])
            reply(position[0])
        if myinput == 'p':
            if position[0] <= 1:
                position = readMsgs(0)
            else:
                position = readMsgs(position[0] - 2)
        if myinput == 'd':
            print 'I have to delete msgID', str(position[1])
            api.trashMessage(position[1])
            position[0] = position[0] - 1
            clearScr()
            position = readMsgs(position[0])
        printReadMenu()






def main():
    oldCount = printMenu(0)
    while True:
        myinput   = readchar.readchar()

        if True:
            if myinput == 'a':
                clearScr()
                AddressesList()
            if myinput == 'q':
                print 'need to Quit rite now see ya'
                break
            if myinput == 'l':
                listMsgs()
                #clearScr()
            if myinput =='r':
                clearScr()
                readMode(myinput)
            if myinput == 's':
                clearScr()
                print 'Status:'
                status()
            if myinput == 't':
                clearScr()
                getNameChan('general')
            if myinput == 'v':
                clearScr()
                print 'Clean Trash & Vacuum...'
                api.deleteAndVacuum()
        else:
            clearScr()
            print 'Status:'
            status()

        print '=' * 72
        oldCount = printMenu(oldCount)

if __name__ == "__main__":
    try:
     api  = xmlrpclib.ServerProxy(apistring)
     main()
    except: print("pyBitMessage is not running or API is disabled in keys.dat or whatever - this app cannot do anything, make sure pyBM & API is running then try again.")