index.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. const fs = require('fs');
  2. const https = require('https');
  3. const seedrandom = require('seedrandom');
  4. const express = require('express');
  5. const app = express();
  6. const options = {
  7. key: fs.readFileSync("ssl/private.key"),
  8. cert: fs.readFileSync("ssl/certificate.crt"),
  9. ca: [fs.readFileSync('ssl/ca_bundle.crt')]
  10. };
  11. const server = https.createServer(options, app);
  12. const io = require('socket.io')(server);
  13. var port = 443;
  14. var admins = {}
  15. fs.readFile("config/admins.json", "utf8", (err, data) => {
  16. if (!err && data) {
  17. admins = JSON.parse(data);
  18. }
  19. })
  20. var users = [];
  21. var bannedIps = [];
  22. fs.readFile("config/ban.json", "utf8", (err, data) => {
  23. if (!err && data) {
  24. bannedIps = JSON.parse(data);
  25. }
  26. })
  27. var randomColors = [
  28. '#c42020',
  29. '#de801e',
  30. '#f6c60b',
  31. '#19ce19',
  32. '#129696',
  33. '#2c53e3',
  34. '#6f23e3',
  35. '#883f88',
  36. ];
  37. var historySize = 50;
  38. var history = [];
  39. fs.readFile("config/config.json", "utf8", (err, data) => {
  40. if (!err) {
  41. const config = JSON.parse(data);
  42. if (config.port) port = config.port;
  43. if (config.historySize) historySize = config.historySize;
  44. if (config.randomColors) randomColors = config.randomColors;
  45. }
  46. })
  47. app.use(express.static(__dirname + '/www'));
  48. app.use('/node_modules', express.static(__dirname + '/node_modules'));
  49. app.get('/img/ava/:username', (req, res) => {
  50. let username = req.params.username; //.toLowerCase();
  51. username = username.normalize('NFD');
  52. username = username.replace(/[\u0300-\u036f]/g, '');
  53. username = username.replace(/ß/g, 'ss');
  54. username = username.replace(/[^\x00-\x7F]/g, '');
  55. const filePath = __dirname + '/www/img/ava/' + username + '.png';
  56. var file = '';
  57. if (fs.existsSync(filePath)) {
  58. file = filePath;
  59. } else {
  60. const files = fs.readdirSync(__dirname + '/www/img/ava/random/').filter((f) => f.toLowerCase().endsWith('.png'));
  61. const seededRandom = seedrandom(username)();
  62. const randomFile = files[Math.floor(seededRandom * files.length)];
  63. file = __dirname + '/www/img/ava/random/' + randomFile;
  64. }
  65. res.sendFile(file);
  66. });
  67. io.on('connection', (socket) => {
  68. const ip = socket.handshake.address;
  69. log('- New user joined the server:', ip);
  70. for (const bannedItem of bannedIps) {
  71. if (bannedItem.ip == ip) {
  72. log('- User blacklisted, kicking:', ip)
  73. socket.emit('serverKick');
  74. socket.disconnect();
  75. break;
  76. }
  77. }
  78. socket.emit('serverHandshake');
  79. const user = {ip: ip, socket: socket};
  80. users.push(user);
  81. socket.on('login', (username, password) => {
  82. if (!username) {
  83. socket.emit('usernameInvalid');
  84. return;
  85. }
  86. for (let user of users) {
  87. if (user.name == username) {
  88. socket.emit('usernameTaken');
  89. return;
  90. }
  91. }
  92. if (admins && admins[username] && admins[username].password) {
  93. if (!password) {
  94. log('- Attempted login as admin without password.', username, '(' + ip + ')');
  95. socket.emit('passwordRequired');
  96. return;
  97. } else if (admins[username].password != password) {
  98. log('- Attempted login as admin with wrong password.', username, '(' + ip + ')');
  99. socket.emit('passwordWrong');
  100. return;
  101. }
  102. log('- Admin "' + username + '" login successful');
  103. user.admin = true;
  104. if (admins[username].color) {
  105. user.color = admins[username].color;
  106. }
  107. socket.on('requestKick', (userToBeKicked) => {
  108. log('- Admin "' + username + '" requested kick of User "' + userToBeKicked.name + '"');
  109. kickUser(userToBeKicked);
  110. });
  111. socket.on('requestLiftBan', (ip) => {
  112. log('- Admin "' + username + '" requested to lift ban for ip "' + ip + '"');
  113. liftBan(ip);
  114. });
  115. }
  116. log('- New user logged in:', username, '(' + ip + ')');
  117. user.name = username;
  118. if (!user.color) {
  119. user.color = getRandomColor(username);
  120. }
  121. socket.emit('serverLogin', getCleanUser(user), history);
  122. io.emit('userJoined', username);
  123. updateUsers();
  124. updateBanned();
  125. socket.on('disconnect', () => {
  126. log('- User joined the server:', ip);
  127. users = users.filter((listUser) => listUser !== user);
  128. io.emit('userLeft', username);
  129. updateUsers();
  130. });
  131. socket.on('message', (msg) => {
  132. if (msg) {
  133. if (!user.admin) {
  134. const regex = /(&nbsp;|<([^>]+)>)/ig;
  135. msg = msg.replace(regex, "");
  136. }
  137. msg = msg.replace(/(?<!(href|src)=")(\b[\w]+:\/\/[\w-?&;#~=\.\/\@%]+[\w\/])/g, (url) => {
  138. try {
  139. const urlObj = new URL(url);
  140. const urlWithoutParams = urlObj.origin + urlObj.pathname;
  141. if (urlWithoutParams.toLowerCase().endsWith('.jpg')
  142. || urlWithoutParams.toLowerCase().endsWith('.gif')
  143. || urlWithoutParams.toLowerCase().endsWith('.png')) {
  144. return '<a href="' + url + '"><img src="' + url + '" alt="" /></a>';
  145. }
  146. } catch (e) {}
  147. var faviconUrl;
  148. try {
  149. faviconUrl = new URL(url).origin + '/favicon.ico';
  150. } catch (e) {
  151. log('Error getting favicon for "' + url + '"');
  152. }
  153. return '<a href="' + url + '"><img class="favicon" src="' + faviconUrl + '" alt="" />' + url + '</a>';
  154. });
  155. for (const u of users) {
  156. while (msg.indexOf('@' + u.name) > -1) {
  157. msg = msg.replace('@' + u.name, '<span class="mention" style="color: ' + u.color + '">' + u.name + '</span>');
  158. }
  159. }
  160. log(user.name + ':', msg, '(' + user.ip + ')');
  161. if (history.length >= historySize) {
  162. history = history.slice(1);
  163. }
  164. history.push({msg: msg, user: getCleanUser(user), timestamp: Date.now()});
  165. io.emit('message', msg, getCleanUser(user), Date.now());
  166. }
  167. });
  168. });
  169. });
  170. function updateUsers() {
  171. io.emit('usersUpdated', users.filter((user) => user.name != null).map(getCleanUser));
  172. }
  173. function getCleanUser(user) {
  174. return {
  175. name: user.name,
  176. admin: user.admin,
  177. color: user.color
  178. }
  179. }
  180. function kickUser(user) {
  181. let ip;
  182. for (const exUser of users) {
  183. if (exUser.name == user.name) {
  184. ip = exUser.ip;
  185. break;
  186. }
  187. }
  188. bannedIps.push({ip: ip, user: user});
  189. updateBanned();
  190. for (const exUser of users) {
  191. if (exUser.ip == ip) {
  192. exUser.socket.emit('serverKick');
  193. exUser.socket.disconnect();
  194. }
  195. }
  196. }
  197. function liftBan(ip) {
  198. bannedIps = bannedIps.filter((listItem) => listItem.ip != ip);
  199. updateBanned();
  200. }
  201. function updateBanned() {
  202. fs.writeFile('config/ban.json', JSON.stringify(bannedIps), (e) => {
  203. });
  204. io.emit('bannedUpdated', bannedIps);
  205. }
  206. function getRandomColor(seed) {
  207. return randomColors[Math.floor(seedrandom(seed)() * randomColors.length)];
  208. }
  209. function log(...inputs) {
  210. var output = '[' + new Date().toISOString().substr(0, 19).replace('T', ', ') + '] ';
  211. for (const i of inputs) {
  212. output += i + ' ';
  213. }
  214. console.log(output);
  215. fs.appendFile('log.txt', '' + output + '\n', (e) => {
  216. });
  217. }
  218. server.listen(port, () => {
  219. log('- Server up and running at port ' + port);
  220. });