Skip to content

Commit

Permalink
Handle AFK '@-in-names' protocol (smogon#1265)
Browse files Browse the repository at this point in the history
  • Loading branch information
scheibo authored and Zarel committed Apr 21, 2019
1 parent 61ff5ae commit ba1e6d3
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 31 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module.exports = {
"BattleOtherAnims": false, "BattlePokedex": false,"BattlePokemonSprites": false, "BattlePokemonSpritesBW": false, "BattleSearchCountIndex": false, "BattleSearchIndex": false, "BattleArticleTitles": false,
"BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStats": false, "BattleStatusAnims": false, "BattleStatuses": false, "BattleTeambuilderTable": false,
"ModifiableValue": false, "BattleStatGuesser": false, "BattleText": true, "BattleTextAFD": false, "BattleTextNotAFD": false,
"BattleTextParser": false,

// Generic global variables
"Config": false, "BattleSearch": false, "soundManager": false, "Storage": false, "Dex": false,
Expand Down
54 changes: 37 additions & 17 deletions js/client-chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
},
updateUser: function () {
var name = app.user.get('name');
var userid = app.user.get('userid');
if (this.expired) {
this.$chatAdd.html(this.expired === true ? 'This room is expired' : BattleLog.sanitizeHTML(this.expired));
this.$chatbox = null;
Expand All @@ -37,7 +36,8 @@
this.$chatAdd.html('<form><button name="login">Join chat</button></form>');
this.$chatbox = null;
} else {
this.$chatAdd.html('<form class="chatbox"><label style="' + BattleLog.hashColor(userid) + '">' + BattleLog.escapeHTML(name) + ':</label> <textarea class="textbox" type="text" size="70" autocomplete="off"></textarea></form>');
var color = app.user.get('away') ? 'color:#AAA;' : BattleLog.hashColor(app.user.get('userid'));
this.$chatAdd.html('<form class="chatbox"><label style="' + color + '">' + BattleLog.escapeHTML(name) + ':</label> <textarea class="textbox" type="text" size="70" autocomplete="off"></textarea></form>');
this.$chatbox = this.$chatAdd.find('textarea');
this.$chatbox.autoResize({
animate: false,
Expand Down Expand Up @@ -175,7 +175,9 @@
position = 'right';
}
var name = $(e.currentTarget).data('name') || $(e.currentTarget).text();
app.addPopup(UserPopup, {name: name, sourceEl: e.currentTarget, position: position});
var away = $(e.currentTarget).data('away') || false;
var status = $(e.currentTarget).data('status');
app.addPopup(UserPopup, {name: name, away: away, status: status, sourceEl: e.currentTarget, position: position});
},
openPM: function (e) {
e.preventDefault();
Expand Down Expand Up @@ -1411,7 +1413,10 @@
this.userCount.users = parseInt(userList.substr(0, commaIndex), 10);
var users = userList.substr(commaIndex + 1).split(',');
for (var i = 0, len = users.length; i < len; i++) {
if (users[i]) this.users[toId(users[i])] = users[i];
if (users[i]) {
var user = BattleTextParser.parseNameParts(users[i]);
this.users[toUserid(user.name)] = user;
}
}
} else {
this.userCount.users = parseInt(userList, 10);
Expand All @@ -1420,7 +1425,8 @@
this.userList.construct();
},
addJoinLeave: function (action, name, oldid, silent) {
var userid = toUserid(name);
var user = BattleTextParser.parseNameParts(name);
var userid = toUserid(user.name);
if (!action) {
this.$joinLeave = null;
this.joinLeave = {
Expand All @@ -1431,7 +1437,7 @@
} else if (action === 'join') {
if (oldid) delete this.users[toUserid(oldid)];
if (!this.users[userid]) this.userCount.users++;
this.users[userid] = name;
this.users[userid] = user;
this.userList.add(userid);
this.userList.updateUserCount();
this.userList.updateNoUsersOnline();
Expand All @@ -1443,7 +1449,13 @@
this.userList.updateNoUsersOnline();
} else if (action === 'rename') {
if (oldid) delete this.users[toUserid(oldid)];
this.users[userid] = name;
if (toUserid(oldid) === app.user.get('userid')) {
app.user.set({
away: user.away,
status: user.status
});
}
this.users[userid] = user;
this.userList.remove(oldid);
this.userList.add(userid);
return;
Expand All @@ -1457,7 +1469,7 @@
this.$chat.append('<div class="message"><small>Loading...</small></div>');
this.$joinLeave = this.$chat.children().last();
}
this.joinLeave[action].push(name);
this.joinLeave[action].push(user.name);
var message = '';
if (this.joinLeave['join'].length) {
var preList = this.joinLeave['join'];
Expand Down Expand Up @@ -1520,7 +1532,8 @@
var userid = toUserid(name);

var speakerHasAuth = " +\u2606".indexOf(name.charAt(0)) < 0;
var readerHasAuth = this.users && " +\u2606\u203D!".indexOf((this.users[app.user.get('userid')] || ' ').charAt(0)) < 0;
var user = (this.users && this.users[app.user.get('userid')]) || {};
var readerHasAuth = user.name && " +\u2606\u203D!".indexOf((user.name || ' ').charAt(0)) < 0;
if (app.ignore[userid] && !speakerHasAuth && !readerHasAuth) return;

// Add this user to the list of people who have spoken recently.
Expand Down Expand Up @@ -1699,21 +1712,23 @@
$('#' + this.room.id + '-userlist-user-' + userid).remove();
},
constructItem: function (userid) {
var name = this.room.users[userid];
var user = this.room.users[userid];
var text = '';
// Sanitising the `userid` here is probably unnecessary, because
// IDs can't contain anything dangerous.
text += '<li' + (this.room.userForm === userid ? ' class="cur"' : '') + ' id="' + this.room.id + '-userlist-user-' + BattleLog.escapeHTML(userid) + '">';
text += '<button class="userbutton username" data-name="' + BattleLog.escapeHTML(name) + '">';
var group = name.charAt(0);
text += '<button class="userbutton username" data-name="' + BattleLog.escapeHTML(user.name) + '"';
text += (user.away ? ' data-away=true' : '') + (user.status ? ' data.status="' + user.status + '"' : '') + '>';
var group = user.name.charAt(0);
var details = Config.groups[group] || {type: 'user'};
var color = user.away ? 'color:#AAA;' : BattleLog.hashColor(userid);
text += '<em class="group' + (details.group === 2 ? ' staffgroup' : '') + '">' + BattleLog.escapeHTML(group) + '</em>';
if (details.type === 'leadership') {
text += '<strong><em style="' + BattleLog.hashColor(userid) + '">' + BattleLog.escapeHTML(name.substr(1)) + '</em></strong>';
text += '<strong><em style="' + color + '">' + BattleLog.escapeHTML(user.name.substr(1)) + '</em></strong>';
} else if (details.type === 'staff') {
text += '<strong style="' + BattleLog.hashColor(userid) + '">' + BattleLog.escapeHTML(name.substr(1)) + '</strong>';
text += '<strong style="' + color + '">' + BattleLog.escapeHTML(user.name.substr(1)) + '</strong>';
} else {
text += '<span style="' + BattleLog.hashColor(userid) + '">' + BattleLog.escapeHTML(name.substr(1)) + '</span>';
text += '<span style="' + color + '">' + BattleLog.escapeHTML(user.name.substr(1)) + '</span>';
}
text += '</button>';
text += '</li>';
Expand All @@ -1736,12 +1751,17 @@
},
comparator: function (a, b) {
if (a === b) return 0;

var aUser = this.room.users[a];
var bUser = this.room.users[b];
if (aUser.away !== bUser.away) return aUser.away - bUser.away;

var aRank = (
Config.groups[(this.room.users[a] ? this.room.users[a].charAt(0) : Config.defaultGroup || ' ')] ||
Config.groups[aUser ? aUser.name.charAt(0) : Config.defaultGroup || ' '] ||
{order: (Config.defaultOrder || 10006.5)}
).order;
var bRank = (
Config.groups[(this.room.users[b] ? this.room.users[b].charAt(0) : Config.defaultGroup || ' ')] ||
Config.groups[bUser ? bUser.name.charAt(0) : Config.defaultGroup || ' '] ||
{order: (Config.defaultOrder || 10006.5)}
).order;

Expand Down
6 changes: 4 additions & 2 deletions js/client-topbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
updateUserbar: function () {
var buf = '';
var name = ' ' + app.user.get('name');
var color = BattleLog.hashColor(app.user.get('userid'));
var away = app.user.get('away');
var status = app.user.get('status');
var color = away ? 'color:#AAA;' : BattleLog.hashColor(app.user.get('userid'));
if (!app.user.loaded) {
buf = '<button disabled>Loading...</button>';
} else if (app.user.get('named')) {
buf = '<span class="username" data-name="' + BattleLog.escapeHTML(name) + '" style="' + color + '"><i class="fa fa-user" style="color:#779EC5"></i> ' + BattleLog.escapeHTML(name) + '</span>';
buf = '<span class="username" data-name="' + BattleLog.escapeHTML(name) + '"' + (away ? ' data-away=true' : '') + (status ? 'data-status="' + status + '"' : '') + ' style="' + color + '"><i class="fa fa-user" style="color:' + (away ? '#AAA;' : '#779EC5') + '"></i> ' + BattleLog.escapeHTML(name) + '</span>';
} else {
buf = '<button name="login">Choose name</button>';
}
Expand Down
30 changes: 18 additions & 12 deletions js/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@
registered: false,
named: false,
avatar: 0,
settings: {}
settings: {},
status: '',
away: false
},
initialize: function () {
app.addGlobalListeners();
Expand Down Expand Up @@ -937,14 +939,14 @@
this.receive(data.substr(nlIndex + 1));
parts = data.substr(0, nlIndex).split('|');
}
var name = parts[1];
var parsed = BattleTextParser.parseNameParts(parts[1]);
var named = !!+parts[2];

var userid = toUserid(name);
if (userid === this.user.get('userid') && name !== this.user.get('name')) {
var userid = toUserid(parsed.name);
if (userid === this.user.get('userid') && parsed.name !== this.user.get('name')) {
$.post(app.user.getActionPHP(), {
act: 'changeusername',
username: name
username: parsed.name
}, function () {}, 'text');
}

Expand All @@ -960,18 +962,20 @@
}

this.user.set({
name: name,
name: parsed.name,
userid: userid,
named: named,
avatar: parts[3],
settings: settings
settings: settings,
status: parsed.status,
away: parsed.away
});
this.user.setPersistentName(named ? name : null);
this.user.setPersistentName(named ? parsed.name : null);
if (named) {
this.trigger('init:choosename');
}
if (app.ignore[toUserid(name)]) {
delete app.ignore[toUserid(name)];
if (app.ignore[userid]) {
delete app.ignore[userid];
}
break;

Expand Down Expand Up @@ -2466,6 +2470,10 @@
var buf = '<div class="userdetails">';
if (avatar) buf += '<img class="trainersprite' + (userid === ownUserid ? ' yours' : '') + '" src="' + Dex.resolveAvatar(avatar) + '" />';
buf += '<strong><a href="//pokemonshowdown.com/users/' + userid + '" target="_blank">' + BattleLog.escapeHTML(name) + '</a></strong><br />';
var offline = data.rooms === false;
if (data.status || offline) {
buf += '<span class="userstatus' + (offline ? ' offline' : '') + '">' + (offline ? 'Offline' : data.status) + '</span><br />';
}
buf += '<small>' + (group || '&nbsp;') + '</small>';
if (globalgroup) buf += '<br /><small>' + globalgroup + '</small>';
if (data.rooms) {
Expand Down Expand Up @@ -2507,8 +2515,6 @@
}
}
buf += '<small class="rooms">' + battlebuf + chatbuf + privatebuf + '</small>';
} else if (data.rooms === false) {
buf += '<strong class="offline">OFFLINE</strong>';
}
buf += '</div>';

Expand Down
17 changes: 17 additions & 0 deletions src/battle-text-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ class BattleTextParser {
}
return BattleTextParser.upgradeArgs({args, kwArgs});
}

static parseNameParts(text: string) {
const atIndex = text.indexOf('@');
let name = text;
let status = '';
let away = false;
if (atIndex > 0) {
name = text.substr(0, atIndex);
status = text.substr(atIndex + 1);
if (status.startsWith('!')) {
away = true;
status = status.substr(1);
}
}
return {name, status, away};
}

static upgradeArgs({args, kwArgs}: {args: Args, kwArgs: KWArgs}): {args: Args, kwArgs: KWArgs} {
switch (args[0]) {
case '-activate': {
Expand Down
5 changes: 5 additions & 0 deletions style/client.css
Original file line number Diff line number Diff line change
Expand Up @@ -3049,6 +3049,11 @@ a.ilink.yours {
width: 200px;
}

.userstatus {
font-style: italic;
font-size: 8pt;
}

.userdetails {
min-height: 80px;
width: 191px;
Expand Down

0 comments on commit ba1e6d3

Please sign in to comment.