Private messages! fixed #1077
This commit is contained in:
parent
ff33a84de7
commit
9a1743f695
7 changed files with 167 additions and 26 deletions
|
@ -3,6 +3,7 @@ For more detailed changelog, see the roadmap:
|
||||||
http://codingteam.net/project/poezio/roadmap
|
http://codingteam.net/project/poezio/roadmap
|
||||||
|
|
||||||
* Poezio 0.6 - dev
|
* Poezio 0.6 - dev
|
||||||
|
- Private messages are handled
|
||||||
- Muc error messages are displayed
|
- Muc error messages are displayed
|
||||||
- Nickname auto-completion
|
- Nickname auto-completion
|
||||||
- Users status are displayed in the MUC user-list
|
- Users status are displayed in the MUC user-list
|
||||||
|
@ -18,6 +19,10 @@ http://codingteam.net/project/poezio/roadmap
|
||||||
- The dates of room history are handled
|
- The dates of room history are handled
|
||||||
- The way the text is displayed on the screen has been rewritten, this fixes
|
- The way the text is displayed on the screen has been rewritten, this fixes
|
||||||
the blink and the slowness-over-ssh problems.
|
the blink and the slowness-over-ssh problems.
|
||||||
|
- User lists are ordered
|
||||||
|
- Messages can be logged in files
|
||||||
|
- Status changes displays only what has really changed
|
||||||
|
- Users can be ignored (/ignore, /unignore)
|
||||||
- Various Bugfixes
|
- Various Bugfixes
|
||||||
|
|
||||||
* Poezio 0.5.1 - 2 Feb 2010
|
* Poezio 0.5.1 - 2 Feb 2010
|
||||||
|
|
|
@ -173,7 +173,10 @@ class Connection(threading.Thread):
|
||||||
if message.getType() == 'error':
|
if message.getType() == 'error':
|
||||||
self.error_message(message)
|
self.error_message(message)
|
||||||
return
|
return
|
||||||
self.handler.emit('room-message', stanza=message)
|
if message.getType() == 'groupchat':
|
||||||
|
self.handler.emit('room-message', stanza=message)
|
||||||
|
else:
|
||||||
|
self.handler.emit('private-message', stanza=message)
|
||||||
raise xmpp.protocol.NodeProcessed
|
raise xmpp.protocol.NodeProcessed
|
||||||
|
|
||||||
def process(self, timeout=10):
|
def process(self, timeout=10):
|
||||||
|
@ -213,3 +216,10 @@ def jid_get_domain(jid):
|
||||||
if isinstance(jid, basestring):
|
if isinstance(jid, basestring):
|
||||||
jid = xmpp.JID(jid)
|
jid = xmpp.JID(jid)
|
||||||
return jid.getDomain()
|
return jid.getDomain()
|
||||||
|
|
||||||
|
def is_jid_the_same(a, b):
|
||||||
|
if isinstance(a, basestring):
|
||||||
|
a = xmpp.JID(a)
|
||||||
|
if isinstance(b, basestring):
|
||||||
|
b = xmpp.JID(b)
|
||||||
|
return a.bareMatch(b)
|
||||||
|
|
118
src/gui.py
118
src/gui.py
|
@ -40,6 +40,8 @@ from user import User
|
||||||
from room import Room
|
from room import Room
|
||||||
from message import Message
|
from message import Message
|
||||||
|
|
||||||
|
from connection import is_jid_the_same
|
||||||
|
|
||||||
from common import debug
|
from common import debug
|
||||||
def doupdate():
|
def doupdate():
|
||||||
debug("doupdate")
|
debug("doupdate")
|
||||||
|
@ -83,6 +85,8 @@ class Gui(object):
|
||||||
'set': (self.command_set, _("Usage: /set <option> [value]\nSet: 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>.")),
|
'set': (self.command_set, _("Usage: /set <option> [value]\nSet: 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>.")),
|
||||||
'kick': (self.command_kick, _("Usage: /kick <nick> [reason]\nKick: Kick the user with the specified nickname. You also can give an optional reason.")),
|
'kick': (self.command_kick, _("Usage: /kick <nick> [reason]\nKick: Kick the user with the specified nickname. You also can give an optional reason.")),
|
||||||
'topic': (self.command_topic, _("Usage: /topic <subject> \nTopic: Change the subject of the room")),
|
'topic': (self.command_topic, _("Usage: /topic <subject> \nTopic: Change the subject of the room")),
|
||||||
|
'query': (self.command_query, _('Usage: /query <nick>\nQuery: Open a private conversation with <nick>. This nick has to be present in the room you\'re currently in.')),
|
||||||
|
|
||||||
'nick': (self.command_nick, _("Usage: /nick <nickname> \nNick: Change your nickname in the current room"))
|
'nick': (self.command_nick, _("Usage: /nick <nickname> \nNick: Change your nickname in the current room"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +113,7 @@ class Gui(object):
|
||||||
self.handler.connect('join-room', self.join_room)
|
self.handler.connect('join-room', self.join_room)
|
||||||
self.handler.connect('room-presence', self.room_presence)
|
self.handler.connect('room-presence', self.room_presence)
|
||||||
self.handler.connect('room-message', self.room_message)
|
self.handler.connect('room-message', self.room_message)
|
||||||
|
self.handler.connect('private-message', self.private_message)
|
||||||
self.handler.connect('error-message', self.room_error)
|
self.handler.connect('error-message', self.room_error)
|
||||||
self.handler.connect('error', self.information)
|
self.handler.connect('error', self.information)
|
||||||
|
|
||||||
|
@ -195,7 +200,7 @@ class Gui(object):
|
||||||
curses.COLOR_RED) # highlight room
|
curses.COLOR_RED) # highlight room
|
||||||
curses.init_pair(14, curses.COLOR_WHITE,
|
curses.init_pair(14, curses.COLOR_WHITE,
|
||||||
curses.COLOR_YELLOW)
|
curses.COLOR_YELLOW)
|
||||||
curses.init_pair(15, curses.COLOR_WHITE,
|
curses.init_pair(15, curses.COLOR_WHITE, # new message in private room
|
||||||
curses.COLOR_GREEN)
|
curses.COLOR_GREEN)
|
||||||
|
|
||||||
def reset_curses(self):
|
def reset_curses(self):
|
||||||
|
@ -270,12 +275,55 @@ class Gui(object):
|
||||||
room = self.get_room_by_name(room)
|
room = self.get_room_by_name(room)
|
||||||
code = error.getAttr('code')
|
code = error.getAttr('code')
|
||||||
typ = error.getAttr('type')
|
typ = error.getAttr('type')
|
||||||
body = error.getTag('text').getData()
|
if error.getTag('text'):
|
||||||
|
body = error.getTag('text').getData()
|
||||||
|
else:
|
||||||
|
body = _('Unknown error')
|
||||||
self.add_message_to_room(room, _('Error: %(code)s-%(msg)s: %(body)s' %
|
self.add_message_to_room(room, _('Error: %(code)s-%(msg)s: %(body)s' %
|
||||||
{'msg':msg, 'code':code, 'body':body}))
|
{'msg':msg, 'code':code, 'body':body}))
|
||||||
if code == '401':
|
if code == '401':
|
||||||
room.add(_('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)'))
|
room.add(_('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)'))
|
||||||
|
|
||||||
|
def private_message(self, stanza):
|
||||||
|
"""
|
||||||
|
When a private message is received
|
||||||
|
"""
|
||||||
|
jid = stanza.getFrom()
|
||||||
|
nick_from = stanza.getFrom().getResource()
|
||||||
|
room_from = stanza.getFrom().getStripped()
|
||||||
|
room = self.get_room_by_name(jid) # get the tab with the private conversation
|
||||||
|
if not room: # It's the first message we receive: create the tab
|
||||||
|
room = self.open_private_window(room_from, nick_from, False)
|
||||||
|
body = stanza.getBody()
|
||||||
|
self.add_message_to_room(room, body, None, nick_from)
|
||||||
|
self.window.input.refresh()
|
||||||
|
doupdate()
|
||||||
|
|
||||||
|
def open_private_window(self, room_name, user_nick, focus=True):
|
||||||
|
complete_jid = room_name+'/'+user_nick
|
||||||
|
for room in self.rooms: # if the room exists, focus it and return
|
||||||
|
if room.jid:
|
||||||
|
if room.jid == complete_jid:
|
||||||
|
self.command_win(str(room.nb))
|
||||||
|
return
|
||||||
|
# create the new tab
|
||||||
|
own_nick = self.get_room_by_name(room_name).own_nick
|
||||||
|
r = Room(complete_jid, own_nick, self.window, complete_jid)
|
||||||
|
# insert it in the rooms
|
||||||
|
if self.current_room().nb == 0:
|
||||||
|
self.rooms.append(r)
|
||||||
|
else:
|
||||||
|
for ro in self.rooms:
|
||||||
|
if ro.nb == 0:
|
||||||
|
self.rooms.insert(self.rooms.index(ro), r)
|
||||||
|
break
|
||||||
|
if focus: # focus the room if needed
|
||||||
|
while self.current_room().nb != r.nb:
|
||||||
|
self.rooms.insert(0, self.rooms.pop())
|
||||||
|
# self.window.new_room(r)
|
||||||
|
self.window.refresh(self.rooms)
|
||||||
|
return r
|
||||||
|
|
||||||
def room_message(self, stanza, date=None):
|
def room_message(self, stanza, date=None):
|
||||||
"""
|
"""
|
||||||
Display the message on the room window
|
Display the message on the room window
|
||||||
|
@ -356,8 +404,22 @@ class Gui(object):
|
||||||
elif change_nick:
|
elif change_nick:
|
||||||
if user.nick == room.own_nick:
|
if user.nick == room.own_nick:
|
||||||
room.own_nick = stanza.getNick().encode('utf-8')
|
room.own_nick = stanza.getNick().encode('utf-8')
|
||||||
|
# also change our nick in all private discussion of this room
|
||||||
|
for _room in self.rooms:
|
||||||
|
if _room.jid is not None and is_jid_the_same(_room.jid, room.name):
|
||||||
|
debug(_room.jid)
|
||||||
|
debug(room.name)
|
||||||
|
_room.own_nick = stanza.getNick()
|
||||||
user.change_nick(stanza.getNick())
|
user.change_nick(stanza.getNick())
|
||||||
self.add_message_to_room(room, _('%(old)s is now known as %(new)s') % {'old':from_nick, 'new':stanza.getNick()})
|
self.add_message_to_room(room, _('%(old)s is now known as %(new)s') % {'old':from_nick, 'new':stanza.getNick()})
|
||||||
|
# rename the private tabs if needed
|
||||||
|
private_room = self.get_room_by_name(stanza.getFrom())
|
||||||
|
if private_room:
|
||||||
|
self.add_message_to_room(private_room, _('%(old_nick)s is now known as %(new_nick)s') % {'old_nick':from_nick, 'new_nick':stanza.getNick()})
|
||||||
|
new_jid = private_room.name.split('/')[0]+'/'+stanza.getNick()
|
||||||
|
private_room.jid = new_jid
|
||||||
|
private_room.name = new_jid
|
||||||
|
|
||||||
# kick
|
# kick
|
||||||
elif kick:
|
elif kick:
|
||||||
room.users.remove(user)
|
room.users.remove(user)
|
||||||
|
@ -386,8 +448,22 @@ class Gui(object):
|
||||||
hide_exit_join = config.get('hide_exit_join', -1) if config.get('hide_exit_join', -1) >= -1 else -1
|
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 hide_exit_join == -1 or user.has_talked_since(hide_exit_join):
|
||||||
self.add_message_to_room(room, _('%s has left the room') % (from_nick))
|
self.add_message_to_room(room, _('%s has left the room') % (from_nick))
|
||||||
|
private_room = self.get_room_by_name(stanza.getFrom())
|
||||||
|
if private_room:
|
||||||
|
self.add_message_to_room(private_room, _('%s has left the room') % (from_nick))
|
||||||
# status change
|
# status change
|
||||||
else:
|
else:
|
||||||
|
# build the message
|
||||||
|
msg = _('%s changed his/her status: ')% from_nick
|
||||||
|
if affiliation != user.affiliation:
|
||||||
|
msg += _('affiliation: %s,') % affiliation
|
||||||
|
if role != user.role:
|
||||||
|
msg += _('role: %s,') % role
|
||||||
|
if show != user.show:
|
||||||
|
msg += _('show: %s,') % show
|
||||||
|
if status != user.status:
|
||||||
|
msg += _('status: %s,') % status
|
||||||
|
msg = msg[:-1] # remove the last ","
|
||||||
hide_status_change = config.get('hide_status_change', -1) if config.get('hide_status_change', -1) >= -1 else -1
|
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 \
|
if (hide_status_change == -1 or \
|
||||||
user.has_talked_since(hide_status_change) or\
|
user.has_talked_since(hide_status_change) or\
|
||||||
|
@ -397,23 +473,18 @@ class Gui(object):
|
||||||
role != user.role or\
|
role != user.role or\
|
||||||
show != user.show or\
|
show != user.show or\
|
||||||
status != user.status):
|
status != user.status):
|
||||||
msg = _('%s changed his/her status: ')% from_nick
|
# display the message in the room
|
||||||
if affiliation != user.affiliation:
|
|
||||||
msg += _('affiliation: %s,') % affiliation
|
|
||||||
if role != user.role:
|
|
||||||
msg += _('role: %s,') % role
|
|
||||||
if show != user.show:
|
|
||||||
msg += _('show: %s,') % show
|
|
||||||
if status != user.status:
|
|
||||||
msg += _('status: %s,') % status
|
|
||||||
# (a)s, %(b)s, %(c)s, %(d)s') % {'nick':from_nick, 'a':affiliation, 'b':role, 'c':show, 'd':status})
|
|
||||||
user.update(affiliation, show, status, role)
|
|
||||||
msg = msg[:-1] # remove the last ","
|
|
||||||
self.add_message_to_room(room, msg)
|
self.add_message_to_room(room, msg)
|
||||||
|
private_room = self.get_room_by_name(stanza.getFrom())
|
||||||
|
# debug('status change: ' + stanza.getFrom()+'\n')
|
||||||
|
if private_room: # display the message in private
|
||||||
|
self.add_message_to_room(private_room, msg)
|
||||||
|
# finally, effectively change the user status
|
||||||
|
user.update(affiliation, show, status, role)
|
||||||
if room == self.current_room():
|
if room == self.current_room():
|
||||||
self.window.user_win.refresh(room.users)
|
self.window.user_win.refresh(room.users)
|
||||||
self.window.input.refresh()
|
self.window.input.refresh()
|
||||||
|
self.window.info_win.refresh(self.rooms, self.current_room())
|
||||||
doupdate()
|
doupdate()
|
||||||
|
|
||||||
def add_message_to_room(self, room, txt, time=None, nickname=None):
|
def add_message_to_room(self, room, txt, time=None, nickname=None):
|
||||||
|
@ -446,7 +517,11 @@ class Gui(object):
|
||||||
else:
|
else:
|
||||||
self.add_message_to_room(self.current_room(), _("Error: unknown command (%s)") % (command))
|
self.add_message_to_room(self.current_room(), _("Error: unknown command (%s)") % (command))
|
||||||
elif self.current_room().name != 'Info':
|
elif self.current_room().name != 'Info':
|
||||||
self.muc.send_message(self.current_room().name, line)
|
if self.current_room().jid is not None:
|
||||||
|
self.muc.send_private_message(self.current_room().name, line)
|
||||||
|
self.add_message_to_room(self.current_room(), line.decode('utf-8'), None, self.current_room().own_nick)
|
||||||
|
else:
|
||||||
|
self.muc.send_message(self.current_room().name, line)
|
||||||
self.window.input.refresh()
|
self.window.input.refresh()
|
||||||
doupdate()
|
doupdate()
|
||||||
|
|
||||||
|
@ -703,6 +778,17 @@ class Gui(object):
|
||||||
self.rooms.remove(self.current_room())
|
self.rooms.remove(self.current_room())
|
||||||
self.window.refresh(self.rooms)
|
self.window.refresh(self.rooms)
|
||||||
|
|
||||||
|
def command_query(self, args):
|
||||||
|
if len(args) != 1:
|
||||||
|
return
|
||||||
|
nick = args[0]
|
||||||
|
room = self.current_room()
|
||||||
|
if room.name == "Info" or room.jid is not None:
|
||||||
|
return
|
||||||
|
for user in room.users:
|
||||||
|
if user.nick == nick:
|
||||||
|
self.open_private_window(room.name, user.nick)
|
||||||
|
|
||||||
def command_topic(self, args):
|
def command_topic(self, args):
|
||||||
"""
|
"""
|
||||||
/topic [new topic]
|
/topic [new topic]
|
||||||
|
|
|
@ -46,6 +46,10 @@ class Handler(Singleton):
|
||||||
# A message is received
|
# A message is received
|
||||||
# Args: the stanza object
|
# Args: the stanza object
|
||||||
|
|
||||||
|
'private-message': list(),
|
||||||
|
# A message is received
|
||||||
|
# Args: the stanza object
|
||||||
|
|
||||||
'room-delayed-message': list(),
|
'room-delayed-message': list(),
|
||||||
# A message is received
|
# A message is received
|
||||||
# Args: the stanza object
|
# Args: the stanza object
|
||||||
|
|
|
@ -149,6 +149,13 @@ class MultiUserChat(object):
|
||||||
mes.setType('groupchat')
|
mes.setType('groupchat')
|
||||||
self.connection.send(mes)
|
self.connection.send(mes)
|
||||||
|
|
||||||
|
def send_private_message(self, user_jid, message):
|
||||||
|
from common import debug
|
||||||
|
mes = Message(to=user_jid)
|
||||||
|
mes.setBody(message)
|
||||||
|
mes.setType('chat')
|
||||||
|
self.connection.send(mes)
|
||||||
|
|
||||||
def join_room(self, room, nick, password=None):
|
def join_room(self, room, nick, password=None):
|
||||||
"""Join a new room"""
|
"""Join a new room"""
|
||||||
pres = Presence(to='%s/%s' % (room, nick))
|
pres = Presence(to='%s/%s' % (room, nick))
|
||||||
|
|
|
@ -27,7 +27,8 @@ class Room(object):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
number = 0
|
number = 0
|
||||||
def __init__(self, name, nick, window):
|
def __init__(self, name, nick, window, jid=None):
|
||||||
|
self.jid = jid # used for a private chat. None if it's a MUC
|
||||||
self.name = name
|
self.name = name
|
||||||
self.own_nick = nick
|
self.own_nick = nick
|
||||||
self.color_state = 11 # color used in RoomInfo
|
self.color_state = 11 # color used in RoomInfo
|
||||||
|
@ -72,7 +73,10 @@ class Room(object):
|
||||||
time = time if time is not None else datetime.now()
|
time = time if time is not None else datetime.now()
|
||||||
color = None
|
color = None
|
||||||
if nickname is not None:
|
if nickname is not None:
|
||||||
self.set_color_state(12)
|
if not self.jid:
|
||||||
|
self.set_color_state(12)
|
||||||
|
else:
|
||||||
|
self.set_color_state(15)
|
||||||
else:
|
else:
|
||||||
color = 8
|
color = 8
|
||||||
if nickname != self.own_nick and self.joined and nickname is not None: # do the highlight
|
if nickname != self.own_nick and self.joined and nickname is not None: # do the highlight
|
||||||
|
|
|
@ -17,6 +17,17 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Poezio. If not, see <http://www.gnu.org/licenses/>.
|
# along with Poezio. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from gettext import (bindtextdomain, textdomain, bind_textdomain_codeset,
|
||||||
|
gettext as _)
|
||||||
|
|
||||||
|
bindtextdomain('poezio')
|
||||||
|
textdomain('poezio')
|
||||||
|
bind_textdomain_codeset('poezio', 'utf-8')
|
||||||
|
|
||||||
|
import locale
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
from config import config
|
from config import config
|
||||||
|
|
||||||
|
@ -108,14 +119,23 @@ class Topic(Win):
|
||||||
def resize(self, height, width, y, x, stdscr, visible):
|
def resize(self, height, width, y, x, stdscr, visible):
|
||||||
self._resize(height, width, y, x, stdscr)
|
self._resize(height, width, y, x, stdscr)
|
||||||
|
|
||||||
def refresh(self, room_name):
|
def refresh(self, topic, jid=None):
|
||||||
if not self.visible:
|
if not self.visible:
|
||||||
return
|
return
|
||||||
self.win.erase()
|
self.win.erase()
|
||||||
try:
|
if not jid:
|
||||||
self.win.addnstr(0, 0, room_name + " "*(self.width-len(room_name)), self.width
|
try:
|
||||||
, curses.color_pair(1))
|
self.win.addnstr(0, 0, topic + " "*(self.width-len(topic)), self.width
|
||||||
except:pass
|
, curses.color_pair(1))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif jid:
|
||||||
|
room = jid.split('/')[0]
|
||||||
|
nick = '/'.join(jid.split('/')[1:])
|
||||||
|
topic = _('%(nick)s from room %(room)s' % {'nick': nick, 'room':room})
|
||||||
|
self.win.addnstr(0, 0, topic.encode('utf-8') + " "*(self.width-len(topic)), self.width-1
|
||||||
|
, curses.color_pair(15))
|
||||||
|
|
||||||
self.win.refresh()
|
self.win.refresh()
|
||||||
|
|
||||||
class RoomInfo(Win):
|
class RoomInfo(Win):
|
||||||
|
@ -147,9 +167,14 @@ class RoomInfo(Win):
|
||||||
break
|
break
|
||||||
(y, x) = self.win.getyx()
|
(y, x) = self.win.getyx()
|
||||||
try:
|
try:
|
||||||
self.win.addstr(y, x-1, '] '+ current.name+ (' '*((self.width)-x)), curses.color_pair(1))
|
self.win.addstr(y, x-1, '] '+ current.name.encode('utf-8') , curses.color_pair(1))
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self.win.addstr(' ', curses.color_pair(1))
|
||||||
|
except:
|
||||||
|
break
|
||||||
self.win.refresh()
|
self.win.refresh()
|
||||||
|
|
||||||
class TextWin(Win):
|
class TextWin(Win):
|
||||||
|
@ -577,7 +602,7 @@ class Window(object):
|
||||||
# self.text_win.redraw(room)
|
# self.text_win.redraw(room)
|
||||||
self.text_win.refresh(room)
|
self.text_win.refresh(room)
|
||||||
self.user_win.refresh(room.users)
|
self.user_win.refresh(room.users)
|
||||||
self.topic_win.refresh(room.topic)
|
self.topic_win.refresh(room.topic, room.jid)
|
||||||
self.info_win.refresh(rooms, room)
|
self.info_win.refresh(rooms, room)
|
||||||
self.input.refresh()
|
self.input.refresh()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue