unrealircd.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. var protocol = {
  2. connected: function(){
  3. ircSend(null, settings.ID, 'PASS', [settings.password]);
  4. ircSend(null, settings.ID, 'PROTOCTL', ['NICKv2', 'VHP', 'UMODE2', 'NICKIP', 'SJOIN', 'SJOIN2', 'SJ3', 'NOQUIT', 'TKLEXT', 'MLOCK', 'SID', 'MTAGS']);
  5. ircSend(null, settings.ID, 'PROTOCTL', ['EAUTH=' + settings.name + ',,,' + settings.version]);
  6. ircSend(null, settings.ID, 'PROTOCTL', ['SID=' + settings.ID]);
  7. ircSend(null, settings.ID, 'SERVER', [settings.name, '1', settings.description]);
  8. },
  9. processMessage: function(msg){
  10. if(!msg) return;
  11. if(msg.command in cmdBinds){
  12. cmdBinds[msg.command](msg);
  13. } else {
  14. console.log('Unhandled cmd: '+msg.command);
  15. console.log(msg);
  16. }
  17. },
  18. processSender: function(sender){
  19. if(sender.nick){
  20. if(server = handlers.findServer(sender.nick)){
  21. sender.nick = server.name;
  22. sender.server = server;
  23. } else if(user = handlers.findUser(sender.nick)){
  24. sender.nick = user.name
  25. sender.ident = user.ident;
  26. sender.host = user.host;
  27. sender.user = user;
  28. } else {
  29. handlers.killUser(sender.nick);
  30. return false;
  31. }
  32. }
  33. return sender;
  34. },
  35. setHandlers: function(newHandlers){
  36. handlers = newHandlers;
  37. },
  38. setSettings: function(newSettings){
  39. settings = newSettings;
  40. },
  41. setEvents: function(newEvents){
  42. events = newEvents;
  43. },
  44. // IRC actions
  45. introduceUser: function(user){
  46. var nick = user.name;
  47. var distance = user.distance.toString();
  48. var TS = user.TS.toString();
  49. var ident = user.ident;
  50. var host = user.host;
  51. var uid = user.uid;
  52. var account = '0';
  53. if(user.account){
  54. account = user.account;
  55. }
  56. var umodes = '+oSB'; // TODO
  57. var vhost = '*';
  58. if(user.vhost){
  59. vhost = user.vhost;
  60. }
  61. var cloakedHost = '*';
  62. if(user.cloakedHost){
  63. cloakedHost = user.cloakedHost;
  64. }
  65. var ip = '*';
  66. if(user.ip){
  67. ip = user.ip;
  68. }
  69. var realname = user.realname;
  70. var args = [nick, distance, TS, ident, host, uid, account, umodes, vhost, cloakedHost, ip, realname];
  71. console.log(args);
  72. ircSend(null, settings.ID, 'UID', args);
  73. },
  74. syncChannel: function(channel){
  75. var modes = null;
  76. var modeArgs = null;
  77. // prepare list modes
  78. var listModes = [];
  79. for(m in channel.listModes){
  80. var mode = channel.listModes[m];
  81. for(var i=0; i<mode.length; i++){
  82. switch(m){
  83. case 'BAN': listModes.push('&' + mode[i]); break;
  84. case 'EXCEPT': listModes.push('"' + mode[i]); break;
  85. case 'INVEX': listModes.push('\'' + mode[i]); break;
  86. }
  87. }
  88. }
  89. // prepare users
  90. var users = [];
  91. for(var i=0; i<channel.users.length; i++){
  92. var user = channel.users[i];
  93. if(user.uplink != settings.me) continue;
  94. var status = '';
  95. if(user.uid in channel.statusModes){
  96. var statusModes = channel.statusModes[user.uid];
  97. for(var j=0; j<statusModes.length; j++){
  98. var statusMode = statusModes[j];
  99. for(c in chmodeMap){
  100. if(chmodeMap[c].name == statusMode){
  101. for(sm in connection.statusmodes){
  102. if(connection.statusmodes[sm] == c){
  103. status += sm;
  104. break;
  105. }
  106. }
  107. break;
  108. }
  109. }
  110. }
  111. }
  112. users.push(status + user.uid);
  113. }
  114. var done = false;
  115. var listCount = 0;
  116. var userCount = 0;
  117. do {
  118. var text = '';
  119. var count = 0;
  120. for(; listCount < listModes.length; listCount++){
  121. if(++count > 10) break;
  122. if(text.length > 0) text += ' ';
  123. text += listModes[listCount];
  124. }
  125. for(; userCount < users.length; userCount++){
  126. if(++count > 10) break;
  127. if(text.length > 0) text += ' ';
  128. text += users[userCount];
  129. }
  130. if(text.length == 0){
  131. break;
  132. }
  133. var args = [Math.floor(new Date() / 1000).toString(10), channel.name];
  134. if(modes) args.push(modes); else args.push('+');
  135. if(modeArgs) args.push(modeArgs);
  136. args.push(text);
  137. console.log(args);
  138. ircSend(null, settings.ID, 'SJOIN', args);
  139. } while(true);
  140. },
  141. 'makeUid': function(){
  142. return settings.me.sid + randomID();
  143. }
  144. }
  145. //>>> @time=2020-03-15T12:17:07.355Z;msgid=FhjgUp20YBKQ32TGPM28tS :143 SJOIN 1580651090 #help +nt :0931Y7O8W 093375CFR 123VTY20R
  146. function randomID(){
  147. var result = '';
  148. var length = 6;
  149. var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  150. var charactersLength = characters.length;
  151. for(var i=0; i<length; i++ ) {
  152. result += characters.charAt(Math.floor(Math.random() * charactersLength));
  153. }
  154. return result;
  155. }
  156. module.exports = protocol;
  157. var handlers = {
  158. sendHandler: null,
  159. findServer: null,
  160. findUser: null,
  161. killUser: null,
  162. newServer: null,
  163. newUser: null,
  164. getChannel: null,
  165. findChannel: null,
  166. quitUser: null,
  167. removeServer: null
  168. };
  169. var settings = {
  170. ID: null,
  171. password: null,
  172. name: null,
  173. description: null,
  174. version: null,
  175. me: null,
  176. maxUsers: null
  177. };
  178. var events = {};
  179. var connection = {
  180. 'protoctl': {},
  181. 'prefixes': {},
  182. 'statusmodes': [],
  183. 'chmodes': [], // 0 - list modes, 1 - argument modes, 2 - argument modes (add only), 3 - standard modes
  184. 'umodes': {}
  185. };
  186. var chmodeMap = {
  187. 'b': { 'name': 'BAN', 'user': true, 'services': true },
  188. 'e': { 'name': 'EXCEPT', 'user': true, 'services': true },
  189. 'I': { 'name': 'INVEX', 'user': true, 'services': true },
  190. 'v': { 'name': 'VOICE', 'user': true, 'services': true },
  191. 'h': { 'name': 'HALFOP', 'user': true, 'services': true },
  192. 'o': { 'name': 'OP', 'user': true, 'services': true },
  193. 'a': { 'name': 'PROTECT', 'user': true, 'services': true },
  194. 'q': { 'name': 'OWNER', 'user': true, 'services': true },
  195. 'k': { 'name': 'KEY', 'user': true, 'services': true },
  196. 'L': { 'name': 'CHANNELLINK', 'user': true, 'services': true },
  197. 'f': { 'name': 'FLOODPROT', 'user': true, 'services': true },
  198. 'l': { 'name': 'LIMIT', 'user': true, 'services': true },
  199. 'H': { 'name': 'HISTORY', 'user': true, 'services': true },
  200. 'p': { 'name': 'PRIVATE', 'user': true, 'services': true },
  201. 's': { 'name': 'SECRET', 'user': true, 'services': true },
  202. 'm': { 'name': 'MODERATED', 'user': true, 'services': true },
  203. 'n': { 'name': 'NOEXTERNAL', 'user': true, 'services': true },
  204. 't': { 'name': 'TOPIC', 'user': true, 'services': true },
  205. 'i': { 'name': 'INVITE', 'user': true, 'services': true },
  206. 'r': { 'name': 'REGISTERED', 'user': false, 'services': true },
  207. 'z': { 'name': 'SECUREONLY', 'user': true, 'services': true },
  208. 'M': { 'name': 'NONREGMODERATED', 'user': true, 'services': true },
  209. 'Q': { 'name': 'NOKICK', 'user': true, 'services': true },
  210. 'N': { 'name': 'NONICK', 'user': true, 'services': true },
  211. 'R': { 'name': 'NONREGINVITE', 'user': true, 'services': true },
  212. 'T': { 'name': 'NOCTCPS', 'user': true, 'services': true },
  213. 'O': { 'name': 'OPERS', 'user': false, 'services': true },
  214. 'V': { 'name': 'NOINVITE', 'user': true, 'services': true },
  215. 'K': { 'name': 'NOKNOCK', 'user': true, 'services': true },
  216. 'D': { 'name': 'OLDDELAYJOIN', 'user': false, 'services': false },
  217. 'd': { 'name': 'DELAYJOIN', 'user': true, 'services': true },
  218. 'G': { 'name': 'CENSOR', 'user': true, 'services': true },
  219. 'P': { 'name': 'PERMANENT', 'user': false, 'services': true },
  220. 'Z': { 'name': 'SECURE', 'user': false, 'services': false },
  221. 'S': { 'name': 'COLORFILTER', 'user': true, 'services': true },
  222. 'C': { 'name': 'NOCTCP', 'user': true, 'services': true },
  223. 'c': { 'name': 'NOCOLOR', 'user': true, 'services': true }
  224. };
  225. var umodeMap = {
  226. 'i': { 'name': 'INVISIBLE', 'user': true, 'services': true },
  227. 'o': { 'name': 'OPER', 'user': false, 'services': false },
  228. 'w': { 'name': 'WALLOP', 'user': true, 'services': true },
  229. 'r': { 'name': 'REGISTEREDNICK', 'user': false, 'services': true },
  230. 's': { 'name': 'SNOTICE', 'user': true, 'services': true },
  231. 'x': { 'name': 'CLOAK', 'user': true, 'services': true },
  232. 'z': { 'name': 'SECUREUSER', 'user': false, 'services': false },
  233. 'd': { 'name': 'DEAF', 'user': true, 'services': true },
  234. 'H': { 'name': 'HIDEIRCOP', 'user': false, 'services': false },
  235. 't': { 'name': 'VHOST', 'user': false, 'services': true },
  236. 'I': { 'name': 'HIDEIDLE', 'user': false, 'services': false },
  237. 'D': { 'name': 'PRIVDEAF', 'user': true, 'services': true },
  238. 'Z': { 'name': 'SECUREMESSAGES', 'user': true, 'services': true },
  239. 'R': { 'name': 'REGISTEREDMESSAGES', 'user': true, 'services': true },
  240. 'q': { 'name': 'UNKICKABLE', 'user': false, 'services': false },
  241. 'p': { 'name': 'HIDECHANNELS', 'user': true, 'services': true },
  242. 'W': { 'name': 'WHOISNOTICE', 'user': false, 'services': false },
  243. 'G': { 'name': 'CENSORMESSAGES', 'user': true, 'services': true },
  244. 'T': { 'name': 'NOCTCPMESSAGES', 'user': true, 'services': true },
  245. 'S': { 'name': 'SERVICEUSER', 'user': false, 'services': true },
  246. 'B': { 'name': 'BOT', 'user': true, 'services': true }
  247. };
  248. function parseChannelModes(modes, args){
  249. var plus = true;
  250. var argIndex = 0;
  251. var output = {};
  252. output['status'] = [];
  253. output['list'] = [];
  254. for(var i=0; i<modes.length; i++){
  255. var c = modes.charAt(i);
  256. if(c in chmodeMap){
  257. var name = chmodeMap[c].name;
  258. } else {
  259. var name = c;
  260. }
  261. if(c == '+'){
  262. plus = true;
  263. } else if(c == '-'){
  264. plus = false;
  265. } else if(connection.chmodes[0].indexOf(c) >= 0){
  266. output['list'].push({ 'name': name, 'value': args[argIndex++], 'status': plus });
  267. } else if(connection.chmodes[1].indexOf(c) >= 0){
  268. if(plus){
  269. output[name] = args[argIndex++];
  270. } else {
  271. output[name] = false;
  272. argIndex++;
  273. }
  274. } else if(connection.chmodes[2].indexOf(c) >= 0){
  275. if(plus){
  276. output[name] = args[argIndex++];
  277. } else {
  278. output[name] = false;
  279. }
  280. } else if(connection.statusmodes.indexOf(c) >= 0){
  281. var user = handlers.findUser(args[argIndex++]);
  282. if(user){
  283. output['status'].push({ 'name': name, 'user': user, 'status': plus });
  284. } else {
  285. throw 'User not found';
  286. }
  287. } else { // treat unknown chars as type 3
  288. if(connection.chmodes[3].indexOf(c) < 0){
  289. console.log('Unknown mode '+name);
  290. }
  291. output[name] = plus;
  292. }
  293. }
  294. return output;
  295. }
  296. function parseUmodes(modeString){
  297. var plus = true;
  298. var modes = {};
  299. for(var i=0; i<modeString.length; i++){
  300. var c = modeString.charAt(i);
  301. switch(c){
  302. case '+': plus = true; break;
  303. case '-': plus = false; break;
  304. default:
  305. var modeName = umodeMap[c].name;
  306. if(!modeName) modeName = c;
  307. modes[modeName] = plus;
  308. break;
  309. }
  310. }
  311. return modes;
  312. }
  313. function parseModePrefix(text){
  314. var modes = {'status': [], 'list': [] };
  315. var isUser = false;
  316. var c = text.charAt(0);
  317. if(c == '&'){ // +b
  318. modes.list.push({ 'name': 'BAN', 'value': text.substring(1), 'status': true });
  319. } else if(c == '"'){ // +e
  320. modes.list.push({ 'name': 'EXCEPT', 'value': text.substring(1), 'status': true });
  321. } else if(c == '\''){ // +I
  322. modes.list.push({ 'name': 'INVEX', 'value': text.substring(1), 'status': true });
  323. } else {
  324. isUser = true;
  325. for(var i=0; i<text.length; i++){
  326. c = text.charAt(i);
  327. if(c in connection.prefixes){
  328. modes.status.push({ 'name': chmodeMap[connection.prefixes[c]].name, 'user': false /* setting it later */, 'status': true });
  329. // can be more than 1
  330. } else {
  331. break;
  332. }
  333. }
  334. }
  335. var user = null;
  336. if(isUser){
  337. var uid = text.substring(i);
  338. user = handlers.findUser(uid);
  339. if(!user){
  340. handlers.killUser(uid);
  341. modes.status = [];
  342. }
  343. for(var i=0; i<modes.status.length; i++){
  344. modes.status[i].user = user;
  345. }
  346. }
  347. return { 'modes': modes, 'user': user };
  348. }
  349. function ircSend(tags, from, cmd, args){
  350. handlers.send(tags, from, cmd, args);
  351. }
  352. var cmdBinds = {
  353. 'PROTOCTL': function(msg){
  354. for(var i=0; i<msg.args.length; i++){
  355. var arg = msg.args[i];
  356. if(arg.indexOf('=') >= 0){
  357. var arg = arg.split('=');
  358. connection.protoctl[arg[0]] = arg[1];
  359. switch(arg[0]){
  360. case 'PREFIX':
  361. var data = arg[1].substring(1).split(')');
  362. var modes = data[0].split('');
  363. var chars = data[1].split('');
  364. for(var i=0; i<modes.length; i++){
  365. connection.prefixes[chars[i]] = modes[i];
  366. connection.statusmodes.push(modes[i]);
  367. }
  368. break;
  369. case 'CHANMODES':
  370. connection.chmodes = arg[1].split(',');
  371. break;
  372. case 'USERMODES':
  373. connection.umodes = arg[1];
  374. break;
  375. }
  376. } else {
  377. connection.protoctl[arg] = true;
  378. }
  379. }
  380. },
  381. 'SERVER': function(msg){
  382. var expr = /^([^-]+)-([^-]+)-([^ ]+) (.*)$/;
  383. var match = expr.exec(msg.args[2]);
  384. if(match){
  385. settings.me.uplink = handlers.newServer(msg.args[0], match[3], match[4], msg.args[1], settings.me);
  386. } else {
  387. throw 'Unknown SERVER message';
  388. }
  389. events.doEvent('netSynced');
  390. ircSend(null, settings.ID, 'EOS');
  391. },
  392. 'MD': function(msg){
  393. switch(msg.args[0]){
  394. case 'client':
  395. var user = handlers.findUser(msg.args[1]);
  396. if(!user) return;
  397. switch(msg.args[2]){
  398. case 'certfp':
  399. if(!msg.args[3] || msg.args[3].length == 0) break;
  400. user.setSecure(true);
  401. user.setFingerprint(msg.args[3]);
  402. break;
  403. default: break;
  404. }
  405. break;
  406. default: break;
  407. }
  408. },
  409. 'SMOD': function(msg){ // ignore
  410. },
  411. 'EOS': function(msg){ // ?
  412. console.log(connection);
  413. },
  414. 'SINFO': function(msg){
  415. console.log(msg);
  416. },
  417. 'SID': function(msg){
  418. handlers.newServer(msg.args[0], msg.args[2], msg.args[3], msg.args[1], msg.sender.server);
  419. },
  420. 'UID': function(msg){
  421. var nick = msg.args[0];
  422. var distance = msg.args[1];
  423. var TS = msg.args[2];
  424. var ident = msg.args[3];
  425. var host = msg.args[4];
  426. var uid = msg.args[5];
  427. var account = msg.args[6];
  428. var umodes = msg.args[7];
  429. var vhost = msg.args[8];
  430. var cloakedHost = msg.args[9];
  431. var ip = msg.args[10];
  432. var realname = msg.args[11];
  433. if(ip != '*'){
  434. var ipBinary = Buffer.from(ip, 'base64');
  435. if(ipBinary.byteLength == 4){ // IPv4
  436. ip = ipBinary[0].toString(10) + '.' + ipBinary[1].toString(10) + '.' + ipBinary[2].toString(10) + '.' + ipBinary[3].toString(10);
  437. } else if(ipBinary.byteLength == 16){ // IPv6
  438. ip = '';
  439. for(var i=0; i<8; i++){
  440. if(i > 0) ip += ':';
  441. ip += ipBinary[i*2].toString(16).padStart(2, '0') + ipBinary[i*2+1].toString(16).padStart(2, '0');
  442. }
  443. } else {
  444. throw 'unknown IP format';
  445. }
  446. } else {
  447. ip = null;
  448. }
  449. if(vhost == '*'){
  450. vhost = null;
  451. }
  452. if(cloakedHost == '*'){
  453. cloakedHost = null;
  454. }
  455. if(account == '0'){
  456. account = null;
  457. }
  458. handlers.newUser(nick, distance, TS, ident, host, uid, account, parseUmodes(umodes), vhost, cloakedHost, ip, realname, msg.sender.server);
  459. },
  460. 'SJOIN': function(msg){
  461. var channel = handlers.getChannel(msg.args[1]);
  462. channel.setTS(msg.args[0]);
  463. if(msg.args.length > 3){
  464. var modeArgs = [];
  465. for(var i=3; i<msg.args.length - 1; i++){
  466. modeArgs.push(msg.args[i]);
  467. }
  468. channel.changeModes(parseChannelModes(msg.args[2], modeArgs));
  469. }
  470. var members = msg.text.split(' ');
  471. for(var i=0; i<members.length; i++){
  472. var member = members[i];
  473. var c = member.charAt(0);
  474. var udata = parseModePrefix(member);
  475. if(udata.user){
  476. console.log(udata.user.name + ' joined ' + msg.args[1]);
  477. channel.joinUser(udata.user);
  478. }
  479. channel.changeModes(udata.modes);
  480. }
  481. },
  482. 'SENDUMODE': function(msg){ // ignore
  483. },
  484. 'SENDSNO': function(msg){ // ignore
  485. },
  486. 'PASS': function(msg){ // ignore
  487. },
  488. 'SWHOIS': function(msg){
  489. console.log(msg);
  490. //TODO
  491. },
  492. 'MODE': function(msg){
  493. var channel = handlers.findChannel(msg.args[0]);
  494. if(channel){
  495. var modeArgs = [];
  496. for(var i=2; i<msg.args.length; i++){
  497. modeArgs.push(msg.args[i]);
  498. }
  499. var chmodes = parseChannelModes(msg.args[1], modeArgs);
  500. channel.changeModes(chmodes);
  501. }
  502. },
  503. 'TKL': function(msg){
  504. // console.log(msg);
  505. //TODO
  506. },
  507. 'METADATA': function(msg){
  508. var what = msg.args[0];
  509. var name = msg.args[1];
  510. var visibility = msg.args[2];
  511. if(msg.args.length > 3){
  512. var value = msg.args[3];
  513. } else {
  514. var value = null;
  515. }
  516. var channel = handlers.findChannel(what);
  517. if(channel){
  518. channel.setMetadata(name, visibility, value);
  519. return;
  520. }
  521. var user = handlers.findUser(what);
  522. if(user){
  523. user.setMetadata(name, visibility, value);
  524. }
  525. },
  526. 'NETINFO': function(msg){
  527. ircSend(null, null, 'NETINFO', [settings.maxUsers.toString(10), Math.floor(new Date() / 1000).toString(10), msg.args[2], msg.args[3], "0", "0", "0", msg.args[7]]);
  528. },
  529. 'SASL': function(msg){
  530. console.log(msg);
  531. //TODO
  532. },
  533. 'PING': function(msg){
  534. msg.reply(null, 'PONG', msg.args);
  535. },
  536. 'QUIT': function(msg){
  537. handlers.quitUser(msg.sender.user);
  538. },
  539. 'PART': function(msg){
  540. var channel = handlers.getChannel(msg.args[0]);
  541. channel.removeUser(msg.sender.user);
  542. },
  543. 'CHGHOST': function(msg){
  544. var user = handlers.findUser(msg.args[0]);
  545. if(!user) return;
  546. user.changeVHost(msg.args[1]);
  547. },
  548. 'CHGIDENT': function(msg){
  549. var user = handlers.findUser(msg.args[0]);
  550. if(!user) return;
  551. user.changeVIdent(msg.args[1]);
  552. },
  553. 'CHGNAME': function(msg){
  554. var user = handlers.findUser(msg.args[0]);
  555. if(!user) return;
  556. user.setRealname(msg.args[1]);
  557. },
  558. 'SETNAME': function(msg){
  559. msg.sender.user.setRealname(msg.args[0]);
  560. },
  561. 'SETHOST': function(msg){
  562. msg.sender.user.changeVHost(msg.args[0]);
  563. },
  564. 'SETIDENT': function(msg){
  565. msg.sender.user.changeVIdent(msg.args[0]);
  566. },
  567. 'NICK': function(msg){
  568. msg.sender.user.changeNick(msg.args[0]);
  569. },
  570. 'SDESC': function(msg){
  571. msg.sender.uplink.setDescription(msg.args[0]);
  572. },
  573. 'TOPIC': function(msg){
  574. var channel = handlers.findChannel(msg.args[0]);
  575. if(!channel) return;
  576. var setter = msg.args[1];
  577. var TS = msg.args[2];
  578. var text = msg.args[3];
  579. channel.setTopic({'setter': setter, 'TS': TS, 'text': text });
  580. },
  581. /*
  582. messagedata {
  583. text: '0',
  584. args: [ '183.89.186.94', '0' ],
  585. tags: [],
  586. command: 'REPUTATION',
  587. sender: {
  588. nick: 'test1.pirc.pl',
  589. ident: '',
  590. host: '',
  591. server: server {
  592. name: 'test1.pirc.pl',
  593. sid: '093',
  594. description: 'serwer testowy!',
  595. distance: '3',
  596. uplink: [Object],
  597. introduce: [Function]
  598. },
  599. user: false
  600. },
  601. time: 2020-03-14T10:48:06.207Z,
  602. reply: [Function],
  603. originalString: ':093 REPUTATION 183.89.186.94 0'
  604. }
  605. */
  606. 'REPUTATION': function(msg){
  607. //TODO
  608. },
  609. 'UMODE2': function(msg){
  610. msg.sender.user.changeUmodes(parseUmodes(msg.args[0]));
  611. },
  612. 'KILL': function(msg){
  613. var user = handlers.findUser(msg.args[0]);
  614. if(!user) return;
  615. handlers.quitUser(user);
  616. },
  617. 'SQUIT': function(msg){
  618. var server = handlers.findServer(msg.args[0]);
  619. if(!server) return;
  620. handlers.removeServer(server);
  621. },
  622. /*messagedata {
  623. text: 'TestServ',
  624. args: [ '11K3R1VLH', 'TestServ' ],
  625. tags: [],
  626. command: 'WHOIS',
  627. sender: {
  628. nick: 'k4be',
  629. ident: 'testowy',
  630. host: 'localhost',
  631. server: false,
  632. user: user {
  633. name: 'k4be',
  634. TS: '1583056052',
  635. ident: 'testowy',
  636. host: 'localhost',
  637. account: null,
  638. umodes: [Object],
  639. vhost: 'staff:netadmin',
  640. cloakedHost: 'ukryty-18D8A42D',
  641. ip: '0000:0000:0000:0000:0000:0000:0000:0001',
  642. realname: 'realname',
  643. uid: '143ZQ0B02',
  644. distance: '0',
  645. uplink: [server],
  646. secure: false,
  647. certfp: null,
  648. vident: null,
  649. metadata: {},
  650. introduce: [Function],
  651. setSecure: [Function],
  652. setFingerprint: [Function],
  653. changeVHost: [Function],
  654. changeVIdent: [Function],
  655. setRealname: [Function],
  656. changeNick: [Function],
  657. changeUmodes: [Function],
  658. setMetadata: [Function]
  659. }
  660. },
  661. time: 2020-03-16T14:02:45.252Z,
  662. reply: [Function],
  663. originalString: ':143ZQ0B02 WHOIS 11K3R1VLH :TestServ'
  664. }*/
  665. 'WHOIS': function(msg){
  666. },
  667. 'PRIVMSG': function(msg){
  668. if(msg.args[0].charAt(0) == '#'){
  669. events.doEvent('channelMessage', msg);
  670. } else {
  671. events.doEvent('botMessage', msg);
  672. }
  673. },
  674. 'NOTICE': function(msg){
  675. if(msg.args[0].charAt(0) == '#'){
  676. events.doEvent('channelNotice', msg);
  677. } else {
  678. events.doEvent('botNotice', msg);
  679. }
  680. }
  681. };