- plugin xep-0045: added roomNewSubject signal - wix: added subject bar for group windows
154 lines
6.6 KiB
Python
Executable file
154 lines
6.6 KiB
Python
Executable file
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
SAT plugin for managing xep-0045
|
|
Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
|
|
|
|
This program 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, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
from logging import debug, info, warning, error
|
|
from twisted.words.xish import domish
|
|
from twisted.internet import protocol, defer, threads, reactor
|
|
from twisted.words.protocols.jabber import client, jid, xmlstream
|
|
from twisted.words.protocols.jabber import error as jab_error
|
|
from twisted.words.protocols.jabber.xmlstream import IQ
|
|
import os.path
|
|
import pdb
|
|
|
|
from zope.interface import implements
|
|
|
|
from wokkel import disco, iwokkel, muc
|
|
|
|
from base64 import b64decode
|
|
from hashlib import sha1
|
|
from time import sleep
|
|
|
|
try:
|
|
from twisted.words.protocols.xmlstream import XMPPHandler
|
|
except ImportError:
|
|
from wokkel.subprotocols import XMPPHandler
|
|
|
|
AVATAR_PATH = "/avatars"
|
|
|
|
IQ_GET = '/iq[@type="get"]'
|
|
NS_VCARD = 'vcard-temp'
|
|
VCARD_REQUEST = IQ_GET + '/vCard[@xmlns="' + NS_VCARD + '"]' #TODO: manage requests
|
|
|
|
PRESENCE = '/presence'
|
|
NS_VCARD_UPDATE = 'vcard-temp:x:update'
|
|
VCARD_UPDATE = PRESENCE + '/x[@xmlns="' + NS_VCARD_UPDATE + '"]'
|
|
|
|
PLUGIN_INFO = {
|
|
"name": "XEP 0045 Plugin",
|
|
"import_name": "XEP_0045",
|
|
"type": "XEP",
|
|
"protocols": ["XEP-0045"],
|
|
"dependencies": [],
|
|
"main": "XEP_0045",
|
|
"handler": "yes",
|
|
"description": _("""Implementation of Multi-User Chat""")
|
|
}
|
|
|
|
class XEP_0045():
|
|
|
|
def __init__(self, host):
|
|
info(_("Plugin XEP_0045 initialization"))
|
|
self.host = host
|
|
self.clients={}
|
|
host.bridge.addMethod("joinMUC", ".communication", in_sign='ssss', out_sign='', method=self.join)
|
|
host.bridge.addSignal("roomJoined", ".communication", signature='ssasss') #args: room_id, room_service, room_nicks, user_nick, profile
|
|
host.bridge.addSignal("roomUserJoined", ".communication", signature='sssa{ss}s') #args: room_id, room_service, user_nick, user_data, profile
|
|
host.bridge.addSignal("roomUserLeft", ".communication", signature='sssa{ss}s') #args: room_id, room_service, user_nick, user_data, profile
|
|
host.bridge.addSignal("roomNewSubject", ".communication", signature='ssss') #args: room_id, room_service, subject, profile
|
|
|
|
def __check_profile(self, profile):
|
|
"""check if profile is used and connected
|
|
if profile known but disconnected, remove it from known profiles
|
|
@param profile: profile to check
|
|
@return: True if the profile is known and connected, else False"""
|
|
if not profile or not self.clients.has_key(profile) or not self.host.isConnected(profile):
|
|
error (_('Unknown or disconnected profile'))
|
|
if self.clients.has_key(profile):
|
|
del self.clients[profile]
|
|
return False
|
|
return True
|
|
|
|
def __room_joined(self, room, profile):
|
|
"""Called when the user is in the requested room"""
|
|
print "room joined (profile = %s)" % profile
|
|
room_jid = room.roomIdentifier+'@'+room.service
|
|
self.clients[profile].joined_rooms[room_jid] = room
|
|
self.host.bridge.roomJoined(room.roomIdentifier, room.service, [user.nick for user in room.roster.values()], room.nick, profile)
|
|
|
|
def __err_joining_room(self, failure, profile):
|
|
"""Called when something is going wrong when joining the room"""
|
|
error ("Error when joining the room")
|
|
#TODO: gof: send an error message throught the bridge
|
|
|
|
def join(self, service, roomId, nick, profile_key='@DEFAULT@'):
|
|
profile = self.host.memory.getProfileName(profile_key)
|
|
if not self.__check_profile(profile):
|
|
return
|
|
room_jid = roomId+'@'+service
|
|
if self.clients[profile].joined_rooms.has_key(room_jid):
|
|
warning(_('%(profile)s is already in room %(room_jid)s') % {'profile':profile, 'room_jid':room_jid})
|
|
return
|
|
info (_("[%(profile)s] is joining room %(room)s with nick %(nick)s") % {'profile':profile,'room':roomId+'@'+service, 'nick':nick})
|
|
self.clients[profile].join(service, roomId, nick).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile':profile}, errbackKeywords={'profile':profile})
|
|
|
|
def getHandler(self, profile):
|
|
#reactor.callLater(15,self.join,"conference.necton2.int", "test", "Goffi \o/", profile)
|
|
self.clients[profile] = SatMUCClient(self)
|
|
return self.clients[profile]
|
|
|
|
|
|
|
|
class SatMUCClient (muc.MUCClient):
|
|
#implements(iwokkel.IDisco)
|
|
|
|
def __init__(self, plugin_parent):
|
|
self.plugin_parent = plugin_parent
|
|
self.host = plugin_parent.host
|
|
muc.MUCClient.__init__(self)
|
|
self.joined_rooms = {} #FIXME gof: check if necessary
|
|
print "init SatMUCClient OK"
|
|
|
|
def receivedGroupChat(self, room, user, body):
|
|
debug('receivedGroupChat: room=%s user=%s body=%s', room, user, body)
|
|
|
|
def userJoinedRoom(self, room, user):
|
|
debug (_("user %(nick)s has joined room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
|
|
user_data={'entity':user.entity or '', 'affiliation':user.affiliation, 'role':user.role}
|
|
self.host.bridge.roomUserJoined(room.roomIdentifier, room.service, user.nick, user_data, self.parent.profile)
|
|
|
|
def userLeftRoom(self, room, user):
|
|
debug (_("user %(nick)s left room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
|
|
user_data={'entity':user.entity or '', 'affiliation':user.affiliation, 'role':user.role}
|
|
self.host.bridge.roomUserLeft(room.roomIdentifier, room.service, user.nick, user_data, self.parent.profile)
|
|
|
|
def receivedSubject(self, room, subject):
|
|
debug (_("New subject for room (%(room_id)s): %(subject)s") % {'room_id':room.occupantJID.userhost(),'subject':subject})
|
|
self.host.bridge.roomNewSubject(room.roomIdentifier, room.service, subject, self.parent.profile)
|
|
|
|
#def connectionInitialized(self):
|
|
#pass
|
|
|
|
#def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
|
#return [disco.DiscoFeature(NS_VCARD)]
|
|
|
|
#def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
|
#return []
|
|
|