forked from 0ad/0ad
Lobby Bot Optimizations and Upgrade to recent SleekXMPP/ejabberd versions.
Based on patch by scythetwirler, fix and improvements by user1, Dunedan and myself. Differential Revision: https://code.wildfiregames.com/D206 This was SVN commit r21718.
This commit is contained in:
parent
d5f2ea9e78
commit
1d158a25ea
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Copyright (C) 2016 Wildfire Games.
|
||||
"""Copyright (C) 2018 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -74,7 +74,7 @@ class LeaderboardList():
|
||||
the Player model, or the one that already
|
||||
exists in the database.
|
||||
"""
|
||||
players = db.query(Player).filter_by(jid=str(JID))
|
||||
players = db.query(Player).filter(Player.jid.ilike(str(JID)))
|
||||
if not players.first():
|
||||
player = Player(jid=str(JID), rating=-1)
|
||||
db.add(player)
|
||||
@ -88,7 +88,7 @@ class LeaderboardList():
|
||||
Returns the player that was removed, or None
|
||||
if that player didn't exist.
|
||||
"""
|
||||
players = db.query(Player).filter_by(jid=JID)
|
||||
players = db.query(Player).filter(Player.jid.ilike(str(JID)))
|
||||
player = players.first()
|
||||
if not player:
|
||||
return None
|
||||
@ -253,6 +253,7 @@ class LeaderboardList():
|
||||
for rank, player in enumerate(players):
|
||||
board[player.jid] = {'name': '@'.join(player.jid.split('@')[:-1]), 'rating': str(player.rating)}
|
||||
return board
|
||||
|
||||
def getRatingList(self, nicks):
|
||||
"""
|
||||
Returns a rating list of players
|
||||
@ -368,6 +369,7 @@ class ReportManager():
|
||||
return len(rawGameReport[key].split(","))-1
|
||||
# Return -1 in case of failure.
|
||||
return -1
|
||||
|
||||
## Class for custom player stanza extension ##
|
||||
class PlayerXmppPlugin(ElementBase):
|
||||
name = 'query'
|
||||
@ -447,6 +449,10 @@ class EcheLOn(sleekxmpp.ClientXMPP):
|
||||
self.sjid = sjid
|
||||
self.room = room
|
||||
self.nick = nick
|
||||
self.ratingListCache = {}
|
||||
self.ratingCacheReload = True
|
||||
self.boardListCache = {}
|
||||
self.boardCacheReload = True
|
||||
|
||||
# Init leaderboard object
|
||||
self.leaderboard = LeaderboardList(room)
|
||||
@ -528,12 +534,11 @@ class EcheLOn(sleekxmpp.ClientXMPP):
|
||||
"""
|
||||
Request lists.
|
||||
"""
|
||||
if 'boardlist' in iq.plugins:
|
||||
if 'boardlist' in iq.loaded_plugins:
|
||||
command = iq['boardlist']['command']
|
||||
recipient = iq['boardlist']['recipient']
|
||||
if command == 'getleaderboard':
|
||||
try:
|
||||
self.leaderboard.getOrCreatePlayer(iq['from'])
|
||||
self.sendBoardList(iq['from'], recipient)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
@ -545,7 +550,7 @@ class EcheLOn(sleekxmpp.ClientXMPP):
|
||||
traceback.print_exc()
|
||||
else:
|
||||
logging.error("Failed to process boardlist request from %s" % iq['from'].bare)
|
||||
elif 'profile' in iq.plugins:
|
||||
elif 'profile' in iq.loaded_plugins:
|
||||
command = iq['profile']['command']
|
||||
recipient = iq['profile']['recipient']
|
||||
try:
|
||||
@ -563,20 +568,23 @@ class EcheLOn(sleekxmpp.ClientXMPP):
|
||||
"""
|
||||
pass
|
||||
elif iq['type'] == 'set':
|
||||
if 'gamereport' in iq.plugins:
|
||||
if 'gamereport' in iq.loaded_plugins:
|
||||
"""
|
||||
Client is reporting end of game statistics
|
||||
"""
|
||||
try:
|
||||
self.leaderboard.getOrCreatePlayer(iq['gamereport']['sender'])
|
||||
self.reportManager.addReport(iq['gamereport']['sender'], iq['gamereport']['game'])
|
||||
if self.leaderboard.getLastRatedMessage() != "":
|
||||
self.ratingCacheReload = True
|
||||
self.boardCacheReload = True
|
||||
self.send_message(mto=self.room, mbody=self.leaderboard.getLastRatedMessage(), mtype="groupchat",
|
||||
mnick=self.nick)
|
||||
self.sendRatingList(iq['from'])
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logging.error("Failed to update game statistics for %s" % iq['from'].bare)
|
||||
elif 'player' in iq.plugins:
|
||||
elif 'player' in iq.loaded_plugins:
|
||||
player = iq['player']['online']
|
||||
#try:
|
||||
self.leaderboard.getOrCreatePlayer(player)
|
||||
@ -591,13 +599,17 @@ class EcheLOn(sleekxmpp.ClientXMPP):
|
||||
If no target is passed the boardlist is broadcasted
|
||||
to all clients.
|
||||
"""
|
||||
## Pull leaderboard data and add it to the stanza
|
||||
board = self.leaderboard.getBoard()
|
||||
## See if we can squeak by with the cached version.
|
||||
# Leaderboard cache is reloaded upon a new rated game being rated.
|
||||
if self.boardCacheReload:
|
||||
self.boardListCache = self.leaderboard.getBoard()
|
||||
self.boardCacheReload = False
|
||||
|
||||
stz = BoardListXmppPlugin()
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'result'
|
||||
for i in board:
|
||||
stz.addItem(board[i]['name'], board[i]['rating'])
|
||||
for i in self.boardListCache:
|
||||
stz.addItem(self.boardListCache[i]['name'], self.boardListCache[i]['rating'])
|
||||
stz.addCommand('boardlist')
|
||||
stz.addRecipient(recipient)
|
||||
iq.setPayload(stz)
|
||||
@ -617,13 +629,24 @@ class EcheLOn(sleekxmpp.ClientXMPP):
|
||||
"""
|
||||
Send the rating list.
|
||||
"""
|
||||
## Pull rating list data and add it to the stanza
|
||||
ratinglist = self.leaderboard.getRatingList(self.nicks)
|
||||
## Attempt to use the cache.
|
||||
# Cache is invalidated when a new game is rated or a uncached player
|
||||
# comes online.
|
||||
if self.ratingCacheReload:
|
||||
self.ratingListCache = self.leaderboard.getRatingList(self.nicks)
|
||||
self.ratingCacheReload = False
|
||||
else:
|
||||
for JID in list(self.nicks):
|
||||
if JID not in self.ratingListCache:
|
||||
self.ratingListCache = self.leaderboard.getRatingList(self.nicks)
|
||||
self.ratingCacheReload = False
|
||||
break
|
||||
|
||||
stz = BoardListXmppPlugin()
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'result'
|
||||
for i in ratinglist:
|
||||
stz.addItem(ratinglist[i]['name'], ratinglist[i]['rating'])
|
||||
for i in self.ratingListCache:
|
||||
stz.addItem(self.ratingListCache[i]['name'], self.ratingListCache[i]['rating'])
|
||||
stz.addCommand('ratinglist')
|
||||
iq.setPayload(stz)
|
||||
## Check recipient exists
|
||||
@ -740,6 +763,11 @@ if __name__ == '__main__':
|
||||
action='store', dest='xroom',
|
||||
default="arena")
|
||||
|
||||
# ejabberd server options
|
||||
optp.add_option('-s', '--server', help='address of the ejabberd server',
|
||||
action='store', dest='xserver',
|
||||
default="localhost")
|
||||
|
||||
opts, args = optp.parse_args()
|
||||
|
||||
# Setup logging.
|
||||
@ -754,7 +782,7 @@ if __name__ == '__main__':
|
||||
xmpp.register_plugin('xep_0060') # PubSub
|
||||
xmpp.register_plugin('xep_0199') # XMPP Ping
|
||||
|
||||
if xmpp.connect():
|
||||
if xmpp.connect((opts.xserver, 5222)):
|
||||
xmpp.process(threaded=False)
|
||||
else:
|
||||
logging.error("Unable to connect")
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Copyright (C) 2016 Wildfire Games.
|
||||
"""Copyright (C) 2018 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -178,6 +178,7 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
|
||||
# Store mapping of nicks and XmppIDs, attached via presence stanza
|
||||
self.nicks = {}
|
||||
self.presences = {} # Obselete when XEP-0060 is implemented.
|
||||
|
||||
self.lastLeft = ""
|
||||
|
||||
@ -212,6 +213,7 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
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)
|
||||
self.add_event_handler("changed_status", self.presence_change)
|
||||
|
||||
def start(self, event):
|
||||
"""
|
||||
@ -233,6 +235,7 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
# 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']
|
||||
self.presences[str(presence['muc']['jid'])] = "available"
|
||||
# Check the jid isn't already in the lobby.
|
||||
# Send Gamelist to new player.
|
||||
self.sendGameList(presence['muc']['jid'])
|
||||
@ -254,6 +257,7 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
self.lastLeft = str(presence['muc']['jid'])
|
||||
if str(presence['muc']['jid']) in self.nicks:
|
||||
del self.nicks[str(presence['muc']['jid'])]
|
||||
del self.presences[str(presence['muc']['jid'])]
|
||||
if presence['muc']['nick'] == self.ratingsBot:
|
||||
self.ratingsBotWarned = False
|
||||
|
||||
@ -266,6 +270,21 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
mbody="I am the administrative bot in this lobby and cannot participate in any games.",
|
||||
mtype='groupchat')
|
||||
|
||||
def presence_change(self, presence):
|
||||
"""
|
||||
Processes presence change
|
||||
"""
|
||||
prefix = "%s/" % self.room
|
||||
nick = str(presence['from']).replace(prefix, "")
|
||||
for JID in self.nicks:
|
||||
if self.nicks[JID] == nick:
|
||||
if self.presences[JID] == 'dnd' and (str(presence['type']) == "available" or str(presence['type']) == "away"):
|
||||
self.sendGameList(JID)
|
||||
self.relayBoardListRequest(JID)
|
||||
self.presences[JID] = str(presence['type'])
|
||||
break
|
||||
|
||||
|
||||
def iqhandler(self, iq):
|
||||
"""
|
||||
Handle the custom stanzas
|
||||
@ -280,36 +299,36 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
"""
|
||||
# Send lists/register on leaderboard; depreciated once muc_online
|
||||
# can send lists/register automatically on joining the room.
|
||||
if 'boardlist' in iq.plugins:
|
||||
if 'boardlist' in iq.loaded_plugins:
|
||||
command = iq['boardlist']['command']
|
||||
try:
|
||||
self.relayBoardListRequest(iq['from'])
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logging.error("Failed to process leaderboardlist request from %s" % iq['from'].bare)
|
||||
elif 'profile' in iq.plugins:
|
||||
elif 'profile' in iq.loaded_plugins:
|
||||
command = iq['profile']['command']
|
||||
try:
|
||||
self.relayProfileRequest(iq['from'], command)
|
||||
except:
|
||||
pass # TODO needed?
|
||||
pass
|
||||
else:
|
||||
logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare)
|
||||
elif iq['type'] == 'result':
|
||||
"""
|
||||
Iq successfully received
|
||||
"""
|
||||
if 'boardlist' in iq.plugins:
|
||||
if 'boardlist' in iq.loaded_plugins:
|
||||
recipient = iq['boardlist']['recipient']
|
||||
self.relayBoardList(iq['boardlist'], recipient)
|
||||
elif 'profile' in iq.plugins:
|
||||
elif 'profile' in iq.loaded_plugins:
|
||||
recipient = iq['profile']['recipient']
|
||||
player = iq['profile']['command']
|
||||
self.relayProfile(iq['profile'], player, recipient)
|
||||
else:
|
||||
pass # TODO error/warn?
|
||||
pass
|
||||
elif iq['type'] == 'set':
|
||||
if 'gamelist' in iq.plugins:
|
||||
if 'gamelist' in iq.loaded_plugins:
|
||||
"""
|
||||
Register-update / unregister a game
|
||||
"""
|
||||
@ -317,8 +336,9 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
if command == 'register':
|
||||
# Add game
|
||||
try:
|
||||
self.gameList.addGame(iq['from'], iq['gamelist']['game'])
|
||||
self.sendGameList()
|
||||
if iq['from'] in self.nicks:
|
||||
self.gameList.addGame(iq['from'], iq['gamelist']['game'])
|
||||
self.sendGameList()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logging.error("Failed to process game registration data")
|
||||
@ -338,10 +358,16 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
self.sendGameList()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logging.error("Failed to process changestate data")
|
||||
logging.error("Failed to process changestate data. Trying to add game")
|
||||
try:
|
||||
if iq['from'] in self.nicks:
|
||||
self.gameList.addGame(iq['from'], iq['gamelist']['game'])
|
||||
self.sendGameList()
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
logging.error("Failed to process command '%s' received from %s" % command, iq['from'].bare)
|
||||
elif 'gamereport' in iq.plugins:
|
||||
elif 'gamereport' in iq.loaded_plugins:
|
||||
"""
|
||||
Client is reporting end of game statistics
|
||||
"""
|
||||
@ -360,20 +386,23 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
to all clients.
|
||||
"""
|
||||
games = self.gameList.getAllGames()
|
||||
|
||||
stz = GameListXmppPlugin()
|
||||
|
||||
## Pull games and add each to the stanza
|
||||
for JIDs in games:
|
||||
g = games[JIDs]
|
||||
stz.addGame(g)
|
||||
|
||||
## Set additional IQ attributes
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'result'
|
||||
iq.setPayload(stz)
|
||||
if to == "":
|
||||
for JID in list(self.nicks):
|
||||
stz = GameListXmppPlugin()
|
||||
|
||||
## Pull games and add each to the stanza
|
||||
for JIDs in games:
|
||||
g = games[JIDs]
|
||||
stz.addGame(g)
|
||||
|
||||
## Set additional IQ attributes
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'result'
|
||||
for JID in list(self.presences):
|
||||
if self.presences[JID] != "available" and self.presences[JID] != "away":
|
||||
continue
|
||||
iq['to'] = JID
|
||||
iq.setPayload(stz)
|
||||
|
||||
## Try sending the stanza
|
||||
try:
|
||||
@ -385,18 +414,7 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
if str(to) not in self.nicks:
|
||||
logging.error("No player with the XmPP ID '%s' known to send gamelist to." % str(to))
|
||||
return
|
||||
stz = GameListXmppPlugin()
|
||||
|
||||
## Pull games and add each to the stanza
|
||||
for JIDs in games:
|
||||
g = games[JIDs]
|
||||
stz.addGame(g)
|
||||
|
||||
## Set additional IQ attributes
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'result'
|
||||
iq['to'] = to
|
||||
iq.setPayload(stz)
|
||||
|
||||
## Try sending the stanza
|
||||
try:
|
||||
@ -520,14 +538,13 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
"""
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'result'
|
||||
"""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
|
||||
for JID in list(self.nicks):
|
||||
for JID in list(self.presences):
|
||||
if self.presences[JID] != "available" and self.presences[JID] != "away":
|
||||
continue
|
||||
## Set additional IQ attributes
|
||||
iq['to'] = JID
|
||||
## Try sending the stanza
|
||||
@ -536,7 +553,7 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
except:
|
||||
logging.error("Failed to send rating list")
|
||||
else:
|
||||
# Leaderboard
|
||||
# Leaderboard or targeted rating list
|
||||
if str(to) not in self.nicks:
|
||||
logging.error("No player with the XmPP ID '%s' known to send boardlist to" % str(to))
|
||||
return
|
||||
@ -618,6 +635,11 @@ if __name__ == '__main__':
|
||||
action='store', dest='xratingsbot',
|
||||
default="disabled")
|
||||
|
||||
# ejabberd server options
|
||||
optp.add_option('-s', '--server', help='address of the ejabberd server',
|
||||
action='store', dest='xserver',
|
||||
default="localhost")
|
||||
|
||||
opts, args = optp.parse_args()
|
||||
|
||||
# Setup logging.
|
||||
@ -632,7 +654,7 @@ if __name__ == '__main__':
|
||||
xmpp.register_plugin('xep_0060') # PubSub
|
||||
xmpp.register_plugin('xep_0199') # XMPP Ping
|
||||
|
||||
if xmpp.connect():
|
||||
if xmpp.connect((opts.xserver, 5222)):
|
||||
xmpp.process(threaded=False)
|
||||
else:
|
||||
logging.error("Unable to connect")
|
||||
|
Loading…
Reference in New Issue
Block a user