root/trunk/RBJabber/RBJabber/Conference.py

Revision 400, 7.2 kB (checked in by sholloway, 6 years ago)

Integrated SubjectObserver? from old foundation into tree for meantime

Line 
1 #!/usr/bin/env python
2 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 ##~ License
4 ##~
5 ##- The RuneBlade Foundation library is intended to ease some
6 ##- aspects of writing intricate Jabber, XML, and User Interface (wxPython, etc.)
7 ##- applications, while providing the flexibility to modularly change the
8 ##- architecture. Enjoy.
9 ##~
10 ##~ Copyright (C) 2002  TechGame Networks, LLC.
11 ##~
12 ##~ This library is free software; you can redistribute it and/or
13 ##~ modify it under the terms of the BSD style License as found in the
14 ##~ LICENSE file included with this distribution.
15 ##~
16 ##~ TechGame Networks, LLC can be reached at:
17 ##~ 3578 E. Hartsel Drive #211
18 ##~ Colorado Springs, Colorado, USA, 80920
19 ##~
20 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
22 """Conference implementation for Jabber conferencing
23
24 The Jabber protocol can be found at http://jabber.org,
25 and at the time of writting, specifically at
26 http://docs.jabber.org/general/html/protocol.html
27
28 Specifically, this module relates to jabber:iq:conference.
29 """
30
31 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 #~ Imports
33 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
34
35 import weakref
36 import JID
37 import JabberObserver as JObs
38 from JabberSubject import JabberSubjectBase
39 from SubjectObserver.Observer import Observer
40 from SubjectObserver.AttributedSubject import AttributedSubject
41
42 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
43 #~ Definitions
44 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45
46 class Conference(JabberSubjectBase):
47     """Sets up a Jabber conference at a specific conference JID.
48
49     Note that conferencing is a Jabber module, and as such, needs to
50     be enabled on the Jabber server itself.
51     """
52    
53     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54     #~ Special
55     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56
57     def __init__(self, JC, ConferenceJID):
58         JabberSubjectBase.__init__(self)
59         # Save the conference JID
60         self.ConferenceJID = JID.JID(ConferenceJID).nominal()
61
62         # Save the Jabber Client
63         self.JC = weakref.ref(JC)
64
65         # Pretend like we are a Jabber.Client
66         self.stream = weakref.proxy(self)
67         # Insert our greedy little meathooks...
68         obs = Observer(self._CallConference, self._BidConference)
69         JC.stream.AddObserver('message', obs)
70         JC.stream.AddObserver('presence', obs)
71         JC.stream.AddObserver('iq', obs)
72
73         # Prepare to get the conference roster
74         self.roster = AttributedSubject()
75         obs = JObs.JabberObserver(self._OnIQBrowseSets, BidValue=0.1)
76         obs.AddRule(JObs.MatchAttributes(type='set', from_=self.ConferenceJID.nominal()))
77         obs.AddRule(JObs.MatchHasChildNamespace('jabber:iq:browse'))
78         self.stream.AddObserver('iq', obs)
79
80     def __del__(self):
81         """Disconnects from the conference if the Jabber Client still exists"""
82         if self.JC(): self.Presence(type='unavailable')
83
84     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
85     #~ Protected Methods
86     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
87
88     def _CallConference(self, subject, **UpdateDict):
89         """Dummy method to place in the stream Observer.  Should never be called."""
90         assert 0
91
92     def _BidConference(self, subject, **UpdateDict):
93         """Selects only the messages off the jabber stream that are pertinate to the conference JID"""
94         for each in UpdateDict.itervalues():
95             if each.from_ not in self.ConferenceJID:
96                 # This 'subject' is defined to be from JIDs in the conference
97                 return 0
98         return self.__super.Bid(subject, **UpdateDict)
99        
100     def _OnIQBrowseSets(self, subject, iq):
101         users = getattr(iq, 'user', [])
102         for conference in getattr(iq, 'conference', []):
103             users.extend(getattr(conference, 'user', []))
104
105         for user in users:
106             if getattr(user, 'type', '') == 'remove':
107                 del self.roster[user.jid]
108             else:
109                 self.roster[user.jid] = getattr(user, 'name', user.jid)
110
111     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112     #~ Public Methods
113     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114
115     def Disconnect(self):
116         """Disconnects from the conference by sending and unavailable presence"""
117         return self.Presence(type='unavailable')
118
119     def GetInformation(self, callback):
120         """Requests information about the conference in question. 
121        
122         Returns iqQuery object."""
123         return self.JC().Query('jabber:iq:conference', toJID=self.ConferenceJID, type='get', callback=callback)
124
125     def JoinAs(self, Nick, Secret='', Privacy=0, callback=None):
126         """Joins the conference using Nick(s), and Secret if need be. 
127        
128         Returns iqQuery object."""
129         xml = ''
130         if Nick:
131             if not isinstance(Nick, (list, tuple)): Nick = (Nick,)
132             xml = ''.join(['<nick>%s</nick>' % x for x in Nick])
133         if Secret: xml += '<secret>%s</secret>' % Secret
134         if Privacy: xml += '<privacy/>'
135
136         self.Presence()
137
138         return self.JC().Query('jabber:iq:conference', toJID=self.ConferenceJID, type='set', xml=xml, callback=callback)
139
140     def ChangeNick(self, Nick, callback=None):
141         """Changes the nickname of the conference connection"""
142         newjid = self.ConferenceJID.join(self.ConferenceJID.split(1,3) + [Nick])
143         return self.JC().Presence(newjid)
144        
145     def Invite(self, *args, **kw):
146         """Invites a JID to join the conference"""
147         kw['xml'] = kw.get('xml', '') + '<x xmlns="jabber:x:conference" jid="%s"/>' % self.ConferenceJID
148         return self.JC().Message(*args, **kw)
149
150     def Message(self, body='', subject='', type='groupchat', id=None, xml=''):
151         """Sends a message to the conference"""
152         return self.JC().Message(self.ConferenceJID, body=body, subject=subject, type=type, id=id, xml=xml)
153
154     def Presence(self, *args, **kw):
155         """Sends a presence to the conference"""
156         return self.JC().Presence(self.ConferenceJID, *args, **kw)
157
158 Conference._Conference__super = super(Conference)
159
160 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
161
162 if __name__=='__main__':
163     print "Testing..."
164     import Client
165
166     def PrintRoster(roster, **kw):
167         print kw.values(), 'is now in', roster.values()
168
169     def PrintStuff(stream, **kw):
170         for each in kw.itervalues():
171             print each._toXML()
172     jc = Client.Client('www.runeblade.com', fileIn=open('conf.in','w'), fileOut=open('conf.out','w'))
173     jc.Authenticate('shane.test1', 'testing', 'PyConferenceTest')
174     conf = Conference(jc, 'shane.conf@private.www.runeblade.com')
175     conf.roster.AddObserver(PrintRoster)
176     #conf.AddObserver('message', PrintStuff)
177     #conf.AddObserver('presence', PrintStuff)
178     #conf.AddObserver('iq', PrintStuff)
179     conf.JoinAs('RuneBlade.Development')
180     conf.Message('Hello from RB development!!!')
181     conf.ChangeNick('shane.test1')
182     conf.Message('Why, isnt this fun?')
183     #conf.Invite('shane.holloway@www.runeblade.com', subject='Please join me!', body='Please join me in shane.conf@private.www.runeblade.com')
184     jc.ProcessPending(1.0)
185     try:
186         while 1: jc.Process(1.0)
187     except KeyboardInterrupt: pass
188     print "Test complete."
189
190
Note: See TracBrowser for help on using the browser.