diff --git a/.coveragerc b/.coveragerc index 4fb0851..22984ec 100644 --- a/.coveragerc +++ b/.coveragerc @@ -3,4 +3,4 @@ include = xpartamupp/* branch = True [report] -fail_under = 40 +fail_under = 30 diff --git a/.prospector.yaml b/.prospector.yaml index 7e067ca..ef773e6 100644 --- a/.prospector.yaml +++ b/.prospector.yaml @@ -17,8 +17,9 @@ pep8: pylint: options: max-args: 8 - max-attributes: 10 + max-attributes: 13 max-line-length: 99 + max-module-lines: 1250 disable: - C0111 - C0103 diff --git a/tests/test_echelon.py b/tests/test_echelon.py index 7f9a223..5281fa0 100644 --- a/tests/test_echelon.py +++ b/tests/test_echelon.py @@ -125,41 +125,41 @@ class TestArgumentParsing(TestCase): ([], Namespace(domain='lobby.wildfiregames.com', login='EcheLOn', log_level=30, xserver=None, no_verify=False, nickname='RatingsBot', password='XXXXXX', room='arena', - database_url='sqlite:///lobby_rankings.sqlite3')), + database_url='sqlite:///lobby_rankings.sqlite3', disable_legacy_lists=False)), (['--debug'], Namespace(domain='lobby.wildfiregames.com', login='EcheLOn', log_level=10, xserver=None, no_verify=False, nickname='RatingsBot', password='XXXXXX', room='arena', - database_url='sqlite:///lobby_rankings.sqlite3')), + database_url='sqlite:///lobby_rankings.sqlite3', disable_legacy_lists=False)), (['--quiet'], Namespace(domain='lobby.wildfiregames.com', login='EcheLOn', log_level=40, xserver=None, no_verify=False, nickname='RatingsBot', password='XXXXXX', room='arena', - database_url='sqlite:///lobby_rankings.sqlite3')), + database_url='sqlite:///lobby_rankings.sqlite3', disable_legacy_lists=False)), (['--verbose'], Namespace(domain='lobby.wildfiregames.com', login='EcheLOn', log_level=20, xserver=None, no_verify=False, nickname='RatingsBot', password='XXXXXX', room='arena', - database_url='sqlite:///lobby_rankings.sqlite3')), + database_url='sqlite:///lobby_rankings.sqlite3', disable_legacy_lists=False)), (['-m', 'lobby.domain.tld'], Namespace(domain='lobby.domain.tld', login='EcheLOn', log_level=30, nickname='RatingsBot', xserver=None, no_verify=False, password='XXXXXX', room='arena', - database_url='sqlite:///lobby_rankings.sqlite3')), + database_url='sqlite:///lobby_rankings.sqlite3', disable_legacy_lists=False)), (['--domain=lobby.domain.tld'], Namespace(domain='lobby.domain.tld', login='EcheLOn', log_level=30, nickname='RatingsBot', xserver=None, no_verify=False, password='XXXXXX', room='arena', - database_url='sqlite:///lobby_rankings.sqlite3')), + database_url='sqlite:///lobby_rankings.sqlite3', disable_legacy_lists=False)), (['-m', 'lobby.domain.tld', '-l', 'bot', '-p', '123456', '-n', 'Bot', '-r', 'arena123', '-v'], Namespace(domain='lobby.domain.tld', login='bot', log_level=20, nickname='Bot', xserver=None, no_verify=False, password='123456', room='arena123', - database_url='sqlite:///lobby_rankings.sqlite3')), + database_url='sqlite:///lobby_rankings.sqlite3', disable_legacy_lists=False)), (['--domain=lobby.domain.tld', '--login=bot', '--password=123456', '--nickname=Bot', '--room=arena123', '--database-url=sqlite:////tmp/db.sqlite3', '--verbose'], Namespace(domain='lobby.domain.tld', login='bot', log_level=20, nickname='Bot', xserver=None, no_verify=False, password='123456', room='arena123', - database_url='sqlite:////tmp/db.sqlite3')), - (['--no-verify'], + database_url='sqlite:////tmp/db.sqlite3', disable_legacy_lists=False)), + (['--disable-legacy-lists'], Namespace(domain='lobby.wildfiregames.com', login='EcheLOn', log_level=30, xserver=None, - no_verify=True, nickname='RatingsBot', password='XXXXXX', room='arena', - database_url='sqlite:///lobby_rankings.sqlite3')), + no_verify=False, nickname='RatingsBot', password='XXXXXX', room='arena', + database_url='sqlite:///lobby_rankings.sqlite3', disable_legacy_lists=True)), ]) def test_valid(self, cmd_args, expected_args): """Test valid parameter combinations.""" diff --git a/tests/test_xpartamupp.py b/tests/test_xpartamupp.py index 28cf74e..59749b4 100644 --- a/tests/test_xpartamupp.py +++ b/tests/test_xpartamupp.py @@ -93,38 +93,42 @@ class TestArgumentParsing(TestCase): @parameterized.expand([ ([], Namespace(domain='lobby.wildfiregames.com', login='xpartamupp', log_level=30, - xserver=None, no_verify=False, - nickname='WFGBot', password='XXXXXX', room='arena')), + xserver=None, no_verify=False, nickname='WFGBot', password='XXXXXX', + room='arena', disable_legacy_lists=False)), (['--debug'], Namespace(domain='lobby.wildfiregames.com', login='xpartamupp', log_level=10, - xserver=None, no_verify=False, - nickname='WFGBot', password='XXXXXX', room='arena')), + xserver=None, no_verify=False, nickname='WFGBot', password='XXXXXX', + room='arena', disable_legacy_lists=False)), (['--quiet'], Namespace(domain='lobby.wildfiregames.com', login='xpartamupp', log_level=40, - xserver=None, no_verify=False, - nickname='WFGBot', password='XXXXXX', room='arena')), + xserver=None, no_verify=False, nickname='WFGBot', password='XXXXXX', + room='arena', disable_legacy_lists=False)), (['--verbose'], Namespace(domain='lobby.wildfiregames.com', login='xpartamupp', log_level=20, - xserver=None, no_verify=False, - nickname='WFGBot', password='XXXXXX', room='arena')), + xserver=None, no_verify=False, nickname='WFGBot', password='XXXXXX', + room='arena', disable_legacy_lists=False)), (['-m', 'lobby.domain.tld'], Namespace(domain='lobby.domain.tld', login='xpartamupp', log_level=30, nickname='WFGBot', - xserver=None, no_verify=False, password='XXXXXX', room='arena')), + xserver=None, no_verify=False, password='XXXXXX', room='arena', + disable_legacy_lists=False)), (['--domain=lobby.domain.tld'], Namespace(domain='lobby.domain.tld', login='xpartamupp', log_level=30, nickname='WFGBot', - xserver=None, no_verify=False, password='XXXXXX', room='arena')), + xserver=None, no_verify=False, password='XXXXXX', room='arena', + disable_legacy_lists=False)), (['-m', 'lobby.domain.tld', '-l', 'bot', '-p', '123456', '-n', 'Bot', '-r', 'arena123', '-v'], Namespace(domain='lobby.domain.tld', login='bot', log_level=20, xserver=None, - no_verify=False, nickname='Bot', password='123456', room='arena123')), + no_verify=False, nickname='Bot', password='123456', room='arena123', + disable_legacy_lists=False)), (['--domain=lobby.domain.tld', '--login=bot', '--password=123456', '--nickname=Bot', '--room=arena123', '--verbose'], Namespace(domain='lobby.domain.tld', login='bot', log_level=20, xserver=None, - no_verify=False, nickname='Bot', password='123456', room='arena123')), - (['--no-verify'], + no_verify=False, nickname='Bot', password='123456', room='arena123', + disable_legacy_lists=False)), + (['--disable-legacy-lists'], Namespace(domain='lobby.wildfiregames.com', login='xpartamupp', log_level=30, - xserver=None, no_verify=True, - nickname='WFGBot', password='XXXXXX', room='arena')), + xserver=None, no_verify=False, nickname='WFGBot', password='XXXXXX', + room='arena', disable_legacy_lists=True)), ]) def test_valid(self, cmd_args, expected_args): """Test valid parameter combinations.""" diff --git a/xpartamupp/echelon.py b/xpartamupp/echelon.py index 0b6f037..012dc0f 100755 --- a/xpartamupp/echelon.py +++ b/xpartamupp/echelon.py @@ -29,7 +29,9 @@ from datetime import datetime, timedelta, timezone from slixmpp import ClientXMPP +from slixmpp.exceptions import IqError from slixmpp.jid import JID +from slixmpp.plugins.xep_0004 import Form from slixmpp.stanza import Iq from slixmpp.xmlstream.handler import Callback from slixmpp.xmlstream.matcher import StanzaPath @@ -477,7 +479,8 @@ def _get_report_diff(report1, report2): class EcheLOn(ClientXMPP): """Main class which handles IQ data and sends new data.""" - def __init__(self, sjid, password, room, nick, leaderboard, verify_certificate=True): + def __init__(self, sjid, password, room, nick, leaderboard, verify_certificate=True, + disable_legacy_lists=False): """Initialize EcheLOn. Arguments: @@ -489,6 +492,10 @@ def __init__(self, sjid, password, room, nick, leaderboard, verify_certificate=T verify_certificate (bool): Whether to verify the TLS certificate provided by the server + disable_legacy_lists (bool): Whether to use the old way to + provide rating information to + players in addition to using + PubSub """ super().__init__(sjid, password) @@ -506,6 +513,11 @@ def __init__(self, sjid, password, room, nick, leaderboard, verify_certificate=T self.room = room self.nick = nick + self.pubsub_jid = JID("pubsub." + self.server) + self.pubsub_leaderbord_node = f"0ad#{self.room.local}#boardlist#v1" + self.pubsub_ratinglist_node = f"0ad#{self.room.local}#ratinglist#v1" + self.legacy_lists_disabled = disable_legacy_lists + self.leaderboard = leaderboard self.report_manager = ReportManager(self.leaderboard) @@ -523,6 +535,7 @@ def __init__(self, sjid, password, room, nick, leaderboard, verify_certificate=T self._iq_profile_handler)) self.add_event_handler('session_start', self._session_start) + self.add_event_handler('disco_items', self._pubsub_node_disco) self.add_event_handler('muc::%s::got_online' % self.room, self._muc_online) self.add_event_handler('muc::%s::got_offline' % self.room, self._muc_offline) self.add_event_handler('groupchat_message', self._muc_message) @@ -576,6 +589,108 @@ async def _reconnect(self, event): # pylint: disable=unused-argument self.connect() + async def _create_pubsub_node(self, node_name, node_config): + """Create a new PubSub node. + + This creates a new PubSub node with the given configuration and + checks whether the node got the expected node name assigned. + + Arguments: + node_name (str): Desired name of the PubSub node + node_config (Form): form with options to send when + creating the node + """ + try: + result = await self.plugin['xep_0060'].create_node(jid=self.pubsub_jid, + node=node_name, + config=node_config) + except IqError as exc: + logging.error("Creating the PubSub node failed: %s", exc.text) + else: + if result["pubsub"]["create"]["node"] != node_name: + logging.error('Created PubSub node got a different node name ("%s") than ' + 'expected ("%s")', result["pubsub"]["create"]["node"], node_name) + + async def _check_pubsub_node_config(self, node_name, node_config): + """Check the configuration of a PubSub node. + + This checks if the configuration of an existing PubSub node is + as expected. + + Arguments: + node_name (str): Name of the PubSub node to check + node_config (Form): form with options to check the node + configuration against + """ + current_node_config = await self.plugin['xep_0060'].get_node_config( + jid=self.pubsub_jid, node=node_name) + current_node_config_form: Form = current_node_config["pubsub_owner"]["configure"]["form"] + + differences = {} + current_node_config_dict = current_node_config_form.get_values() + for key, new_value in node_config.get_values().items(): + if current_node_config_dict.get(key) != new_value: + differences[key] = (new_value, current_node_config_dict.get(key)) + + if differences: + logging.warning("Existing PubSub node config differs from expected config! This " + "will likely cause the lobby not to behave as expected!") + for key, value in differences.items(): + logging.warning('Current value ("%s") for option "%s" is different than the ' + 'expected one ("%s")', value[1], key, value[0]) + + async def _pubsub_node_disco(self, event): + """Handle discovery and creation of PubSub nodes. + + This handles disco responses from the PubSub service to + discover the necessary PubSub node for publishing game list + information. If the node doesn't exist, it'll be created with + the proper configuration. Creation only needs to happen once + per node name and can be done manually as well. + + Arguments: + event (IQ): Disco response event + """ + if event["from"] != self.pubsub_jid or not event.get("disco_items"): + return + + nodes = event["disco_items"]["items"] + node_names = [node[1] for node in nodes] + + default_node_config = await self.plugin['xep_0060'].get_node_config(jid=self.pubsub_jid) + new_node_config_form: Form = default_node_config["pubsub_owner"]["default"]["form"] + new_node_config_form.reply() + + answers = { + "pubsub#access_model": "open", + "pubsub#deliver_notifications": True, + "pubsub#deliver_payloads": True, + "pubsub#itemreply": "none", + "pubsub#max_payload_size": "250000", # current maximum for ejabberd + "pubsub#notification_type": "normal", + "pubsub#notify_config": False, + "pubsub#notify_delete": False, + "pubsub#notify_retract": False, + "pubsub#persist_items": False, + "pubsub#presence_based_delivery": True, + "pubsub#publish_model": "publishers", + "pubsub#purge_offline": False, + "pubsub#send_last_published_item": "on_sub_and_presence", + "pubsub#subscribe": True, + } + for field, answer in answers.items(): + new_node_config_form.field[field].set_answer(answer) + + if self.pubsub_leaderbord_node not in node_names: + await self._create_pubsub_node(self.pubsub_leaderbord_node, new_node_config_form) + else: + await self._check_pubsub_node_config(self.pubsub_leaderbord_node, new_node_config_form) + + if self.pubsub_ratinglist_node not in node_names: + await self._create_pubsub_node(self.pubsub_ratinglist_node, new_node_config_form) + else: + await self._check_pubsub_node_config(self.pubsub_ratinglist_node, new_node_config_form) + def _muc_online(self, presence): """Add joining players to the list of players. @@ -594,7 +709,9 @@ def _muc_online(self, presence): jid_0ad_res.resource = "0ad" self.leaderboard.get_or_create_player(jid_0ad_res) - self._broadcast_rating_list() + self._publish_rating_list() + if not self.legacy_lists_disabled: + self._broadcast_rating_list() logging.debug("Client '%s' connected with a nick of '%s'.", jid, nick) @@ -650,6 +767,11 @@ def _iq_board_list_handler(self, iq): iq (IQ): Received IQ stanza """ + if self.legacy_lists_disabled: + logging.debug("Retrieved request from client for ratings, but this deprecated " + "feature is disabled") + return + if not iq['from'].resource.startswith('0ad'): return @@ -689,7 +811,11 @@ def _iq_game_report_handler(self, iq): while rating_messages: message = rating_messages.popleft() self.send_message(mto=self.room, mbody=message, mtype='groupchat', mnick=self.nick) - self._broadcast_rating_list() + + self._publish_leaderboard() + self._publish_rating_list() + if not self.legacy_lists_disabled: + self._broadcast_rating_list() def _iq_profile_handler(self, iq): """Handle profile requests from clients. @@ -707,6 +833,21 @@ def _iq_profile_handler(self, iq): logging.exception("Failed to send profile about %s to %s", iq['profile']['command'], iq['from'].bare) + def _publish_leaderboard(self): + """Publish the leaderboard. + + This publishes the current leaderboard as an item to the + configured PubSub node. + """ + ratings = self.leaderboard.get_board() + stanza = BoardListXmppPlugin() + stanza.add_command('boardlist') + for player in ratings.values(): + stanza.add_item(player['name'], player['rating']) + + self.plugin['xep_0060'].publish(jid=self.pubsub_jid, node=self.pubsub_leaderbord_node, + payload=stanza) + def _send_leaderboard(self, iq): """Send the whole leaderboard. @@ -728,6 +869,31 @@ def _send_leaderboard(self, iq): except Exception: logging.exception("Failed to send leaderboard to %s", iq['to']) + def _publish_rating_list(self): + """Publish the rating list. + + This publishes the ratings of all currently online players as + an item to the configured PubSub node. + """ + nicks = {} + for nick in self.plugin['xep_0045'].get_roster(self.room): + jid = JID(self.plugin['xep_0045'].get_jid_property(self.room, nick, 'jid')) + + if not jid.resource.startswith('0ad'): + continue + + nicks[jid] = nick + + ratings = self.leaderboard.get_rating_list(nicks) + + stanza = BoardListXmppPlugin() + stanza.add_command('ratinglist') + for player in ratings.values(): + stanza.add_item(player['name'], player['rating']) + + self.plugin['xep_0060'].publish(jid=self.pubsub_jid, node=self.pubsub_ratinglist_node, + payload=stanza) + def _send_rating_list(self, iq): """Send the ratings of all online players. @@ -858,6 +1024,9 @@ def parse_args(): parser.add_argument('--no-verify', help="Don't verify the TLS server certificate when connecting", action='store_true') + parser.add_argument('--disable-legacy-lists', + help='Disable the deprecated pre-PubSub way of sending lists to players.', + action='store_true') return parser.parse_args() @@ -873,7 +1042,8 @@ def main(): leaderboard = Leaderboard(args.database_url) xmpp = EcheLOn(JID('%s@%s/%s' % (args.login, args.domain, 'CC')), args.password, JID(args.room + '@conference.' + args.domain), args.nickname, leaderboard, - verify_certificate=not args.no_verify) + verify_certificate=not args.no_verify, + disable_legacy_lists=args.disable_legacy_lists) xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0045') # Multi-User Chat diff --git a/xpartamupp/xpartamupp.py b/xpartamupp/xpartamupp.py index 5940913..13fad07 100755 --- a/xpartamupp/xpartamupp.py +++ b/xpartamupp/xpartamupp.py @@ -27,7 +27,9 @@ from datetime import datetime, timedelta, timezone from slixmpp import ClientXMPP +from slixmpp.exceptions import IqError from slixmpp.jid import JID +from slixmpp.plugins.xep_0004 import Form from slixmpp.stanza import Iq from slixmpp.xmlstream.handler import Callback from slixmpp.xmlstream.matcher import StanzaPath @@ -136,7 +138,8 @@ def change_game_state(self, jid, data): class XpartaMuPP(ClientXMPP): """Main class which handles IQ data and sends new data.""" - def __init__(self, sjid, password, room, nick, verify_certificate=True): + def __init__(self, sjid, password, room, nick, verify_certificate=True, + disable_legacy_lists=False): """Initialize XpartaMuPP. Arguments: @@ -147,6 +150,9 @@ def __init__(self, sjid, password, room, nick, verify_certificate=True): verify_certificate (bool): Whether to verify the TLS certificate provided by the server + disable_legacy_lists (bool): Whether to use the old way to + send game lists to players in + addition to using PubSub """ super().__init__(sjid, password) @@ -163,6 +169,10 @@ def __init__(self, sjid, password, room, nick, verify_certificate=True): self.room = room self.nick = nick + self.pubsub_jid = JID("pubsub." + self.server) + self.pubsub_gamelist_node = f"0ad#{self.room.local}#gamelist#v1" + self.legacy_lists_disabled = disable_legacy_lists + self.games = Games() self.last_info_msg = None @@ -173,6 +183,7 @@ def __init__(self, sjid, password, room, nick, verify_certificate=True): self._iq_game_list_handler)) self.add_event_handler('session_start', self._session_start) + self.add_event_handler('disco_items', self._pubsub_node_disco) self.add_event_handler('muc::%s::got_online' % self.room, self._muc_online) self.add_event_handler('muc::%s::got_offline' % self.room, self._muc_offline) self.add_event_handler('groupchat_message', self._muc_message) @@ -187,6 +198,8 @@ async def _session_start(self, event): # pylint: disable=unused-argument """ self._connect_loop_wait_reconnect = 0 + + await self.plugin['xep_0060'].get_nodes(jid=self.pubsub_jid) await self.plugin['xep_0045'].join_muc_wait(self.room, self.nick) self.send_presence() self.get_roster() @@ -226,6 +239,103 @@ async def _reconnect(self, event): # pylint: disable=unused-argument self.connect() + async def _create_pubsub_node(self, node_name, node_config): + """Create a new PubSub node. + + This creates a new PubSub node with the given configuration and + checks whether the node got the expected node name assigned. + + Arguments: + node_name (str): Desired name of the PubSub node + node_config (Form): form with options to send when + creating the node + """ + try: + result = await self.plugin['xep_0060'].create_node(jid=self.pubsub_jid, + node=node_name, + config=node_config) + except IqError as exc: + logging.error("Creating the PubSub node failed: %s", exc.text) + else: + if result["pubsub"]["create"]["node"] != node_name: + logging.error('Created PubSub node got a different node name ("%s") than ' + 'expected ("%s")', result["pubsub"]["create"]["node"], node_name) + + async def _check_pubsub_node_config(self, node_name, node_config): + """Check the configuration of a PubSub node. + + This checks if the configuration of an existing PubSub node is + as expected. + + Arguments: + node_name (str): Name of the PubSub node to check + node_config (Form): form with options to check the node + configuration against + """ + current_node_config = await self.plugin['xep_0060'].get_node_config( + jid=self.pubsub_jid, node=node_name) + current_node_config_form: Form = current_node_config["pubsub_owner"]["configure"]["form"] + + differences = {} + current_node_config_dict = current_node_config_form.get_values() + for key, new_value in node_config.get_values().items(): + if current_node_config_dict.get(key) != new_value: + differences[key] = (new_value, current_node_config_dict.get(key)) + + if differences: + logging.warning("Existing PubSub node config differs from expected config! This " + "will likely cause the lobby not to behave as expected!") + for key, value in differences.items(): + logging.warning('Current value ("%s") for option "%s" is different than the ' + 'expected one ("%s")', value[1], key, value[0]) + + async def _pubsub_node_disco(self, event): + """Handle discovery and creation of PubSub nodes. + + This handles disco responses from the PubSub service to + discover the necessary PubSub node for publishing game list + information. If the node doesn't exist, it'll be created with + the proper configuration. Creation only needs to happen once + per node name and can be done manually as well. + + Arguments: + event (IQ): Disco response event + """ + if event["from"] != self.pubsub_jid or not event.get("disco_items"): + return + + nodes = event["disco_items"]["items"] + node_names = [node[1] for node in nodes] + + default_node_config = await self.plugin['xep_0060'].get_node_config(jid=self.pubsub_jid) + new_node_config_form: Form = default_node_config["pubsub_owner"]["default"]["form"] + new_node_config_form.reply() + + answers = { + "pubsub#access_model": "open", + "pubsub#deliver_notifications": True, + "pubsub#deliver_payloads": True, + "pubsub#itemreply": "none", + "pubsub#max_payload_size": "250000", # current maximum for ejabberd + "pubsub#notification_type": "normal", + "pubsub#notify_config": False, + "pubsub#notify_delete": False, + "pubsub#notify_retract": False, + "pubsub#persist_items": False, + "pubsub#presence_based_delivery": True, + "pubsub#publish_model": "publishers", + "pubsub#purge_offline": False, + "pubsub#send_last_published_item": "on_sub_and_presence", + "pubsub#subscribe": True, + } + for field, answer in answers.items(): + new_node_config_form.field[field].set_answer(answer) + + if self.pubsub_gamelist_node not in node_names: + await self._create_pubsub_node(self.pubsub_gamelist_node, new_node_config_form) + else: + await self._check_pubsub_node_config(self.pubsub_gamelist_node, new_node_config_form) + def _muc_online(self, presence): """Add joining players to the list of players. @@ -243,7 +353,9 @@ def _muc_online(self, presence): if not jid.resource.startswith('0ad'): return - self._send_game_list(jid) + self._publish_game_list() + if not self.legacy_lists_disabled: + self._send_game_list(jid) logging.debug("Client '%s' connected with a nick '%s'.", jid, nick) @@ -265,7 +377,9 @@ def _muc_offline(self, presence): return if self.games.remove_game(jid): - self._send_game_list() + self._publish_game_list() + if not self.legacy_lists_disabled: + self._send_game_list() logging.debug("Client '%s' with nick '%s' disconnected", jid, nick) @@ -327,10 +441,33 @@ def _iq_game_list_handler(self, iq): if success: try: - self._send_game_list() + self._publish_game_list() + if not self.legacy_lists_disabled: + self._send_game_list() except Exception: logging.exception('Failed to send game list after "%s" command', command) + def _publish_game_list(self): + """Publish the game list. + + This publishes the game list as an item to the configured + PubSub node. + """ + games = self.games.get_all_games() + + online_jids = [] + for nick in self.plugin['xep_0045'].get_roster(self.room): + online_jids.append(JID(self.plugin['xep_0045'].get_jid_property(self.room, nick, + 'jid'))) + + stanza = GameListXmppPlugin() + for jid in games: + if jid in online_jids: + stanza.add_game(games[jid]) + + self.plugin['xep_0060'].publish(jid=self.pubsub_jid, node=self.pubsub_gamelist_node, + payload=stanza) + def _send_game_list(self, to=None): """Send a massive stanza with the whole game list. @@ -403,6 +540,9 @@ def parse_args(): parser.add_argument('--no-verify', help="Don't verify the TLS server certificate when connecting", action='store_true') + parser.add_argument('--disable-legacy-lists', + help='Disable the deprecated pre-PubSub way of sending lists to players.', + action='store_true') return parser.parse_args() @@ -417,7 +557,8 @@ def main(): xmpp = XpartaMuPP(JID('%s@%s/%s' % (args.login, args.domain, 'CC')), args.password, JID(args.room + '@conference.' + args.domain), args.nickname, - verify_certificate=not args.no_verify) + verify_certificate=not args.no_verify, + disable_legacy_lists=args.disable_legacy_lists) xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0045') # Multi-User Chat