Adds profiles to the multiplayer lobby. Fixes #2504.
This was SVN commit r15768.
This commit is contained in:
parent
d62e3729d5
commit
6b2677a3fd
@ -190,6 +190,126 @@ function updatePlayerList()
|
||||
return [playerList, presenceList, nickList, ratingList];
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the profile of the selected player.
|
||||
* Displays N/A for all stats until updateProfile is called when the stats
|
||||
* are actually received from the bot.
|
||||
*
|
||||
* @param caller From which screen is the user requesting data from?
|
||||
*/
|
||||
function displayProfile(caller)
|
||||
{
|
||||
var playerList, rating;
|
||||
if (caller == "leaderboard")
|
||||
playerList = Engine.GetGUIObjectByName("leaderboardBox");
|
||||
else if (caller == "lobbylist")
|
||||
playerList = Engine.GetGUIObjectByName("playersBox");
|
||||
else if (caller == "fetch")
|
||||
{
|
||||
Engine.SendGetProfile(Engine.GetGUIObjectByName("fetchInput").caption);
|
||||
return;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
if (!playerList.list[playerList.selected])
|
||||
{
|
||||
Engine.GetGUIObjectByName("profileArea").hidden = true;
|
||||
return;
|
||||
}
|
||||
Engine.GetGUIObjectByName("profileArea").hidden = false;
|
||||
|
||||
Engine.SendGetProfile(playerList.list[playerList.selected]);
|
||||
|
||||
var user = playerList.list_name[playerList.selected];
|
||||
var role = Engine.LobbyGetPlayerRole(playerList.list[playerList.selected]);
|
||||
var userList = Engine.GetGUIObjectByName("playersBox");
|
||||
if (role && caller == "lobbylist")
|
||||
{
|
||||
// Make the role uppercase.
|
||||
role = role.charAt(0).toUpperCase() + role.slice(1);
|
||||
if (role == "Moderator")
|
||||
role = '[color="0 125 0"]' + translate(role) + '[/color]';
|
||||
}
|
||||
else
|
||||
role = "";
|
||||
|
||||
Engine.GetGUIObjectByName("usernameText").caption = user;
|
||||
Engine.GetGUIObjectByName("roleText").caption = translate(role);
|
||||
Engine.GetGUIObjectByName("rankText").caption = translate("N/A");
|
||||
Engine.GetGUIObjectByName("highestRatingText").caption = translate("N/A");
|
||||
Engine.GetGUIObjectByName("totalGamesText").caption = translate("N/A");
|
||||
Engine.GetGUIObjectByName("winsText").caption = translate("N/A");
|
||||
Engine.GetGUIObjectByName("lossesText").caption = translate("N/A");
|
||||
Engine.GetGUIObjectByName("ratioText").caption = translate("N/A");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the profile of the selected player with data from the bot.
|
||||
*
|
||||
*/
|
||||
function updateProfile()
|
||||
{
|
||||
var playerList, user;
|
||||
var attributes = Engine.GetProfile();
|
||||
|
||||
if (!Engine.GetGUIObjectByName("profileFetch").hidden)
|
||||
{
|
||||
user = attributes[0].player;
|
||||
if (attributes[0].rating == "-2") // Profile not found code
|
||||
{
|
||||
Engine.GetGUIObjectByName("profileWindowArea").hidden = true;
|
||||
Engine.GetGUIObjectByName("profileErrorText").hidden = false;
|
||||
return;
|
||||
}
|
||||
Engine.GetGUIObjectByName("profileWindowArea").hidden = false;
|
||||
Engine.GetGUIObjectByName("profileErrorText").hidden = true;
|
||||
|
||||
if (attributes[0].rating != "")
|
||||
user = sprintf(translate("%(nick)s (%(rating)s)"), { nick: user, rating: attributes[0].rating });
|
||||
|
||||
Engine.GetGUIObjectByName("profileUsernameText").caption = user;
|
||||
Engine.GetGUIObjectByName("profileRankText").caption = attributes[0].rank;
|
||||
Engine.GetGUIObjectByName("profileHighestRatingText").caption = attributes[0].highestRating;
|
||||
Engine.GetGUIObjectByName("profileTotalGamesText").caption = attributes[0].totalGamesPlayed;
|
||||
Engine.GetGUIObjectByName("profileWinsText").caption = attributes[0].wins;
|
||||
Engine.GetGUIObjectByName("profileLossesText").caption = attributes[0].losses;
|
||||
|
||||
var winRate = (attributes[0].wins / attributes[0].totalGamesPlayed * 100).toFixed(2);
|
||||
if (attributes[0].totalGamesPlayed != 0)
|
||||
Engine.GetGUIObjectByName("profileRatioText").caption = sprintf(translate("%(percentage)s%%"), { percentage: winRate });
|
||||
else
|
||||
Engine.GetGUIObjectByName("profileRatioText").caption = translate("-");
|
||||
return;
|
||||
}
|
||||
else if (!Engine.GetGUIObjectByName("leaderboard").hidden)
|
||||
playerList = Engine.GetGUIObjectByName("leaderboardBox");
|
||||
else
|
||||
playerList = Engine.GetGUIObjectByName("playersBox");
|
||||
|
||||
if (attributes[0].rating == "-2")
|
||||
return;
|
||||
// Make sure the stats we have received coincide with the selected player.
|
||||
if (attributes[0].player != playerList.list[playerList.selected])
|
||||
return;
|
||||
user = playerList.list_name[playerList.selected];
|
||||
if (attributes[0].rating != "")
|
||||
user = sprintf(translate("%(nick)s (%(rating)s)"), { nick: user, rating: attributes[0].rating });
|
||||
|
||||
Engine.GetGUIObjectByName("usernameText").caption = user;
|
||||
Engine.GetGUIObjectByName("rankText").caption = attributes[0].rank;
|
||||
Engine.GetGUIObjectByName("highestRatingText").caption = attributes[0].highestRating;
|
||||
Engine.GetGUIObjectByName("totalGamesText").caption = attributes[0].totalGamesPlayed;
|
||||
Engine.GetGUIObjectByName("winsText").caption = attributes[0].wins;
|
||||
Engine.GetGUIObjectByName("lossesText").caption = attributes[0].losses;
|
||||
|
||||
var winRate = (attributes[0].wins / attributes[0].totalGamesPlayed * 100).toFixed(2);
|
||||
if (attributes[0].totalGamesPlayed != 0)
|
||||
Engine.GetGUIObjectByName("ratioText").caption = sprintf(translate("%(percentage)s%%"), { percentage: winRate });
|
||||
else
|
||||
Engine.GetGUIObjectByName("ratioText").caption = translate("-");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the leaderboard from data cached in C++.
|
||||
*/
|
||||
@ -592,8 +712,11 @@ function onTick()
|
||||
case "ratinglist updated":
|
||||
updatePlayerList();
|
||||
break;
|
||||
case "profile updated":
|
||||
updateProfile();
|
||||
break;
|
||||
}
|
||||
break
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -19,7 +19,7 @@
|
||||
</action>
|
||||
|
||||
<!-- Left panel: Player list. -->
|
||||
<object name="leftPanel" size="20 30 20% 100%-50">
|
||||
<object name="leftPanel" size="20 30 20% 100%-280">
|
||||
<object name="playersBox" style="ModernList" type="olist" size="0 0 100% 100%" font="sans-bold-stroke-13">
|
||||
<def id="status" width="26%">
|
||||
<translatableAttribute id="heading">Status</translatableAttribute>
|
||||
@ -30,13 +30,60 @@
|
||||
<def id="rating" width="24%">
|
||||
<translatableAttribute id="heading">Rating</translatableAttribute>
|
||||
</def>
|
||||
<action on="SelectionChange">
|
||||
displayProfile("lobbylist");
|
||||
</action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<object name="leftButtonPanel" size="20 100%-45 20% 100%-20">
|
||||
<object type="button" style="ModernButtonRed" size="0 0 100% 100%">
|
||||
<object name="profilePanel" size="20 100%-275 20% 100%-80">
|
||||
<object name="profileBox" type="image" sprite="ModernDarkBoxGold" size="0 0 100% 100%">
|
||||
<object name="profileArea" size="0 0 100% 100%" hidden="true">
|
||||
<object name="usernameText" size="0 0 100% 45" type="text" style="ModernLabelText" text_align="center" font="sans-bold-16" />
|
||||
<object name="roleText" size="0 45 100% 70" type="text" style="ModernLabelText" text_align="center" font="sans-bold-stroke-12" />
|
||||
<object size="0 70 40%+40 90" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Current Rank:</translatableAttribute>
|
||||
</object>
|
||||
<object name="rankText" size="40%+45 70 100% 90" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 90 40%+40 110" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Highest Rating:</translatableAttribute>
|
||||
</object>
|
||||
<object name="highestRatingText" size="40%+45 90 100% 110" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 110 40%+40 130" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Total Games:</translatableAttribute>
|
||||
</object>
|
||||
<object name="totalGamesText" size="40%+45 110 100% 130" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 130 40%+40 150" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Wins:</translatableAttribute>
|
||||
</object>
|
||||
<object name="winsText" size="40%+45 130 100% 150" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 150 40%+40 170" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Losses:</translatableAttribute>
|
||||
</object>
|
||||
<object name="lossesText" size="40%+45 150 100% 170" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 170 40%+40 190" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Win Rate:</translatableAttribute>
|
||||
</object>
|
||||
<object name="ratioText" size="40%+45 170 100% 190" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<object name="leftButtonPanel" size="20 100%-75 20% 100%-20">
|
||||
<object type="button" style="ModernButtonRed" size="0 0 100% 25">
|
||||
<translatableAttribute id="caption">Leaderboard</translatableAttribute>
|
||||
<action on="Press">Engine.GetGUIObjectByName("leaderboard").hidden = false;Engine.GetGUIObjectByName("leaderboardFade").hidden = false;</action>
|
||||
<action on="Press">
|
||||
Engine.GetGUIObjectByName("leaderboard").hidden = false;
|
||||
Engine.GetGUIObjectByName("fade").hidden = false;
|
||||
displayProfile("leaderboard");
|
||||
</action>
|
||||
</object>
|
||||
<object type="button" style="ModernButtonRed" size="0 30 100% 100%">
|
||||
<translatableAttribute id="caption">User Profile Lookup</translatableAttribute>
|
||||
<action on="Press">
|
||||
Engine.GetGUIObjectByName("profileFetch").hidden = false;
|
||||
Engine.GetGUIObjectByName("fade").hidden = false;
|
||||
</action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
@ -191,7 +238,7 @@
|
||||
|
||||
<!-- START Window for leaderboard stats -->
|
||||
<!-- Add a translucent black background to fade out the menu page -->
|
||||
<object hidden="true" name="leaderboardFade" type="image" z="100" sprite="ModernFade"/>
|
||||
<object hidden="true" name="fade" type="image" z="100" sprite="ModernFade"/>
|
||||
<object hidden="true" name="leaderboard" type="image" style="ModernDialog" size="50%-224 50%-160 50%+224 50%+160" z="101">
|
||||
<object style="ModernLabelText" type="text" size="50%-128 -18 50%+128 14">
|
||||
<translatableAttribute id="caption">Leaderboard</translatableAttribute>
|
||||
@ -209,10 +256,17 @@
|
||||
<def id="rating" color="255 255 255" width="30%">
|
||||
<translatableAttribute id="heading">Rating</translatableAttribute>
|
||||
</def>
|
||||
<action on="SelectionChange">
|
||||
displayProfile("leaderboard");
|
||||
</action>
|
||||
</object>
|
||||
<object type="button" style="ModernButtonRed" size="50%-133 100%-45 50%-5 100%-17">
|
||||
<translatableAttribute id="caption">Back</translatableAttribute>
|
||||
<action on="Press">Engine.GetGUIObjectByName("leaderboard").hidden = true;Engine.GetGUIObjectByName("leaderboardFade").hidden = true;</action>
|
||||
<action on="Press">
|
||||
Engine.GetGUIObjectByName("leaderboard").hidden = true;
|
||||
Engine.GetGUIObjectByName("fade").hidden = true;
|
||||
displayProfile("lobbylist");
|
||||
</action>
|
||||
</object>
|
||||
<object type="button" style="ModernButtonRed" size="50%+5 100%-45 50%+133 100%-17">
|
||||
<translatableAttribute id="caption">Update</translatableAttribute>
|
||||
@ -220,5 +274,61 @@
|
||||
</object>
|
||||
</object>
|
||||
<!-- END Window for leaderboard stats -->
|
||||
<object hidden="true" name="profileFetch" type="image" style="ModernDialog" size="50%-224 50%-160 50%+224 50%+160" z="102">
|
||||
<object style="ModernLabelText" type="text" size="50%-128 -18 50%+128 14">
|
||||
<translatableAttribute id="caption">User Profile Lookup</translatableAttribute>
|
||||
</object>
|
||||
<object type="text" size="15 25 40% 50" text_align="right" textcolor="white">
|
||||
<translatableAttribute id="caption">Enter username:</translatableAttribute>
|
||||
</object>
|
||||
<object name="fetchInput" size="40%+10 25 100%-25 50" type="input" style="ModernInput" font="sans-13">
|
||||
<action on="Press">displayProfile("fetch");</action>
|
||||
</object>
|
||||
<object type="button" style="ModernButtonRed" size="50%-64 60 50%+64 85">
|
||||
<translatableAttribute id="caption">View Profile</translatableAttribute>
|
||||
<action on="Press">displayProfile("fetch");</action>
|
||||
</object>
|
||||
<object name="profileWindowPanel" size="25 95 100%-25 100%-60">
|
||||
<object name="profileWindowBox" type="image" sprite="ModernDarkBoxGold" size="0 0 100% 100%">
|
||||
<object name="profileWindowArea" size="0 0 100% 100%" hidden="false">
|
||||
<object name="profileUsernameText" size="0 0 100% 25" type="text" style="ModernLabelText" text_align="center" font="sans-bold-16" />
|
||||
<object size="0 30 40%+40 50" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Current Rank:</translatableAttribute>
|
||||
</object>
|
||||
<object name="profileRankText" size="40%+45 30 100% 50" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 50 40%+40 70" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Highest Rating:</translatableAttribute>
|
||||
</object>
|
||||
<object name="profileHighestRatingText" size="40%+45 50 100% 70" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 70 40%+40 90" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Total Games:</translatableAttribute>
|
||||
</object>
|
||||
<object name="profileTotalGamesText" size="40%+45 70 100% 90" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 90 40%+40 110" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Wins:</translatableAttribute>
|
||||
</object>
|
||||
<object name="profileWinsText" size="40%+45 90 100% 110" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 110 40%+40 130" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Losses:</translatableAttribute>
|
||||
</object>
|
||||
<object name="profileLossesText" size="40%+45 110 100% 130" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
<object size="0 130 40%+40 150" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
|
||||
<translatableAttribute id="caption">Win Rate:</translatableAttribute>
|
||||
</object>
|
||||
<object name="profileRatioText" size="40%+45 130 100% 150" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
|
||||
</object>
|
||||
<object name="profileErrorText" size="25% 25% 75% 75%" type="text" style="ModernLabelText" text_align="center" font="sans-bold-stroke-13" hidden="true">
|
||||
<translatableAttribute id="caption">Player not found.</translatableAttribute>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object type="button" style="ModernButtonRed" size="50%-64 100%-50 50%+64 100%-25">
|
||||
<translatableAttribute id="caption">Back</translatableAttribute>
|
||||
<action on="Press">
|
||||
Engine.GetGUIObjectByName("profileFetch").hidden = true;
|
||||
Engine.GetGUIObjectByName("fade").hidden = true;
|
||||
</action>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</objects>
|
||||
|
@ -1027,6 +1027,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetGameList>("SendGetGameList");
|
||||
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetBoardList>("SendGetBoardList");
|
||||
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetRatingList>("SendGetRatingList");
|
||||
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::SendGetProfile>("SendGetProfile");
|
||||
scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendRegisterGame>("SendRegisterGame");
|
||||
scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendGameReport>("SendGameReport");
|
||||
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendUnregisterGame>("SendUnregisterGame");
|
||||
@ -1034,6 +1035,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetPlayerList>("GetPlayerList");
|
||||
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetGameList>("GetGameList");
|
||||
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetBoardList>("GetBoardList");
|
||||
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetProfile>("GetProfile");
|
||||
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::LobbyGuiPollMessage>("LobbyGuiPollMessage");
|
||||
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySendMessage>("LobbySendMessage");
|
||||
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySetPlayerPresence>("LobbySetPlayerPresence");
|
||||
|
@ -34,6 +34,7 @@ public:
|
||||
virtual void SendIqGetGameList() = 0;
|
||||
virtual void SendIqGetBoardList() = 0;
|
||||
virtual void SendIqGetRatingList() = 0;
|
||||
virtual void SendIqGetProfile(const std::string& player) = 0;
|
||||
virtual void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data) = 0;
|
||||
virtual void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data) = 0;
|
||||
virtual void SendIqUnregisterGame() = 0;
|
||||
@ -50,6 +51,7 @@ public:
|
||||
virtual CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface) = 0;
|
||||
virtual CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface) = 0;
|
||||
virtual CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface) = 0;
|
||||
virtual CScriptValRooted GUIGetProfile(ScriptInterface& scriptInterface) = 0;
|
||||
|
||||
virtual CScriptValRooted GuiPollMessage(ScriptInterface& scriptInterface) = 0;
|
||||
virtual void SendMUCMessage(const std::string& message) = 0;
|
||||
|
@ -185,3 +185,67 @@ GameListQuery::~GameListQuery()
|
||||
glooxwrapper::Tag::free(*it);
|
||||
m_GameList.clear();
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* ProfileQuery, a custom IQ Stanza useful for fetching
|
||||
* user profiles
|
||||
* Example stanza:
|
||||
* <profile player="foobar" highestRating="1500" rank="1895" totalGamesPlayed="50"
|
||||
* wins="25" losses="25" /><command>foobar</command>
|
||||
*/
|
||||
ProfileQuery::ProfileQuery(const glooxwrapper::Tag* tag):StanzaExtension(ExtProfileQuery)
|
||||
{
|
||||
if (!tag || tag->name() != "query" || tag->xmlns() != XMLNS_PROFILE)
|
||||
return;
|
||||
|
||||
const glooxwrapper::Tag* c = tag->findTag_clone("query/command");
|
||||
if (c)
|
||||
m_Command = c->cdata();
|
||||
glooxwrapper::Tag::free(c);
|
||||
|
||||
const glooxwrapper::ConstTagList profileTags = tag->findTagList_clone("query/profile");
|
||||
glooxwrapper::ConstTagList::const_iterator it = profileTags.begin();
|
||||
for (; it != profileTags.end(); ++it)
|
||||
m_StanzaProfile.push_back(*it);
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by gloox, used to find the Profile element in a received IQ.
|
||||
*/
|
||||
const glooxwrapper::string& ProfileQuery::filterString() const
|
||||
{
|
||||
static const glooxwrapper::string filter = "/iq/query[@xmlns='" XMLNS_PROFILE "']";
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by gloox, used to serialize the Profile request into XML for sending.
|
||||
*/
|
||||
glooxwrapper::Tag* ProfileQuery::tag() const
|
||||
{
|
||||
glooxwrapper::Tag* t = glooxwrapper::Tag::allocate("query");
|
||||
t->setXmlns(XMLNS_PROFILE);
|
||||
|
||||
if (!m_Command.empty())
|
||||
t->addChild(glooxwrapper::Tag::allocate("command", m_Command));
|
||||
|
||||
std::vector<const glooxwrapper::Tag*>::const_iterator it = m_StanzaProfile.begin();
|
||||
for (; it != m_StanzaProfile.end(); ++it)
|
||||
t->addChild((*it)->clone());
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
glooxwrapper::StanzaExtension* ProfileQuery::clone() const
|
||||
{
|
||||
ProfileQuery* q = new ProfileQuery();
|
||||
return q;
|
||||
}
|
||||
|
||||
ProfileQuery::~ProfileQuery()
|
||||
{
|
||||
std::vector<const glooxwrapper::Tag*>::const_iterator it = m_StanzaProfile.begin();
|
||||
for (; it != m_StanzaProfile.end(); ++it)
|
||||
glooxwrapper::Tag::free(*it);
|
||||
m_StanzaProfile.clear();
|
||||
}
|
||||
|
@ -31,6 +31,10 @@
|
||||
#define ExtGameReport 1405
|
||||
#define XMLNS_GAMEREPORT "jabber:iq:gamereport"
|
||||
|
||||
/// Global Profile Extension
|
||||
#define ExtProfileQuery 1406
|
||||
#define XMLNS_PROFILE "jabber:iq:profile"
|
||||
|
||||
class GameReport : public glooxwrapper::StanzaExtension
|
||||
{
|
||||
public:
|
||||
@ -87,4 +91,24 @@ public:
|
||||
glooxwrapper::string m_Command;
|
||||
std::vector<const glooxwrapper::Tag*> m_StanzaBoardList;
|
||||
};
|
||||
|
||||
class ProfileQuery : public glooxwrapper::StanzaExtension
|
||||
{
|
||||
public:
|
||||
ProfileQuery(const glooxwrapper::Tag* tag = 0);
|
||||
|
||||
// Following four methods are all required by gloox
|
||||
virtual StanzaExtension* newInstance(const glooxwrapper::Tag* tag) const
|
||||
{
|
||||
return new ProfileQuery(tag);
|
||||
}
|
||||
virtual const glooxwrapper::string& filterString() const;
|
||||
virtual glooxwrapper::Tag* tag() const;
|
||||
virtual glooxwrapper::StanzaExtension* clone() const;
|
||||
|
||||
~ProfileQuery();
|
||||
|
||||
glooxwrapper::string m_Command;
|
||||
std::vector<const glooxwrapper::Tag*> m_StanzaProfile;
|
||||
};
|
||||
#endif
|
||||
|
@ -102,19 +102,22 @@ XmppClient::XmppClient(const std::string& sUsername, const std::string& sPasswor
|
||||
const int mechs = gloox::SaslMechAll ^ gloox::SaslMechPlain;
|
||||
m_client->setSASLMechanisms(mechs);
|
||||
|
||||
m_client->registerConnectionListener( this );
|
||||
m_client->registerConnectionListener(this);
|
||||
m_client->setPresence(gloox::Presence::Available, -1);
|
||||
m_client->disco()->setVersion( "Pyrogenesis", "0.0.17" );
|
||||
m_client->disco()->setIdentity( "client", "bot" );
|
||||
m_client->disco()->setVersion("Pyrogenesis", "0.0.17");
|
||||
m_client->disco()->setIdentity("client", "bot");
|
||||
m_client->setCompression(false);
|
||||
|
||||
m_client->registerStanzaExtension( new GameListQuery() );
|
||||
m_client->registerIqHandler( this, ExtGameListQuery);
|
||||
m_client->registerStanzaExtension(new GameListQuery());
|
||||
m_client->registerIqHandler(this, ExtGameListQuery);
|
||||
|
||||
m_client->registerStanzaExtension( new BoardListQuery() );
|
||||
m_client->registerIqHandler( this, ExtBoardListQuery);
|
||||
m_client->registerStanzaExtension(new BoardListQuery());
|
||||
m_client->registerIqHandler(this, ExtBoardListQuery);
|
||||
|
||||
m_client->registerMessageHandler( this );
|
||||
m_client->registerStanzaExtension(new ProfileQuery());
|
||||
m_client->registerIqHandler(this, ExtProfileQuery);
|
||||
|
||||
m_client->registerMessageHandler(this);
|
||||
|
||||
// Uncomment to see the raw stanzas
|
||||
//m_client->getWrapped()->logInstance().registerLogHandler( gloox::LogLevelDebug, gloox::LogAreaAll, this );
|
||||
@ -152,6 +155,8 @@ XmppClient::~XmppClient()
|
||||
glooxwrapper::Tag::free(*it);
|
||||
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it)
|
||||
glooxwrapper::Tag::free(*it);
|
||||
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it)
|
||||
glooxwrapper::Tag::free(*it);
|
||||
}
|
||||
|
||||
/// Network
|
||||
@ -212,9 +217,12 @@ void XmppClient::onDisconnect(gloox::ConnectionError error)
|
||||
glooxwrapper::Tag::free(*it);
|
||||
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it)
|
||||
glooxwrapper::Tag::free(*it);
|
||||
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it)
|
||||
glooxwrapper::Tag::free(*it);
|
||||
m_BoardList.clear();
|
||||
m_GameList.clear();
|
||||
m_PlayerMap.clear();
|
||||
m_Profile.clear();
|
||||
|
||||
if(error == gloox::ConnAuthenticationFailed)
|
||||
CreateSimpleMessage("system", "authentication failed", "error");
|
||||
@ -283,6 +291,22 @@ void XmppClient::SendIqGetBoardList()
|
||||
m_client->send(iq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the profile data from the server.
|
||||
*/
|
||||
void XmppClient::SendIqGetProfile(const std::string& player)
|
||||
{
|
||||
glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
|
||||
|
||||
// Send IQ
|
||||
ProfileQuery* b = new ProfileQuery();
|
||||
b->m_Command = player;
|
||||
glooxwrapper::IQ iq(gloox::IQ::Get, xpartamuppJid);
|
||||
iq.addExtension(b);
|
||||
DbgXMPP("SendIqGetProfile [" << tag_xml(iq) << "]");
|
||||
m_client->send(iq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the rating data from the server.
|
||||
*/
|
||||
@ -518,8 +542,7 @@ CScriptValRooted XmppClient::GUIGetGameList(ScriptInterface& scriptInterface)
|
||||
scriptInterface.Eval("({})", &game);
|
||||
|
||||
const char* stats[] = { "name", "ip", "state", "nbp", "tnbp", "players", "mapName", "niceMapName", "mapSize", "mapType", "victoryCondition" };
|
||||
short stats_length = 11;
|
||||
for (short i = 0; i < stats_length; i++)
|
||||
for (size_t i = 0; i < ARRAY_SIZE(stats); ++i)
|
||||
scriptInterface.SetProperty(game, stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string()));
|
||||
|
||||
scriptInterface.CallFunctionVoid(gameList, "push", game);
|
||||
@ -546,8 +569,7 @@ CScriptValRooted XmppClient::GUIGetBoardList(ScriptInterface& scriptInterface)
|
||||
scriptInterface.Eval("({})", &board);
|
||||
|
||||
const char* attributes[] = { "name", "rank", "rating" };
|
||||
short attributes_length = 3;
|
||||
for (short i = 0; i < attributes_length; i++)
|
||||
for (size_t i = 0; i < ARRAY_SIZE(attributes); ++i)
|
||||
scriptInterface.SetProperty(board, attributes[i], wstring_from_utf8((*it)->findAttribute(attributes[i]).to_string()));
|
||||
|
||||
scriptInterface.CallFunctionVoid(boardList, "push", board);
|
||||
@ -556,6 +578,33 @@ CScriptValRooted XmppClient::GUIGetBoardList(ScriptInterface& scriptInterface)
|
||||
return CScriptValRooted(cx, boardList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle requests from the GUI for profile data.
|
||||
*
|
||||
* @return A JS array containing the specific user's profile data
|
||||
*/
|
||||
CScriptValRooted XmppClient::GUIGetProfile(ScriptInterface& scriptInterface)
|
||||
{
|
||||
JSContext* cx = scriptInterface.GetContext();
|
||||
JSAutoRequest rq(cx);
|
||||
|
||||
JS::RootedValue profileFetch(cx);
|
||||
scriptInterface.Eval("([])", &profileFetch);
|
||||
const char* stats[] = { "player", "rating", "totalGamesPlayed", "highestRating", "wins", "losses", "rank" };
|
||||
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it)
|
||||
{
|
||||
JS::RootedValue profile(cx);
|
||||
scriptInterface.Eval("({})", &profile);
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(stats); ++i)
|
||||
scriptInterface.SetProperty(profile, stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string()));
|
||||
|
||||
scriptInterface.CallFunctionVoid(profileFetch, "push", profile);
|
||||
}
|
||||
|
||||
return CScriptValRooted(cx, profileFetch);
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* Message interfaces *
|
||||
*****************************************************/
|
||||
@ -648,6 +697,7 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq)
|
||||
{
|
||||
const GameListQuery* gq = iq.findExtension<GameListQuery>( ExtGameListQuery );
|
||||
const BoardListQuery* bq = iq.findExtension<BoardListQuery>( ExtBoardListQuery );
|
||||
const ProfileQuery* pq = iq.findExtension<ProfileQuery>( ExtProfileQuery );
|
||||
if(gq)
|
||||
{
|
||||
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_GameList.begin(); it != m_GameList.end(); ++it )
|
||||
@ -684,8 +734,19 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq)
|
||||
CreateSimpleMessage("system", "ratinglist updated", "internal");
|
||||
}
|
||||
}
|
||||
if (pq)
|
||||
{
|
||||
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it)
|
||||
glooxwrapper::Tag::free(*it);
|
||||
m_Profile.clear();
|
||||
|
||||
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = pq->m_StanzaProfile.begin(); it != pq->m_StanzaProfile.end(); ++it)
|
||||
m_Profile.push_back((*it)->clone());
|
||||
|
||||
CreateSimpleMessage("system", "profile updated", "internal");
|
||||
}
|
||||
}
|
||||
else if(iq.subtype() == gloox::IQ::Error)
|
||||
else if (iq.subtype() == gloox::IQ::Error)
|
||||
{
|
||||
gloox::StanzaError err = iq.error_error();
|
||||
std::string msg = StanzaErrorToString(err);
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
void SendIqGetGameList();
|
||||
void SendIqGetBoardList();
|
||||
void SendIqGetRatingList();
|
||||
void SendIqGetProfile(const std::string& player);
|
||||
void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data);
|
||||
void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data);
|
||||
void SendIqUnregisterGame();
|
||||
@ -77,6 +78,7 @@ public:
|
||||
CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface);
|
||||
CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface);
|
||||
CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface);
|
||||
CScriptValRooted GUIGetProfile(ScriptInterface& scriptInterface);
|
||||
//Script
|
||||
ScriptInterface& GetScriptInterface();
|
||||
|
||||
@ -143,6 +145,8 @@ private:
|
||||
std::vector<const glooxwrapper::Tag*> m_GameList;
|
||||
/// List of rankings
|
||||
std::vector<const glooxwrapper::Tag*> m_BoardList;
|
||||
/// Profile data
|
||||
std::vector<const glooxwrapper::Tag*> m_Profile;
|
||||
/// Queue of messages for the GUI
|
||||
std::deque<GUIMessage> m_GuiMessageQueue;
|
||||
/// Current room subject/topic.
|
||||
|
@ -101,6 +101,13 @@ void JSI_Lobby::SendGetRatingList(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)
|
||||
g_XmppClient->SendIqGetRatingList();
|
||||
}
|
||||
|
||||
void JSI_Lobby::SendGetProfile(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring player)
|
||||
{
|
||||
if (!g_XmppClient)
|
||||
return;
|
||||
g_XmppClient->SendIqGetProfile(utf8_from_wstring(player));
|
||||
}
|
||||
|
||||
void JSI_Lobby::SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data)
|
||||
{
|
||||
if (!g_XmppClient)
|
||||
@ -161,6 +168,16 @@ CScriptVal JSI_Lobby::GetBoardList(ScriptInterface::CxPrivate* pCxPrivate)
|
||||
return boardList.get();
|
||||
}
|
||||
|
||||
CScriptVal JSI_Lobby::GetProfile(ScriptInterface::CxPrivate* pCxPrivate)
|
||||
{
|
||||
if (!g_XmppClient)
|
||||
return CScriptVal();
|
||||
|
||||
CScriptValRooted profileFetch = g_XmppClient->GUIGetProfile(*(pCxPrivate->pScriptInterface));
|
||||
|
||||
return profileFetch.get();
|
||||
}
|
||||
|
||||
CScriptVal JSI_Lobby::LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate)
|
||||
{
|
||||
if (!g_XmppClient)
|
||||
|
@ -37,6 +37,7 @@ namespace JSI_Lobby
|
||||
void SendGetGameList(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
void SendGetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
void SendGetRatingList(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
void SendGetProfile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring player);
|
||||
void SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data);
|
||||
void SendRegisterGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data);
|
||||
void SendUnregisterGame(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
@ -44,6 +45,7 @@ namespace JSI_Lobby
|
||||
CScriptVal GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
CScriptVal GetGameList(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
CScriptVal GetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
CScriptVal GetProfile(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
CScriptVal LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate);
|
||||
void LobbySendMessage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring message);
|
||||
void LobbySetPlayerPresence(ScriptInterface::CxPrivate* pCxPrivate, std::wstring presence);
|
||||
|
@ -33,6 +33,7 @@ class Player(Base):
|
||||
id = Column(Integer, primary_key=True)
|
||||
jid = Column(String(255))
|
||||
rating = Column(Integer)
|
||||
highest_rating = Column(Integer)
|
||||
games = relationship('Game', secondary='players_info')
|
||||
# These two relations really only exist to satisfy the linkage
|
||||
# between PlayerInfo and Player and Game and player.
|
||||
|
@ -26,6 +26,8 @@ from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin, ET
|
||||
from sleekxmpp.xmlstream.handler import Callback
|
||||
from sleekxmpp.xmlstream.matcher import StanzaPath
|
||||
|
||||
from sqlalchemy import func
|
||||
|
||||
from LobbyRanking import session as db, Game, Player, PlayerInfo
|
||||
from ELO import get_rating_adjustment
|
||||
# Rating that new players should be inserted into the
|
||||
@ -37,6 +39,34 @@ class LeaderboardList():
|
||||
def __init__(self, room):
|
||||
self.room = room
|
||||
self.lastRated = ""
|
||||
|
||||
def getProfile(self, JID):
|
||||
"""
|
||||
Retrieves the profile for the specified JID
|
||||
"""
|
||||
stats = {}
|
||||
player = db.query(Player).filter(Player.jid.ilike(str(JID)))
|
||||
if not player.first():
|
||||
return
|
||||
if player.first().rating != -1:
|
||||
stats['rating'] = str(player.first().rating)
|
||||
|
||||
if player.first().highest_rating != -1:
|
||||
stats['highestRating'] = str(player.first().highest_rating)
|
||||
|
||||
playerID = player.first().id
|
||||
players = db.query(Player).order_by(Player.rating.desc()).all()
|
||||
|
||||
for rank, user in enumerate(players):
|
||||
if (user.jid.lower() == JID.lower()):
|
||||
stats['rank'] = str(rank+1)
|
||||
break
|
||||
|
||||
stats['totalGamesPlayed'] = str(db.query(PlayerInfo).filter_by(player_id=playerID).count())
|
||||
stats['wins'] = str(db.query(Game).filter_by(winner_id=playerID).count())
|
||||
stats['losses'] = str(db.query(PlayerInfo).filter_by(player_id=playerID).count() - db.query(Game).filter_by(winner_id=playerID).count())
|
||||
return stats
|
||||
|
||||
def getOrCreatePlayer(self, JID):
|
||||
"""
|
||||
Stores a player(JID) in the database if they don't yet exist.
|
||||
@ -180,6 +210,14 @@ class LeaderboardList():
|
||||
name2, player2.rating, player2.rating + rating_adjustment2)
|
||||
player1.rating += rating_adjustment1
|
||||
player2.rating += rating_adjustment2
|
||||
if not player1.highest_rating:
|
||||
player1.highest_rating = -1
|
||||
if not player2.highest_rating:
|
||||
player2.highest_rating = -1
|
||||
if player1.rating > player1.highest_rating:
|
||||
player1.highest_rating = player1.rating
|
||||
if player2.rating > player2.highest_rating:
|
||||
player2.highest_rating = player2.rating
|
||||
db.commit()
|
||||
return self
|
||||
|
||||
@ -426,6 +464,22 @@ class GameReportXmppPlugin(ElementBase):
|
||||
data[key] = item
|
||||
return data
|
||||
|
||||
## Class for custom profile ##
|
||||
class ProfileXmppPlugin(ElementBase):
|
||||
name = 'query'
|
||||
namespace = 'jabber:iq:profile'
|
||||
interfaces = set(('profile', 'command'))
|
||||
sub_interfaces = interfaces
|
||||
plugin_attrib = 'profile'
|
||||
def addCommand(self, command):
|
||||
commandXml = ET.fromstring("<command>%s</command>" % command)
|
||||
self.xml.append(commandXml)
|
||||
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)
|
||||
|
||||
## Main class which handles IQ data and sends new data ##
|
||||
class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
"""
|
||||
@ -454,6 +508,7 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
register_stanza_plugin(Iq, GameListXmppPlugin)
|
||||
register_stanza_plugin(Iq, BoardListXmppPlugin)
|
||||
register_stanza_plugin(Iq, GameReportXmppPlugin)
|
||||
register_stanza_plugin(Iq, ProfileXmppPlugin)
|
||||
|
||||
self.register_handler(Callback('Iq Gamelist',
|
||||
StanzaPath('iq/gamelist'),
|
||||
@ -467,6 +522,11 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
StanzaPath('iq/gamereport'),
|
||||
self.iqhandler,
|
||||
instream=True))
|
||||
|
||||
self.register_handler(Callback('Iq Profile',
|
||||
StanzaPath('iq/profile'),
|
||||
self.iqhandler,
|
||||
instream=True))
|
||||
|
||||
self.add_event_handler("session_start", self.start)
|
||||
self.add_event_handler("muc::%s::got_online" % self.room, self.muc_online)
|
||||
@ -566,6 +626,15 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
logging.error("Failed to process ratinglist request from %s" % iq['from'].bare)
|
||||
else:
|
||||
logging.error("Failed to process boardlist request from %s" % iq['from'].bare)
|
||||
elif 'profile' in iq.plugins:
|
||||
command = iq['profile']['command']
|
||||
try:
|
||||
self.sendProfile(iq['from'], command)
|
||||
except:
|
||||
try:
|
||||
self.sendProfileNotFound(iq['from'], command)
|
||||
except:
|
||||
logging.debug("No record found for %s" % command)
|
||||
else:
|
||||
logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare)
|
||||
elif iq['type'] == 'result':
|
||||
@ -748,6 +817,73 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
|
||||
except:
|
||||
logging.error("Failed to send rating list")
|
||||
|
||||
def sendProfile(self, to, player):
|
||||
"""
|
||||
Send the profile to a specified player.
|
||||
"""
|
||||
if to == "":
|
||||
logging.error("Failed to send profile")
|
||||
return
|
||||
|
||||
online = False;
|
||||
## Pull stats and add it to the stanza
|
||||
for JID in self.nicks.keys():
|
||||
if self.nicks[JID] == player:
|
||||
stats = self.leaderboard.getProfile(JID)
|
||||
online = True
|
||||
break
|
||||
|
||||
if online == False:
|
||||
stats = self.leaderboard.getProfile(player + "@" + str(to).split('@')[1])
|
||||
stz = ProfileXmppPlugin()
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'result'
|
||||
|
||||
stz.addItem(player, stats['rating'], stats['highestRating'], stats['rank'], stats['totalGamesPlayed'], stats['wins'], stats['losses'])
|
||||
stz.addCommand(player)
|
||||
iq.setPayload(stz)
|
||||
## 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")
|
||||
|
||||
def sendProfileNotFound(self, to, player):
|
||||
"""
|
||||
Send a profile not-found error to a specified player.
|
||||
"""
|
||||
stz = ProfileXmppPlugin()
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'result'
|
||||
|
||||
filler = str(0)
|
||||
stz.addItem(player, str(-2), filler, filler, filler, filler, filler)
|
||||
stz.addCommand(player)
|
||||
iq.setPayload(stz)
|
||||
## 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")
|
||||
|
||||
## Main Program ##
|
||||
if __name__ == '__main__':
|
||||
# Setup the command line arguments.
|
||||
|
Loading…
Reference in New Issue
Block a user