Mercurial > libervia-backend
comparison src/plugins/plugin_misc_room_game.py @ 718:074970227bc0
plugin tools: turn src/plugin/games.py into a plugin and move it to src/plugins/plugin_misc_room_game.py
author | souliane <souliane@mailoo.org> |
---|---|
date | Thu, 21 Nov 2013 18:23:08 +0100 |
parents | src/tools/plugins/games.py@358018c5c398 |
children | 539f278bc265 |
comparison
equal
deleted
inserted
replaced
717:358018c5c398 | 718:074970227bc0 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT: a jabber client | |
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013 Jérôme Poisson (goffi@goffi.org) | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 from logging import debug, warning, error | |
21 from twisted.words.protocols.jabber.jid import JID | |
22 from twisted.words.xish import domish | |
23 from time import time | |
24 from wokkel import disco, iwokkel | |
25 from zope.interface import implements | |
26 try: | |
27 from twisted.words.protocols.xmlstream import XMPPHandler | |
28 except ImportError: | |
29 from wokkel.subprotocols import XMPPHandler | |
30 | |
31 # Don't forget to set it to False before you commit | |
32 _DEBUG = False | |
33 _DEBUG_FILE = False | |
34 | |
35 PLUGIN_INFO = { | |
36 "name": "Room game", | |
37 "import_name": "ROOM-GAME", | |
38 "type": "MISC", | |
39 "protocols": [], | |
40 "dependencies": ["XEP-0045", "XEP-0249"], | |
41 "main": "RoomGame", | |
42 "handler": "no", # handler MUST be "no" (dynamic inheritance) | |
43 "description": _("""Base class for MUC games""") | |
44 } | |
45 | |
46 | |
47 class RoomGame(object): | |
48 """This class is used to help launching a MUC game.""" | |
49 | |
50 # Values for self.invite_mode (who can invite after the game creation) | |
51 FROM_ALL, FROM_NONE, FROM_REFEREE, FROM_PLAYERS = xrange(0, 4) | |
52 # Values for self.wait_mode (for who we should wait before creating the game) | |
53 FOR_ALL, FOR_NONE = xrange(0, 2) | |
54 # Values for self.join_mode (who can join the game - NONE means solo game) | |
55 ALL, INVITED, NONE = xrange(0, 3) | |
56 # Values for ready_mode (how to turn a MUC user into a player) | |
57 ASK, FORCE = xrange(0, 2) | |
58 | |
59 MESSAGE = '/message' | |
60 REQUEST = '%s/%s[@xmlns="%s"]' | |
61 | |
62 def __init__(self, host): | |
63 self.host = host | |
64 | |
65 def _init_(self, host, plugin_info, ns_tag, game_init={}, player_init={}): | |
66 """ | |
67 @param host | |
68 @param plugin_info: PLUGIN_INFO map of the game plugin | |
69 @ns_tag: couple (nameservice, tag) to construct the messages | |
70 @param game_init: dictionary for general game initialization | |
71 @param player_init: dictionary for player initialization, applicable to each player | |
72 """ | |
73 self.host = host | |
74 self.name = plugin_info["import_name"] | |
75 self.ns_tag = ns_tag | |
76 self.request = self.REQUEST % (self.MESSAGE, ns_tag[1], ns_tag[0]) | |
77 self.game_init = game_init | |
78 self.player_init = player_init | |
79 self.games = {} | |
80 self.invitations = {} # list of couple (x, y) with x the time and y a list of users | |
81 | |
82 # These are the default settings, which can be overwritten by child class after initialization | |
83 self.invite_mode = self.FROM_PLAYERS if self.player_init == {} else self.FROM_NONE | |
84 self.wait_mode = self.FOR_NONE if self.player_init == {} else self.FOR_ALL | |
85 self.join_mode = self.INVITED | |
86 self.ready_mode = self.FORCE # TODO: asking for confirmation is not implemented | |
87 | |
88 host.trigger.add("MUC user joined", self.userJoinedTrigger) | |
89 host.trigger.add("MUC user left", self.userLeftTrigger) | |
90 | |
91 def createOrInvite(self, room, other_players, profile): | |
92 """ | |
93 This is called only when someone explicitly wants to play. | |
94 The game must not be created if one already exists in the room, | |
95 or its creation could be postponed until all the expected players | |
96 join the room (in that case it will be created from userJoinedTrigger). | |
97 @param room: instance of wokkel.muc.Room | |
98 @param other_players: list for other players JID userhosts | |
99 """ | |
100 user_jid = self.host.getJidNStream(profile)[0] | |
101 room_jid_s = room.occupantJID.userhost() | |
102 nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid_s, profile) | |
103 nicks = [nick] | |
104 if self.gameExists(room_jid_s): | |
105 if not self.checkJoinAuth(room_jid_s, user_jid.userhost(), nick): | |
106 return | |
107 nicks.extend(self.invitePlayers(room, other_players, nick, profile)) | |
108 self.updatePlayers(room_jid_s, nicks, profile) | |
109 else: | |
110 self.initGame(room_jid_s, nick) | |
111 (auth, waiting, missing) = self.checkWaitAuth(room, other_players) | |
112 nicks.extend(waiting) | |
113 nicks.extend(self.invitePlayers(room, missing, nick, profile)) | |
114 if auth: | |
115 self.createGame(room_jid_s, nicks, profile) | |
116 else: | |
117 self.updatePlayers(room_jid_s, nicks, profile) | |
118 | |
119 def initGame(self, room_jid_s, referee_nick): | |
120 """Important: do not add the referee to 'players' yet. For a | |
121 <players /> message to be emitted whenever a new player is joining, | |
122 it is necessary to not modify 'players' outside of updatePlayers. | |
123 """ | |
124 referee = room_jid_s + '/' + referee_nick | |
125 self.games[room_jid_s] = {'referee': referee, 'players': [], 'started': False} | |
126 self.games[room_jid_s].update(self.game_init) | |
127 | |
128 def gameExists(self, room_jid_s, started=False): | |
129 """Return True if a game has been initialized/started. | |
130 @param started: if False, the game must be initialized only, | |
131 otherwise it must be initialized and started with createGame. | |
132 @return: True if a game is initialized/started in that room""" | |
133 return room_jid_s in self.games and (not started or self.games[room_jid_s]['started']) | |
134 | |
135 def checkJoinAuth(self, room_jid_s, user_jid_s=None, nick="", verbose=False): | |
136 """Checks if this profile is allowed to join the game. | |
137 The parameter nick is used to check if the user is already | |
138 a player in that game. When this method is called from | |
139 userJoinedTrigger, nick is also used to check the user | |
140 identity instead of user_jid_s (see TODO remark below). | |
141 @param room_jid_s: the room hosting the game | |
142 @param user_jid_s: JID userhost of the user | |
143 @param nick: nick of the user | |
144 """ | |
145 auth = False | |
146 if not self.gameExists(room_jid_s): | |
147 auth = False | |
148 elif self.join_mode == self.ALL or self.isPlayer(room_jid_s, nick): | |
149 auth = True | |
150 elif self.join_mode == self.INVITED: | |
151 # considering all the batches of invitations | |
152 for invitations in self.invitations[room_jid_s]: | |
153 if user_jid_s is not None: | |
154 if user_jid_s in invitations[1]: | |
155 auth = True | |
156 break | |
157 else: | |
158 # TODO: that's not secure enough but what to do if | |
159 # wokkel.muc.User's 'entity' attribute is not set?! | |
160 if nick in [JID(invited).user for invited in invitations[1]]: | |
161 auth = True | |
162 break | |
163 | |
164 if not auth and (verbose or _DEBUG): | |
165 debug(_("%s not allowed to join the game %s in %s") % (user_jid_s or nick, self.name, room_jid_s)) | |
166 return auth | |
167 | |
168 def updatePlayers(self, room_jid_s, nicks, profile): | |
169 """Signal to the room or to each player that some players joined the game""" | |
170 if nicks == []: | |
171 return | |
172 new_nicks = set(nicks).difference(self.games[room_jid_s]['players']) | |
173 if len(new_nicks) == 0: | |
174 return | |
175 self.games[room_jid_s]['players'].extend(new_nicks) | |
176 self.signalPlayers(room_jid_s, [JID(room_jid_s)], profile) | |
177 | |
178 def signalPlayers(self, room_jid_s, recipients, profile): | |
179 """Let these guys know that we are playing (they may not play themselves).""" | |
180 if self.gameExists(room_jid_s, started=True): | |
181 element = self.createStartElement(self.games[room_jid_s]['players']) | |
182 else: | |
183 element = self.createStartElement(self.games[room_jid_s]['players'], name="players") | |
184 for recipient in recipients: | |
185 self.send(recipient, element, profile=profile) | |
186 | |
187 def invitePlayers(self, room, other_players, nick, profile): | |
188 """Invite players to a room, associated game may exist or not. | |
189 @param room: wokkel.muc.Room instance | |
190 @param other_players: list of JID userhosts to invite | |
191 @param nick: nick of the user who send the invitation | |
192 @return: list of the invited players who were already in the room | |
193 """ | |
194 room_jid = room.occupantJID.userhostJID() | |
195 room_jid_s = room.occupantJID.userhost() | |
196 if not self.checkInviteAuth(room_jid_s, nick): | |
197 return [] | |
198 self.invitations.setdefault(room_jid_s, []) | |
199 # TODO: remove invitation waiting for too long, using the time data | |
200 self.invitations[room_jid_s].append((time(), other_players)) | |
201 nicks = [nick] | |
202 for player_jid in [JID(player) for player in other_players]: | |
203 # TODO: find a way to make it secure | |
204 other_nick = self.host.plugins["XEP-0045"].getRoomNickOfUser(room, player_jid, secure=False) | |
205 if other_nick is None: | |
206 self.host.plugins["XEP-0249"].invite(player_jid, room_jid, {"game": self.name}, profile) | |
207 else: | |
208 nicks.append(other_nick) | |
209 return nicks | |
210 | |
211 def checkInviteAuth(self, room_jid_s, nick, verbose=False): | |
212 """Checks if this profile is allowed to invite players""" | |
213 auth = False | |
214 if self.invite_mode == self.FROM_ALL or not self.gameExists(room_jid_s): | |
215 auth = True | |
216 elif self.invite_mode == self.FROM_NONE: | |
217 auth = not self.gameExists(room_jid_s, started=True) | |
218 elif self.invite_mode == self.FROM_REFEREE: | |
219 auth = self.isReferee(room_jid_s, nick) | |
220 elif self.invite_mode == self.FROM_PLAYERS: | |
221 auth = self.isPlayer(room_jid_s, nick) | |
222 if not auth and (verbose or _DEBUG): | |
223 debug(_("%s not allowed to invite for the game %s in %s") % (nick, self.name, room_jid_s)) | |
224 return auth | |
225 | |
226 def isReferee(self, room_jid_s, nick): | |
227 """Checks if the player with this nick is the referee for the game in this room""" | |
228 if not self.gameExists(room_jid_s): | |
229 return False | |
230 return room_jid_s + '/' + nick == self.games[room_jid_s]['referee'] | |
231 | |
232 def isPlayer(self, room_jid_s, nick): | |
233 """Checks if the player with this nick is a player for the game in this room. | |
234 Important: the referee is not in the 'players' list right after the game | |
235 initialization - check with isReferee to be sure nick is not a player. | |
236 """ | |
237 if not self.gameExists(room_jid_s): | |
238 return False | |
239 return nick in self.games[room_jid_s]['players'] or self.isReferee(room_jid_s, nick) | |
240 | |
241 def checkWaitAuth(self, room, other_players, verbose=False): | |
242 """Check if we must wait before starting the game or not. | |
243 @return: (x, y, z) with: | |
244 x: False if we must wait, True otherwise | |
245 y: the nicks of the players that have been checked and confirmed | |
246 z: the players that have not been checked or that are missing | |
247 """ | |
248 if self.wait_mode == self.FOR_NONE or other_players == []: | |
249 result = (True, [], other_players) | |
250 elif len(room.roster) < len(other_players) + 1: | |
251 result = (False, [], other_players) | |
252 else: | |
253 # TODO: find a way to make it secure | |
254 (nicks, missing) = self.host.plugins["XEP-0045"].getRoomNicksOfUsers(room, other_players, secure=False) | |
255 result = (len(nicks) == len(other_players), nicks, missing) | |
256 if not result[0] and (verbose or _DEBUG): | |
257 debug(_("Still waiting for %s before starting the game %s in %s") % (result[2], self.name, room.occupantJID.userhost())) | |
258 return result | |
259 | |
260 def getUniqueName(self, muc_service="", profile_key='@DEFAULT@'): | |
261 room = self.host.plugins["XEP-0045"].getUniqueName(muc_service, profile_key=profile_key) | |
262 return "sat_%s_%s" % (self.name.lower(), room) if room != "" else "" | |
263 | |
264 def prepareRoom(self, other_players=[], room_jid=None, profile_key='@NONE@'): | |
265 """Prepare the room for a game: create it and invite players. | |
266 @param other_players: list for other players JID userhosts | |
267 @param room_jid: JID userhost of the room to reuse or None to create a new room | |
268 """ | |
269 debug(_('Preparing room for %s game') % self.name) | |
270 profile = self.host.memory.getProfileName(profile_key) | |
271 if not profile: | |
272 error(_("Unknown profile")) | |
273 return | |
274 | |
275 def roomJoined(room): | |
276 """@param room: instance of wokkel.muc.Room""" | |
277 self.createOrInvite(room, other_players, profile) | |
278 | |
279 def afterClientInit(room_jid): | |
280 """Create/join the given room, or a unique generated one if no room is specified. | |
281 @param room_jid: room to join | |
282 """ | |
283 if room_jid is not None and room_jid != "": # a room name has been specified | |
284 if room_jid in self.host.plugins["XEP-0045"].clients[profile].joined_rooms: | |
285 roomJoined(self.host.plugins["XEP-0045"].clients[profile].joined_rooms[room_jid]) | |
286 return | |
287 else: | |
288 room_jid = self.getUniqueName(profile_key=profile_key) | |
289 if room_jid == "": | |
290 return | |
291 user_jid = self.host.getJidNStream(profile)[0] | |
292 d = self.host.plugins["XEP-0045"].join(JID(room_jid), user_jid.user, {}, profile) | |
293 d.addCallback(roomJoined) | |
294 | |
295 client = self.host.getClient(profile) | |
296 if not client: | |
297 error(_('No client for this profile key: %s') % profile_key) | |
298 return | |
299 client.client_initialized.addCallback(lambda ignore: afterClientInit(room_jid)) | |
300 | |
301 def userJoinedTrigger(self, room, user, profile): | |
302 """This trigger is used to check if the new user can take part of a game, | |
303 create the game if we were waiting for him or just update the players list. | |
304 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} | |
305 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID | |
306 @return: True to not interrupt the main process. | |
307 """ | |
308 room_jid_s = room.occupantJID.userhost() | |
309 profile_nick = room.occupantJID.resource | |
310 if not self.isReferee(room_jid_s, profile_nick): | |
311 return True # profile is not the referee | |
312 if not self.checkJoinAuth(room_jid_s, nick=user.nick): | |
313 # user not allowed but let him know that we are playing :p | |
314 self.signalPlayers(room_jid_s, [JID(room_jid_s + '/' + user.nick)], profile) | |
315 return True | |
316 if self.wait_mode == self.FOR_ALL: | |
317 # considering the last batch of invitations | |
318 batch = len(self.invitations[room_jid_s]) - 1 | |
319 if batch < 0: | |
320 error("Invitations from %s to play %s in %s have been lost!" % (profile_nick, self.name, room_jid_s)) | |
321 return True | |
322 other_players = self.invitations[room_jid_s][batch][1] | |
323 (auth, nicks, dummy) = self.checkWaitAuth(room, other_players) | |
324 if auth: | |
325 del self.invitations[room_jid_s][batch] | |
326 nicks.insert(0, profile_nick) # add the referee | |
327 self.createGame(room_jid_s, nicks, profile_key=profile) | |
328 return True | |
329 # let the room know that a new player joined | |
330 self.updatePlayers(room_jid_s, [user.nick], profile) | |
331 return True | |
332 | |
333 def userLeftTrigger(self, room, user, profile): | |
334 """This trigger is used to update or stop the game when a user leaves. | |
335 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} | |
336 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID | |
337 @return: True to not interrupt the main process. | |
338 """ | |
339 room_jid_s = room.occupantJID.userhost() | |
340 profile_nick = room.occupantJID.resource | |
341 if not self.isReferee(room_jid_s, profile_nick): | |
342 return True # profile is not the referee | |
343 if self.isPlayer(room_jid_s, user.nick): | |
344 try: | |
345 self.games[room_jid_s]['players'].remove(user.nick) | |
346 except ValueError: | |
347 pass | |
348 if self.wait_mode == self.FOR_ALL: | |
349 # allow this user to join the game again | |
350 user_jid = user.entity.userhost() | |
351 if len(self.invitations[room_jid_s]) == 0: | |
352 self.invitations[room_jid_s].append((time(), [user_jid])) | |
353 else: | |
354 batch = 0 # add to the first batch of invitations | |
355 if user_jid not in self.invitations[room_jid_s][batch][1]: | |
356 self.invitations[room_jid_s][batch][1].append(user_jid) | |
357 return True | |
358 | |
359 def checkCreateGameAndInit(self, room_jid_s, profile): | |
360 """Check if that profile can create the game. If the game can be created | |
361 but is not initialized yet, this method will also do the initialization | |
362 @return: a couple (create, sync) with: | |
363 - create: set to True to allow the game creation | |
364 - sync: set to True to advice a game synchronization | |
365 """ | |
366 user_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid_s, profile) | |
367 if not user_nick: | |
368 error('Internal error') | |
369 return False, False | |
370 if self.gameExists(room_jid_s): | |
371 referee = self.isReferee(room_jid_s, user_nick) | |
372 if self.gameExists(room_jid_s, started=True): | |
373 warning(_("%s game already created in room %s") % (self.name, room_jid_s)) | |
374 return False, referee | |
375 elif not referee: | |
376 warning(_("%s game in room %s can only be created by %s") % (self.name, room_jid_s, user_nick)) | |
377 return False, False | |
378 else: | |
379 self.initGame(room_jid_s, user_nick) | |
380 return True, False | |
381 | |
382 def createGame(self, room_jid_s, nicks=[], profile_key='@NONE@'): | |
383 """Create a new game - this can be called directly from a frontend | |
384 and skips all the checks and invitation system, but the game must | |
385 not exist and all the players must be in the room already. | |
386 @param room_jid: JID userhost of the room | |
387 @param nicks: list of players nicks in the room | |
388 @param profile_key: %(doc_profile_key)s""" | |
389 debug(_("Creating %s game in room %s") % (self.name, room_jid_s)) | |
390 profile = self.host.memory.getProfileName(profile_key) | |
391 if not profile: | |
392 error(_("profile %s is unknown") % profile_key) | |
393 return | |
394 (create, sync) = self.checkCreateGameAndInit(room_jid_s, profile) | |
395 if not create: | |
396 if sync: | |
397 debug(_('Synchronize game %s in %s for %s') % (self.name, room_jid_s, ', '.join(nicks))) | |
398 # TODO: we should call a method to re-send the information to a player who left | |
399 # and joined the room again, currently: we may restart a whole new round... | |
400 self.updatePlayers(room_jid_s, nicks, profile) | |
401 return | |
402 self.games[room_jid_s]['started'] = True | |
403 self.updatePlayers(room_jid_s, nicks, profile) | |
404 if self.player_init == {}: | |
405 return | |
406 # specific data to each player | |
407 status = {} | |
408 players_data = {} | |
409 for nick in nicks: | |
410 # The dict must be COPIED otherwise it is shared between all users | |
411 players_data[nick] = self.player_init.copy() | |
412 status[nick] = "init" | |
413 self.games[room_jid_s].update({'status': status, 'players_data': players_data}) | |
414 | |
415 def playerReady(self, player, referee, profile_key='@NONE@'): | |
416 """Must be called when player is ready to start a new game""" | |
417 profile = self.host.memory.getProfileName(profile_key) | |
418 if not profile: | |
419 error(_("profile %s is unknown") % profile_key) | |
420 return | |
421 debug('new player ready: %s' % profile) | |
422 self.send(JID(referee), 'player_ready', {'player': player}, profile=profile) | |
423 | |
424 def newRound(self, room_jid, data, profile): | |
425 """Launch a new round (reinit the user data)""" | |
426 debug(_('new round for %s game') % self.name) | |
427 game_data = self.games[room_jid.userhost()] | |
428 players = game_data['players'] | |
429 players_data = game_data['players_data'] | |
430 game_data['stage'] = "init" | |
431 | |
432 common_data, msg_elts = data if data is not None else (None, None) | |
433 | |
434 if isinstance(msg_elts, dict): | |
435 for player in players: | |
436 to_jid = JID(room_jid.userhost() + "/" + player) # FIXME: gof: | |
437 elem = msg_elts[player] if isinstance(msg_elts[player], domish.Element) else None | |
438 self.send(to_jid, elem, profile=profile) | |
439 elif isinstance(msg_elts, domish.Element): | |
440 self.send(room_jid, msg_elts, profile=profile) | |
441 if common_data is not None: | |
442 for player in players: | |
443 players_data[player].update(common_data) | |
444 | |
445 def createGameElt(self, to_jid, type_="normal"): | |
446 """Create a generic domish Element for the game""" | |
447 type_ = "normal" if to_jid.resource else "groupchat" | |
448 elt = domish.Element((None, 'message')) | |
449 elt["to"] = to_jid.full() | |
450 elt["type"] = type_ | |
451 elt.addElement(self.ns_tag) | |
452 return elt | |
453 | |
454 def createStartElement(self, players=None, name="started"): | |
455 """Create a game "started" domish Element | |
456 @param name: element name (default: "started"). | |
457 """ | |
458 started_elt = domish.Element((None, name)) | |
459 if players is None: | |
460 return started_elt | |
461 idx = 0 | |
462 for player in players: | |
463 player_elt = domish.Element((None, 'player')) | |
464 player_elt.addContent(player) | |
465 player_elt['index'] = str(idx) | |
466 idx += 1 | |
467 started_elt.addChild(player_elt) | |
468 return started_elt | |
469 | |
470 def send(self, to_jid, elem=None, attrs=None, content=None, profile=None): | |
471 """ | |
472 @param to_jid: recipient JID | |
473 @param elem: domish.Element, unicode or a couple: | |
474 - domish.Element to be directly added as a child to the message | |
475 - unicode name or couple (uri, name) to create a new domish.Element | |
476 and add it as a child to the message (see domish.Element.addElement) | |
477 @param attrs: dictionary of attributes for the new child | |
478 @param content: unicode that is appended to the child content | |
479 @param profile: the profile from which the message is sent | |
480 """ | |
481 if profile is None: | |
482 error(_("Message can not be sent without a sender profile")) | |
483 return | |
484 msg = self.createGameElt(to_jid) | |
485 if elem is not None: | |
486 if isinstance(elem, domish.Element): | |
487 msg.firstChildElement().addChild(elem) | |
488 else: | |
489 elem = msg.firstChildElement().addElement(elem) | |
490 if attrs is not None: | |
491 elem.attributes.update(attrs) | |
492 if content is not None: | |
493 elem.addContent(content) | |
494 self.host.profiles[profile].xmlstream.send(msg) | |
495 | |
496 if _DEBUG_FILE: | |
497 # From here you will see all the game messages | |
498 file_ = open("/tmp/game_messages", "a") | |
499 file_.write("%s from %s to %s: %s\n" % (self.name, profile, "room" if to_jid.resource is None else to_jid.resource, elem.toXml())) | |
500 file_.close() | |
501 | |
502 def getHandler(self, profile): | |
503 return RoomGameHandler(self) | |
504 | |
505 | |
506 class RoomGameHandler (XMPPHandler): | |
507 implements(iwokkel.IDisco) | |
508 | |
509 def __init__(self, plugin_parent): | |
510 self.plugin_parent = plugin_parent | |
511 self.host = plugin_parent.host | |
512 | |
513 def connectionInitialized(self): | |
514 self.xmlstream.addObserver(self.plugin_parent.request, self.plugin_parent.room_game_cmd, profile=self.parent.profile) | |
515 | |
516 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
517 return [disco.DiscoFeature(self.plugin_parent.ns_tag[0])] | |
518 | |
519 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
520 return [] |