Handle AFK '@-in-names' protocol (take 2) (#1270)

This commit is contained in:
Kirk Scheibelhut
2019-05-05 06:13:54 -07:00
committed by Guangcong Luo
parent 2b879e4561
commit 883cd91fc8
8 changed files with 117 additions and 55 deletions

View File

@@ -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,

View File

@@ -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;
@@ -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,
@@ -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();
@@ -312,7 +314,7 @@
if (m2 && (m2[0] === '/' || m2[0] === '!')) spaceprefix = '';
for (var i in users) {
if (spaceprefix && users[i].substr(1).replace(/[^A-Za-z0-9 ]+/g, '').toLowerCase().substr(0, spaceprefix.length) === spaceprefix) {
if (spaceprefix && users[i].name.substr(1).replace(/[^A-Za-z0-9 ]+/g, '').toLowerCase().substr(0, spaceprefix.length) === spaceprefix) {
candidates.push([i, m2[1].length]);
} else if (idprefix && i.substr(0, idprefix.length) === idprefix) {
candidates.push([i, m1[1].length]);
@@ -350,8 +352,9 @@
// Substitute in the tab-completed name.
var candidate = this.tabComplete.candidates[this.tabComplete.index];
var substituteUserId = candidate[0];
if (!users[substituteUserId]) return true;
var name = users[substituteUserId].substr(1);
var substituteUser = users[substituteUserId];
if (!substituteUser) return true;
var name = substituteUser.name.substr(1);
name = Dex.getShortName(name);
var fullPrefix = this.tabComplete.prefix.substr(0, candidate[1]) + name;
$textbox.val(fullPrefix + text.substr(idx));
@@ -1411,7 +1414,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);
@@ -1420,7 +1426,6 @@
this.userList.construct();
},
addJoinLeave: function (action, name, oldid, silent) {
var userid = toUserid(name);
if (!action) {
this.$joinLeave = null;
this.joinLeave = {
@@ -1428,10 +1433,13 @@
'leave': []
};
return;
} else if (action === 'join') {
}
var user = BattleTextParser.parseNameParts(name);
var userid = toUserid(user.name);
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();
@@ -1443,7 +1451,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;
@@ -1457,7 +1471,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'];
@@ -1520,7 +1534,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.
@@ -1699,21 +1714,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>';
@@ -1736,12 +1753,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;

View File

@@ -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>';
}

View File

@@ -139,7 +139,9 @@
registered: false,
named: false,
avatar: 0,
settings: {}
settings: {},
status: '',
away: false
},
initialize: function () {
app.addGlobalListeners();
@@ -937,14 +939,14 @@
this.receive(data.substr(nlIndex + 1));
parts = data.slice(1, 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');
}
@@ -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;
@@ -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) {
@@ -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>';

View File

@@ -90,20 +90,23 @@ class BattleLog {
}
break;
case 'join': case 'j':
divHTML = '<small>' + BattleLog.escapeHTML(args[1]) + ' joined.</small>';
case 'join': case 'j': {
const user = BattleTextParser.parseNameParts(args[1]);
divHTML = '<small>' + BattleLog.escapeHTML(user.name) + ' joined.</small>';
break;
case 'leave': case 'l':
divHTML = '<small>' + BattleLog.escapeHTML(args[1]) + ' left.</small>';
}
case 'leave': case 'l': {
const user = BattleTextParser.parseNameParts(args[1]);
divHTML = '<small>' + BattleLog.escapeHTML(user.name) + ' left.</small>';
break;
case 'name': case 'n':
if (toId(args[2]) !== toId(args[1])) {
divHTML = '<small>' + BattleLog.escapeHTML(args[1]) + ' renamed from ' + BattleLog.escapeHTML(args[2]) + '.</small>';
}
case 'name': case 'n': {
const user = BattleTextParser.parseNameParts(args[1]);
if (toId(args[2]) !== toId(user.name)) {
divHTML = '<small>' + BattleLog.escapeHTML(user.name) + ' renamed from ' + BattleLog.escapeHTML(args[2]) + '.</small>';
}
break;
}
case 'chatmsg': case '':
divHTML = BattleLog.escapeHTML(args[1]);
break;
@@ -133,14 +136,14 @@ class BattleLog {
this.addDiv('chat', '<div class="broadcast-blue"><b>Register an account to protect your ladder rating!</b><br /><button name="register" value="' + BattleLog.escapeHTML(args[1]) + '"><b>Register</b></button></div>');
return;
case 'unlink':
case 'unlink': {
const user = toId(args[2]) || toId(args[1]);
this.unlinkChatFrom(user);
if (args[2]) {
this.hideChatFrom(user);
}
return;
}
case 'debug':
divClass = 'debug';
divHTML = '<div class="chat"><small style="color:#999">[DEBUG] ' + BattleLog.escapeHTML(args[1]) + '.</small></div>';

View File

@@ -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': {

View File

@@ -3139,9 +3139,9 @@ class Battle {
case 'join': case 'j': {
if (this.roomid) {
let room = app!.rooms[this.roomid];
let user = args[1];
let userid = toUserid(user);
if (/^[a-z0-9]/i.test(user)) user = ' ' + user;
let user = BattleTextParser.parseNameParts(args[1]);
let userid = toUserid(user.name);
if (/^[a-z0-9]/i.test(user.name)) user.name = ' ' + user.name;
if (!room.users[userid]) room.userCount.users++;
room.users[userid] = user;
room.userList.add(userid);
@@ -3172,11 +3172,17 @@ class Battle {
case 'name': case 'n': {
if (this.roomid) {
let room = app!.rooms[this.roomid];
let newuser = args[1];
let olduser = args[2];
let userid = toUserid(newuser);
room.users[userid] = newuser;
room.userList.remove(olduser);
let user = BattleTextParser.parseNameParts(args[1]);
let oldid = args[2];
if (toUserid(oldid) === app!.user.get('userid')) {
app!.user.set({
away: user.away,
status: user.status,
});
}
let userid = toUserid(user.name);
room.users[userid] = user;
room.userList.remove(oldid);
room.userList.add(userid);
}
if (!this.ignoreSpects) {

View File

@@ -3049,6 +3049,11 @@ a.ilink.yours {
width: 200px;
}
.userstatus {
font-style: italic;
font-size: 8pt;
}
.userdetails {
min-height: 80px;
width: 191px;