Advertisement
Guest User

Untitled

a guest
Feb 1st, 2015
186
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 78.61 KB | None | 0 0
  1. /**
  2. * System commands
  3. * Pokemon Showdown - http://pokemonshowdown.com/
  4. *
  5. * These are system commands - commands required for Pokemon Showdown
  6. * to run. A lot of these are sent by the client.
  7. *
  8. * If you'd like to modify commands, please go to config/commands.js,
  9. * which also teaches you how to use commands.
  10. *
  11. * @license MIT license
  12. */
  13.  
  14. var crypto = require('crypto');
  15. var fs = require('fs');
  16.  
  17. const MAX_REASON_LENGTH = 300;
  18.  
  19. var commands = exports.commands = {
  20.  
  21. version: function (target, room, user) {
  22. if (!this.canBroadcast()) return;
  23. this.sendReplyBox("Server version: <b>" + CommandParser.package.version + "</b>");
  24. },
  25.  
  26. me: function (target, room, user, connection) {
  27. // By default, /me allows a blank message
  28. if (target) target = this.canTalk(target);
  29. if (!target) return;
  30.  
  31. return '/me ' + target;
  32. },
  33.  
  34. mee: function (target, room, user, connection) {
  35. // By default, /mee allows a blank message
  36. if (target) target = this.canTalk(target);
  37. if (!target) return;
  38.  
  39. return '/mee ' + target;
  40. },
  41.  
  42. avatar: function (target, room, user) {
  43. if (!target) return this.parse('/avatars');
  44. var parts = target.split(',');
  45. var avatar = parseInt(parts[0]);
  46. if (!avatar || avatar > 294 || avatar < 1) {
  47. if (!parts[1]) {
  48. this.sendReply("Invalid avatar.");
  49. }
  50. return false;
  51. }
  52.  
  53. user.avatar = avatar;
  54. if (!parts[1]) {
  55. this.sendReply("Avatar changed to:\n" +
  56. '|raw|<img src="//play.pokemonshowdown.com/sprites/trainers/' + avatar + '.png" alt="" width="80" height="80" />');
  57. }
  58. },
  59.  
  60. logout: function (target, room, user) {
  61. user.resetName();
  62. },
  63.  
  64. requesthelp: 'report',
  65. report: function (target, room, user) {
  66. this.sendReply("Use the Help room.");
  67. },
  68.  
  69. r: 'reply',
  70. reply: function (target, room, user) {
  71. if (!target) return this.parse('/help reply');
  72. if (!user.lastPM) {
  73. return this.sendReply("No one has PMed you yet.");
  74. }
  75. return this.parse('/msg ' + (user.lastPM || '') + ', ' + target);
  76. },
  77.  
  78. pm: 'msg',
  79. whisper: 'msg',
  80. w: 'msg',
  81. msg: function (target, room, user, connection) {
  82. if (!target) return this.parse('/help msg');
  83. target = this.splitTarget(target);
  84. var targetUser = this.targetUser;
  85. if (!target) {
  86. this.sendReply("You forgot the comma.");
  87. return this.parse('/help msg');
  88. }
  89. if (!targetUser || !targetUser.connected) {
  90. if (targetUser && !targetUser.connected) {
  91. this.popupReply("User " + this.targetUsername + " is offline.");
  92. } else if (!target) {
  93. this.popupReply("User " + this.targetUsername + " not found. Did you forget a comma?");
  94. } else {
  95. this.popupReply("User " + this.targetUsername + " not found. Did you misspell their name?");
  96. }
  97. return this.parse('/help msg');
  98. }
  99.  
  100. if (Config.pmmodchat) {
  101. var userGroup = user.group;
  102. if (Config.groupsranking.indexOf(userGroup) < Config.groupsranking.indexOf(Config.pmmodchat)) {
  103. var groupName = Config.groups[Config.pmmodchat].name || Config.pmmodchat;
  104. this.popupReply("Because moderated chat is set, you must be of rank " + groupName + " or higher to PM users.");
  105. return false;
  106. }
  107. }
  108.  
  109. if (user.locked && !targetUser.can('lock')) {
  110. return this.popupReply("You can only private message members of the moderation team (users marked by %, @, &, or ~) when locked.");
  111. }
  112. if (targetUser.locked && !user.can('lock')) {
  113. return this.popupReply("This user is locked and cannot PM.");
  114. }
  115. if (targetUser.ignorePMs && !user.can('lock')) {
  116. if (!targetUser.can('lock')) {
  117. return this.popupReply("This user is blocking Private Messages right now.");
  118. } else if (targetUser.can('bypassall')) {
  119. return this.popupReply("This admin is too busy to answer Private Messages right now. Please contact a different staff member.");
  120. }
  121. }
  122.  
  123. target = this.canTalk(target, null);
  124. if (!target) return false;
  125.  
  126. if (target.charAt(0) === '/' && target.charAt(1) !== '/') {
  127. // PM command
  128. var targetCmdIndex = target.indexOf(' ');
  129. var targetCmd = (targetCmdIndex >= 0 ? target.slice(1, targetCmdIndex) : target.slice(1));
  130. switch (targetCmd) {
  131. case 'me':
  132. case 'announce':
  133. break;
  134. case 'invite':
  135. var targetRoomid = toId(target.substr(8));
  136. if (targetRoomid === 'global') return false;
  137.  
  138. var targetRoom = Rooms.search(targetRoomid);
  139. if (!targetRoom) return connection.send('|pm|' + user.getIdentity() + '|' + targetUser.getIdentity() + '|/text The room "' + targetRoomid + '" does not exist.');
  140. if (targetRoom.staffRoom && !targetUser.isStaff) return connection.send('|pm|' + user.getIdentity() + '|' + targetUser.getIdentity() + '|/text User "' + this.targetUsername + '" requires global auth to join room "' + targetRoom.id + '".');
  141. if (targetRoom.isPrivate === true && targetRoom.modjoin && targetRoom.auth) {
  142. if (Config.groupsranking.indexOf(targetRoom.auth[targetUser.userid] || ' ') < Config.groupsranking.indexOf(targetRoom.modjoin) && !targetUser.can('bypassall')) {
  143. return connection.send('|pm|' + user.getIdentity() + '|' + targetUser.getIdentity() + '|/text The room "' + targetRoomid + '" does not exist.');
  144. }
  145. }
  146.  
  147. target = '/invite ' + targetRoom.id;
  148. break;
  149. default:
  150. return connection.send('|pm|' + user.getIdentity() + '|' + targetUser.getIdentity() + "|/text The command '/" + targetCmd + "' was unrecognized or unavailable in private messages. To send a message starting with '/" + targetCmd + "', type '//" + targetCmd + "'.");
  151. }
  152. }
  153.  
  154. var message = '|pm|' + user.getIdentity() + '|' + targetUser.getIdentity() + '|' + target;
  155. user.send(message);
  156. if (targetUser !== user) targetUser.send(message);
  157. targetUser.lastPM = user.userid;
  158. user.lastPM = targetUser.userid;
  159. },
  160.  
  161. blockpm: 'ignorepms',
  162. blockpms: 'ignorepms',
  163. ignorepm: 'ignorepms',
  164. ignorepms: function (target, room, user) {
  165. if (user.ignorePMs) return this.sendReply("You are already blocking Private Messages!");
  166. if (user.can('lock') && !user.can('bypassall')) return this.sendReply("You are not allowed to block Private Messages.");
  167. user.ignorePMs = true;
  168. return this.sendReply("You are now blocking Private Messages.");
  169. },
  170.  
  171. unblockpm: 'unignorepms',
  172. unblockpms: 'unignorepms',
  173. unignorepm: 'unignorepms',
  174. unignorepms: function (target, room, user) {
  175. if (!user.ignorePMs) return this.sendReply("You are not blocking Private Messages!");
  176. user.ignorePMs = false;
  177. return this.sendReply("You are no longer blocking Private Messages.");
  178. },
  179.  
  180. makechatroom: function (target, room, user) {
  181. if (!this.can('makeroom')) return;
  182. var id = toId(target);
  183. if (!id) return this.parse('/help makechatroom');
  184. if (Rooms.rooms[id]) return this.sendReply("The room '" + target + "' already exists.");
  185. if (Rooms.global.addChatRoom(target)) {
  186. return this.sendReply("The room '" + target + "' was created.");
  187. }
  188. return this.sendReply("An error occurred while trying to create the room '" + target + "'.");
  189. },
  190.  
  191. deregisterchatroom: function (target, room, user) {
  192. if (!this.can('makeroom')) return;
  193. var id = toId(target);
  194. if (!id) return this.parse('/help deregisterchatroom');
  195. var targetRoom = Rooms.search(id);
  196. if (!targetRoom) return this.sendReply("The room '" + target + "' doesn't exist.");
  197. target = targetRoom.title || targetRoom.id;
  198. if (Rooms.global.deregisterChatRoom(id)) {
  199. this.sendReply("The room '" + target + "' was deregistered.");
  200. this.sendReply("It will be deleted as of the next server restart.");
  201. return;
  202. }
  203. return this.sendReply("The room '" + target + "' isn't registered.");
  204. },
  205.  
  206. hideroom: 'privateroom',
  207. hiddenroom: 'privateroom',
  208. privateroom: function (target, room, user, connection, cmd) {
  209. var setting;
  210. switch (cmd) {
  211. case 'privateroom':
  212. if (!this.can('makeroom')) return;
  213. setting = true;
  214. break;
  215. default:
  216. if (!this.can('privateroom', null, room)) return;
  217. if (room.isPrivate === true) {
  218. if (this.can('makeroom'))
  219. this.sendReply("This room is a secret room. Use /privateroom to toggle instead.");
  220. return;
  221. }
  222. setting = 'hidden';
  223. break;
  224. }
  225.  
  226. if (target === 'off') {
  227. delete room.isPrivate;
  228. this.addModCommand("" + user.name + " made this room public.");
  229. if (room.chatRoomData) {
  230. delete room.chatRoomData.isPrivate;
  231. Rooms.global.writeChatRoomData();
  232. }
  233. } else {
  234. room.isPrivate = setting;
  235. this.addModCommand("" + user.name + " made this room " + (setting === true ? 'secret' : setting) + ".");
  236. if (room.chatRoomData) {
  237. room.chatRoomData.isPrivate = setting;
  238. Rooms.global.writeChatRoomData();
  239. }
  240. }
  241. },
  242.  
  243. modjoin: function (target, room, user) {
  244. if (!this.can('privateroom', null, room)) return;
  245. if (target === 'off' || target === 'false') {
  246. delete room.modjoin;
  247. this.addModCommand("" + user.name + " turned off modjoin.");
  248. if (room.chatRoomData) {
  249. delete room.chatRoomData.modjoin;
  250. Rooms.global.writeChatRoomData();
  251. }
  252. } else {
  253. if ((target === 'on' || target === 'true' || !target) || !user.can('privateroom')) {
  254. room.modjoin = true;
  255. this.addModCommand("" + user.name + " turned on modjoin.");
  256. } else if (target in Config.groups) {
  257. room.modjoin = target;
  258. this.addModCommand("" + user.name + " set modjoin to " + target + ".");
  259. } else {
  260. this.sendReply("Unrecognized modjoin setting.");
  261. return false;
  262. }
  263. if (room.chatRoomData) {
  264. room.chatRoomData.modjoin = true;
  265. Rooms.global.writeChatRoomData();
  266. }
  267. if (!room.modchat) this.parse('/modchat ' + Config.groupsranking[1]);
  268. if (!room.isPrivate) this.parse('/hiddenroom');
  269. }
  270. },
  271.  
  272. officialchatroom: 'officialroom',
  273. officialroom: function (target, room, user) {
  274. if (!this.can('makeroom')) return;
  275. if (!room.chatRoomData) {
  276. return this.sendReply("/officialroom - This room can't be made official");
  277. }
  278. if (target === 'off') {
  279. delete room.isOfficial;
  280. this.addModCommand("" + user.name + " made this chat room unofficial.");
  281. delete room.chatRoomData.isOfficial;
  282. Rooms.global.writeChatRoomData();
  283. } else {
  284. room.isOfficial = true;
  285. this.addModCommand("" + user.name + " made this chat room official.");
  286. room.chatRoomData.isOfficial = true;
  287. Rooms.global.writeChatRoomData();
  288. }
  289. },
  290.  
  291. roomdesc: function (target, room, user) {
  292. if (!target) {
  293. if (!this.canBroadcast()) return;
  294. var re = /(https?:\/\/(([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?))/g;
  295. if (!room.desc) return this.sendReply("This room does not have a description set.");
  296. this.sendReplyBox("The room description is: " + room.desc.replace(re, '<a href="$1">$1</a>'));
  297. return;
  298. }
  299. if (!this.can('declare')) return false;
  300. if (target.length > 80) return this.sendReply("Error: Room description is too long (must be at most 80 characters).");
  301. var normalizedTarget = ' ' + target.toLowerCase().replace('[^a-zA-Z0-9]+', ' ').trim() + ' ';
  302.  
  303. if (normalizedTarget.indexOf(' welcome ') >= 0) {
  304. return this.sendReply("Error: Room description must not contain the word 'welcome'.");
  305. }
  306. if (normalizedTarget.slice(0, 9) === ' discuss ') {
  307. return this.sendReply("Error: Room description must not start with the word 'discuss'.");
  308. }
  309. if (normalizedTarget.slice(0, 12) === ' talk about ' || normalizedTarget.slice(0, 17) === ' talk here about ') {
  310. return this.sendReply("Error: Room description must not start with the phrase 'talk about'.");
  311. }
  312.  
  313. room.desc = target;
  314. this.sendReply("(The room description is now: " + target + ")");
  315.  
  316. this.privateModCommand("(" + user.name + " changed the roomdesc to: \"" + target + "\".)");
  317.  
  318. if (room.chatRoomData) {
  319. room.chatRoomData.desc = room.desc;
  320. Rooms.global.writeChatRoomData();
  321. }
  322. },
  323.  
  324. roomintro: function (target, room, user) {
  325. if (!target) {
  326. if (!this.canBroadcast()) return;
  327. if (!room.introMessage) return this.sendReply("This room does not have an introduction set.");
  328. this.sendReplyBox(room.introMessage);
  329. if (!this.broadcasting && user.can('declare', null, room)) {
  330. this.sendReply('Source:');
  331. this.sendReplyBox('<code>' + Tools.escapeHTML(room.introMessage) + '</code>');
  332. }
  333. return;
  334. }
  335. if (!this.can('declare', null, room)) return false;
  336. if (!this.canHTML(target)) return;
  337. if (!/</.test(target)) {
  338. // not HTML, do some simple URL linking
  339. var re = /(https?:\/\/(([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?))/g;
  340. target = target.replace(re, '<a href="$1">$1</a>');
  341. }
  342.  
  343. if (!target.trim()) target = '';
  344. room.introMessage = target;
  345. this.sendReply("(The room introduction has been changed to:)");
  346. this.sendReplyBox(target);
  347.  
  348. this.privateModCommand("(" + user.name + " changed the roomintro.)");
  349.  
  350. if (room.chatRoomData) {
  351. room.chatRoomData.introMessage = room.introMessage;
  352. Rooms.global.writeChatRoomData();
  353. }
  354. },
  355.  
  356. roomalias: function (target, room, user) {
  357. if (!room.chatRoomData) return this.sendReply("This room isn't designed for aliases.");
  358. if (!target) {
  359. if (!room.chatRoomData.aliases || !room.chatRoomData.aliases.length) return this.sendReplyBox("This room does not have any aliases.");
  360. return this.sendReplyBox("This room has the following aliases: " + room.chatRoomData.aliases.join(", ") + "");
  361. }
  362. if (!this.can('setalias')) return false;
  363. var alias = toId(target);
  364. if (!alias.length) return this.sendReply("Only alphanumeric characters are valid in an alias.");
  365. if (Rooms.get(alias) || Rooms.aliases[alias]) return this.sendReply("You cannot set an alias to an existing room or alias.");
  366.  
  367. this.privateModCommand("(" + user.name + " added the room alias '" + target + "'.)");
  368.  
  369. if (!room.chatRoomData.aliases) room.chatRoomData.aliases = [];
  370. room.chatRoomData.aliases.push(alias);
  371. Rooms.aliases[alias] = room;
  372. Rooms.global.writeChatRoomData();
  373. },
  374.  
  375. removeroomalias: function (target, room, user) {
  376. if (!room.chatRoomData) return this.sendReply("This room isn't designed for aliases.");
  377. if (!room.chatRoomData.aliases) return this.sendReply("This room does not have any aliases.");
  378. if (!this.can('setalias')) return false;
  379. var alias = toId(target);
  380. if (!alias.length || !Rooms.aliases[alias]) return this.sendReply("Please specify an existing alias.");
  381. if (Rooms.aliases[alias] !== room) return this.sendReply("You may only remove an alias from the current room.");
  382.  
  383. this.privateModCommand("(" + user.name + " removed the room alias '" + target + "'.)");
  384.  
  385. var aliasIndex = room.chatRoomData.aliases.indexOf(alias);
  386. if (aliasIndex >= 0) {
  387. room.chatRoomData.aliases.splice(aliasIndex, 1);
  388. delete Rooms.aliases[alias];
  389. Rooms.global.writeChatRoomData();
  390. }
  391. },
  392.  
  393. roomowner: function (target, room, user) {
  394. if (!room.chatRoomData) {
  395. return this.sendReply("/roomowner - This room isn't designed for per-room moderation to be added");
  396. }
  397. target = this.splitTarget(target, true);
  398. var targetUser = this.targetUser;
  399.  
  400. if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' is not online.");
  401.  
  402. if (!this.can('makeroom', targetUser, room)) return false;
  403.  
  404. if (!room.auth) room.auth = room.chatRoomData.auth = {};
  405.  
  406. var name = targetUser.name;
  407.  
  408. room.auth[targetUser.userid] = '#';
  409. this.addModCommand("" + name + " was appointed Room Owner by " + user.name + ".");
  410. room.onUpdateIdentity(targetUser);
  411. Rooms.global.writeChatRoomData();
  412. },
  413.  
  414. roomdeowner: 'deroomowner',
  415. deroomowner: function (target, room, user) {
  416. if (!room.auth) {
  417. return this.sendReply("/roomdeowner - This room isn't designed for per-room moderation");
  418. }
  419. target = this.splitTarget(target, true);
  420. var targetUser = this.targetUser;
  421. var name = this.targetUsername;
  422. var userid = toId(name);
  423. if (!userid || userid === '') return this.sendReply("User '" + name + "' does not exist.");
  424.  
  425. if (room.auth[userid] !== '#') return this.sendReply("User '" + name + "' is not a room owner.");
  426. if (!this.can('makeroom', null, room)) return false;
  427.  
  428. delete room.auth[userid];
  429. this.sendReply("(" + name + " is no longer Room Owner.)");
  430. if (targetUser) targetUser.updateIdentity();
  431. if (room.chatRoomData) {
  432. Rooms.global.writeChatRoomData();
  433. }
  434. },
  435.  
  436. roomdemote: 'roompromote',
  437. roompromote: function (target, room, user, connection, cmd) {
  438. if (!room.auth) {
  439. this.sendReply("/roompromote - This room isn't designed for per-room moderation");
  440. return this.sendReply("Before setting room mods, you need to set it up with /roomowner");
  441. }
  442. if (!target) return this.parse('/help roompromote');
  443.  
  444. target = this.splitTarget(target, true);
  445. var targetUser = this.targetUser;
  446. var userid = toId(this.targetUsername);
  447. var name = targetUser ? targetUser.name : this.targetUsername;
  448.  
  449. if (!userid) return this.parse('/help roompromote');
  450. if (!targetUser && (!room.auth || !room.auth[userid])) {
  451. return this.sendReply("User '" + name + "' is offline and unauthed, and so can't be promoted.");
  452. }
  453.  
  454. var currentGroup = ((room.auth && room.auth[userid]) || ' ')[0];
  455. var nextGroup = target || Users.getNextGroupSymbol(currentGroup, cmd === 'roomdemote', true);
  456. if (target === 'deauth') nextGroup = Config.groupsranking[0];
  457. if (!Config.groups[nextGroup]) {
  458. return this.sendReply("Group '" + nextGroup + "' does not exist.");
  459. }
  460.  
  461. if (Config.groups[nextGroup].globalonly) {
  462. return this.sendReply("Group 'room" + Config.groups[nextGroup].id + "' does not exist as a room rank.");
  463. }
  464.  
  465. var groupName = Config.groups[nextGroup].name || "regular user";
  466. if (currentGroup === nextGroup) {
  467. return this.sendReply("User '" + name + "' is already a " + groupName + " in this room.");
  468. }
  469. if (currentGroup !== ' ' && !user.can('room' + (Config.groups[currentGroup] ? Config.groups[currentGroup].id : 'voice'), null, room)) {
  470. return this.sendReply("/" + cmd + " - Access denied for promoting from " + (Config.groups[currentGroup] ? Config.groups[currentGroup].name : "an undefined group") + ".");
  471. }
  472. if (nextGroup !== ' ' && !user.can('room' + Config.groups[nextGroup].id, null, room)) {
  473. return this.sendReply("/" + cmd + " - Access denied for promoting to " + Config.groups[nextGroup].name + ".");
  474. }
  475.  
  476. if (nextGroup === ' ') {
  477. delete room.auth[userid];
  478. } else {
  479. room.auth[userid] = nextGroup;
  480. }
  481.  
  482. if (Config.groups[nextGroup].rank < Config.groups[currentGroup].rank) {
  483. this.privateModCommand("(" + name + " was demoted to Room " + groupName + " by " + user.name + ".)");
  484. if (targetUser && Rooms.rooms[room.id].users[targetUser.userid]) targetUser.popup("You were demoted to Room " + groupName + " by " + user.name + ".");
  485. } else if (nextGroup === '#') {
  486. this.addModCommand("" + name + " was promoted to " + groupName + " by " + user.name + ".");
  487. } else {
  488. this.addModCommand("" + name + " was promoted to Room " + groupName + " by " + user.name + ".");
  489. }
  490.  
  491. if (targetUser) targetUser.updateIdentity();
  492. if (room.chatRoomData) Rooms.global.writeChatRoomData();
  493. },
  494.  
  495. roomauth: function (target, room, user, connection) {
  496. var targetRoom = room;
  497. if (target) targetRoom = Rooms.search(target);
  498. if (!targetRoom || (targetRoom !== room && targetRoom.modjoin && !user.can('bypassall'))) return this.sendReply("The room '" + target + "' does not exist.");
  499. if (!targetRoom.auth) return this.sendReply("/roomauth - The room '" + (targetRoom.title ? targetRoom.title : target) + "' isn't designed for per-room moderation and therefore has no auth list.");
  500.  
  501. var rankLists = {};
  502. for (var u in targetRoom.auth) {
  503. if (!rankLists[targetRoom.auth[u]]) rankLists[targetRoom.auth[u]] = [];
  504. rankLists[targetRoom.auth[u]].push(u);
  505. }
  506.  
  507. var buffer = [];
  508. Object.keys(rankLists).sort(function (a, b) {
  509. return (Config.groups[b] || {rank:0}).rank - (Config.groups[a] || {rank:0}).rank;
  510. }).forEach(function (r) {
  511. buffer.push((Config.groups[r] ? Config.groups[r] .name + "s (" + r + ")" : r) + ":\n" + rankLists[r].sort().join(", "));
  512. });
  513.  
  514. if (!buffer.length) {
  515. connection.popup("The room '" + targetRoom.title + "' has no auth.");
  516. return;
  517. }
  518. if (targetRoom !== room) buffer.unshift("" + targetRoom.title + " room auth:");
  519. connection.popup(buffer.join("\n\n"));
  520. },
  521.  
  522. userauth: function (target, room, user, connection) {
  523. var targetId = toId(target) || user.userid;
  524. var targetUser = Users.getExact(targetId);
  525. var targetUsername = (targetUser ? targetUser.name : target);
  526.  
  527. var buffer = [];
  528. var innerBuffer = [];
  529. var group = Users.usergroups[targetId];
  530. if (group) {
  531. buffer.push('Global auth: ' + group.charAt(0));
  532. }
  533. for (var i = 0; i < Rooms.global.chatRooms.length; i++) {
  534. var curRoom = Rooms.global.chatRooms[i];
  535. if (!curRoom.auth || curRoom.isPrivate) continue;
  536. group = curRoom.auth[targetId];
  537. if (!group) continue;
  538. innerBuffer.push(group + curRoom.id);
  539. }
  540. if (innerBuffer.length) {
  541. buffer.push('Room auth: ' + innerBuffer.join(', '));
  542. }
  543. if (targetId === user.userid || user.can('makeroom')) {
  544. innerBuffer = [];
  545. for (var i = 0; i < Rooms.global.chatRooms.length; i++) {
  546. var curRoom = Rooms.global.chatRooms[i];
  547. if (!curRoom.auth || !curRoom.isPrivate) continue;
  548. var auth = curRoom.auth[targetId];
  549. if (!auth) continue;
  550. innerBuffer.push(auth + curRoom.id);
  551. }
  552. if (innerBuffer.length) {
  553. buffer.push('Private room auth: ' + innerBuffer.join(', '));
  554. }
  555. }
  556. if (!buffer.length) {
  557. buffer.push("No global or room auth.");
  558. }
  559.  
  560. buffer.unshift("" + targetUsername + " user auth:");
  561. connection.popup(buffer.join("\n\n"));
  562. },
  563.  
  564. rb: 'roomban',
  565. roomban: function (target, room, user, connection) {
  566. if (!target) return this.parse('/help roomban');
  567. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  568.  
  569. target = this.splitTarget(target, true);
  570. var targetUser = this.targetUser;
  571. var name = this.targetUsername;
  572. var userid = toId(name);
  573.  
  574. if (!userid || !targetUser) return this.sendReply("User '" + name + "' does not exist.");
  575. if (!this.can('ban', targetUser, room)) return false;
  576. if (!room.bannedUsers || !room.bannedIps) {
  577. return this.sendReply("Room bans are not meant to be used in room " + room.id + ".");
  578. }
  579. if (room.bannedUsers[userid] || room.bannedIps[targetUser.latestIp]) return this.sendReply("User " + targetUser.name + " is already banned from room " + room.id + ".");
  580. room.bannedUsers[userid] = true;
  581. for (var ip in targetUser.ips) {
  582. room.bannedIps[ip] = true;
  583. }
  584. targetUser.popup("" + user.name + " has banned you from the room " + room.id + "." + (target ? "\n\nReason: " + target + "" : "") + "\n\nTo appeal the ban, PM the staff member that banned you or a room owner. If you are unsure who the room owners are, type this into any room: /roomauth " + room.id);
  585. this.addModCommand("" + targetUser.name + " was banned from room " + room.id + " by " + user.name + "." + (target ? " (" + target + ")" : ""));
  586. var alts = targetUser.getAlts();
  587. if (alts.length) {
  588. this.privateModCommand("(" + targetUser.name + "'s alts were also banned from room " + room.id + ": " + alts.join(", ") + ")");
  589. for (var i = 0; i < alts.length; ++i) {
  590. var altId = toId(alts[i]);
  591. this.add('|unlink|' + altId);
  592. room.bannedUsers[altId] = true;
  593. Users.getExact(altId).leaveRoom(room.id);
  594. }
  595. }
  596. this.add('|unlink|' + this.getLastIdOf(targetUser));
  597. if (!targetUser.can('bypassall')) targetUser.leaveRoom(room.id);
  598. },
  599.  
  600. unroomban: 'roomunban',
  601. roomunban: function (target, room, user, connection) {
  602. if (!target) return this.parse('/help roomunban');
  603. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  604.  
  605. target = this.splitTarget(target, true);
  606. var targetUser = this.targetUser;
  607. var name = this.targetUsername;
  608. var userid = toId(name);
  609. var success;
  610.  
  611. if (!userid || !targetUser) return this.sendReply("User '" + name + "' does not exist.");
  612. if (!this.can('ban', targetUser, room)) return false;
  613. if (!room.bannedUsers || !room.bannedIps) {
  614. return this.sendReply("Room bans are not meant to be used in room " + room.id + ".");
  615. }
  616. if (room.bannedUsers[userid]) {
  617. delete room.bannedUsers[userid];
  618. success = true;
  619. }
  620. for (var ip in targetUser.ips) {
  621. if (room.bannedIps[ip]) {
  622. delete room.bannedIps[ip];
  623. success = true;
  624. }
  625. }
  626. if (!success) return this.sendReply("User " + targetUser.name + " is not banned from room " + room.id + ".");
  627.  
  628. targetUser.popup("" + user.name + " has unbanned you from the room " + room.id + ".");
  629. this.addModCommand("" + targetUser.name + " was unbanned from room " + room.id + " by " + user.name + ".");
  630. var alts = targetUser.getAlts();
  631. if (!alts.length) return;
  632. for (var i = 0; i < alts.length; ++i) {
  633. var altId = toId(alts[i]);
  634. if (room.bannedUsers[altId]) delete room.bannedUsers[altId];
  635. }
  636. this.privateModCommand("(" + targetUser.name + "'s alts were also unbanned from room " + room.id + ": " + alts.join(", ") + ")");
  637. },
  638.  
  639. autojoin: function (target, room, user, connection) {
  640. Rooms.global.autojoinRooms(user, connection);
  641. },
  642.  
  643. joim: 'join',
  644. join: function (target, room, user, connection) {
  645. if (!target) return false;
  646. var targetRoom = Rooms.search(target);
  647. if (!targetRoom) {
  648. return connection.sendTo(target, "|noinit|nonexistent|The room '" + target + "' does not exist.");
  649. }
  650. if (targetRoom.isPrivate) {
  651. if (targetRoom.modjoin && !user.can('bypassall')) {
  652. var userGroup = user.group;
  653. if (targetRoom.auth) {
  654. if (targetRoom.isPrivate === true) {
  655. userGroup = ' ';
  656. }
  657. userGroup = targetRoom.auth[user.userid] || userGroup;
  658. }
  659. if (Config.groupsranking.indexOf(userGroup) < Config.groupsranking.indexOf(targetRoom.modjoin !== true ? targetRoom.modjoin : targetRoom.modchat)) {
  660. return connection.sendTo(target, "|noinit|nonexistent|The room '" + target + "' does not exist.");
  661. }
  662. }
  663. if (!user.named) {
  664. return connection.sendTo(target, "|noinit|namerequired|You must have a name in order to join the room '" + target + "'.");
  665. }
  666. }
  667.  
  668. var joinResult = user.joinRoom(targetRoom, connection);
  669. if (!joinResult) {
  670. if (joinResult === null) {
  671. return connection.sendTo(target, "|noinit|joinfailed|You are banned from the room '" + target + "'.");
  672. }
  673. return connection.sendTo(target, "|noinit|joinfailed|You do not have permission to join '" + target + "'.");
  674. }
  675. },
  676.  
  677. leave: 'part',
  678. part: function (target, room, user, connection) {
  679. if (room.id === 'global') return false;
  680. var targetRoom = Rooms.search(target);
  681. if (target && !targetRoom) {
  682. return this.sendReply("The room '" + target + "' does not exist.");
  683. }
  684. user.leaveRoom(targetRoom || room, connection);
  685. },
  686.  
  687. /*********************************************************
  688. * Moderating: Punishments
  689. *********************************************************/
  690.  
  691. kick: 'warn',
  692. k: 'warn',
  693. warn: function (target, room, user) {
  694. if (!target) return this.parse('/help warn');
  695. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  696.  
  697. target = this.splitTarget(target);
  698. var targetUser = this.targetUser;
  699. if (!targetUser || !targetUser.connected) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
  700. if (room.isPrivate === true && room.auth) {
  701. return this.sendReply("You can't warn here: This is a privately-owned room not subject to global rules.");
  702. }
  703. if (!Rooms.rooms[room.id].users[targetUser.userid]) {
  704. return this.sendReply("User " + this.targetUsername + " is not in the room " + room.id + ".");
  705. }
  706. if (target.length > MAX_REASON_LENGTH) {
  707. return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
  708. }
  709. if (!this.can('warn', targetUser, room)) return false;
  710.  
  711. this.addModCommand("" + targetUser.name + " was warned by " + user.name + "." + (target ? " (" + target + ")" : ""));
  712. targetUser.send('|c|~|/warn ' + target);
  713. this.add('|unlink|' + this.getLastIdOf(targetUser));
  714. },
  715.  
  716. redirect: 'redir',
  717. redir: function (target, room, user, connection) {
  718. if (!target) return this.parse('/help redirect');
  719. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  720. target = this.splitTarget(target);
  721. var targetUser = this.targetUser;
  722. var targetRoom = Rooms.search(target);
  723. if (!targetRoom) {
  724. return this.sendReply("The room '" + target + "' does not exist.");
  725. }
  726. if (!this.can('warn', targetUser, room) || !this.can('warn', targetUser, targetRoom)) return false;
  727. if (!targetUser || !targetUser.connected) {
  728. return this.sendReply("User " + this.targetUsername + " not found.");
  729. }
  730. if (Rooms.rooms[targetRoom.id].users[targetUser.userid]) {
  731. return this.sendReply("User " + targetUser.name + " is already in the room " + targetRoom.title + "!");
  732. }
  733. if (!Rooms.rooms[room.id].users[targetUser.userid]) {
  734. return this.sendReply("User " + this.targetUsername + " is not in the room " + room.id + ".");
  735. }
  736. if (targetUser.joinRoom(targetRoom.id) === false) return this.sendReply("User " + targetUser.name + " could not be joined to room " + targetRoom.title + ". They could be banned from the room.");
  737. var roomName = (targetRoom.isPrivate) ? "a private room" : "room " + targetRoom.title;
  738. this.addModCommand("" + targetUser.name + " was redirected to " + roomName + " by " + user.name + ".");
  739. targetUser.leaveRoom(room);
  740. },
  741.  
  742. m: 'mute',
  743. mute: function (target, room, user) {
  744. if (!target) return this.parse('/help mute');
  745. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  746.  
  747. target = this.splitTarget(target);
  748. var targetUser = this.targetUser;
  749. if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
  750. if (target.length > MAX_REASON_LENGTH) {
  751. return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
  752. }
  753. if (!this.can('mute', targetUser, room)) return false;
  754. if (targetUser.mutedRooms[room.id] || targetUser.locked || !targetUser.connected) {
  755. var problem = " but was already " + (!targetUser.connected ? "offline" : targetUser.locked ? "locked" : "muted");
  756. if (!target) {
  757. return this.privateModCommand("(" + targetUser.name + " would be muted by " + user.name + problem + ".)");
  758. }
  759. return this.addModCommand("" + targetUser.name + " would be muted by " + user.name + problem + "." + (target ? " (" + target + ")" : ""));
  760. }
  761.  
  762. targetUser.popup("" + user.name + " has muted you for 7 minutes. " + (target ? "\n\nReason: " + target : ""));
  763. this.addModCommand("" + targetUser.name + " was muted by " + user.name + " for 7 minutes." + (target ? " (" + target + ")" : ""));
  764. var alts = targetUser.getAlts();
  765. if (alts.length) this.privateModCommand("(" + targetUser.name + "'s alts were also muted: " + alts.join(", ") + ")");
  766. this.add('|unlink|' + this.getLastIdOf(targetUser));
  767.  
  768. targetUser.mute(room.id, 7 * 60 * 1000);
  769. },
  770.  
  771. hm: 'hourmute',
  772. hourmute: function (target, room, user) {
  773. if (!target) return this.parse('/help hourmute');
  774. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  775.  
  776. target = this.splitTarget(target);
  777. var targetUser = this.targetUser;
  778. if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
  779. if (target.length > MAX_REASON_LENGTH) {
  780. return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
  781. }
  782. if (!this.can('mute', targetUser, room)) return false;
  783.  
  784. if (((targetUser.mutedRooms[room.id] && (targetUser.muteDuration[room.id] || 0) >= 50 * 60 * 1000) || targetUser.locked) && !target) {
  785. var problem = " but was already " + (!targetUser.connected ? "offline" : targetUser.locked ? "locked" : "muted");
  786. return this.privateModCommand("(" + targetUser.name + " would be muted by " + user.name + problem + ".)");
  787. }
  788.  
  789. targetUser.popup("" + user.name + " has muted you for 60 minutes. " + (target ? "\n\nReason: " + target : ""));
  790. this.addModCommand("" + targetUser.name + " was muted by " + user.name + " for 60 minutes." + (target ? " (" + target + ")" : ""));
  791. var alts = targetUser.getAlts();
  792. if (alts.length) this.privateModCommand("(" + targetUser.name + "'s alts were also muted: " + alts.join(", ") + ")");
  793. this.add('|unlink|' + this.getLastIdOf(targetUser));
  794.  
  795. targetUser.mute(room.id, 60 * 60 * 1000, true);
  796. },
  797.  
  798. um: 'unmute',
  799. unmute: function (target, room, user) {
  800. if (!target) return this.parse('/help unmute');
  801. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  802. var targetUser = Users.get(target);
  803. if (!targetUser) return this.sendReply("User '" + target + "' does not exist.");
  804. if (!this.can('mute', targetUser, room)) return false;
  805.  
  806. if (!targetUser.mutedRooms[room.id]) {
  807. return this.sendReply("" + targetUser.name + " is not muted.");
  808. }
  809.  
  810. this.addModCommand("" + targetUser.name + " was unmuted by " + user.name + ".");
  811.  
  812. targetUser.unmute(room.id);
  813. },
  814.  
  815. l: 'lock',
  816. ipmute: 'lock',
  817. lock: function (target, room, user) {
  818. if (!target) return this.parse('/help lock');
  819. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  820.  
  821. target = this.splitTarget(target);
  822. var targetUser = this.targetUser;
  823. if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
  824. if (target.length > MAX_REASON_LENGTH) {
  825. return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
  826. }
  827. if (!this.can('lock', targetUser)) return false;
  828.  
  829. if ((targetUser.locked || Users.checkBanned(targetUser.latestIp)) && !target) {
  830. var problem = " but was already " + (targetUser.locked ? "locked" : "banned");
  831. return this.privateModCommand("(" + targetUser.name + " would be locked by " + user.name + problem + ".)");
  832. }
  833.  
  834. targetUser.popup("" + user.name + " has locked you from talking in chats, battles, and PMing regular users." + (target ? "\n\nReason: " + target : "") + "\n\nIf you feel that your lock was unjustified, you can still PM staff members (%, @, &, and ~) to discuss it" + (Config.appealurl ? " or you can appeal:\n" + Config.appealurl : ".") + "\n\nYour lock will expire in a few days.");
  835.  
  836. this.addModCommand("" + targetUser.name + " was locked from talking by " + user.name + "." + (target ? " (" + target + ")" : ""));
  837. var alts = targetUser.getAlts();
  838. var acAccount = (targetUser.autoconfirmed !== targetUser.userid && targetUser.autoconfirmed);
  839. if (alts.length) {
  840. this.privateModCommand("(" + targetUser.name + "'s " + (acAccount ? " ac account: " + acAccount + ", " : "") + "locked alts: " + alts.join(", ") + ")");
  841. } else if (acAccount) {
  842. this.privateModCommand("(" + targetUser.name + "'s ac account: " + acAccount + ")");
  843. }
  844. this.add('|unlink|hide|' + this.getLastIdOf(targetUser));
  845.  
  846. targetUser.lock();
  847. },
  848.  
  849. unlock: function (target, room, user) {
  850. if (!target) return this.parse('/help unlock');
  851. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  852. if (!this.can('lock')) return false;
  853.  
  854. var unlocked = Users.unlock(target);
  855.  
  856. if (unlocked) {
  857. var names = Object.keys(unlocked);
  858. this.addModCommand(names.join(", ") + " " +
  859. ((names.length > 1) ? "were" : "was") +
  860. " unlocked by " + user.name + ".");
  861. } else {
  862. this.sendReply("User '" + target + "' is not locked.");
  863. }
  864. },
  865.  
  866. b: 'ban',
  867. ban: function (target, room, user) {
  868. if (!target) return this.parse('/help ban');
  869. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  870.  
  871. target = this.splitTarget(target);
  872. var targetUser = this.targetUser;
  873. if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' does not exist.");
  874. if (target.length > MAX_REASON_LENGTH) {
  875. return this.sendReply("The reason is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
  876. }
  877. if (!this.can('ban', targetUser)) return false;
  878.  
  879. if (Users.checkBanned(targetUser.latestIp) && !target && !targetUser.connected) {
  880. var problem = " but was already banned";
  881. return this.privateModCommand("(" + targetUser.name + " would be banned by " + user.name + problem + ".)");
  882. }
  883.  
  884. targetUser.popup("" + user.name + " has banned you." + (target ? "\n\nReason: " + target : "") + (Config.appealurl ? "\n\nIf you feel that your ban was unjustified, you can appeal:\n" + Config.appealurl : "") + "\n\nYour ban will expire in a few days.");
  885.  
  886. this.addModCommand("" + targetUser.name + " was banned by " + user.name + "." + (target ? " (" + target + ")" : ""), " (" + targetUser.latestIp + ")");
  887. var alts = targetUser.getAlts();
  888. var acAccount = (targetUser.autoconfirmed !== targetUser.userid && targetUser.autoconfirmed);
  889. if (alts.length) {
  890. this.privateModCommand("(" + targetUser.name + "'s " + (acAccount ? " ac account: " + acAccount + ", " : "") + "banned alts: " + alts.join(", ") + ")");
  891. for (var i = 0; i < alts.length; ++i) {
  892. this.add('|unlink|' + toId(alts[i]));
  893. }
  894. } else if (acAccount) {
  895. this.privateModCommand("(" + targetUser.name + "'s ac account: " + acAccount + ")");
  896. }
  897.  
  898. this.add('|unlink|hide|' + this.getLastIdOf(targetUser));
  899. targetUser.ban();
  900. },
  901.  
  902. unban: function (target, room, user) {
  903. if (!target) return this.parse('/help unban');
  904. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  905. if (!this.can('ban')) return false;
  906.  
  907. var name = Users.unban(target);
  908.  
  909. if (name) {
  910. this.addModCommand("" + name + " was unbanned by " + user.name + ".");
  911. } else {
  912. this.sendReply("User '" + target + "' is not banned.");
  913. }
  914. },
  915.  
  916. unbanall: function (target, room, user) {
  917. if (!this.can('rangeban')) return false;
  918. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  919. // we have to do this the hard way since it's no longer a global
  920. for (var i in Users.bannedIps) {
  921. delete Users.bannedIps[i];
  922. }
  923. for (var i in Users.lockedIps) {
  924. delete Users.lockedIps[i];
  925. }
  926. this.addModCommand("All bans and locks have been lifted by " + user.name + ".");
  927. },
  928.  
  929. banip: function (target, room, user) {
  930. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  931. target = target.trim();
  932. if (!target) {
  933. return this.parse('/help banip');
  934. }
  935. if (!this.can('rangeban')) return false;
  936. if (Users.bannedIps[target] === '#ipban') return this.sendReply("The IP " + (target.charAt(target.length - 1) === '*' ? "range " : "") + target + " has already been temporarily banned.");
  937.  
  938. Users.bannedIps[target] = '#ipban';
  939. this.addModCommand("" + user.name + " temporarily banned the " + (target.charAt(target.length - 1) === '*' ? "IP range" : "IP") + ": " + target);
  940. },
  941.  
  942. unbanip: function (target, room, user) {
  943. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  944. target = target.trim();
  945. if (!target) {
  946. return this.parse('/help unbanip');
  947. }
  948. if (!this.can('rangeban')) return false;
  949. if (!Users.bannedIps[target]) {
  950. return this.sendReply("" + target + " is not a banned IP or IP range.");
  951. }
  952. delete Users.bannedIps[target];
  953. this.addModCommand("" + user.name + " unbanned the " + (target.charAt(target.length - 1) === '*' ? "IP range" : "IP") + ": " + target);
  954. },
  955.  
  956. rangelock: function (target, room, user) {
  957. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  958. if (!target) return this.sendReply("Please specify a range to lock.");
  959. if (!this.can('rangeban')) return false;
  960.  
  961. var isIp = (target.slice(-1) === '*' ? true : false);
  962. var range = (isIp ? target : Users.shortenHost(target));
  963. if (Users.lockedRanges[range]) return this.sendReply("The range " + range + " has already been temporarily locked.");
  964.  
  965. Users.lockRange(range, isIp);
  966. this.addModCommand("" + user.name + " temporarily locked the range " + range + ".");
  967. },
  968.  
  969. rangeunlock: function (target, room, user) {
  970. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  971. if (!target) return this.sendReply("Please specify a range to unlock.");
  972. if (!this.can('rangeban')) return false;
  973.  
  974. var range = (target.slice(-1) === '*' ? target : Users.shortenHost(target));
  975. if (!Users.lockedRanges[range]) return this.sendReply("The range " + range + " is not locked.");
  976.  
  977. Users.unlockRange(range);
  978. this.addModCommand("" + user.name + " unlocked the range " + range + ".");
  979. },
  980.  
  981. /*********************************************************
  982. * Moderating: Other
  983. *********************************************************/
  984.  
  985. mn: 'modnote',
  986. modnote: function (target, room, user, connection) {
  987. if (!target) return this.parse('/help modnote');
  988. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  989.  
  990. if (target.length > MAX_REASON_LENGTH) {
  991. return this.sendReply("The note is too long. It cannot exceed " + MAX_REASON_LENGTH + " characters.");
  992. }
  993. if (!this.can('receiveauthmessages', null, room)) return false;
  994. return this.privateModCommand("(" + user.name + " notes: " + target + ")");
  995. },
  996.  
  997. globaldemote: 'promote',
  998. globalpromote: 'promote',
  999. demote: 'promote',
  1000. promote: function (target, room, user, connection, cmd) {
  1001. if (!target) return this.parse('/help promote');
  1002.  
  1003. target = this.splitTarget(target, true);
  1004. var targetUser = this.targetUser;
  1005. var userid = toId(this.targetUsername);
  1006. var name = targetUser ? targetUser.name : this.targetUsername;
  1007.  
  1008. if (!userid) return this.parse('/help promote');
  1009.  
  1010. var currentGroup = ((targetUser && targetUser.group) || Users.usergroups[userid] || ' ')[0];
  1011. var nextGroup = target ? target : Users.getNextGroupSymbol(currentGroup, cmd === 'demote', true);
  1012. if (target === 'deauth') nextGroup = Config.groupsranking[0];
  1013. if (!Config.groups[nextGroup]) {
  1014. return this.sendReply("Group '" + nextGroup + "' does not exist.");
  1015. }
  1016. if (Config.groups[nextGroup].roomonly) {
  1017. return this.sendReply("Group '" + nextGroup + "' does not exist as a global rank.");
  1018. }
  1019.  
  1020. var groupName = Config.groups[nextGroup].name || "regular user";
  1021. if (currentGroup === nextGroup) {
  1022. return this.sendReply("User '" + name + "' is already a " + groupName);
  1023. }
  1024. if (!user.canPromote(currentGroup, nextGroup)) {
  1025. return this.sendReply("/" + cmd + " - Access denied.");
  1026. }
  1027.  
  1028. if (!Users.setOfflineGroup(name, nextGroup)) {
  1029. return this.sendReply("/promote - WARNING: This user is offline and could be unregistered. Use /forcepromote if you're sure you want to risk it.");
  1030. }
  1031. if (Config.groups[nextGroup].rank < Config.groups[currentGroup].rank) {
  1032. this.privateModCommand("(" + name + " was demoted to " + groupName + " by " + user.name + ".)");
  1033. if (targetUser) targetUser.popup("You were demoted to " + groupName + " by " + user.name + ".");
  1034. } else {
  1035. this.addModCommand("" + name + " was promoted to " + groupName + " by " + user.name + ".");
  1036. }
  1037.  
  1038. if (targetUser) targetUser.updateIdentity();
  1039. },
  1040.  
  1041. forcepromote: function (target, room, user) {
  1042. // warning: never document this command in /help
  1043. if (!this.can('forcepromote')) return false;
  1044. target = this.splitTarget(target, true);
  1045. var name = this.targetUsername;
  1046. var nextGroup = target || Users.getNextGroupSymbol(' ', false);
  1047. if (!Config.groups[nextGroup]) return this.sendReply("Group '" + nextGroup + "' does not exist.");
  1048.  
  1049. if (!Users.setOfflineGroup(name, nextGroup, true)) {
  1050. return this.sendReply("/forcepromote - Don't forcepromote unless you have to.");
  1051. }
  1052.  
  1053. this.addModCommand("" + name + " was promoted to " + (Config.groups[nextGroup].name || "regular user") + " by " + user.name + ".");
  1054. },
  1055.  
  1056. deauth: function (target, room, user) {
  1057. return this.parse('/demote ' + target + ', deauth');
  1058. },
  1059.  
  1060. deroomauth: 'roomdeauth',
  1061. roomdeauth: function (target, room, user) {
  1062. return this.parse('/roomdemote ' + target + ', deauth');
  1063. },
  1064.  
  1065. modchat: function (target, room, user) {
  1066. if (!target) return this.sendReply("Moderated chat is currently set to: " + room.modchat);
  1067. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  1068. if (!this.can('modchat', null, room)) return false;
  1069.  
  1070. if (room.modchat && room.modchat.length <= 1 && Config.groupsranking.indexOf(room.modchat) > 1 && !user.can('modchatall', null, room)) {
  1071. return this.sendReply("/modchat - Access denied for removing a setting higher than " + Config.groupsranking[1] + ".");
  1072. }
  1073.  
  1074. target = target.toLowerCase();
  1075. var currentModchat = room.modchat;
  1076. switch (target) {
  1077. case 'off':
  1078. case 'false':
  1079. case 'no':
  1080. case ' ':
  1081. room.modchat = false;
  1082. break;
  1083. case 'ac':
  1084. case 'autoconfirmed':
  1085. room.modchat = 'autoconfirmed';
  1086. break;
  1087. case '*':
  1088. case 'player':
  1089. target = '\u2605';
  1090. /* falls through */
  1091. default:
  1092. if (!Config.groups[target]) {
  1093. return this.parse('/help modchat');
  1094. }
  1095. if (Config.groupsranking.indexOf(target) > 1 && !user.can('modchatall', null, room)) {
  1096. return this.sendReply("/modchat - Access denied for setting higher than " + Config.groupsranking[1] + ".");
  1097. }
  1098. room.modchat = target;
  1099. break;
  1100. }
  1101. if (currentModchat === room.modchat) {
  1102. return this.sendReply("Modchat is already set to " + currentModchat + ".");
  1103. }
  1104. if (!room.modchat) {
  1105. this.add("|raw|<div class=\"broadcast-blue\"><b>Moderated chat was disabled!</b><br />Anyone may talk now.</div>");
  1106. } else {
  1107. var modchat = Tools.escapeHTML(room.modchat);
  1108. this.add("|raw|<div class=\"broadcast-red\"><b>Moderated chat was set to " + modchat + "!</b><br />Only users of rank " + modchat + " and higher can talk.</div>");
  1109. }
  1110. this.logModCommand(user.name + " set modchat to " + room.modchat);
  1111.  
  1112. if (room.chatRoomData) {
  1113. room.chatRoomData.modchat = room.modchat;
  1114. Rooms.global.writeChatRoomData();
  1115. }
  1116. },
  1117.  
  1118. declare: function (target, room, user) {
  1119. if (!target) return this.parse('/help declare');
  1120. if (!this.can('declare', null, room)) return false;
  1121.  
  1122. if (!this.canTalk()) return;
  1123.  
  1124. this.add('|raw|<div class="broadcast-blue"><b>' + Tools.escapeHTML(target) + '</b></div>');
  1125. this.logModCommand(user.name + " declared " + target);
  1126. },
  1127.  
  1128. htmldeclare: function (target, room, user) {
  1129. if (!target) return this.parse('/help htmldeclare');
  1130. if (!this.can('gdeclare', null, room)) return false;
  1131.  
  1132. if (!this.canTalk()) return;
  1133.  
  1134. this.add('|raw|<div class="broadcast-blue"><b>' + target + '</b></div>');
  1135. this.logModCommand(user.name + " declared " + target);
  1136. },
  1137.  
  1138. gdeclare: 'globaldeclare',
  1139. globaldeclare: function (target, room, user) {
  1140. if (!target) return this.parse('/help globaldeclare');
  1141. if (!this.can('gdeclare')) return false;
  1142.  
  1143. for (var id in Rooms.rooms) {
  1144. if (id !== 'global') Rooms.rooms[id].addRaw('<div class="broadcast-blue"><b>' + target + '</b></div>');
  1145. }
  1146. this.logModCommand(user.name + " globally declared " + target);
  1147. },
  1148.  
  1149. cdeclare: 'chatdeclare',
  1150. chatdeclare: function (target, room, user) {
  1151. if (!target) return this.parse('/help chatdeclare');
  1152. if (!this.can('gdeclare')) return false;
  1153.  
  1154. for (var id in Rooms.rooms) {
  1155. if (id !== 'global') if (Rooms.rooms[id].type !== 'battle') Rooms.rooms[id].addRaw('<div class="broadcast-blue"><b>' + target + '</b></div>');
  1156. }
  1157. this.logModCommand(user.name + " globally declared (chat level) " + target);
  1158. },
  1159.  
  1160. wall: 'announce',
  1161. announce: function (target, room, user) {
  1162. if (!target) return this.parse('/help announce');
  1163.  
  1164. if (!this.can('announce', null, room)) return false;
  1165.  
  1166. target = this.canTalk(target);
  1167. if (!target) return;
  1168.  
  1169. return '/announce ' + target;
  1170. },
  1171.  
  1172. fr: 'forcerename',
  1173. forcerename: function (target, room, user) {
  1174. if (!target) return this.parse('/help forcerename');
  1175. if ((user.locked || user.mutedRooms[room.id]) && !user.can('bypassall')) return this.sendReply("You cannot do this while unable to talk.");
  1176. var commaIndex = target.indexOf(',');
  1177. var targetUser, reason;
  1178. if (commaIndex !== -1) {
  1179. reason = target.substr(commaIndex + 1).trim();
  1180. target = target.substr(0, commaIndex);
  1181. }
  1182. targetUser = Users.get(target);
  1183. if (!targetUser) return this.sendReply("User '" + this.targetUsername + "' not found.");
  1184. if (!this.can('forcerename', targetUser)) return false;
  1185.  
  1186. if (targetUser.userid !== toId(target)) {
  1187. return this.sendReply("User '" + target + "' had already changed its name to '" + targetUser.name + "'.");
  1188. }
  1189.  
  1190. var entry = targetUser.name + " was forced to choose a new name by " + user.name + (reason ? ": " + reason : "");
  1191. this.privateModCommand("(" + entry + ")");
  1192. Rooms.global.cancelSearch(targetUser);
  1193. targetUser.resetName();
  1194. targetUser.send("|nametaken||" + user.name + " considers your name inappropriate" + (reason ? ": " + reason : "."));
  1195. },
  1196.  
  1197. modlog: function (target, room, user, connection) {
  1198. var lines = 0;
  1199. // Specific case for modlog command. Room can be indicated with a comma, lines go after the comma.
  1200. // Otherwise, the text is defaulted to text search in current room's modlog.
  1201. var roomId = room.id;
  1202. var hideIps = !user.can('ban');
  1203. var path = require('path');
  1204. var isWin = process.platform === 'win32';
  1205. var logPath = 'logs/modlog/';
  1206.  
  1207. if (target.indexOf(',') > -1) {
  1208. var targets = target.split(',');
  1209. target = targets[1].trim();
  1210. roomId = toId(targets[0]) || room.id;
  1211. }
  1212.  
  1213. // Let's check the number of lines to retrieve or if it's a word instead
  1214. if (!target.match('[^0-9]')) {
  1215. lines = parseInt(target || 15, 10);
  1216. if (lines > 100) lines = 100;
  1217. }
  1218. var wordSearch = (!lines || lines < 0);
  1219.  
  1220. // Control if we really, really want to check all modlogs for a word.
  1221. var roomNames = '';
  1222. var filename = '';
  1223. var command = '';
  1224. if (roomId === 'all' && wordSearch) {
  1225. if (!this.can('modlog')) return;
  1226. roomNames = "all rooms";
  1227. // Get a list of all the rooms
  1228. var fileList = fs.readdirSync('logs/modlog');
  1229. for (var i = 0; i < fileList.length; ++i) {
  1230. filename += path.normalize(__dirname + '/' + logPath + fileList[i]) + ' ';
  1231. }
  1232. } else {
  1233. if (!this.can('modlog', null, Rooms.get(roomId))) return;
  1234. roomNames = "the room " + roomId;
  1235. filename = path.normalize(__dirname + '/' + logPath + 'modlog_' + roomId + '.txt');
  1236. }
  1237.  
  1238. // Seek for all input rooms for the lines or text
  1239. if (isWin) {
  1240. command = path.normalize(__dirname + '/lib/winmodlog') + ' tail ' + lines + ' ' + filename;
  1241. } else {
  1242. command = 'tail -' + lines + ' ' + filename;
  1243. }
  1244. var grepLimit = 100;
  1245. if (wordSearch) { // searching for a word instead
  1246. if (target.match(/^["'].+["']$/)) target = target.substring(1, target.length - 1);
  1247. if (isWin) {
  1248. command = path.normalize(__dirname + '/lib/winmodlog') + ' ws ' + grepLimit + ' "' + target.replace(/%/g, "%%").replace(/([\^"&<>\|])/g, "^$1") + '" ' + filename;
  1249. } else {
  1250. command = "awk '{print NR,$0}' " + filename + " | sort -nr | cut -d' ' -f2- | grep -m" + grepLimit + " -i '" + target.replace(/\\/g, '\\\\\\\\').replace(/["'`]/g, '\'\\$&\'').replace(/[\{\}\[\]\(\)\$\^\.\?\+\-\*]/g, '[$&]') + "'";
  1251. }
  1252. }
  1253.  
  1254. // Execute the file search to see modlog
  1255. require('child_process').exec(command, function (error, stdout, stderr) {
  1256. if (error && stderr) {
  1257. connection.popup("/modlog empty on " + roomNames + " or erred");
  1258. console.log("/modlog error: " + error);
  1259. return false;
  1260. }
  1261. if (stdout && hideIps) {
  1262. stdout = stdout.replace(/\([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\)/g, '');
  1263. }
  1264. if (lines) {
  1265. if (!stdout) {
  1266. connection.popup("The modlog is empty. (Weird.)");
  1267. } else {
  1268. connection.popup("Displaying the last " + lines + " lines of the Moderator Log of " + roomNames + ":\n\n" + stdout);
  1269. }
  1270. } else {
  1271. if (!stdout) {
  1272. connection.popup("No moderator actions containing '" + target + "' were found on " + roomNames + ".");
  1273. } else {
  1274. connection.popup("Displaying the last " + grepLimit + " logged actions containing '" + target + "' on " + roomNames + ":\n\n" + stdout);
  1275. }
  1276. }
  1277. });
  1278. },
  1279.  
  1280. /*********************************************************
  1281. * Server management commands
  1282. *********************************************************/
  1283.  
  1284. hotpatch: function (target, room, user) {
  1285. if (!target) return this.parse('/help hotpatch');
  1286. if (!this.can('hotpatch')) return false;
  1287.  
  1288. this.logEntry(user.name + " used /hotpatch " + target);
  1289.  
  1290. if (target === 'chat' || target === 'commands') {
  1291. try {
  1292. CommandParser.uncacheTree('./command-parser.js');
  1293. global.CommandParser = require('./command-parser.js');
  1294.  
  1295. var runningTournaments = Tournaments.tournaments;
  1296. CommandParser.uncacheTree('./tournaments');
  1297. global.Tournaments = require('./tournaments');
  1298. Tournaments.tournaments = runningTournaments;
  1299.  
  1300. return this.sendReply("Chat commands have been hot-patched.");
  1301. } catch (e) {
  1302. return this.sendReply("Something failed while trying to hotpatch chat: \n" + e.stack);
  1303. }
  1304. } else if (target === 'tournaments') {
  1305. try {
  1306. var runningTournaments = Tournaments.tournaments;
  1307. CommandParser.uncacheTree('./tournaments');
  1308. global.Tournaments = require('./tournaments');
  1309. Tournaments.tournaments = runningTournaments;
  1310. return this.sendReply("Tournaments have been hot-patched.");
  1311. } catch (e) {
  1312. return this.sendReply("Something failed while trying to hotpatch tournaments: \n" + e.stack);
  1313. }
  1314. } else if (target === 'battles') {
  1315. Simulator.SimulatorProcess.respawn();
  1316. return this.sendReply("Battles have been hotpatched. Any battles started after now will use the new code; however, in-progress battles will continue to use the old code.");
  1317. } else if (target === 'formats') {
  1318. try {
  1319. // uncache the tools.js dependency tree
  1320. CommandParser.uncacheTree('./tools.js');
  1321. // reload tools.js
  1322. global.Tools = require('./tools.js'); // note: this will lock up the server for a few seconds
  1323. // rebuild the formats list
  1324. Rooms.global.formatListText = Rooms.global.getFormatListText();
  1325. // respawn validator processes
  1326. TeamValidator.ValidatorProcess.respawn();
  1327. // respawn simulator processes
  1328. Simulator.SimulatorProcess.respawn();
  1329. // broadcast the new formats list to clients
  1330. Rooms.global.send(Rooms.global.formatListText);
  1331.  
  1332. return this.sendReply("Formats have been hotpatched.");
  1333. } catch (e) {
  1334. return this.sendReply("Something failed while trying to hotpatch formats: \n" + e.stack);
  1335. }
  1336. } else if (target === 'learnsets') {
  1337. try {
  1338. // uncache the tools.js dependency tree
  1339. CommandParser.uncacheTree('./tools.js');
  1340. // reload tools.js
  1341. global.Tools = require('./tools.js'); // note: this will lock up the server for a few seconds
  1342.  
  1343. return this.sendReply("Learnsets have been hotpatched.");
  1344. } catch (e) {
  1345. return this.sendReply("Something failed while trying to hotpatch learnsets: \n" + e.stack);
  1346. }
  1347. }
  1348. this.sendReply("Your hot-patch command was unrecognized.");
  1349. },
  1350.  
  1351. savelearnsets: function (target, room, user) {
  1352. if (!this.can('hotpatch')) return false;
  1353. fs.writeFile('data/learnsets.js', 'exports.BattleLearnsets = ' + JSON.stringify(Tools.data.Learnsets) + ";\n");
  1354. this.sendReply("learnsets.js saved.");
  1355. },
  1356.  
  1357. disableladder: function (target, room, user) {
  1358. if (!this.can('disableladder')) return false;
  1359. if (LoginServer.disabled) {
  1360. return this.sendReply("/disableladder - Ladder is already disabled.");
  1361. }
  1362. LoginServer.disabled = true;
  1363. this.logModCommand("The ladder was disabled by " + user.name + ".");
  1364. this.add("|raw|<div class=\"broadcast-red\"><b>Due to high server load, the ladder has been temporarily disabled</b><br />Rated games will no longer update the ladder. It will be back momentarily.</div>");
  1365. },
  1366.  
  1367. enableladder: function (target, room, user) {
  1368. if (!this.can('disableladder')) return false;
  1369. if (!LoginServer.disabled) {
  1370. return this.sendReply("/enable - Ladder is already enabled.");
  1371. }
  1372. LoginServer.disabled = false;
  1373. this.logModCommand("The ladder was enabled by " + user.name + ".");
  1374. this.add("|raw|<div class=\"broadcast-green\"><b>The ladder is now back.</b><br />Rated games will update the ladder now.</div>");
  1375. },
  1376.  
  1377. lockdown: function (target, room, user) {
  1378. if (!this.can('lockdown')) return false;
  1379.  
  1380. Rooms.global.lockdown = true;
  1381. for (var id in Rooms.rooms) {
  1382. if (id === 'global') continue;
  1383. var curRoom = Rooms.rooms[id];
  1384. curRoom.addRaw("<div class=\"broadcast-red\"><b>The server is restarting soon.</b><br />Please finish your battles quickly. No new battles can be started until the server resets in a few minutes.</div>");
  1385. if (curRoom.requestKickInactive && !curRoom.battle.ended) {
  1386. curRoom.requestKickInactive(user, true);
  1387. if (curRoom.modchat !== '+') {
  1388. curRoom.modchat = '+';
  1389. curRoom.addRaw("<div class=\"broadcast-red\"><b>Moderated chat was set to +!</b><br />Only users of rank + and higher can talk.</div>");
  1390. }
  1391. }
  1392. }
  1393.  
  1394. this.logEntry(user.name + " used /lockdown");
  1395. },
  1396.  
  1397. prelockdown: function (target, room, user) {
  1398. if (!this.can('lockdown')) return false;
  1399. Rooms.global.lockdown = 'pre';
  1400. this.sendReply("Tournaments have been disabled in preparation for the server restart.");
  1401. this.logEntry(user.name + " used /prelockdown");
  1402. },
  1403.  
  1404. slowlockdown: function (target, room, user) {
  1405. if (!this.can('lockdown')) return false;
  1406.  
  1407. Rooms.global.lockdown = true;
  1408. for (var id in Rooms.rooms) {
  1409. if (id === 'global') continue;
  1410. var curRoom = Rooms.rooms[id];
  1411. if (curRoom.battle) continue;
  1412. curRoom.addRaw("<div class=\"broadcast-red\"><b>The server is restarting soon.</b><br />Please finish your battles quickly. No new battles can be started until the server resets in a few minutes.</div>");
  1413. }
  1414.  
  1415. this.logEntry(user.name + " used /slowlockdown");
  1416. },
  1417.  
  1418. endlockdown: function (target, room, user) {
  1419. if (!this.can('lockdown')) return false;
  1420.  
  1421. if (!Rooms.global.lockdown) {
  1422. return this.sendReply("We're not under lockdown right now.");
  1423. }
  1424. if (Rooms.global.lockdown === true) {
  1425. for (var id in Rooms.rooms) {
  1426. if (id !== 'global') Rooms.rooms[id].addRaw("<div class=\"broadcast-green\"><b>The server shutdown was canceled.</b></div>");
  1427. }
  1428. } else {
  1429. this.sendReply("Preparation for the server shutdown was canceled.");
  1430. }
  1431. Rooms.global.lockdown = false;
  1432.  
  1433. this.logEntry(user.name + " used /endlockdown");
  1434. },
  1435.  
  1436. emergency: function (target, room, user) {
  1437. if (!this.can('lockdown')) return false;
  1438.  
  1439. if (Config.emergency) {
  1440. return this.sendReply("We're already in emergency mode.");
  1441. }
  1442. Config.emergency = true;
  1443. for (var id in Rooms.rooms) {
  1444. if (id !== 'global') Rooms.rooms[id].addRaw("<div class=\"broadcast-red\">The server has entered emergency mode. Some features might be disabled or limited.</div>");
  1445. }
  1446.  
  1447. this.logEntry(user.name + " used /emergency");
  1448. },
  1449.  
  1450. endemergency: function (target, room, user) {
  1451. if (!this.can('lockdown')) return false;
  1452.  
  1453. if (!Config.emergency) {
  1454. return this.sendReply("We're not in emergency mode.");
  1455. }
  1456. Config.emergency = false;
  1457. for (var id in Rooms.rooms) {
  1458. if (id !== 'global') Rooms.rooms[id].addRaw("<div class=\"broadcast-green\"><b>The server is no longer in emergency mode.</b></div>");
  1459. }
  1460.  
  1461. this.logEntry(user.name + " used /endemergency");
  1462. },
  1463.  
  1464. kill: function (target, room, user) {
  1465. if (!this.can('lockdown')) return false;
  1466.  
  1467. if (Rooms.global.lockdown !== true) {
  1468. return this.sendReply("For safety reasons, /kill can only be used during lockdown.");
  1469. }
  1470.  
  1471. if (CommandParser.updateServerLock) {
  1472. return this.sendReply("Wait for /updateserver to finish before using /kill.");
  1473. }
  1474.  
  1475. for (var i in Sockets.workers) {
  1476. Sockets.workers[i].kill();
  1477. }
  1478.  
  1479. if (!room.destroyLog) {
  1480. process.exit();
  1481. return;
  1482. }
  1483. room.destroyLog(function () {
  1484. room.logEntry(user.name + " used /kill");
  1485. }, function () {
  1486. process.exit();
  1487. });
  1488.  
  1489. // Just in the case the above never terminates, kill the process
  1490. // after 10 seconds.
  1491. setTimeout(function () {
  1492. process.exit();
  1493. }, 10000);
  1494. },
  1495.  
  1496. loadbanlist: function (target, room, user, connection) {
  1497. if (!this.can('hotpatch')) return false;
  1498.  
  1499. connection.sendTo(room, "Loading ipbans.txt...");
  1500. fs.readFile('config/ipbans.txt', function (err, data) {
  1501. if (err) return;
  1502. data = ('' + data).split('\n');
  1503. var rangebans = [];
  1504. for (var i = 0; i < data.length; ++i) {
  1505. var line = data[i].split('#')[0].trim();
  1506. if (!line) continue;
  1507. if (line.indexOf('/') >= 0) {
  1508. rangebans.push(line);
  1509. } else if (line && !Users.bannedIps[line]) {
  1510. Users.bannedIps[line] = '#ipban';
  1511. }
  1512. }
  1513. Users.checkRangeBanned = Cidr.checker(rangebans);
  1514. connection.sendTo(room, "ipbans.txt has been reloaded.");
  1515. });
  1516. },
  1517.  
  1518. refreshpage: function (target, room, user) {
  1519. if (!this.can('hotpatch')) return false;
  1520. Rooms.global.send('|refresh|');
  1521. this.logEntry(user.name + " used /refreshpage");
  1522. },
  1523.  
  1524. updateserver: function (target, room, user, connection) {
  1525. if (!user.hasConsoleAccess(connection)) {
  1526. return this.sendReply("/updateserver - Access denied.");
  1527. }
  1528.  
  1529. if (CommandParser.updateServerLock) {
  1530. return this.sendReply("/updateserver - Another update is already in progress.");
  1531. }
  1532.  
  1533. CommandParser.updateServerLock = true;
  1534.  
  1535. var logQueue = [];
  1536. logQueue.push(user.name + " used /updateserver");
  1537.  
  1538. connection.sendTo(room, "updating...");
  1539.  
  1540. var exec = require('child_process').exec;
  1541. exec('git diff-index --quiet HEAD --', function (error) {
  1542. var cmd = 'git pull --rebase';
  1543. if (error) {
  1544. if (error.code === 1) {
  1545. // The working directory or index have local changes.
  1546. cmd = 'git stash && ' + cmd + ' && git stash pop';
  1547. } else {
  1548. // The most likely case here is that the user does not have
  1549. // `git` on the PATH (which would be error.code === 127).
  1550. connection.sendTo(room, "" + error);
  1551. logQueue.push("" + error);
  1552. logQueue.forEach(function (line) {
  1553. room.logEntry(line);
  1554. });
  1555. CommandParser.updateServerLock = false;
  1556. return;
  1557. }
  1558. }
  1559. var entry = "Running `" + cmd + "`";
  1560. connection.sendTo(room, entry);
  1561. logQueue.push(entry);
  1562. exec(cmd, function (error, stdout, stderr) {
  1563. ("" + stdout + stderr).split("\n").forEach(function (s) {
  1564. connection.sendTo(room, s);
  1565. logQueue.push(s);
  1566. });
  1567. logQueue.forEach(function (line) {
  1568. room.logEntry(line);
  1569. });
  1570. CommandParser.updateServerLock = false;
  1571. });
  1572. });
  1573. },
  1574.  
  1575. crashfixed: function (target, room, user) {
  1576. if (Rooms.global.lockdown !== true) {
  1577. return this.sendReply('/crashfixed - There is no active crash.');
  1578. }
  1579. if (!this.can('hotpatch')) return false;
  1580.  
  1581. Rooms.global.lockdown = false;
  1582. if (Rooms.lobby) {
  1583. Rooms.lobby.modchat = false;
  1584. Rooms.lobby.addRaw("<div class=\"broadcast-green\"><b>We fixed the crash without restarting the server!</b><br />You may resume talking in the lobby and starting new battles.</div>");
  1585. }
  1586. this.logEntry(user.name + " used /crashfixed");
  1587. },
  1588.  
  1589. 'memusage': 'memoryusage',
  1590. memoryusage: function (target) {
  1591. if (!this.can('hotpatch')) return false;
  1592. target = toId(target) || 'all';
  1593. if (target === 'all') {
  1594. this.sendReply("Loading memory usage, this might take a while.");
  1595. }
  1596. var roomSize, configSize, rmSize, cpSize, simSize, usersSize, toolsSize;
  1597. if (target === 'all' || target === 'rooms' || target === 'room') {
  1598. this.sendReply("Calculating Room size...");
  1599. roomSize = ResourceMonitor.sizeOfObject(Rooms);
  1600. this.sendReply("Rooms are using " + roomSize + " bytes of memory.");
  1601. }
  1602. if (target === 'all' || target === 'config') {
  1603. this.sendReply("Calculating config size...");
  1604. configSize = ResourceMonitor.sizeOfObject(Config);
  1605. this.sendReply("Config is using " + configSize + " bytes of memory.");
  1606. }
  1607. if (target === 'all' || target === 'resourcemonitor' || target === 'rm') {
  1608. this.sendReply("Calculating Resource Monitor size...");
  1609. rmSize = ResourceMonitor.sizeOfObject(ResourceMonitor);
  1610. this.sendReply("The Resource Monitor is using " + rmSize + " bytes of memory.");
  1611. }
  1612. if (target === 'all' || target === 'cmdp' || target === 'cp' || target === 'commandparser') {
  1613. this.sendReply("Calculating Command Parser size...");
  1614. cpSize = ResourceMonitor.sizeOfObject(CommandParser);
  1615. this.sendReply("Command Parser is using " + cpSize + " bytes of memory.");
  1616. }
  1617. if (target === 'all' || target === 'sim' || target === 'simulator') {
  1618. this.sendReply("Calculating Simulator size...");
  1619. simSize = ResourceMonitor.sizeOfObject(Simulator);
  1620. this.sendReply("Simulator is using " + simSize + " bytes of memory.");
  1621. }
  1622. if (target === 'all' || target === 'users') {
  1623. this.sendReply("Calculating Users size...");
  1624. usersSize = ResourceMonitor.sizeOfObject(Users);
  1625. this.sendReply("Users is using " + usersSize + " bytes of memory.");
  1626. }
  1627. if (target === 'all' || target === 'tools') {
  1628. this.sendReply("Calculating Tools size...");
  1629. toolsSize = ResourceMonitor.sizeOfObject(Tools);
  1630. this.sendReply("Tools are using " + toolsSize + " bytes of memory.");
  1631. }
  1632. if (target === 'all' || target === 'v8') {
  1633. this.sendReply("Retrieving V8 memory usage...");
  1634. var o = process.memoryUsage();
  1635. this.sendReply("Resident set size: " + o.rss + ", " + o.heapUsed + " heap used of " + o.heapTotal + " total heap. " + (o.heapTotal - o.heapUsed) + " heap left.");
  1636. }
  1637. if (target === 'all') {
  1638. this.sendReply("Calculating Total size...");
  1639. var total = (roomSize + configSize + rmSize + cpSize + simSize + usersSize + toolsSize) || 0;
  1640. var units = ["bytes", "K", "M", "G"];
  1641. var converted = total;
  1642. var unit = 0;
  1643. while (converted > 1024) {
  1644. converted /= 1024;
  1645. ++unit;
  1646. }
  1647. converted = Math.round(converted);
  1648. this.sendReply("Total memory used: " + converted + units[unit] + " (" + total + " bytes).");
  1649. }
  1650. return;
  1651. },
  1652.  
  1653. bash: function (target, room, user, connection) {
  1654. if (!user.hasConsoleAccess(connection)) {
  1655. return this.sendReply("/bash - Access denied.");
  1656. }
  1657.  
  1658. var exec = require('child_process').exec;
  1659. exec(target, function (error, stdout, stderr) {
  1660. connection.sendTo(room, ("" + stdout + stderr));
  1661. });
  1662. },
  1663.  
  1664. eval: function (target, room, user, connection) {
  1665. if (!user.hasConsoleAccess(connection)) {
  1666. return this.sendReply("/eval - Access denied.");
  1667. }
  1668. if (!this.canBroadcast()) return;
  1669.  
  1670. if (!this.broadcasting) this.sendReply('||>> ' + target);
  1671. try {
  1672. var battle = room.battle;
  1673. var me = user;
  1674. this.sendReply('||<< ' + eval(target));
  1675. } catch (e) {
  1676. this.sendReply('||<< error: ' + e.message);
  1677. var stack = '||' + ('' + e.stack).replace(/\n/g, '\n||');
  1678. connection.sendTo(room, stack);
  1679. }
  1680. },
  1681.  
  1682. evalbattle: function (target, room, user, connection) {
  1683. if (!user.hasConsoleAccess(connection)) {
  1684. return this.sendReply("/evalbattle - Access denied.");
  1685. }
  1686. if (!this.canBroadcast()) return;
  1687. if (!room.battle) {
  1688. return this.sendReply("/evalbattle - This isn't a battle room.");
  1689. }
  1690.  
  1691. room.battle.send('eval', target.replace(/\n/g, '\f'));
  1692. },
  1693.  
  1694. /*********************************************************
  1695. * Battle commands
  1696. *********************************************************/
  1697.  
  1698. forfeit: function (target, room, user) {
  1699. if (!room.battle) {
  1700. return this.sendReply("There's nothing to forfeit here.");
  1701. }
  1702. if (!room.forfeit(user)) {
  1703. return this.sendReply("You can't forfeit this battle.");
  1704. }
  1705. },
  1706.  
  1707. savereplay: function (target, room, user, connection) {
  1708. if (!room || !room.battle) return;
  1709. var logidx = 2; // spectator log (no exact HP)
  1710. if (room.battle.ended) {
  1711. // If the battle is finished when /savereplay is used, include
  1712. // exact HP in the replay log.
  1713. logidx = 3;
  1714. }
  1715. var data = room.getLog(logidx).join("\n");
  1716. var datahash = crypto.createHash('md5').update(data.replace(/[^(\x20-\x7F)]+/g, '')).digest('hex');
  1717.  
  1718. LoginServer.request('prepreplay', {
  1719. id: room.id.substr(7),
  1720. loghash: datahash,
  1721. p1: room.p1.name,
  1722. p2: room.p2.name,
  1723. format: room.format
  1724. }, function (success) {
  1725. if (success && success.errorip) {
  1726. connection.popup("This server's request IP " + success.errorip + " is not a registered server.");
  1727. return;
  1728. }
  1729. connection.send('|queryresponse|savereplay|' + JSON.stringify({
  1730. log: data,
  1731. id: room.id.substr(7)
  1732. }));
  1733. });
  1734. },
  1735.  
  1736. mv: 'move',
  1737. attack: 'move',
  1738. move: function (target, room, user) {
  1739. if (!room.decision) return this.sendReply("You can only do this in battle rooms.");
  1740.  
  1741. room.decision(user, 'choose', 'move ' + target);
  1742. },
  1743.  
  1744. sw: 'switch',
  1745. switch: function (target, room, user) {
  1746. if (!room.decision) return this.sendReply("You can only do this in battle rooms.");
  1747.  
  1748. room.decision(user, 'choose', 'switch ' + parseInt(target, 10));
  1749. },
  1750.  
  1751. choose: function (target, room, user) {
  1752. if (!room.decision) return this.sendReply("You can only do this in battle rooms.");
  1753.  
  1754. room.decision(user, 'choose', target);
  1755. },
  1756.  
  1757. undo: function (target, room, user) {
  1758. if (!room.decision) return this.sendReply("You can only do this in battle rooms.");
  1759.  
  1760. room.decision(user, 'undo', target);
  1761. },
  1762.  
  1763. team: function (target, room, user) {
  1764. if (!room.decision) return this.sendReply("You can only do this in battle rooms.");
  1765.  
  1766. room.decision(user, 'choose', 'team ' + target);
  1767. },
  1768.  
  1769. joinbattle: function (target, room, user) {
  1770. if (!room.joinBattle) return this.sendReply("You can only do this in battle rooms.");
  1771. if (!user.can('joinbattle', null, room)) return this.popupReply("You must be a roomvoice to join a battle you didn't start. Ask a player to use /roomvoice on you to join this battle.");
  1772.  
  1773. room.joinBattle(user);
  1774. },
  1775.  
  1776. partbattle: 'leavebattle',
  1777. leavebattle: function (target, room, user) {
  1778. if (!room.leaveBattle) return this.sendReply("You can only do this in battle rooms.");
  1779.  
  1780. room.leaveBattle(user);
  1781. },
  1782.  
  1783. kickbattle: function (target, room, user) {
  1784. if (!room.leaveBattle) return this.sendReply("You can only do this in battle rooms.");
  1785.  
  1786. target = this.splitTarget(target);
  1787. var targetUser = this.targetUser;
  1788. if (!targetUser || !targetUser.connected) {
  1789. return this.sendReply("User " + this.targetUsername + " not found.");
  1790. }
  1791. if (!this.can('kick', targetUser)) return false;
  1792.  
  1793. if (room.leaveBattle(targetUser)) {
  1794. this.addModCommand("" + targetUser.name + " was kicked from a battle by " + user.name + (target ? " (" + target + ")" : ""));
  1795. } else {
  1796. this.sendReply("/kickbattle - User isn't in battle.");
  1797. }
  1798. },
  1799.  
  1800. kickinactive: function (target, room, user) {
  1801. if (room.requestKickInactive) {
  1802. room.requestKickInactive(user);
  1803. } else {
  1804. this.sendReply("You can only kick inactive players from inside a room.");
  1805. }
  1806. },
  1807.  
  1808. timer: function (target, room, user) {
  1809. target = toId(target);
  1810. if (room.requestKickInactive) {
  1811. if (target === 'off' || target === 'false' || target === 'stop') {
  1812. room.stopKickInactive(user, user.can('timer'));
  1813. } else if (target === 'on' || target === 'true' || !target) {
  1814. room.requestKickInactive(user, user.can('timer'));
  1815. } else {
  1816. this.sendReply("'" + target + "' is not a recognized timer state.");
  1817. }
  1818. } else {
  1819. this.sendReply("You can only set the timer from inside a room.");
  1820. }
  1821. },
  1822.  
  1823. autotimer: 'forcetimer',
  1824. forcetimer: function (target, room, user) {
  1825. target = toId(target);
  1826. if (!this.can('autotimer')) return;
  1827. if (target === 'off' || target === 'false' || target === 'stop') {
  1828. Config.forcetimer = false;
  1829. this.addModCommand("Forcetimer is now OFF: The timer is now opt-in. (set by " + user.name + ")");
  1830. } else if (target === 'on' || target === 'true' || !target) {
  1831. Config.forcetimer = true;
  1832. this.addModCommand("Forcetimer is now ON: All battles will be timed. (set by " + user.name + ")");
  1833. } else {
  1834. this.sendReply("'" + target + "' is not a recognized forcetimer setting.");
  1835. }
  1836. },
  1837.  
  1838. forcetie: 'forcewin',
  1839. forcewin: function (target, room, user) {
  1840. if (!this.can('forcewin')) return false;
  1841. if (!room.battle) {
  1842. this.sendReply("/forcewin - This is not a battle room.");
  1843. return false;
  1844. }
  1845.  
  1846. room.battle.endType = 'forced';
  1847. if (!target) {
  1848. room.battle.tie();
  1849. this.logModCommand(user.name + " forced a tie.");
  1850. return false;
  1851. }
  1852. target = Users.get(target);
  1853. if (target) target = target.userid;
  1854. else target = '';
  1855.  
  1856. if (target) {
  1857. room.battle.win(target);
  1858. this.logModCommand(user.name + " forced a win for " + target + ".");
  1859. }
  1860. },
  1861.  
  1862. /*********************************************************
  1863. * Challenging and searching commands
  1864. *********************************************************/
  1865.  
  1866. cancelsearch: 'search',
  1867. search: function (target, room, user) {
  1868. if (target) {
  1869. if (Config.pmmodchat) {
  1870. var userGroup = user.group;
  1871. if (Config.groupsranking.indexOf(userGroup) < Config.groupsranking.indexOf(Config.pmmodchat)) {
  1872. var groupName = Config.groups[Config.pmmodchat].name || Config.pmmodchat;
  1873. this.popupReply("Because moderated chat is set, you must be of rank " + groupName + " or higher to search for a battle.");
  1874. return false;
  1875. }
  1876. }
  1877. Rooms.global.searchBattle(user, target);
  1878. } else {
  1879. Rooms.global.cancelSearch(user);
  1880. }
  1881. },
  1882.  
  1883. chall: 'challenge',
  1884. challenge: function (target, room, user, connection) {
  1885. target = this.splitTarget(target);
  1886. var targetUser = this.targetUser;
  1887. if (!targetUser || !targetUser.connected) {
  1888. return this.popupReply("The user '" + this.targetUsername + "' was not found.");
  1889. }
  1890. if (targetUser.blockChallenges && !user.can('bypassblocks', targetUser)) {
  1891. return this.popupReply("The user '" + this.targetUsername + "' is not accepting challenges right now.");
  1892. }
  1893. if (Config.pmmodchat) {
  1894. var userGroup = user.group;
  1895. if (Config.groupsranking.indexOf(userGroup) < Config.groupsranking.indexOf(Config.pmmodchat)) {
  1896. var groupName = Config.groups[Config.pmmodchat].name || Config.pmmodchat;
  1897. this.popupReply("Because moderated chat is set, you must be of rank " + groupName + " or higher to challenge users.");
  1898. return false;
  1899. }
  1900. }
  1901. user.prepBattle(target, 'challenge', connection, function (result) {
  1902. if (result) user.makeChallenge(targetUser, target);
  1903. });
  1904. },
  1905.  
  1906. away: 'blockchallenges',
  1907. idle: 'blockchallenges',
  1908. blockchallenges: function (target, room, user) {
  1909. if (user.blockChallenges) return this.sendReply("You are already blocking challenges!");
  1910. user.blockChallenges = true;
  1911. this.sendReply("You are now blocking all incoming challenge requests.");
  1912. },
  1913.  
  1914. back: 'allowchallenges',
  1915. allowchallenges: function (target, room, user) {
  1916. if (!user.blockChallenges) return this.sendReply("You are already available for challenges!");
  1917. user.blockChallenges = false;
  1918. this.sendReply("You are available for challenges from now on.");
  1919. },
  1920.  
  1921. cchall: 'cancelChallenge',
  1922. cancelchallenge: function (target, room, user) {
  1923. user.cancelChallengeTo(target);
  1924. },
  1925.  
  1926. accept: function (target, room, user, connection) {
  1927. var userid = toId(target);
  1928. var format = '';
  1929. if (user.challengesFrom[userid]) format = user.challengesFrom[userid].format;
  1930. if (!format) {
  1931. this.popupReply(target + " cancelled their challenge before you could accept it.");
  1932. return false;
  1933. }
  1934. user.prepBattle(format, 'challenge', connection, function (result) {
  1935. if (result) user.acceptChallengeFrom(userid);
  1936. });
  1937. },
  1938.  
  1939. reject: function (target, room, user) {
  1940. user.rejectChallengeFrom(toId(target));
  1941. },
  1942.  
  1943. saveteam: 'useteam',
  1944. utm: 'useteam',
  1945. useteam: function (target, room, user) {
  1946. user.team = target;
  1947. },
  1948.  
  1949. /*********************************************************
  1950. * Low-level
  1951. *********************************************************/
  1952.  
  1953. cmd: 'query',
  1954. query: function (target, room, user, connection) {
  1955. // Avoid guest users to use the cmd errors to ease the app-layer attacks in emergency mode
  1956. var trustable = (!Config.emergency || (user.named && user.authenticated));
  1957. if (Config.emergency && ResourceMonitor.countCmd(connection.ip, user.name)) return false;
  1958. var spaceIndex = target.indexOf(' ');
  1959. var cmd = target;
  1960. if (spaceIndex > 0) {
  1961. cmd = target.substr(0, spaceIndex);
  1962. target = target.substr(spaceIndex + 1);
  1963. } else {
  1964. target = '';
  1965. }
  1966. if (cmd === 'userdetails') {
  1967. var targetUser = Users.get(target);
  1968. if (!trustable || !targetUser) {
  1969. connection.send('|queryresponse|userdetails|' + JSON.stringify({
  1970. userid: toId(target),
  1971. rooms: false
  1972. }));
  1973. return false;
  1974. }
  1975. var roomList = {};
  1976. for (var i in targetUser.roomCount) {
  1977. if (i === 'global') continue;
  1978. var targetRoom = Rooms.get(i);
  1979. if (!targetRoom || targetRoom.isPrivate) continue;
  1980. var roomData = {};
  1981. if (targetRoom.battle) {
  1982. var battle = targetRoom.battle;
  1983. roomData.p1 = battle.p1 ? ' ' + battle.p1 : '';
  1984. roomData.p2 = battle.p2 ? ' ' + battle.p2 : '';
  1985. }
  1986. roomList[i] = roomData;
  1987. }
  1988. if (!targetUser.roomCount['global']) roomList = false;
  1989. var userdetails = {
  1990. userid: targetUser.userid,
  1991. avatar: targetUser.avatar,
  1992. rooms: roomList
  1993. };
  1994. connection.send('|queryresponse|userdetails|' + JSON.stringify(userdetails));
  1995. } else if (cmd === 'roomlist') {
  1996. if (!trustable) return false;
  1997. connection.send('|queryresponse|roomlist|' + JSON.stringify({
  1998. rooms: Rooms.global.getRoomList(target)
  1999. }));
  2000. } else if (cmd === 'rooms') {
  2001. if (!trustable) return false;
  2002. connection.send('|queryresponse|rooms|' + JSON.stringify(
  2003. Rooms.global.getRooms()
  2004. ));
  2005. }
  2006. },
  2007.  
  2008. trn: function (target, room, user, connection) {
  2009. var commaIndex = target.indexOf(',');
  2010. var targetName = target;
  2011. var targetAuth = false;
  2012. var targetToken = '';
  2013. if (commaIndex >= 0) {
  2014. targetName = target.substr(0, commaIndex);
  2015. target = target.substr(commaIndex + 1);
  2016. commaIndex = target.indexOf(',');
  2017. targetAuth = target;
  2018. if (commaIndex >= 0) {
  2019. targetAuth = !!parseInt(target.substr(0, commaIndex), 10);
  2020. targetToken = target.substr(commaIndex + 1);
  2021. }
  2022. }
  2023. user.rename(targetName, targetToken, targetAuth, connection);
  2024. }
  2025.  
  2026. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement