#!/usr/bin/env python # -*- coding: latin1 -*- import sys import time import re import getopt import errno from string import * import curses.ascii import curses.wrapper import os import signal import socket import shutil import struct import fcntl import termios import urllib import commands from __init__ import VERSION import MSN import ui ### Options ##### SEND_TYPING= 1 SHOW_INVITED= 1 AUTO_LIST= 1 AUTO_LIST_ON_0= 0 AUTO_LIST_ON_0_CLEAR= 0 AUTO_LIST_LAST_MOVE= 0 BEEP_ON_MSG= 0 BEEP_ON_NEW_CHAT= 0 MAX_NAME_SIZE= 0 TIMESTAMP= 1 TIMESTAMP_FORMAT= '[%H:%M]' DELETE_KEY= -1 # keycode of the BACKSPACE key in your system MONOCHROME= 0 CHAT_TITLE= 'user' CHAT_TITLE_USE_ALIAS= 1 PING_TIME= 0 IDLE_TIME= 0 IDLE_STATE= 'IDL' AUTO_UNIDLE= 0 AUTO_ADD_TO_ALLOWED= 1 EXEC_ON_MSG= '' EXEC_ON_NEW_CHAT= '' EXEC_ON_FILE_RECV_BEGIN= '' EXEC_ON_FILE_RECV_END= '' EXEC_ON_PAL_STATE_NLN= '' EXEC_ON_PAL_STATE_CHANGE= '' EXEC_ON_PAL_CONNECTED= '' EXEC_ON_PAL_DISCONNECTED= '' ################# PROG_NAME= 'Pebrot' EXE_NAME= 'pebrot.py' INIT_STATE= 'NLN' CONFIG_DIR= os.getenv('HOME') + '/.pebrot/' CONFIG_FILE= CONFIG_DIR + 'pebrotrc' LOGO_FILE= sys.prefix + '/share/doc/pebrot/logos/pebrot.txt' MAIN_LOG_DIR= CONFIG_DIR + 'logs/' MSN.DEBUG= 0 MSN.VERBOSE= 1 LISTLINE= '----------%s----------' IP_URL= 'http://simple.showmyip.com' USER='' PASSWD='' HELPMSG= _(r'''Commands begin with '\' or '/', everything else are messages. \number Create chat to user with this number. \c Close active chat. tab Change active chat. \i number Invite user to active chat. \l List connected users. \l list Show requested list. list must be one of the following: fl: Forward list, users on your contact list rl: Reverse list, users who have you on their contact list al: Allow list, users allowed to see your status bl: Block list, users not allowed to see your status \u List users on active chat. \n Change nick. \a user Add user to buddy list. \b user Block user. \r user Remove user from buddy list. \o user Open chat window with a user not on your buddy list. \s state Change state. state must be one of the following: nln: Online fln: Offline hdn: Appear Offline idl: Idle awy: Away bsy: Busy brb: Be Right Back phn: On the Phone lun: Out to Lunch \f filePath Send file. \fa Accept file for download. \fr pos Reject file for download. pos specifies its position on chat bar (1,2,3....). Can input several numbers at once, just separate them with spaces. If executed without any pos just rejects all file downloads \fc pos Cancel file send. pos works like in '\fr' \! Execute shell command. \e Erase chat window contents. \d file Dump chat text to file. If no file is supplied it will use a temporal file name. Ctrl-l Redraw screen. \h \? This help. \q Ctrl-d Quit.''') USAGE= _(r'''%s - version %s usage: %s [-d] [-m] [-u user] [-s state] -d debug output -u user log in with alternate user -m monochrome. Don't use colors -s state initial state. state must be one of the following: NLN: Online FLN: Offline HDN: Appear Offline IDL: Idle AWY: Away BSY: Busy BRB: Be Right Back PHN: On the Phone LUN: Out to Lunch ''') % (PROG_NAME, VERSION, EXE_NAME) VER_MSG= _('''Version - %s Enter \h for help.\n\n''') % VERSION IMG_COLORS= { 'R': 'RED', 'G': 'GREEN', 'Y': 'YELLOW', 'W': 'WHITE', 'B': 'BLUE', 'C': 'CYAN', 'M': 'MAGENTA', } COLORABLE= [ 'DEFAULT', 'INFO', 'INFO_DELIM', 'ERROR', 'ERROR_DELIM', 'CHAT_BAR', 'CHAT_BAR_NAME', 'CHAT_BAR_NAME_TYPING', 'STATE_BAR', 'STATE_BAR_WRITTEN', 'STATE_BAR_NOT_WRITTEN', 'MY_NAME', 'OTHERS_NAME', 'LIST_DELIM', 'LIST_NUM', 'LIST_PASS', 'LIST_STATE', 'LIST_PARENS', 'LIST_NAME', 'TIMESTAMP' ] def onlyCreated( x ): return x.created class TextMSN(MSN.Session): def __init__( self, stdscr ): MSN.Session.__init__( self ) self.stdscr= stdscr self.usersList= {} self.firstKeyPress= 1 self.altPressed= 0 self.history= [] self.historyIndex= 0 self.accounts= {} self.COLORS= {} self.alias= {} self.lastPing= 0 self.lastStartedTyping= time.time() self.typingCommand= 0 self.needToSendTypingNotif= 1 self.needToResetTypingNotif= 0 self.idle= 0 # To handle terminal resize signal.signal(signal.SIGWINCH, self.handleResize) # To handle defunct childs signal.signal(signal.SIGCHLD, self.handleDefunct) curses.use_env( 1 ) # Lets add chat 0 (the one in window 0 ) chat= MSN.Chat( self ) chat.ready= 0 self.chats['']= chat self.sortedChats= [chat] self.activeChat= chat # set alarm handler for typing notifications signal.signal( signal.SIGALRM, self.alarmHandler ) self.lastPing= time.time() self.defineColors() if curses.has_colors() and not MONOCHROME: self.setColors( curses.COLOR_BLACK ) self.defaultColors() (self.maxY, self.maxX)= self.stdscr.getmaxyx() #self.StateBar= curses.newwin( 1, self.maxX, 0, 0 ) self.StateBar= ui.MyScrollWin( 1, self.maxX, 0, 0 ) self.Main= ui.MyWinStack( self.maxY-4, self.maxX, 1, 0 ) self.ChatBar= ui.MyScrollWin( 1, self.maxX, self.maxY-3, 0 ) self.Text= ui.MyInputWin( 2, self.maxX, self.maxY-2, 0 ) #print >>sys.stderr, 'COLORS:', str(self.COLORS) #print >>sys.stderr, 'DEFAULT:', self.COLORS['DEFAULT'] #Add initial chat self.Main.newWin( chat ) def defineColors( self ): colors= ['WHITE', 'BLUE', 'RED', 'CYAN', 'YELLOW', 'GREEN', 'MAGENTA' ] for f in (COLORABLE + colors): self.COLORS[f]= '' def defaultColors( self ): self.COLORS['DEFAULT']= self.COLORS['WHITE'] self.COLORS['INFO']= self.COLORS['CYAN'] self.COLORS['INFO_DELIM']= self.COLORS['RED'] self.COLORS['ERROR']= self.COLORS['YELLOW'] self.COLORS['ERROR_DELIM']= self.COLORS['RED'] self.COLORS['CHAT_BAR']= self.COLORS['BLUE'] self.COLORS['CHAT_BAR_NAME']= self.COLORS['BLUE'] self.COLORS['CHAT_BAR_NAME_TYPING']= self.COLORS['GREEN'] self.COLORS['STATE_BAR']= self.COLORS['BLUE'] self.COLORS['STATE_BAR_WRITTEN']= self.COLORS['YELLOW'] self.COLORS['STATE_BAR_NOT_WRITTEN']= self.COLORS['BLUE'] self.COLORS['MY_NAME']= self.COLORS['BLUE'] self.COLORS['OTHERS_NAME']= self.COLORS['RED'] self.COLORS['LIST_DELIM']= self.COLORS['WHITE'] self.COLORS['LIST_NUM']= self.COLORS['RED'] self.COLORS['LIST_PASS']= self.COLORS['BLUE'] self.COLORS['LIST_STATE']= self.COLORS['GREEN'] self.COLORS['LIST_PARENS']= self.COLORS['WHITE'] self.COLORS['LIST_NAME']= self.COLORS['WHITE'] self.COLORS['TIMESTAMP']= self.COLORS['WHITE'] def initBarColors( self ): if curses.has_colors() and not MONOCHROME: self.ChatBar.attrset( self.COLORS['CHAT_BAR'] ) self.StateBar.attrset( self.COLORS['STATE_BAR'] ) self.Text.setAttr( self.COLORS['DEFAULT'] ) #self.showChatBar() #self.showStateBar() def termSize( self, ): try: return int(os.environ["LINES"]), int(os.environ["COLUMNS"]) except KeyError: height, width = struct.unpack( "hhhh", fcntl.ioctl(0, termios.TIOCGWINSZ ,"\000"*8))[0:2] if not height: return 25, 80 return height, width def reinitCurses( self ): while 1: try: curses.endwin(); self.stdscr= curses.initscr() break except curses.error, e: time.sleep(1) def handleDefunct( self, a, b ): # Let's clean those zombie sons >:-] try: os.wait() except OSError: pass def handleResize( self, a, b ): #stdscr= curses.initscr() #self.stdscr= stdscr #size= self.termSize() #(self.maxY, self.maxX)= size[0], size[1] self.reinitCurses() try: self.maxY, self.maxX= self.stdscr.getmaxyx() #print >>sys.stderr, 'maxX= %d, maxY= %d' % (self.maxX, self.maxY) if self.maxY-3 >= 6: self.StateBar.resize( 1, self.maxX, 0, 0 ) self.Main.resize( self.maxY-4, self.maxX, 1, 0 ) self.ChatBar.resize( 1, self.maxX, self.maxY-3, 0 ) self.Text.resize( 2, self.maxX, self.maxY-2, 0 ) self.refreshAll() except curses.error, e: pass def setColors( self, background ): try: import usedefault usedefault.use_default_colors() back= -1 except ImportError: back= background curses.init_pair( 1, curses.COLOR_WHITE, back ) curses.init_pair( 2, curses.COLOR_BLUE, back ) curses.init_pair( 3, curses.COLOR_RED, back ) curses.init_pair( 4, curses.COLOR_CYAN, back ) curses.init_pair( 5, curses.COLOR_YELLOW, back ) curses.init_pair( 6, curses.COLOR_GREEN, back ) curses.init_pair( 7, curses.COLOR_MAGENTA, back ) curses.init_pair( 8, curses.COLOR_MAGENTA, back ) self.COLORS['WHITE']= curses.color_pair( 1 ) self.COLORS['BLUE']= curses.color_pair( 2 ) self.COLORS['RED']= curses.color_pair( 3 ) self.COLORS['CYAN']= curses.color_pair( 4 ) self.COLORS['YELLOW']= curses.color_pair( 5 ) self.COLORS['GREEN']= curses.color_pair( 6 ) self.COLORS['MAGENTA']= curses.color_pair( 7 ) #self.stdscr.touchwin() #self.stdscr.refresh() #self.stdscr.bkgdset( curses.color_pair(8) ) def pActive( self, text, color= None, refresh= 1 ): #(y,x)= curses.getsyx() if color == None: color= 'DEFAULT' if curses.has_colors() and not MONOCHROME: self.Main.addstr( self.activeChat, '%s' % (text) , self.COLORS[color] ) else: self.Main.addstr( self.activeChat, '%s' % (text) ) #curses.setsyx( y, x ) if refresh: self.refreshWin() self.Text.refresh() def pWin( self, chat, text, color= None, refresh= 1 ): if color == None: color= 'DEFAULT' try: if curses.has_colors() and not MONOCHROME: self.Main.addstr( chat, text, self.COLORS[color] ) else: self.Main.addstr( chat, text ) except TypeError, e: self.pError( _('Type error when displaying text: %s') % str(e) ) #curses.setsyx( y, x ) if refresh and self.activeChat == chat: #self.pDebug( 'REFRESH' ) self.refreshWin( chat ) self.Text.refresh() def pVerbose( self, level, st ): if level >= MSN.VERBOSE: self.pWin( self.activeChat, st ) def refreshWin( self, chat= None ): if not chat: chat= self.activeChat self.Main.refresh( chat ) def refreshAll( self ): #self.stdscr.refresh() #print >>sys.stderr, '-- STATE BAR --' #self.initBarColors() self.showStateBar() #print >>sys.stderr, '-- MAIN --' self.refreshWin() #print >>sys.stderr, '-- CHAT BAR --' self.showChatBar() #print >>sys.stderr, '-- TEXT --' self.Text.refresh() def addHistory( self, line ): self.history.append( line ) self.historyIndex= len( self.history ) def showPrevHistory( self ): if self.historyIndex > 0: self.historyIndex= self.historyIndex - 1 self.Text.erase() self.Text.addstr( self.history[self.historyIndex] ) self.Text.refresh() def showNextHistory( self ): if self.historyIndex < len( self.history ) - 1: self.historyIndex= self.historyIndex + 1 self.Text.erase() self.Text.addstr( self.history[self.historyIndex] ) self.Text.refresh() def pInfo( self, text, chat= None ): if not chat: chat= self.activeChat self.pWin( chat, '>> ', 'INFO_DELIM' ) self.pWin( chat, text, 'INFO' ) self.pWin( chat, ' <<\n', 'INFO_DELIM' ) def aliasOrName( self, passport ): if self.alias.has_key( passport ): name= self.alias[passport] else: try: name= self.usersList[passport]['name'] except KeyError: name= passport return name def pChat( self, chat, passport, msg ): if passport == self.user: name= self.name color= 'MY_NAME' chat.written= 0 else: name= self.aliasOrName( passport ) color= 'OTHERS_NAME' if BEEP_ON_MSG: curses.beep() if EXEC_ON_MSG: escMsg= re.escape( msg ) escName= re.escape( name ) self.execAction( EXEC_ON_MSG, {'$passport':passport , '$user':escName, '$msg':escMsg} ) chat.written= 1 chat.activateUser( passport ) if MAX_NAME_SIZE > 0: name= name[:MAX_NAME_SIZE] if TIMESTAMP: self.pWin( chat, '%s ' % time.strftime(TIMESTAMP_FORMAT , time.localtime()), 'TIMESTAMP' ) self.pWin( chat, '%s: ' % name, color ) self.pWin( chat, '%s\n' % msg ) chat.logText( '%s %s: %s' % (time.strftime('[%H:%M:%S]' , time.localtime()) , passport, msg) ) def pError( self, text, chat= None ): if not chat: chat= self.activeChat self.pWin( chat, '>> ', 'ERROR_DELIM' ) self.pWin( chat, text, 'ERROR' ) self.pWin( chat, ' <<\n', 'ERROR_DELIM' ) def fatalError( self, msg ): #curses.endwin() #print >>sys.stderr, 'Error:', msg #self.refreshWin() #sys.exit() raise _('Error: ') + msg def usage( self ): print sys.stderr.write( USAGE + '\n' ) def parseArgs( self ): global USER, INIT_STATE, MONOCHROME args= split( '-m -d -u -s' ) try: opts, args= getopt.getopt( sys.argv[1:], 'dmu:s:', args ) except getopt.GetoptError: curses.endwin() self.usage() #self.refreshWin() sys.exit( 1 ) for o, a in opts: if o == '-d': MSN.DEBUG= 1 if o == '-m': MONOCHROME= 1 if o == '-s': a= upper( a ) if not MSN.STATES.has_key( a ): curses.endwin() self.usage() #self.refreshWin() sys.exit( 1 ) INIT_STATE= a if o == '-u': USER= a def readConfig( self, file ): global USER, PASSWD, INIT_STATE, SHOW_INVITED, AUTO_LIST, BEEP_ON_MSG \ , BEEP_ON_NEW_CHAT, SEND_TYPING, MAX_NAME_SIZE \ , AUTO_LIST_ON_0, AUTO_LIST_ON_0_CLEAR, AUTO_LIST_LAST_MOVE \ , TIMESTAMP, TIMESTAMP_FORMAT, DELETE_KEY, CHAT_TITLE \ , CHAT_TITLE_USE_ALIAS, PING_TIME, IDLE_TIME, IDLE_STATE \ , AUTO_UNIDLE, LOGO_FILE, EXEC_ON_MSG, EXEC_ON_NEW_CHAT \ , EXEC_ON_FILE_RECV_BEGIN, EXEC_ON_FILE_RECV_END \ , EXEC_ON_PAL_STATE_NLN, EXEC_ON_PAL_STATE_CHANGE \ , EXEC_ON_PAL_CONNECTED, EXEC_ON_PAL_DISCONNECTED \ , AUTO_ADD_TO_ALLOWED try: f= open( file ) except IOError: self.fatalError( _('Opening config file \'%s\'') % file ) l= f.readline() lastUser='' while l: l= strip(l) if len(l) > 0: if l[0] == '#': self.pDebug( 'comment' ) else: (opt, value)= split(l, '=', 1) opt= strip( opt ) value= strip( value ) if opt == 'user': if USER == '': USER= value lastUser= value self.accounts[value]= '' elif opt == 'password': if PASSWD == '': PASSWD= value if lastUser == '': if USER: # To catch error later (no user defined) self.fatalError( _('Reading config file \'%s\', password \'%s\' must have a preceding \'user\' option') % (file, value) ) else: self.accounts[lastUser]= value lastUser= '' elif opt == 'state': INIT_STATE= upper( value ) elif opt == 'encoding': MSN.ENCODING= value elif opt == 'show_invited': if value == '1': SHOW_INVITED= 1 elif value == '0': SHOW_INVITED= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'auto_list': if value == '1': AUTO_LIST= 1 elif value == '0': AUTO_LIST= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'beep_on_msg': if value == '1': BEEP_ON_MSG= 1 elif value == '0': BEEP_ON_MSG= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'beep_on_new_chat': if value == '1': BEEP_ON_NEW_CHAT= 1 elif value == '0': BEEP_ON_NEW_CHAT= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'send_typing': if value == '1': SEND_TYPING= 1 elif value == '0': SEND_TYPING= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'download_dir': MSN.DOWNLOAD_DIR= value if MSN.DOWNLOAD_DIR[-1] != '/': MSN.DOWNLOAD_DIR= MSN.DOWNLOAD_DIR + '/' elif opt == 'max_name_size': try: MAX_NAME_SIZE= int(value) except ValueError: self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) ) elif opt == 'auto_list_on_0': if value == '1': AUTO_LIST_ON_0= 1 elif value == '0': AUTO_LIST_ON_0= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'auto_list_on_0_clear': if value == '1': AUTO_LIST_ON_0_CLEAR= 1 elif value == '0': AUTO_LIST_ON_0_CLEAR= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'auto_list_last_move': if value == '1': AUTO_LIST_LAST_MOVE= 1 elif value == '0': AUTO_LIST_LAST_MOVE= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'pal_connect_timeout': try: MSN.PAL_CONNECT_TIMEOUT= int(value) except ValueError: self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) ) elif opt == 'timestamp': if value == '1': TIMESTAMP= 1 elif value == '0': TIMESTAMP= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'log_chats': if value == '1': MSN.LOG_CHATS= 1 elif value == '0': MSN.LOG_CHATS= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'delete_key': try: if value[:2] == '0x': DELETE_KEY= int(value[2:], 16) else: DELETE_KEY= int(value) except ValueError: self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) ) elif opt == 'timestamp_format': TIMESTAMP_FORMAT= value elif opt == 'chat_title': if value in ['number', 'user', 'both']: CHAT_TITLE= value else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values number, user or both') % (file, opt) ) elif opt == 'alias': l= split( value, ' ', 1 ) if len(l) == 2: self.alias[l[0]]= l[1] else: self.fatalError( _('Reading config file \'%s\', option \'%s\': malformed alias') % (file, opt) ) elif opt == 'chat_title_use_alias': if value == '1': CHAT_TITLE_USE_ALIAS= 1 elif value == '0': CHAT_TITLE_USE_ALIAS= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'ping_time': try: PING_TIME= int(value) except ValueError: self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) ) elif opt == 'idle_time': try: IDLE_TIME= int(value) except ValueError: self.fatalError( _('Reading config file \'%s\', \'%s\' must be an integer number') % (file, opt) ) elif opt == 'idle_state': IDLE_STATE= upper(value) if not MSN.STATES.has_key( IDLE_STATE ): self.fatalError( _('Reading config file \'%s\', \'%s\' must be a valid state') % (file, opt) ) elif opt == 'auto_add_to_allowed': if value == '1': AUTO_ADD_TO_ALLOWED= 1 elif value == '0': AUTO_ADD_TO_ALLOWED= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'auto_unidle': if value == '1': AUTO_UNIDLE= 1 elif value == '0': AUTO_UNIDLE= 0 else: self.fatalError( _('Reading config file \'%s\', option \'%s\' only can get values 1 or 0') % (file, opt) ) elif opt == 'logo_file': if value != '' and value[0] == '/': LOGO_FILE= value else: LOGO_FILE= sys.prefix + '/share/doc/pebrot/' + value elif opt == 'my_ip': MSN.MY_IP= value elif opt[:4] == 'col_': if not curses.has_colors(): l= f.readline() continue if self.COLORS.has_key( opt[4:].upper() ): color= value.upper() #print >>sys.stderr, 'color=', color if not self.COLORS.has_key( color ): self.fatalError( _('Reading config file \'%s\', \'%s\' has a non valid color value (%s)') % (file, opt, value) ) self.COLORS[opt[4:].upper()]= self.COLORS[color] else: self.fatalError( _('Reading config file \'%s\', \'%s\' is not a valid color option') % (file, opt) ) elif opt[:8] == 'exec_on_': opt= opt.upper() try: exec(opt) except NameError: self.fatalError( _('Reading config file \'%s\', \'%s\' is not a valid option') % (file, opt) ) exec( '%s= \'%s\'' % (opt, value), globals() ) else: self.fatalError( _('Reading config file \'%s\', unknown option \'%s\' ') % (file, opt) ) l= f.readline() #MSN.Session.__init__( self ) #print >>sys.stderr, 'COLORS=', self.COLORS #self.showChatBar() #self.showStateBar() def connect( self, user, passwd, state ): self.pVerbose( 1, _('User: %s\n') % user ) MSN.Session.connect( self, user, passwd, state ) def getPassword( self ): if USER == '': self.fatalError( _('No user or password, you should take a look at README ;)') ) if self.accounts.has_key( USER ) and self.accounts[USER]: passwd= self.accounts[USER] else: self.pActive( _('Enter password for %s: ') % USER ) passwd= strip( self.Text.getLine( self.handleInput, hidden= 1 ) ) self.pActive('\n') return passwd def printUsers( self, chat= None, hash=None, title= _('=| CONNECTED |=') , numbers=1, state=1, name=1 ): if not chat: chat= self.activeChat if hash: users= hash.keys() else: users= self.usersList.keys() i= 0 users.sort() self.pWin( chat, '\n', refresh= 0 ) self.pWin( chat, (LISTLINE % title) + '\n', color= 'LIST_DELIM' ) for pal in users: if numbers: self.pWin( chat, str(i), 'LIST_NUM', 0 ) self.pWin( chat, '- ', 'DEFAULT', 0 ) self.pWin( chat, '%s ' % pal, 'LIST_PASS', 0 ) if state: self.pWin( chat, '(', 'LIST_PARENS', 0 ) self.pWin( chat, MSN.STATES[self.usersList[pal]['state']] , 'LIST_STATE', 0 ) self.pWin( chat, ')', 'LIST_PARENS', 0 ) if name: self.pWin( chat, ': ', 'LIST_PARENS', 0 ) if self.alias.has_key( pal ): name= self.alias[pal] else: name= replace(self.usersList[pal]['name'], '\r', '') self.pWin( chat, '%s' % name, 'LIST_NAME', 0 ) self.pWin( chat, '\n' ) #content= content + '%d- %s (%s): %s\n' % ( i #, pal #, MSN.STATES[self.usersList[pal]['state']] #, self.usersList[pal]['name'] ) i= i + 1 #self.pMain( (LISTLINE % ('-' * len(title))) + '\n' ) if TIMESTAMP: self.pWin( chat, '------------= %s =------------\n' % time.strftime(TIMESTAMP_FORMAT , time.localtime()), 'TIMESTAMP' ) else: self.pWin( chat, (LISTLINE % ('-' * len(title))) + '\n', refresh= 0 , color= 'LIST_DELIM' ) self.refreshWin() self.Text.refresh() def printList( self, chat, title, content ): self.pWin( chat, '\n' ) self.pWin( chat, (LISTLINE % title) + '\n' ) self.pWin( chat, content + '\n' ) self.pWin( chat, (LISTLINE % ('-' * len(title))) + '\n' ) def updateUsersList( self ): self.usersList= {} for user in self.usersOnline.keys(): self.usersList[user]= self.usersOnline[user] def nextChat( self ): if SHOW_INVITED: list= self.sortedChats else: list= filter( onlyCreated, self.sortedChats ) elems= len(list) if elems == 0: return None else: if self.activeChat in list: i= list.index( self.activeChat ) return list[(i+1)%elems] else: return list[-1] def dumpFileName( self ): pref= 'pebrot-dump' name= pref i= 0 while( 1 ): if not os.access( name, os.F_OK ): return name else: name= pref + '-' + str(i) i+= 1 def helpInfo( self ): self.printList( self.activeChat, _('=| HELP |='), HELPMSG ) def processInput( self, st ): st= strip( st ) parts= split( st ) if len(parts) == 0: return # Added to allow Pebrot to remain offline if self.state == 'FLN': if parts[0][1:] == 'q': sys.exit() elif parts[0][1:] == 's': if len(parts)<2: self.pInfo( _('state: %s') % MSN.STATES[self.state] ) else: state= upper( parts[1] ) if state != 'FLN' and MSN.STATES.has_key( state ): self.state = state self.reconnect() self.idle= 0 #self.lastStartedTyping= time.time() #self.alarmExecAndProgramNext() else: self.helpInfo() return else: self.pError( _('Error: Must be online to send commands or messages.') ) return # End Edit if parts[0][0] in ['\\', '/']: command= parts[0][1:] self.pDebug( 'com=' + command ) if command.isdigit(): if ( int(command) >= len(self.usersList) ): self.pError( _('Error: Select an existing user number') ) return if self.state in ['HDN', 'FLN']: self.pError( _('Error: Must be online for talking') ) return allUsers= self.usersList.keys() allUsers.sort() user= allUsers[int(command)] if user == self.user: self.pInfo( _('You can\'t talk to yourself') ) return chat= self.newChat( user ) self.sortedChats.append( chat ) self.activeChat= chat self.Main.newWin( chat ) self.refreshWin() self.showChatBar() self.showStateBar() elif command == 'h' or command == '?': self.helpInfo() elif command == 'l': if len(parts) == 1: self.printUsers() elif len(parts) == 2: list= upper( parts[1] ) if list in ['FL', 'RL', 'AL', 'BL']: self.printUsers( hash= self.lists[list] , title= (_('=| USERS ON %s |=') % MSN.LISTS[list]) , numbers=0, state=0, name=0 ) else: self.helpInfo() else: self.helpInfo() elif command == 'i': if len(parts)<2: self.helpInfo() return if self.activeChat == self.sortedChats[0]: self.pInfo( _('Must be on active chat to invite') ) return allUsers= self.usersList.keys() allUsers.sort() user= allUsers[int(parts[1])] self.activeChat.invite( user ) elif command == 'c': if self.activeChat != self.sortedChats[0]: self.pDebug( _('closing chat %s') % str(self.activeChat) ) try: self.closeChat( self.activeChat ) except MSN.SocketError, e: pass self.sortedChats.remove( self.activeChat ) self.Main.delWin( self.activeChat ) self.activeChat= self.nextChat() #if len(self.chats.keys()) == 0: #self.activeChat= None self.refreshWin() self.showChatBar() self.showStateBar() elif command == 'a': if len(parts)<2: self.helpInfo() else: self.delUser( parts[1], 'BL' ) self.addUser( parts[1], 'AL' ) self.addUser( parts[1], 'FL' ) elif command == 'b': if len(parts)<2: self.helpInfo() else: self.delUser( parts[1], 'AL' ) self.addUser( parts[1], 'BL' ) elif command == 'r': if len(parts)<2: self.helpInfo() else: self.delUser( parts[1], 'AL' ) self.delUser( parts[1], 'FL' ) elif command == 'u': if self.activeChat != self.sortedChats[0]: self.showChatUsers( self.activeChat ) elif command == 'o': if len(parts)<2: self.helpInfo() else: if self.state in ['HDN', 'FLN']: self.pError( _('Error: Must be online for talking') ) return user= parts[1] if user == self.user: self.pInfo( _('You can\'t talk to yourself') ) return if not "@" in user: self.pError( _('You must use give the full passport address. Ex.: somebody@example.com') ) return chat= self.newChat( user ) self.sortedChats.append( chat ) self.activeChat= chat self.Main.newWin( chat ) self.refreshWin() self.showChatBar() self.showStateBar() elif command == 'n': if len(parts) == 1: self.pInfo( _('Name: %s') % self.name ) else: self.changeName( join(parts[1:], ' ') ) elif command == 's': if len(parts)<2: self.pInfo( _('state: %s') % MSN.STATES[self.state] ) else: state= upper( parts[1] ) if MSN.STATES.has_key( state ): self.changeState( state ) self.idle= 0 self.lastStartedTyping= time.time() self.alarmExecAndProgramNext() else: self.helpInfo() elif command == 'p': self.ping() elif command == 'f': if self.activeChat == self.sortedChats[0]: self.pError( _('Must be connected to a chat for file sending') ) return if len(parts)<2: self.helpInfo() return file= join( parts[1:] ) if not os.access( file, os.R_OK ): self.pError( _('Can\'t open \'%s\'') % file ) return self.sendChatFile( self.activeChat, file ) elif command == 'fa': for fRecv in self.activeChat.recvFile.values(): if fRecv.state != MSN.RECV_ACCEPTED: fRecv.accept() self.pInfo( _('Receiving \'%s\' accepted') % (fRecv.fileName) ) elif command == 'fr': if len(parts) == 1: for fRecv in self.activeChat.recvFile.values(): fRecv.reject() self.pInfo( _('Receiving \'%s\' rejected') % (fRecv.fileName) ) self.showChatBar() else: recvs= self.activeChat.recvFile.values() for num in parts[1:]: num= int(num) if num >= 1 and num <= len(recvs): self.activeChat.closeFileRecv( recvs[num-1] ) self.pInfo( _('Aborting downloading number %d') % num ) self.showChatBar() else: if len(recvs) == 0: self.pError( _('There are no downloads to cancel') ) else: self.pError( _('Number must be between 1 and %d') % (len(recvs)) ) self.showChatBar() elif command == 'fc': if len(parts) == 1: for fSend in self.activeChat.sendFile.values(): fSend.cancel() self.removeFileSend( fSend ) self.pInfo( _('Sending \'%s\' rejected') % (fSend.fileName) ) self.showChatBar() else: sends= self.activeChat.sendFile.values() for num in parts[1:]: num= int(num) if num >= 1 and num <= len(sends): self.activeChat.cancelFileSend( sends[num-1] ) self.pInfo( _('Cancelling sending number %d') % num ) self.showChatBar() else: if len(sends) == 0: self.pError( _('There are no downloads to cancel') ) else: self.pError( _('Number must be between 1 and %d') % (len(sends)) ) self.showChatBar() elif command == '!': com= join( parts[1:] ) (stdin, out)= os.popen4(com, 'r') self.pActive( out.read() + '\n' ) elif command == 'e': self.Main.erase( self.activeChat ) self.Main.toBottom( self.activeChat ) self.refreshWin() self.Text.refresh() elif command == 't': self.pActive( str(self.termSize()) ) elif command == 'crash': self.crash() elif command == 'ex': self.subExec( self.replaceVars( 'echo $user es una $prueba' , {'$user':'de aqui', '$prueba':'kk' } ) ) ## added for pebrot no error message shutdown elif command == 'rec': if self.offline == 1: self.crash() self.offline = 0 elif command == 'd': if len( parts ) == 1: name= self.dumpFileName() else: name= parts[1] try: f= open( name, 'w' ) except IOError: self.pError( _('Can\'t open file \'%s\'') % name ) return self.Main.dump( self.activeChat, f ) self.pInfo( _('Chat dumped to file %s') % name ) elif command == 'color': if len( parts ) == 1: self.pInfo( _('The colors are: %s') % self.COLORS ) elif len ( parts ) == 3: #self.pInfo( _('%s') % parts[1] + ' and ' + parts[2] ) self.COLORS[ parts[1] ] = self.COLORS[ parts[2] ] else: self.pInfo( _('You messed up somehow and i can\'t fix it. :-P') ) elif command == 'q': sys.exit() else: self.pError( _(r'Error: Unknown command: %s') % parts[0] ) else: # It's a message, lets send it if AUTO_UNIDLE and self.idle: self.changeState( 'NLN' ); self.idle= 0 self.lastStartedTyping= time.time() self.alarmExecAndProgramNext() if self.activeChat == self.sortedChats[0]: self.pError( _('Error: You are not connected to any chat.') ) else: self.sendChatMsg( self.activeChat, st ) # Invite pals who are out from chat (e.g. timeout kicked) def inviteInactive( self ): list= self.listInactiveUsers( self.activeChat ) if list: t0= time.time() for user in list: self.activeChat.invite( user ) t1= time.time() while self.listInactiveUsers( self.activeChat ) and \ (t1-t0) < MSN.PAL_CONNECT_TIMEOUT: self.step() t1= time.time() self.pDebug( 'Waiting for pals to connect...(t1-t0= %s)' % (t1-t0) ) if (t1-t0) >= MSN.PAL_CONNECT_TIMEOUT: self.pError( _('Timeout while waiting for pals to connect') ) def showStateBar( self ): self.StateBar.erase() if curses.has_colors() and not MONOCHROME: self.StateBar.attrset( self.COLORS['STATE_BAR'] ) if SHOW_INVITED: list= self.sortedChats else: list= filter( onlyCreated, self.sortedChats ) #list= [None] + list if curses.has_colors() and not MONOCHROME: cBar= self.COLORS['STATE_BAR'] cWritten= self.COLORS['STATE_BAR_WRITTEN'] cNormal= self.COLORS['STATE_BAR_NOT_WRITTEN'] else: cBar= cNormal= cWritten= 0 #self.pMain( str(list) ) i= 0 for chat in list: if chat.written: color= cWritten if curses.has_colors() and not MONOCHROME: plus= '' else: plus= '+' else: color= cNormal plus= '' if chat == self.activeChat: self.StateBar.addstr( '[', cBar ) if CHAT_TITLE == 'number' or CHAT_TITLE == 'both': self.StateBar.addstr( '%d' % i, color ) if CHAT_TITLE == 'both': self.StateBar.addstr( '-', color ) if (CHAT_TITLE == 'user' or CHAT_TITLE == 'both'): users= chat.listUsers() if len( users ) == 0: if chat == self.sortedChats[0]: title= _('Main') else: title= _('Empty') elif len( users ) == 1: if CHAT_TITLE_USE_ALIAS and \ self.alias.has_key( users[0] ): title= replace( self.alias[users[0]], ' ', '_' ) else: title= users[0].split('@')[0] else: title= _('Multiple') self.StateBar.addstr( '%s' % title, color ) self.StateBar.addstr( plus, color ) if chat == self.activeChat: self.StateBar.addstr( ']', cBar ) i= i + 1 if list: self.StateBar.addstr( ' ' ) self.StateBar.line() self.StateBar.toTop() self.StateBar.refresh() self.Text.refresh() # Find public ip using http://simple.showmyip.com def getPublicIp( self ): self.pActive( _('Querying public ip from simple.showmyip.com ') ) ip= '' for i in range(10): try: st= urllib.urlopen( IP_URL ).read().strip() ip= split( st )[0] except IOError: pass self.pActive( '.' ) if ip != '': self.pActive( ' %s\n' % ip ) return ip self.pActive( _(' ERROR: can\'t read page\n') ) def showChatBar( self ): self.ChatBar.erase() if curses.has_colors() and not MONOCHROME: self.ChatBar.attrset( self.COLORS['CHAT_BAR'] ) if self.activeChat != self.sortedChats[0]: for fSend in self.activeChat.sendFile.values(): try: percent= (float(fSend.sent)/fSend.size)*100 self.pDebug( '%s= (up: %d)' % ( str(self.sendFile) , percent) ) self.ChatBar.addstr( _('(up: %d%%)') % percent ) except ZeroDivisionError: pass for fSend in self.activeChat.recvFile.values(): #asd try: percent= (float(fSend.received)/fSend.size)*100 self.pDebug( '%s= (dl: %d)' % ( str(self.recvFile) , percent) ) self.ChatBar.addstr( _('(dl: %d%%)') % percent ) except ZeroDivisionError: pass users= self.activeChat.getUsers() self.pDebug( 'USERS= ' + str(users) ) self.ChatBar.addstr( '>>' ) for u in users: user= u[0].split('@')[0] if curses.has_colors() and not MONOCHROME: if u[1] == 'TYPING': color= self.COLORS['CHAT_BAR_NAME_TYPING'] else: color= self.COLORS['CHAT_BAR_NAME'] self.ChatBar.addstr( ' ' + user, color ) else: if u[1] == 'TYPING': self.ChatBar.addstr( ' *' + user ) else: self.ChatBar.addstr( ' ' + user ) self.ChatBar.line() self.ChatBar.toTop() self.ChatBar.refresh() self.Text.refresh() def activateChat( self, num ): if num < len(self.sortedChats): self.activeChat= self.sortedChats[num] self.refreshWin() self.showChatBar() self.showStateBar() def handleInput( self, c ): if c in [curses.ascii.BS, curses.ascii.DEL, curses.KEY_BACKSPACE , DELETE_KEY]: self.Text.delChar() self.Text.refresh() elif self.altPressed: if c >= 0x30 and c <= 0x39: self.activateChat( c - 0x30 ) elif c == 0x60: # Key left from number 1 self.activateChat( 0 ) elif c == 0x14a: # supr self.Text.suprChar() self.Text.refresh() elif c == curses.KEY_LEFT: self.Text.cursBack() self.Text.refresh() elif c == curses.KEY_RIGHT: self.Text.cursForward() self.Text.refresh() elif c == curses.KEY_UP: self.showPrevHistory() elif c == curses.KEY_DOWN: self.showNextHistory() elif c == curses.KEY_PPAGE: self.Main.scrollUp( self.activeChat, 5 ) self.Text.refresh() elif c == curses.KEY_NPAGE: self.Main.scrollDown( self.activeChat, 5 ) self.Text.refresh() elif c == 0x9: # tab self.activeChat= self.nextChat() self.refreshWin() self.showChatBar() self.showStateBar() elif c == curses.KEY_HOME: # Begin self.Text.cursBegin() self.Text.refresh() elif c == curses.KEY_END: # End self.Text.cursEnd() self.Text.refresh() elif c == 0x1b: # Alt pulsado self.altPressed= 1 elif c == 0xc: # Ctrl-l (redraw window) self.reinitCurses() self.refreshAll() elif c == 0x4: # Ctrl-d sys.exit(0) elif c > 255: pass else: if c in [0x5c, 0x2f] and self.firstKeyPress: # '\' for not sending typing notification with commands self.typingCommand= 1 elif c != curses.ERR and SEND_TYPING and not self.typingCommand \ and self.needToSendTypingNotif and self.activeChat: self.firstKeyPress= 0 self.needToSendTypingNotif= 0 self.lastStartedTyping= time.time() # This alarm is for self.needToSendTypingNotif= 1 and sending # a new typingNotification after 5 more seconds signal.alarm( 5 ) self.needToResetTypingNotif= 1 try: self.activeChat.typingNotification() except MSN.SocketError, e: pass #self.pError( 'Socket Error 3: %s' % e[1] ) self.altPressed= 0 return 0 if c != 0x1b: self.altPressed= 0 return 1 def alarmExecAndProgramNext( self ): t= time.time() self.pDebug( 'alarmExecAndProgramNext: lastPing= %s, t= %s' % (str(self.lastPing), str(t)) ) # Setting to 600 as a failsafe, and because python appears not to have # support for infinity minTimeTillNext= 600 # Usage: a three element list per time-out action # first element - should return true or false, according to whether # the action should be considered. # second element - should return the time the action is to be # performed at. As with all deadlines, the action may be taken # after it has passed, but never before. # third element - the function to call to perform the action. for [getcond, getdeadline, action] in [ [ lambda: PING_TIME, lambda: self.lastPing + PING_TIME, self.pingWrap ], [ lambda: IDLE_TIME and self.state == 'NLN', lambda: self.lastStartedTyping + IDLE_TIME, self.idleTimeOut ], [ lambda: self.needToResetTypingNotif, lambda: self.lastStartedTyping + 5, self.resetTypingNotif ] ]: if getcond() and (t >= getdeadline()): # action should change the deadline and/or cond, # which is why we use lambdas #self.pInfo( 'action %s called on %d' % (action, time.time()) ) action() # Reset t in case action() takes a long time - t = time.time() delta= getdeadline() - t #self.pInfo( ' %s - %s (%d)' % (delta, action, bool(getcond())) ) if getcond() and delta < minTimeTillNext: minTimeTillNext= delta #self.pInfo( 'minTimeTillNext= %s' % minTimeTillNext ) timeTillNext= int(minTimeTillNext) + 1 if timeTillNext <= 0: timeTillNext= 1 #self.pInfo( 'alarm set to %d' % timeTillNext) signal.alarm(timeTillNext) def alarmHandler( self, signum, frame ): self.alarmExecAndProgramNext() def pingWrap( self ): # It's time to ping server self.ping() #self.pInfo( 'PING - %s' % str(t) ) self.lastPing= time.time() def resetTypingNotif( self ): #self.pInfo( 'resetTypingNotif' ) self.needToResetTypingNotif= 0 self.needToSendTypingNotif= 1 for chat in self.chats.values(): for user in chat.listUsers(): if chat.isTyping( user ): chat.activateUser( user ) self.showChatBar() def idleTimeOut( self ): # TODO - allow other actions (e.g. shell scripts) self.idle= 1 self.pInfo( _('Automatically setting state to %s') % MSN.STATES[IDLE_STATE] ) self.changeState( IDLE_STATE ) def printArt( self, chat, img, refresh= 0 ): color= 'W' i= 0 while i < len(img): piece= '' while i < len(img) and img[i] != 'º': piece= piece + str(img[i]) i= i + 1 self.pWin( chat, piece, IMG_COLORS[upper(color)] ) if i < len(img): color= img[i+1] i= i + 2 self.pWin( chat, '\n', refresh= 0 ) if refresh: self.refreshWin( chat ) self.Text.refresh() def showChatUsers( self, chat ): self.printUsers( chat, chat.users, _('=| USERS ON ACTIVE CHAT |='), 0 ) def oneStep( self ): try: st= self.Text.getLine( self.handleInput, self.step ) if st: self.addHistory( st ) self.lastStartedTyping= time.time() self.firstKeyPress= 1 self.typingCommand= 0 self.needToSendTypingNotif= 1 self.Text.erase() self.refreshWin() self.processInput( st ) except MSN.SocketError, e: if e.errno() == errno.EINTR: # Avoid error 'Interrupted system call' pass else: type= self.socketType( e.socket ) if type == MSN.SOCK_MAIN: # Added to Fix offline status if self.state != 'FLN': self.pError( _('Socket error on main server connection (%s)') % e.errorMsg() ) self.pError( _('Trying to reconnect...') ) self.reconnect() elif type == MSN.SOCK_RECV: fRecv= self.recvFile[e.socket] self.pError( _('Socket error receiving \'%s\' (%s)') % (fRecv.fileName, e.errorMsg()), fRecv.chat ) self.removeFileRecv( fRecv ) self.showChatBar() elif type == MSN.SOCK_SEND: fSend= self.sendFile[e.socket] self.pError( _('Socket error sending \'%s\' (%s)') % (fSend.fileName, e.errorMsg()), fSend.chat ) self.removeFileSend( fSend ) self.showChatBar() elif type == MSN.SOCK_CHAT: # To hide errors when server closes idle chat if not self.chats[e.socket].hideSocketError: self.pError( _('Socket error (%s)') % e.errorMsg(), self.chats[e.socket] ) else: self.pError( _('Unhandled socket error (%s)') % e.errorMsg() ) self.Text.refresh() def replaceVars( self, action, params ): #self.pInfo( str(params) ) #self.pInfo( action ) #return '' try: for var in re.findall( r'(?<=[^\\])\$\w+', action ): var= lower( var ) self.pDebug( 'var=%s, params[var]=%s' % (var, str(params[var])) ) action= re.sub( '\\' + var, str(params[var]), action ) except KeyError, e: self.pError( _('Variable \'%s\' not found in action \'%s\'') % (e, action) ) return '' return action def subExec( self, command ): if os.fork() == 0: os.system( command ) os._exit( 0 ) def execAction( self, action, params ): self.subExec( self.replaceVars( action, params ) ) def loopInput( self ): while 1: self.oneStep() def getAutoListChat( self ): if AUTO_LIST_ON_0: chat= self.sortedChats[0] if AUTO_LIST_ON_0_CLEAR: self.Main.erase( chat ) else: chat= self.activeChat return chat def drawLastMove( self, chat, msg ): allUsers= self.usersList.keys() allUsers.sort() number= allUsers.index( msg.fields['passport'] ) self.pWin( chat, '%s' % number ,'LIST_NUM', 0 ) self.pWin( chat, '- ', 'DEFAULT', 0 ) self.pWin( chat, '%s ' % msg.fields['passport'] , 'LIST_PASS', 0 ) self.pWin( chat, _('changed state to '), 'DEFAULT', 0 ) self.pWin( chat , '\'%s\'\n' % MSN.STATES[msg.fields['state']] , 'LIST_STATE', 1 ) def userChangesState( self, msg ): self.updateUsersList() if AUTO_LIST: chat = self.getAutoListChat() self.printUsers( chat ) if AUTO_LIST_LAST_MOVE: self.drawLastMove( chat, msg ) if msg.fields['state'] == 'NLN' and EXEC_ON_PAL_STATE_NLN: passport= msg.fields['passport'] escName= re.escape( self.aliasOrName(passport) ) self.execAction( EXEC_ON_PAL_STATE_NLN, {'$passport':passport , '$user':escName} ) elif EXEC_ON_PAL_STATE_CHANGE: passport= msg.fields['passport'] escName= re.escape( self.aliasOrName(passport) ) state= msg.fields['state'] self.execAction( EXEC_ON_PAL_STATE_CHANGE, {'$passport':passport , '$user':escName, '$state':state} ) def userConnected( self, msg ): self.updateUsersList() if AUTO_LIST: chat = self.getAutoListChat() self.printUsers( chat ) if AUTO_LIST_LAST_MOVE: self.drawLastMove( chat, msg ) if EXEC_ON_PAL_CONNECTED: passport= msg.fields['passport'] escName= re.escape( self.aliasOrName(passport) ) state= msg.fields['state'] self.execAction( EXEC_ON_PAL_CONNECTED, {'$passport':passport , '$user':escName, '$state':state} ) def userOn( self, passport ): self.updateUsersList() # Lets reshow user list on init when an ILN arrives after it wasn't expected if self.listShown: chat = self.getAutoListChat() self.printUsers( chat ) def userOff( self, passport ): self.updateUsersList() if AUTO_LIST: chat = self.getAutoListChat() self.printUsers( chat ) if AUTO_LIST_LAST_MOVE: self.pWin( chat, '%s ' % passport, 'LIST_PASS', 0 ) self.pWin( chat, _('disconnected\n'), 'DEFAULT', 1 ) if EXEC_ON_PAL_DISCONNECTED: escName= re.escape( self.aliasOrName(passport) ) self.execAction( EXEC_ON_PAL_DISCONNECTED, {'$passport':passport , '$user':escName} ) def otherLogin( self ): #self.fatalError( _('Detected other login to this account') ) self.pError( _('You have been signed in from another computer. Type \\q to exit or \\s nln to reconnect.') ) self.state = 'FLN' self.pError( _('You are currently OFFLINE.') ) def userAdded( self, passport, list ): list= upper( list ) self.pInfo( _('%s added to %s') % (passport, MSN.LISTS[list]) ) if AUTO_ADD_TO_ALLOWED and list == 'RL': self.addUser( passport, 'FL' ) self.addUser( passport, 'AL' ) def userRemoved( self, passport, list ): list= upper( list ) self.pInfo( _('%s removed from %s') % (passport, MSN.LISTS[list]) ) def selfStateChanged( self, state ): self.pInfo( _('state: %s') % MSN.STATES[state] ) # This is for firing alarms after login self.alarmExecAndProgramNext() def invitedBy( self, msg ): self.acceptChat( msg ) def msgFrom( self, chat, passport, msg ): # Make chat visible on first msg received self.pDebug(passport) if chat not in self.sortedChats: self.pInfo( _('%s is talking to you') % passport ) if BEEP_ON_NEW_CHAT: curses.beep() if EXEC_ON_NEW_CHAT: escName= re.escape( self.aliasOrName(passport) ) escMsg= re.escape( msg ) self.execAction( EXEC_ON_NEW_CHAT, {'$passport':passport , '$user':escName, '$msg':escMsg} ) if SHOW_INVITED: self.sortedChats.append( chat ) self.Main.newWin( chat ) # Avoid problems displaying \r msg= replace( msg, '\r', '' ) self.pDebug( 'MSG===' + msg ) if SHOW_INVITED: self.pChat( chat, passport, msg ) else: self.pChat( self.activeChat, passport, msg ) self.showChatBar() self.showStateBar() def gotMail( self, numUnread ): self.pInfo( _('You\'ve got mail: %s unread.') % numUnread ) def incomingMail( self, mailFrom ): self.pInfo( _('Incoming mail from: %s') % mailFrom ) def userAcceptChat( self, chat, passport ): if not self.Main.hasWin( chat ): self.Main.newWin( chat ) self.pInfo( _('%s joined the chat') % passport, chat ) chat.logText( _('%s >> %s joined the chat <<') % (time.strftime('[%H:%M:%S]'), passport) ) self.showStateBar() self.showChatBar() def nameChanged( self, name ): self.name= name self.pInfo( _('Name changed to: %s') % name ) def protoError( self, code ): self.pError( _('Protocol error: %s') % MSN.ERRORS[code] ) def fatalProtoError( self, code ): self.fatalError( _('Protocol error (%s)') % MSN.ERRORS[code] ) def addUserError( self, passport, list ): self.pError( _('Failed to add \'%s\' to %s') % (passport, MSN.LISTS[list]) ) def delUserError( self, passport, list ): self.pError( _('Failed to remove \'%s\' from %s (user wasn\'t on this list)') % (passport, MSN.LISTS[list]) ) def userOnListError( self, passport, list ): self.pError( _('User \'%s\' already on %s') % (passport, MSN.LISTS[list]) ) def userLeavesChat( self, chat, user ): self.pInfo( _('%s left the chat') % user, chat ) chat.logText( _('%s >> %s left the chat <<') % (time.strftime('[%H:%M:%S]'), user) ) if len(chat.users) == 0: if self.usersOnline.has_key(user): chat.deactivateUser( user ) #chat.invite( user ) #else: #self.sortedChats.remove( chat ) #if self.activeChat== chat: #self.activeChat= self.nextChat() self.showStateBar() self.showChatBar() def msgNotReceived( self, chat ): self.pError( _('Message was not received'), chat ) def typing( self, chat, user ): chat.typingUser( user ) self.showChatBar() self.needToResetTypingNotif= 1 signal.alarm( 5 ) def chatConnectError( self, chat ): self.pError( _('Can\'t connect to chat server'), chat ) def sendFileAccepted( self, fSend ): self.pInfo( _('Request for sending \'%s\' accepted') % fSend.fileName , fSend.chat ) def sendFileRejected( self, fSend ): self.pInfo( _('Request for sending \'%s\' rejected') % fSend.fileName , fSend.chat ) def sendFileCancelled( self, fSend ): self.pInfo( _('Sending \'%s\' cancelled by peer') % fSend.fileName , fSend.chat ) self.showChatBar() def sendFileFinished( self, fSend ): self.pInfo( _('\'%s\' successfully sent') % fSend.fileName, fSend.chat ) self.showChatBar() def recvFileRequest( self, fRecv ): passport= fRecv.user name= self.aliasOrName( passport ) msg= _('Request for receiving file \'%s\' (%s bytes) from %s') % \ (fRecv.fileName, fRecv.size, fRecv.user) # Make chat visible on first msg received if fRecv.chat not in self.sortedChats: self.pInfo( _('File send request received') ) if BEEP_ON_NEW_CHAT: curses.beep() if EXEC_ON_NEW_CHAT: escName= re.escape( name ) escMsg= re.escape( msg ) self.execAction( EXEC_ON_NEW_CHAT, {'$passport':passport , '$user':escName, '$msg':escMsg} ) if SHOW_INVITED: self.sortedChats.append( fRecv.chat ) self.Main.newWin( fRecv.chat ) fRecv.chat.written= 1 self.pInfo( msg, fRecv.chat ) if EXEC_ON_FILE_RECV_BEGIN: escName= re.escape( name ) escFile= re.escape( fRecv.fileName ) self.execAction( EXEC_ON_FILE_RECV_BEGIN, {'$passport':passport , '$user':escName, '$file':escFile, '$size':fRecv.size } ) self.showChatBar() self.showStateBar() def chunkSent( self, fSend ): if fSend.chat == self.activeChat: self.showChatBar() def chunkReceived( self, fRecv ): if fRecv.chat == self.activeChat: self.showChatBar() def recvFileFinished( self, fRecv ): self.pInfo( _('\'%s\' successfully received') % fRecv.fileName, fRecv.chat ) if EXEC_ON_FILE_RECV_END: passport= fRecv.user escName= re.escape( self.aliasOrName(passport) ) escFile= re.escape( fRecv.fileName ) self.execAction( EXEC_ON_FILE_RECV_END, {'$passport':passport , '$user':escName, '$file':escFile, '$size':fRecv.size } ) self.showChatBar() def recvFileCancelled( self, fRecv ): self.pInfo( _('Receiving \'%s\' cancelled by peer') % fRecv.fileName , fRecv.chat ) self.showChatBar() def listUpdated( self, list, type ): self.printUsers( self.activeChat , list, '=| %s |=' % (upper(MSN.LISTS[type])) , numbers=0, state=0, name=0 ) def palListComplete( self ): self.printUsers() #def qngReceived( self ): #self.pInfo( 'QNG received' ) def userAddedYou( self, passport ): self.pInfo( _('User %s added you to his/her list') % passport ); if AUTO_ADD_TO_ALLOWED: self.addUser( passport, 'FL' ) self.addUser( passport, 'AL' ) def gogogo( stdscr ): global PASSWD curses.noecho() stdscr.keypad(1) #try: con= TextMSN( stdscr ) con.readConfig( CONFIG_FILE ) con.parseArgs() PASSWD= con.getPassword() con.initBarColors() con.refreshAll() # Create dir for chat logs under MAIN_LOG_DIR MSN.LOG_DIR= MAIN_LOG_DIR + USER + '/' if MSN.LOG_CHATS and not os.access( MSN.LOG_DIR, os.F_OK ): os.mkdir( MSN.LOG_DIR ) # Print logo try: f= open( LOGO_FILE ) LOGO= f.read() con.printArt( con.activeChat, LOGO ) except IOError: pass con.pActive( VER_MSG ) if MSN.MY_IP == 'auto': MSN.MY_IP= con.getPublicIp() con.connect( USER, PASSWD, INIT_STATE ) # Just begin with the alarms con.loopInput() #except (KeyboardInterrupt): #curses.endwin() def replaceOldConfig( oldConfig ): print print _('Old config file found. I\'m going to move this config file to %s.') % CONFIG_FILE raw_input( _('Press Enter to continue...') ) print print _('Moving %s to %s ...') % (oldConfig, CONFIG_FILE) os.rename( oldConfig, CONFIG_FILE ) print raw_input( _('All done, press Enter to continue...') ) def main(): if not os.access( CONFIG_DIR, os.F_OK ): os.mkdir( CONFIG_DIR ) if not os.access( MAIN_LOG_DIR, os.F_OK ): os.mkdir( MAIN_LOG_DIR ) os.chmod( CONFIG_DIR, 0700 ) oldConfig= os.getenv('HOME') + '/.pebrotrc' if os.access( oldConfig, os.F_OK ): replaceOldConfig( oldConfig ) else: if not os.access( CONFIG_FILE, os.R_OK ): print _('I can\'t find %s, so I will copy the supplied pebrotrc there.') \ % CONFIG_FILE raw_input( _('Press Enter to continue...') ) shutil.copyfile( sys.prefix + '/share/doc/pebrot/pebrotrc', CONFIG_FILE ) #os.chmod( CONFIG_FILE, 0600 ) print _('Please edit %s and specify your login and password.') \ % CONFIG_FILE sys.exit() curses.wrapper( gogogo ) if __name__ == '__main__': sys.stderr.write( _('You should execute pebrot, not pebrot.py :)\n') )