mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-09-11 21:38:42 -05:00
Handle AFK '@-in-names' protocol (take 2) (#1270)
This commit is contained in:
committed by
Guangcong Luo
parent
2b879e4561
commit
883cd91fc8
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>';
|
||||
}
|
||||
|
||||
30
js/client.js
30
js/client.js
@@ -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 || ' ') + '</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>';
|
||||
|
||||
|
||||
@@ -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>';
|
||||
|
||||
@@ -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': {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -3049,6 +3049,11 @@ a.ilink.yours {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.userstatus {
|
||||
font-style: italic;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
.userdetails {
|
||||
min-height: 80px;
|
||||
width: 191px;
|
||||
|
||||
Reference in New Issue
Block a user