view plugins/ @ 198:3d7a06fe3997

plugin XEP-0045: fixed receivedSubject
author Goffi <>
date Fri, 20 Aug 2010 01:11:51 +0800
parents 9ee4a1d0d7fb
children c5274bf5e18b
line wrap: on
line source

# -*- coding: utf-8 -*-

SAT plugin for managing xep-0045
Copyright (C) 2009, 2010  Jérôme Poisson (

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
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 <>.

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

    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 + '"]'

"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")) = host
        host.bridge.addMethod("joinMUC", ".communication", in_sign='ssss', out_sign='', method=self.join)
        host.bridge.addMethod("getRoomJoined", ".communication", in_sign='s', out_sign='a(ssass)', method=self.getRoomJoined)
        host.bridge.addMethod("getRoomSubjects", ".communication", in_sign='s', out_sign='a(sss)', method=self.getRoomSubjects)
        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
            error (_('Unknown or disconnected profile (%s)') % 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"""
        room_jid = room.roomIdentifier+'@'+room.service
        self.clients[profile].joined_rooms[room_jid] = room, 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"""
        mess = _("Error when joining the room")
        error (mess), _("Group chat error"), "ERROR", profile)

    def getRoomJoined(self, profile_key='@DEFAULT@'):
        """Return room where user is"""
        profile =
        result = []
        if not self.__check_profile(profile):
            return result
        for room in self.clients[profile].joined_rooms.values():
            result.append((room.roomIdentifier, room.service, [user.nick for user in room.roster.values()], room.nick))
        return result

    def getRoomNick(self, room_jid, profile_key='@DEFAULT@'):
        """return nick used in room by user
        @param room_jid: unicode room id
        @profile_key: profile
        @return: nick or empty string in case of error"""
        profile =
        if not self.__check_profile(profile) or not self.clients[profile].joined_rooms.has_key(room_jid):
            return ''
        return self.clients[profile].joined_rooms[room_jid].nick

    def getRoomSubjects(self, profile_key='@DEFAULT@'):
        """Return received subjects of rooms"""
        profile =
        if not self.__check_profile(profile):
            return []
        return self.clients[profile].rec_subjects.values()

    def join(self, service, roomId, nick, profile_key='@DEFAULT@'):
        profile =
        if not self.__check_profile(profile):
        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})
        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,"", "test", "Goffi \o/", profile) 
        self.clients[profile] = SatMUCClient(self)
        return self.clients[profile]

class SatMUCClient (muc.MUCClient):
    def __init__(self, plugin_parent):
        self.plugin_parent = plugin_parent =
        self.joined_rooms = {}
        self.rec_subjects = {}
        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}, 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}, room.service, user.nick, user_data, self.parent.profile)

    def userUpdatedStatus(self, room, user, show, status):
        print("FIXME: MUC status not managed yet")
        #FIXME: gof

    def receivedSubject(self, occupantJID, subject):
        room = self._getRoom(occupantJID)
        debug (_("New subject for room (%(room_id)s): %(subject)s") % {'room_id':room.occupantJID.userhost(),'subject':subject})
        room_jid = room.roomIdentifier+'@'+room.service
        self.rec_subjects[room_jid] = (room.roomIdentifier, room.service, subject), room.service, subject, self.parent.profile)

    #def connectionInitialized(self):
    #def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
        #return [disco.DiscoFeature(NS_VCARD)]

    #def getDiscoItems(self, requestor, target, nodeIdentifier=''):
        #return []