2010-10-04 01:45:16 +00:00
# Copyright 2010 Le Coz Florent <louiz@louiz.org>
2010-01-10 20:14:17 +00:00
#
# This file is part of Poezio.
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# Poezio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Poezio. If not, see <http://www.gnu.org/licenses/>.
2010-01-31 05:33:54 +00:00
from gettext import ( bindtextdomain , textdomain , bind_textdomain_codeset ,
gettext as _ )
2010-06-13 15:40:03 +00:00
from os . path import isfile
2010-01-10 20:14:17 +00:00
2010-06-13 15:40:03 +00:00
from time import sleep
2010-06-30 09:56:03 +00:00
import os
2010-08-02 19:22:41 +00:00
import re
2010-09-11 04:07:04 +00:00
import sys
import shlex
2010-01-31 05:33:54 +00:00
import curses
2010-10-05 00:14:13 +00:00
import threading
2010-08-02 19:22:41 +00:00
import webbrowser
2010-01-21 01:54:50 +00:00
from datetime import datetime
2010-03-19 03:30:51 +00:00
import common
2010-08-20 20:55:42 +00:00
import theme
2010-11-09 22:41:31 +00:00
import logging
log = logging . getLogger ( __name__ )
2010-01-21 01:54:50 +00:00
2010-08-31 23:11:02 +00:00
import multiuserchat as muc
2010-11-09 20:37:39 +00:00
from connection import connection
2010-01-30 02:50:24 +00:00
from config import config
2010-09-26 18:01:38 +00:00
from tab import MucTab , InfoTab , PrivateTab , RosterInfoTab , ConversationTab
2010-11-15 11:59:09 +00:00
from logger import logger
2010-03-18 19:43:44 +00:00
from user import User
from room import Room
2010-10-31 18:57:48 +00:00
from roster import Roster , RosterGroup , roster
2010-10-17 17:27:07 +00:00
from contact import Contact , Resource
2010-06-10 01:17:59 +00:00
from message import Message
2010-09-14 02:11:07 +00:00
from text_buffer import TextBuffer
2010-07-08 19:27:53 +00:00
from keyboard import read_char
2010-10-16 18:47:39 +00:00
from common import jid_get_domain , is_jid
2010-06-13 13:51:02 +00:00
2010-08-05 22:24:23 +00:00
# http://xmpp.org/extensions/xep-0045.html#errorstatus
ERROR_AND_STATUS_CODES = {
2010-09-14 02:11:07 +00:00
' 401 ' : _ ( ' A password is required ' ) ,
' 403 ' : _ ( ' You are banned from the room ' ) ,
' 404 ' : _ ( ' The room does \' nt exist ' ) ,
' 405 ' : _ ( ' Your are not allowed to create a new room ' ) ,
' 406 ' : _ ( ' A reserved nick must be used ' ) ,
' 407 ' : _ ( ' You are not in the member list ' ) ,
' 409 ' : _ ( ' This nickname is already in use or has been reserved ' ) ,
' 503 ' : _ ( ' The maximum number of users has been reached ' ) ,
2010-08-05 22:24:23 +00:00
}
2010-09-02 00:44:18 +00:00
SHOW_NAME = {
' dnd ' : _ ( ' busy ' ) ,
' away ' : _ ( ' away ' ) ,
' xa ' : _ ( ' not available ' ) ,
' chat ' : _ ( ' chatty ' ) ,
' ' : _ ( ' available ' )
}
2010-10-05 00:14:13 +00:00
resize_lock = threading . Lock ( )
2010-11-09 20:37:39 +00:00
class Core ( object ) :
2010-01-10 20:14:17 +00:00
"""
2010-05-18 13:29:02 +00:00
User interface using ncurses
2010-01-10 20:14:17 +00:00
"""
2010-08-31 23:11:02 +00:00
def __init__ ( self , xmpp ) :
2010-10-20 23:51:34 +00:00
self . running = True
2010-08-31 23:11:02 +00:00
self . stdscr = curses . initscr ( )
self . init_curses ( self . stdscr )
self . xmpp = xmpp
2010-11-19 16:27:20 +00:00
default_tab = InfoTab ( self , " Info " ) if self . xmpp . anon \
else RosterInfoTab ( self )
2010-10-17 05:14:22 +00:00
default_tab . on_gain_focus ( )
2010-09-15 19:05:20 +00:00
self . tabs = [ default_tab ]
2010-09-14 02:11:07 +00:00
# a unique buffer used to store global informations
# that are displayed in almost all tabs, in an
# information window.
self . information_buffer = TextBuffer ( )
self . information_win_size = 2 # Todo, get this from config
2010-10-05 00:14:13 +00:00
self . resize_timer = None
2010-10-05 00:36:43 +00:00
self . previous_tab_nb = 0
2010-11-10 21:15:08 +00:00
self . own_nick = config . get ( ' own_nick ' , self . xmpp . boundjid . bare )
2010-11-24 05:41:06 +00:00
# global commands, available from all tabs (having an input, of course)
2010-01-13 01:04:30 +00:00
self . commands = {
2010-09-03 18:01:43 +00:00
' help ' : ( self . command_help , ' \ _o< KOIN KOIN KOIN ' ) ,
2010-07-01 21:38:57 +00:00
' join ' : ( self . command_join , _ ( " Usage: /join [room_name][@server][/nick] [password] \n Join: Join the specified room. You can specify a nickname after a slash (/). If no nickname is specified, you will use the default_nick in the configuration file. You can omit the room name: you will then join the room you \' re looking at (useful if you were kicked). You can also provide a room_name without specifying a server, the server of the room you ' re currently in will be used. You can also provide a password to join the room. \n Examples: \n /join room@server.tld \n /join room@server.tld/John \n /join room2 \n /join /me_again \n /join \n /join room@server.tld/my_nick password \n /join / password " ) ) ,
2010-06-13 00:09:28 +00:00
' quit ' : ( self . command_quit , _ ( " Usage: /quit \n Quit: Just disconnect from the server and exit poezio. " ) ) ,
' exit ' : ( self . command_quit , _ ( " Usage: /exit \n Exit: Just disconnect from the server and exit poezio. " ) ) ,
' next ' : ( self . rotate_rooms_right , _ ( " Usage: /next \n Next: Go to the next room. " ) ) ,
' n ' : ( self . rotate_rooms_right , _ ( " Usage: /n \n N: Go to the next room. " ) ) ,
' prev ' : ( self . rotate_rooms_left , _ ( " Usage: /prev \n Prev: Go to the previous room. " ) ) ,
' p ' : ( self . rotate_rooms_left , _ ( " Usage: /p \n P: Go to the previous room. " ) ) ,
' win ' : ( self . command_win , _ ( " Usage: /win <number> \n Win: Go to the specified room. " ) ) ,
' w ' : ( self . command_win , _ ( " Usage: /w <number> \n W: Go to the specified room. " ) ) ,
2010-10-31 19:03:49 +00:00
' show ' : ( self . command_show , _ ( " Usage: /show <availability> [status] \n Show: Change your availability and (optionaly) your status. The <availability> argument is one of \" avail, available, ok, here, chat, away, afk, dnd, busy, xa \" and the optional [status] argument will be your status message " ) ) ,
2010-06-13 00:09:28 +00:00
' away ' : ( self . command_away , _ ( " Usage: /away [message] \n Away: Sets your availability to away and (optional) sets your status message. This is equivalent to ' /show away [message] ' " ) ) ,
' busy ' : ( self . command_busy , _ ( " Usage: /busy [message] \n Busy: Sets your availability to busy and (optional) sets your status message. This is equivalent to ' /show busy [message] ' " ) ) ,
' avail ' : ( self . command_avail , _ ( " Usage: /avail [message] \n Avail: Sets your availability to available and (optional) sets your status message. This is equivalent to ' /show available [message] ' " ) ) ,
' available ' : ( self . command_avail , _ ( " Usage: /available [message] \n Available: Sets your availability to available and (optional) sets your status message. This is equivalent to ' /show available [message] ' " ) ) ,
' bookmark ' : ( self . command_bookmark , _ ( " Usage: /bookmark [roomname][/nick] \n Bookmark: Bookmark the specified room (you will then auto-join it on each poezio start). This commands uses the same syntaxe as /join. Type /help join for syntaxe examples. Note that when typing \" /bookmark \" on its own, the room will be bookmarked with the nickname you \' re currently using in this room (instead of default_nick) " ) ) ,
' set ' : ( self . command_set , _ ( " Usage: /set <option> [value] \n Set: Sets the value to the option in your configuration file. You can, for example, change your default nickname by doing `/set default_nick toto` or your resource with `/set resource blabla`. You can also set an empty value (nothing) by providing no [value] after <option>. " ) ) ,
2010-08-02 19:22:41 +00:00
' link ' : ( self . command_link , _ ( " Usage: /link [option] [number] \n Link: Interact with a link in the conversation. Available options are ' open ' , ' copy ' . Open just opens the link in the browser if it ' s http://, Copy just copy the link in the clipboard. An optional number can be provided, it indicates which link to interact with. " ) ) ,
2010-08-07 00:28:47 +00:00
' whois ' : ( self . command_whois , _ ( ' Usage: /whois <nickname> \n Whois: Request many informations about the user. ' ) ) ,
2010-08-20 20:55:42 +00:00
' theme ' : ( self . command_theme , _ ( ' Usage: /theme \n Theme: Reload the theme defined in the config file. ' ) ) ,
2010-01-13 01:04:30 +00:00
}
2010-01-27 19:46:08 +00:00
self . key_func = {
2010-06-10 01:17:59 +00:00
" KEY_PPAGE " : self . scroll_page_up ,
" KEY_NPAGE " : self . scroll_page_down ,
2010-01-28 00:55:02 +00:00
" KEY_F(5) " : self . rotate_rooms_left ,
2010-07-20 10:01:37 +00:00
" ^P " : self . rotate_rooms_left ,
2010-11-22 01:39:37 +00:00
' kLFT3 ' : self . rotate_rooms_left ,
2010-01-28 00:55:02 +00:00
" KEY_F(6) " : self . rotate_rooms_right ,
2010-11-22 01:39:37 +00:00
" ^N " : self . rotate_rooms_right ,
' kRIT3 ' : self . rotate_rooms_right ,
2010-09-14 02:11:07 +00:00
" KEY_F(7) " : self . shrink_information_win ,
" KEY_F(8) " : self . grow_information_win ,
2010-10-05 00:14:13 +00:00
" KEY_RESIZE " : self . call_for_resize ,
2010-10-27 23:11:35 +00:00
' M-e ' : self . go_to_important_room ,
2010-10-05 00:36:43 +00:00
' M-r ' : self . go_to_roster ,
2010-10-27 23:11:35 +00:00
' M-z ' : self . go_to_previous_tab ,
' M-v ' : self . move_separator ,
2010-01-27 19:46:08 +00:00
}
2010-08-31 23:11:02 +00:00
# Add handlers
self . xmpp . add_event_handler ( " session_start " , self . on_connected )
self . xmpp . add_event_handler ( " groupchat_presence " , self . on_groupchat_presence )
self . xmpp . add_event_handler ( " groupchat_message " , self . on_groupchat_message )
2010-11-09 22:41:31 +00:00
self . xmpp . add_event_handler ( " groupchat_subject " , self . on_groupchat_subject )
2010-08-31 23:11:02 +00:00
self . xmpp . add_event_handler ( " message " , self . on_message )
2010-09-26 18:01:38 +00:00
self . xmpp . add_event_handler ( " got_online " , self . on_got_online )
self . xmpp . add_event_handler ( " got_offline " , self . on_got_offline )
2010-09-08 23:00:55 +00:00
self . xmpp . add_event_handler ( " roster_update " , self . on_roster_update )
2010-10-17 17:27:07 +00:00
self . xmpp . add_event_handler ( " changed_status " , self . on_presence )
2010-09-14 02:11:07 +00:00
2010-10-31 18:57:48 +00:00
2010-09-14 02:11:07 +00:00
def grow_information_win ( self ) :
"""
"""
if self . information_win_size == 14 :
return
self . information_win_size + = 1
for tab in self . tabs :
2010-11-19 16:27:20 +00:00
tab . on_info_win_size_changed ( )
2010-09-14 02:11:07 +00:00
self . refresh_window ( )
def shrink_information_win ( self ) :
"""
"""
if self . information_win_size == 0 :
return
self . information_win_size - = 1
for tab in self . tabs :
2010-11-19 16:27:20 +00:00
tab . on_info_win_size_changed ( )
2010-09-14 02:11:07 +00:00
self . refresh_window ( )
2010-08-31 23:11:02 +00:00
2010-09-26 18:01:38 +00:00
def on_got_offline ( self , presence ) :
jid = presence [ ' from ' ]
2010-10-31 18:57:48 +00:00
contact = roster . get_contact_by_jid ( jid . bare )
2010-09-26 18:01:38 +00:00
if not contact :
return
2010-10-17 17:27:07 +00:00
resource = contact . get_resource_by_fulljid ( jid . full )
assert resource
self . information ( ' %s is offline ' % ( resource . get_jid ( ) ) , " Roster " )
contact . remove_resource ( resource )
if isinstance ( self . current_tab ( ) , RosterInfoTab ) :
self . refresh_window ( )
2010-09-26 18:01:38 +00:00
def on_got_online ( self , presence ) :
jid = presence [ ' from ' ]
2010-10-31 18:57:48 +00:00
contact = roster . get_contact_by_jid ( jid . bare )
2010-09-26 18:01:38 +00:00
if not contact :
2010-10-17 17:27:07 +00:00
# Todo, handle presence comming from contacts not in roster
2010-09-26 18:01:38 +00:00
return
2010-10-17 17:27:07 +00:00
resource = contact . get_resource_by_fulljid ( jid . full )
assert not resource
resource = Resource ( jid . full )
2010-09-26 18:01:38 +00:00
status = presence [ ' type ' ]
2010-11-10 21:15:08 +00:00
status_message = presence [ ' status ' ]
2010-10-17 17:27:07 +00:00
priority = presence . getPriority ( ) or 0
2010-11-10 21:15:08 +00:00
resource . set_status ( status_message )
2010-10-17 17:27:07 +00:00
resource . set_presence ( status )
resource . set_priority ( priority )
contact . add_resource ( resource )
self . information ( " %s is online ( %s ) " % ( resource . get_jid ( ) . full , status ) , " Roster " )
2010-09-26 18:01:38 +00:00
2010-08-31 23:11:02 +00:00
def on_connected ( self , event ) :
"""
Called when we are connected and authenticated
"""
self . information ( _ ( " Welcome on Poezio \ o/! " ) )
2010-10-16 18:47:39 +00:00
self . information ( _ ( " Your JID is %s " ) % self . xmpp . boundjid . full )
2010-08-31 23:11:02 +00:00
2010-09-08 23:00:55 +00:00
if not self . xmpp . anon :
# request the roster
self . xmpp . getRoster ( )
# send initial presence
2010-11-10 21:15:08 +00:00
self . xmpp . makePresence ( ) . send ( )
2010-08-31 23:11:02 +00:00
rooms = config . get ( ' rooms ' , ' ' )
if rooms == ' ' or not isinstance ( rooms , str ) :
return
rooms = rooms . split ( ' : ' )
for room in rooms :
args = room . split ( ' / ' )
if args [ 0 ] == ' ' :
return
roomname = args [ 0 ]
if len ( args ) == 2 :
nick = args [ 1 ]
else :
default = os . environ . get ( ' USER ' ) if os . environ . get ( ' USER ' ) else ' poezio '
nick = config . get ( ' default_nick ' , ' ' )
if nick == ' ' :
nick = default
2010-09-27 02:00:49 +00:00
self . open_new_room ( roomname , nick , False )
2010-08-31 23:11:02 +00:00
muc . join_groupchat ( self . xmpp , roomname , nick )
2010-09-08 23:00:55 +00:00
# if not self.xmpp.anon:
2010-08-31 23:11:02 +00:00
# Todo: SEND VCARD
return
if config . get ( ' jid ' , ' ' ) == ' ' : # Don't send the vcard if we're not anonymous
self . vcard_sender . start ( ) # because the user ALREADY has one on the server
def on_groupchat_presence ( self , presence ) :
"""
Triggered whenever a presence stanza is received from a user in a multi - user chat room .
Display the presence on the room window and update the
presence information of the concerned user
"""
from_nick = presence [ ' from ' ] . resource
from_room = presence [ ' from ' ] . bare
2010-09-02 01:49:11 +00:00
room = self . get_room_by_name ( from_room )
2010-08-31 23:11:02 +00:00
code = presence . find ( ' { jabber:client}status ' )
status_codes = set ( [ s . attrib [ ' code ' ] for s in presence . findall ( ' { http://jabber.org/protocol/muc#user}x/ { http://jabber.org/protocol/muc#user}status ' ) ] )
# Check if it's not an error presence.
if presence [ ' type ' ] == ' error ' :
return self . room_error ( presence , from_room )
2010-09-02 01:49:11 +00:00
if not room :
2010-08-31 23:11:02 +00:00
return
2010-09-02 00:26:37 +00:00
msg = None
affiliation = presence [ ' muc ' ] [ ' affiliation ' ]
show = presence [ ' show ' ]
status = presence [ ' status ' ]
role = presence [ ' muc ' ] [ ' role ' ]
jid = presence [ ' muc ' ] [ ' jid ' ]
typ = presence [ ' type ' ]
if not room . joined : # user in the room BEFORE us.
# ignore redondant presence message, see bug #1509
if from_nick not in [ user . nick for user in room . users ] :
new_user = User ( from_nick , affiliation , show , status , role )
room . users . append ( new_user )
2010-09-03 18:01:43 +00:00
if from_nick == room . own_nick :
2010-09-02 00:26:37 +00:00
room . joined = True
new_user . color = theme . COLOR_OWN_NICK
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , _ ( " Your nickname is %s " ) % ( from_nick ) )
2010-09-02 00:26:37 +00:00
if ' 170 ' in status_codes :
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , ' Warning: this room is publicly logged ' )
2010-08-31 23:11:02 +00:00
else :
2010-09-02 00:26:37 +00:00
change_nick = ' 303 ' in status_codes
kick = ' 307 ' in status_codes and typ == ' unavailable '
user = room . get_user_by_name ( from_nick )
# New user
if not user :
self . on_user_join ( room , from_nick , affiliation , show , status , role , jid )
# nick change
elif change_nick :
2010-09-02 01:11:21 +00:00
self . on_user_nick_change ( room , presence , user , from_nick , from_room )
2010-09-02 00:26:37 +00:00
# kick
elif kick :
2010-09-02 01:16:17 +00:00
self . on_user_kicked ( room , presence , user , from_nick )
2010-09-02 00:26:37 +00:00
# user quit
elif typ == ' unavailable ' :
2010-09-02 01:19:08 +00:00
self . on_user_leave_groupchat ( room , user , jid , status , from_nick , from_room )
2010-09-02 00:26:37 +00:00
# status change
else :
2010-09-02 01:22:22 +00:00
self . on_user_change_status ( room , user , from_nick , from_room , affiliation , role , show , status )
2010-09-14 02:11:07 +00:00
self . refresh_window ( )
2010-10-17 05:14:22 +00:00
self . doupdate ( )
2010-08-31 23:11:02 +00:00
2010-09-02 00:26:37 +00:00
def on_user_join ( self , room , from_nick , affiliation , show , status , role , jid ) :
"""
When a new user joins a groupchat
"""
room . users . append ( User ( from_nick , affiliation ,
show , status , role ) )
hide_exit_join = config . get ( ' hide_exit_join ' , - 1 )
if hide_exit_join != 0 :
if not jid . full :
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , _ ( ' %(spec)s " [ %(nick)s ] " joined the room ' ) % { ' nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' spec ' : theme . CHAR_JOIN . replace ( ' " ' , ' \\ " ' ) } , colorized = True )
2010-09-02 00:26:37 +00:00
else :
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , _ ( ' %(spec)s " [ %(nick)s ] " " ( %(jid)s ) " joined the room ' ) % { ' spec ' : theme . CHAR_JOIN . replace ( ' " ' , ' \\ " ' ) , ' nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' jid ' : jid . full } , colorized = True )
2010-09-02 00:26:37 +00:00
2010-09-02 01:11:21 +00:00
def on_user_nick_change ( self , room , presence , user , from_nick , from_room ) :
new_nick = presence . find ( ' { http://jabber.org/protocol/muc#user}x/ { http://jabber.org/protocol/muc#user}item ' ) . attrib [ ' nick ' ]
if user . nick == room . own_nick :
room . own_nick = new_nick
# also change our nick in all private discussion of this room
2010-09-14 02:11:07 +00:00
for _tab in self . tabs :
2010-09-20 23:35:07 +00:00
if isinstance ( _tab , PrivateTab ) and _tab . get_name ( ) . split ( ' / ' , 1 ) [ 0 ] == room . name :
_tab . get_room ( ) . own_nick = new_nick
2010-09-02 01:11:21 +00:00
user . change_nick ( new_nick )
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , _ ( ' " [ %(old)s ] " is now known as " [ %(new)s ] " ' ) % { ' old ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' new ' : new_nick . replace ( ' " ' , ' \\ " ' ) } , colorized = True )
2010-09-02 01:11:21 +00:00
# rename the private tabs if needed
private_room = self . get_room_by_name ( ' %s / %s ' % ( from_room , from_nick ) )
if private_room :
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( private_room , _ ( ' " [ %(old_nick)s ] " is now known as " [ %(new_nick)s ] " ' ) % { ' old_nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' new_nick ' : new_nick . replace ( ' " ' , ' \\ " ' ) } , colorized = True )
2010-09-20 23:35:07 +00:00
new_jid = private_room . name . split ( ' / ' , 1 ) [ 0 ] + ' / ' + new_nick
2010-09-14 02:11:07 +00:00
private_room . name = new_jid
2010-09-02 01:11:21 +00:00
2010-09-02 01:16:17 +00:00
def on_user_kicked ( self , room , presence , user , from_nick ) :
"""
When someone is kicked
"""
room . users . remove ( user )
by = presence . find ( ' { http://jabber.org/protocol/muc#user}x/ { http://jabber.org/protocol/muc#user}item/ { http://jabber.org/protocol/muc#user}actor ' )
reason = presence . find ( ' { http://jabber.org/protocol/muc#user}x/ { http://jabber.org/protocol/muc#user}item/ { http://jabber.org/protocol/muc#user}reason ' )
2010-09-11 15:45:28 +00:00
by = by . attrib [ ' jid ' ] if by is not None else None
2010-10-05 00:19:48 +00:00
reason = reason . text if reason else ' '
2010-09-02 01:16:17 +00:00
if from_nick == room . own_nick : # we are kicked
room . disconnect ( )
if by :
2010-09-14 02:11:07 +00:00
kick_msg = _ ( ' %(spec)s [You] have been kicked by " [ %(by)s ] " ' ) % { ' spec ' : theme . CHAR_KICK . replace ( ' " ' , ' \\ " ' ) , ' by ' : by }
2010-09-02 01:16:17 +00:00
else :
2010-09-11 04:24:09 +00:00
kick_msg = _ ( ' %(spec)s [You] have been kicked. ' ) % { ' spec ' : theme . CHAR_KICK . replace ( ' " ' , ' \\ " ' ) }
2010-09-02 01:16:17 +00:00
# try to auto-rejoin
if config . get ( ' autorejoin ' , ' false ' ) == ' true ' :
muc . join_groupchat ( self . xmpp , room . name , room . own_nick )
else :
if by :
2010-09-11 15:45:28 +00:00
kick_msg = _ ( ' %(spec)s " [ %(nick)s ] " has been kicked by " [ %(by)s ] " ' ) % { ' spec ' : theme . CHAR_KICK . replace ( ' " ' , ' \\ " ' ) , ' nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' by ' : by . replace ( ' " ' , ' \\ " ' ) }
2010-09-02 01:16:17 +00:00
else :
2010-09-11 04:24:09 +00:00
kick_msg = _ ( ' %(spec)s " [ %(nick)s ] " has been kicked ' ) % { ' spec ' : theme . CHAR_KICK , ' nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) }
2010-09-02 01:16:17 +00:00
if reason :
kick_msg + = _ ( ' Reason: %(reason)s ' ) % { ' reason ' : reason }
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , kick_msg , colorized = True )
2010-09-02 01:16:17 +00:00
2010-09-02 01:19:08 +00:00
def on_user_leave_groupchat ( self , room , user , jid , status , from_nick , from_room ) :
"""
When an user leaves a groupchat
"""
room . users . remove ( user )
2010-10-05 00:22:18 +00:00
if room . own_nick == user . nick :
# We are now out of the room. Happens with some buggy (? not sure) servers
room . disconnect ( )
2010-09-02 01:19:08 +00:00
hide_exit_join = config . get ( ' hide_exit_join ' , - 1 ) if config . get ( ' hide_exit_join ' , - 1 ) > = - 1 else - 1
if hide_exit_join == - 1 or user . has_talked_since ( hide_exit_join ) :
if not jid . full :
2010-09-11 04:24:09 +00:00
leave_msg = _ ( ' %(spec)s " [ %(nick)s ] " has left the room ' ) % { ' nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' spec ' : theme . CHAR_QUIT . replace ( ' " ' , ' \\ " ' ) }
2010-09-02 01:19:08 +00:00
else :
2010-10-04 00:27:40 +00:00
leave_msg = _ ( ' %(spec)s " [ %(nick)s ] " " ( %(jid)s ) " has left the room ' ) % { ' spec ' : theme . CHAR_QUIT . replace ( ' " ' , ' \\ " ' ) , ' nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' jid ' : jid . full . replace ( ' " ' , ' \\ " ' ) }
2010-09-02 01:19:08 +00:00
if status :
leave_msg + = ' ( %s ) ' % status
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , leave_msg , colorized = True )
2010-09-02 01:19:08 +00:00
private_room = self . get_room_by_name ( ' %s / %s ' % ( from_room , from_nick ) )
if private_room :
if not status :
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( private_room , _ ( ' %(spec)s " [ %(nick)s ] " has left the room ' ) % { ' nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' spec ' : theme . CHAR_QUIT . replace ( ' " ' , ' \\ " ' ) } , colorized = True )
2010-09-02 01:19:08 +00:00
else :
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( private_room , _ ( ' %(spec)s " [ %(nick)s ] " has left the room " ( %(status)s ) " ' ) % { ' nick ' : from_nick . replace ( ' " ' , ' \\ " ' ) , ' spec ' : theme . CHAR_QUIT , ' status ' : status . replace ( ' " ' , ' \\ " ' ) } , colorized = True )
2010-09-02 01:19:08 +00:00
2010-09-02 01:22:22 +00:00
def on_user_change_status ( self , room , user , from_nick , from_room , affiliation , role , show , status ) :
"""
When an user changes her status
"""
# build the message
2010-09-20 23:35:07 +00:00
display_message = False # flag to know if something significant enough
# to be displayed has changed
2010-09-14 02:11:07 +00:00
msg = _ ( ' " %s " changed: ' ) % from_nick . replace ( ' " ' , ' \\ " ' )
2010-09-02 01:22:22 +00:00
if affiliation != user . affiliation :
2010-09-14 02:11:07 +00:00
msg + = _ ( ' affiliation: %s , ' ) % affiliation
2010-09-20 23:35:07 +00:00
display_message = True
2010-09-02 01:22:22 +00:00
if role != user . role :
2010-09-14 02:11:07 +00:00
msg + = _ ( ' role: %s , ' ) % role
2010-09-20 23:35:07 +00:00
display_message = True
2010-09-03 18:01:43 +00:00
if show != user . show and show in list ( SHOW_NAME . keys ( ) ) :
2010-09-14 02:11:07 +00:00
msg + = _ ( ' show: %s , ' ) % SHOW_NAME [ show ]
2010-09-20 23:35:07 +00:00
display_message = True
if status and status != user . status :
2010-09-14 02:11:07 +00:00
msg + = _ ( ' status: %s , ' ) % status
2010-09-20 23:35:07 +00:00
display_message = True
if not display_message :
return
2010-09-14 02:11:07 +00:00
msg = msg [ : - 2 ] # remove the last ", "
2010-09-02 01:22:22 +00:00
hide_status_change = config . get ( ' hide_status_change ' , - 1 ) if config . get ( ' hide_status_change ' , - 1 ) > = - 1 else - 1
if ( hide_status_change == - 1 or \
user . has_talked_since ( hide_status_change ) or \
user . nick == room . own_nick ) \
and \
( affiliation != user . affiliation or \
role != user . role or \
show != user . show or \
status != user . status ) :
# display the message in the room
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , msg , colorized = True )
2010-09-02 01:22:22 +00:00
private_room = self . get_room_by_name ( ' %s / %s ' % ( from_room , from_nick ) )
if private_room : # display the message in private
2010-09-20 23:35:07 +00:00
self . add_message_to_text_buffer ( private_room , msg , colorized = True )
2010-09-02 01:22:22 +00:00
# finally, effectively change the user status
user . update ( affiliation , show , status , role )
2010-08-31 23:11:02 +00:00
def on_message ( self , message ) :
"""
When receiving private message from a muc OR a normal message
( from one of our contacts )
"""
if message [ ' type ' ] == ' groupchat ' :
2010-11-11 04:22:37 +00:00
return
2010-08-31 23:11:02 +00:00
# Differentiate both type of messages, and call the appropriate handler.
jid_from = message [ ' from ' ]
2010-09-14 02:11:07 +00:00
for tab in self . tabs :
2010-11-24 05:55:36 +00:00
if tab . get_name ( ) == jid_from . full : # check all the MUC we are in
2010-09-08 23:00:55 +00:00
if message [ ' type ' ] == ' error ' :
2010-09-14 02:11:07 +00:00
return self . room_error ( message , tab . get_room ( ) . name )
2010-09-08 23:00:55 +00:00
else :
return self . on_groupchat_private_message ( message )
2010-08-31 23:11:02 +00:00
return self . on_normal_message ( message )
def on_groupchat_private_message ( self , message ) :
"""
We received a Private Message ( from someone in a Muc )
"""
jid = message [ ' from ' ]
2010-10-17 05:14:22 +00:00
nick_from = jid . resource
2010-09-02 01:11:21 +00:00
room_from = jid . bare
2010-08-31 23:11:02 +00:00
room = self . get_room_by_name ( jid . full ) # get the tab with the private conversation
if not room : # It's the first message we receive: create the tab
2010-09-03 18:01:43 +00:00
room = self . open_private_window ( room_from , nick_from , False )
2010-08-31 23:11:02 +00:00
if not room :
return
body = message [ ' body ' ]
2010-11-24 05:41:06 +00:00
room . add_message ( body , time = None , nickname = nick_from ,
colorized = False ,
forced_user = self . get_room_by_name ( room_from ) . get_user_by_name ( nick_from ) )
2010-09-14 02:11:07 +00:00
self . refresh_window ( )
2010-10-17 05:14:22 +00:00
self . doupdate ( )
2010-08-31 23:11:02 +00:00
2010-09-27 01:40:34 +00:00
def focus_tab_named ( self , tab_name ) :
for tab in self . tabs :
if tab . get_name ( ) == tab_name :
self . command_win ( ' %s ' % ( tab . nb , ) )
2010-08-31 23:11:02 +00:00
def on_normal_message ( self , message ) :
"""
When receiving " normal " messages ( from someone in our roster )
"""
2010-11-11 04:22:37 +00:00
jid = message [ ' from ' ]
2010-09-26 18:35:22 +00:00
body = message [ ' body ' ]
if not body :
return
2010-11-11 04:22:37 +00:00
# We first check if we have a conversation opened with this precise resource
conversation = self . get_tab_by_name ( jid . full )
if not conversation :
# If not, we search for a conversation with the bare jid
conversation = self . get_tab_by_name ( jid . bare )
if not conversation :
# We create the conversation with the bare Jid if nothing was found
conversation = self . open_conversation_window ( jid . bare , False )
# room = self.open_conversation_window(jid, False)
self . add_message_to_text_buffer ( conversation . get_room ( ) , body , None , jid . full )
if self . current_tab ( ) is not conversation :
conversation . set_color_state ( theme . COLOR_TAB_PRIVATE )
2010-09-26 18:01:38 +00:00
self . refresh_window ( )
2010-09-08 23:00:55 +00:00
return
def on_presence ( self , presence ) :
"""
"""
2010-10-17 17:27:07 +00:00
jid = presence [ ' from ' ]
2010-10-31 18:57:48 +00:00
contact = roster . get_contact_by_jid ( jid . bare )
2010-10-17 17:27:07 +00:00
if not contact :
return
resource = contact . get_resource_by_fulljid ( jid . full )
if not resource :
return
status = presence [ ' type ' ]
2010-11-10 21:15:08 +00:00
status_message = presence [ ' status ' ]
2010-10-17 17:27:07 +00:00
priority = presence . getPriority ( ) or 0
resource . set_presence ( status )
resource . set_priority ( priority )
2010-11-10 21:15:08 +00:00
resource . set_status ( status_message )
2010-10-17 17:27:07 +00:00
if isinstance ( self . current_tab ( ) , RosterInfoTab ) :
self . refresh_window ( )
2010-01-21 01:54:50 +00:00
2010-09-08 23:00:55 +00:00
def on_roster_update ( self , iq ) :
"""
A subscription changed , or we received a roster item
after a roster request , etc
"""
2010-09-26 18:01:38 +00:00
for item in iq . findall ( ' { jabber:iq:roster}query/ { jabber:iq:roster}item ' ) :
jid = item . attrib [ ' jid ' ]
2010-10-31 18:57:48 +00:00
contact = roster . get_contact_by_jid ( jid )
2010-09-26 18:01:38 +00:00
if not contact :
contact = Contact ( jid )
2010-10-31 18:57:48 +00:00
roster . add_contact ( contact , jid )
2010-09-26 18:01:38 +00:00
if ' ask ' in item . attrib :
contact . set_ask ( item . attrib [ ' ask ' ] )
else :
contact . set_ask ( None )
2010-09-27 01:40:34 +00:00
if ' name ' in item . attrib :
contact . set_name ( item . attrib [ ' name ' ] )
else :
contact . set_name ( None )
2010-09-26 18:01:38 +00:00
if item . attrib [ ' subscription ' ] :
contact . set_subscription ( item . attrib [ ' subscription ' ] )
groups = item . findall ( ' { jabber:iq:roster}group ' )
2010-10-31 18:57:48 +00:00
roster . edit_groups_of_contact ( contact , [ group . text for group in groups ] )
2010-09-26 18:01:38 +00:00
if isinstance ( self . current_tab ( ) , RosterInfoTab ) :
self . refresh_window ( )
2010-09-08 23:00:55 +00:00
2010-10-05 00:14:13 +00:00
def call_for_resize ( self ) :
"""
Starts a very short timer . If no other terminal resize
occured in this delay then poezio is REALLY resize .
This is to avoid multiple unnecessary software resizes ( this
can be heavy on resource on slow computers or networks )
"""
2010-11-22 02:39:56 +00:00
with resize_lock :
if self . resize_timer :
# a recent terminal resize occured.
# Cancel the programmed software resize
self . resize_timer . cancel ( )
# add the new timer
self . resize_timer = threading . Timer ( 0.1 , self . resize_window )
self . resize_timer . start ( )
# self.resize_window()
2010-10-05 00:14:13 +00:00
2010-07-08 20:20:43 +00:00
def resize_window ( self ) :
"""
Resize the whole screen
"""
2010-10-05 00:14:13 +00:00
with resize_lock :
2010-10-27 22:49:52 +00:00
# self.resize_timer = None
2010-10-05 00:14:13 +00:00
for tab in self . tabs :
2010-11-19 16:27:20 +00:00
tab . resize ( )
2010-10-05 00:14:13 +00:00
self . refresh_window ( )
2010-07-08 20:20:43 +00:00
2010-08-31 23:11:02 +00:00
def main_loop ( self ) :
2010-05-18 13:29:02 +00:00
"""
main loop waiting for the user to press a key
"""
2010-08-04 23:54:02 +00:00
self . refresh_window ( )
2010-10-20 23:51:34 +00:00
while self . running :
2010-10-17 05:14:22 +00:00
self . doupdate ( )
2010-08-31 23:11:02 +00:00
char = read_char ( self . stdscr )
2010-07-08 19:27:53 +00:00
# search for keyboard shortcut
2010-09-03 18:01:43 +00:00
if char in list ( self . key_func . keys ( ) ) :
2010-07-08 19:27:53 +00:00
self . key_func [ char ] ( )
2010-01-27 19:46:08 +00:00
else :
2010-09-14 02:11:07 +00:00
self . do_command ( char )
2010-01-27 19:46:08 +00:00
2010-09-14 02:11:07 +00:00
def current_tab ( self ) :
2010-05-18 13:29:02 +00:00
"""
returns the current room , the one we are viewing
"""
2010-09-14 02:11:07 +00:00
return self . tabs [ 0 ]
2010-01-26 17:10:37 +00:00
2010-09-26 18:01:38 +00:00
def get_conversation_by_jid ( self , jid ) :
"""
Return the room of the ConversationTab with the given jid
"""
for tab in self . tabs :
if isinstance ( tab , ConversationTab ) :
if tab . get_name ( ) == jid :
return tab . get_room ( )
return None
2010-11-11 04:22:37 +00:00
def get_tab_by_name ( self , name ) :
"""
Get the tab with the given name .
"""
for tab in self . tabs :
if tab . get_name ( ) == name :
return tab
return None
2010-01-26 17:10:37 +00:00
def get_room_by_name ( self , name ) :
2010-05-18 13:29:02 +00:00
"""
returns the room that has this name
"""
2010-09-14 02:11:07 +00:00
for tab in self . tabs :
if ( isinstance ( tab , MucTab ) or
isinstance ( tab , PrivateTab ) ) and tab . get_name ( ) == name :
return tab . get_room ( )
2010-05-18 13:29:02 +00:00
return None
2010-01-26 17:10:37 +00:00
2010-01-21 01:54:50 +00:00
def init_curses ( self , stdscr ) :
2010-05-18 13:29:02 +00:00
"""
ncurses initialization
"""
2010-10-17 05:14:22 +00:00
curses . curs_set ( 1 )
2010-01-21 01:54:50 +00:00
curses . noecho ( )
2010-10-17 05:14:22 +00:00
# curses.raw()
theme . init_colors ( )
2010-01-27 19:46:08 +00:00
stdscr . keypad ( True )
2010-01-21 01:54:50 +00:00
2010-01-26 17:10:37 +00:00
def reset_curses ( self ) :
2010-05-18 13:29:02 +00:00
"""
Reset terminal capabilities to what they were before ncurses
init
"""
curses . echo ( )
2010-02-14 04:18:34 +00:00
curses . nocbreak ( )
2010-01-27 17:02:13 +00:00
curses . endwin ( )
2010-01-26 17:10:37 +00:00
2010-08-04 00:17:53 +00:00
def refresh_window ( self ) :
"""
Refresh everything
"""
2010-09-14 02:11:07 +00:00
self . current_tab ( ) . set_color_state ( theme . COLOR_TAB_CURRENT )
2010-10-31 18:57:48 +00:00
self . current_tab ( ) . refresh ( self . tabs , self . information_buffer , roster )
2010-10-17 12:43:56 +00:00
self . doupdate ( )
2010-08-04 00:17:53 +00:00
2010-08-31 23:11:02 +00:00
def open_new_room ( self , room , nick , focus = True ) :
2010-05-18 13:29:02 +00:00
"""
2010-09-14 02:11:07 +00:00
Open a new MucTab containing a muc Room , using the specified nick
2010-05-18 13:29:02 +00:00
"""
2010-09-14 02:11:07 +00:00
r = Room ( room , nick )
2010-11-19 16:27:20 +00:00
new_tab = MucTab ( self , r )
2010-09-14 02:11:07 +00:00
if self . current_tab ( ) . nb == 0 :
self . tabs . append ( new_tab )
2010-02-11 04:59:58 +00:00
else :
2010-09-14 02:11:07 +00:00
for ta in self . tabs :
if ta . nb == 0 :
self . tabs . insert ( self . tabs . index ( ta ) , new_tab )
2010-02-11 04:59:58 +00:00
break
2010-08-31 23:11:02 +00:00
if focus :
2010-09-14 02:11:07 +00:00
self . command_win ( " %s " % new_tab . nb )
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-01-21 01:54:50 +00:00
2010-10-05 00:36:43 +00:00
def go_to_roster ( self ) :
self . command_win ( ' 0 ' )
def go_to_previous_tab ( self ) :
self . command_win ( ' %s ' % ( self . previous_tab_nb , ) )
2010-07-08 19:27:53 +00:00
def go_to_important_room ( self ) :
"""
Go to the next room with activity , in this order :
- A personal conversation with a new message
- A Muc with an highlight
- A Muc with any new message
"""
2010-09-14 02:11:07 +00:00
for tab in self . tabs :
if tab . get_color_state ( ) == theme . COLOR_TAB_PRIVATE :
self . command_win ( ' %s ' % tab . nb )
2010-07-08 19:27:53 +00:00
return
2010-09-14 02:11:07 +00:00
for tab in self . tabs :
if tab . get_color_state ( ) == theme . COLOR_TAB_HIGHLIGHT :
self . command_win ( ' %s ' % tab . nb )
2010-07-08 19:27:53 +00:00
return
2010-09-14 02:11:07 +00:00
for tab in self . tabs :
if tab . get_color_state ( ) == theme . COLOR_TAB_NEW_MESSAGE :
self . command_win ( ' %s ' % tab . nb )
2010-08-04 00:17:53 +00:00
return
2010-07-08 19:27:53 +00:00
2010-02-11 04:59:58 +00:00
def rotate_rooms_right ( self , args = None ) :
2010-05-18 13:29:02 +00:00
"""
rotate the rooms list to the right
"""
2010-09-14 02:11:07 +00:00
self . current_tab ( ) . on_lose_focus ( )
self . tabs . append ( self . tabs . pop ( 0 ) )
self . current_tab ( ) . on_gain_focus ( )
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-01-21 01:54:50 +00:00
2010-02-11 04:59:58 +00:00
def rotate_rooms_left ( self , args = None ) :
2010-05-18 13:29:02 +00:00
"""
rotate the rooms list to the right
"""
2010-09-14 02:11:07 +00:00
self . current_tab ( ) . on_lose_focus ( )
self . tabs . insert ( 0 , self . tabs . pop ( ) )
self . current_tab ( ) . on_gain_focus ( )
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-01-21 01:54:50 +00:00
2010-06-10 01:17:59 +00:00
def scroll_page_down ( self , args = None ) :
2010-09-14 02:11:07 +00:00
self . current_tab ( ) . on_scroll_down ( )
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-06-10 01:17:59 +00:00
def scroll_page_up ( self , args = None ) :
2010-09-14 02:11:07 +00:00
self . current_tab ( ) . on_scroll_up ( )
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-06-10 01:17:59 +00:00
2010-08-31 23:11:02 +00:00
def room_error ( self , error , room_name ) :
2010-05-18 13:29:02 +00:00
"""
Display the error on the room window
"""
2010-08-31 23:11:02 +00:00
room = self . get_room_by_name ( room_name )
msg = error [ ' error ' ] [ ' type ' ]
condition = error [ ' error ' ] [ ' condition ' ]
code = error [ ' error ' ] [ ' code ' ]
body = error [ ' error ' ] [ ' text ' ]
if not body :
2010-09-03 18:01:43 +00:00
if code in list ( ERROR_AND_STATUS_CODES . keys ( ) ) :
2010-08-05 22:24:51 +00:00
body = ERROR_AND_STATUS_CODES [ code ]
else :
2010-08-31 23:11:02 +00:00
body = condition or _ ( ' Unknown error ' )
if code :
2010-09-14 02:11:07 +00:00
msg = _ ( ' Error: %(code)s - %(msg)s : %(body)s ' ) % { ' msg ' : msg , ' body ' : body , ' code ' : code }
self . add_message_to_text_buffer ( room , msg )
2010-08-31 23:11:02 +00:00
else :
2010-09-14 02:11:07 +00:00
msg = _ ( ' Error: %(msg)s : %(body)s ' ) % { ' msg ' : msg , ' body ' : body }
self . add_message_to_text_buffer ( room , msg )
2010-03-24 18:01:22 +00:00
if code == ' 401 ' :
2010-09-14 02:11:07 +00:00
msg = _ ( ' To provide a password in order to join the room, type " /join / password " (replace " password " by the real password) ' )
self . add_message_to_text_buffer ( room , msg )
2010-08-05 22:24:23 +00:00
if code == ' 409 ' :
2010-08-07 00:57:36 +00:00
if config . get ( ' alternative_nickname ' , ' ' ) != ' ' :
self . command_join ( ' %s / %s ' % ( room . name , room . own_nick + config . get ( ' alternative_nickname ' , ' ' ) ) )
else :
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , _ ( ' You can join the room with an other nick, by typing " /join /other_nick " ' ) )
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-03-24 18:01:22 +00:00
2010-11-10 21:15:08 +00:00
def open_conversation_window ( self , jid , focus = True ) :
2010-09-26 18:01:38 +00:00
"""
open a new conversation tab and focus it if needed
"""
2010-11-10 21:15:08 +00:00
text_buffer = TextBuffer ( )
2010-11-19 16:27:20 +00:00
new_tab = ConversationTab ( self , text_buffer , jid )
2010-09-26 18:01:38 +00:00
# insert it in the rooms
if self . current_tab ( ) . nb == 0 :
self . tabs . append ( new_tab )
else :
for ta in self . tabs :
if ta . nb == 0 :
self . tabs . insert ( self . tabs . index ( ta ) , new_tab )
break
if focus : # focus the room if needed
self . command_win ( ' %s ' % ( new_tab . nb ) )
self . refresh_window ( )
2010-11-11 04:22:37 +00:00
return new_tab
2010-09-26 18:01:38 +00:00
2010-06-13 13:51:02 +00:00
def open_private_window ( self , room_name , user_nick , focus = True ) :
2010-09-03 18:01:43 +00:00
complete_jid = room_name + ' / ' + user_nick
2010-09-14 02:11:07 +00:00
for tab in self . tabs : # if the room exists, focus it and return
if isinstance ( tab , PrivateTab ) :
if tab . get_name ( ) == complete_jid :
self . command_win ( ' %s ' % tab . nb )
2010-06-13 13:51:02 +00:00
return
# create the new tab
2010-09-02 01:11:21 +00:00
room = self . get_room_by_name ( room_name )
2010-07-19 19:38:33 +00:00
if not room :
return None
own_nick = room . own_nick
2010-09-14 02:11:07 +00:00
r = Room ( complete_jid , own_nick ) # PrivateRoom here
2010-11-19 16:27:20 +00:00
new_tab = PrivateTab ( self , r )
2010-10-31 18:57:48 +00:00
# insert it in the tabs
2010-09-14 02:11:07 +00:00
if self . current_tab ( ) . nb == 0 :
self . tabs . append ( new_tab )
2010-06-13 13:51:02 +00:00
else :
2010-09-14 02:11:07 +00:00
for ta in self . tabs :
if ta . nb == 0 :
self . tabs . insert ( self . tabs . index ( ta ) , new_tab )
2010-06-13 13:51:02 +00:00
break
if focus : # focus the room if needed
2010-09-14 02:11:07 +00:00
self . command_win ( ' %s ' % ( new_tab . nb ) )
2010-06-13 13:51:02 +00:00
# self.window.new_room(r)
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-06-13 13:51:02 +00:00
return r
2010-11-09 22:41:31 +00:00
def on_groupchat_subject ( self , message ) :
"""
triggered when the topic is changed
"""
nick_from = message [ ' mucnick ' ]
room_from = message . getMucroom ( )
room = self . get_room_by_name ( room_from )
subject = message [ ' subject ' ]
if not subject :
return
2010-11-11 04:37:48 +00:00
self . add_message_to_text_buffer ( room , _ ( " %(nick)s set the subject to: %(subject)s " ) % { ' nick ' : nick_from , ' subject ' : subject } , time = None )
2010-11-09 22:41:31 +00:00
room . topic = subject . replace ( ' \n ' , ' | ' )
self . refresh_window ( )
2010-08-31 23:11:02 +00:00
def on_groupchat_message ( self , message ) :
2010-05-18 13:29:02 +00:00
"""
2010-08-31 23:11:02 +00:00
Triggered whenever a message is received from a multi - user chat room .
2010-05-18 13:29:02 +00:00
"""
2010-08-31 23:11:02 +00:00
delay_tag = message . find ( ' { urn:xmpp:delay}delay ' )
if delay_tag is not None :
2010-03-19 03:30:51 +00:00
delayed = True
2010-08-31 23:11:02 +00:00
date = common . datetime_tuple ( delay_tag . attrib [ ' stamp ' ] )
2010-03-19 03:30:51 +00:00
else :
2010-07-26 00:51:07 +00:00
# We support the OLD and deprecated XEP: http://xmpp.org/extensions/xep-0091.html
# But it sucks, please, Jabber servers, don't do this :(
2010-08-31 23:11:02 +00:00
delay_tag = message . find ( ' { jabber:x:delay}x ' )
if delay_tag is not None :
2010-07-26 00:51:07 +00:00
delayed = True
2010-08-31 23:11:02 +00:00
date = common . datetime_tuple ( delay_tag . attrib [ ' stamp ' ] )
2010-07-26 00:51:07 +00:00
else :
delayed = False
2010-08-31 23:11:02 +00:00
date = None
2010-09-02 00:44:18 +00:00
nick_from = message [ ' mucnick ' ]
2010-08-31 23:11:02 +00:00
room_from = message . getMucroom ( )
2010-09-08 23:00:55 +00:00
if message [ ' type ' ] == ' error ' : # Check if it's an error
return self . room_error ( message , from_room )
2010-09-02 00:44:18 +00:00
if nick_from == room_from :
nick_from = None
2010-09-02 01:49:11 +00:00
room = self . get_room_by_name ( room_from )
2010-11-24 05:41:06 +00:00
tab = self . get_tab_by_name ( room_from )
if tab and tab . get_room ( ) and tab . get_room ( ) . get_user_by_name ( nick_from ) and \
tab . get_room ( ) . get_user_by_name ( nick_from ) in tab . ignores :
2010-06-12 17:28:51 +00:00
return
2010-08-31 23:11:02 +00:00
if not room :
self . information ( _ ( " message received for a non-existing room: %s " ) % ( room_from ) )
2010-01-28 00:55:02 +00:00
return
2010-09-02 00:44:18 +00:00
body = message [ ' body ' ]
2010-11-09 22:41:31 +00:00
if body :
2010-10-11 23:09:16 +00:00
date = date if delayed == True else None
2010-11-15 13:16:30 +00:00
# if not delayed:
# logger.groupchat(room_from, nick_from, body)
2010-10-11 23:09:16 +00:00
self . add_message_to_text_buffer ( room , body , date , nick_from )
2010-11-09 22:41:31 +00:00
self . refresh_window ( )
self . doupdate ( )
2010-01-21 01:54:50 +00:00
2010-09-14 02:11:07 +00:00
def add_message_to_text_buffer ( self , room , txt , time = None , nickname = None , colorized = False ) :
2010-03-18 19:43:44 +00:00
"""
2010-09-14 02:11:07 +00:00
Add the message to the room if possible , else , add it to the Info window
( in the Info tab of the info window in the RosterTab )
2010-03-18 19:43:44 +00:00
"""
2010-09-14 02:11:07 +00:00
if not room :
self . information ( ' Error, trying to add a message in no room: %s ' % txt )
2010-06-10 01:17:59 +00:00
else :
2010-09-14 02:11:07 +00:00
room . add_message ( txt , time , nickname , colorized )
self . refresh_window ( )
2010-01-13 01:04:30 +00:00
2010-08-02 19:22:41 +00:00
def command_help ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ help < command_name >
"""
2010-08-02 19:22:41 +00:00
args = arg . split ( )
2010-01-30 04:42:29 +00:00
if len ( args ) == 0 :
2010-06-13 00:09:28 +00:00
msg = _ ( ' Available commands are: ' )
2010-09-03 18:01:43 +00:00
for command in list ( self . commands . keys ( ) ) :
2010-01-30 04:42:29 +00:00
msg + = " %s " % command
2010-11-24 05:41:06 +00:00
for command in list ( self . current_tab ( ) . commands . keys ( ) ) :
msg + = " %s " % command
2010-01-31 05:33:54 +00:00
msg + = _ ( " \n Type /help <command_name> to know what each command does " )
2010-10-31 19:02:57 +00:00
if len ( args ) > = 1 :
2010-09-03 18:01:43 +00:00
if args [ 0 ] in list ( self . commands . keys ( ) ) :
2010-01-30 04:42:29 +00:00
msg = self . commands [ args [ 0 ] ] [ 1 ]
else :
2010-01-31 05:33:54 +00:00
msg = _ ( ' Unknown command: %s ' ) % args [ 0 ]
2010-09-14 02:11:07 +00:00
self . information ( msg )
2010-01-30 04:42:29 +00:00
2010-08-07 00:28:47 +00:00
def command_whois ( self , arg ) :
"""
/ whois < nickname >
"""
2010-08-31 23:11:02 +00:00
# TODO
return
2010-09-11 04:07:04 +00:00
# check shlex here
try :
args = shlex . split ( arg )
except ValueError as error :
2010-09-14 02:11:07 +00:00
return self . information ( str ( error ) , _ ( " Error " ) )
2010-08-07 00:28:47 +00:00
room = self . current_room ( )
if len ( args ) != 1 :
2010-09-14 02:11:07 +00:00
self . add_message_to_text_buffer ( room , _ ( ' whois command takes exactly one argument ' ) )
2010-08-07 00:28:47 +00:00
return
# check if current room is a MUC
if room . jid or room . name == ' Info ' :
return
nickname = args [ 0 ]
self . muc . request_vcard ( room . name , nickname )
2010-08-20 20:55:42 +00:00
def command_theme ( self , arg ) :
"""
"""
theme . reload_theme ( )
2010-08-22 14:57:47 +00:00
self . resize_window ( )
2010-08-20 20:55:42 +00:00
2010-08-02 19:22:41 +00:00
def command_win ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ win < number >
"""
2010-08-02 19:22:41 +00:00
args = arg . split ( )
2010-02-12 02:50:07 +00:00
if len ( args ) != 1 :
2010-08-11 19:14:36 +00:00
self . command_help ( ' win ' )
2010-02-12 02:50:07 +00:00
return
try :
nb = int ( args [ 0 ] )
except ValueError :
2010-08-11 19:14:36 +00:00
self . command_help ( ' win ' )
2010-02-12 02:50:07 +00:00
return
2010-09-14 02:11:07 +00:00
if self . current_tab ( ) . nb == nb :
2010-02-12 02:50:07 +00:00
return
2010-10-05 00:36:43 +00:00
self . previous_tab_nb = self . current_tab ( ) . nb
2010-09-14 02:11:07 +00:00
self . current_tab ( ) . on_lose_focus ( )
start = self . current_tab ( )
self . tabs . append ( self . tabs . pop ( 0 ) )
while self . current_tab ( ) . nb != nb :
self . tabs . append ( self . tabs . pop ( 0 ) )
if self . current_tab ( ) == start :
self . current_tab ( ) . set_color_state ( theme . COLOR_TAB_CURRENT )
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-02-12 02:50:07 +00:00
return
2010-09-14 02:11:07 +00:00
self . current_tab ( ) . on_gain_focus ( )
2010-08-04 00:17:53 +00:00
self . refresh_window ( )
2010-02-12 02:50:07 +00:00
2010-08-02 19:22:41 +00:00
def command_join ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ join [ room ] [ / nick ] [ password ]
"""
2010-08-02 19:22:41 +00:00
args = arg . split ( )
2010-03-24 18:01:22 +00:00
password = None
2010-01-30 04:02:23 +00:00
if len ( args ) == 0 :
2010-09-14 02:11:07 +00:00
t = self . current_tab ( )
if not isinstance ( t , MucTab ) and not isinstance ( t , PrivateTab ) :
2010-01-30 04:02:23 +00:00
return
2010-09-14 02:11:07 +00:00
room = t . get_name ( )
nick = t . get_room ( ) . own_nick
2010-01-30 02:50:24 +00:00
else :
2010-01-30 04:02:23 +00:00
info = args [ 0 ] . split ( ' / ' )
if len ( info ) == 1 :
2010-06-30 09:56:03 +00:00
default = os . environ . get ( ' USER ' ) if os . environ . get ( ' USER ' ) else ' poezio '
nick = config . get ( ' default_nick ' , ' ' )
if nick == ' ' :
nick = default
2010-01-30 04:02:23 +00:00
else :
nick = info [ 1 ]
2010-01-31 03:07:30 +00:00
if info [ 0 ] == ' ' : # happens with /join /nickname, which is OK
2010-09-14 02:11:07 +00:00
t = self . current_tab ( )
if not isinstance ( t , MucTab ) :
2010-01-30 04:02:23 +00:00
return
2010-09-14 02:11:07 +00:00
room = t . get_name ( )
2010-03-24 18:01:22 +00:00
if nick == ' ' :
2010-09-14 02:11:07 +00:00
nick = t . get_room ( ) . own_nick
2010-01-30 04:02:23 +00:00
else :
room = info [ 0 ]
2010-07-01 21:32:44 +00:00
if not is_jid ( room ) : # no server is provided, like "/join hello"
# use the server of the current room if available
# check if the current room's name has a server
2010-09-14 02:11:07 +00:00
if isinstance ( self . current_tab ( ) , MucTab ) and \
is_jid ( self . current_tab ( ) . get_name ( ) ) :
room + = ' @ %s ' % jid_get_domain ( self . current_tab ( ) . get_name ( ) )
2010-07-01 21:32:44 +00:00
else : # no server could be found, print a message and return
2010-09-27 22:53:25 +00:00
self . information ( _ ( " You didn ' t specify a server for the room you want to join " ) , ' Error ' )
2010-07-01 21:32:44 +00:00
return
2010-09-14 02:11:07 +00:00
r = self . get_room_by_name ( room )
2010-03-24 18:01:22 +00:00
if len ( args ) == 2 : # a password is provided
password = args [ 1 ]
2010-09-27 01:40:34 +00:00
if r and r . joined : # if we are already in the room
self . focus_tab_named ( r . name )
return
2010-08-31 23:11:02 +00:00
room = room . lower ( )
2010-09-14 02:11:07 +00:00
if r and not r . joined :
2010-10-18 22:57:54 +00:00
muc . join_groupchat ( self . xmpp , room , nick , password )
2010-03-24 16:44:55 +00:00
if not r : # if the room window exists, we don't recreate it.
2010-08-31 23:11:02 +00:00
self . open_new_room ( room , nick )
2010-10-18 22:57:54 +00:00
muc . join_groupchat ( self . xmpp , room , nick , password )
2010-03-24 16:44:55 +00:00
else :
2010-07-08 20:05:06 +00:00
r . own_nick = nick
2010-03-24 16:44:55 +00:00
r . users = [ ]
2010-01-13 01:04:30 +00:00
2010-08-02 19:22:41 +00:00
def command_bookmark ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ bookmark [ room ] [ / nick ]
"""
2010-08-02 19:22:41 +00:00
args = arg . split ( )
2010-01-31 03:42:25 +00:00
nick = None
2010-09-14 02:11:07 +00:00
if not isinstance ( self . current_tab ( ) , MucTab ) :
return
2010-01-31 03:42:25 +00:00
if len ( args ) == 0 :
2010-09-14 02:11:07 +00:00
room = self . current_tab ( ) . get_room ( )
roomname = self . current_tab ( ) . get_name ( )
2010-01-31 03:42:25 +00:00
if room . joined :
nick = room . own_nick
else :
info = args [ 0 ] . split ( ' / ' )
if len ( info ) == 2 :
nick = info [ 1 ]
roomname = info [ 0 ]
2010-02-14 04:10:13 +00:00
if roomname == ' ' :
2010-09-14 02:11:07 +00:00
roomname = self . current_tab ( ) . get_name ( )
2010-01-31 03:42:25 +00:00
if nick :
res = roomname + ' / ' + nick
else :
res = roomname
2010-02-14 04:10:13 +00:00
bookmarked = config . get ( ' rooms ' , ' ' )
# check if the room is already bookmarked.
# if yes, replace it (i.e., update the associated nick)
bookmarked = bookmarked . split ( ' : ' )
for room in bookmarked :
if room . split ( ' / ' ) [ 0 ] == roomname :
bookmarked . remove ( room )
break
bookmarked = ' : ' . join ( bookmarked )
2010-10-05 21:28:11 +00:00
if bookmarked :
bookmarks = bookmarked + ' : ' + res
else :
bookmarks = res
2010-07-14 17:04:50 +00:00
config . set_and_save ( ' rooms ' , bookmarks )
2010-09-14 02:11:07 +00:00
self . information ( _ ( ' Your bookmarks are now: %s ' ) % bookmarks )
2010-01-31 03:42:25 +00:00
2010-08-02 19:22:41 +00:00
def command_set ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ set < option > [ value ]
"""
2010-08-02 19:22:41 +00:00
args = arg . split ( )
2010-02-12 19:35:18 +00:00
if len ( args ) != 2 and len ( args ) != 1 :
2010-08-11 19:14:36 +00:00
self . command_help ( ' set ' )
2010-02-02 23:35:01 +00:00
return
option = args [ 0 ]
2010-02-12 19:35:18 +00:00
if len ( args ) == 2 :
value = args [ 1 ]
else :
value = ' '
2010-02-10 16:47:43 +00:00
config . set_and_save ( option , value )
2010-02-02 23:35:01 +00:00
msg = " %s = %s " % ( option , value )
2010-09-14 02:11:07 +00:00
self . information ( msg )
2010-02-02 23:35:01 +00:00
2010-08-02 19:22:41 +00:00
def command_show ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ show < status > [ msg ]
"""
2010-08-02 19:22:41 +00:00
args = arg . split ( )
2010-07-22 17:47:34 +00:00
possible_show = { ' avail ' : None ,
' available ' : None ,
' ok ' : None ,
' here ' : None ,
2010-01-31 03:07:30 +00:00
' chat ' : ' chat ' ,
' away ' : ' away ' ,
' afk ' : ' away ' ,
' dnd ' : ' dnd ' ,
' busy ' : ' dnd ' ,
' xa ' : ' xa '
}
if len ( args ) < 1 :
return
2010-09-03 18:01:43 +00:00
if not args [ 0 ] in list ( possible_show . keys ( ) ) :
2010-08-11 19:14:36 +00:00
self . command_help ( ' show ' )
2010-01-31 03:07:30 +00:00
return
show = possible_show [ args [ 0 ] ]
if len ( args ) > 1 :
msg = ' ' . join ( args [ 1 : ] )
else :
msg = None
2010-09-14 02:11:07 +00:00
for tab in self . tabs :
if isinstance ( tab , MucTab ) and tab . get_room ( ) . joined :
muc . change_show ( self . xmpp , tab . get_room ( ) . name , tab . get_room ( ) . own_nick , show , msg )
2010-01-31 03:07:30 +00:00
2010-08-02 19:22:41 +00:00
def command_away ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ away [ msg ]
"""
2010-08-02 19:22:41 +00:00
self . command_show ( " away " + arg )
2010-01-31 03:07:30 +00:00
2010-08-02 19:22:41 +00:00
def command_busy ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ busy [ msg ]
"""
2010-08-05 00:11:11 +00:00
self . command_show ( " busy " + arg )
2010-01-31 03:07:30 +00:00
2010-08-02 19:22:41 +00:00
def command_avail ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ avail [ msg ]
"""
2010-08-05 00:11:11 +00:00
self . command_show ( " available " + arg )
2010-01-31 00:25:17 +00:00
2010-11-11 04:22:37 +00:00
def close_tab ( self , tab = None ) :
2010-06-21 08:46:06 +00:00
"""
2010-11-11 04:22:37 +00:00
Close the given tab . If None , close the current one
2010-06-21 08:46:06 +00:00
"""
2010-11-11 04:22:37 +00:00
tab = tab or self . current_tab ( )
if isinstance ( tab , RosterInfoTab ) or \
isinstance ( tab , InfoTab ) :
return # The tab 0 should NEVER be closed
tab . on_close ( )
self . tabs . remove ( tab )
self . rotate_rooms_left ( )
del tab
2010-06-21 08:46:06 +00:00
2010-08-02 19:22:41 +00:00
def command_link ( self , arg ) :
"""
/ link < option > < nb >
Opens the link in a browser , or join the room , or add the JID , or
copy it in the clipboard
"""
2010-09-14 02:11:07 +00:00
if not isinstance ( self . current_tab ( ) , MucTab ) and \
not isinstance ( self . current_tab ( ) , PrivateTab ) :
return
2010-08-02 19:22:41 +00:00
args = arg . split ( )
if len ( args ) > 2 :
2010-09-14 02:11:07 +00:00
# INFO
# self.add_message_to_text_buffer(self.current_room(),
# _("Link: This command takes at most 2 arguments"))
2010-08-02 19:22:41 +00:00
return
# set the default parameters
option = " open "
nb = 0
# check the provided parameters
if len ( args ) > = 1 :
try : # check if the argument is the number
nb = int ( args [ 0 ] )
except ValueError : # nope, it's the option
option = args [ 0 ]
if len ( args ) == 2 :
try :
nb = int ( args [ 0 ] )
except ValueError :
2010-09-14 02:11:07 +00:00
# INFO
# self.add_message_to_text_buffer(self.current_room(),
# _("Link: 2nd parameter should be a number"))
2010-08-02 19:22:41 +00:00
return
# find the nb-th link in the current buffer
i = 0
link = None
2010-09-14 02:11:07 +00:00
for msg in self . current_tab ( ) . get_room ( ) . messages [ : - 200 : - 1 ] :
2010-08-14 08:58:31 +00:00
if not msg :
continue
2010-08-02 19:22:41 +00:00
matches = re . findall ( ' " ((ftp|http|https|gopher|mailto|news|nntp|telnet|wais|file|prospero|aim|webcal):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])| % [A-Fa-f0-9] {2} ) { 2,}(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*(),;/?:@&~= % -]*))?([A-Za-z0-9$_+!*();/?:~-])) " ' , msg . txt )
for m in matches :
if i == nb :
url = m [ 0 ]
self . link_open ( url )
return
def url_open ( self , url ) :
"""
Use webbrowser to open the provided link
"""
webbrowser . open ( url )
2010-09-11 20:07:50 +00:00
def move_separator ( self ) :
"""
Move the new - messages separator at the bottom on the current
text .
"""
2010-09-14 02:11:07 +00:00
try :
room = self . current_tab ( ) . get_room ( )
except :
return
room . remove_line_separator ( )
room . add_line_separator ( )
self . refresh_window ( )
2010-09-11 20:07:50 +00:00
2010-09-14 02:11:07 +00:00
def information ( self , msg , typ = ' ' ) :
2010-05-18 13:29:02 +00:00
"""
Displays an informational message in the " Info " room window
"""
2010-09-14 02:11:07 +00:00
self . information_buffer . add_message ( msg , nickname = typ )
self . refresh_window ( )
2010-01-28 00:55:02 +00:00
2010-08-02 19:22:41 +00:00
def command_quit ( self , arg ) :
2010-05-18 13:29:02 +00:00
"""
/ quit
"""
2010-08-02 19:22:41 +00:00
if len ( arg . strip ( ) ) != 0 :
msg = arg
2010-06-13 15:40:03 +00:00
else :
msg = None
2010-09-14 02:11:07 +00:00
for tab in self . tabs :
if isinstance ( tab , MucTab ) :
muc . leave_groupchat ( self . xmpp , tab . get_room ( ) . name , tab . get_room ( ) . own_nick , msg )
2010-08-31 23:11:02 +00:00
self . xmpp . disconnect ( )
2010-10-20 23:51:34 +00:00
self . running = False
2010-08-31 23:11:02 +00:00
self . reset_curses ( )
2010-09-14 02:11:07 +00:00
def do_command ( self , key ) :
2010-09-14 22:59:40 +00:00
if not key :
return
2010-09-14 02:11:07 +00:00
res = self . current_tab ( ) . on_input ( key )
2010-11-15 13:16:30 +00:00
if res :
self . refresh_window ( )
2010-09-26 18:01:38 +00:00
def on_roster_enter_key ( self , roster_row ) :
"""
when enter is pressed on the roster window
"""
if isinstance ( roster_row , Contact ) :
2010-10-17 17:27:07 +00:00
if not self . get_conversation_by_jid ( roster_row . get_bare_jid ( ) ) :
self . open_conversation_window ( roster_row . get_bare_jid ( ) )
else :
self . focus_tab_named ( roster_row . get_bare_jid ( ) )
if isinstance ( roster_row , Resource ) :
if not self . get_conversation_by_jid ( roster_row . get_jid ( ) . full ) :
self . open_conversation_window ( roster_row . get_jid ( ) . full )
2010-09-26 18:01:38 +00:00
else :
2010-10-17 17:27:07 +00:00
self . focus_tab_named ( roster_row . get_jid ( ) . full )
2010-09-27 01:40:34 +00:00
self . refresh_window ( )
2010-09-14 02:11:07 +00:00
def execute ( self , line ) :
"""
Execute the / command or just send the line on the current room
"""
if line == " " :
return
2010-10-31 19:11:51 +00:00
if line . startswith ( ' // ' ) :
self . command_say ( line [ 1 : ] )
elif line . startswith ( ' / ' ) and not line . startswith ( ' /me ' ) :
2010-09-14 02:11:07 +00:00
command = line . strip ( ) [ : ] . split ( ) [ 0 ] [ 1 : ]
arg = line [ 2 + len ( command ) : ] # jump the '/' and the ' '
# example. on "/link 0 open", command = "link" and arg = "0 open"
if command in list ( self . commands . keys ( ) ) :
func = self . commands [ command ] [ 0 ]
func ( arg )
return
else :
self . information ( _ ( " unknown command ( %s ) " ) % ( command ) , _ ( ' Error ' ) )
else :
self . command_say ( line )
2010-11-24 05:41:06 +00:00
# def command_say(self, line):
# if isinstance(self.current_tab(), PrivateTab):
# muc.send_private_message(self.xmpp, self.current_tab().get_name(), line)
# elif isinstance(self.current_tab(), ConversationTab): # todo, special case # hu, I can't remember what special case was needed when I wrote that…
# if isinstance(self.current_tab(), PrivateTab) or\
# isinstance(self.current_tab(), ConversationTab):
# self.add_message_to_text_buffer(self.current_tab().get_room(), line, None, self.own_nick)
# elif isinstance(self.current_tab(), MucTab):
# muc.send_groupchat_message(self.xmpp, self.current_tab().get_name(), line)
# self.doupdate()
2010-10-17 05:14:22 +00:00
def doupdate ( self ) :
2010-10-17 12:43:56 +00:00
self . current_tab ( ) . just_before_refresh ( )
2010-10-17 05:14:22 +00:00
curses . doupdate ( )
2010-11-09 20:37:39 +00:00
# # global core object
core = Core ( connection )