├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── node.js.yml │ └── zip.yml ├── .gitignore ├── README.md ├── client ├── .babelrc ├── .gitignore ├── package.json ├── public │ ├── electron.js │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo192_notif.png │ ├── manifest.json │ ├── preload.js │ └── robots.txt ├── src │ ├── App.js │ ├── Components.js │ ├── components │ │ ├── AccountOptionsDialog.js │ │ ├── AccountStatusDialog.js │ │ ├── AddFriendsDialog.js │ │ ├── ChannelHeader.js │ │ ├── ChannelOptionsDialog.js │ │ ├── ChannelSelector.js │ │ ├── Chat.js │ │ ├── CopiedIDDialog.js │ │ ├── CreateChannelDialog.js │ │ ├── CreateEmoteDialog.js │ │ ├── CreateServerDialog.js │ │ ├── CropImageDialog.js │ │ ├── EditAccountDialog.js │ │ ├── EditChannelDialog.js │ │ ├── EditServerDialog.js │ │ ├── ForgottenPasswordDialog.js │ │ ├── ImageDialog.js │ │ ├── InviteFriendsDialog.js │ │ ├── LoginForm.js │ │ ├── MessageOptionsDialog.js │ │ ├── PinnedMessagesDialog.js │ │ ├── ProfileDialog.js │ │ ├── ProfileOptionsDialog.js │ │ ├── RegisterForm.js │ │ ├── SearchMessagesDialog.js │ │ ├── Send.js │ │ ├── ServerDiscoveryDialog.js │ │ ├── ServerOptionsDialog.js │ │ ├── SetCustomStatusDialog.js │ │ ├── SettingsDialog.js │ │ └── index.js │ ├── index.js │ ├── public │ │ ├── fonts │ │ │ └── uwuFont.woff │ │ ├── icon.ico │ │ └── scripts │ │ │ ├── API.js │ │ │ ├── Constants.js │ │ │ ├── DateFormatter.js │ │ │ ├── ElementBuilder.js │ │ │ ├── Functions.js │ │ │ ├── MessageFormatter.js │ │ │ └── SizeFormatter.js │ └── style.css ├── webpack.config.js └── yarn.lock └── server ├── package-lock.json ├── package.json ├── scripts ├── data │ ├── db_add.js │ ├── db_delete.js │ ├── db_edit.js │ ├── db_fetch.js │ └── db_manager.js ├── endpoints │ ├── acceptFriendRequest.js │ ├── addToDMChannel.js │ ├── auth_blizzard.js │ ├── auth_discord.js │ ├── auth_gh.js │ ├── auth_osu.js │ ├── auth_reddit.js │ ├── auth_spotify.js │ ├── auth_twitch.js │ ├── cloneChannel.js │ ├── createChannel.js │ ├── createEmote.js │ ├── createInvite.js │ ├── createServer.js │ ├── declineFriendRequest.js │ ├── deleteChannel.js │ ├── deleteEmote.js │ ├── deleteMessage.js │ ├── deleteServer.js │ ├── editChannel.js │ ├── editMessage.js │ ├── editNote.js │ ├── editServer.js │ ├── editUser.js │ ├── fetchChannel.js │ ├── fetchChannelMessages.js │ ├── fetchChannels.js │ ├── fetchDMChannels.js │ ├── fetchDefaultEmotes.js │ ├── fetchEmote.js │ ├── fetchFriendRequest.js │ ├── fetchFriendRequests.js │ ├── fetchInvite.js │ ├── fetchNote.js │ ├── fetchNotes.js │ ├── fetchServer.js │ ├── fetchServers.js │ ├── fetchUser.js │ ├── joinServer.js │ ├── joinVoiceChannel.js │ ├── kickFromServer.js │ ├── leaveServer.js │ ├── leaveVoiceChannel.js │ ├── login.js │ ├── logout.js │ ├── message.js │ ├── register.js │ ├── removeConnection.js │ ├── removeFriend.js │ ├── removeFromDMChannel.js │ ├── sendFriendRequest.js │ ├── updateAvatar.js │ ├── updateServerAvatar.js │ ├── updateServerBanner.js │ ├── upload.js │ ├── voiceTransports_connect.js │ ├── voiceTransports_consume.js │ ├── voiceTransports_create.js │ └── voiceTransports_produce.js ├── util.js └── utils │ ├── epFunc.js │ └── mediaFunc.js ├── server.js └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" 9 | directory: "/client" 10 | open-pull-requests-limit: 99 11 | schedule: 12 | interval: "daily" 13 | 14 | - package-ecosystem: "npm" 15 | directory: "/server" 16 | open-pull-requests-limit: 99 17 | schedule: 18 | interval: "daily" 19 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: cd client && yarn && CI=false npm run build 28 | -------------------------------------------------------------------------------- /.github/workflows/zip.yml: -------------------------------------------------------------------------------- 1 | name: Create Web Release 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | node-version: [14.x] 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Create Web Release 12 | uses: liquidchat-devs/liquidchat-web-action@master 13 | with: 14 | directory: 'client' 15 | filename: 'build.zip' 16 | - name: Upload build.zip 17 | uses: actions/upload-artifact@v2 18 | with: 19 | name: build.zip 20 | path: client/build/build.zip 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | client/build 2 | client/dist 3 | client/node_modules 4 | 5 | server/node_modules 6 | server/data/config_persistent.txt 7 | server/keys 8 | 9 | icon/ 10 | database.json 11 | .vscode 12 | *.dat 13 | .VSCodeCounter/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | Open-source chat application similar to Discord- 7 |

8 | 9 |

10 | 11 | 12 | 13 | 14 |

15 | 16 |

17 | 💛 Official website: https://nekonetwork.net 18 |
19 | ✔️ Releases: https://github.com/LamkasDev/liquidchat/releases 20 |

21 | 22 |

23 | 📓 Documentation: https://nekonetwork.net/docs 24 |
25 | 🔥 API: https://github.com/LamkasDev/liquidchat.js 26 |
27 | 🔴 Found a bug or want to contribute?: 28 | [Open a pull request] 29 |

30 | 31 | 32 | 🏁 **Features:** 33 | - [x] Servers with Text and Voice Channels 34 | - [x] DM Channels 35 | - [x] Group Chats 36 | - [x] File Support (up to 100MB by default) 37 | - [x] Personal Emotes (max. 500) 38 | - [x] Server Emotes (max. 400) 39 | - [x] Idle/DND/Invisible Status 40 | - [x] Custom Statuses 41 | - [x] Friend Features (Adding/Removing friends) 42 | - [x] Searches, Pinned Messages, Connections, Notes 43 | - [x] Server banners and animated avatars for free 44 | - [ ] Better Server Management (Built-in bans, mutes, assigning server moderators) 45 | - [ ] Server Discovery 46 | - [ ] Voice Channels 47 | - [ ] Android/IOS Support 48 | - [ ] Rich Presence 49 | - [ ] a lot more... 50 | 51 | 52 | ### Build instructions: 53 | ##### a) How to build client (Windows) 54 |
 55 | run npm run createproduction-win
 56 | the installer will be built into /client/dist
 57 | 
58 | 59 | ##### c) How to test client (Windows) 60 |
 61 | run yarn start
 62 | 
63 | 64 | ##### d) How to host server yourself 65 |
 66 | 1) Setup your folder structure to look like the following:
 67 | 
 68 | lc-full.sh
 69 | /lc
 70 |    /liquidchat
 71 |       /client
 72 |       /server
 73 |    /liquidchat-fileserver
 74 |       /public
 75 |       /keys
 76 |       simpleserver.js
 77 |    /liquidchat-web
 78 |       /public
 79 |       /keys
 80 |       server.js
 81 |   
 82 |  2) (Alternative, but recommended) Get a certificate and key for your server's domain, if you don't have one already
 83 |  3) (Alternative, but recommended) Put key.pem and cert.pem into each /keys directories
 84 |  4) You will need MySQL installed so do it if you don't have it already (for Ubuntu here)
 85 |  5) Import the MySQL template file from here to create the needed datebase structure (will add later™)
 86 |  6) Create a /data folder in liquidchat/server and a file named config_persistent.txt
 87 |  7) Edit this file to fit the format (will add later™)
 88 |  8) Run sudo lc-full.sh /home/YOUR_USERNAME/lc/
 89 | 
90 | 91 | ##### d) How to deploy web versions... 92 |
 93 | 1) On your local machine, in the /client directory run npm run build
 94 | 2) Zip all the files in the /client/build folder (not the folder) into a file named build.zip
 95 | 3) Place this file into /lc folder on your server
 96 | 4) Restart your server (that'll unpack the assets from build.zip and place them into /liquidchat-web/public)
 97 | 
98 | 99 | ##### or alternatively use Github Actions where web versions are built automatically 100 | 101 | ##### f) Additional setup 102 |
103 | change APIEndpoint in /client/App.js to your server's domain:8080  
104 | change fileEnpoint in /client/App.js to your server's domain:8081  
105 | change filesStorage in /server/server.js to directory where you run your file server 
106 | 
107 | 108 | ##### g) Known issues 109 |
110 | if using the insecure useHTTP flag, websocket sessions can't exchange browser cookies,
111 | so server uses the first logged in session
112 | 
113 | 114 | ### Example Screenshots: 115 | > Client (2021/2/1) 116 | ![example1](https://qtlamkas.why-am-i-he.re/5X3X1x.png) 117 | 118 | > Documentation (2021/2/1) 119 | ![example2](https://qtlamkas.why-am-i-he.re/RL0Ksh.png) 120 | -------------------------------------------------------------------------------- /client/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"] 3 | } -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | .eslintcache 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LiquidChat", 3 | "author": "LamkasDev", 4 | "description": "An open-source chatting app", 5 | "version": "0.1.0", 6 | "proxy": "https://nekonetwork.net:8080", 7 | "private": true, 8 | "dependencies": { 9 | "mediasoup-client": "^3.6.20", 10 | "react": "^17.0.1", 11 | "react-dom": "^17.0.1", 12 | "react-movable": "^2.5.3", 13 | "socket.io-client": "^3.1.0" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject", 20 | "pack": "electron-builder --dir", 21 | "dist": "electron-builder", 22 | "package": "electron-builder build --win -c.extraMetadata.main=build/electron/electron.js --publish never", 23 | "createproduction-win-win": "react-scripts build && mkdir build\\electron && move build\\electron.js build\\electron\\electron.js && move build\\preload.js build\\electron\\preload.js && xcopy src build\\src /e /i /h && move build\\src\\public\\icon.ico build\\icon.ico && electron-builder build --win -c.extraMetadata.main=build/electron/electron.js --publish never", 24 | "createproduction-win-linux": "react-scripts build && mkdir build\\electron && move build\\electron.js build\\electron\\electron.js && move build\\preload.js build\\electron\\preload.js && xcopy src build\\src /e /i /h && move build\\src\\public\\icon.ico build\\icon.ico && electron-builder build --linux -c.extraMetadata.main=build/electron/electron.js --publish never", 25 | "createproduction-linux-win": "react-scripts build && mkdir build\\electron && move build\\electron.js build\\electron\\electron.js && move build\\preload.js build\\electron\\preload.js && copy -R src build\\src && move build\\src\\public\\icon.ico build\\icon.ico && electron-builder build --win -c.extraMetadata.main=build/electron/electron.js --publish never", 26 | "createproduction-linux-linux": "react-scripts build && mkdir build\\electron && move build\\electron.js build\\electron\\electron.js && move build\\preload.js build\\electron\\preload.js && copy -R src build\\src && move build\\src\\public\\icon.ico build\\icon.ico && electron-builder build --linux -c.extraMetadata.main=build/electron/electron.js --publish never" 27 | }, 28 | "eslintConfig": { 29 | "extends": "react-app" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | }, 43 | "build": { 44 | "appId": "com.electron.chatapp", 45 | "files": [ 46 | "./node_modules/**/*", 47 | "./build/**/*", 48 | "package.json" 49 | ], 50 | "extends": null, 51 | "publish": [ 52 | { 53 | "provider": "github", 54 | "owner": "LamkasDev", 55 | "repo": "liquidchat-releases" 56 | } 57 | ], 58 | "nsis": { 59 | "artifactName": "${productName} Installer.${ext}", 60 | "shortcutName": "LiquidChat" 61 | } 62 | }, 63 | "devDependencies": { 64 | "@babel/core": "^7.12.10", 65 | "@babel/preset-env": "^7.12.11", 66 | "@babel/preset-react": "^7.12.10", 67 | "babel-loader": "^8.1.0", 68 | "electron": "9.4.0", 69 | "electron-builder": "^22.9.1", 70 | "react-scripts": "4.0.1", 71 | "webpack-cli": "^4.4.0" 72 | }, 73 | "postinstall": "electron-builder install-app-deps", 74 | "main": "./public/electron.js", 75 | "homepage": "./" 76 | } 77 | -------------------------------------------------------------------------------- /client/public/electron.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | const path = require('path'); 3 | 4 | let app = electron.app; 5 | let mainWindow; 6 | let tray; 7 | 8 | function createWindow() { 9 | mainWindow = new electron.BrowserWindow({ width: 900, height: 680, webPreferences: { preload: path.join(__dirname, "preload.js"), zoomFactor: 0.8 }, frame: false }); 10 | mainWindow.setMenu(null); 11 | mainWindow.loadURL(`https://nekonetwork.net`); 12 | 13 | mainWindow.on('closed', () => mainWindow = null); 14 | } 15 | 16 | app.on('ready', () => { 17 | tray = new electron.Tray(`${path.join(__dirname, '../logo192.png')}`) 18 | const contextMenu = new electron.Menu(); 19 | contextMenu.append(new electron.MenuItem({ label: "LiquidChat", type: "normal" })) 20 | contextMenu.append(new electron.MenuItem({ type: "separator" })) 21 | contextMenu.append(new electron.MenuItem({ label: "Quit LiquidChat", type: "normal", role: "quit" })) 22 | tray.setToolTip('LiquidChat') 23 | tray.setContextMenu(contextMenu) 24 | createWindow(); 25 | }); 26 | 27 | app.on('window-all-closed', () => { 28 | if (process.platform !== 'darwin') { 29 | app.quit(); 30 | } 31 | }); 32 | 33 | app.on('activate', () => { 34 | if (mainWindow === null) { 35 | createWindow(); 36 | } 37 | }); -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidchat-devs/liquidchat/871569e152fa374889306897fd59c71e45be68c7/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | LiquidChat 17 | 18 | 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidchat-devs/liquidchat/871569e152fa374889306897fd59c71e45be68c7/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo192_notif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidchat-devs/liquidchat/871569e152fa374889306897fd59c71e45be68c7/client/public/logo192_notif.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/preload.js: -------------------------------------------------------------------------------- 1 | const { remote, ipcRenderer } = require("electron"); 2 | 3 | window.addEventListener("DOMContentLoaded", () => { 4 | window.minimizeWindow = minimizeWindow; 5 | window.unmaximizeWindow = unmaximizeWindow; 6 | window.maxUnmaxWindow = maxUnmaxWindow; 7 | window.isWindowMaximized = isWindowMaximized; 8 | window.closeWindow = closeWindow; 9 | window.setIcon = setIcon; 10 | }); 11 | 12 | function getCurrentWindow() { 13 | return remote.getCurrentWindow(); 14 | } 15 | 16 | function setIcon(type, mainWindow = getCurrentWindow()) { 17 | if(type === true) { 18 | mainWindow.setOverlayIcon(require("path").join(__dirname, "../logo192_notif.png"), "New Message") 19 | mainWindow.flashFrame(true); 20 | } else { 21 | mainWindow.setOverlayIcon(null, "") 22 | mainWindow.flashFrame(false); 23 | } 24 | } 25 | 26 | function minimizeWindow(mainWindow = getCurrentWindow()) { 27 | if (mainWindow.minimizable) { 28 | mainWindow.minimize(); 29 | } 30 | } 31 | 32 | function maximizeWindow(mainWindow = getCurrentWindow()) { 33 | if (mainWindow.maximizable) { 34 | mainWindow.maximize(); 35 | } 36 | } 37 | 38 | function unmaximizeWindow(mainWindow = getCurrentWindow()) { 39 | mainWindow.unmaximize(); 40 | } 41 | 42 | function maxUnmaxWindow(mainWindow = getCurrentWindow()) { 43 | if (mainWindow.isMaximized()) { 44 | mainWindow.unmaximize(); 45 | } else { 46 | mainWindow.maximize(); 47 | } 48 | } 49 | 50 | function closeWindow(mainWindow = getCurrentWindow()) { 51 | mainWindow.close(); 52 | } 53 | 54 | function isWindowMaximized(mainWindow = getCurrentWindow()) { 55 | return mainWindow.isMaximized(); 56 | } -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/Components.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as c from './components/index'; 3 | 4 | export default class DialogManager extends React.Component { 5 | render() { 6 | switch(this.props.state.dialogState) { 7 | case 1: 8 | return 9 | 10 | case 2: 11 | return 12 | 13 | case 3: 14 | return 15 | 16 | case 4: 17 | return 18 | 19 | case 5: 20 | return 21 | 22 | case 6: 23 | return 24 | 25 | case 7: 26 | return 27 | 28 | case 8: 29 | return 30 | 31 | case 9: 32 | return 33 | 34 | case 10: 35 | return 36 | 37 | case 11: 38 | return 39 | 40 | case 12: 41 | return 42 | 43 | case 13: 44 | return 45 | 46 | case 14: 47 | return 48 | 49 | case 15: 50 | return 51 | 52 | case 16: 53 | return 54 | 55 | case 17: 56 | return 57 | 58 | case 18: 59 | return 60 | 61 | case 19: 62 | return 63 | 64 | case 20: 65 | return 66 | 67 | case 21: 68 | return 69 | 70 | case 22: 71 | return 72 | 73 | case 23: 74 | return 75 | 76 | case 24: 77 | return 78 | 79 | case 25: 80 | return 81 | 82 | case 26: 83 | return 84 | 85 | default: 86 | return null; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /client/src/components/AccountOptionsDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class AccountOptionsDialog extends React.Component { 4 | render() { 5 | const user = this.props.functions.getUser(this.props.state.session.userID) 6 | 7 | return ( 8 |
9 |
{ this.props.functions.switchDialogState(0); }} style={{ opacity: 0.3 }}>
10 |
11 | {this.props.elements.getContextButton("Profile", (e) => { this.props.functions.setSelectedUser(user.id); this.props.functions.switchDialogState(5); })} 12 | {this.props.elements.getContextButton("Logout", (e) => { this.props.API.endpoints["logout"]({}); }, "var(--color8)")} 13 | {this.props.elements.getContextButton("Copy ID", (e) => { this.props.functions.copyID(this.props.state.session.userID); })} 14 |
15 |
16 | ); 17 | } 18 | } -------------------------------------------------------------------------------- /client/src/components/AccountStatusDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class AccountStatusDialog extends React.Component { 4 | render() { 5 | const user = this.props.functions.getUser(this.props.state.session.userID) 6 | 7 | return ( 8 |
9 |
{ this.props.functions.switchDialogState(0); }} style={{ opacity: 0.3 }}>
10 |
11 |
{ this.props.API.endpoints["updateStatus"]({ status: 1 }); this.props.functions.switchDialogState(-1); }}> 12 |
13 |

Online

14 |
15 |
{ this.props.API.endpoints["updateStatus"]({ status: 2 }); this.props.functions.switchDialogState(-1); }}> 16 |
17 |

Idle

18 |
19 |
{ this.props.API.endpoints["updateStatus"]({ status: 3 }); this.props.functions.switchDialogState(-1); }}> 20 |
21 |

Do not Disturb

22 |
23 |
{ this.props.API.endpoints["updateStatus"]({ status: 0 }); this.props.functions.switchDialogState(-1); }}> 24 |
25 |

Offline

26 |
27 |
{ this.props.functions.switchDialogState(23); }}> 28 |
29 |

{ user.customStatus === undefined ? "Change Status" : user.customStatus }

30 |
31 |
32 |
33 | ); 34 | } 35 | } -------------------------------------------------------------------------------- /client/src/components/AddFriendsDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class AddFriendsDialog extends React.Component { 4 | state = { 5 | friendUsername: "", 6 | friendRequestResult: 0 7 | }; 8 | 9 | handleChange = e => { 10 | this.setState({ 11 | [e.target.name]: e.target.value, 12 | }); 13 | } 14 | 15 | handleSubmit = async e => { 16 | e.preventDefault(); 17 | const res = await this.props.API.endpoints["sendFriendRequestByUsername"]({ target: { username: this.state.friendUsername }}); 18 | this.setState({ 19 | friendRequestResult: res, 20 | }); 21 | 22 | if(res === 1) { this.props.functions.switchDialogState(-1); } 23 | return true; 24 | } 25 | 26 | getErrorText(code) { 27 | switch(code) { 28 | case -1: 29 | return "You already sent a request to this user-"; 30 | 31 | case -2: 32 | return "Nobody with this username found-"; 33 | 34 | case -3: 35 | return "You can't send friend requests to yourself-"; 36 | 37 | default: 38 | return ""; 39 | } 40 | } 41 | 42 | render() { 43 | return ( 44 |
45 |
{ this.props.functions.switchDialogState(0) }}>
46 |
47 |
Add a friend-
48 |
49 |
50 |
51 |
Send request!
52 | { 53 | (this.getErrorText(this.state.friendRequestResult).length > 0 ? 54 |
55 | {this.getErrorText(this.state.friendRequestResult)} 56 |
57 | : "") 58 | } 59 |
60 |
61 | ); 62 | } 63 | } -------------------------------------------------------------------------------- /client/src/components/ChannelHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ChannelHeader extends React.Component { 4 | state = { 5 | searchResult: -1, 6 | searches: -1, 7 | searchTerm: "" 8 | } 9 | 10 | handleChange = e => { 11 | let term = e.target.value; 12 | this.setState({ 13 | searchTerm: term 14 | }) 15 | } 16 | 17 | handleSubmit = async e => { 18 | e.preventDefault(); 19 | 20 | var res = await this.props.API.endpoints["searchMessages"]({ id: this.props.state.currentChannel, filters: { term: this.state.searchTerm }}); 21 | if(isNaN(res) || res.length === 0) { 22 | this.props.functions.setSearches(res); 23 | this.props.functions.setSearchedTerm(this.state.searchTerm); 24 | } else { 25 | this.setState({ 26 | searchResult: res, 27 | }); 28 | } 29 | } 30 | 31 | render() { 32 | let server = this.props.functions.getServer(this.props.state.selectedServer) 33 | let channel = this.props.functions.getChannel(this.props.state.currentChannel) 34 | if(channel === undefined || (server !== undefined && server.channels.includes(channel.id) === false) || (channel.type !== 2 && server === undefined)) { 35 | return null; 36 | } 37 | 38 | let tip = -1; 39 | let messages = -1; 40 | switch(channel.type) { 41 | case 0: 42 | case 2: 43 | messages = channel.messages === undefined ? [] : channel.messages; 44 | tip = "#" + channel.name + " (" + messages.length + ")"; 45 | break; 46 | 47 | case 1: 48 | tip = "." + channel.name + " " + (this.props.state.currentVoiceGroup !== -1 ? this.props.state.currentVoiceGroup.users.length : "Connecting..."); 49 | break; 50 | 51 | default: 52 | break; 53 | } 54 | 55 | switch(channel.type) { 56 | default: 57 | return ( 58 |
59 |
60 |
{tip}
61 | { 62 | channel.description !== undefined ? 63 |
- {channel.description}
64 | : "" 65 | } 66 |
67 |
68 |
{ this.props.functions.switchDialogState(26); }}> 69 |
70 | 71 |
72 | 73 |
74 |
{ this.props.functions.switchDialogState(25); }}> 75 | 76 |
77 |
78 |
79 | ); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /client/src/components/ChannelOptionsDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ChannelOptionsDialog extends React.Component { 4 | state = { 5 | channelDeletionResult: 0, 6 | channelCloneResult: 0 7 | }; 8 | 9 | handleDelete = async e => { 10 | e.preventDefault(); 11 | const res = await this.props.API.endpoints["deleteChannel"]({ id: this.props.state.selectedChannel }); 12 | this.setState({ 13 | channelDeletionResult: res, 14 | }); 15 | 16 | if(res === 1) { this.props.functions.switchDialogState(-1); } 17 | return true; 18 | } 19 | 20 | handleClone = async e => { 21 | e.preventDefault(); 22 | const res = await this.props.API.endpoints["cloneChannel"]({ id: this.props.state.selectedChannel }); 23 | this.setState({ 24 | channelCloneResult: res, 25 | }); 26 | 27 | if(res === 1) { this.props.functions.switchDialogState(-1); } 28 | return true; 29 | } 30 | 31 | render() { 32 | const channel = this.props.functions.getChannel(this.props.state.selectedChannel) 33 | if(channel === undefined) { return null; } 34 | 35 | return ( 36 |
37 |
{ this.props.functions.switchDialogState(0); }} style={{ opacity: 0.3 }}>
38 |
39 | { 40 | channel.author.id === this.props.state.session.userID ? 41 |
42 | {this.props.elements.getContextButton("Edit Channel", (e) => { this.props.functions.switchDialogState(11); })} 43 | {channel.type === 2 ? this.props.elements.getContextButton("Add Friends", (e) => { this.props.functions.switchDialogState(12); }) : ""} 44 | {this.props.elements.getContextButton("Clone Channel", (e) => { this.handleClone(e); })} 45 | {this.props.elements.getContextButton("Delete Channel", (e) => { this.handleDelete(e); }, "var(--color8)")} 46 |
: 47 | "" 48 | } 49 | {this.props.elements.getContextButton("Copy ID", (e) => { this.props.functions.copyID(this.props.state.selectedChannel); })} 50 |
51 |
52 | ); 53 | } 54 | } -------------------------------------------------------------------------------- /client/src/components/CopiedIDDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class CopiedIDDialog extends React.Component { 4 | componentDidMount() { 5 | setTimeout(() => { this.props.functions.switchDialogState(-1); }, 3000) 6 | } 7 | 8 | render() { 9 | return ( 10 |
11 |
12 |
13 |

Copied {this.props.state.copiedID}!

14 |
15 |
16 |
17 | ); 18 | } 19 | } -------------------------------------------------------------------------------- /client/src/components/CreateChannelDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class CreateChannelDialog extends React.Component { 4 | state = { 5 | channelName: "", 6 | channelDescription: undefined, 7 | channelType: 0, 8 | isNSFW: false, 9 | channelCreationResult: 0 10 | }; 11 | 12 | handleChange = e => { 13 | this.setState({ 14 | [e.target.name]: e.target.value, 15 | }); 16 | } 17 | 18 | handleChangeType = val => { 19 | this.setState({ 20 | channelType: val, 21 | }); 22 | } 23 | 24 | handleSwitchNSFW = () => { 25 | this.setState({ 26 | isNSFW: !this.state.isNSFW, 27 | }); 28 | } 29 | 30 | handleSubmit = async e => { 31 | e.preventDefault(); 32 | const res = await this.props.API.endpoints["createChannel"]({ 33 | server: { id: this.props.state.selectedServer }, 34 | name: this.state.channelName, 35 | type: this.state.channelType, 36 | description: this.state.channelDescription, 37 | nsfw: this.state.isNSFW 38 | }); 39 | this.setState({ 40 | channelCreationResult: res, 41 | }); 42 | 43 | if(isNaN(res)) { this.props.functions.switchDialogState(-1); } 44 | return true; 45 | } 46 | 47 | getErrorText(code) { 48 | switch(code) { 49 | case -1: 50 | return "Channel name is too short-"; 51 | 52 | default: 53 | return ""; 54 | } 55 | } 56 | 57 | render() { 58 | return ( 59 |
60 |
{ this.props.functions.switchDialogState(0) }}>
61 |
62 |
Create new channel-
63 |
64 |
65 | 66 |
{this.state.channelType === 1 ? "." : "#"}
67 |
68 |
69 |
{ this.handleChangeType(0); }}> 70 |

Text

71 |
72 |
{ this.handleChangeType(1); }}> 73 |

Voice

74 |
75 |
76 |
77 |
78 | 79 |
80 |
81 |
82 |
{ this.handleSwitchNSFW(); }}> 83 |

{this.state.isNSFW === true ? "NSFW" : "SFW"}

84 |
85 |
86 |
87 |
88 |
89 |
Create!
90 |
91 | { 92 | (this.getErrorText(this.state.channelCreationResult).length > 0 ? 93 |
94 | {this.getErrorText(this.state.channelCreationResult)} 95 |
96 | : "") 97 | } 98 |
99 |
100 | ); 101 | } 102 | } -------------------------------------------------------------------------------- /client/src/components/CreateEmoteDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class CreateEmoteDialog extends React.Component { 4 | state = { 5 | emoteName: "", 6 | emoteAvatar: "defaultAvatar.png", 7 | emoteCreationResult: 0 8 | }; 9 | 10 | handleAvatar = async (box, e) => { 11 | if(e.target.files.length < 1) { return; } 12 | 13 | var file = e.target.files[0]; 14 | e.target.value = "" 15 | this.setState({ 16 | emoteAvatar: file, 17 | }); 18 | 19 | var reader = new FileReader(); 20 | reader.onload = function(e) { 21 | box.refs["emoteImage"].src = e.target.result; 22 | } 23 | reader.readAsDataURL(file); 24 | } 25 | 26 | handleChange = e => { 27 | this.setState({ 28 | [e.target.name]: e.target.value, 29 | }); 30 | } 31 | 32 | handleSubmit = async e => { 33 | e.preventDefault(); 34 | 35 | let res = -1; 36 | switch(this.props.type) { 37 | case 1: 38 | res = await this.props.API.endpoints["createEmote"](this.state.emoteAvatar, {}, { emoteName: this.state.emoteName, type: 1 }); 39 | break; 40 | 41 | case 0: 42 | res = await this.props.API.endpoints["createServerEmote"](this.state.emoteAvatar, {}, { emoteName: this.state.emoteName, serverID: this.props.state.selectedServer, type: 0 }); 43 | break; 44 | 45 | default: 46 | break; 47 | } 48 | 49 | this.setState({ 50 | emoteCreationResult: res, 51 | }); 52 | 53 | if(isNaN(res)) { 54 | this.props.functions.switchDialogState(-1); 55 | return true; 56 | } else { 57 | this.setState({ 58 | emoteCreationResult: res, 59 | }); 60 | } 61 | } 62 | 63 | getErrorText(code) { 64 | switch(code) { 65 | case -1: 66 | return "Emote name is too short-"; 67 | 68 | default: 69 | return ""; 70 | } 71 | } 72 | 73 | render() { 74 | return ( 75 |
76 |
{ this.props.functions.switchDialogState(0) }}>
77 |
78 |
Create new{this.props.type === 0 ? " server" : ""} emote-
79 |
80 | this.refs["emoteEditOverlay"].style = "display: flex;" }/> 81 | 86 | this.handleAvatar(this, e) } type='file' name="fileUploaded"/> 87 |
88 |
89 |
90 |
Create!
91 |
92 | { 93 | (this.getErrorText(this.state.emoteCreationResult).length > 0 ? 94 |
95 | {this.getErrorText(this.state.emoteCreationResult)} 96 |
97 | : "") 98 | } 99 |
100 |
101 | ); 102 | } 103 | } -------------------------------------------------------------------------------- /client/src/components/CreateServerDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class CreateServerDialog extends React.Component { 4 | state = { 5 | serverName: "", 6 | serverAvatar: "defaultAvatar.png", 7 | serverCreationResult: 0 8 | }; 9 | 10 | handleAvatar = async (box, e) => { 11 | if(e.target.files.length < 1) { return; } 12 | 13 | var file = e.target.files[0]; 14 | e.target.value = "" 15 | this.setState({ 16 | serverAvatar: file, 17 | }); 18 | 19 | var reader = new FileReader(); 20 | reader.onload = function(e) { 21 | box.refs["serverImage"].src = e.target.result; 22 | } 23 | reader.readAsDataURL(file); 24 | } 25 | 26 | handleChange = e => { 27 | this.setState({ 28 | [e.target.name]: e.target.value, 29 | }); 30 | } 31 | 32 | handleSubmit = async e => { 33 | e.preventDefault(); 34 | let res = await this.props.API.endpoints["createServer"]({ name: this.state.serverName }); 35 | this.setState({ 36 | serverCreationResult: res, 37 | }); 38 | 39 | if(isNaN(res)) { 40 | if(this.state.serverAvatar !== -1) { 41 | res = await this.props.API.endpoints["updateServerAvatar"](this.state.serverAvatar, {}, { serverID: res.id }) 42 | this.setState({ 43 | serverCreationResult: res, 44 | }); 45 | } 46 | } 47 | 48 | if(isNaN(res)) { 49 | this.props.functions.switchDialogState(-1); 50 | return true; 51 | } else { 52 | this.setState({ 53 | serverCreationResult: res, 54 | }); 55 | } 56 | } 57 | 58 | getErrorText(code) { 59 | switch(code) { 60 | case -1: 61 | return "Server name is too short-"; 62 | 63 | default: 64 | return ""; 65 | } 66 | } 67 | 68 | render() { 69 | return ( 70 |
71 |
{ this.props.functions.switchDialogState(0) }}>
72 |
73 |
Create new server-
74 |
75 | this.refs["serverEditOverlay"].style = "display: flex;" }/> 76 |
{ this.props.functions.setSelectedAvatar(this.state.serverAvatar); this.props.functions.switchDialogState(19) }}> 77 |
Crop
78 |
79 | 84 | this.handleAvatar(this, e) } type='file' name="fileUploaded"/> 85 |
86 |
87 |
88 |
Create!
89 |
90 | { 91 | (this.getErrorText(this.state.serverCreationResult).length > 0 ? 92 |
93 | {this.getErrorText(this.state.serverCreationResult)} 94 |
95 | : "") 96 | } 97 |
98 |
99 | ); 100 | } 101 | } -------------------------------------------------------------------------------- /client/src/components/CropImageDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class CropImageDialog extends React.Component { 4 | componentDidMount = () => { 5 | if(typeof this.props.state.selectedAvatar !== "string") { 6 | var reader = new FileReader(); 7 | reader.onload = function(e) { 8 | this.refs["serverImage"].src = e.target.result; 9 | }.bind(this); 10 | 11 | reader.readAsDataURL(this.props.state.selectedAvatar); 12 | } 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
{ this.props.functions.switchDialogState(0) }}>
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | ); 27 | } 28 | } -------------------------------------------------------------------------------- /client/src/components/EditAccountDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class EditAccountDialog extends React.Component { 4 | state = { 5 | email: "", 6 | accountEditResult: 0 7 | }; 8 | 9 | handleChange = e => { 10 | this.setState({ 11 | [e.target.name]: e.target.value, 12 | }); 13 | } 14 | 15 | handleSubmit = async e => { 16 | e.preventDefault(); 17 | const res = await this.props.API.endpoints["editUser"]({ email: this.state.email }); 18 | this.setState({ 19 | accountEditResult: res, 20 | }); 21 | 22 | if(res === 1) { this.props.functions.switchDialogState(-1); } 23 | return true; 24 | } 25 | 26 | getErrorText(code) { 27 | switch(code) { 28 | default: 29 | return ""; 30 | } 31 | } 32 | 33 | render() { 34 | return ( 35 |
36 |
{ this.props.functions.switchDialogState(0) }}>
37 |
38 |
Manage Account-
39 |
40 |
41 |
42 |
Edit!
43 | { 44 | (this.getErrorText(this.state.accountEditResult).length > 0 ? 45 |
46 | {this.getErrorText(this.state.accountEditResult)} 47 |
48 | : "") 49 | } 50 |
51 |
52 | ); 53 | } 54 | } -------------------------------------------------------------------------------- /client/src/components/EditChannelDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class EditChannelDialog extends React.Component { 4 | state = { 5 | channelName: "", 6 | channelDescription: undefined, 7 | isNSFW: false, 8 | channelEditResult: 0 9 | }; 10 | 11 | componentDidMount = () => { 12 | const channel = this.props.functions.getChannel(this.props.state.selectedChannel); 13 | if(channel !== undefined) { 14 | this.setState({ 15 | channelName: channel.name, 16 | channelDescription: channel.description, 17 | isNSFW: channel.nsfw 18 | }) 19 | } 20 | } 21 | 22 | handleChange = e => { 23 | this.setState({ 24 | [e.target.name]: e.target.value, 25 | }); 26 | } 27 | 28 | handleSwitchNSFW = () => { 29 | this.setState({ 30 | isNSFW: !this.state.isNSFW, 31 | }); 32 | } 33 | 34 | handleSubmit = async e => { 35 | e.preventDefault(); 36 | const res = await this.props.API.endpoints["editChannel"]({ 37 | id: this.props.state.selectedChannel, 38 | name: this.state.channelName, 39 | description: this.state.channelDescription, 40 | nsfw: this.state.isNSFW 41 | }); 42 | this.setState({ 43 | channelEditResult: res, 44 | }); 45 | 46 | if(res === 1) { this.props.functions.switchDialogState(-1); } 47 | return true; 48 | } 49 | 50 | getErrorText(code) { 51 | switch(code) { 52 | case -2: 53 | return "You're not this channel's author-"; 54 | 55 | case -3: 56 | return "Channel name is too short-"; 57 | 58 | default: 59 | return ""; 60 | } 61 | } 62 | 63 | render() { 64 | const channel = this.props.functions.getChannel(this.props.state.selectedChannel) 65 | 66 | return ( 67 |
68 |
{ this.props.functions.switchDialogState(0) }}>
69 |
70 |
Edit channel-
71 |
72 |
73 | 74 |
{channel.type === 1 ? "." : "#"}
75 |
76 |
77 | 78 |
79 |
80 |
81 |
{ this.handleSwitchNSFW(); }}> 82 |

{this.state.isNSFW ? "NSFW" : "SFW"}

83 |
84 |
85 |
86 |
87 |
88 |
Edit!
89 |
90 | { 91 | (this.getErrorText(this.state.channelEditResult).length > 0 ? 92 |
93 | {this.getErrorText(this.state.channelEditResult)} 94 |
95 | : "") 96 | } 97 |
98 |
99 | ); 100 | } 101 | } -------------------------------------------------------------------------------- /client/src/components/EditServerDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class EditServerDialog extends React.Component { 4 | state = { 5 | serverName: "", 6 | serverAvatar: -1, 7 | serverEditResult: 0, 8 | avatarChangeResult: 0, 9 | deletingEmotesEnabled: false 10 | }; 11 | 12 | componentDidMount = () => { 13 | const server = this.props.functions.getServer(this.props.state.selectedServer); 14 | if(server !== undefined) { 15 | this.setState({ 16 | serverName: server.name 17 | }) 18 | } 19 | } 20 | 21 | handleAvatar = async (box, e) => { 22 | if(e.target.files.length < 1) { return; } 23 | 24 | var file = e.target.files[0]; 25 | e.target.value = "" 26 | this.setState({ 27 | serverAvatar: file, 28 | }); 29 | 30 | var reader = new FileReader(); 31 | reader.onload = function(e) { 32 | box.refs["serverImage"].src = e.target.result; 33 | } 34 | reader.readAsDataURL(file); 35 | } 36 | 37 | handleBanner = async (box, e) => { 38 | if(e.target.files.length < 1) { return; } 39 | 40 | var file = e.target.files[0]; 41 | e.target.value = "" 42 | this.setState({ 43 | serverBanner: file, 44 | }); 45 | 46 | var reader = new FileReader(); 47 | reader.onload = function(e) { 48 | box.refs["serverBannerImage"].src = e.target.result; 49 | } 50 | reader.readAsDataURL(file); 51 | } 52 | 53 | handleChange = e => { 54 | this.setState({ 55 | [e.target.name]: e.target.value, 56 | }); 57 | } 58 | 59 | handleSubmit = async e => { 60 | e.preventDefault(); 61 | let res = await this.props.API.endpoints["editServer"]({ id: this.props.state.selectedServer, name: this.state.serverName }); 62 | this.setState({ 63 | serverEditResult: res, 64 | }); 65 | 66 | if(isNaN(res)) { 67 | if(this.state.serverAvatar !== -1) { 68 | res = await this.props.API.endpoints["updateServerAvatar"](this.state.serverAvatar, {}, { serverID: this.props.state.selectedServer }); 69 | this.setState({ 70 | serverEditResult: res, 71 | }); 72 | } 73 | } 74 | 75 | if(isNaN(res)) { 76 | if(this.state.serverBanner !== -1) { 77 | res = await this.props.API.endpoints["updateServerBanner"](this.state.serverBanner, {}, { serverID: this.props.state.selectedServer }); 78 | this.setState({ 79 | serverEditResult: res, 80 | }); 81 | } 82 | } 83 | 84 | if(isNaN(res)) { 85 | this.props.functions.switchDialogState(-1); 86 | return true; 87 | } else { 88 | this.setState({ 89 | serverEditResult: res, 90 | }); 91 | } 92 | } 93 | 94 | getErrorText(code) { 95 | switch(code) { 96 | case -2: 97 | return "You're not this server's author-"; 98 | 99 | case -3: 100 | return "Server name is too short-"; 101 | 102 | default: 103 | return ""; 104 | } 105 | } 106 | 107 | render() { 108 | const server = this.props.functions.getServer(this.props.state.selectedServer); 109 | if(server === undefined) { 110 | return null; 111 | } 112 | 113 | let emotes = [] 114 | server.emotes.forEach(emoteID => { 115 | if(this.props.state.emotes.has(emoteID)) { 116 | emotes.push(this.props.state.emotes.get(emoteID)); 117 | } 118 | }) 119 | 120 | let emoteList = emotes.map((emote, i) => { 121 | return
{ if(this.state.deletingEmotesEnabled) { this.props.API.endpoints["deleteEmote"]({ id: emote.id }); } }}> 122 | 123 | {this.state.deletingEmotesEnabled ? 124 |
125 | 126 |
127 | : ""} 128 | :{emote.name}: 129 |
130 | }, "") 131 | 132 | return ( 133 |
134 |
{ this.props.functions.switchDialogState(0) }}>
135 |
136 |
Edit server-
137 |
138 | this.refs["serverEditOverlay"].style = "display: flex;" }/> 139 |
{ this.props.functions.setSelectedAvatar(server.avatar); this.props.functions.switchDialogState(19) }}> 140 |
Crop
141 |
142 | 147 | this.handleAvatar(this, e) } type='file' name="fileUploaded"/> 148 | 149 |
150 |
151 |
Server Emotes ({emotes.length})
152 |
153 | {emoteList} 154 |
{ this.props.functions.switchDialogState(21); }}> 155 | + 156 |
157 |
{ this.setState({ deletingEmotesEnabled: !this.state.deletingEmotesEnabled }) }}> 158 | - 159 |
160 |
161 |
Server Banner
162 |
163 | {server.banner == null && this.state.serverBanner == null ? 164 |
this.refs["serverBannerEditOverlay"].style = "display: flex;" }>
165 | : this.refs["serverBannerEditOverlay"].style = "display: flex;" }/>} 166 |
{ this.props.functions.setSelectedBanner(server.banner); this.props.functions.switchDialogState(19) }}> 167 |
Crop
168 |
169 | 174 | this.handleBanner(this, e) } type='file' name="fileUploaded"/> 175 |
176 |
177 |
178 |
Edit!
179 |
180 | { 181 | (this.getErrorText(this.state.serverEditResult).length > 0 ? 182 |
183 | {this.getErrorText(this.state.serverEditResult)} 184 |
185 | : "") 186 | } 187 |
188 |
189 | ); 190 | } 191 | } -------------------------------------------------------------------------------- /client/src/components/ForgottenPasswordDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ForgottenPasswordDialog extends React.Component { 4 | state = { 5 | state: 0, 6 | code: "", 7 | passwordRecoveryResult: 0 8 | }; 9 | 10 | handleChange = e => { 11 | this.setState({ 12 | [e.target.name]: e.target.value, 13 | }); 14 | } 15 | 16 | handleSubmit = async e => { 17 | e.preventDefault(); 18 | /*const res = await this.props.API.endpoints["editUser"]({ email: this.state.email }); 19 | this.setState({ 20 | accountEditResult: res, 21 | }); 22 | 23 | if(res === 1) { this.props.functions.switchDialogState(-1); }*/ 24 | return true; 25 | } 26 | 27 | getErrorText(code) { 28 | switch(code) { 29 | default: 30 | return ""; 31 | } 32 | } 33 | 34 | render() { 35 | return ( 36 |
37 |
{ this.props.functions.switchDialogState(0) }}>
38 |
39 |
Password Recovery
40 | {this.state.state === 1 ? 41 |
42 |
43 |
44 |
45 |
Enter-
46 | { 47 | (this.getErrorText(this.state.passwordRecoveryResult).length > 0 ? 48 |
49 | {this.getErrorText(this.state.passwordRecoveryResult)} 50 |
51 | : "") 52 | } 53 |
54 | : 55 |
56 |
57 |

{ this.props.functions.switchDialogState(14); }}>To recover your account enter a code that was sent to your email-

58 |
59 |
{ this.setState({ state: 1 }) }}>I'm ready!
60 | { 61 | (this.getErrorText(this.state.passwordRecoveryResult).length > 0 ? 62 |
63 | {this.getErrorText(this.state.passwordRecoveryResult)} 64 |
65 | : "") 66 | } 67 |
68 | } 69 |
70 |
71 | ); 72 | } 73 | } -------------------------------------------------------------------------------- /client/src/components/ImageDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ImageDialog extends React.Component { 4 | render() { 5 | return ( 6 |
7 |
{ this.props.functions.switchDialogState(0) }}>
8 | { this.props.functions.switchDialogState(9); this.props.functions.setBox(e.pageX, e.pageY); e.preventDefault(); }} 11 | onClick={(e) => { this.props.functions.switchDialogState(8); e.preventDefault(); }} 12 | /> 13 | {this.props.isMenuOpen === true ? 14 |
15 | {this.props.elements.getContextButton("Copy Link", (e) => { this.props.functions.copyID(this.props.state.fileEndpoint + "/" + this.props.state.selectedImage.name); })} 16 | {this.props.elements.getContextButton("Open Link", (e) => { window.open(this.props.state.fileEndpoint + "/" + this.props.state.selectedImage.name); })} 17 |
18 | : "" 19 | } 20 |
21 | ); 22 | } 23 | } -------------------------------------------------------------------------------- /client/src/components/InviteFriendsDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class InviteFriendsDialog extends React.Component { 4 | state = { 5 | invitationResult: 0, 6 | pendingInvitations: [] 7 | }; 8 | 9 | inviteUser = async (id, userID, type) => { 10 | let res = -1; 11 | switch(type) { 12 | case 0: 13 | res = await this.props.API.endpoints["addToDMChannel"]({ channel: id, user: userID }); 14 | break; 15 | 16 | case 1: 17 | res = await this.props.API.endpoints["createInvite"]({ server: { id: id }}); 18 | if(isNaN(res)) { 19 | res = await this.props.API.endpoints["sendDM"]({ user: { id: userID }, text: 'http://nekonetwork.net/invite/' + res.id }); 20 | this.setState({ 21 | invitationResult: res, 22 | }); 23 | } 24 | 25 | if(isNaN(res)) { 26 | let newPending = this.state.pendingInvitations; 27 | newPending.push(userID); 28 | this.setState({ 29 | pendingInvitations: newPending 30 | }); 31 | 32 | return true; 33 | } else { 34 | this.setState({ 35 | invitationResult: res, 36 | }); 37 | } 38 | break; 39 | 40 | default: 41 | break; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | render() { 48 | let loggedUser = this.props.functions.getUser(this.props.state.session.userID); 49 | let server = this.props.functions.getServer(this.props.state.selectedServer); 50 | let channel = this.props.functions.getChannel(this.props.state.selectedChannel); 51 | let target = channel === undefined || channel.members === undefined ? server : channel; 52 | let type = channel === undefined || channel.members === undefined ? 1 : 0; 53 | 54 | const friendList = loggedUser.friends.map((friendID, i) => { 55 | const friend = this.props.functions.getUser(friendID); 56 | if(friend === undefined) { 57 | return null; 58 | } 59 | 60 | return ( 61 |
{ this.props.functions.setSelectedUser(friend.id); this.props.functions.setBox(e.pageX, e.pageY); this.props.functions.switchDialogState(6); e.preventDefault(); e.stopPropagation(); } }> 62 |
63 |
64 | 65 |
66 | {friend.username} 67 |
68 | { 69 | target.members.includes(friend.id) ? 70 |
Joined!
71 | : (this.state.pendingInvitations.includes(friend.id) ? 72 |
Invited!
: 73 |
{ this.inviteUser(target.id, friend.id, type); e.preventDefault(); } }>Invite
74 | ) 75 | } 76 |
77 |
78 |
79 | ) 80 | }); 81 | 82 | return ( 83 |
84 |
{ this.props.functions.switchDialogState(0) }}>
85 |
86 |
{type === 0 ? "> Add Friends-" : "> Invite Friends-"}
87 | {friendList} 88 |
89 |
90 | ); 91 | } 92 | } -------------------------------------------------------------------------------- /client/src/components/LoginForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class LoginForm extends React.Component { 4 | state = { 5 | username: "", 6 | password: "", 7 | clicked: false, 8 | loginResult: 0 9 | }; 10 | 11 | componentDidMount = async() => { 12 | const res = await this.props.API.endpoints["login"]({ username: this.state.username, password: this.state.password, authType: "autologin" }); 13 | this.setState({ 14 | loginResult: res, 15 | }); 16 | } 17 | 18 | handleChange = e => { 19 | this.setState({ 20 | [e.target.name]: e.target.value, 21 | }); 22 | } 23 | 24 | handleSubmit = async e => { 25 | e.preventDefault(); 26 | const res = await this.props.API.endpoints["login"]({ username: this.state.username, password: this.state.password, authType: "default" }); 27 | this.setState({ 28 | loginResult: res, 29 | }); 30 | } 31 | 32 | getErrorText(code) { 33 | switch(code) { 34 | case -3: 35 | return "Session expired-"; 36 | 37 | case -2: 38 | return "User not found-"; 39 | 40 | case -1: 41 | return "Invalid password-"; 42 | 43 | case 0: 44 | default: 45 | return ""; 46 | } 47 | } 48 | 49 | getSuccessText(code) { 50 | const user = this.props.functions.getUser(this.props.state.session.userID) 51 | switch(code) { 52 | case 0: 53 | case -3: 54 | case -2: 55 | case -1: 56 | return ""; 57 | 58 | default: 59 | return "Logging in as " + (user !== undefined ? user.username : "Loading") + "..."; 60 | } 61 | } 62 | 63 | render() { 64 | const form = ( 65 |
66 |
67 | 68 |
69 | ); 70 | return ( 71 |
72 |
73 |
74 |
75 |
76 | 77 |
Login
78 |
79 | {form} 80 |
81 |
Login!
82 |
83 |
84 |

{ this.props.functions.switchFormState(); }}>Register?

85 | { 86 | (this.getErrorText(this.state.loginResult).length > 0 ? 87 |
88 | {this.getErrorText(this.state.loginResult)} 89 |
90 | : (this.getSuccessText(this.state.loginResult).length > 0 ? 91 |
92 | {this.getSuccessText(this.state.loginResult)} 93 |
94 | : 95 | "")) 96 | } 97 |
98 |
99 |
100 | ); 101 | } 102 | } -------------------------------------------------------------------------------- /client/src/components/MessageOptionsDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class MessageOptionsDialog extends React.Component { 4 | state = { 5 | messageDeletionResult: 0 6 | }; 7 | 8 | handleDelete = async e => { 9 | e.preventDefault(); 10 | const res = await this.props.API.endpoints["deleteMessage"]({ id: this.props.state.selectedMessage.id }); 11 | this.setState({ 12 | messageDeletionResult: res, 13 | }); 14 | 15 | if(res === 1) { this.props.functions.switchDialogState(-1); } 16 | return true; 17 | } 18 | 19 | handleEdit = async e => { 20 | e.preventDefault(); 21 | const res = 1 22 | this.props.functions.setEditedMessage(this.props.state.selectedMessage.text == null ? "" : this.props.state.selectedMessage.text); 23 | this.props.functions.startEditingMessage(this.props.state.selectedMessage); 24 | 25 | if(res === 1) { this.props.functions.switchDialogState(-1); } 26 | return true; 27 | } 28 | 29 | render() { 30 | return ( 31 |
32 |
{ this.props.functions.switchDialogState(0); }} style={{ opacity: 0.3 }}>
33 |
34 | {this.props.state.selectedMessage.author.id === this.props.state.session.userID ? this.props.elements.getContextButton("Delete", (e) => { this.handleDelete(e); }, "var(--color8)") : "" } 35 | {this.props.state.selectedMessage.author.id === this.props.state.session.userID ? this.props.elements.getContextButton("Edit", (e) => { this.handleEdit(e); }) : ""} 36 | {this.props.state.selectedMessage.file == null ? "" : this.props.elements.getContextButton("Copy link to file", (e) => { this.props.functions.copyID(this.props.state.fileEndpoint + "/" + this.props.state.selectedMessage.file.name); })} 37 | {this.props.state.selectedMessage.file == null ? "" : this.props.elements.getContextButton("Open link to file", (e) => { window.open(this.props.state.fileEndpoint + "/" + this.props.state.selectedMessage.file.name, -1); })} 38 | {this.props.elements.getContextButton("Copy ID", (e) => { this.props.functions.copyID(this.props.state.selectedMessage.id); })} 39 |
40 |
41 | ); 42 | } 43 | } -------------------------------------------------------------------------------- /client/src/components/PinnedMessagesDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class PinnedMessagesDialog extends React.Component { 4 | render() { 5 | return ( 6 |
7 |
{ this.props.functions.switchDialogState(0) }}>
8 |
9 |
10 |
Pinned Messages
11 |
12 |
13 |
14 | ); 15 | } 16 | } -------------------------------------------------------------------------------- /client/src/components/ProfileOptionsDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ProfileOptionsDialog extends React.Component { 4 | sendFriendRequest(id) { 5 | this.props.API.endpoints["sendFriendRequest"]({ id: id }); 6 | } 7 | 8 | removeFriend(id) { 9 | this.props.API.endpoints["removeFriend"]({ id: id }); 10 | } 11 | 12 | removeFromDMChannel(channelID, userID) { 13 | this.props.API.endpoints["removeFromDMChannel"]({ channel: { id: channelID }, user: { id: userID } }); 14 | } 15 | 16 | removeFromServer(serverID, userID) { 17 | this.props.API.endpoints["kickFromServer"]({ server: { id: serverID }, user: { id: userID }}); 18 | } 19 | 20 | async dmUser(id) { 21 | var channel = await this.props.API.endpoints["getSuitableDMChannel"](id); 22 | if(channel !== undefined) { 23 | this.props.functions.switchDialogState(0); 24 | this.props.functions.switchChannelTypes(1); 25 | this.props.functions.switchChannel(channel.id); 26 | } 27 | } 28 | 29 | render() { 30 | let loggedUser = this.props.functions.getUser(this.props.state.session.userID); 31 | let selectedUser = this.props.functions.getUser(this.props.state.selectedUser); 32 | let currentChannel = this.props.functions.getChannel(this.props.state.currentChannel) 33 | let currentServer = this.props.functions.getServer(this.props.state.selectedServer) 34 | 35 | return ( 36 |
37 |
{ this.props.functions.switchDialogState(0); }} style={{ opacity: 0.3 }}>
38 |
39 | {this.props.elements.getContextButton("Profile", (e) => { this.props.functions.switchDialogState(5); })} 40 | {loggedUser.friends.includes(selectedUser.id) === true ? this.props.elements.getContextButton("Message", (e) => { this.dmUser(selectedUser.id); }) : ""} 41 | {selectedUser.id !== loggedUser.id && loggedUser.friends.includes(selectedUser.id) === false ? this.props.elements.getContextButton("Add Friend", (e) => { this.sendFriendRequest(selectedUser.id); }) : ""} 42 | {loggedUser.friends.includes(selectedUser.id) === true ? this.props.elements.getContextButton("Remove Friend", (e) => { this.removeFriend(selectedUser.id); }, "var(--color8)") : ""} 43 | {currentChannel !== undefined && currentChannel.type === 2 && currentChannel.author.id === loggedUser.id && selectedUser.id !== loggedUser.id ? this.props.elements.getContextButton("Remove from group", (e) => { this.removeFromDMChannel(currentChannel.id , selectedUser.id); }, "var(--color8)") : ""} 44 | {currentServer !== undefined && currentServer.author.id === loggedUser.id && selectedUser.id !== loggedUser.id ? this.props.elements.getContextButton("Kick from server", (e) => { this.removeFromServer(currentServer.id, selectedUser.id); }, "var(--color8)") : ""} 45 | {this.props.elements.getContextButton("Copy ID", (e) => { this.props.functions.copyID(selectedUser.id); })} 46 |
47 |
48 | ); 49 | } 50 | } -------------------------------------------------------------------------------- /client/src/components/RegisterForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class RegisterForm extends React.Component { 4 | state = { 5 | username: "", 6 | password: "", 7 | password2: "", 8 | clicked: false, 9 | registerResult: 0 10 | }; 11 | 12 | handleChange = e => { 13 | this.setState({ 14 | [e.target.name]: e.target.value, 15 | }); 16 | } 17 | 18 | handleSubmit = async e => { 19 | e.preventDefault(); 20 | const res = await this.props.API.endpoints["register"]({ username: this.state.username, password: this.state.password, password2: this.state.password2 }); 21 | this.setState({ 22 | registerResult: res, 23 | }); 24 | } 25 | 26 | getErrorText(code) { 27 | switch(code) { 28 | case -4: 29 | return "Password is too short, atleast 8 characters."; 30 | 31 | case -3: 32 | return "Username is too short, atleast 3 characters."; 33 | 34 | case -2: 35 | return "Passwords don't match-"; 36 | 37 | case -1: 38 | return "Username already taken-"; 39 | 40 | case 0: 41 | default: 42 | return ""; 43 | } 44 | } 45 | 46 | getSuccessText(code) { 47 | const user = this.props.functions.getUser(this.props.state.session.userID) 48 | switch(code) { 49 | case 0: 50 | case -4: 51 | case -3: 52 | case -2: 53 | case -1: 54 | return ""; 55 | 56 | default: 57 | return "Registering as " + (user !== undefined ? user.username : "Loading") + "..."; 58 | } 59 | } 60 | 61 | render(){ 62 | const form = ( 63 |
64 |
65 |
66 | 67 |
68 | ); 69 | return ( 70 |
71 |
72 |
73 |
74 |
75 | 76 |
Register
77 |
78 | {form} 79 |
80 |
Register!
81 |
82 |
83 |

{ this.props.functions.switchFormState(); }}>Login?

84 | { 85 | (this.getErrorText(this.state.registerResult).length > 0 ? 86 |
87 | {this.getErrorText(this.state.registerResult)} 88 |
89 | : "") 90 | } 91 | { 92 | (this.getSuccessText(this.state.registerResult).length > 0 ? 93 |
94 | {this.getSuccessText(this.state.registerResult)} 95 |
96 | : "") 97 | } 98 |
99 |
100 |
101 | ); 102 | } 103 | } -------------------------------------------------------------------------------- /client/src/components/SearchMessagesDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class SearchMessagesDialog extends React.Component { 4 | state = { 5 | currentFace: this.getRandomFace() 6 | } 7 | 8 | render() { 9 | let channel = this.props.functions.getChannel(this.props.state.currentChannel) 10 | var searches = this.props.state.searches === -1 ? null : this.props.state.searches.map((message, i) => { 11 | let messageHTML = this.props.functions.getFormattedMessage(this, message); 12 | return ( 13 |
14 | {messageHTML} 15 |
16 | ) 17 | }); 18 | 19 | return ( 20 |
21 |
{ this.props.functions.switchDialogState(0); this.props.functions.setSearches(-1); }}>
22 |
23 |
24 |
Search:
25 |
{this.props.state.searchedTerm.length === 0 ? this.state.currentFace : this.props.state.searchedTerm}
26 |
in #{channel.name}
27 |
{this.props.state.searches === -1 ? "" : " (" + this.props.state.searches.length + ")"}
28 |
29 |
30 | {searches} 31 |
32 |
33 |
34 | ); 35 | } 36 | 37 | getRandomFace() { 38 | let array = [ 39 | ">w<", 40 | ">~<", 41 | ">.<", 42 | ">..<", 43 | ">//<", 44 | ">v<", 45 | ">o<", 46 | ">x<", 47 | ">-<" 48 | ] 49 | 50 | return array[Math.floor(Math.random() * array.length)]; 51 | } 52 | } -------------------------------------------------------------------------------- /client/src/components/ServerDiscoveryDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ServerDiscoveryDialog extends React.Component { 4 | state = { 5 | serverName: "", 6 | serverAvatar: "defaultAvatar.png", 7 | serverCreationResult: 0 8 | }; 9 | 10 | handleAvatar = async (box, e) => { 11 | if(e.target.files.length < 1) { return; } 12 | 13 | var file = e.target.files[0]; 14 | e.target.value = "" 15 | this.setState({ 16 | serverAvatar: file, 17 | }); 18 | 19 | var reader = new FileReader(); 20 | reader.onload = function(e) { 21 | box.refs["serverImage"].src = e.target.result; 22 | } 23 | reader.readAsDataURL(file); 24 | } 25 | 26 | handleChange = e => { 27 | this.setState({ 28 | [e.target.name]: e.target.value, 29 | }); 30 | } 31 | 32 | handleSubmit = async e => { 33 | e.preventDefault(); 34 | let res = await this.props.API.endpoints["createServer"]({ name: this.state.serverName }); 35 | this.setState({ 36 | serverCreationResult: res, 37 | }); 38 | 39 | if(isNaN(res)) { 40 | if(this.state.serverAvatar !== -1) { 41 | res = await this.props.API.endpoints["updateServerAvatar"](this.state.serverAvatar, {}, { serverID: res.id }) 42 | this.setState({ 43 | serverCreationResult: res, 44 | }); 45 | } 46 | } 47 | 48 | if(isNaN(res)) { 49 | this.props.functions.switchDialogState(-1); 50 | return true; 51 | } else { 52 | this.setState({ 53 | serverCreationResult: res, 54 | }); 55 | } 56 | } 57 | 58 | getErrorText(code) { 59 | switch(code) { 60 | case -1: 61 | return "Server name is too short-"; 62 | 63 | default: 64 | return ""; 65 | } 66 | } 67 | 68 | render() { 69 | return ( 70 |
71 |
{ this.props.functions.switchDialogState(0) }}>
72 |
73 |
Server Discovery
74 |
75 |
76 |
Popular
77 |
78 |
79 |
Anime
80 |
81 |
82 |
Games
83 |
84 |
85 |
86 |
87 | ); 88 | } 89 | } -------------------------------------------------------------------------------- /client/src/components/ServerOptionsDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ServerOptionsDialog extends React.Component { 4 | state = { 5 | serverDeletionResult: 0 6 | }; 7 | 8 | handleDelete = async e => { 9 | e.preventDefault(); 10 | const res = await this.props.API.endpoints["deleteServer"]({ id: this.props.state.selectedServer }); 11 | this.setState({ 12 | serverDeletionResult: res, 13 | }); 14 | 15 | if(res === 1) { this.props.functions.switchDialogState(-1); } 16 | return true; 17 | } 18 | 19 | render() { 20 | const server = this.props.functions.getServer(this.props.state.selectedServer) 21 | if(server === undefined) { return null; } 22 | 23 | return ( 24 |
25 |
{ this.props.functions.switchDialogState(0); }} style={{ opacity: 0.3 }}>
26 |
27 | { 28 | server.author.id === this.props.state.session.userID ? 29 |
30 | {this.props.elements.getContextButton("Edit Server", (e) => { this.props.functions.switchDialogState(18); })} 31 | {this.props.elements.getContextButton("Invite Friends", (e) => { this.props.functions.switchDialogState(12); })} 32 |
: 33 | "" 34 | } 35 | {this.props.elements.getContextButton("Leave Server", (e) => { this.props.API.endpoints["leaveServer"]({ id: server.id }); }, "var(--color8)")} 36 | {server.author.id === this.props.state.session.userID ? this.props.elements.getContextButton("Delete Server", (e) => { this.handleDelete(e); }, "var(--color8)") : "" } 37 | {this.props.elements.getContextButton("Copy ID", (e) => { this.props.functions.copyID(server.id); })} 38 |
39 |
40 | ); 41 | } 42 | } -------------------------------------------------------------------------------- /client/src/components/SetCustomStatusDialog.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class SetCustomStatusDialog extends React.Component { 4 | state = { 5 | status: "", 6 | statusChangeResult: 0 7 | }; 8 | 9 | componentDidMount = () => { 10 | const user = this.props.functions.getUser(this.props.state.session.userID); 11 | if(user !== undefined) { 12 | this.setState({ 13 | status: user.customStatus 14 | }) 15 | } 16 | } 17 | 18 | handleChange = e => { 19 | this.setState({ 20 | [e.target.name]: e.target.value, 21 | }); 22 | } 23 | 24 | handleSubmit = async e => { 25 | e.preventDefault(); 26 | const res = await this.props.API.endpoints["updateCustomStatus"]({ customStatus: this.state.status }); 27 | this.setState({ 28 | statusChangeResult: res, 29 | }); 30 | 31 | if(isNaN(res)) { this.props.functions.switchDialogState(-1); } 32 | return true; 33 | } 34 | 35 | getErrorText(code) { 36 | switch(code) { 37 | case -1: 38 | return "Status is too short-"; 39 | 40 | default: 41 | return ""; 42 | } 43 | } 44 | 45 | render() { 46 | return ( 47 |
48 |
{ this.props.functions.switchDialogState(0) }}>
49 |
50 |
Set a custom status
51 |
52 | 53 |
~
54 |
55 |
56 |
57 |
Change!
58 |
59 | { 60 | (this.getErrorText(this.state.statusChangeResult).length > 0 ? 61 |
62 | {this.getErrorText(this.state.statusChangeResult)} 63 |
64 | : "") 65 | } 66 |
67 |
68 | ); 69 | } 70 | } -------------------------------------------------------------------------------- /client/src/components/index.js: -------------------------------------------------------------------------------- 1 | export {default as AccountOptionsDialog} from './AccountOptionsDialog' 2 | export {default as AccountStatusDialog} from './AccountStatusDialog' 3 | export {default as AddFriendsDialog} from './AddFriendsDialog' 4 | export {default as ChannelHeader} from './ChannelHeader' 5 | export {default as ChannelOptionsDialog} from './ChannelOptionsDialog' 6 | export {default as ChannelSelector} from './ChannelSelector' 7 | export {default as Chat} from './Chat' 8 | export {default as CopiedIDDialog} from './CopiedIDDialog' 9 | export {default as CreateChannelDialog} from './CreateChannelDialog' 10 | export {default as CreateEmoteDialog} from './CreateEmoteDialog' 11 | export {default as CreateServerDialog} from './CreateServerDialog' 12 | export {default as CropImageDialog} from './CropImageDialog' 13 | export {default as EditAccountDialog} from './EditAccountDialog' 14 | export {default as EditChannelDialog} from './EditChannelDialog' 15 | export {default as EditServerDialog} from './EditServerDialog' 16 | export {default as ForgottenPasswordDialog} from './ForgottenPasswordDialog' 17 | export {default as ImageDialog} from './ImageDialog' 18 | export {default as InviteFriendsDialog} from './InviteFriendsDialog' 19 | export {default as LoginForm} from './LoginForm' 20 | export {default as MessageOptionsDialog} from './MessageOptionsDialog' 21 | export {default as ProfileDialog} from './ProfileDialog' 22 | export {default as ProfileOptionsDialog} from './ProfileOptionsDialog' 23 | export {default as RegisterForm} from './RegisterForm' 24 | export {default as Send} from './Send' 25 | export {default as ServerOptionsDialog} from './ServerOptionsDialog' 26 | export {default as SetCustomStatusDialog} from './SetCustomStatusDialog' 27 | export {default as SettingsDialog} from './SettingsDialog' 28 | export {default as ServerDiscoveryDialog} from './ServerDiscoveryDialog' 29 | export {default as PinnedMessagesDialog} from './PinnedMessagesDialog' 30 | export {default as SearchMessagesDialog} from './SearchMessagesDialog' -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './style.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | /**/ 8 | 9 | /**/, 10 | document.getElementById('root') 11 | ); -------------------------------------------------------------------------------- /client/src/public/fonts/uwuFont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidchat-devs/liquidchat/871569e152fa374889306897fd59c71e45be68c7/client/src/public/fonts/uwuFont.woff -------------------------------------------------------------------------------- /client/src/public/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidchat-devs/liquidchat/871569e152fa374889306897fd59c71e45be68c7/client/src/public/icon.ico -------------------------------------------------------------------------------- /client/src/public/scripts/Constants.js: -------------------------------------------------------------------------------- 1 | export default class Constants { 2 | constructor(_main) { 3 | this.mainClass = _main; 4 | } 5 | 6 | getStatusColor(status) { 7 | switch(status) { 8 | case 0: 9 | return "#676767"; 10 | 11 | case 1: 12 | return "#3baf3b"; 13 | 14 | case 2: 15 | return "#ec9c24"; 16 | 17 | case 3: 18 | return "#ff6161"; 19 | 20 | case 4: 21 | return "#6b61ff"; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /client/src/public/scripts/DateFormatter.js: -------------------------------------------------------------------------------- 1 | function formatDate(timestamp) { 2 | const date = new Date(timestamp) 3 | const curr = new Date() 4 | 5 | var i = date.getDay() === curr.getDay() ? 1 : (date.getDay() === curr.getDay() - 1 ? 2 : 3) 6 | var i2 = i === 1 ? "Today" : "Yesterday" 7 | switch(i) { 8 | case 1: 9 | case 2: 10 | return i2 + " at " + 11 | (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) 12 | + ":" + 13 | (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) 14 | 15 | case 3: 16 | return date.toUTCString() 17 | 18 | default: 19 | return timestamp 20 | } 21 | } 22 | 23 | function formatDuration(timestamp, timestamp2) { 24 | var time = timestamp2 - timestamp; 25 | var timeString = ""; 26 | var timeLeft = time; 27 | 28 | var ms = timeLeft % 1000; 29 | timeLeft = (timeLeft - ms) / 1000; 30 | var secs = timeLeft % 60; 31 | timeLeft = (timeLeft - secs) / 60; 32 | var mins = timeLeft % 60; 33 | timeLeft = (timeLeft - mins) / 60; 34 | var hrs = timeLeft % 24; 35 | timeLeft = (timeLeft - hrs) / 24; 36 | var days = timeLeft % 30; 37 | timeLeft = (timeLeft - days) / 30; 38 | var months = timeLeft % 12; 39 | timeLeft = (timeLeft - months) / 12; 40 | var years = timeLeft; 41 | 42 | if(years > 0) { timeString += years + "y "; } 43 | if(months > 0) { timeString += months + "mon "; } 44 | if(days > 0) { timeString += days + "d "; } 45 | if(hrs > 0) { timeString += hrs + "h "; } 46 | if(mins > 0) { timeString += mins + "m "; } 47 | if(secs > 0) { timeString += secs + "s "; } 48 | 49 | timeString = timeString.substring(0, timeString.length - 1); 50 | return timeString; 51 | } 52 | 53 | function formatDuration2(time) { 54 | var timeString = ""; 55 | var timeLeft = time; 56 | 57 | var ms = timeLeft % 1000; 58 | timeLeft = (timeLeft - ms) / 1000; 59 | var secs = timeLeft % 60; 60 | timeLeft = (timeLeft - secs) / 60; 61 | var mins = timeLeft % 60; 62 | timeLeft = (timeLeft - mins) / 60; 63 | var hrs = timeLeft % 24; 64 | timeLeft = (timeLeft - hrs) / 24; 65 | var days = timeLeft; 66 | 67 | timeString = (secs < 10 ? "0" + secs : secs) + ":" + timeString; 68 | if(mins > 0) { timeString = (mins < 10 ? "0" + mins : mins) + ":" + timeString; } else { timeString = "0:" + timeString } 69 | if(hrs > 0) { timeString = (hrs < 10 ? "0" + hrs : hrs) + ":" + timeString; } 70 | if(days > 0) { timeString = (days < 10 ? "0" + days : days) + ":" + timeString; } 71 | 72 | timeString = timeString.substring(0, timeString.length - 1); 73 | return timeString; 74 | } 75 | 76 | export { formatDate, formatDuration, formatDuration2 } -------------------------------------------------------------------------------- /client/src/public/scripts/ElementBuilder.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ElementBuilder { 4 | constructor(_main) { 5 | this.mainClass = _main; 6 | } 7 | 8 | getContextButton(text, callback, color) { 9 | return
{ callback(e); }}> 10 |

> {text}

11 |
12 | } 13 | } -------------------------------------------------------------------------------- /client/src/public/scripts/SizeFormatter.js: -------------------------------------------------------------------------------- 1 | function formatBytes(bytes, appendSize) { 2 | var k = 1024; 3 | var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 4 | if(appendSize === undefined) { appendSize = false; } 5 | if(bytes === 0) { return bytes + (appendSize ? ' ' + sizes[0] : '') } 6 | 7 | var i = Math.floor(Math.log(bytes) / Math.log(k)); 8 | var result = parseFloat((bytes / (k ** i)).toFixed(2)); 9 | return result + (appendSize ? ' ' + sizes[i] : ''); 10 | } 11 | 12 | export { formatBytes } -------------------------------------------------------------------------------- /client/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebPackPlugin = require("html-webpack-plugin"); 2 | 3 | module.exports = { 4 | module: { 5 | rules: [ 6 | { 7 | test: /\.(js|jsx)$/, 8 | exclude: /node_modules/, 9 | use: { 10 | loader: "babel-loader" 11 | } 12 | }, 13 | { 14 | test: /\.html$/, 15 | use: [ 16 | { 17 | loader: "html-loader" 18 | } 19 | ] 20 | } 21 | ] 22 | }, 23 | plugins: [ 24 | new HtmlWebPackPlugin({ 25 | template: "./src/index.html", 26 | filename: "./index.html" 27 | }) 28 | ] 29 | }; -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "nodemon server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "axios": "^0.21.1", 15 | "bcrypt": "^5.0.0", 16 | "body-parser": "^1.19.0", 17 | "compression": "^1.7.4", 18 | "cookie": "^0.4.1", 19 | "cookie-parser": "^1.4.5", 20 | "cors": "^2.8.5", 21 | "express": "^4.17.1", 22 | "formidable": "^1.2.2", 23 | "fs-extra": "^9.0.1", 24 | "mysql2": "^2.1.0", 25 | "socket.io": "^3.1.0", 26 | "websocket": "^1.0.32" 27 | }, 28 | "devDependencies": { 29 | "nodemon": "^2.0.4" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/scripts/data/db_add.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | addServer(db, server) { 3 | if(db.DEBUG) { 4 | console.log(" - [db] Adding Server(id: " + server.id + ") into the database..."); 5 | } 6 | 7 | var query = "INSERT IGNORE INTO servers (id, name, createdAt, authorID, avatar, channels, members, invites, emotes) VALUES" + db.contructQuestionMarks(9); 8 | db.sqlConn.promise().execute(query, [ server.id, db.escapeString(server.name), server.createdAt, server.author.id, server.avatar, server.channels.join(","), server.members.join(","), server.invites.join(","), server.emotes.join(",") ]) 9 | .then((result, err) => { 10 | if(err) { throw err; } 11 | }); 12 | }, 13 | 14 | addUser(db, user) { 15 | if(db.DEBUG) { 16 | console.log(" - [db] Adding User(id: " + user.id + ") into the database..."); 17 | } 18 | 19 | var query0 = "(id, username, createdAt, avatar, password, friends, dmChannels, servers, userStatus, badges, emotes" + (user.customStatus == null ? ")" : ", customStatus)") 20 | var query1 = [ user.id, db.escapeString(user.username), user.createdAt, user.avatar, user.password, user.friends.join(","), user.dmChannels.join(","), user.servers.join(","), user.userStatus, user.badges.join(","), user.emotes.join(",") ] 21 | if(user.customStatus != null) { query1.push(user.customStatus); } 22 | 23 | var query = "INSERT IGNORE INTO users " + query0 + " VALUES " + db.contructQuestionMarks(query1.length); 24 | db.sqlConn.promise().execute(query, query1) 25 | .then((result, err) => { 26 | if(err) { throw err; } 27 | }); 28 | }, 29 | 30 | addChannel(db, channel) { 31 | if(db.DEBUG) { 32 | console.log(" - [db] Adding Channel(id: " + channel.id + ") into the database..."); 33 | } 34 | 35 | var query0 = "(id, name, type, nsfw, createdAt, authorID" + (channel.position == null ? "" : ", position") + (channel.members == null ? "" : ", members") + (channel.description == null ? "" : ", description") + (channel.server == null ? ")" : ", serverID)") 36 | var query1 = [ channel.id, db.escapeString(channel.name), channel.type, channel.nsfw, channel.createdAt, channel.author.id ] 37 | if(channel.position != null) { query1.push(channel.position) } 38 | if(channel.members != null) { query1.push(channel.members.join(",")) } 39 | if(channel.description != null) { query1.push(channel.description) } 40 | if(channel.server != null) { query1.push(channel.server.id) } 41 | 42 | var query = "INSERT IGNORE INTO channels " + query0 + " VALUES" + db.contructQuestionMarks(query1.length); 43 | db.sqlConn.promise().execute(query, query1) 44 | .then((result, err) => { 45 | if(err) { throw err; } 46 | }); 47 | }, 48 | 49 | addMessage(db, message) { 50 | if(db.DEBUG) { 51 | console.log(" - [db] Adding Message(id: " + message.id + ") into the database..."); 52 | } 53 | 54 | var query0 = "(id, createdAt, channelID, edited, type" + (message.text == null ? "" : ", text") + (message.author == null ? "" : ", authorID") + (message.file == null ? ")" : ", fileName, fileSize)") 55 | var query1 = [ message.id, message.createdAt, message.channel.id, message.edited, message.type ] 56 | if(message.text != null) { query1.push(db.escapeString(message.text)) } 57 | if(message.author != null) { query1.push(message.author.id) } 58 | if(message.file != null) { query1.push(db.escapeString(message.file.name), message.file.size) } 59 | 60 | var query = "INSERT IGNORE INTO messages " + query0 + " VALUES" + db.contructQuestionMarks(query1.length); 61 | db.sqlConn.promise().execute(query, query1) 62 | .then((result, err) => { 63 | if(err) { throw err; } 64 | }); 65 | }, 66 | 67 | addFriendRequest(db, friendRequest) { 68 | if(db.DEBUG) { 69 | console.log(" - [db] Adding FriendRequest(id: " + friendRequest.id + ") into the database..."); 70 | } 71 | 72 | var query = "INSERT IGNORE INTO friendrequests (id, authorID, targetID, createdAt) VALUES('" + friendRequest.id + "', '" + friendRequest.author.id + "', '" + friendRequest.target.id + "', " + friendRequest.createdAt + ")"; 73 | db.sqlConn.promise().query(query) 74 | .then((result, err) => { 75 | if(err) { throw err; } 76 | }); 77 | }, 78 | 79 | addInvite(db, invite) { 80 | if(db.DEBUG) { 81 | console.log(" - [db] Adding Invite(id: " + invite.id + ") into the database..."); 82 | } 83 | 84 | var query = "INSERT IGNORE INTO invites (id, authorID, serverID, createdAt) VALUES('" + invite.id + "', '" + invite.author.id + "', '" + invite.server.id + "', " + invite.createdAt + ")"; 85 | db.sqlConn.promise().query(query) 86 | .then((result, err) => { 87 | if(err) { throw err; } 88 | }); 89 | }, 90 | 91 | addEmote(db, emote) { 92 | if(db.DEBUG) { 93 | console.log(" - [db] Adding Emote(id: " + emote.id + ") into the database..."); 94 | } 95 | 96 | var query0 = "(id, name, createdAt, authorID, file, type" + (emote.server == null ? ")" : ", serverID)") 97 | var query1 = [ emote.id, emote.name, emote.createdAt, emote.author.id, emote.file, emote.type ] 98 | if(emote.server != null) { query1.push(db.escapeString(emote.server.id)) } 99 | 100 | var query = "INSERT IGNORE INTO emotes " + query0 + " VALUES" + db.contructQuestionMarks(query1.length); 101 | db.sqlConn.promise().execute(query, query1) 102 | .then((result, err) => { 103 | if(err) { throw err; } 104 | }); 105 | }, 106 | 107 | addNote(db, note) { 108 | if(db.DEBUG) { 109 | console.log(" - [db] Adding Note(id: " + note.id + ") into the database..."); 110 | } 111 | 112 | var query = "INSERT IGNORE INTO notes (id, authorID, targetID, createdAt, text) VALUES('" + note.id + "', '" + note.author.id + "', '" + note.target.id + "', " + note.createdAt + ", '" + db.escapeString(note.text) + "')"; 113 | db.sqlConn.promise().query(query) 114 | .then((result, err) => { 115 | if(err) { throw err; } 116 | }); 117 | } 118 | } -------------------------------------------------------------------------------- /server/scripts/data/db_delete.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | deleteServer(db, id) { 3 | if(db.DEBUG) { 4 | console.log(" - [db] Deleting Server(id: " + id + ") from the database..."); 5 | } 6 | 7 | var query = "DELETE FROM servers WHERE id='" + id + "'"; 8 | db.sqlConn.promise().query(query) 9 | .then((result, err) => { 10 | if(err) { throw err; } 11 | }); 12 | }, 13 | 14 | deleteUser(db, id) { 15 | if(db.DEBUG) { 16 | console.log(" - [db] Deleting User(id: " + id + ") from the database..."); 17 | } 18 | 19 | var query = "DELETE FROM users WHERE id='" + id + "'"; 20 | db.sqlConn.promise().query(query) 21 | .then((result, err) => { 22 | if(err) { throw err; } 23 | }); 24 | }, 25 | 26 | deleteChannel(db, id) { 27 | if(db.DEBUG) { 28 | console.log(" - [db] Deleting Channel(id: " + id + ") from the database..."); 29 | } 30 | 31 | var query = "DELETE FROM channels WHERE id='" + id + "'"; 32 | db.sqlConn.promise().query(query) 33 | .then((result, err) => { 34 | if(err) { throw err; } 35 | }); 36 | }, 37 | 38 | deleteMessage(db, id) { 39 | if(db.DEBUG) { 40 | console.log(" - [db] Deleting Message(id: " + id + ") from the database..."); 41 | } 42 | 43 | var query = "DELETE FROM messages WHERE id='" + id + "'"; 44 | db.sqlConn.promise().query(query) 45 | .then((result, err) => { 46 | if(err) { throw err; } 47 | }); 48 | }, 49 | 50 | deleteFriendRequest(db, id) { 51 | if(db.DEBUG) { 52 | console.log(" - [db] Deleting FriendRequest(id: " + id + ") from the database..."); 53 | } 54 | 55 | var query = "DELETE FROM friendrequests WHERE id='" + id + "'"; 56 | db.sqlConn.promise().query(query) 57 | .then((result, err) => { 58 | if(err) { throw err; } 59 | }); 60 | }, 61 | 62 | deleteInvite(db, id) { 63 | if(db.DEBUG) { 64 | console.log(" - [db] Deleting Invite(id: " + id + ") from the database..."); 65 | } 66 | 67 | var query = "DELETE FROM invites WHERE id='" + id + "'"; 68 | db.sqlConn.promise().query(query) 69 | .then((result, err) => { 70 | if(err) { throw err; } 71 | }); 72 | }, 73 | 74 | deleteEmote(db, id) { 75 | if(db.DEBUG) { 76 | console.log(" - [db] Deleting Emote(id: " + id + ") from the database..."); 77 | } 78 | 79 | var query = "DELETE FROM emotes WHERE id='" + id + "'"; 80 | db.sqlConn.promise().query(query) 81 | .then((result, err) => { 82 | if(err) { throw err; } 83 | }); 84 | }, 85 | 86 | deleteAllData(db) { 87 | if(db.DEBUG) { 88 | console.log(" - [db] Deleting all data from the database..."); 89 | } 90 | 91 | var query = "DELETE FROM users"; 92 | db.sqlConn.promise().query(query) 93 | .then((result, err) => { 94 | if(err) { throw err; } 95 | }); 96 | query = "DELETE FROM channels"; 97 | db.sqlConn.promise().query(query) 98 | .then((result, err) => { 99 | if(err) { throw err; } 100 | }); 101 | query = "DELETE FROM messages"; 102 | db.sqlConn.promise().query(query) 103 | .then((result, err) => { 104 | if(err) { throw err; } 105 | }); 106 | query = "DELETE FROM friendrequests"; 107 | db.sqlConn.promise().query(query) 108 | .then((result, err) => { 109 | if(err) { throw err; } 110 | }); 111 | query = "DELETE FROM invites"; 112 | db.sqlConn.promise().query(query) 113 | .then((result, err) => { 114 | if(err) { throw err; } 115 | }); 116 | query = "DELETE FROM emotes"; 117 | db.sqlConn.promise().query(query) 118 | .then((result, err) => { 119 | if(err) { throw err; } 120 | }); 121 | query = "DELETE FROM notes"; 122 | db.sqlConn.promise().query(query) 123 | .then((result, err) => { 124 | if(err) { throw err; } 125 | }); 126 | } 127 | } -------------------------------------------------------------------------------- /server/scripts/data/db_edit.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | editServer(db, server) { 3 | if(db.DEBUG) { 4 | console.log(" - [db] Editing Server(id: " + server.id + ") in the database..."); 5 | } 6 | 7 | var query0 = "name=?, avatar=?, channels=?, members=?, emotes=?" + (server.banner == null ? "" : ", banner=?") 8 | var query1 = [ db.escapeString(server.name), server.avatar, server.channels.join(","), server.members.join(","), server.emotes.join(",") ] 9 | if(server.banner != null) { query1.push(server.banner); } 10 | 11 | var query = "UPDATE servers SET " + query0 + " WHERE id='" + server.id + "'"; 12 | db.sqlConn.promise().execute(query, query1) 13 | .then((result, err) => { 14 | if(err) { throw err; } 15 | }); 16 | }, 17 | 18 | editUser(db, user) { 19 | if(db.DEBUG) { 20 | console.log(" - [db] Editing User(id: " + user.id + ") in the database..."); 21 | } 22 | 23 | var query0 = "username=?, avatar=?, friends=?, dmChannels=?, servers=?, userStatus=?, badges=?, emotes=?" + (user.email == null ? "" : ", email=?") + (user.password == null ? "" : ", password=?") + (user.customStatus == null || user.customStatus.length < 1 ? "" : ", customStatus=?") 24 | + (user.gh_username == null ? "" : ", gh_username=?") + (user.gh_token == null ? "" : ", gh_token=?") + (user.reddit_username == null ? "" : ", reddit_username=?") + (user.reddit_token == null ? "" : ", reddit_token=?") 25 | + (user.osu_username == null ? "" : ", osu_username=?") + (user.osu_token == null ? "" : ", osu_token=?") + (user.twitch_username == null ? "" : ", twitch_username=?") + (user.twitch_token == null ? "" : ", twitch_token=?") 26 | + (user.blizzard_username == null ? "" : ", blizzard_username=?") + (user.blizzard_token == null ? "" : ", blizzard_token=?") + (user.spotify_username == null ? "" : ", spotify_username=?") + (user.spotify_token == null ? "" : ", spotify_token=?") 27 | + (user.discord_username == null ? "" : ", discord_username=?") + (user.discord_token == null ? "" : ", discord_token=?") 28 | var query1 = [ db.escapeString(user.username), user.avatar, user.friends.join(","), user.dmChannels.join(","), user.servers.join(","), user.userStatus, user.badges.join(","), user.emotes.join(",") ] 29 | if(user.email != null) { query1.push(db.escapeString(user.email)); } 30 | if(user.password != null) { query1.push(user.password); } 31 | if(user.customStatus != null) { if(user.customStatus.length < 1) { this.clearUserStatus(db, user); } else { query1.push(user.customStatus); } } 32 | if(user.gh_username != null) { query1.push(user.gh_username); } 33 | if(user.gh_token != null) { query1.push(user.gh_token); } 34 | if(user.reddit_username != null) { query1.push(user.reddit_username); } 35 | if(user.reddit_token != null) { query1.push(user.reddit_token); } 36 | if(user.osu_username != null) { query1.push(user.osu_username); } 37 | if(user.osu_token != null) { query1.push(user.osu_token); } 38 | if(user.twitch_username != null) { query1.push(user.twitch_username); } 39 | if(user.twitch_token != null) { query1.push(user.twitch_token); } 40 | if(user.blizzard_username != null) { query1.push(user.blizzard_username); } 41 | if(user.blizzard_token != null) { query1.push(user.blizzard_token); } 42 | if(user.spotify_username != null) { query1.push(user.spotify_username); } 43 | if(user.spotify_token != null) { query1.push(user.spotify_token); } 44 | if(user.discord_username != null) { query1.push(user.discord_username); } 45 | if(user.discord_token != null) { query1.push(user.discord_token); } 46 | 47 | var query = "UPDATE users SET " + query0 + " WHERE id='" + user.id + "'"; 48 | db.sqlConn.promise().execute(query, query1) 49 | .then((result, err) => { 50 | if(err) { throw err; } 51 | }); 52 | }, 53 | 54 | clearUserStatus(db, user) { 55 | db.sqlConn.promise().query("UPDATE users SET customStatus=NULL WHERE id='" + user.id + "'") 56 | .then((result, err) => { 57 | if(err) { throw err; } 58 | }); 59 | }, 60 | 61 | removeUserConnection(db, user, type) { 62 | db.sqlConn.promise().query("UPDATE users SET " + db.escapeString(type) + "_token=NULL, " + db.escapeString(type) + "_username=NULL WHERE id='" + user.id + "'") 63 | .then((result, err) => { 64 | if(err) { throw err; } 65 | }); 66 | }, 67 | 68 | editMessage(db, message) { 69 | if(db.DEBUG) { 70 | console.log(" - [db] Editing Message(id: " + message.id + ") in the database..."); 71 | } 72 | 73 | var query0 = "edited=?, type=?" + (message.text == null ? "" : ", text=?") + (message.file == null ? "" : ", fileName=?, fileSize=?") 74 | var query1 = [ message.edited, message.type ] 75 | if(message.text != null) { query1.push(db.escapeString(message.text)); } 76 | if(message.file != null) { query1.push(db.escapeString(message.file.name), message.file.size); } 77 | 78 | var query = "UPDATE messages SET " + query0 + " WHERE id='" + message.id + "'"; 79 | db.sqlConn.promise().execute(query, query1) 80 | .then((result, err) => { 81 | if(err) { throw err; } 82 | }); 83 | }, 84 | 85 | editChannel(db, channel) { 86 | if(db.DEBUG) { 87 | console.log(" - [db] Editing Channel(id: " + channel.id + ") in the database..."); 88 | } 89 | 90 | var query0 = "name=?, nsfw=?" + (channel.position == null ? "" : ", position=?") + (channel.members == null ? "" : ", members=?") + (channel.description == null ? "" : ", description=?") 91 | var query1 = [ db.escapeString(channel.name), channel.nsfw ] 92 | if(channel.position != null) { query1.push(channel.position); } 93 | if(channel.members != null) { query1.push(channel.members.join(",")); } 94 | if(channel.description != null) { query1.push(channel.description); } 95 | 96 | var query = "UPDATE channels SET " + query0 + " WHERE id='" + channel.id + "'"; 97 | db.sqlConn.promise().execute(query, query1) 98 | .then((result, err) => { 99 | if(err) { throw err; } 100 | }); 101 | }, 102 | 103 | editNote(db, note) { 104 | if(db.DEBUG) { 105 | console.log(" - [db] Editing Note(id: " + note.id + ") in the database..."); 106 | } 107 | 108 | var query0 = "text=?" 109 | var query1 = [ db.escapeString(note.text) ] 110 | 111 | var query = "UPDATE notes SET " + query0 + " WHERE id='" + note.id + "'"; 112 | db.sqlConn.promise().execute(query, query1) 113 | .then((result, err) => { 114 | if(err) { throw err; } 115 | }); 116 | } 117 | } -------------------------------------------------------------------------------- /server/scripts/data/db_manager.js: -------------------------------------------------------------------------------- 1 | class DatabaseManager { 2 | constructor(_app, _sql, _conn) { 3 | this.sql = _sql 4 | this.sqlConn = _conn; 5 | this.app = _app; 6 | 7 | this.db_add = require('./db_add'); 8 | this.db_edit = require('./db_edit'); 9 | this.db_delete = require('./db_delete'); 10 | this.db_fetch = require('./db_fetch'); 11 | this.DEBUG = true; 12 | 13 | if(process.argv.includes("-rd")) { 14 | this.db_delete.deleteAllData(this); 15 | } 16 | } 17 | 18 | contructQuestionMarks(i) { 19 | let str = "("; 20 | for(let i2 = 0; i2 < i; i2++) { 21 | str += "?," 22 | } 23 | str = str.substring(0, str.length - 1) + ")" 24 | 25 | return str; 26 | } 27 | 28 | escapeString(str) { 29 | return str; 30 | } 31 | } 32 | 33 | module.exports = DatabaseManager; -------------------------------------------------------------------------------- /server/scripts/endpoints/acceptFriendRequest.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/acceptFriendRequest', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.app.epFunc.processFriendRequest(req, res, req.body, true) 11 | console.log("> accepted friend request - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | } 15 | 16 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/addToDMChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/addToDMChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.addToDMChannel(req, res, req.body) 11 | console.log("> added to dm channel - " + req.body.channel.id + "/" + req.body.user.id) 12 | }).bind(this)); 13 | } 14 | 15 | async addToDMChannel(req, res, _data) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, _data.channel.id); 19 | var targetUser = await this.app.db.db_fetch.fetchUser(this.app.db, _data.user.id); 20 | 21 | if(channel === undefined) { 22 | res.send(JSON.stringify({ status: -1 })) 23 | return; 24 | } else if(targetUser === undefined) { 25 | res.send(JSON.stringify({ status: -2 })) 26 | return; 27 | } else if(channel.author.id !== user.id) { 28 | res.send(JSON.stringify({ status: -3 })) 29 | return; 30 | } else if(channel.members.includes(targetUser.id) === true) { 31 | res.send(JSON.stringify({ status: -4 })) 32 | return; 33 | } else { 34 | res.sendStatus(200); 35 | } 36 | 37 | channel.members.push(targetUser.id); 38 | channel.members.forEach(id => { 39 | this.app.epFunc.emitToUser(id, "updateChannel", channel) 40 | }); 41 | 42 | targetUser.dmChannels.push(channel.id); 43 | this.app.epFunc.emitToUser(targetUser.id, "createChannel", channel); 44 | 45 | await this.app.db.db_edit.editChannel(this.app.db, channel); 46 | await this.app.db.db_edit.editUser(this.app.db, targetUser); 47 | this.app.epFunc.sendSystemMessage({ channel: { id: channel.id }, text: "<@" + targetUser.id + "> was added by <@" + user.id + ">", type: 1 }) 48 | } 49 | } 50 | 51 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/auth_blizzard.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/auth_blizzard', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | var session = this.app.sessions.get(req.cookies['sessionID']); 10 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 11 | var data = req.query; 12 | 13 | //https://us.battle.net/oauth/authorize?client_id=24428d45ed4b42448c9a33f1161585c5&redirect_uri=https://nekonetwork.net:8080/auth_blizzard&response_type=code&scope=openid 14 | this.app.axios.post("https://us.battle.net/oauth/token", 15 | "code=" + data.code + "&grant_type=authorization_code&redirect_uri=https://nekonetwork.net:8080/auth_blizzard&scope=openid", 16 | { 17 | headers: { 18 | "Content-Type": "application/x-www-form-urlencoded", 19 | "Authorization": "Basic " + Buffer.from(this.app.config["auth_blizzard_id"] + ":" + this.app.config["auth_blizzard_token"]).toString("base64"), 20 | } 21 | }).then(res1 => { 22 | if(res1.data["access_token"] !== undefined) { 23 | this.app.axios.get("https://us.battle.net/oauth/userinfo", { 24 | headers: { 25 | "Authorization": "Bearer " + res1.data["access_token"] 26 | } 27 | }).then(res2 => { 28 | this.app.epFunc.editUser(req, res, { "blizzard_token": res1.data["access_token"], "blizzard_username": res2.data["battletag"] }); 29 | }); 30 | } else { 31 | res.sendStatus(401) 32 | } 33 | }); 34 | }).bind(this)); 35 | } 36 | } 37 | 38 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/auth_discord.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/auth_discord', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | var session = this.app.sessions.get(req.cookies['sessionID']); 10 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 11 | var data = req.query; 12 | 13 | //https://discord.com/api/oauth2/authorize?client_id=803587850762453072&redirect_uri=https://nekonetwork.net:8080/auth_discord&response_type=code&scope=identify 14 | this.app.axios.post("https://discord.com/api/v6/oauth2/token", 15 | "client_id=" + this.app.config["auth_discord_id"] + "&client_secret=" + this.app.config["auth_discord_token"] + "&code=" + data.code + "&grant_type=authorization_code&redirect_uri=https://nekonetwork.net:8080/auth_discord&scope=identify", 16 | { 17 | headers: { 18 | "Content-Type": "application/x-www-form-urlencoded", 19 | } 20 | }).then(res1 => { 21 | if(res1.data["access_token"] !== undefined) { 22 | this.app.axios.get("https://discord.com/api/v6/users/@me", { 23 | headers: { 24 | "Authorization": "Bearer " + res1.data["access_token"] 25 | } 26 | }).then(res2 => { 27 | this.app.epFunc.editUser(req, res, { "discord_token": res1.data["access_token"], "discord_username": res2.data["username"] }); 28 | }); 29 | } else { 30 | res.sendStatus(401) 31 | } 32 | }); 33 | }).bind(this)); 34 | } 35 | } 36 | 37 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/auth_gh.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/auth_gh', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | var session = this.app.sessions.get(req.cookies['sessionID']); 10 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 11 | var data = req.query; 12 | 13 | //https://github.com/login/oauth/authorize?client_id=91bcd730211830731d9a 14 | this.app.axios.post("https://github.com/login/oauth/access_token?client_id=" + this.app.config["auth_gh_id"] + "&client_secret=" + this.app.config["auth_gh_token"] + "&code=" + data.code, {}, 15 | { 16 | headers: { "Accept": "application/json" } 17 | }).then(res1 => { 18 | if(res1.data["access_token"] !== undefined) { 19 | this.app.axios.get("https://api.github.com/user", { 20 | headers: { "Authorization": "token " + res1.data["access_token"] } 21 | }).then(res2 => { 22 | this.app.epFunc.editUser(req, res, { "gh_token": res1.data["access_token"], "gh_username": res2.data.login }); 23 | }); 24 | } else { 25 | res.sendStatus(401) 26 | } 27 | }); 28 | }).bind(this)); 29 | } 30 | } 31 | 32 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/auth_osu.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/auth_osu', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | var session = this.app.sessions.get(req.cookies['sessionID']); 10 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 11 | var data = req.query; 12 | 13 | //https://osu.ppy.sh/oauth/authorize?client_id=4883&redirect_uri=https://nekonetwork.net:8080/auth_osu&response_type=code&scope=public 14 | this.app.axios.post("https://osu.ppy.sh/oauth/token", 15 | "client_id=" + this.app.config["auth_osu_id"] + "&client_secret=" + this.app.config["auth_osu_token"] + "&code=" + data.code + "&grant_type=authorization_code&redirect_uri=https://nekonetwork.net:8080/auth_osu", 16 | { 17 | headers: { "Accept": "application/json" } 18 | }).then(res1 => { 19 | if(res1.data["access_token"] !== undefined) { 20 | this.app.axios.get("https://osu.ppy.sh/api/v2/me", { 21 | headers: { "Authorization": "Bearer " + res1.data["access_token"] } 22 | }).then(res2 => { 23 | this.app.epFunc.editUser(req, res, { "osu_token": res1.data["access_token"], "osu_username": res2.data.username }); 24 | }); 25 | } else { 26 | res.sendStatus(401) 27 | } 28 | }); 29 | }).bind(this)); 30 | } 31 | } 32 | 33 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/auth_reddit.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/auth_reddit', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | var session = this.app.sessions.get(req.cookies['sessionID']); 10 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 11 | var data = req.query; 12 | 13 | //https://www.reddit.com/api/v1/authorize?client_id=g8QfIB742iwMKw&response_type=code&state=a&redirect_uri=https://nekonetwork.net:8080/auth_reddit&duration=permanent&scope=identity 14 | this.app.axios.post("https://www.reddit.com/api/v1/access_token", 15 | "grant_type=authorization_code&code=" + data.code + "&redirect_uri=https://nekonetwork.net:8080/auth_reddit", 16 | { 17 | headers: { 18 | "Content-Type": "application/x-www-form-urlencoded", 19 | "Authorization": "Basic " + Buffer.from(this.app.config["auth_reddit_id"] + ":" + this.app.config["auth_reddit_token"]).toString("base64"), 20 | } 21 | }).then(res1 => { 22 | if(res1.data["access_token"] !== undefined) { 23 | this.app.axios.get("https://oauth.reddit.com/api/v1/me", { 24 | headers: { "Authorization": "bearer " + res1.data["access_token"] } 25 | }).then(res2 => { 26 | this.app.epFunc.editUser(req, res, { "reddit_token": res1.data["access_token"], "reddit_username": res2.data.name }); 27 | }); 28 | } else { 29 | res.sendStatus(401) 30 | } 31 | }); 32 | }).bind(this)); 33 | } 34 | } 35 | 36 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/auth_spotify.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/auth_spotify', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | var session = this.app.sessions.get(req.cookies['sessionID']); 10 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 11 | var data = req.query; 12 | 13 | //https://accounts.spotify.com/authorize?client_id=d10fc3159d9c4c3ea3c307df4b04ca43&redirect_uri=https://nekonetwork.net:8080/auth_spotify&response_type=code 14 | this.app.axios.post("https://accounts.spotify.com/api/token", 15 | "code=" + data.code + "&grant_type=authorization_code&redirect_uri=https://nekonetwork.net:8080/auth_spotify&scope=openid", 16 | { 17 | headers: { 18 | "Content-Type": "application/x-www-form-urlencoded", 19 | "Authorization": "Basic " + Buffer.from(this.app.config["auth_spotify_id"] + ":" + this.app.config["auth_spotify_token"]).toString("base64"), 20 | } 21 | }).then(res1 => { 22 | if(res1.data["access_token"] !== undefined) { 23 | this.app.axios.get("https://api.spotify.com/v1/me", { 24 | headers: { 25 | "Authorization": "Bearer " + res1.data["access_token"] 26 | } 27 | }).then(res2 => { 28 | this.app.epFunc.editUser(req, res, { "spotify_token": res1.data["access_token"], "spotify_username": res2.data["display_name"] }); 29 | }); 30 | } else { 31 | res.sendStatus(401) 32 | } 33 | }); 34 | }).bind(this)); 35 | } 36 | } 37 | 38 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/auth_twitch.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/auth_twitch', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | var session = this.app.sessions.get(req.cookies['sessionID']); 10 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 11 | var data = req.query; 12 | 13 | //https://id.twitch.tv/oauth2/authorize?client_id=3oxkg8rjxqox7kkx4qu9b7d441mzse&redirect_uri=https://nekonetwork.net:8080/auth_twitch&response_type=code&scope=user:read:email 14 | this.app.axios.post("https://id.twitch.tv/oauth2/token", 15 | "client_id=" + this.app.config["auth_twitch_id"] + "&client_secret=" + this.app.config["auth_twitch_token"] + "&code=" + data.code + "&grant_type=authorization_code&redirect_uri=https://nekonetwork.net:8080/auth_twitch", 16 | { 17 | headers: { "Accept": "application/json" } 18 | }).then(res1 => { 19 | if(res1.data["access_token"] !== undefined) { 20 | this.app.axios.get("https://api.twitch.tv/helix/users", { 21 | headers: { 22 | "Authorization": "Bearer " + res1.data["access_token"], 23 | "Client-Id": this.app.config["auth_twitch_id"] 24 | } 25 | }).then(res2 => { 26 | this.app.epFunc.editUser(req, res, { "twitch_token": res1.data["access_token"], "twitch_username": res2.data.data[0]["login"] }); 27 | }); 28 | } else { 29 | res.sendStatus(401) 30 | } 31 | }); 32 | }).bind(this)); 33 | } 34 | } 35 | 36 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/cloneChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/cloneChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.cloneChannel(req, res, req.body) 11 | console.log("> cloned channel - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | 15 | async cloneChannel(req, res, _channel) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, _channel.id); 19 | if(channel === undefined) { 20 | res.send(JSON.stringify({ status: -1 })) 21 | return; 22 | } else if(channel.type === 2) { 23 | res.send(JSON.stringify({ status: -2 })) 24 | return; 25 | } 26 | 27 | channel.id = this.app.crypto.randomBytes(16).toString("hex"); 28 | channel.createdAt = Date.now(); 29 | channel.author = { id: user.id }; 30 | 31 | switch(channel.type) { 32 | case 0: 33 | case 1: 34 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, channel.server.id); 35 | if(server === undefined) { 36 | res.send(JSON.stringify({ status: -3 })) 37 | return; 38 | } else if(server.author.id !== user.id) { 39 | res.send(JSON.stringify({ status: -4 })) 40 | return; 41 | } 42 | 43 | channel.position = server.channels.length; 44 | server.channels.push(channel.id) 45 | server.members.forEach(id => { 46 | this.app.epFunc.emitToUser(id, "createChannel", channel); 47 | this.app.epFunc.emitToUser(id, "updateServer", server); 48 | }); 49 | 50 | await this.app.db.db_edit.editServer(this.app.db, server); 51 | break; 52 | } 53 | 54 | await this.app.db.db_add.addChannel(this.app.db, channel); 55 | res.send(JSON.stringify(channel)) 56 | } 57 | } 58 | 59 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/createChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/createChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.createChannel(req, res, req.body) 11 | console.log("> created channel - " + req.body.name) 12 | }).bind(this)); 13 | } 14 | 15 | async createChannel(req, res, _channel) { 16 | if(_channel.name.length < 1) { 17 | res.send(JSON.stringify({ status: -1 })) 18 | return; 19 | } 20 | 21 | var session = this.app.sessions.get(req.cookies['sessionID']); 22 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 23 | var channel = { 24 | id: this.app.crypto.randomBytes(16).toString("hex"), 25 | name: _channel.name, 26 | description: _channel.description, 27 | nsfw: _channel.nsfw === undefined ? false : _channel.nsfw, 28 | type: _channel.type, 29 | createdAt: Date.now(), 30 | author: { 31 | id: user.id 32 | } 33 | } 34 | 35 | switch(_channel.type) { 36 | case 0: 37 | case 1: 38 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, _channel.server.id); 39 | if(server === undefined) { 40 | res.send(JSON.stringify({ status: -2 })) 41 | return; 42 | } else if(server.author.id !== user.id) { 43 | res.send(JSON.stringify({ status: -4 })) 44 | return; 45 | } 46 | 47 | channel.server = { id: _channel.server.id }; 48 | channel.position = server.channels.length; 49 | server.channels.push(channel.id) 50 | server.members.forEach(id => { 51 | this.app.epFunc.emitToUser(id, "createChannel", channel); 52 | this.app.epFunc.emitToUser(id, "updateServer", server); 53 | }); 54 | 55 | await this.app.db.db_edit.editServer(this.app.db, server); 56 | break; 57 | 58 | case 2: 59 | channel.members = _channel.members; 60 | channel.members.forEach(async(id) => { 61 | var user2 = await this.app.db.db_fetch.fetchUser(this.app.db, id); 62 | user2.dmChannels.push(channel.id); 63 | this.app.db.db_edit.editUser(this.app.db, user2); 64 | 65 | this.app.epFunc.emitToUser(user2.id, "createChannel", channel); 66 | this.app.epFunc.emitToUser(user2.id, "updateUser", user2); 67 | }); 68 | break; 69 | } 70 | 71 | await this.app.db.db_add.addChannel(this.app.db, channel); 72 | res.send(JSON.stringify(channel)) 73 | } 74 | } 75 | 76 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/createEmote.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/createEmote', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.createEmote(req, res, req.query.emoteName, req.query.serverID, req.query.type); 11 | console.log("> created emote - " + req.query.emoteName + "/" + req.query.serverID + "/" + req.query.type); 12 | }).bind(this)); 13 | } 14 | 15 | async createEmote(req, res, emoteName, serverID, type) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var form = this.app.formidable({ multiples: true }); 19 | 20 | var emote = { 21 | id: this.app.crypto.randomBytes(16).toString("hex"), 22 | createdAt: Date.now(), 23 | author: { 24 | id: user.id 25 | }, 26 | name: emoteName, 27 | type: parseInt(type) 28 | } 29 | 30 | switch(emote.type) { 31 | case 0: 32 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, serverID); 33 | 34 | if(server === undefined) { 35 | res.send(JSON.stringify({ status: -1 })) 36 | return; 37 | } else if (server.author.id !== user.id) { 38 | res.send(JSON.stringify({ status: -2 })) 39 | return; 40 | } 41 | 42 | form.uploadDir = this.app.filesStorage; 43 | form.keepExtensions = true; 44 | 45 | var fileName = req.query.fileName; 46 | var fileID = this.app.crypto.randomBytes(16).toString("hex"); 47 | var fileID2 = fileID + (fileName.substring(fileName.lastIndexOf("."))) 48 | console.log("> received emote - " + fileName) 49 | 50 | form.parse(req, async function(err, fields, files) { 51 | this.app.fs.rename(files.fileUploaded.path, this.app.filesStorage + fileID2, function(err) { 52 | if (err) { throw err; } 53 | }); 54 | 55 | emote.file = fileID2; 56 | emote.server = { id: serverID } 57 | 58 | await this.app.db.db_add.addEmote(this.app.db, emote); 59 | server.emotes.push(emote.id) 60 | await this.app.epFunc.updateServer(server, true) 61 | 62 | res.send(JSON.stringify(emote)); 63 | }.bind(this)); 64 | break; 65 | 66 | case 1: 67 | form.uploadDir = this.app.filesStorage; 68 | form.keepExtensions = true; 69 | 70 | var fileName = req.query.fileName; 71 | var fileID = this.app.crypto.randomBytes(16).toString("hex"); 72 | var fileID2 = fileID + (fileName.substring(fileName.lastIndexOf("."))) 73 | console.log("> received emote - " + fileName) 74 | 75 | form.parse(req, async function(err, fields, files) { 76 | this.app.fs.rename(files.fileUploaded.path, this.app.filesStorage + fileID2, function(err) { 77 | if (err) { throw err; } 78 | }); 79 | 80 | emote.file = fileID2; 81 | 82 | await this.app.db.db_add.addEmote(this.app.db, emote); 83 | user.emotes.push(emote.id) 84 | await this.app.epFunc.updateUser(user, true) 85 | 86 | res.send(JSON.stringify(emote)); 87 | }.bind(this)); 88 | break; 89 | 90 | case 2: 91 | if (user.badges.includes("2")) { 92 | res.send(JSON.stringify({ status: -2 })) 93 | return; 94 | } 95 | 96 | form.uploadDir = this.app.filesStorage; 97 | form.keepExtensions = true; 98 | 99 | var fileName = req.query.fileName; 100 | var fileID = this.app.crypto.randomBytes(16).toString("hex"); 101 | var fileID2 = fileID + (fileName.substring(fileName.lastIndexOf("."))) 102 | console.log("> received emote - " + fileName) 103 | 104 | form.parse(req, async function(err, fields, files) { 105 | this.app.fs.rename(files.fileUploaded.path, this.app.filesStorage + fileID2, function(err) { 106 | if (err) { throw err; } 107 | }); 108 | 109 | emote.file = fileID2; 110 | 111 | await this.app.db.db_add.addEmote(this.app.db, emote); 112 | 113 | res.send(JSON.stringify(emote)); 114 | }.bind(this)); 115 | break; 116 | } 117 | } 118 | } 119 | 120 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/createInvite.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/createInvite', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.createInvite(req, res, req.body) 11 | console.log("> created invite - " + req.body.server.id) 12 | }).bind(this)); 13 | } 14 | 15 | async createInvite(req, res, _invite) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, _invite.server.id); 19 | 20 | if(server === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if (server.author.id !== user.id) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } 27 | 28 | var invite = { 29 | id: this.app.crypto.randomBytes(16).toString("hex"), 30 | createdAt: Date.now(), 31 | server: { 32 | id: _invite.server.id 33 | }, 34 | author: { 35 | id: user.id 36 | } 37 | } 38 | 39 | server.invites.push(invite); 40 | server.members.forEach(async(id) => { 41 | this.app.epFunc.emitToUser(id, "updateServer", server); 42 | }); 43 | 44 | await this.app.db.db_add.addInvite(this.app.db, invite); 45 | res.send(JSON.stringify(invite)) 46 | } 47 | } 48 | 49 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/createServer.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/createServer', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.createServer(req, res, req.body) 11 | console.log("> created server - " + req.body.name) 12 | }).bind(this)); 13 | } 14 | 15 | async createServer(req, res, _server) { 16 | if(_server.name.length < 1) { 17 | res.send(JSON.stringify({ status: -1 })) 18 | return; 19 | } 20 | 21 | var socket = this.app.sessionSockets.get(req.cookies['sessionID']); 22 | var session = this.app.sessions.get(req.cookies['sessionID']); 23 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 24 | var server = { 25 | id: this.app.crypto.randomBytes(16).toString("hex"), 26 | name: _server.name, 27 | avatar: "defaultAvatar.png", 28 | createdAt: Date.now(), 29 | author: { 30 | id: user.id 31 | }, 32 | channels: [], 33 | members: [ user.id ], 34 | invites: [], 35 | emotes: [] 36 | } 37 | 38 | server.members.forEach(async(id) => { 39 | var user2 = await this.app.db.db_fetch.fetchUser(this.app.db, id); 40 | user2.servers.push(server.id); 41 | this.app.db.db_edit.editUser(this.app.db, user2); 42 | 43 | this.app.epFunc.emitToUser(user2.id, "createServer", server); 44 | this.app.epFunc.emitToUser(user2.id, "updateUser", user2); 45 | }); 46 | 47 | await this.app.db.db_add.addServer(this.app.db, server); 48 | res.send(JSON.stringify(server)) 49 | } 50 | } 51 | 52 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/declineFriendRequest.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/declineFriendRequest', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.app.epFunc.processFriendRequest(req, res, req.body, false) 11 | console.log("> declined friend request - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | } 15 | 16 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/deleteChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/deleteChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.deleteChannel(req, res, req.body) 11 | console.log("> deleted channel - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | 15 | async deleteChannel(req, res, _channel) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, _channel.id); 19 | 20 | if(channel === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if(channel.author.id !== user.id) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } 27 | 28 | switch(channel.type) { 29 | case 0: 30 | case 1: 31 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, channel.server.id); 32 | if(server.channels.includes(channel.id) === false) { 33 | res.send(JSON.stringify({ status: -3 })) 34 | return; 35 | } 36 | 37 | server.channels.splice(server.channels.indexOf(channel.id), 1) 38 | server.members.forEach(id => { 39 | this.app.epFunc.emitToUser(id, "deleteChannel", channel) 40 | }); 41 | 42 | await this.app.db.db_edit.editServer(this.app.db, server); 43 | break; 44 | 45 | case 2: 46 | channel.members = channel.members; 47 | channel.members.forEach(async(id) => { 48 | var user2 = await this.app.db.db_fetch.fetchUser(this.app.db, id); 49 | user2.dmChannels.splice(user2.dmChannels.indexOf(channel.id), 1); 50 | this.app.db.db_edit.editUser(this.app.db, user2); 51 | 52 | this.app.epFunc.emitToUser(user2.id, "deleteChannel", channel); 53 | this.app.epFunc.emitToUser(user2.id, "updateUser", user2); 54 | }); 55 | break; 56 | } 57 | 58 | res.sendStatus(200); 59 | await this.app.db.db_delete.deleteChannel(this.app.db, channel.id); 60 | } 61 | } 62 | 63 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/deleteEmote.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/deleteEmote', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.deleteEmote(req, res, req.body) 11 | console.log("> deleted emote - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | 15 | async deleteEmote(req, res, _emote) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var emote = await this.app.db.db_fetch.fetchEmote(this.app.db, _emote.id); 19 | 20 | if(emote === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if(emote.author.id !== user.id) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } 27 | 28 | switch(emote.type) { 29 | case 0: 30 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, emote.server.id); 31 | if(server.emotes.includes(emote.id) === false) { 32 | res.send(JSON.stringify({ status: -3 })) 33 | return; 34 | } 35 | 36 | server.emotes.splice(server.emotes.indexOf(emote.id), 1) 37 | server.members.forEach(id => { 38 | this.app.epFunc.emitToUser(id, "deleteEmote", emote) 39 | }); 40 | 41 | await this.app.db.db_edit.editServer(this.app.db, server); 42 | break; 43 | 44 | case 1: 45 | var user2 = await this.app.db.db_fetch.fetchUser(this.app.db, emote.author.id); 46 | user2.emotes.splice(user2.emotes.indexOf(emote.id), 1); 47 | this.app.db.db_edit.editUser(this.app.db, user2); 48 | 49 | this.app.epFunc.emitToUser(user2.id, "deleteEmote", emote); 50 | this.app.epFunc.emitToUser(user2.id, "updateUser", user2); 51 | break; 52 | } 53 | 54 | res.sendStatus(200); 55 | await this.app.db.db_delete.deleteEmote(this.app.db, emote.id); 56 | } 57 | } 58 | 59 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/deleteMessage.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/deleteMessage', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.deleteMessage(req, res, req.body) 11 | console.log("> deleted message - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | 15 | async deleteMessage(req, res, _message) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var message = await this.app.db.db_fetch.fetchMessage(this.app.db, _message.id); 19 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, message.channel.id); 20 | 21 | if(message === undefined) { 22 | res.send(JSON.stringify({ status: -1 })) 23 | return; 24 | } else if(message.author.id !== user.id) { 25 | res.send(JSON.stringify({ status: -2 })) 26 | return; 27 | } else { 28 | res.sendStatus(200); 29 | } 30 | 31 | switch(channel.type) { 32 | case 0: 33 | case 1: 34 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, channel.server.id); 35 | server.members.forEach(id => { 36 | this.app.epFunc.emitToUser(id, "deleteMessage", message) 37 | }); 38 | break; 39 | 40 | case 2: 41 | channel.members.forEach(async(id) => { 42 | this.app.epFunc.emitToUser(id, "deleteMessage", message); 43 | }); 44 | break; 45 | } 46 | 47 | await this.app.db.db_delete.deleteMessage(this.app.db, message.id); 48 | } 49 | } 50 | 51 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/deleteServer.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/deleteServer', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.deleteServer(req, res, req.body) 11 | console.log("> deleted server - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | 15 | async deleteServer(req, res, _server) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, _server.id); 19 | 20 | if(server === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if(server.author.id !== user.id) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } else { 27 | res.sendStatus(200); 28 | } 29 | 30 | server.members.forEach(async(id) => { 31 | var user2 = await this.app.db.db_fetch.fetchUser(this.app.db, id); 32 | user2.servers.splice(user2.servers.indexOf(server.id), 1); 33 | this.app.db.db_edit.editUser(this.app.db, user2); 34 | }); 35 | 36 | this.app.sessionSockets.forEach(socket => { 37 | if(socket.connected) { 38 | socket.emit("deleteServer", JSON.stringify(server)) 39 | } 40 | }) 41 | 42 | await this.app.db.db_delete.deleteServer(this.app.db, server.id); 43 | } 44 | } 45 | 46 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/editChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/editChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.editChannel(req, res, req.body) 11 | console.log("> edited channel - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | 15 | async editChannel(req, res, _channel) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, _channel.id); 19 | 20 | if(channel === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if(channel.author.id !== user.id) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } else if(_channel.name !== undefined && _channel.name.length < 1) { 27 | res.send(JSON.stringify({ status: -3 })) 28 | return; 29 | } 30 | 31 | //Make sure client doesn't overwrite something he's not allowed to 32 | channel.name = _channel.name !== undefined ? _channel.name : channel.name; 33 | channel.description = _channel.description !== undefined ? _channel.description : channel.description; 34 | channel.nsfw = _channel.nsfw !== undefined ? _channel.nsfw : channel.nsfw; 35 | 36 | switch(channel.type) { 37 | case 0: 38 | case 1: 39 | channel.position = _channel.position !== undefined ? _channel.position : channel.position; 40 | 41 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, channel.server.id); 42 | server.members.forEach(id => { 43 | this.app.epFunc.emitToUser(id, "updateChannel", channel) 44 | }); 45 | break; 46 | 47 | case 2: 48 | channel.members.forEach(async(id) => { 49 | this.app.epFunc.emitToUser(id, "updateChannel", channel) 50 | }); 51 | break; 52 | } 53 | 54 | await this.app.db.db_edit.editChannel(this.app.db, channel); 55 | res.send(JSON.stringify(channel)) 56 | } 57 | } 58 | 59 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/editMessage.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/editMessage', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.editMessage(req, res, req.body) 11 | console.log("> received message edit - " + req.body.text) 12 | }).bind(this)); 13 | } 14 | 15 | async editMessage(req, res, _message) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var message = await this.app.db.db_fetch.fetchMessage(this.app.db, _message.id); 19 | 20 | if(message === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if(message.author.id !== user.id) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } 27 | 28 | //Make sure client doesn't overwrite something he's not allowed to 29 | message.text = _message.text; 30 | message.edited = true; 31 | 32 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, message.channel.id); 33 | switch(channel.type) { 34 | case 0: 35 | case 1: 36 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, channel.server.id); 37 | server.members.forEach(id => { 38 | this.app.epFunc.emitToUser(id, "editMessage", message) 39 | }); 40 | break; 41 | 42 | case 2: 43 | channel.members.forEach(async(id) => { 44 | this.app.epFunc.emitToUser(id, "editMessage", message) 45 | }); 46 | break; 47 | } 48 | 49 | await this.app.db.db_edit.editMessage(this.app.db, message); 50 | res.send(JSON.stringify(message)) 51 | } 52 | } 53 | 54 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/editNote.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/editNote', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.app.epFunc.editNote(req, res, req.body); 11 | console.log("> received note update - " + req.body.text); 12 | }).bind(this)); 13 | } 14 | } 15 | 16 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/editServer.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/editServer', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.editServer(req, res, req.body); 11 | console.log("> received server update - " + req.body.id); 12 | }).bind(this)); 13 | } 14 | 15 | async editServer(req, res, _server) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, _server.id); 19 | 20 | if(server === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if(server.author.id !== user.id) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } else if(_server.name !== undefined && _server.name.length < 1) { 27 | res.send(JSON.stringify({ status: -3 })) 28 | return; 29 | } 30 | 31 | //Make sure client doesn't overwrite something he's not allowed to 32 | server.name = _server.name !== undefined ? _server.name : server.name; 33 | 34 | server.members.forEach(id => { 35 | this.app.epFunc.emitToUser(id, "updateServer", server) 36 | }); 37 | 38 | await this.app.db.db_edit.editServer(this.app.db, server); 39 | res.send(JSON.stringify(server)) 40 | } 41 | } 42 | 43 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/editUser.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/editUser', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.app.epFunc.editUser(req, res, req.body); 11 | console.log("> received user update - " + req.body.email); 12 | }).bind(this)); 13 | } 14 | } 15 | 16 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, data.id); 14 | if(channel === undefined) { 15 | res.send(JSON.stringify({ status: -1 })) 16 | } else { 17 | res.send(JSON.stringify(channel)) 18 | } 19 | }).bind(this)); 20 | } 21 | } 22 | 23 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchChannelMessages.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/fetchChannelMessages', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.body; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, data.id); 14 | var messages = await this.app.db.db_fetch.fetchMessages(this.app.db, data.id); 15 | if(channel === undefined) { 16 | res.send(JSON.stringify({ status: -1 })) 17 | } else if(channel.type === 2) { 18 | if(channel.members.includes(user.id) === false) { 19 | res.send(JSON.stringify({ status: -2 })) 20 | } else { 21 | messages = this.filterMessages(messages, data.filters); 22 | res.send(JSON.stringify(messages)); 23 | } 24 | } else { 25 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, channel.server.id); 26 | if(server.members.includes(user.id) === false) { 27 | res.send(JSON.stringify({ status: -2 })) 28 | } else { 29 | messages = this.filterMessages(messages, data.filters); 30 | res.send(JSON.stringify(messages)); 31 | } 32 | } 33 | }).bind(this)); 34 | } 35 | 36 | filterMessages(messages, filters) { 37 | if(filters !== undefined) { 38 | if(filters.term !== undefined) { 39 | messages = messages.filter(m => { return m.text.includes(filters.term); }) 40 | } 41 | } 42 | 43 | return messages; 44 | } 45 | } 46 | 47 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchChannels.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchChannels', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, data.id); 14 | var channels = await this.app.db.db_fetch.fetchChannels(this.app.db, data.id); 15 | if(server === undefined) { 16 | res.send(JSON.stringify({ status: -1 })) 17 | } else if(server.members.includes(user.id) === false) { 18 | res.send(JSON.stringify({ status: -2 })) 19 | } else { 20 | res.send(JSON.stringify(channels)); 21 | } 22 | }).bind(this)); 23 | } 24 | } 25 | 26 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchDMChannels.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchDMChannels', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var channels = []; 14 | for(var i = 0; i < user.dmChannels.length; i++) { 15 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, user.dmChannels[i]); 16 | channels.push(channel); 17 | } 18 | res.send(JSON.stringify(channels)); 19 | }).bind(this)); 20 | } 21 | } 22 | 23 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchDefaultEmotes.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchDefaultEmotes', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var emotes = await this.app.db.db_fetch.fetchDefaultEmotes(this.app.db, data.id); 14 | if(emotes === undefined) { 15 | res.send(JSON.stringify({ status: -1 })) 16 | } else { 17 | res.send(JSON.stringify(emotes)) 18 | } 19 | }).bind(this)); 20 | } 21 | } 22 | 23 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchEmote.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchEmote', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var emote = await this.app.db.db_fetch.fetchEmote(this.app.db, data.id); 14 | if(emote === undefined) { 15 | res.send(JSON.stringify({ status: -1 })) 16 | } else { 17 | res.send(JSON.stringify(emote)) 18 | } 19 | }).bind(this)); 20 | } 21 | } 22 | 23 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchFriendRequest.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchFriendRequest', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var friendRequest = await this.app.db.db_fetch.fetchFriendRequest(this.app.db, data.id); 14 | if(friendRequest === undefined) { 15 | res.send(JSON.stringify({ status: -1 })) 16 | } else if(friendRequest.author.id !== user.id && friendRequest.target.id !== user.id) { 17 | res.send(JSON.stringify({ status: -2 })) 18 | } else { 19 | res.send(JSON.stringify(friendRequest)); 20 | } 21 | }).bind(this)); 22 | } 23 | } 24 | 25 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchFriendRequests.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchFriendRequests', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var friendRequestsOut = await this.app.db.db_fetch.fetchFriendRequests(this.app.db, user.id, 0); 14 | var friendRequestsIn = await this.app.db.db_fetch.fetchFriendRequests(this.app.db, user.id, 1); 15 | var friendRequests = friendRequestsOut.concat(friendRequestsIn); 16 | res.send(JSON.stringify(friendRequests)); 17 | }).bind(this)); 18 | } 19 | } 20 | 21 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchInvite.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchInvite', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var invite = await this.app.db.db_fetch.fetchInvite(this.app.db, data.id); 14 | if(invite === undefined) { 15 | res.send(JSON.stringify({ status: -1 })) 16 | } else { 17 | res.send(JSON.stringify(invite)) 18 | } 19 | }).bind(this)); 20 | } 21 | } 22 | 23 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchNote.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchNote', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var note = await this.app.db.db_fetch.fetchNote(this.app.db, data.id); 14 | if(note === undefined) { 15 | res.send(JSON.stringify({ status: -1 })) 16 | } else if(note.author.id !== user.id) { 17 | res.send(JSON.stringify({ status: -2 })) 18 | } else { 19 | res.send(JSON.stringify(note)) 20 | } 21 | }).bind(this)); 22 | } 23 | } 24 | 25 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchNotes.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchNotes', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var notes = await this.app.db.db_fetch.fetchNotes(this.app.db, user.id); 14 | res.send(JSON.stringify(notes)); 15 | }).bind(this)); 16 | } 17 | } 18 | 19 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchServer.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchServer', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, data.id); 14 | if(server === undefined) { 15 | res.send(JSON.stringify({ status: -1 })) 16 | } else { 17 | res.send(JSON.stringify(server)) 18 | } 19 | }).bind(this)); 20 | } 21 | } 22 | 23 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchServers.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchServers', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | 13 | var servers = []; 14 | for(var i = 0; i < user.servers.length; i++) { 15 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, user.servers[i]); 16 | servers.push(server); 17 | } 18 | res.send(JSON.stringify(servers)); 19 | }).bind(this)); 20 | } 21 | } 22 | 23 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/fetchUser.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.get('/fetchUser', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | const data = req.query; 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, data.id, data.containSensitive && session.userID === data.id); 12 | 13 | if(user === undefined) { 14 | res.send(JSON.stringify({ status: -1 })) 15 | } else { 16 | res.send(JSON.stringify(user)) 17 | } 18 | }).bind(this)); 19 | } 20 | } 21 | 22 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/joinServer.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/joinServer', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.joinServer(req, res, req.body) 11 | console.log("> added to server - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | 15 | async joinServer(req, res, _data) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, _data.id); 19 | 20 | if(server === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if(server.members.includes(user.id) === true) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } else { 27 | res.sendStatus(200); 28 | } 29 | 30 | user.servers.push(server.id); 31 | this.app.epFunc.emitToUser(user.id, "createServer", server); 32 | this.app.epFunc.emitToUser(user.id, "updateUser", user); 33 | 34 | server.members.push(user.id); 35 | server.members.forEach(id => { 36 | this.app.epFunc.emitToUser(id, "updateServer", server) 37 | }); 38 | 39 | await this.app.db.db_edit.editServer(this.app.db, server); 40 | await this.app.db.db_edit.editUser(this.app.db, user); 41 | if(server.channels.length > 0) { this.app.epFunc.sendSystemMessage({ channel: { id: server.channels[0] }, text: "<@" + user.id + "> joined the server!", type: 3 }) } 42 | } 43 | } 44 | 45 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/joinVoiceChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/joinVoiceChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.joinVoiceChannel(req, res, req.body) 11 | console.log("> received voice connection - " + req.body.channel.id) 12 | }).bind(this)); 13 | } 14 | 15 | async joinVoiceChannel(req, res, connection) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, connection.channel.id); 19 | connection.author = { id: user.id } 20 | 21 | if(channel === undefined) { 22 | res.send(JSON.stringify({ status: -1 })) 23 | return; 24 | } 25 | 26 | var voiceGroup = -1; 27 | if(this.app.voiceGroups.has(channel.id) === false) { 28 | //Create Mediasoup Router 29 | const mediaCodecs = [ 30 | { 31 | kind : "audio", 32 | mimeType : "audio/opus", 33 | clockRate : 48000, 34 | channels : 2 35 | }, 36 | { 37 | kind : "video", 38 | mimeType : "video/H264", 39 | clockRate : 90000, 40 | parameters : 41 | { 42 | "packetization-mode" : 1, 43 | "profile-level-id" : "42e01f", 44 | "level-asymmetry-allowed" : 1 45 | } 46 | }]; 47 | 48 | let router = await this.app.mediaWorkers[0].createRouter({ mediaCodecs }); 49 | this.app.voiceGroupRouters.set(channel.id, router); 50 | this.app.voiceGroupTransports.set(channel.id, { consumer: new Map(), producer: new Map() }) 51 | 52 | //Create Voice Group 53 | voiceGroup = { 54 | id: channel.id, 55 | createdAt: Date.now(), 56 | author: { 57 | id: user.id 58 | }, 59 | users: [ user.id ], 60 | rtpCapabilities: router.rtpCapabilities 61 | } 62 | 63 | this.app.voiceGroups.set(channel.id, voiceGroup); 64 | } else { 65 | voiceGroup = this.app.voiceGroups.get(channel.id) 66 | if(voiceGroup.users.includes(user.id) === false) { 67 | voiceGroup.users.push(user.id); 68 | } 69 | } 70 | 71 | res.send(JSON.stringify(voiceGroup)); 72 | voiceGroup.users.forEach(id => { 73 | this.app.epFunc.emitToUser(id, "updateVoiceGroup", voiceGroup) 74 | }); 75 | } 76 | } 77 | 78 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/kickFromServer.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/kickFromServer', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.kickFromServer(req, res, req.body) 11 | console.log("> kicked from server - " + req.body.server.id + "/" + req.body.user.id) 12 | }).bind(this)); 13 | } 14 | 15 | async kickFromServer(req, res, _data) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, _data.server.id); 19 | var targetUser = await this.app.db.db_fetch.fetchUser(this.app.db, _data.user.id); 20 | 21 | if(server === undefined) { 22 | res.send(JSON.stringify({ status: -1 })) 23 | return; 24 | } else if(targetUser === undefined) { 25 | res.send(JSON.stringify({ status: -2 })) 26 | return; 27 | } else if(server.author.id !== user.id) { 28 | res.send(JSON.stringify({ status: -3 })) 29 | return; 30 | } else if(server.members.includes(user.id) === false) { 31 | res.send(JSON.stringify({ status: -4 })) 32 | return; 33 | } else { 34 | res.sendStatus(200); 35 | } 36 | 37 | server.members.splice(server.members.indexOf(targetUser.id), 1); 38 | server.members.forEach(id => { 39 | this.app.epFunc.emitToUser(id, "updateServer", server) 40 | }); 41 | 42 | targetUser.servers.splice(targetUser.servers.indexOf(server.id), 1); 43 | this.app.epFunc.emitToUser(targetUser.id, "deleteServer", server); 44 | 45 | await this.app.db.db_edit.editServer(this.app.db, server); 46 | await this.app.db.db_edit.editUser(this.app.db, targetUser); 47 | if(server.channels.length > 0) { this.app.epFunc.sendSystemMessage({ channel: { id: server.channels[0] }, text: "<@" + user.id + "> left the server!", type: 4 }) } 48 | } 49 | } 50 | 51 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/leaveServer.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/leaveServer', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.leaveServer(req, res, req.body) 11 | console.log("> removed from server - " + req.body.id) 12 | }).bind(this)); 13 | } 14 | 15 | async leaveServer(req, res, _data) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, _data.id); 19 | 20 | if(server === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else if(server.members.includes(user.id) === false) { 24 | res.send(JSON.stringify({ status: -2 })) 25 | return; 26 | } else { 27 | res.sendStatus(200); 28 | } 29 | 30 | server.members.splice(server.members.indexOf(user.id), 1); 31 | server.members.forEach(id => { 32 | this.app.epFunc.emitToUser(id, "updateServer", server) 33 | }); 34 | 35 | user.servers.splice(user.servers.indexOf(server.id), 1); 36 | this.app.epFunc.emitToUser(user.id, "deleteServer", server); 37 | 38 | await this.app.db.db_edit.editServer(this.app.db, server); 39 | await this.app.db.db_edit.editUser(this.app.db, user); 40 | if(server.channels.length > 0) { this.app.epFunc.sendSystemMessage({ channel: { id: server.channels[0] }, text: "<@" + user.id + "> left the server!", type: 4 }) } 41 | } 42 | } 43 | 44 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/leaveVoiceChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/leaveVoiceChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.leaveVoiceChannel(req, res, req.body) 11 | console.log("> received voice disconnection - " + req.body.channel.id) 12 | }).bind(this)); 13 | } 14 | 15 | async leaveVoiceChannel(req, res, connection) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, connection.channel.id); 19 | 20 | if(channel === undefined) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } 24 | 25 | var voiceGroup = -1; 26 | if(this.app.voiceGroups.has(channel.id) === false) { 27 | res.send(JSON.stringify({ status: -2 })) 28 | return; 29 | } else { 30 | voiceGroup = this.app.voiceGroups.get(channel.id) 31 | if(voiceGroup.users.includes(user.id) === false) { 32 | res.send(JSON.stringify({ status: -3 })) 33 | return; 34 | } 35 | 36 | voiceGroup.users = voiceGroup.users.filter(u => { return u.id !== user.id; }); 37 | } 38 | 39 | res.sendStatus(200); 40 | voiceGroup.users.forEach(id => { 41 | this.app.epFunc.emitToUser(id, "updateVoiceGroup", voiceGroup) 42 | }); 43 | } 44 | } 45 | 46 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/login.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/login', (async(req, res) => { 8 | const data = req.body; 9 | switch(data.authType) { 10 | case "autologin": 11 | if(!this.app.sessions.has(req.cookies["sessionID"])) { 12 | res.send(JSON.stringify({ status: -3 })) 13 | } else { 14 | res.send(JSON.stringify(this.app.sessions.get(req.cookies["sessionID"]))); 15 | } 16 | break; 17 | 18 | case "default": 19 | var user = await this.app.db.db_fetch.fetchUserByUsername(this.app.db, data.username, true, true); 20 | 21 | if(user === undefined) { 22 | res.send(JSON.stringify({ status: -2 })) 23 | } else if(this.app.bcrypt.compareSync(data.password, user.password.toString()) == false) { 24 | res.send(JSON.stringify({ status: -1 })) 25 | } else { 26 | const sessionID = this.app.crypto.randomBytes(16).toString("hex"); 27 | const session = { 28 | id: sessionID, 29 | userID: user.id 30 | } 31 | 32 | this.app.sessions.set(sessionID, session); 33 | if(this.app.userSessions.has(user.id)) { 34 | this.app.userSessions.get(user.id).push(sessionID); 35 | } else { 36 | this.app.userSessions.set(user.id, [ sessionID ]); 37 | } 38 | 39 | res.cookie("sessionID", session.id, { 40 | sameSite: "None", secure: true 41 | }); 42 | res.send(JSON.stringify(session)); 43 | } 44 | break; 45 | } 46 | }).bind(this)) 47 | } 48 | } 49 | 50 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/logout.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/logout', (async(req, res) => { 8 | res.clearCookie("sessionID"); 9 | res.sendStatus(200); 10 | }).bind(this)) 11 | } 12 | } 13 | 14 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/message.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/message', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.app.epFunc.sendMessage(req, res, req.body) 11 | console.log("> received message - " + req.body.text) 12 | }).bind(this)); 13 | } 14 | } 15 | 16 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/register.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/register', (async(req, res) => { 8 | const data = req.body; 9 | var user = await this.app.db.db_fetch.fetchUserByUsername(this.app.db, data.username); 10 | 11 | if(user !== undefined) { 12 | res.send(JSON.stringify({ status: -1 })) 13 | } else if(data.password !== data.password2) { 14 | res.send(JSON.stringify({ status: -2 })) 15 | } else if(data.username.length < 3) { 16 | res.send(JSON.stringify({ status: -3 })) 17 | } else if(data.password.length < 8) { 18 | res.send(JSON.stringify({ status: -4 })) 19 | } else { 20 | const userID = this.app.crypto.randomBytes(16).toString("hex"); 21 | const sessionID = this.app.crypto.randomBytes(16).toString("hex"); 22 | const passwordHash = this.app.bcrypt.hashSync(data.password, 10) 23 | 24 | const session = { 25 | id: sessionID, 26 | userID: userID 27 | } 28 | 29 | const user = { 30 | id: userID, 31 | username: data.username, 32 | createdAt: Date.now(), 33 | avatar: "defaultAvatar.png", 34 | password: passwordHash, 35 | friends: [], 36 | dmChannels: [], 37 | servers: [], 38 | badges: [], 39 | emotes: [], 40 | status: 0 41 | } 42 | 43 | await this.app.db.db_add.addUser(this.app.db, user); 44 | 45 | this.app.sessions.set(sessionID, session); 46 | if(this.app.userSessions.has(user.id)) { 47 | this.app.userSessions.get(user.id).push(sessionID); 48 | } else { 49 | this.app.userSessions.set(user.id, [ sessionID ]); 50 | } 51 | 52 | res.cookie("sessionID", session.id, { 53 | sameSite: "None", secure: true 54 | }); 55 | res.send(JSON.stringify(session)); 56 | } 57 | }).bind(this)) 58 | } 59 | } 60 | 61 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/removeConnection.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/removeConnection', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | var session = this.app.sessions.get(req.cookies['sessionID']); 11 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 12 | this.app.db.db_edit.removeUserConnection(this.app.db, user, req.body.type); 13 | 14 | var newUser = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 15 | this.app.epFunc.emitToUser(newUser.id, "updateUser", newUser); 16 | }).bind(this)); 17 | } 18 | } 19 | 20 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/removeFriend.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/removeFriend', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.removeFriend(req, res, req.body) 11 | console.log("> removed friend - " + req.body.target.id) 12 | }).bind(this)); 13 | } 14 | 15 | async removeFriend(req, res, _removalRequest) { 16 | var socket = this.app.sessionSockets.get(req.cookies['sessionID']); 17 | var session = this.app.sessions.get(req.cookies['sessionID']); 18 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 19 | 20 | if(user.friends.includes(_removalRequest.target.id) === false) { 21 | res.send(JSON.stringify({ status: -1 })) 22 | return; 23 | } else { 24 | res.sendStatus(200); 25 | } 26 | 27 | var targetUser = await this.app.db.db_fetch.fetchUser(this.app.db, _removalRequest.target.id); 28 | user.friends.splice(user.friends.indexOf(targetUser.id), 1); 29 | targetUser.friends.splice(targetUser.friends.indexOf(user.id), 1); 30 | 31 | await this.app.db.db_edit.editUser(this.app.db, user); 32 | await this.app.db.db_edit.editUser(this.app.db, targetUser); 33 | 34 | if(socket.connected) { 35 | socket.emit("updateUser", JSON.stringify(user)) 36 | this.app.epFunc.emitToUser(targetUser.id, "updateUser", targetUser); 37 | } 38 | } 39 | } 40 | 41 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/removeFromDMChannel.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/removeFromDMChannel', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.removeFromDMChannel(req, res, req.body) 11 | console.log("> removed from dm channel - " + req.body.channel.id + "/" + req.body.user.id) 12 | }).bind(this)); 13 | } 14 | 15 | async removeFromDMChannel(req, res, _data) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, _data.channel.id); 19 | var targetUser = await this.app.db.db_fetch.fetchUser(this.app.db, _data.user.id); 20 | 21 | if(channel === undefined) { 22 | res.send(JSON.stringify({ status: -1 })) 23 | return; 24 | } else if(targetUser === undefined) { 25 | res.send(JSON.stringify({ status: -2 })) 26 | return; 27 | } else if(channel.author.id !== user.id) { 28 | res.send(JSON.stringify({ status: -3 })) 29 | return; 30 | } else if(channel.members.includes(targetUser.id) === false) { 31 | res.send(JSON.stringify({ status: -4 })) 32 | return; 33 | } else { 34 | res.sendStatus(200); 35 | } 36 | 37 | channel.members.splice(channel.members.indexOf(targetUser.id), 1); 38 | channel.members.forEach(id => { 39 | this.app.epFunc.emitToUser(id, "updateChannel", channel) 40 | }); 41 | 42 | targetUser.dmChannels.splice(targetUser.dmChannels.indexOf(channel.id), 1); 43 | this.app.epFunc.emitToUser(targetUser.id, "deleteChannel", channel); 44 | 45 | await this.app.db.db_edit.editChannel(this.app.db, channel); 46 | await this.app.db.db_edit.editUser(this.app.db, targetUser); 47 | this.app.epFunc.sendSystemMessage({ channel: { id: channel.id }, text: "<@" + targetUser.id + "> was removed by <@" + user.id + ">", type: 2 }) 48 | } 49 | } 50 | 51 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/sendFriendRequest.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/sendFriendRequest', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.sendFriendRequest(req, res, req.body) 11 | console.log("> sent friend request - " + req.body.target.username + " (id: " + req.body.target.id + ")") 12 | }).bind(this)); 13 | } 14 | 15 | async sendFriendRequest(req, res, _friendRequest) { 16 | var socket = this.app.sessionSockets.get(req.cookies['sessionID']); 17 | var session = this.app.sessions.get(req.cookies['sessionID']); 18 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 19 | 20 | var targetUser = -1; 21 | if(_friendRequest.target.id !== undefined) { 22 | targetUser = await this.app.db.db_fetch.fetchUser(this.app.db, _friendRequest.target.id); 23 | } else { 24 | targetUser = await this.app.db.db_fetch.fetchUserByUsername(this.app.db, _friendRequest.target.username); 25 | } 26 | 27 | if(targetUser === undefined) { 28 | res.send(JSON.stringify({ status: -2 })) 29 | return; 30 | } 31 | 32 | var friendRequest = await this.app.db.db_fetch.fetchFriendRequestByTarget(this.app.db, targetUser.id); 33 | if(friendRequest !== undefined) { 34 | res.send(JSON.stringify({ status: -1 })) 35 | return; 36 | } else if(user.id === targetUser.id) { 37 | res.send(JSON.stringify({ status: -3 })) 38 | return; 39 | } else { 40 | res.sendStatus(200); 41 | } 42 | 43 | var friendRequest = { 44 | id: this.app.crypto.randomBytes(16).toString("hex"), 45 | author: { 46 | id: user.id 47 | }, 48 | target: { 49 | id: targetUser.id 50 | }, 51 | createdAt: Date.now() 52 | } 53 | 54 | await this.app.db.db_add.addFriendRequest(this.app.db, friendRequest); 55 | 56 | if(socket.connected) { 57 | var friendRequestsOut = await this.app.db.db_fetch.fetchFriendRequests(this.app.db, user.id, 0); 58 | var friendRequestsIn = await this.app.db.db_fetch.fetchFriendRequests(this.app.db, user.id, 1); 59 | var friendRequests = friendRequestsOut.concat(friendRequestsIn); 60 | 61 | socket.emit("updateFriendRequests", JSON.stringify(friendRequests)) 62 | 63 | friendRequestsOut = await this.app.db.db_fetch.fetchFriendRequests(this.app.db, targetUser.id, 0); 64 | friendRequestsIn = await this.app.db.db_fetch.fetchFriendRequests(this.app.db, targetUser.id, 1); 65 | friendRequests = friendRequestsOut.concat(friendRequestsIn); 66 | 67 | this.app.epFunc.emitToUser(targetUser.id, "updateUser", targetUser); 68 | this.app.epFunc.emitToUser(targetUser.id, "updateFriendRequests", friendRequests); 69 | } 70 | } 71 | } 72 | 73 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/updateAvatar.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/updateAvatar', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.updateAvatar(req, res); 11 | console.log("> received avatar update - " + req.query.fileName); 12 | }).bind(this)); 13 | } 14 | 15 | async updateAvatar(req, res) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var form = this.app.formidable({ multiples: true }); 19 | form.uploadDir = this.app.filesStorage; 20 | form.keepExtensions = true; 21 | 22 | var fileName = req.query.fileName; 23 | var fileID = this.app.crypto.randomBytes(16).toString("hex"); 24 | var fileID2 = fileID + (fileName.substring(fileName.lastIndexOf("."))) 25 | console.log("> received avatar - " + fileName) 26 | 27 | form.parse(req, async function(err, fields, files) { 28 | this.app.fs.rename(files.fileUploaded.path, this.app.filesStorage + fileID2, function(err) { 29 | if (err) { throw err; } 30 | }); 31 | 32 | user.avatar = fileID2 33 | await this.app.epFunc.updateUser(user, true) 34 | res.sendStatus(200); 35 | }.bind(this)); 36 | } 37 | } 38 | 39 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/updateServerAvatar.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/updateServerAvatar', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.updateServerAvatar(req, res, req.query.serverID); 11 | console.log("> received server avatar update - " + req.query.serverID); 12 | }).bind(this)); 13 | } 14 | 15 | async updateServerAvatar(req, res, serverID) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var form = this.app.formidable({ multiples: true }); 19 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, serverID); 20 | 21 | if(server === undefined) { 22 | res.send(JSON.stringify({ status: -1 })) 23 | return; 24 | } else if (server.author.id !== user.id) { 25 | res.send(JSON.stringify({ status: -2 })) 26 | return; 27 | } 28 | 29 | form.uploadDir = this.app.filesStorage; 30 | form.keepExtensions = true; 31 | 32 | var fileName = req.query.fileName; 33 | var fileID = this.app.crypto.randomBytes(16).toString("hex"); 34 | var fileID2 = fileID + (fileName.substring(fileName.lastIndexOf("."))) 35 | console.log("> received avatar - " + fileName) 36 | 37 | form.parse(req, async function(err, fields, files) { 38 | this.app.fs.rename(files.fileUploaded.path, this.app.filesStorage + fileID2, function(err) { 39 | if (err) { throw err; } 40 | }); 41 | 42 | server.avatar = fileID2 43 | await this.app.epFunc.updateServer(server, true) 44 | res.sendStatus(200); 45 | }.bind(this)); 46 | } 47 | } 48 | 49 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/updateServerBanner.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/updateServerBanner', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.updateServerBanner(req, res, req.query.serverID); 11 | console.log("> received server banner update - " + req.query.serverID); 12 | }).bind(this)); 13 | } 14 | 15 | async updateServerBanner(req, res, serverID) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var form = this.app.formidable({ multiples: true }); 19 | var server = await this.app.db.db_fetch.fetchServer(this.app.db, serverID); 20 | 21 | if(server === undefined) { 22 | res.send(JSON.stringify({ status: -1 })) 23 | return; 24 | } else if (server.author.id !== user.id) { 25 | res.send(JSON.stringify({ status: -2 })) 26 | return; 27 | } 28 | 29 | form.uploadDir = this.app.filesStorage; 30 | form.keepExtensions = true; 31 | 32 | var fileName = req.query.fileName; 33 | var fileID = this.app.crypto.randomBytes(16).toString("hex"); 34 | var fileID2 = fileID + (fileName.substring(fileName.lastIndexOf("."))) 35 | console.log("> received banner - " + fileName) 36 | 37 | form.parse(req, async function(err, fields, files) { 38 | this.app.fs.rename(files.fileUploaded.path, this.app.filesStorage + fileID2, function(err) { 39 | if (err) { throw err; } 40 | }); 41 | 42 | server.banner = fileID2 43 | await this.app.epFunc.updateServer(server, true) 44 | res.sendStatus(200); 45 | }.bind(this)); 46 | } 47 | } 48 | 49 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/upload.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/upload', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.uploadFile(req, res) 11 | console.log("> received file - " + req.query.fileName) 12 | }).bind(this)) 13 | } 14 | 15 | async uploadFile(req, res) { 16 | var socket = this.app.sessionSockets.get(req.cookies['sessionID']); 17 | var form = this.app.formidable({ multiples: true, maxFileSize: 1024 * 1024 * 100 }); 18 | form.uploadDir = this.app.filesStorage; 19 | form.keepExtensions = true; 20 | 21 | var fileName = req.query.fileName; 22 | var fileSize = -1; 23 | var sentStartPacket = false; 24 | 25 | var fileID = this.app.crypto.randomBytes(16).toString("hex"); 26 | var fileID2 = fileID + (fileName.substring(fileName.lastIndexOf("."))) 27 | console.log("> received file - " + fileName) 28 | 29 | form.on('progress', function(bytesReceived, bytesExpected) { 30 | if(!sentStartPacket) { 31 | sentStartPacket = true; 32 | fileSize = bytesExpected; 33 | socket.emit("uploadStart", fileID, fileName); 34 | 35 | //File size check 36 | if (fileSize >= (1024 * 1024 * 100)) { 37 | form.emit('error', new Error(`File is too big-`)); 38 | socket.emit("uploadFail", fileID, fileName, fileSize); 39 | return; 40 | } 41 | } 42 | 43 | //File size check 44 | if (fileSize >= (1024 * 1024 * 100)) { 45 | return false; 46 | } else { 47 | socket.emit("uploadProgress", fileID, fileName, bytesReceived, bytesExpected); 48 | } 49 | }.bind(this)); 50 | 51 | form.parse(req, async function(err, fields, files) { 52 | if(files.fileUploaded === undefined) { return; } 53 | this.app.fs.rename(files.fileUploaded.path, this.app.filesStorage + fileID2, function(err) { 54 | if (err) { throw err; } 55 | }); 56 | 57 | socket.emit("uploadFinish", fileID, fileName); 58 | 59 | var message = { 60 | text: fields.text, 61 | channel: { 62 | id: fields["channel.id"] 63 | }, 64 | file: { 65 | name: fileID2, 66 | size: fileSize 67 | } 68 | } 69 | 70 | await this.app.epFunc.sendMessage(req, res, message) 71 | }.bind(this)); 72 | } 73 | } 74 | 75 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/voiceTransports_connect.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/connectVoiceTransports', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.connectVoiceTransports(req, res, req.body) 11 | console.log("> received voice transport connection - " + req.body.channel.id); 12 | }).bind(this)); 13 | } 14 | 15 | async connectVoiceTransports(req, res, connection) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, connection.channel.id); 19 | 20 | var transports = this.app.voiceGroupTransports.get(channel.id); 21 | var consumer = transports.consumer.get(user.id); 22 | var producer = transports.producer.get(user.id); 23 | consumer.connect({ dtlsParameters: connection.consumerDTLS }); 24 | producer.connect({ dtlsParameters: connection.producerDTLS }); 25 | 26 | res.sendStatus(200); 27 | } 28 | } 29 | 30 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/voiceTransports_consume.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/consumeVoiceTransports', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.consumeVoiceTransports(req, res, req.body) 11 | console.log("> received voice transport consume - " + req.body.channel.id); 12 | }).bind(this)); 13 | } 14 | 15 | async consumeVoiceTransports(req, res, connection) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, connection.channel.id); 19 | 20 | var transports = this.app.voiceGroupTransports.get(channel.id); 21 | var consumer = transports.consumer.get(user.id); 22 | var a = await consumer.consume({ producerId: connection.producerID, rtpCapabilities: connection.rtpCapabilities }); 23 | var b = { 24 | producerID: connection.producerID, 25 | id: a.id, 26 | kind: a.kind, 27 | rtpParameters: a.rtpParameters, 28 | type: a.type, 29 | producerPaused: a.producerPaused 30 | } 31 | 32 | res.send(JSON.stringify(b)); 33 | } 34 | } 35 | 36 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/voiceTransports_create.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/createVoiceTransports', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.createVoiceTransports(req, res, req.body) 11 | console.log("> received voice transport creation - " + req.body.channel.id); 12 | }).bind(this)); 13 | } 14 | 15 | async createVoiceTransports(req, res, connection) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, connection.channel.id); 19 | 20 | var consumer = await this.app.mediaFunc.createMediaTransport(channel.id); 21 | var producer = await this.app.mediaFunc.createMediaTransport(channel.id); 22 | var transports = this.app.voiceGroupTransports.get(channel.id); 23 | transports.consumer.set(user.id, consumer); 24 | transports.producer.set(user.id, producer); 25 | 26 | res.send(JSON.stringify({ 27 | consumerData: { 28 | id: consumer.id, 29 | iceParameters: consumer.iceParameters, 30 | iceCandidates: consumer.iceCandidates, 31 | dtlsParameters: consumer.dtlsParameters 32 | }, 33 | producerData: { 34 | id: producer.id, 35 | iceParameters: producer.iceParameters, 36 | iceCandidates: producer.iceCandidates, 37 | dtlsParameters: producer.dtlsParameters 38 | } 39 | })); 40 | } 41 | } 42 | 43 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/endpoints/voiceTransports_produce.js: -------------------------------------------------------------------------------- 1 | class Endpoint { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | handle() { 7 | this.app.post('/produceVoiceTransports', (async(req, res) => { 8 | if(!this.app.isSessionValid(this.app, req, res)) { return; } 9 | 10 | await this.produceVoiceTransports(req, res, req.body) 11 | console.log("> received voice transport produce - " + req.body.channel.id); 12 | }).bind(this)); 13 | } 14 | 15 | async produceVoiceTransports(req, res, connection) { 16 | var session = this.app.sessions.get(req.cookies['sessionID']); 17 | var user = await this.app.db.db_fetch.fetchUser(this.app.db, session.userID); 18 | var channel = await this.app.db.db_fetch.fetchChannel(this.app.db, connection.channel.id); 19 | var voiceGroup = this.app.voiceGroups.get(channel.id); 20 | 21 | var transports = this.app.voiceGroupTransports.get(channel.id); 22 | var producer = transports.producer.get(user.id); 23 | var a = await producer.produce({ kind: connection.kind, rtpParameters : connection.rtpParameters }); 24 | var b = { channel: { id: channel.id }, id: a.id }; 25 | 26 | res.send(JSON.stringify(b)); 27 | voiceGroup.users.forEach(id => { 28 | this.app.epFunc.emitToUser(id, "newProducer", b) 29 | }); 30 | } 31 | } 32 | 33 | module.exports = Endpoint; -------------------------------------------------------------------------------- /server/scripts/utils/mediaFunc.js: -------------------------------------------------------------------------------- 1 | class Endpoint2 { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | async createMediaWorker() { 7 | var worker = await this.app.mediasoup.createWorker(); 8 | worker.on('died', () => { 9 | console.error('[MEDIASOUP] Media worker(pid: ' + worker.pid + ') died!'); 10 | }); 11 | 12 | this.app.mediaWorkers.push(worker); 13 | return worker; 14 | } 15 | 16 | async createMediaTransport(channelID) { 17 | var router = this.app.voiceGroupRouters.get(channelID); 18 | var transport = await router.createWebRtcTransport({ 19 | listenIps: [ { ip: "::", announcedIp: "35.189.74.206" } ] 20 | }); 21 | 22 | return transport; 23 | } 24 | } 25 | 26 | module.exports = Endpoint2; -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | //Import modules 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | //Setup server 6 | const Util = require("./scripts/util") 7 | const util = new Util(app, express) 8 | util.setupApp() 9 | util.setupRoutes() 10 | util.setupSocketServer() --------------------------------------------------------------------------------