2013-11-07 21:07:24 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
2016-08-16 05:35:53 +02:00
|
|
|
"""Copyright (C) 2016 Wildfire Games.
|
2013-11-07 21:07:24 +01:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. 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 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
|
|
|
2013-11-10 05:28:18 +01:00
|
|
|
import logging, time, traceback
|
2013-11-07 21:07:24 +01:00
|
|
|
from optparse import OptionParser
|
|
|
|
|
|
|
|
import sleekxmpp
|
|
|
|
from sleekxmpp.stanza import Iq
|
|
|
|
from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin, ET
|
|
|
|
from sleekxmpp.xmlstream.handler import Callback
|
|
|
|
from sleekxmpp.xmlstream.matcher import StanzaPath
|
|
|
|
|
|
|
|
## Class to tracks all games in the lobby ##
|
|
|
|
class GameList():
|
|
|
|
def __init__(self):
|
|
|
|
self.gameList = {}
|
|
|
|
def addGame(self, JID, data):
|
|
|
|
"""
|
|
|
|
Add a game
|
|
|
|
"""
|
|
|
|
data['players-init'] = data['players']
|
|
|
|
data['nbp-init'] = data['nbp']
|
|
|
|
data['state'] = 'init'
|
|
|
|
self.gameList[str(JID)] = data
|
|
|
|
def removeGame(self, JID):
|
|
|
|
"""
|
|
|
|
Remove a game attached to a JID
|
|
|
|
"""
|
|
|
|
del self.gameList[str(JID)]
|
|
|
|
def getAllGames(self):
|
|
|
|
"""
|
|
|
|
Returns all games
|
|
|
|
"""
|
|
|
|
return self.gameList
|
|
|
|
def changeGameState(self, JID, data):
|
|
|
|
"""
|
|
|
|
Switch game state between running and waiting
|
|
|
|
"""
|
|
|
|
JID = str(JID)
|
|
|
|
if JID in self.gameList:
|
|
|
|
if self.gameList[JID]['nbp-init'] > data['nbp']:
|
|
|
|
logging.debug("change game (%s) state from %s to %s", JID, self.gameList[JID]['state'], 'waiting')
|
|
|
|
self.gameList[JID]['state'] = 'waiting'
|
|
|
|
else:
|
|
|
|
logging.debug("change game (%s) state from %s to %s", JID, self.gameList[JID]['state'], 'running')
|
|
|
|
self.gameList[JID]['state'] = 'running'
|
2016-07-20 18:04:23 +02:00
|
|
|
self.gameList[JID]['nbp'] = data['nbp']
|
|
|
|
self.gameList[JID]['players'] = data['players']
|
2016-08-14 11:03:30 +02:00
|
|
|
if 'startTime' not in self.gameList[JID]:
|
2016-08-21 22:35:13 +02:00
|
|
|
self.gameList[JID]['startTime'] = str(round(time.time()))
|
2013-11-07 21:07:24 +01:00
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
## Class for custom player stanza extension ##
|
|
|
|
class PlayerXmppPlugin(ElementBase):
|
|
|
|
name = 'query'
|
|
|
|
namespace = 'jabber:iq:player'
|
|
|
|
interfaces = set(('online'))
|
|
|
|
sub_interfaces = interfaces
|
|
|
|
plugin_attrib = 'player'
|
2013-11-07 21:07:24 +01:00
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
def addPlayerOnline(self, player):
|
|
|
|
playerXml = ET.fromstring("<online>%s</online>" % player)
|
|
|
|
self.xml.append(playerXml)
|
2013-11-07 21:07:24 +01:00
|
|
|
|
|
|
|
## Class for custom gamelist stanza extension ##
|
|
|
|
class GameListXmppPlugin(ElementBase):
|
|
|
|
name = 'query'
|
|
|
|
namespace = 'jabber:iq:gamelist'
|
|
|
|
interfaces = set(('game', 'command'))
|
|
|
|
sub_interfaces = interfaces
|
|
|
|
plugin_attrib = 'gamelist'
|
|
|
|
|
|
|
|
def addGame(self, data):
|
|
|
|
itemXml = ET.Element("game", data)
|
|
|
|
self.xml.append(itemXml)
|
|
|
|
|
|
|
|
def getGame(self):
|
|
|
|
"""
|
|
|
|
Required to parse incoming stanzas with this
|
|
|
|
extension.
|
|
|
|
"""
|
|
|
|
game = self.xml.find('{%s}game' % self.namespace)
|
|
|
|
data = {}
|
|
|
|
for key, item in game.items():
|
|
|
|
data[key] = item
|
|
|
|
return data
|
|
|
|
|
2014-01-24 00:13:13 +01:00
|
|
|
## Class for custom boardlist and ratinglist stanza extension ##
|
2013-11-07 21:07:24 +01:00
|
|
|
class BoardListXmppPlugin(ElementBase):
|
|
|
|
name = 'query'
|
|
|
|
namespace = 'jabber:iq:boardlist'
|
2016-08-16 05:35:53 +02:00
|
|
|
interfaces = set(('board', 'command', 'recipient'))
|
2013-11-07 21:07:24 +01:00
|
|
|
sub_interfaces = interfaces
|
|
|
|
plugin_attrib = 'boardlist'
|
2014-01-24 00:13:13 +01:00
|
|
|
def addCommand(self, command):
|
|
|
|
commandXml = ET.fromstring("<command>%s</command>" % command)
|
|
|
|
self.xml.append(commandXml)
|
2016-08-16 05:35:53 +02:00
|
|
|
def addRecipient(self, recipient):
|
|
|
|
recipientXml = ET.fromstring("<recipient>%s</recipient>" % recipient)
|
|
|
|
self.xml.append(recipientXml)
|
2013-11-07 21:07:24 +01:00
|
|
|
def addItem(self, name, rating):
|
|
|
|
itemXml = ET.Element("board", {"name": name, "rating": rating})
|
|
|
|
self.xml.append(itemXml)
|
|
|
|
|
|
|
|
## Class for custom gamereport stanza extension ##
|
|
|
|
class GameReportXmppPlugin(ElementBase):
|
|
|
|
name = 'report'
|
|
|
|
namespace = 'jabber:iq:gamereport'
|
|
|
|
plugin_attrib = 'gamereport'
|
2016-08-16 05:35:53 +02:00
|
|
|
interfaces = ('game', 'sender')
|
2013-11-07 21:07:24 +01:00
|
|
|
sub_interfaces = interfaces
|
2016-08-16 05:35:53 +02:00
|
|
|
def addSender(self, sender):
|
|
|
|
senderXml = ET.fromstring("<sender>%s</sender>" % sender)
|
|
|
|
self.xml.append(senderXml)
|
|
|
|
def addGame(self, gr):
|
|
|
|
game = ET.fromstring(str(gr)).find('{%s}game' % self.namespace)
|
|
|
|
self.xml.append(game)
|
2013-11-07 21:07:24 +01:00
|
|
|
def getGame(self):
|
|
|
|
"""
|
|
|
|
Required to parse incoming stanzas with this
|
|
|
|
extension.
|
|
|
|
"""
|
|
|
|
game = self.xml.find('{%s}game' % self.namespace)
|
|
|
|
data = {}
|
|
|
|
for key, item in game.items():
|
|
|
|
data[key] = item
|
|
|
|
return data
|
|
|
|
|
2014-09-20 17:35:26 +02:00
|
|
|
## Class for custom profile ##
|
|
|
|
class ProfileXmppPlugin(ElementBase):
|
|
|
|
name = 'query'
|
|
|
|
namespace = 'jabber:iq:profile'
|
2016-08-16 05:35:53 +02:00
|
|
|
interfaces = set(('profile', 'command', 'recipient'))
|
2014-09-20 17:35:26 +02:00
|
|
|
sub_interfaces = interfaces
|
|
|
|
plugin_attrib = 'profile'
|
|
|
|
def addCommand(self, command):
|
|
|
|
commandXml = ET.fromstring("<command>%s</command>" % command)
|
|
|
|
self.xml.append(commandXml)
|
2016-08-16 05:35:53 +02:00
|
|
|
def addRecipient(self, recipient):
|
|
|
|
recipientXml = ET.fromstring("<recipient>%s</recipient>" % recipient)
|
|
|
|
self.xml.append(recipientXml)
|
2014-09-20 17:35:26 +02:00
|
|
|
def addItem(self, player, rating, highestRating, rank, totalGamesPlayed, wins, losses):
|
|
|
|
itemXml = ET.Element("profile", {"player": player, "rating": rating, "highestRating": highestRating,
|
|
|
|
"rank" : rank, "totalGamesPlayed" : totalGamesPlayed, "wins" : wins,
|
|
|
|
"losses" : losses})
|
|
|
|
self.xml.append(itemXml)
|
|
|
|
|
2013-11-07 21:07:24 +01:00
|
|
|
## Main class which handles IQ data and sends new data ##
|
|
|
|
class XpartaMuPP(sleekxmpp.ClientXMPP):
|
|
|
|
"""
|
|
|
|
A simple list provider
|
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
def __init__(self, sjid, password, room, nick, ratingsbot):
|
2013-11-07 21:07:24 +01:00
|
|
|
sleekxmpp.ClientXMPP.__init__(self, sjid, password)
|
|
|
|
self.sjid = sjid
|
|
|
|
self.room = room
|
|
|
|
self.nick = nick
|
2016-08-16 05:35:53 +02:00
|
|
|
self.ratingsBotWarned = False
|
2013-11-07 21:07:24 +01:00
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
self.ratingsBot = ratingsbot
|
2013-11-07 21:07:24 +01:00
|
|
|
# Game collection
|
|
|
|
self.gameList = GameList()
|
|
|
|
|
|
|
|
# Store mapping of nicks and XmppIDs, attached via presence stanza
|
|
|
|
self.nicks = {}
|
2013-12-26 20:52:07 +01:00
|
|
|
|
2013-11-07 21:07:24 +01:00
|
|
|
self.lastLeft = ""
|
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
register_stanza_plugin(Iq, PlayerXmppPlugin)
|
2013-11-07 21:07:24 +01:00
|
|
|
register_stanza_plugin(Iq, GameListXmppPlugin)
|
|
|
|
register_stanza_plugin(Iq, BoardListXmppPlugin)
|
|
|
|
register_stanza_plugin(Iq, GameReportXmppPlugin)
|
2014-09-20 17:35:26 +02:00
|
|
|
register_stanza_plugin(Iq, ProfileXmppPlugin)
|
2013-11-07 21:07:24 +01:00
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
self.register_handler(Callback('Iq Player',
|
|
|
|
StanzaPath('iq/player'),
|
|
|
|
self.iqhandler,
|
|
|
|
instream=True))
|
2013-11-07 21:07:24 +01:00
|
|
|
self.register_handler(Callback('Iq Gamelist',
|
|
|
|
StanzaPath('iq/gamelist'),
|
|
|
|
self.iqhandler,
|
|
|
|
instream=True))
|
|
|
|
self.register_handler(Callback('Iq Boardlist',
|
|
|
|
StanzaPath('iq/boardlist'),
|
|
|
|
self.iqhandler,
|
|
|
|
instream=True))
|
|
|
|
self.register_handler(Callback('Iq GameReport',
|
|
|
|
StanzaPath('iq/gamereport'),
|
|
|
|
self.iqhandler,
|
|
|
|
instream=True))
|
2014-09-20 17:35:26 +02:00
|
|
|
self.register_handler(Callback('Iq Profile',
|
|
|
|
StanzaPath('iq/profile'),
|
|
|
|
self.iqhandler,
|
|
|
|
instream=True))
|
2013-12-26 20:52:07 +01:00
|
|
|
|
2013-11-07 21:07:24 +01:00
|
|
|
self.add_event_handler("session_start", self.start)
|
|
|
|
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)
|
2014-01-25 20:00:22 +01:00
|
|
|
self.add_event_handler("groupchat_message", self.muc_message)
|
2013-11-07 21:07:24 +01:00
|
|
|
|
|
|
|
def start(self, event):
|
|
|
|
"""
|
|
|
|
Process the session_start event
|
|
|
|
"""
|
|
|
|
self.plugin['xep_0045'].joinMUC(self.room, self.nick)
|
|
|
|
self.send_presence()
|
|
|
|
self.get_roster()
|
|
|
|
logging.info("XpartaMuPP started")
|
|
|
|
|
|
|
|
def muc_online(self, presence):
|
|
|
|
"""
|
|
|
|
Process presence stanza from a chat room.
|
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
if self.ratingsBot in self.nicks:
|
|
|
|
self.relayRatingListRequest(self.ratingsBot)
|
|
|
|
self.relayPlayerOnline(presence['muc']['jid'])
|
2013-11-07 21:07:24 +01:00
|
|
|
if presence['muc']['nick'] != self.nick:
|
2013-11-25 05:05:41 +01:00
|
|
|
# If it doesn't already exist, store player JID mapped to their nick.
|
|
|
|
if str(presence['muc']['jid']) not in self.nicks:
|
|
|
|
self.nicks[str(presence['muc']['jid'])] = presence['muc']['nick']
|
2013-11-07 21:07:24 +01:00
|
|
|
# Check the jid isn't already in the lobby.
|
2015-11-08 18:09:59 +01:00
|
|
|
# Send Gamelist to new player.
|
|
|
|
self.sendGameList(presence['muc']['jid'])
|
2013-11-07 21:07:24 +01:00
|
|
|
logging.debug("Client '%s' connected with a nick of '%s'." %(presence['muc']['jid'], presence['muc']['nick']))
|
|
|
|
|
|
|
|
def muc_offline(self, presence):
|
|
|
|
"""
|
|
|
|
Process presence stanza from a chat room.
|
|
|
|
"""
|
|
|
|
# Clean up after a player leaves
|
|
|
|
if presence['muc']['nick'] != self.nick:
|
2013-11-25 05:05:41 +01:00
|
|
|
# Delete any games they were hosting.
|
2013-11-07 21:07:24 +01:00
|
|
|
for JID in self.gameList.getAllGames():
|
2013-11-25 03:55:47 +01:00
|
|
|
if JID == str(presence['muc']['jid']):
|
2013-11-07 21:07:24 +01:00
|
|
|
self.gameList.removeGame(JID)
|
|
|
|
self.sendGameList()
|
|
|
|
break
|
2013-11-25 05:05:41 +01:00
|
|
|
# Remove them from the local player list.
|
2013-11-07 21:07:24 +01:00
|
|
|
self.lastLeft = str(presence['muc']['jid'])
|
2013-11-25 05:05:41 +01:00
|
|
|
if str(presence['muc']['jid']) in self.nicks:
|
|
|
|
del self.nicks[str(presence['muc']['jid'])]
|
2016-08-16 05:35:53 +02:00
|
|
|
if presence['muc']['nick'] == self.ratingsBot:
|
|
|
|
self.ratingsBotWarned = False
|
2013-11-07 21:07:24 +01:00
|
|
|
|
2014-01-25 19:57:28 +01:00
|
|
|
def muc_message(self, msg):
|
|
|
|
"""
|
|
|
|
Process new messages from the chatroom.
|
|
|
|
"""
|
|
|
|
if msg['mucnick'] != self.nick and self.nick.lower() in msg['body'].lower():
|
|
|
|
self.send_message(mto=msg['from'].bare,
|
2014-02-16 07:06:16 +01:00
|
|
|
mbody="I am the administrative bot in this lobby and cannot participate in any games.",
|
2014-01-25 19:57:28 +01:00
|
|
|
mtype='groupchat')
|
|
|
|
|
2013-11-07 21:07:24 +01:00
|
|
|
def iqhandler(self, iq):
|
|
|
|
"""
|
|
|
|
Handle the custom stanzas
|
|
|
|
This method should be very robust because we could receive anything
|
|
|
|
"""
|
|
|
|
if iq['type'] == 'error':
|
|
|
|
logging.error('iqhandler error' + iq['error']['condition'])
|
|
|
|
#self.disconnect()
|
|
|
|
elif iq['type'] == 'get':
|
|
|
|
"""
|
|
|
|
Request lists.
|
|
|
|
"""
|
2013-11-25 05:05:41 +01:00
|
|
|
# Send lists/register on leaderboard; depreciated once muc_online
|
|
|
|
# can send lists/register automatically on joining the room.
|
2017-04-29 01:22:41 +02:00
|
|
|
if 'boardlist' in iq.plugins:
|
2014-01-24 00:13:13 +01:00
|
|
|
command = iq['boardlist']['command']
|
2016-08-16 05:35:53 +02:00
|
|
|
try:
|
|
|
|
self.relayBoardListRequest(iq['from'])
|
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
|
|
|
logging.error("Failed to process leaderboardlist request from %s" % iq['from'].bare)
|
2014-09-20 17:35:26 +02:00
|
|
|
elif 'profile' in iq.plugins:
|
|
|
|
command = iq['profile']['command']
|
|
|
|
try:
|
2016-08-16 05:35:53 +02:00
|
|
|
self.relayProfileRequest(iq['from'], command)
|
2014-09-20 17:35:26 +02:00
|
|
|
except:
|
2016-08-21 22:35:13 +02:00
|
|
|
pass # TODO needed?
|
2013-12-26 20:45:20 +01:00
|
|
|
else:
|
|
|
|
logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare)
|
2013-11-07 21:07:24 +01:00
|
|
|
elif iq['type'] == 'result':
|
|
|
|
"""
|
|
|
|
Iq successfully received
|
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
if 'boardlist' in iq.plugins:
|
|
|
|
recipient = iq['boardlist']['recipient']
|
|
|
|
self.relayBoardList(iq['boardlist'], recipient)
|
|
|
|
elif 'profile' in iq.plugins:
|
|
|
|
recipient = iq['profile']['recipient']
|
|
|
|
player = iq['profile']['command']
|
|
|
|
self.relayProfile(iq['profile'], player, recipient)
|
|
|
|
else:
|
2016-08-21 22:35:13 +02:00
|
|
|
pass # TODO error/warn?
|
2013-11-07 21:07:24 +01:00
|
|
|
elif iq['type'] == 'set':
|
2013-12-26 20:45:20 +01:00
|
|
|
if 'gamelist' in iq.plugins:
|
2013-11-07 21:07:24 +01:00
|
|
|
"""
|
|
|
|
Register-update / unregister a game
|
|
|
|
"""
|
2013-12-15 19:33:01 +01:00
|
|
|
command = iq['gamelist']['command']
|
2013-11-07 21:07:24 +01:00
|
|
|
if command == 'register':
|
|
|
|
# Add game
|
|
|
|
try:
|
2013-12-15 19:33:01 +01:00
|
|
|
self.gameList.addGame(iq['from'], iq['gamelist']['game'])
|
2013-11-07 21:07:24 +01:00
|
|
|
self.sendGameList()
|
|
|
|
except:
|
2013-11-10 05:28:18 +01:00
|
|
|
traceback.print_exc()
|
2013-11-07 21:07:24 +01:00
|
|
|
logging.error("Failed to process game registration data")
|
|
|
|
elif command == 'unregister':
|
|
|
|
# Remove game
|
|
|
|
try:
|
|
|
|
self.gameList.removeGame(iq['from'])
|
|
|
|
self.sendGameList()
|
|
|
|
except:
|
2013-11-10 05:28:18 +01:00
|
|
|
traceback.print_exc()
|
2013-11-07 21:07:24 +01:00
|
|
|
logging.error("Failed to process game unregistration data")
|
|
|
|
|
|
|
|
elif command == 'changestate':
|
|
|
|
# Change game status (waiting/running)
|
|
|
|
try:
|
2013-12-15 19:33:01 +01:00
|
|
|
self.gameList.changeGameState(iq['from'], iq['gamelist']['game'])
|
2013-11-07 21:07:24 +01:00
|
|
|
self.sendGameList()
|
|
|
|
except:
|
2013-11-10 05:28:18 +01:00
|
|
|
traceback.print_exc()
|
2013-11-07 21:07:24 +01:00
|
|
|
logging.error("Failed to process changestate data")
|
|
|
|
else:
|
|
|
|
logging.error("Failed to process command '%s' received from %s" % command, iq['from'].bare)
|
2013-12-26 20:45:20 +01:00
|
|
|
elif 'gamereport' in iq.plugins:
|
2013-11-07 21:07:24 +01:00
|
|
|
"""
|
|
|
|
Client is reporting end of game statistics
|
|
|
|
"""
|
|
|
|
try:
|
2016-08-16 05:35:53 +02:00
|
|
|
self.relayGameReport(iq['gamereport'], iq['from'])
|
2013-11-07 21:07:24 +01:00
|
|
|
except:
|
2013-11-10 05:28:18 +01:00
|
|
|
traceback.print_exc()
|
2013-11-07 21:07:24 +01:00
|
|
|
logging.error("Failed to update game statistics for %s" % iq['from'].bare)
|
|
|
|
else:
|
|
|
|
logging.error("Failed to process stanza type '%s' received from %s" % iq['type'], iq['from'].bare)
|
|
|
|
|
|
|
|
def sendGameList(self, to = ""):
|
|
|
|
"""
|
|
|
|
Send a massive stanza with the whole game list.
|
|
|
|
If no target is passed the gamelist is broadcasted
|
|
|
|
to all clients.
|
|
|
|
"""
|
2013-12-25 16:13:58 +01:00
|
|
|
games = self.gameList.getAllGames()
|
2014-01-24 00:13:13 +01:00
|
|
|
if to == "":
|
2015-10-11 22:47:52 +02:00
|
|
|
for JID in list(self.nicks):
|
2013-12-25 16:13:58 +01:00
|
|
|
stz = GameListXmppPlugin()
|
|
|
|
|
2013-12-26 20:45:20 +01:00
|
|
|
## Pull games and add each to the stanza
|
2013-12-25 16:13:58 +01:00
|
|
|
for JIDs in games:
|
|
|
|
g = games[JIDs]
|
2014-09-14 22:59:35 +02:00
|
|
|
stz.addGame(g)
|
2013-12-25 16:13:58 +01:00
|
|
|
|
|
|
|
## Set additional IQ attributes
|
|
|
|
iq = self.Iq()
|
|
|
|
iq['type'] = 'result'
|
|
|
|
iq['to'] = JID
|
|
|
|
iq.setPayload(stz)
|
|
|
|
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
2013-12-26 20:45:20 +01:00
|
|
|
iq.send(block=False, now=True)
|
2013-12-25 16:13:58 +01:00
|
|
|
except:
|
|
|
|
logging.error("Failed to send game list")
|
|
|
|
else:
|
2013-11-07 21:07:24 +01:00
|
|
|
## Check recipient exists
|
2013-11-25 05:05:41 +01:00
|
|
|
if str(to) not in self.nicks:
|
2013-12-26 20:45:20 +01:00
|
|
|
logging.error("No player with the XmPP ID '%s' known to send gamelist to." % str(to))
|
2013-11-07 21:07:24 +01:00
|
|
|
return
|
|
|
|
stz = GameListXmppPlugin()
|
2013-12-26 20:52:07 +01:00
|
|
|
|
2013-11-07 21:07:24 +01:00
|
|
|
## Pull games and add each to the stanza
|
2013-12-26 20:45:20 +01:00
|
|
|
for JIDs in games:
|
|
|
|
g = games[JIDs]
|
2014-09-15 00:11:03 +02:00
|
|
|
stz.addGame(g)
|
2013-11-07 21:07:24 +01:00
|
|
|
|
|
|
|
## Set additional IQ attributes
|
|
|
|
iq = self.Iq()
|
|
|
|
iq['type'] = 'result'
|
|
|
|
iq['to'] = to
|
|
|
|
iq.setPayload(stz)
|
|
|
|
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
2013-12-26 20:45:20 +01:00
|
|
|
iq.send(block=False, now=True)
|
2013-11-07 21:07:24 +01:00
|
|
|
except:
|
|
|
|
logging.error("Failed to send game list")
|
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
def relayBoardListRequest(self, recipient):
|
2013-11-07 21:07:24 +01:00
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
Send a boardListRequest to EcheLOn.
|
2013-11-07 21:07:24 +01:00
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
to = self.ratingsBot
|
|
|
|
if to not in self.nicks:
|
|
|
|
self.warnRatingsBotOffline()
|
|
|
|
return
|
2013-12-25 16:13:58 +01:00
|
|
|
stz = BoardListXmppPlugin()
|
|
|
|
iq = self.Iq()
|
2016-08-16 05:35:53 +02:00
|
|
|
iq['type'] = 'get'
|
|
|
|
stz.addCommand('getleaderboard')
|
|
|
|
stz.addRecipient(recipient)
|
2013-12-25 16:13:58 +01:00
|
|
|
iq.setPayload(stz)
|
2016-08-16 05:35:53 +02:00
|
|
|
## Set additional IQ attributes
|
|
|
|
iq['to'] = to
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
|
|
|
iq.send(block=False, now=True)
|
|
|
|
except:
|
|
|
|
logging.error("Failed to send leaderboard list request")
|
|
|
|
|
|
|
|
def relayRatingListRequest(self, recipient):
|
|
|
|
"""
|
|
|
|
Send a ratingListRequest to EcheLOn.
|
|
|
|
"""
|
|
|
|
to = self.ratingsBot
|
|
|
|
if to not in self.nicks:
|
|
|
|
self.warnRatingsBotOffline()
|
|
|
|
return
|
|
|
|
stz = BoardListXmppPlugin()
|
|
|
|
iq = self.Iq()
|
|
|
|
iq['type'] = 'get'
|
|
|
|
stz.addCommand('getratinglist')
|
|
|
|
iq.setPayload(stz)
|
|
|
|
## Set additional IQ attributes
|
|
|
|
iq['to'] = to
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
|
|
|
iq.send(block=False, now=True)
|
|
|
|
except:
|
|
|
|
logging.error("Failed to send rating list request")
|
|
|
|
|
|
|
|
def relayProfileRequest(self, recipient, player):
|
|
|
|
"""
|
|
|
|
Send a profileRequest to EcheLOn.
|
|
|
|
"""
|
|
|
|
to = self.ratingsBot
|
|
|
|
if to not in self.nicks:
|
|
|
|
self.warnRatingsBotOffline()
|
|
|
|
return
|
|
|
|
stz = ProfileXmppPlugin()
|
|
|
|
iq = self.Iq()
|
|
|
|
iq['type'] = 'get'
|
|
|
|
stz.addCommand(player)
|
|
|
|
stz.addRecipient(recipient)
|
|
|
|
iq.setPayload(stz)
|
|
|
|
## Set additional IQ attributes
|
|
|
|
iq['to'] = to
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
|
|
|
iq.send(block=False, now=True)
|
|
|
|
except:
|
|
|
|
logging.error("Failed to send profile request")
|
2015-08-30 19:50:16 +02:00
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
def relayPlayerOnline(self, jid):
|
|
|
|
"""
|
|
|
|
Tells EcheLOn that someone comes online.
|
|
|
|
"""
|
|
|
|
## Check recipient exists
|
|
|
|
to = self.ratingsBot
|
|
|
|
if to not in self.nicks:
|
|
|
|
return
|
|
|
|
stz = PlayerXmppPlugin()
|
|
|
|
iq = self.Iq()
|
|
|
|
iq['type'] = 'set'
|
|
|
|
stz.addPlayerOnline(jid)
|
|
|
|
iq.setPayload(stz)
|
|
|
|
## Set additional IQ attributes
|
|
|
|
iq['to'] = to
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
|
|
|
iq.send(block=False, now=True)
|
|
|
|
except:
|
|
|
|
logging.error("Failed to send player muc online")
|
|
|
|
|
|
|
|
def relayGameReport(self, data, sender):
|
|
|
|
"""
|
|
|
|
Relay a game report to EcheLOn.
|
2014-01-24 00:13:13 +01:00
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
to = self.ratingsBot
|
|
|
|
if to not in self.nicks:
|
|
|
|
self.warnRatingsBotOffline()
|
|
|
|
return
|
|
|
|
stz = GameReportXmppPlugin()
|
|
|
|
stz.addGame(data)
|
|
|
|
stz.addSender(sender)
|
|
|
|
iq = self.Iq()
|
|
|
|
iq['type'] = 'set'
|
|
|
|
iq.setPayload(stz)
|
|
|
|
## Set additional IQ attributes
|
|
|
|
iq['to'] = to
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
|
|
|
iq.send(block=False, now=True)
|
|
|
|
except:
|
|
|
|
logging.error("Failed to send game report request")
|
|
|
|
|
|
|
|
def relayBoardList(self, boardList, to = ""):
|
|
|
|
"""
|
|
|
|
Send the whole leaderboard list.
|
|
|
|
If no target is passed the boardlist is broadcasted
|
2014-01-24 00:13:13 +01:00
|
|
|
to all clients.
|
|
|
|
"""
|
|
|
|
iq = self.Iq()
|
|
|
|
iq['type'] = 'result'
|
2016-08-16 05:35:53 +02:00
|
|
|
"""for i in board:
|
|
|
|
stz.addItem(board[i]['name'], board[i]['rating'])
|
|
|
|
stz.addCommand('boardlist')"""
|
|
|
|
iq.setPayload(boardList)
|
|
|
|
## Check recipient exists
|
|
|
|
if to == "":
|
|
|
|
# Rating List
|
2015-10-11 22:47:52 +02:00
|
|
|
for JID in list(self.nicks):
|
2014-01-24 00:13:13 +01:00
|
|
|
## Set additional IQ attributes
|
|
|
|
iq['to'] = JID
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
|
|
|
iq.send(block=False, now=True)
|
|
|
|
except:
|
|
|
|
logging.error("Failed to send rating list")
|
|
|
|
else:
|
2016-08-16 05:35:53 +02:00
|
|
|
# Leaderboard
|
2014-01-24 00:13:13 +01:00
|
|
|
if str(to) not in self.nicks:
|
2016-08-16 05:35:53 +02:00
|
|
|
logging.error("No player with the XmPP ID '%s' known to send boardlist to" % str(to))
|
2014-01-24 00:13:13 +01:00
|
|
|
return
|
|
|
|
## Set additional IQ attributes
|
|
|
|
iq['to'] = to
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
|
|
|
iq.send(block=False, now=True)
|
|
|
|
except:
|
2016-08-16 05:35:53 +02:00
|
|
|
logging.error("Failed to send leaderboard list")
|
2013-11-07 21:07:24 +01:00
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
def relayProfile(self, data, player, to):
|
2014-09-20 17:35:26 +02:00
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
Send the player profile to a specified target.
|
2014-09-20 17:35:26 +02:00
|
|
|
"""
|
|
|
|
if to == "":
|
2016-08-16 05:35:53 +02:00
|
|
|
logging.error("Failed to send profile, target unspecified")
|
2014-09-20 17:35:26 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
iq = self.Iq()
|
|
|
|
iq['type'] = 'result'
|
2016-08-16 05:35:53 +02:00
|
|
|
iq.setPayload(data)
|
2014-09-20 17:35:26 +02:00
|
|
|
## Check recipient exists
|
|
|
|
if str(to) not in self.nicks:
|
|
|
|
logging.error("No player with the XmPP ID '%s' known to send profile to" % str(to))
|
|
|
|
return
|
|
|
|
|
|
|
|
## Set additional IQ attributes
|
|
|
|
iq['to'] = to
|
|
|
|
|
|
|
|
## Try sending the stanza
|
|
|
|
try:
|
|
|
|
iq.send(block=False, now=True)
|
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
|
|
|
logging.error("Failed to send profile")
|
|
|
|
|
2016-08-16 05:35:53 +02:00
|
|
|
def warnRatingsBotOffline(self):
|
2014-09-20 17:35:26 +02:00
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
Warns that the ratings bot is offline.
|
2014-09-20 17:35:26 +02:00
|
|
|
"""
|
2016-08-16 05:35:53 +02:00
|
|
|
if not self.ratingsBotWarned:
|
|
|
|
logging.warn("Ratings bot '%s' is offline" % str(self.ratingsBot))
|
|
|
|
self.ratingsBotWarned = True
|
2014-09-20 17:35:26 +02:00
|
|
|
|
2013-11-07 21:07:24 +01:00
|
|
|
## Main Program ##
|
|
|
|
if __name__ == '__main__':
|
|
|
|
# Setup the command line arguments.
|
|
|
|
optp = OptionParser()
|
|
|
|
|
|
|
|
# Output verbosity options.
|
|
|
|
optp.add_option('-q', '--quiet', help='set logging to ERROR',
|
|
|
|
action='store_const', dest='loglevel',
|
|
|
|
const=logging.ERROR, default=logging.INFO)
|
|
|
|
optp.add_option('-d', '--debug', help='set logging to DEBUG',
|
|
|
|
action='store_const', dest='loglevel',
|
|
|
|
const=logging.DEBUG, default=logging.INFO)
|
|
|
|
optp.add_option('-v', '--verbose', help='set logging to COMM',
|
|
|
|
action='store_const', dest='loglevel',
|
|
|
|
const=5, default=logging.INFO)
|
|
|
|
|
|
|
|
# XpartaMuPP configuration options
|
|
|
|
optp.add_option('-m', '--domain', help='set xpartamupp domain',
|
|
|
|
action='store', dest='xdomain',
|
|
|
|
default="lobby.wildfiregames.com")
|
|
|
|
optp.add_option('-l', '--login', help='set xpartamupp login',
|
|
|
|
action='store', dest='xlogin',
|
|
|
|
default="xpartamupp")
|
|
|
|
optp.add_option('-p', '--password', help='set xpartamupp password',
|
|
|
|
action='store', dest='xpassword',
|
|
|
|
default="XXXXXX")
|
|
|
|
optp.add_option('-n', '--nickname', help='set xpartamupp nickname',
|
|
|
|
action='store', dest='xnickname',
|
|
|
|
default="WFGbot")
|
|
|
|
optp.add_option('-r', '--room', help='set muc room to join',
|
|
|
|
action='store', dest='xroom',
|
|
|
|
default="arena")
|
2016-08-16 05:35:53 +02:00
|
|
|
optp.add_option('-e', '--elo', help='set rating bot username',
|
|
|
|
action='store', dest='xratingsbot',
|
|
|
|
default="disabled")
|
2013-11-07 21:07:24 +01:00
|
|
|
|
|
|
|
opts, args = optp.parse_args()
|
|
|
|
|
|
|
|
# Setup logging.
|
|
|
|
logging.basicConfig(level=opts.loglevel,
|
2014-01-24 00:13:13 +01:00
|
|
|
format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
2013-11-07 21:07:24 +01:00
|
|
|
|
|
|
|
# XpartaMuPP
|
2016-08-16 05:35:53 +02:00
|
|
|
xmpp = XpartaMuPP(opts.xlogin+'@'+opts.xdomain+'/CC', opts.xpassword, opts.xroom+'@conference.'+opts.xdomain, opts.xnickname, opts.xratingsbot+'@'+opts.xdomain+'/CC')
|
2013-11-07 21:07:24 +01:00
|
|
|
xmpp.register_plugin('xep_0030') # Service Discovery
|
|
|
|
xmpp.register_plugin('xep_0004') # Data Forms
|
|
|
|
xmpp.register_plugin('xep_0045') # Multi-User Chat # used
|
|
|
|
xmpp.register_plugin('xep_0060') # PubSub
|
|
|
|
xmpp.register_plugin('xep_0199') # XMPP Ping
|
|
|
|
|
|
|
|
if xmpp.connect():
|
2013-12-15 19:33:01 +01:00
|
|
|
xmpp.process(threaded=False)
|
2013-11-07 21:07:24 +01:00
|
|
|
else:
|
|
|
|
logging.error("Unable to connect")
|