PageRenderTime 63ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/chat-commands.js

https://github.com/CanBoy67/Pokemon-Showdown
JavaScript | 2134 lines | 1890 code | 181 blank | 63 comment | 424 complexity | b230d2c5713942189b1ead1f84151dd4 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /* to reload chat commands:
  2. >> for (var i in require.cache) delete require.cache[i];parseCommand = require('./chat-commands.js').parseCommand;''
  3. */
  4. var crypto = require('crypto');
  5. /**
  6. * `parseCommand`. This is the function most of you are interested in,
  7. * apparently.
  8. *
  9. * `message` is exactly what the user typed in.
  10. * If the user typed in a command, `cmd` and `target` are the command (with "/"
  11. * omitted) and command target. Otherwise, they're both the empty string.
  12. *
  13. * For instance, say a user types in "/foo":
  14. * cmd === "/foo", target === "", message === "/foo bar baz"
  15. *
  16. * Or, say a user types in "/foo bar baz":
  17. * cmd === "foo", target === "bar baz", message === "/foo bar baz"
  18. *
  19. * Or, say a user types in "!foo bar baz":
  20. * cmd === "!foo", target === "bar baz", message === "!foo bar baz"
  21. *
  22. * Or, say a user types in "foo bar baz":
  23. * cmd === "", target === "", message === "foo bar baz"
  24. *
  25. * `user` and `socket` are the user and socket that sent the message,
  26. * and `room` is the room that sent the message.
  27. *
  28. * Deal with the message however you wish:
  29. * return; will output the message normally: "user: message"
  30. * return false; will supress the message output.
  31. * returning a string will replace the message with that string,
  32. * then output it normally.
  33. *
  34. */
  35. var modlog = modlog || fs.createWriteStream('logs/modlog.txt', {flags:'a+'});
  36. var updateServerLock = false;
  37. function parseCommandLocal(user, cmd, target, room, socket, message) {
  38. if (!room) return;
  39. cmd = cmd.toLowerCase();
  40. switch (cmd) {
  41. case 'cmd':
  42. var spaceIndex = target.indexOf(' ');
  43. var cmd = target;
  44. if (spaceIndex > 0) {
  45. cmd = target.substr(0, spaceIndex);
  46. target = target.substr(spaceIndex+1);
  47. } else {
  48. target = '';
  49. }
  50. if (cmd === 'userdetails') {
  51. var targetUser = Users.get(target);
  52. if (!targetUser || !room) return false;
  53. var roomList = {};
  54. for (var i in targetUser.roomCount) {
  55. if (i==='lobby') continue;
  56. var targetRoom = Rooms.get(i);
  57. if (!targetRoom) continue;
  58. var roomData = {};
  59. if (targetRoom.battle) {
  60. var battle = targetRoom.battle;
  61. roomData.p1 = battle.p1?' '+battle.p1:'';
  62. roomData.p2 = battle.p2?' '+battle.p2:'';
  63. }
  64. roomList[i] = roomData;
  65. }
  66. var userdetails = {
  67. command: 'userdetails',
  68. userid: targetUser.userid,
  69. avatar: targetUser.avatar,
  70. rooms: roomList,
  71. room: room.id
  72. };
  73. if (user.can('ip', targetUser)) {
  74. userdetails.ip = targetUser.ip;
  75. }
  76. emit(socket, 'command', userdetails);
  77. } else if (cmd === 'roomlist') {
  78. if (!room || !room.getRoomList) return false;
  79. emit(socket, 'command', {
  80. command: 'roomlist',
  81. rooms: room.getRoomList(true),
  82. room: room.id
  83. });
  84. }
  85. return false;
  86. break;
  87. case 'me':
  88. case 'mee':
  89. if (canTalk(user, room)) return true;
  90. break;
  91. case '!birkal':
  92. case 'birkal':
  93. if (canTalk(user, room) && user.can('broadcast') && room.id === 'lobby') {
  94. if (cmd === '!birkal') {
  95. room.add('|c|'+user.getIdentity()+'|!birkal '+target, true);
  96. }
  97. room.logEntry(user.name + ' used /birkal ' + target);
  98. room.add('|c| Birkal|/me '+target, true);
  99. return false;
  100. }
  101. break;
  102. case 'namelock':
  103. case 'nl':
  104. if(!target) {
  105. return false;
  106. }
  107. var targets = splitTarget(target);
  108. var targetUser = targets[0];
  109. var targetName = targets[1] || (targetUser && targetUser.name);
  110. if (!user.can('namelock', targetUser)) {
  111. emit(socket, 'console', '/namelock - access denied.');
  112. return false;
  113. } else if (targetUser && targetName) {
  114. var oldname = targetUser.name;
  115. var targetId = toUserid(targetName);
  116. var userOfName = Users.users[targetId];
  117. var isAlt = false;
  118. if (userOfName) {
  119. for(var altName in userOfName.getAlts()) {
  120. var altUser = Users.users[toUserid(altName)];
  121. if (!altUser) continue;
  122. if (targetId === altUser.userid) {
  123. isAlt = true;
  124. break;
  125. }
  126. for (var prevName in altUser.prevNames) {
  127. if (targetId === toUserid(prevName)) {
  128. isAlt = true;
  129. break;
  130. }
  131. }
  132. if (isAlt) break;
  133. }
  134. }
  135. if (!userOfName || oldname === targetName || isAlt) {
  136. targetUser.nameLock(targetName, true);
  137. }
  138. if (targetUser.nameLocked()) {
  139. logModCommand(room,user.name+" name-locked "+oldname+" to "+targetName+".");
  140. return false;
  141. }
  142. emit(socket, 'console', oldname+" can't be name-locked to "+targetName+".");
  143. } else {
  144. emit(socket, 'console', "User "+targets[2]+" not found.");
  145. }
  146. return false;
  147. break;
  148. case 'nameunlock':
  149. case 'unnamelock':
  150. case 'nul':
  151. case 'unl':
  152. if(!user.can('namelock') || !target) {
  153. return false;
  154. }
  155. var removed = false;
  156. for (var i in nameLockedIps) {
  157. if (nameLockedIps[i] === target) {
  158. delete nameLockedIps[i];
  159. removed = true;
  160. }
  161. }
  162. if (removed) {
  163. var targetUser = Users.get(target);
  164. if (targetUser) {
  165. rooms.lobby.sendIdentity(targetUser);
  166. }
  167. logModCommand(room,user.name+" unlocked the name of "+target+".");
  168. } else {
  169. emit(socket, 'console', target+" not found.");
  170. }
  171. return false;
  172. break;
  173. case 'forfeit':
  174. case 'concede':
  175. case 'surrender':
  176. if (!room.battle) {
  177. emit(socket, 'console', "There's nothing to forfeit here.");
  178. return false;
  179. }
  180. if (!room.forfeit(user)) {
  181. emit(socket, 'console', "You can't forfeit this battle.");
  182. }
  183. return false;
  184. break;
  185. case 'register':
  186. emit(socket, 'console', 'You must win a rated battle to register.');
  187. return false;
  188. break;
  189. case 'avatar':
  190. if (!target) return parseCommand(user, 'avatars', '', room, socket);
  191. var parts = target.split(',');
  192. var avatar = parseInt(parts[0]);
  193. if (!avatar || avatar > 294 || avatar < 1) {
  194. if (!parts[1]) {
  195. emit(socket, 'console', 'Invalid avatar.');
  196. }
  197. return false;
  198. }
  199. user.avatar = avatar;
  200. if (!parts[1]) {
  201. emit(socket, 'console', 'Avatar changed to:');
  202. emit(socket, 'console', {rawMessage: '<img src="/sprites/trainers/'+avatar+'.png" alt="" width="80" height="80" />'});
  203. }
  204. return false;
  205. break;
  206. case 'rooms':
  207. var targetUser = user;
  208. if (target) targetUser = Users.get(target);
  209. if (!targetUser) {
  210. emit(socket, 'console', 'User '+target+' not found.');
  211. } else {
  212. var output = "";
  213. var first = true;
  214. for (var i in targetUser.roomCount) {
  215. if (!first) output += ' | ';
  216. first = false;
  217. output += '<a href="/'+i+'" room="'+i+'">'+i+'</a>';
  218. }
  219. if (!output) {
  220. emit(socket, 'console', ""+targetUser.name+" is offline.");
  221. } else {
  222. emit(socket, 'console', {rawMessage: ""+targetUser.name+" is in: "+output});
  223. }
  224. }
  225. return false;
  226. break;
  227. case 'altcheck':
  228. case 'alt':
  229. case 'alts':
  230. case 'getalts':
  231. if (!target) return parseCommand(user, '?', cmd, room, socket);
  232. var targetUser = Users.get(target);
  233. if (!targetUser) {
  234. emit(socket, 'console', 'User '+target+' not found.');
  235. return false;
  236. }
  237. if (!user.can('alts', targetUser)) {
  238. emit(socket, 'console', '/alts - Access denied.');
  239. return false;
  240. }
  241. var alts = targetUser.getAlts();
  242. emit(socket, 'console', 'User: '+targetUser.name);
  243. if (!user.can('alts', targetUser.getHighestRankedAlt())) {
  244. return false;
  245. }
  246. var output = '';
  247. for (var i in targetUser.prevNames) {
  248. if (output) output += ", ";
  249. output += targetUser.prevNames[i];
  250. }
  251. if (output) emit(socket, 'console', 'Previous names: '+output);
  252. for (var j=0; j<alts.length; j++) {
  253. var targetAlt = Users.get(alts[j]);
  254. if (!targetAlt.named && !targetAlt.connected) continue;
  255. emit(socket, 'console', 'Alt: '+targetAlt.name);
  256. output = '';
  257. for (var i in targetAlt.prevNames) {
  258. if (output) output += ", ";
  259. output += targetAlt.prevNames[i];
  260. }
  261. if (output) emit(socket, 'console', 'Previous names: '+output);
  262. }
  263. return false;
  264. break;
  265. case 'whois':
  266. var targetUser = user;
  267. if (target) {
  268. targetUser = Users.get(target);
  269. }
  270. if (!targetUser) {
  271. emit(socket, 'console', 'User '+target+' not found.');
  272. } else {
  273. emit(socket, 'console', 'User: '+targetUser.name);
  274. if (config.groups[targetUser.group] && config.groups[targetUser.group].name) {
  275. emit(socket, 'console', 'Group: ' + config.groups[targetUser.group].name + ' (' + targetUser.group + ')');
  276. }
  277. if (!targetUser.authenticated) {
  278. emit(socket, 'console', '(Unregistered)');
  279. }
  280. if (user.can('ip', targetUser)) {
  281. emit(socket, 'console', 'IP: '+targetUser.ip);
  282. }
  283. var output = 'In rooms: ';
  284. var first = true;
  285. for (var i in targetUser.roomCount) {
  286. if (!first) output += ' | ';
  287. first = false;
  288. output += '<a href="/'+i+'" room="'+i+'">'+i+'</a>';
  289. }
  290. emit(socket, 'console', {rawMessage: output});
  291. }
  292. return false;
  293. break;
  294. case 'ban':
  295. case 'b':
  296. if (!target) return parseCommand(user, '?', cmd, room, socket);
  297. var targets = splitTarget(target);
  298. var targetUser = targets[0];
  299. if (!targetUser) {
  300. emit(socket, 'console', 'User '+targets[2]+' not found.');
  301. return false;
  302. }
  303. if (!user.can('ban', targetUser)) {
  304. emit(socket, 'console', '/ban - Access denied.');
  305. return false;
  306. }
  307. logModCommand(room,""+targetUser.name+" was banned by "+user.name+"." + (targets[1] ? " (" + targets[1] + ")" : ""));
  308. targetUser.emit('message', user.name+' has banned you. If you feel that your banning was unjustified you can <a href="http://www.smogon.com/forums/announcement.php?f=126&a=204" target="_blank">appeal the ban</a>. '+targets[1]);
  309. var alts = targetUser.getAlts();
  310. if (alts.length) logModCommand(room,""+targetUser.name+"'s alts were also banned: "+alts.join(", "));
  311. targetUser.ban();
  312. return false;
  313. break;
  314. case 'banredirect':
  315. case 'br':
  316. emit(socket, 'console', '/banredirect - This command is obsolete and has been removed.');
  317. return false;
  318. break;
  319. case 'redirect':
  320. case 'redir':
  321. emit(socket, 'console', '/redirect - This command is obsolete and has been removed.');
  322. return false;
  323. break;
  324. case 'kick':
  325. case 'k':
  326. // TODO: /kick will be removed in due course.
  327. if (!target) return parseCommand(user, '?', cmd, room, socket);
  328. var targets = splitTarget(target);
  329. var targetUser = targets[0];
  330. if (!targetUser || !targetUser.connected) {
  331. emit(socket, 'console', 'User '+targets[2]+' not found.');
  332. return false;
  333. }
  334. if (!user.can('redirect', targetUser)) {
  335. emit(socket, 'console', '/redirect - Access denied.');
  336. return false;
  337. }
  338. logModCommand(room,''+targetUser.name+' was kicked to the Rules page by '+user.name+'' + (targets[1] ? " (" + targets[1] + ")" : ""));
  339. targetUser.emit('console', {evalRulesRedirect: 1});
  340. return false;
  341. break;
  342. case 'unban':
  343. if (!target) return parseCommand(user, '?', cmd, room, socket);
  344. if (!user.can('ban')) {
  345. emit(socket, 'console', '/unban - Access denied.');
  346. return false;
  347. }
  348. var targetid = toUserid(target);
  349. var success = false;
  350. for (var ip in bannedIps) {
  351. if (bannedIps[ip] === targetid) {
  352. delete bannedIps[ip];
  353. success = true;
  354. }
  355. }
  356. if (success) {
  357. logModCommand(room,''+target+' was unbanned by '+user.name+'.');
  358. } else {
  359. emit(socket, 'console', 'User '+target+' is not banned.');
  360. }
  361. return false;
  362. break;
  363. case 'unbanall':
  364. if (!user.can('ban')) {
  365. emit(socket, 'console', '/unbanall - Access denied.');
  366. return false;
  367. }
  368. logModCommand(room,'All bans and ip mutes have been lifted by '+user.name+'.');
  369. bannedIps = {};
  370. mutedIps = {};
  371. return false;
  372. break;
  373. case 'reply':
  374. case 'r':
  375. if (!target) return parseCommand(user, '?', cmd, room, socket);
  376. if (!user.lastPM) {
  377. emit(socket, 'console', 'No one has PMed you yet.');
  378. return false;
  379. }
  380. return parseCommand(user, 'msg', ''+(user.lastPM||'')+', '+target, room, socket);
  381. break;
  382. case 'msg':
  383. case 'pm':
  384. case 'whisper':
  385. case 'w':
  386. if (!target) return parseCommand(user, '?', cmd, room, socket);
  387. var targets = splitTarget(target);
  388. var targetUser = targets[0];
  389. if (!targets[1]) {
  390. emit(socket, 'console', 'You forgot the comma.');
  391. return parseCommand(user, '?', cmd, room, socket);
  392. }
  393. if (!targets[0] || !targetUser.connected) {
  394. if (target.indexOf(' ')) {
  395. emit(socket, 'console', 'User '+targets[2]+' not found. Did you forget a comma?');
  396. } else {
  397. emit(socket, 'console', 'User '+targets[2]+' not found. Did you misspell their name?');
  398. }
  399. return parseCommand(user, '?', cmd, room, socket);
  400. }
  401. // temporarily disable this because blarajan
  402. /* if (user.muted && !targetUser.can('mute', user)) {
  403. emit(socket, 'console', 'You can only private message members of the Moderation Team (users marked by %, @, &, or ~) when muted.');
  404. return false;
  405. } */
  406. if (!user.named) {
  407. emit(socket, 'console', 'You must choose a name before you can send private messages.');
  408. return false;
  409. }
  410. var message = {
  411. name: user.getIdentity(),
  412. pm: targetUser.getIdentity(),
  413. message: targets[1]
  414. };
  415. user.emit('console', message);
  416. targets[0].emit('console', message);
  417. targets[0].lastPM = user.userid;
  418. user.lastPM = targets[0].userid;
  419. return false;
  420. break;
  421. case 'ip':
  422. case 'getip':
  423. if (!target) {
  424. emit(socket, 'console', 'Your IP is: '+user.ip);
  425. return false;
  426. }
  427. var targetUser = Users.get(target);
  428. if (!targetUser) {
  429. emit(socket, 'console', 'User '+target+' not found.');
  430. return false;
  431. }
  432. if (!user.can('ip', targetUser)) {
  433. emit(socket, 'console', '/ip - Access denied.');
  434. return false;
  435. }
  436. emit(socket, 'console', 'User '+targetUser.name+' has IP: '+targetUser.ip);
  437. return false;
  438. break;
  439. case 'mute':
  440. case 'm':
  441. if (!target) return parseCommand(user, '?', cmd, room, socket);
  442. var targets = splitTarget(target);
  443. var targetUser = targets[0];
  444. if (!targetUser) {
  445. emit(socket, 'console', 'User '+targets[2]+' not found.');
  446. return false;
  447. }
  448. if (!user.can('mute', targetUser)) {
  449. emit(socket, 'console', '/mute - Access denied.');
  450. return false;
  451. }
  452. logModCommand(room,''+targetUser.name+' was muted by '+user.name+'.' + (targets[1] ? " (" + targets[1] + ")" : ""));
  453. targetUser.emit('message', user.name+' has muted you. '+targets[1]);
  454. var alts = targetUser.getAlts();
  455. if (alts.length) logModCommand(room,""+targetUser.name+"'s alts were also muted: "+alts.join(", "));
  456. targetUser.muted = true;
  457. rooms.lobby.sendIdentity(targetUser);
  458. for (var i=0; i<alts.length; i++) {
  459. var targetAlt = Users.get(alts[i]);
  460. if (targetAlt) {
  461. targetAlt.muted = true;
  462. rooms.lobby.sendIdentity(targetAlt);
  463. }
  464. }
  465. return false;
  466. break;
  467. case 'ipmute':
  468. if (!target) return parseCommand(user, '?', cmd, room, socket);
  469. var targetUser = Users.get(target);
  470. if (!targetUser) {
  471. emit(socket, 'console', 'User '+target+' not found.');
  472. return false;
  473. }
  474. if (!user.can('mute', targetUser)) {
  475. emit(socket, 'console', '/ipmute - Access denied.');
  476. return false;
  477. }
  478. logModCommand(room,''+targetUser.name+"'s IP was muted by "+user.name+'.');
  479. var alts = targetUser.getAlts();
  480. if (alts.length) logModCommand(room,""+targetUser.name+"'s alts were also muted: "+alts.join(", "));
  481. targetUser.muted = true;
  482. rooms.lobby.sendIdentity(targetUser);
  483. mutedIps[targetUser.ip] = targetUser.userid;
  484. for (var i=0; i<alts.length; i++) {
  485. var targetAlt = Users.get(alts[i]);
  486. if (targetAlt) {
  487. targetAlt.muted = true;
  488. rooms.lobby.sendIdentity(targetAlt);
  489. }
  490. }
  491. return false;
  492. break;
  493. case 'unmute':
  494. case 'um':
  495. if (!target) return parseCommand(user, '?', cmd, room, socket);
  496. var targetid = toUserid(target);
  497. var targetUser = Users.get(target);
  498. if (!targetUser) {
  499. emit(socket, 'console', 'User '+target+' not found.');
  500. return false;
  501. }
  502. if (!user.can('mute', targetUser)) {
  503. emit(socket, 'console', '/unmute - Access denied.');
  504. return false;
  505. }
  506. var success = false;
  507. for (var ip in mutedIps) {
  508. if (mutedIps[ip] === targetid) {
  509. delete mutedIps[ip];
  510. success = true;
  511. }
  512. }
  513. if (success) {
  514. logModCommand(room,''+(targetUser?targetUser.name:target)+"'s IP was unmuted by "+user.name+'.');
  515. }
  516. targetUser.muted = false;
  517. rooms.lobby.sendIdentity(targetUser);
  518. logModCommand(room,''+targetUser.name+' was unmuted by '+user.name+'.');
  519. return false;
  520. break;
  521. case 'promote':
  522. case 'demote':
  523. if (!target) return parseCommand(user, '?', cmd, room, socket);
  524. var targets = splitTarget(target, true);
  525. var targetUser = targets[0];
  526. var userid = toUserid(targets[2]);
  527. var currentGroup = ' ';
  528. if (targetUser) {
  529. currentGroup = targetUser.group;
  530. } else if (Users.usergroups[userid]) {
  531. currentGroup = Users.usergroups[userid].substr(0,1);
  532. }
  533. var name = targetUser ? targetUser.name : targets[2];
  534. var nextGroup = targets[1] ? targets[1] : Users.getNextGroupSymbol(currentGroup, cmd === 'demote');
  535. if (targets[1] === 'deauth') nextGroup = config.groupsranking[0];
  536. if (!config.groups[nextGroup]) {
  537. emit(socket, 'console', 'Group \'' + nextGroup + '\' does not exist.');
  538. return false;
  539. }
  540. if (!user.checkPromotePermission(currentGroup, nextGroup)) {
  541. emit(socket, 'console', '/promote - Access denied.');
  542. return false;
  543. }
  544. var isDemotion = (config.groups[nextGroup].rank < config.groups[currentGroup].rank);
  545. if (!Users.setOfflineGroup(name, nextGroup)) {
  546. emit(socket, 'console', '/promote - WARNING: This user is offline and could be unregistered. Use /forcepromote if you\'re sure you want to risk it.');
  547. return false;
  548. }
  549. var groupName = (config.groups[nextGroup].name || nextGroup || '').trim() || 'a regular user';
  550. var entry = ''+name+' was '+(isDemotion?'demoted':'promoted')+' to ' + groupName + ' by '+user.name+'.';
  551. logModCommand(room, entry, isDemotion);
  552. if (isDemotion) {
  553. rooms.lobby.logEntry(entry);
  554. emit(socket, 'console', 'You demoted ' + name + ' to ' + groupName + '.');
  555. if (targetUser) {
  556. targetUser.emit('console', 'You were demoted to ' + groupName + ' by ' + user.name + '.');
  557. }
  558. }
  559. rooms.lobby.sendIdentity(targetUser);
  560. return false;
  561. break;
  562. case 'forcepromote':
  563. // warning: never document this command in /help
  564. if (!user.can('forcepromote')) {
  565. emit(socket, 'console', '/forcepromote - Access denied.');
  566. return false;
  567. }
  568. var targets = splitTarget(target, true);
  569. var name = targets[2];
  570. var nextGroup = targets[1] ? targets[1] : Users.getNextGroupSymbol(' ', false);
  571. if (!Users.setOfflineGroup(name, nextGroup, true)) {
  572. emit(socket, 'console', '/forcepromote - Don\'t forcepromote unless you have to.');
  573. return false;
  574. }
  575. var groupName = config.groups[nextGroup].name || nextGroup || '';
  576. logModCommand(room,''+name+' was promoted to ' + (groupName.trim()) + ' by '+user.name+'.');
  577. return false;
  578. break;
  579. case 'deauth':
  580. return parseCommand(user, 'demote', target+', deauth', room, socket);
  581. break;
  582. case 'modchat':
  583. if (!target) {
  584. emit(socket, 'console', 'Moderated chat is currently set to: '+config.modchat);
  585. return false;
  586. }
  587. if (!user.can('modchat')) {
  588. emit(socket, 'console', '/modchat - Access denied.');
  589. return false;
  590. }
  591. target = target.toLowerCase();
  592. switch (target) {
  593. case 'on':
  594. case 'true':
  595. case 'yes':
  596. config.modchat = true;
  597. break;
  598. case 'off':
  599. case 'false':
  600. case 'no':
  601. config.modchat = false;
  602. break;
  603. default:
  604. if (!config.groups[target]) {
  605. emit(socket, 'console', 'That moderated chat setting is unrecognized.');
  606. return false;
  607. }
  608. if (config.groupsranking.indexOf(target) > 1 && !user.can('modchatall')) {
  609. emit(socket, 'console', '/modchat - Access denied for setting higher than ' + config.groupsranking[1] + '.');
  610. return false;
  611. }
  612. config.modchat = target;
  613. break;
  614. }
  615. if (config.modchat === true) {
  616. room.addRaw('<div class="broadcast-red"><b>Moderated chat was enabled!</b><br />Only registered users can talk.</div>');
  617. } else if (!config.modchat) {
  618. room.addRaw('<div class="broadcast-blue"><b>Moderated chat was disabled!</b><br />Anyone may talk now.</div>');
  619. } else {
  620. var modchat = sanitize(config.modchat);
  621. room.addRaw('<div class="broadcast-red"><b>Moderated chat was set to '+modchat+'!</b><br />Only users of rank '+modchat+' and higher can talk.</div>');
  622. }
  623. logModCommand(room,user.name+' set modchat to '+config.modchat,true);
  624. return false;
  625. break;
  626. case 'declare':
  627. if (!target) return parseCommand(user, '?', cmd, room, socket);
  628. if (!user.can('declare')) {
  629. emit(socket, 'console', '/declare - Access denied.');
  630. return false;
  631. }
  632. room.addRaw('<div class="broadcast-blue"><b>'+target+'</b></div>');
  633. logModCommand(room,user.name+' declared '+target,true);
  634. return false;
  635. break;
  636. case 'announce':
  637. case 'wall':
  638. if (!target) return parseCommand(user, '?', cmd, room, socket);
  639. if (!user.can('announce')) {
  640. emit(socket, 'console', '/announce - Access denied.');
  641. return false;
  642. }
  643. return '/announce '+target;
  644. break;
  645. case 'hotpatch':
  646. if (!target) return parseCommand(user, '?', cmd, room, socket);
  647. if (!user.can('hotpatch')) {
  648. emit(socket, 'console', '/hotpatch - Access denied.');
  649. return false;
  650. }
  651. if (target === 'all') {
  652. for (var i in require.cache) delete require.cache[i];
  653. Tools = require('./tools.js');
  654. parseCommand = require('./chat-commands.js').parseCommand;
  655. sim = require('./battles.js');
  656. BattlePokemon = sim.BattlePokemon;
  657. BattleSide = sim.BattleSide;
  658. Battle = sim.Battle;
  659. emit(socket, 'console', 'The game engine has been hot-patched.');
  660. return false;
  661. } else if (target === 'data') {
  662. for (var i in require.cache) delete require.cache[i];
  663. Tools = require('./tools.js');
  664. emit(socket, 'console', 'Game resources have been hot-patched.');
  665. return false;
  666. } else if (target === 'chat') {
  667. for (var i in require.cache) delete require.cache[i];
  668. parseCommand = require('./chat-commands.js').parseCommand;
  669. emit(socket, 'console', 'Chat commands have been hot-patched.');
  670. return false;
  671. }
  672. emit(socket, 'console', 'Your hot-patch command was unrecognized.');
  673. return false;
  674. break;
  675. case 'savelearnsets':
  676. if (user.can('hotpatch')) {
  677. emit(socket, 'console', '/savelearnsets - Access denied.');
  678. return false;
  679. }
  680. fs.writeFile('data/learnsets.js', 'exports.BattleLearnsets = '+JSON.stringify(BattleLearnsets)+";\n");
  681. emit(socket, 'console', 'learnsets.js saved.');
  682. return false;
  683. break;
  684. case 'rating':
  685. case 'ranking':
  686. case 'rank':
  687. case 'ladder':
  688. emit(socket, 'console', 'You are using an old version of Pokemon Showdown. Please reload the page.');
  689. return false;
  690. break;
  691. case 'nick':
  692. if (!target) return parseCommand(user, '?', cmd, room, socket);
  693. user.rename(target);
  694. return false;
  695. break;
  696. case 'disableladder':
  697. if (!user.can('disableladder')) {
  698. emit(socket, 'console', '/disableladder - Access denied.');
  699. return false;
  700. }
  701. if (LoginServer.disabled) {
  702. emit(socket, 'console', '/disableladder - Ladder is already disabled.');
  703. return false;
  704. }
  705. LoginServer.disabled = true;
  706. logModCommand(room, 'The ladder was disabled by ' + user.name + '.', true);
  707. room.addRaw('<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>');
  708. return false;
  709. break;
  710. case 'enableladder':
  711. if (!user.can('disableladder')) {
  712. emit(socket, 'console', '/enable - Access denied.');
  713. return false;
  714. }
  715. if (!LoginServer.disabled) {
  716. emit(socket, 'console', '/enable - Ladder is already enabled.');
  717. return false;
  718. }
  719. LoginServer.disabled = false;
  720. logModCommand(room, 'The ladder was enabled by ' + user.name + '.', true);
  721. room.addRaw('<div class="broadcast-green"><b>The ladder is now back.</b><br />Rated games will update the ladder now.</div>');
  722. return false;
  723. break;
  724. case 'savereplay':
  725. if (!room || !room.battle) return false;
  726. var data = room.log.join("\n");
  727. var datahash = crypto.createHash('md5').update(data.replace(/[^(\x20-\x7F)]+/g,'')).digest('hex');
  728. LoginServer.request('prepreplay', {
  729. id: room.id.substr(7),
  730. loghash: datahash,
  731. p1: room.p1.name,
  732. p2: room.p2.name,
  733. format: room.format
  734. }, function(success) {
  735. emit(socket, 'command', {
  736. command: 'savereplay',
  737. log: data,
  738. room: 'lobby',
  739. id: room.id.substr(7)
  740. });
  741. });
  742. return false;
  743. break;
  744. case 'trn':
  745. var commaIndex = target.indexOf(',');
  746. var targetName = target;
  747. var targetAuth = false;
  748. var targetToken = '';
  749. if (commaIndex >= 0) {
  750. targetName = target.substr(0,commaIndex);
  751. target = target.substr(commaIndex+1);
  752. commaIndex = target.indexOf(',');
  753. targetAuth = target;
  754. if (commaIndex >= 0) {
  755. targetAuth = !!parseInt(target.substr(0,commaIndex),10);
  756. targetToken = target.substr(commaIndex+1);
  757. }
  758. }
  759. user.rename(targetName, targetToken, targetAuth, socket);
  760. return false;
  761. break;
  762. case 'forcerename':
  763. case 'fr':
  764. if (!target) return parseCommand(user, '?', cmd, room, socket);
  765. var targets = splitTarget(target);
  766. var targetUser = targets[0];
  767. if (!targetUser) {
  768. emit(socket, 'console', 'User '+targets[2]+' not found.');
  769. return false;
  770. }
  771. if (!user.can('forcerename', targetUser)) {
  772. emit(socket, 'console', '/forcerename - Access denied.');
  773. return false;
  774. }
  775. if (targetUser.userid === toUserid(targets[2])) {
  776. var entry = ''+targetUser.name+' was forced to choose a new name by '+user.name+'.' + (targets[1] ? " (" + targets[1] + ")" : "");
  777. logModCommand(room, entry, true);
  778. rooms.lobby.sendAuth(entry);
  779. if (room.id !== 'lobby') {
  780. room.add(entry);
  781. } else {
  782. room.logEntry(entry);
  783. }
  784. targetUser.resetName();
  785. targetUser.emit('nameTaken', {reason: user.name+" has forced you to change your name. "+targets[1]});
  786. } else {
  787. emit(socket, 'console', "User "+targetUser.name+" is no longer using that name.");
  788. }
  789. return false;
  790. break;
  791. case 'forcerenameto':
  792. case 'frt':
  793. if (!target) return parseCommand(user, '?', cmd, room, socket);
  794. var targets = splitTarget(target);
  795. var targetUser = targets[0];
  796. if (!targetUser) {
  797. emit(socket, 'console', 'User '+targets[2]+' not found.');
  798. return false;
  799. }
  800. if (!targets[1]) {
  801. emit(socket, 'console', 'No new name was specified.');
  802. return false;
  803. }
  804. if (!user.can('forcerenameto', targetUser)) {
  805. emit(socket, 'console', '/forcerenameto - Access denied.');
  806. return false;
  807. }
  808. if (targetUser.userid === toUserid(targets[2])) {
  809. var entry = ''+targetUser.name+' was forcibly renamed to '+targets[1]+' by '+user.name+'.';
  810. logModCommand(room, entry, true);
  811. rooms.lobby.sendAuth(entry);
  812. if (room.id !== 'lobby') {
  813. room.add(entry);
  814. } else {
  815. room.logEntry(entry);
  816. }
  817. targetUser.forceRename(targets[1]);
  818. } else {
  819. emit(socket, 'console', "User "+targetUser.name+" is no longer using that name.");
  820. }
  821. return false;
  822. break;
  823. // INFORMATIONAL COMMANDS
  824. case 'data':
  825. case '!data':
  826. case 'stats':
  827. case '!stats':
  828. case 'dex':
  829. case '!dex':
  830. case 'pokedex':
  831. case '!pokedex':
  832. showOrBroadcastStart(user, cmd, room, socket, message);
  833. var dataMessages = getDataMessage(target);
  834. for (var i=0; i<dataMessages.length; i++) {
  835. if (cmd.substr(0,1) !== '!') {
  836. sendData(socket, '>'+room.id+'\n'+dataMessages[i]);
  837. } else if (user.can('broadcast') && canTalk(user, room)) {
  838. room.add(dataMessages[i]);
  839. }
  840. }
  841. return false;
  842. break;
  843. case 'learnset':
  844. case '!learnset':
  845. case 'learn':
  846. case '!learn':
  847. case 'learnall':
  848. case '!learnall':
  849. case 'learn5':
  850. case '!learn5':
  851. var lsetData = {set:{}};
  852. var targets = target.split(',');
  853. if (!targets[1]) return parseCommand(user, 'help', 'learn', room, socket);
  854. var template = Tools.getTemplate(targets[0]);
  855. var move = {};
  856. var problem;
  857. var all = (cmd.substr(cmd.length-3) === 'all');
  858. if (cmd === 'learn5' || cmd === '!learn5') lsetData.set.level = 5;
  859. showOrBroadcastStart(user, cmd, room, socket, message);
  860. if (!template.exists) {
  861. showOrBroadcast(user, cmd, room, socket,
  862. 'Pokemon "'+template.id+'" not found.');
  863. return false;
  864. }
  865. for (var i=1, len=targets.length; i<len; i++) {
  866. move = Tools.getMove(targets[i]);
  867. if (!move.exists) {
  868. showOrBroadcast(user, cmd, room, socket,
  869. 'Move "'+move.id+'" not found.');
  870. return false;
  871. }
  872. problem = Tools.checkLearnset(move, template, lsetData);
  873. if (problem) break;
  874. }
  875. var buffer = ''+template.name+(problem?" <span class=\"message-learn-cannotlearn\">can't</span> learn ":" <span class=\"message-learn-canlearn\">can</span> learn ")+(targets.length>2?"these moves":move.name);
  876. if (!problem) {
  877. var sourceNames = {E:"egg",S:"event",D:"dream world"};
  878. if (lsetData.sources || lsetData.sourcesBefore) buffer += " only when obtained from:<ul class=\"message-learn-list\">";
  879. if (lsetData.sources) {
  880. var sources = lsetData.sources.sort();
  881. var prevSource;
  882. var prevSourceType;
  883. for (var i=0, len=sources.length; i<len; i++) {
  884. var source = sources[i];
  885. if (source.substr(0,2) === prevSourceType) {
  886. if (prevSourceCount < 0) buffer += ": "+source.substr(2);
  887. else if (all || prevSourceCount < 3) buffer += ', '+source.substr(2);
  888. else if (prevSourceCount == 3) buffer += ', ...';
  889. prevSourceCount++;
  890. continue;
  891. }
  892. prevSourceType = source.substr(0,2);
  893. prevSourceCount = source.substr(2)?0:-1;
  894. buffer += "<li>gen "+source.substr(0,1)+" "+sourceNames[source.substr(1,1)];
  895. if (prevSourceType === '5E' && template.maleOnlyDreamWorld) buffer += " (cannot have DW ability)";
  896. if (source.substr(2)) buffer += ": "+source.substr(2);
  897. }
  898. }
  899. if (lsetData.sourcesBefore) buffer += "<li>any generation before "+(lsetData.sourcesBefore+1);
  900. buffer += "</ul>";
  901. }
  902. showOrBroadcast(user, cmd, room, socket,
  903. buffer);
  904. return false;
  905. break;
  906. case 'uptime':
  907. case '!uptime':
  908. var uptime = process.uptime();
  909. var uptimeText;
  910. if (uptime > 24*60*60) {
  911. var uptimeDays = Math.floor(uptime/(24*60*60));
  912. uptimeText = ''+uptimeDays+' '+(uptimeDays == 1 ? 'day' : 'days');
  913. var uptimeHours = Math.floor(uptime/(60*60)) - uptimeDays*24;
  914. if (uptimeHours) uptimeText += ', '+uptimeHours+' '+(uptimeHours == 1 ? 'hour' : 'hours');
  915. } else {
  916. uptimeText = uptime.seconds().duration();
  917. }
  918. showOrBroadcastStart(user, cmd, room, socket, message);
  919. showOrBroadcast(user, cmd, room, socket,
  920. '<div class="infobox">' +
  921. 'Uptime: <b>'+uptimeText+'</b>'+
  922. '</div>');
  923. return false;
  924. break;
  925. case 'groups':
  926. case '!groups':
  927. showOrBroadcastStart(user, cmd, room, socket, message);
  928. showOrBroadcast(user, cmd, room, socket,
  929. '<div class="infobox">' +
  930. '+ <b>Voice</b> - They can use ! commands like !groups, and talk during moderated chat<br />' +
  931. '% <b>Driver</b> - The above, and they can also mute users and run tournaments<br />' +
  932. '@ <b>Moderator</b> - The above, and they can ban users and check for alts<br />' +
  933. '&amp; <b>Leader</b> - The above, and they can promote moderators and force ties<br />'+
  934. '~ <b>Administrator</b> - They can do anything, like change what this message says'+
  935. '</div>');
  936. return false;
  937. break;
  938. case 'opensource':
  939. case '!opensource':
  940. showOrBroadcastStart(user, cmd, room, socket, message);
  941. showOrBroadcast(user, cmd, room, socket,
  942. '<div class="infobox">Pokemon Showdown is open source:<br />- Language: JavaScript<br />- <a href="https://github.com/Zarel/Pokemon-Showdown/commits/master" target="_blank">What\'s new?</a><br />- <a href="https://github.com/Zarel/Pokemon-Showdown" target="_blank">Server source code</a><br />- <a href="https://github.com/Zarel/Pokemon-Showdown-Client" target="_blank">Client source code</a></div>');
  943. return false;
  944. break;
  945. case 'avatars':
  946. case '!avatars':
  947. showOrBroadcastStart(user, cmd, room, socket, message);
  948. showOrBroadcast(user, cmd, room, socket,
  949. '<div class="infobox">Want a custom avatar?<br />- <a href="/sprites/trainers/" target="_blank">How to change your avatar</a></div>');
  950. return false;
  951. break;
  952. case 'intro':
  953. case 'introduction':
  954. case '!intro':
  955. case '!introduction':
  956. showOrBroadcastStart(user, cmd, room, socket, message);
  957. showOrBroadcast(user, cmd, room, socket,
  958. '<div class="infobox">New to competitive pokemon?<br />' +
  959. '- <a href="http://www.smogon.com/dp/articles/intro_comp_pokemon" target="_blank">An introduction to competitive pokemon</a><br />' +
  960. '- <a href="http://www.smogon.com/bw/articles/bw_tiers" target="_blank">What do "OU", "UU", etc mean?</a><br />' +
  961. '- <a href="http://www.smogon.com/bw/banlist/" target="_blank">What are the rules for each format? What is "Sleep Clause"?</a>' +
  962. '</div>');
  963. return false;
  964. break;
  965. case 'calc':
  966. case '!calc':
  967. case 'calculator':
  968. case '!calculator':
  969. showOrBroadcastStart(user, cmd, room, socket, message);
  970. showOrBroadcast(user, cmd , room , socket,
  971. '<div class="infobox">Pokemon Showdown! damage calculator. (Courtesy of Honko)<br />' +
  972. '- <a href="http://pokemonshowdown.com/damagecalc/" target="_blank">Damage Calculator</a><br />' +
  973. '</div>');
  974. return false;
  975. break;
  976. case 'cap':
  977. case '!cap':
  978. showOrBroadcastStart(user, cmd, room, socket, message);
  979. showOrBroadcast(user, cmd, room, socket,
  980. '<div class="infobox">An introduction to the Create-A-Pokemon project:<br />' +
  981. '- <a href="http://www.smogon.com/cap/" target="_blank">CAP project website and description</a><br />' +
  982. '- <a href="http://www.smogon.com/forums/showthread.php?t=48782" target="_blank">What Pokemon have been made?</a><br />' +
  983. '- <a href="http://www.smogon.com/forums/showthread.php?t=3464513" target="_blank">Talk about the metagame here</a><br />' +
  984. '- <a href="http://www.smogon.com/forums/showthread.php?t=3466826" target="_blank">Practice BW CAP teams</a>' +
  985. '</div>');
  986. return false;
  987. break;
  988. case 'om':
  989. case 'othermetas':
  990. case '!om':
  991. case '!othermetas':
  992. showOrBroadcastStart(user, cmd, room, socket, message);
  993. showOrBroadcast(user, cmd, room, socket,
  994. '<div class="infobox">Information on the Other Metagames:<br />' +
  995. '- <a href="http://www.smogon.com/forums/showthread.php?t=3463764" target="_blank">Balanced Hackmons</a><br />' +
  996. '- <a href="http://www.smogon.com/forums/showthread.php?t=3471810" target="_blank">Dream World OU</a><br />' +
  997. '- <a href="http://www.smogon.com/forums/showthread.php?t=3467120" target="_blank">Glitchmons</a><br />' +
  998. '- <a href="http://www.smogon.com/sim/seasonal" target="_blank">Seasonal: Spring Forward</a><br />' +
  999. '- <a href="http://www.smogon.com/forums/showthread.php?t=3476469" target="_blank">Smogon Doubles</a><br />' +
  1000. '- <a href="http://www.smogon.com/forums/showthread.php?t=3471161" target="_blank">VGC 2013</a>' +
  1001. '</div>');
  1002. return false;
  1003. break;
  1004. case 'rules':
  1005. case 'rule':
  1006. case '!rules':
  1007. case '!rule':
  1008. showOrBroadcastStart(user, cmd, room, socket, message);
  1009. showOrBroadcast(user, cmd, room, socket,
  1010. '<div class="infobox">Please follow the rules:<br />' +
  1011. '- <a href="http://pokemonshowdown.com/rules" target="_blank">Rules</a><br />' +
  1012. '</div>');
  1013. return false;
  1014. break;
  1015. case 'faq':
  1016. case '!faq':
  1017. target = target.toLowerCase();
  1018. var buffer = '<div class="infobox">';
  1019. var matched = false;
  1020. if (!target || target === 'all') {
  1021. matched = true;
  1022. buffer += '<a href="http://www.smogon.com/sim/faq" target="_blank">Frequently Asked Questions</a><br />';
  1023. }
  1024. if (target === 'all' || target === 'deviation') {
  1025. matched = true;
  1026. buffer += '<a href="http://www.smogon.com/sim/faq#deviation" target="_blank">Why did this user gain or lose so many points?</a><br />';
  1027. }
  1028. if (target === 'all' || target === 'doubles' || target === 'triples' || target === 'rotation') {
  1029. matched = true;
  1030. buffer += '<a href="http://www.smogon.com/sim/faq#doubles" target="_blank">Can I play doubles/triples/rotation battles here?</a><br />';
  1031. }
  1032. if (target === 'all' || target === 'randomcap') {
  1033. matched = true;
  1034. buffer += '<a href="http://www.smogon.com/sim/faq#randomcap" target="_blank">What is this fakemon and what is it doing in my random battle?</a><br />';
  1035. }
  1036. if (target === 'all' || target === 'restarts') {
  1037. matched = true;
  1038. buffer += '<a href="http://www.smogon.com/sim/faq#restarts" target="_blank">Why is the server restarting?</a><br />';
  1039. }
  1040. if (target === 'all' || target === 'staff') {
  1041. matched = true;
  1042. buffer += '<a href="http://www.smogon.com/sim/staff_faq" target="_blank">Staff FAQ</a><br />';
  1043. }
  1044. if (!matched) {
  1045. emit(socket, 'console', 'The FAQ entry "'+target+'" was not found. Try /faq for general help.');
  1046. return false;
  1047. }
  1048. buffer += '</div>';
  1049. showOrBroadcastStart(user, cmd, room, socket, message);
  1050. showOrBroadcast(user, cmd, room, socket, buffer);
  1051. return false;
  1052. break;
  1053. case 'banlists':
  1054. case 'tiers':
  1055. case '!banlists':
  1056. case '!tiers':
  1057. showOrBroadcastStart(user, cmd, room, socket, message);
  1058. showOrBroadcast(user, cmd, room, socket,
  1059. '<div class="infobox">Smogon tiers:<br />' +
  1060. '- <a href="http://www.smogon.com/bw/banlist/" target="_blank">The banlists for each tier</a><br />' +
  1061. '- <a href="http://www.smogon.com/bw/tiers/uber" target="_blank">Uber Pokemon</a><br />' +
  1062. '- <a href="http://www.smogon.com/bw/tiers/ou" target="_blank">Overused Pokemon</a><br />' +
  1063. '- <a href="http://www.smogon.com/bw/tiers/uu" target="_blank">Underused Pokemon</a><br />' +
  1064. '- <a href="http://www.smogon.com/bw/tiers/ru" target="_blank">Rarelyused Pokemon</a><br />' +
  1065. '- <a href="http://www.smogon.com/bw/tiers/nu" target="_blank">Neverused Pokemon</a><br />' +
  1066. '- <a href="http://www.smogon.com/bw/tiers/lc" target="_blank">Little Cup Pokemon</a><br />' +
  1067. '</div>');
  1068. return false;
  1069. break;
  1070. case 'analysis':
  1071. case '!analysis':
  1072. case 'strategy':
  1073. case '!strategy':
  1074. case 'smogdex':
  1075. case '!smogdex':
  1076. var targets = target.split(',');
  1077. var pokemon = Tools.getTemplate(targets[0]);
  1078. var item = Tools.getItem(targets[0]);
  1079. var move = Tools.getMove(targets[0]);
  1080. var ability = Tools.getAbility(targets[0]);
  1081. var atLeastOne = false;
  1082. var generation = (targets[1] || "bw").trim().toLowerCase();
  1083. var genNumber = 5;
  1084. showOrBroadcastStart(user, cmd, room, socket, message);
  1085. if (generation === "bw" || generation === "bw2" || generation === "5" || generation === "five") {
  1086. generation = "bw";
  1087. } else if (generation === "dp" || generation === "dpp" || generation === "4" || generation === "four") {
  1088. generation = "dp";
  1089. genNumber = 4;
  1090. } else if (generation === "adv" || generation === "rse" || generation === "rs" || generation === "3" || generation === "three") {
  1091. generation = "rs";
  1092. genNumber = 3;
  1093. } else if (generation === "gsc" || generation === "gs" || generation === "2" || generation === "two") {
  1094. generation = "gs";
  1095. genNumber = 2;
  1096. } else if(generation === "rby" || generation === "rb" || generation === "1" || generation === "one") {
  1097. generation = "rb";
  1098. genNumber = 1;
  1099. } else {
  1100. generation = "bw";
  1101. }
  1102. // Pokemon
  1103. if (pokemon.exists) {
  1104. atLeastOne = true;
  1105. if (genNumber < pokemon.gen) {
  1106. showOrBroadcast(user, cmd, room, socket, pokemon.name+' did not exist in '+generation.toUpperCase()+'!');
  1107. return false;
  1108. }
  1109. if (pokemon.tier === 'G4CAP' || pokemon.tier === 'G5CAP') {
  1110. generation = "cap";
  1111. }
  1112. var poke = pokemon.name.toLowerCase();
  1113. if (poke === 'nidoranm') poke = 'nidoran-m';
  1114. if (poke === 'nidoranf') poke = 'nidoran-f';
  1115. if (poke === 'farfetch\'d') poke = 'farfetchd';
  1116. if (poke === 'mr. mime') poke = 'mr_mime';
  1117. if (poke === 'mime jr.') poke = 'mime_jr';
  1118. if (poke === 'deoxys-attack' || poke === 'deoxys-defense' || poke === 'deoxys-speed' || poke === 'kyurem-black' || poke === 'kyurem-white') poke = poke.substr(0,8);
  1119. if (poke === 'wormadam-trash') poke = 'wormadam-s';
  1120. if (poke === 'wormadam-sandy') poke = 'wormadam-g';
  1121. if (poke === 'rotom-wash' || poke === 'rotom-frost' || poke === 'rotom-heat') poke = poke.substr(0,7);
  1122. if (poke === 'rotom-mow') poke = 'rotom-c';
  1123. if (poke === 'rotom-fan') poke = 'rotom-s';
  1124. if (poke === 'giratina-origin' || poke === 'tornadus-therian' || poke === 'landorus-therian') poke = poke.substr(0,10);
  1125. if (poke === 'shaymin-sky') poke = 'shaymin-s';
  1126. if (poke === 'arceus') poke = 'arceus-normal';
  1127. if (poke === 'thundurus-therian') poke = 'thundurus-t';
  1128. showOrBroadcast(user, cmd, room, socket,
  1129. '<a href="http://www.smogon.com/'+generation+'/pokemon/'+poke+'" target="_blank">'+generation.toUpperCase()+' '+pokemon.name+' analysis</a>, brought to you by <a href="http://www.smogon.com" target="_blank">Smogon University</a>');
  1130. }
  1131. // Item
  1132. if (item.exists && genNumber > 1) {
  1133. atLeastOne = true;
  1134. var itemName = item.name.toLowerCase().replace(' ', '_');
  1135. showOrBroadcast(user, cmd, room, socket,
  1136. '<a href="http://www.smogon.com/'+generation+'/items/'+itemName+'" target="_blank">'+generation.toUpperCase()+' '+item.name+' item analysis</a>, brought to you by <a href="http://www.smogon.com" target="_blank">Smogon University</a>');
  1137. }
  1138. // Ability
  1139. if (ability.exists && genNumber > 2) {
  1140. atLeastOne = true;
  1141. var abilityName = ability.name.toLowerCase().replace(' ', '_');
  1142. showOrBroadcast(user, cmd, room, socket,
  1143. '<a href="http://www.smogon.com/'+generation+'/abilities/'+abilityName+'" target="_blank">'+generation.toUpperCase()+' '+ability.name+' ability analysis</a>, brought to you by <a href="http://www.smogon.com" target="_blank">Smogon University</a>');
  1144. }
  1145. // Move
  1146. if (move.exists) {
  1147. atLeastOne = true;
  1148. var moveName = move.name.toLowerCase().replace(' ', '_');
  1149. showOrBroadcast(user, cmd, room, socket,
  1150. '<a href="http://www.smogon.com/'+generation+'/moves/'+moveName+'" target="_blank">'+generation.toUpperCase()+' '+move.name+' move analysis</a>, brought to you by <a href="http://www.smogon.com" target="_blank">Smogon University</a>');
  1151. }
  1152. if (!atLeastOne) {
  1153. showOrBroadcast(user, cmd, room, socket, 'Pokemon, item, move, or ability not found for generation ' + generation.toUpperCase() + '.');
  1154. return false;
  1155. }
  1156. return false;
  1157. break;
  1158. case 'join':
  1159. var targetRoom = Rooms.get(target);
  1160. if (!targetRoom) {
  1161. emit(socket, 'console', "The room '"+target+"' does not exist.");
  1162. return false;
  1163. }
  1164. if (!user.joinRoom(targetRoom, socket)) {
  1165. emit(socket, 'console', "The room '"+target+"' could not be joined (most likely, you're already in it).");
  1166. return false;
  1167. }
  1168. return false;
  1169. break;
  1170. case 'leave':
  1171. case 'part':
  1172. if (room.id === 'lobby') return false;
  1173. user.leaveRoom(room, socket);
  1174. return false;
  1175. break;
  1176. // Battle commands
  1177. case 'reset':
  1178. case 'restart':
  1179. emit(socket, 'console', 'This functionality is no longer available.');
  1180. return false;
  1181. break;
  1182. case 'move':
  1183. case 'attack':
  1184. case 'mv':
  1185. if (!room.decision) { emit(socket, 'console', 'You can only do this in battle rooms.'); return false; }
  1186. room.decision(user, 'choose', 'move '+target);
  1187. return false;
  1188. break;
  1189. case 'switch':
  1190. case 'sw':
  1191. if (!room.decision) { emit(socket, 'console', 'You can only do this in battle rooms.'); return false; }
  1192. room.decision(user, 'choose', 'switch '+parseInt(target,10));
  1193. return false;
  1194. break;
  1195. case 'choose':
  1196. if (!room.decision) { emit(socket, 'console', 'You can only do this in battle rooms.'); return false; }
  1197. room.decision(user, 'choose', target);
  1198. return false;
  1199. break;
  1200. case 'undo':
  1201. if (!room.decision) { emit(socket, 'console', 'You can only do this in battle rooms.'); return false; }
  1202. room.decision(user, 'undo', target);
  1203. return false;
  1204. break;
  1205. case 'team':
  1206. if (!room.decision) { emit(socket, 'console', 'You can only do this in battle rooms.'); return false; }
  1207. room.decision(user, 'choose', 'team '+target);
  1208. return false;
  1209. break;
  1210. case 'search':
  1211. case 'cancelsearch':
  1212. if (!room.searchBattle) { emit(socket, 'console', 'You can only do this in lobby rooms.'); return false; }
  1213. if (target) {
  1214. room.searchBattle(user, target);
  1215. } else {
  1216. room.cancelSearch(user);
  1217. }
  1218. return false;
  1219. break;
  1220. case 'challenge':
  1221. case 'chall':
  1222. var targets = splitTarget(target);
  1223. var targetUser = targets[0];
  1224. target = targets[1];
  1225. if (!targetUser || !targetUser.connected) {
  1226. emit(socket, 'message', "The user '"+targets[2]+"' was not found.");
  1227. return false;
  1228. }
  1229. if (targetUser.blockChallenges && !user.can('bypassblocks', targetUser)) {
  1230. emit(socket, 'message', "The user '"+targets[2]+"' is not accepting challenges right now.");
  1231. return false;
  1232. }
  1233. if (typeof target !== 'string') target = 'customgame';
  1234. var problems = Tools.validateTeam(user.team, target);
  1235. if (problems) {
  1236. emit(socket, 'message', "Your team was rejected for the following reasons:\n\n- "+problems.join("\n- "));
  1237. return false;
  1238. }
  1239. user.makeChallenge(targetUser, target);
  1240. return false;
  1241. break;
  1242. case 'away':
  1243. case 'idle':
  1244. case 'blockchallenges':
  1245. user.blockChallenges = true;
  1246. emit(socket, 'console', 'You are now blocking all incoming challenge requests.');
  1247. return false;
  1248. break;
  1249. case 'back':
  1250. case 'allowchallenges':
  1251. user.blockChallenges = false;
  1252. emit(socket, 'console', 'You are available for challenges from now on.');
  1253. return false;
  1254. break;
  1255. case 'cancelchallenge':
  1256. case 'cchall':
  1257. user.cancelChallengeTo(target);
  1258. return false;
  1259. break;
  1260. case 'accept':
  1261. var userid = toUserid(target);
  1262. var format = 'debugmode';
  1263. if (user.challengesFrom[userid]) format = user.challengesFrom[userid].format;
  1264. var problems = Tools.validateTeam(user.team, format);
  1265. if (problems) {
  1266. emit(socket, 'message', "Your team was rejected for the following reasons:\n\n- "+problems.join("\n- "));
  1267. return false;
  1268. }
  1269. user.acceptChallengeFrom(userid);
  1270. return false;
  1271. break;
  1272. case 'reject':
  1273. user.rejectChallengeFrom(toUserid(target));
  1274. return false;
  1275. break;
  1276. case 'saveteam':
  1277. case 'utm':
  1278. try {
  1279. user.team = JSON.parse(target);
  1280. user.emit('update', {team: 'saved', room: 'teambuilder'});
  1281. } catch (e) {
  1282. emit(socket, 'console', 'Not a valid team.');
  1283. }
  1284. return false;
  1285. break;
  1286. case 'joinbattle':
  1287. case 'partbattle':
  1288. if (!room.joinBattle) { emit(socket, 'console', 'You can only do this in battle rooms.'); return false; }
  1289. room.joinBattle(user);
  1290. return false;
  1291. break;
  1292. case 'leavebattle':
  1293. if (!room.leaveBattle) { emit(socket, 'console', 'You can only do this in battle rooms.'); return false; }
  1294. room.leaveBattle(user);
  1295. return false;
  1296. break;
  1297. case 'kickinactive':
  1298. if (room.requestKickInactive) {
  1299. room.requestKickInactive(user);
  1300. } else {
  1301. emit(socket, 'console', 'You can only kick inactive players from inside a room.');
  1302. }
  1303. return false;
  1304. break;
  1305. case 'timer':
  1306. target = toId(target);
  1307. if (room.requestKickInactive) {
  1308. if (target === 'off' || target === 'stop') {
  1309. room.stopKickInactive(user, user.can('timer'));
  1310. } else if (target === 'on' || !target) {
  1311. room.requestKickInactive(user, user.can('timer'));
  1312. } else {
  1313. emit(socket, 'console', "'"+target+"' is not a recognized timer state.");
  1314. }
  1315. } else {
  1316. emit(socket, 'console', 'You can only set the timer from inside a room.');
  1317. }
  1318. return false;
  1319. break;
  1320. break;
  1321. case 'lobbychat':
  1322. target = toId(target);
  1323. if (target === 'off') {
  1324. user.blockLobbyChat = true;
  1325. emit(socket, 'console', 'You are now blocking lobby chat.');
  1326. } else {
  1327. user.blockLobbyChat = false;
  1328. emit(socket, 'console', 'You are now receiving lobby chat.');
  1329. }
  1330. return false;
  1331. break;
  1332. break;
  1333. case 'a':
  1334. if (user.can('battlemessage')) {
  1335. // secret sysop command
  1336. room.battle.add(target);
  1337. return false;
  1338. }
  1339. break;
  1340. // Admin commands
  1341. case 'forcewin':
  1342. case 'forcetie':
  1343. if (!user.can('forcewin') || !room.battle) {
  1344. emit(socket, 'console', '/forcewin - Access denied.');
  1345. return false;
  1346. }
  1347. room.battle.endType = 'forced';
  1348. if (!target) {
  1349. room.battle.tie();
  1350. logModCommand(room,user.name+' forced a tie.',true);
  1351. return false;
  1352. }
  1353. target = Users.get(target);
  1354. if (target) target = target.userid;
  1355. else target = '';
  1356. if (target) {
  1357. room.battle.win(target);
  1358. logModCommand(room,user.name+' forced a win for '+target+'.',true);
  1359. }
  1360. return false;
  1361. break;
  1362. case 'potd':
  1363. if (!user.can('potd')) {
  1364. emit(socket, 'console', '/potd - Access denied.');
  1365. return false;
  1366. }
  1367. config.potd = target;
  1368. Simulator.eval('config.potd = \''+toId(target)+'\'');
  1369. if (target) {
  1370. rooms.lobby.addRaw('<div class="broadcast-blue"><b>The Pokemon of the Day is now '+target+'!</b><br />This Pokemon will be guaranteed to show up in random battles.</div>');
  1371. logModCommand(room, 'The Pokemon of the Day was changed to '+target+' by '+user.name+'.', true);
  1372. } else {
  1373. rooms.lobby.addRaw('<div class="broadcast-blue"><b>The Pokemon of the Day was removed!</b><br />No pokemon will be guaranteed in random battles.</div>');
  1374. logModCommand(room, 'The Pokemon of the Day was removed by '+user.name+'.', true);
  1375. }
  1376. return false;
  1377. break;
  1378. case 'lockdown':
  1379. if (!user.can('lockdown')) {
  1380. emit(socket, 'console', '/lockdown - Access denied.');
  1381. return false;
  1382. }
  1383. lockdown = true;
  1384. for (var id in rooms) {
  1385. rooms[id].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>');
  1386. if (rooms[id].requestKickInactive) rooms[id].requestKickInactive(user, true);
  1387. }
  1388. rooms.lobby.logEntry(user.name + ' used /lockdown');
  1389. return false;
  1390. break;
  1391. case 'endlockdown':
  1392. if (!user.can('lockdown')) {
  1393. emit(socket, 'console', '/endlockdown - Access denied.');
  1394. return false;
  1395. }
  1396. lockdown = false;
  1397. for (var id in rooms) {
  1398. rooms[id].addRaw('<div class="broadcast-green"><b>The server shutdown was canceled.</b></div>');
  1399. }
  1400. rooms.lobby.logEntry(user.name + ' used /endlockdown');
  1401. return false;
  1402. break;
  1403. case 'kill':
  1404. if (!user.can('lockdown')) {
  1405. emit(socket, 'console', '/lockdown - Access denied.');
  1406. return false;
  1407. }
  1408. if (!lockdown) {
  1409. emit(socket, 'console', 'For safety reasons, /kill can only be used during lockdown.');
  1410. return false;
  1411. }
  1412. if (updateServerLock) {
  1413. emit(socket, 'console', 'Wait for /updateserver to fin…

Large files files are truncated, but you can click here to view the full file