/**
* System commands
* Pokemon Showdown - http://pokemonshowdown.com/
*
* These are system commands - commands required for Pokemon Showdown
* to run. A lot of these are sent by the client.
*
* If you'd like to modify commands, please go to config/commands.js,
* which also teaches you how to use commands.
*
* @license MIT license
*/
var crypto = require('crypto');
var fs = require('fs');
const MAX_REASON_LENGTH = 300;
var commands = exports.commands = {
version: function (target, room, user) {
if (!this.canBroadcast()) return;
this.sendReplyBox("Server version: " + CommandParser.package.version + "");
},
me: function (target, room, user, connection) {
// By default, /me allows a blank message
if (target) target = this.canTalk(target);
if (!target) return;
return '/me ' + target;
},
mee: function (target, room, user, connection) {
// By default, /mee allows a blank message
if (target) target = this.canTalk(target);
if (!target) return;
return '/mee ' + target;
},
avatar: function (target, room, user) {
if (!target) return this.parse('/avatars');
var parts = target.split(',');
var avatar = parseInt(parts[0]);
if (!avatar || avatar > 294 || avatar < 1) {
if (!parts[1]) {
this.sendReply("Invalid avatar.");
}
return false;
}
user.avatar = avatar;
if (!parts[1]) {
this.sendReply("Avatar changed to:\n" +
'|raw|
');
}
},
logout: function (target, room, user) {
user.resetName();
},
r: 'reply',
reply: function (target, room, user) {
if (!target) return this.parse('/help reply');
if (!user.lastPM) {
return this.sendReply("No one has PMed you yet.");
}
return this.parse('/msg ' + (user.lastPM || '') + ', ' + target);
},
pm: 'msg',
whisper: 'msg',
w: 'msg',
msg: function (target, room, user) {
if (!target) return this.parse('/help msg');
target = this.splitTarget(target);
var targetUser = this.targetUser;
if (!target) {
this.sendReply("You forgot the comma.");
return this.parse('/help msg');
}
if (!targetUser || !targetUser.connected) {
if (targetUser && !targetUser.connected) {
this.popupReply("User " + this.targetUsername + " is offline.");
} else if (!target) {
this.popupReply("User " + this.targetUsername + " not found. Did you forget a comma?");
} else {
this.popupReply("User " + this.targetUsername + " not found. Did you misspell their name?");
}
return this.parse('/help msg');
}
if (Config.pmmodchat) {
var userGroup = user.group;
if (Config.groupsranking.indexOf(userGroup) < Config.groupsranking.indexOf(Config.pmmodchat)) {
var groupName = Config.groups[Config.pmmodchat].name || Config.pmmodchat;
this.popupReply("Because moderated chat is set, you must be of rank " + groupName + " or higher to PM users.");
return false;
}
}
if (user.locked && !targetUser.can('lock', user)) {
return this.popupReply("You can only private message members of the moderation team (users marked by %, @, &, or ~) when locked.");
}
if (targetUser.locked && !user.can('lock', targetUser)) {
return this.popupReply("This user is locked and cannot PM.");
}
if (targetUser.ignorePMs && !user.can('lock')) {
if (!targetUser.can('lock')) {
return this.popupReply("This user is blocking Private Messages right now.");
} else if (targetUser.can('hotpatch')) {
return this.popupReply("This admin is too busy to answer Private Messages right now. Please contact a different staff member.");
}
}
target = this.canTalk(target, null);
if (!target) return false;
var message = '|pm|' + user.getIdentity() + '|' + targetUser.getIdentity() + '|' + target;
user.send(message);
if (targetUser !== user) targetUser.send(message);
targetUser.lastPM = user.userid;
user.lastPM = targetUser.userid;
},
blockpm: 'ignorepms',
blockpms: 'ignorepms',
ignorepm: 'ignorepms',
ignorepms: function (target, room, user) {
if (user.ignorePMs) return this.sendReply("You are already blocking Private Messages!");
if (user.can('lock') && !user.can('hotpatch')) return this.sendReply("You are not allowed to block Private Messages.");
user.ignorePMs = true;
return this.sendReply("You are now blocking Private Messages.");
},
unblockpm: 'unignorepms',
unblockpms: 'unignorepms',
unignorepm: 'unignorepms',
unignorepms: function (target, room, user) {
if (!user.ignorePMs) return this.sendReply("You are not blocking Private Messages!");
user.ignorePMs = false;
return this.sendReply("You are no longer blocking Private Messages.");
},
makechatroom: function (target, room, user) {
if (!this.can('makeroom')) return;
var id = toId(target);
if (!id) return this.parse('/help makechatroom');
if (Rooms.rooms[id]) return this.sendReply("The room '" + target + "' already exists.");
if (Rooms.global.addChatRoom(target)) {
return this.sendReply("The room '" + target + "' was created.");
}
return this.sendReply("An error occurred while trying to create the room '" + target + "'.");
},
deregisterchatroom: function (target, room, user) {
if (!this.can('makeroom')) return;
var id = toId(target);
if (!id) return this.parse('/help deregisterchatroom');
var targetRoom = Rooms.get(id);
if (!targetRoom) return this.sendReply("The room '" + target + "' doesn't exist.");
target = targetRoom.title || targetRoom.id;
if (Rooms.global.deregisterChatRoom(id)) {
this.sendReply("The room '" + target + "' was deregistered.");
this.sendReply("It will be deleted as of the next server restart.");
return;
}
return this.sendReply("The room '" + target + "' isn't registered.");
},
privateroom: function (target, room, user) {
if (!this.can('privateroom', null, room)) return;
if (target === 'off') {
delete room.isPrivate;
this.addModCommand("" + user.name + " made this room public.");
if (room.chatRoomData) {
delete room.chatRoomData.isPrivate;
Rooms.global.writeChatRoomData();
}
} else {
room.isPrivate = true;
this.addModCommand("" + user.name + " made this room private.");
if (room.chatRoomData) {
room.chatRoomData.isPrivate = true;
Rooms.global.writeChatRoomData();
}
}
},
modjoin: function (target, room, user) {
if (!this.can('privateroom', null, room)) return;
if (target === 'off') {
delete room.modjoin;
this.addModCommand("" + user.name + " turned off modjoin.");
if (room.chatRoomData) {
delete room.chatRoomData.modjoin;
Rooms.global.writeChatRoomData();
}
} else {
room.modjoin = true;
this.addModCommand("" + user.name + " turned on modjoin.");
if (room.chatRoomData) {
room.chatRoomData.modjoin = true;
Rooms.global.writeChatRoomData();
}
}
},
officialchatroom: 'officialroom',
officialroom: function (target, room, user) {
if (!this.can('makeroom')) return;
if (!room.chatRoomData) {
return this.sendReply("/officialroom - This room can't be made official");
}
if (target === 'off') {
delete room.isOfficial;
this.addModCommand("" + user.name + " made this chat room unofficial.");
delete room.chatRoomData.isOfficial;
Rooms.global.writeChatRoomData();
} else {
room.isOfficial = true;
this.addModCommand("" + user.name + " made this chat room official.");
room.chatRoomData.isOfficial = true;
Rooms.global.writeChatRoomData();
}
},
roomdesc: function (target, room, user) {
if (!target) {
if (!this.canBroadcast()) return;
var re = /(https?:\/\/(([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?))/g;
if (!room.desc) return this.sendReply("This room does not have a description set.");
this.sendReplyBox("The room description is: " + room.desc.replace(re, '$1'));
return;
}
if (!this.can('declare')) return false;
if (target.length > 80) return this.sendReply("Error: Room description is too long (must be at most 80 characters).");
room.desc = target;
this.sendReply("(The room description is now: " + target + ")");
if (room.chatRoomData) {
room.chatRoomData.desc = room.desc;
Rooms.global.writeChatRoomData();
}
},
roomintro: function (target, room, user) {
if (!target) {
if (!this.canBroadcast()) return;
var re = /(https?:\/\/(([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?))/g;
if (!room.introMessage) return this.sendReply("This room does not have an introduction set.");
this.sendReplyBox(room.introMessage);
if (!this.broadcasting && user.can('declare', null, room)) {
this.sendReply('Source:');
this.sendReplyBox(''+Tools.escapeHTML(room.introMessage)+'');
}
return;
}
if (!this.can('declare', null, room)) return false;
if (!this.canHTML(target)) return;
if (!/$1');
}
if (!target.trim()) target = '';
room.introMessage = target;
this.sendReply("(The room introduction has been changed to:)");
this.sendReplyBox(target);
if (room.chatRoomData) {
room.chatRoomData.introMessage = room.introMessage;
Rooms.global.writeChatRoomData();
}
},
roomowner: function (target, room, user) {
if (!room.chatRoomData) {
return this.sendReply("/roomowner - This room isn't designed for per-room moderation to be added");
}
var target = this.splitTarget(target, true);
var targetUser = this.targetUser;
if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' is not online.");
if (!this.can('makeroom', targetUser, room)) return false;
if (!room.auth) room.auth = room.chatRoomData.auth = {};
var name = targetUser.name;
room.auth[targetUser.userid] = '#';
this.addModCommand("" + name + " was appointed Room Owner by " + user.name + ".");
room.onUpdateIdentity(targetUser);
Rooms.global.writeChatRoomData();
},
roomdeowner: 'deroomowner',
deroomowner: function (target, room, user) {
if (!room.auth) {
return this.sendReply("/roomdeowner - This room isn't designed for per-room moderation");
}
var target = this.splitTarget(target, true);
var targetUser = this.targetUser;
var name = this.targetUsername;
var userid = toId(name);
if (!userid || userid === '') return this.sendReply("User '" + name + "' does not exist.");
if (room.auth[userid] !== '#') return this.sendReply("User '" + name + "' is not a room owner.");
if (!this.can('makeroom', null, room)) return false;
delete room.auth[userid];
this.sendReply("(" + name + " is no longer Room Owner.)");
if (targetUser) targetUser.updateIdentity();
if (room.chatRoomData) {
Rooms.global.writeChatRoomData();
}
},
roomdemote: 'roompromote',
roompromote: function (target, room, user, connection, cmd) {
if (!room.auth) {
this.sendReply("/roompromote - This room isn't designed for per-room moderation");
return this.sendReply("Before setting room mods, you need to set it up with /roomowner");
}
if (!target) return this.parse('/help roompromote');
target = this.splitTarget(target, true);
var targetUser = this.targetUser;
var userid = toId(this.targetUsername);
var name = targetUser ? targetUser.name : this.targetUsername;
if (!userid) return this.parse('/help roompromote');
if (!targetUser && (!room.auth || !room.auth[userid])) {
return this.sendReply("User '" + name + "' is offline and unauthed, and so can't be promoted.");
}
var currentGroup = ((room.auth && room.auth[userid]) || ' ')[0];
var nextGroup = target || Users.getNextGroupSymbol(currentGroup, cmd === 'roomdemote', true);
if (target === 'deauth') nextGroup = Config.groupsranking[0];
if (!Config.groups[nextGroup]) {
return this.sendReply("Group '" + nextGroup + "' does not exist.");
}
if (Config.groups[nextGroup].globalonly) {
return this.sendReply("Group 'room" + Config.groups[nextGroup].id + "' does not exist as a room rank.");
}
var groupName = Config.groups[nextGroup].name || "regular user";
if (currentGroup === nextGroup) {
return this.sendReply("User '" + name + "' is already a " + groupName + " in this room.");
}
if (currentGroup !== ' ' && !user.can('room' + Config.groups[currentGroup].id, null, room)) {
return this.sendReply("/" + cmd + " - Access denied for promoting from " + Config.groups[currentGroup].name + ".");
}
if (nextGroup !== ' ' && !user.can('room' + Config.groups[nextGroup].id, null, room)) {
return this.sendReply("/" + cmd + " - Access denied for promoting to " + Config.groups[nextGroup].name + ".");
}
if (nextGroup === ' ') {
delete room.auth[userid];
} else {
room.auth[userid] = nextGroup;
}
if (Config.groups[nextGroup].rank < Config.groups[currentGroup].rank) {
this.privateModCommand("(" + name + " was demoted to Room " + groupName + " by " + user.name + ".)");
if (targetUser) targetUser.popup("You were demoted to Room " + groupName + " by " + user.name + ".");
} else if (nextGroup === '#') {
this.addModCommand("" + name + " was promoted to " + groupName + " by " + user.name + ".");
} else {
this.addModCommand("" + name + " was promoted to Room " + groupName + " by " + user.name + ".");
}
if (targetUser) targetUser.updateIdentity();
if (room.chatRoomData) Rooms.global.writeChatRoomData();
},
roomauth: function (target, room, user, connection) {
if (!room.auth) return this.sendReply("/roomauth - This room isn't designed for per-room moderation and therefore has no auth list.");
var rankLists = {};
for (var u in room.auth) {
if (!rankLists[room.auth[u]]) rankLists[room.auth[u]] = [];
rankLists[room.auth[u]].push(u);
}
var buffer = [];
Object.keys(rankLists).sort(function (a, b) {
return Config.groups[b].rank - Config.groups[a].rank;
}).forEach(function (r) {
buffer.push(Config.groups[r].name + "s (" + r + "):\n" + rankLists[r].sort().join(", "));
});
if (!buffer.length) {
buffer = "This room has no auth.";
}
connection.popup(buffer.join("\n\n"));
},
rb: 'roomban',
roomban: function (target, room, user, connection) {
if (!target) return this.parse('/help roomban');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = this.splitTarget(target, true);
var targetUser = this.targetUser;
var name = this.targetUsername;
var userid = toId(name);
if (!userid || !targetUser) return this.sendReply("User '" + name + "' does not exist.");
if (!this.can('ban', targetUser, room)) return false;
if (!room.bannedUsers || !room.bannedIps) {
return this.sendReply("Room bans are not meant to be used in room " + room.id + ".");
}
if (room.bannedUsers[userid] || room.bannedIps[targetUser.latestIp]) return this.sendReply("User " + targetUser.name + " is already banned from room " + room.id + ".");
room.bannedUsers[userid] = true;
for (var ip in targetUser.ips) {
room.bannedIps[ip] = true;
}
targetUser.popup("" + user.name + " has banned you from the room " + room.id + ". To appeal the ban, PM the moderator that banned you or a room owner." + (target ? " (" + target + ")" : ""));
this.addModCommand("" + targetUser.name + " was banned from room " + room.id + " by " + user.name + "." + (target ? " (" + target + ")" : ""));
var alts = targetUser.getAlts();
if (alts.length) {
this.privateModCommand("(" + targetUser.name + "'s alts were also banned from room " + room.id + ": " + alts.join(", ") + ")");
for (var i = 0; i < alts.length; ++i) {
var altId = toId(alts[i]);
this.add('|unlink|' + altId);
room.bannedUsers[altId] = true;
Users.getExact(altId).leaveRoom(room.id);
}
}
this.add('|unlink|' + this.getLastIdOf(targetUser));
targetUser.leaveRoom(room.id);
},
unroomban: 'roomunban',
roomunban: function (target, room, user, connection) {
if (!target) return this.parse('/help roomunban');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = this.splitTarget(target, true);
var targetUser = this.targetUser;
var name = this.targetUsername;
var userid = toId(name);
var success;
if (!userid || !targetUser) return this.sendReply("User '" + name + "' does not exist.");
if (!this.can('ban', targetUser, room)) return false;
if (!room.bannedUsers || !room.bannedIps) {
return this.sendReply("Room bans are not meant to be used in room " + room.id + ".");
}
if (room.bannedUsers[userid]) {
delete room.bannedUsers[userid];
success = true;
}
for (var ip in targetUser.ips) {
if (room.bannedIps[ip]) {
delete room.bannedIps[ip];
success = true;
}
}
if (!success) return this.sendReply("User " + targetUser.name + " is not banned from room " + room.id + ".");
targetUser.popup("" + user.name + " has unbanned you from the room " + room.id + ".");
this.addModCommand("" + targetUser.name + " was unbanned from room " + room.id + " by " + user.name + ".");
var alts = targetUser.getAlts();
if (!alts.length) return;
for (var i = 0; i < alts.length; ++i) {
var altId = toId(alts[i]);
if (room.bannedUsers[altId]) delete room.bannedUsers[altId];
}
this.privateModCommand("(" + targetUser.name + "'s alts were also unbanned from room " + room.id + ": " + alts.join(", ") + ")");
},
autojoin: function (target, room, user, connection) {
Rooms.global.autojoinRooms(user, connection);
},
join: function (target, room, user, connection) {
if (!target) return false;
var targetRoom = Rooms.get(target) || Rooms.get(toId(target));
if (!targetRoom) {
return connection.sendTo(target, "|noinit|nonexistent|The room '" + target + "' does not exist.");
}
if (targetRoom.isPrivate) {
if (targetRoom.modjoin) {
var userGroup = user.group;
if (targetRoom.auth) {
userGroup = targetRoom.auth[user.userid] || ' ';
}
if (Config.groupsranking.indexOf(userGroup) < Config.groupsranking.indexOf(targetRoom.modchat)) {
return connection.sendTo(target, "|noinit|nonexistent|The room '" + target + "' does not exist.");
}
}
if (!user.named) {
return connection.sendTo(target, "|noinit|namerequired|You must have a name in order to join the room '" + target + "'.");
}
}
if (!user.joinRoom(targetRoom || room, connection)) {
return connection.sendTo(target, "|noinit|joinfailed|The room '" + target + "' could not be joined.");
}
},
leave: 'part',
part: function (target, room, user, connection) {
if (room.id === 'global') return false;
var targetRoom = Rooms.get(target);
if (target && !targetRoom) {
return this.sendReply("The room '" + target + "' does not exist.");
}
user.leaveRoom(targetRoom || room, connection);
},
/*********************************************************
* Moderating: Punishments
*********************************************************/
kick: 'warn',
k: 'warn',
warn: function (target, room, user) {
if (!target) return this.parse('/help warn');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = this.splitTarget(target);
var targetUser = this.targetUser;
if (!targetUser || !targetUser.connected) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
if (room.isPrivate && room.auth) {
return this.sendReply("You can't warn here: This is a privately-owned room not subject to global rules.");
}
if (target.length > MAX_REASON_LENGTH) {
return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
}
if (!this.can('warn', targetUser, room)) return false;
this.addModCommand("" + targetUser.name + " was warned by " + user.name + "." + (target ? " (" + target + ")" : ""));
targetUser.send('|c|~|/warn ' + target);
this.add('|unlink|' + this.getLastIdOf(targetUser));
},
redirect: 'redir',
redir: function (target, room, user, connection) {
if (!target) return this.parse('/help redirect');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = this.splitTarget(target);
var targetUser = this.targetUser;
var targetRoom = Rooms.get(target) || Rooms.get(toId(target));
if (!targetRoom) {
return this.sendReply("The room '" + target + "' does not exist.");
}
if (!this.can('warn', targetUser, room) || !this.can('warn', targetUser, targetRoom)) return false;
if (!targetUser || !targetUser.connected) {
return this.sendReply("User " + this.targetUsername + " not found.");
}
if (Rooms.rooms[targetRoom.id].users[targetUser.userid]) {
return this.sendReply("User " + targetUser.name + " is already in the room " + target + "!");
}
if (!Rooms.rooms[room.id].users[targetUser.userid]) {
return this.sendReply("User " + this.targetUsername + " is not in the room " + room.id + ".");
}
if (targetUser.joinRoom(target) === false) return this.sendReply("User " + targetUser.name + " could not be joined to room " + target + ". They could be banned from the room.");
var roomName = (targetRoom.isPrivate)? "a private room" : "room " + targetRoom.title;
this.addModCommand("" + targetUser.name + " was redirected to " + roomName + " by " + user.name + ".");
targetUser.leaveRoom(room);
},
m: 'mute',
mute: function (target, room, user) {
if (!target) return this.parse('/help mute');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = this.splitTarget(target);
var targetUser = this.targetUser;
if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
if (target.length > MAX_REASON_LENGTH) {
return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
}
if (!this.can('mute', targetUser, room)) return false;
if (targetUser.mutedRooms[room.id] || targetUser.locked || !targetUser.connected) {
var problem = " but was already " + (!targetUser.connected ? "offline" : targetUser.locked ? "locked" : "muted");
if (!target) {
return this.privateModCommand("(" + targetUser.name + " would be muted by " + user.name + problem + ".)");
}
return this.addModCommand("" + targetUser.name + " would be muted by " + user.name + problem + "." + (target ? " (" + target + ")" : ""));
}
targetUser.popup("" + user.name + " has muted you for 7 minutes. " + target);
this.addModCommand("" + targetUser.name + " was muted by " + user.name + " for 7 minutes." + (target ? " (" + target + ")" : ""));
var alts = targetUser.getAlts();
if (alts.length) this.privateModCommand("(" + targetUser.name + "'s alts were also muted: " + alts.join(", ") + ")");
this.add('|unlink|' + this.getLastIdOf(targetUser));
targetUser.mute(room.id, 7 * 60 * 1000);
},
hm: 'hourmute',
hourmute: function (target, room, user) {
if (!target) return this.parse('/help hourmute');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = this.splitTarget(target);
var targetUser = this.targetUser;
if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
if (target.length > MAX_REASON_LENGTH) {
return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
}
if (!this.can('mute', targetUser, room)) return false;
if (((targetUser.mutedRooms[room.id] && (targetUser.muteDuration[room.id] || 0) >= 50 * 60 * 1000) || targetUser.locked) && !target) {
var problem = " but was already " + (!targetUser.connected ? "offline" : targetUser.locked ? "locked" : "muted");
return this.privateModCommand("(" + targetUser.name + " would be muted by " + user.name + problem + ".)");
}
targetUser.popup("" + user.name + " has muted you for 60 minutes. " + target);
this.addModCommand("" + targetUser.name + " was muted by " + user.name + " for 60 minutes." + (target ? " (" + target + ")" : ""));
var alts = targetUser.getAlts();
if (alts.length) this.privateModCommand("(" + targetUser.name + "'s alts were also muted: " + alts.join(", ") + ")");
this.add('|unlink|' + this.getLastIdOf(targetUser));
targetUser.mute(room.id, 60 * 60 * 1000, true);
},
um: 'unmute',
unmute: function (target, room, user) {
if (!target) return this.parse('/help unmute');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
var targetUser = Users.get(target);
if (!targetUser) return this.sendReply("User '" + target + "' does not exist.");
if (!this.can('mute', targetUser, room)) return false;
if (!targetUser.mutedRooms[room.id]) {
return this.sendReply("" + targetUser.name + " is not muted.");
}
this.addModCommand("" + targetUser.name + " was unmuted by " + user.name + ".");
targetUser.unmute(room.id);
},
l: 'lock',
ipmute: 'lock',
lock: function (target, room, user) {
if (!target) return this.parse('/help lock');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = this.splitTarget(target);
var targetUser = this.targetUser;
if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
if (target.length > MAX_REASON_LENGTH) {
return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
}
if (!this.can('lock', targetUser)) return false;
if ((targetUser.locked || Users.checkBanned(targetUser.latestIp)) && !target) {
var problem = " but was already " + (targetUser.locked ? "locked" : "banned");
return this.privateModCommand("(" + targetUser.name + " would be locked by " + user.name + problem + ".)");
}
targetUser.popup("" + user.name + " has locked you from talking in chats, battles, and PMing regular users.\n\n" + target + "\n\nIf you feel that your lock was unjustified, you can still PM staff members (%, @, &, and ~) to discuss it.");
this.addModCommand("" + targetUser.name + " was locked from talking by " + user.name + "." + (target ? " (" + target + ")" : ""));
var alts = targetUser.getAlts();
if (alts.length) this.privateModCommand("(" + targetUser.name + "'s alts were also locked: " + alts.join(", ") + ")");
this.add('|unlink|' + this.getLastIdOf(targetUser));
targetUser.lock();
},
unlock: function (target, room, user) {
if (!target) return this.parse('/help unlock');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
if (!this.can('lock')) return false;
var unlocked = Users.unlock(target);
if (unlocked) {
var names = Object.keys(unlocked);
this.addModCommand(names.join(", ") + " " +
((names.length > 1) ? "were" : "was") +
" unlocked by " + user.name + ".");
} else {
this.sendReply("User '" + target + "' is not locked.");
}
},
b: 'ban',
ban: function (target, room, user) {
if (!target) return this.parse('/help ban');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = this.splitTarget(target);
var targetUser = this.targetUser;
if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
if (target.length > MAX_REASON_LENGTH) {
return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
}
if (!this.can('ban', targetUser)) return false;
if (Users.checkBanned(targetUser.latestIp) && !target && !targetUser.connected) {
var problem = " but was already banned";
return this.privateModCommand("(" + targetUser.name + " would be banned by " + user.name + problem + ".)");
}
targetUser.popup("" + user.name + " has banned you." + (Config.appealurl ? (" If you feel that your banning was unjustified you can appeal the ban:\n" + Config.appealurl) : "") + "\n\n" + target);
this.addModCommand("" + targetUser.name + " was banned by " + user.name + "." + (target ? " (" + target + ")" : ""), " (" + targetUser.latestIp + ")");
var alts = targetUser.getAlts();
if (alts.length) {
this.privateModCommand("(" + targetUser.name + "'s alts were also banned: " + alts.join(", ") + ")");
for (var i = 0; i < alts.length; ++i) {
this.add('|unlink|' + toId(alts[i]));
}
}
this.add('|unlink|' + this.getLastIdOf(targetUser));
targetUser.ban();
},
unban: function (target, room, user) {
if (!target) return this.parse('/help unban');
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
if (!this.can('ban')) return false;
var name = Users.unban(target);
if (name) {
this.addModCommand("" + name + " was unbanned by " + user.name + ".");
} else {
this.sendReply("User '" + target + "' is not banned.");
}
},
unbanall: function (target, room, user) {
if (!this.can('rangeban')) return false;
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
// we have to do this the hard way since it's no longer a global
for (var i in Users.bannedIps) {
delete Users.bannedIps[i];
}
for (var i in Users.lockedIps) {
delete Users.lockedIps[i];
}
this.addModCommand("All bans and locks have been lifted by " + user.name + ".");
},
banip: function (target, room, user) {
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = target.trim();
if (!target) {
return this.parse('/help banip');
}
if (!this.can('rangeban')) return false;
if (Users.bannedIps[target] === '#ipban') return this.sendReply("The IP " + (target.charAt(target.length - 1) === '*' ? "range " : "") + target + " has already been temporarily banned.");
Users.bannedIps[target] = '#ipban';
this.addModCommand("" + user.name + " temporarily banned the " + (target.charAt(target.length - 1) === '*' ? "IP range" : "IP") + ": " + target);
},
unbanip: function (target, room, user) {
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
target = target.trim();
if (!target) {
return this.parse('/help unbanip');
}
if (!this.can('rangeban')) return false;
if (!Users.bannedIps[target]) {
return this.sendReply("" + target + " is not a banned IP or IP range.");
}
delete Users.bannedIps[target];
this.addModCommand("" + user.name + " unbanned the " + (target.charAt(target.length - 1) === '*' ? "IP range" : "IP") + ": " + target);
},
/*********************************************************
* Moderating: Other
*********************************************************/
modnote: function (target, room, user, connection, cmd) {
if (!target) return this.parse('/help modnote');
if (target.length > MAX_REASON_LENGTH) {
return this.sendReply("The note is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
}
if (!this.can('receiveauthmessages', null, room)) return false;
return this.privateModCommand("(" + user.name + " notes: " + target + ")");
},
demote: 'promote',
promote: function (target, room, user, connection, cmd) {
if (!target) return this.parse('/help promote');
target = this.splitTarget(target, true);
var targetUser = this.targetUser;
var userid = toId(this.targetUsername);
var name = targetUser ? targetUser.name : this.targetUsername;
if (!userid) return this.parse('/help promote');
var currentGroup = ((targetUser && targetUser.group) || Users.usergroups[userid] || ' ')[0];
var nextGroup = target ? target : Users.getNextGroupSymbol(currentGroup, cmd === 'demote', true);
if (target === 'deauth') nextGroup = Config.groupsranking[0];
if (!Config.groups[nextGroup]) {
return this.sendReply("Group '" + nextGroup + "' does not exist.");
}
if (Config.groups[nextGroup].roomonly) {
return this.sendReply("Group '" + nextGroup + "' does not exist as a global rank.");
}
var groupName = Config.groups[nextGroup].name || "regular user";
if (currentGroup === nextGroup) {
return this.sendReply("User '" + name + "' is already a " + groupName);
}
if (!user.canPromote(currentGroup, nextGroup)) {
return this.sendReply("/" + cmd + " - Access denied.");
}
if (!Users.setOfflineGroup(name, nextGroup)) {
return this.sendReply("/promote - WARNING: This user is offline and could be unregistered. Use /forcepromote if you're sure you want to risk it.");
}
if (Config.groups[nextGroup].rank < Config.groups[currentGroup].rank) {
this.privateModCommand("(" + name + " was demoted to " + groupName + " by " + user.name + ".)");
if (targetUser) targetUser.popup("You were demoted to " + groupName + " by " + user.name + ".");
} else {
this.addModCommand("" + name + " was promoted to " + groupName + " by " + user.name + ".");
}
if (targetUser) targetUser.updateIdentity();
},
forcepromote: function (target, room, user) {
// warning: never document this command in /help
if (!this.can('forcepromote')) return false;
target = this.splitTarget(target, true);
var name = this.targetUsername;
var nextGroup = target || Users.getNextGroupSymbol(' ', false);
if (!Users.setOfflineGroup(name, nextGroup, true)) {
return this.sendReply("/forcepromote - Don't forcepromote unless you have to.");
}
this.addModCommand("" + name + " was promoted to " + (Config.groups[nextGroup].name || "regular user") + " by " + user.name + ".");
},
deauth: function (target, room, user) {
return this.parse('/demote ' + target + ', deauth');
},
modchat: function (target, room, user) {
if (!target) return this.sendReply("Moderated chat is currently set to: " + room.modchat);
if (user.locked || user.mutedRooms[room.id]) return this.sendReply("You cannot do this while unable to talk.");
if (!this.can('modchat', null, room)) return false;
if (room.modchat && room.modchat.length <= 1 && Config.groupsranking.indexOf(room.modchat) > 1 && !user.can('modchatall', null, room)) {
return this.sendReply("/modchat - Access denied for removing a setting higher than " + Config.groupsranking[1] + ".");
}
target = target.toLowerCase();
var currentModchat = room.modchat;
switch (target) {
case 'off':
case 'false':
case 'no':
room.modchat = false;
break;
case 'ac':
case 'autoconfirmed':
room.modchat = 'autoconfirmed';
break;
case '*':
case 'player':
target = '\u2605';
// fallthrough
default:
if (!Config.groups[target]) {
return this.parse('/help modchat');
}
if (Config.groupsranking.indexOf(target) > 1 && !user.can('modchatall', null, room)) {
return this.sendReply("/modchat - Access denied for setting higher than " + Config.groupsranking[1] + ".");
}
room.modchat = target;
break;
}
if (currentModchat === room.modchat) {
return this.sendReply("Modchat is already set to " + currentModchat + ".");
}
if (!room.modchat) {
this.add("|raw|