Initialize project
This commit is contained in:
parent
0655175a62
commit
37392f6a31
2 changed files with 350 additions and 0 deletions
330
index.js
Normal file
330
index.js
Normal file
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
Unofficial brainlet interface functions
|
||||
Copyright (C) 2020 hiimgoodpack
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const validator = require("validator");
|
||||
const urllib = require("urllib");
|
||||
const WebSocket = require("ws");
|
||||
|
||||
const reportBug = `
|
||||
|
||||
You've also discovered something that shouldn't occur.
|
||||
You can report it at https://git.hippoz.xyz/hiimgoodpack/brainlet-lib/issues`;
|
||||
|
||||
const rateLimited = "You are being rate limited";
|
||||
module.exports = {};
|
||||
|
||||
// I didn't make the rules. This is what the server requires
|
||||
module.exports.valid = {
|
||||
username: (name) => {
|
||||
if (validator.isEmpty(name))
|
||||
return "Empty username";
|
||||
if (!validator.isLength(name.trim(), { min: 3, max: 32 }))
|
||||
return "Username length must be between 3 and 32 characters";
|
||||
if (!validator.isAlphanumeric(name))
|
||||
return "Username must use alphanumeric characters";
|
||||
},
|
||||
|
||||
email: (email) => {
|
||||
if (validator.isEmpty(email))
|
||||
return "Empty email";
|
||||
if (!validator.isEmail(email))
|
||||
return "Invalid email";
|
||||
},
|
||||
|
||||
password: (password) => {
|
||||
if (validator.isEmpty(password))
|
||||
return "Empty password";
|
||||
if (!validator.isLength(password, { min: 8, max: 128 }))
|
||||
return "Password length must be between 8 and 128 characters";
|
||||
},
|
||||
|
||||
category: (category) => {
|
||||
if (validator.isEmpty(category))
|
||||
return "Empty category name";
|
||||
if (!validator.isLength(category.trim(), { min: 3, max: 32 }))
|
||||
return "Category name must be between 3 and 32 characters";
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
User format:
|
||||
{
|
||||
name;
|
||||
email;
|
||||
password;
|
||||
token;
|
||||
|
||||
permission;
|
||||
role;
|
||||
color;
|
||||
_id;
|
||||
}
|
||||
*/
|
||||
// WARNING: You should trust the server being used!
|
||||
module.exports.users = {
|
||||
create: (server, user) => {
|
||||
return new Promise((res, rej) => {
|
||||
const validationError =
|
||||
module.exports.valid.username(user.name)
|
||||
|| module.exports.valid.email(user.email)
|
||||
|| module.exports.valid.password(user.password);
|
||||
if (validationError) {
|
||||
rej(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
const api = `http://${server}/api/v1/users/account/create`;
|
||||
urllib.request(api, {
|
||||
method: "POST",
|
||||
data: {
|
||||
username: user.name,
|
||||
email: user.email,
|
||||
password: user.password
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result.res.statusCode === 200)
|
||||
res();
|
||||
else {
|
||||
const rawData = result.res.data.toString();
|
||||
if (rawData === ratelimited) {
|
||||
rej(`Error when creating account: ${rateLimited}`);
|
||||
return;
|
||||
}
|
||||
const error = JSON.parse(rawData);
|
||||
rej(`Error when creating account: ${error.errors[0].msg} (${error.message})`);
|
||||
}
|
||||
}).catch((error) => {
|
||||
rej(`Error when sending request to ${api}: ${error.code}`);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
login: (server, user) => {
|
||||
return new Promise((res, rej) => {
|
||||
const validationError =
|
||||
module.exports.valid.username(user.name)
|
||||
|| module.exports.valid.password(user.password);
|
||||
if (validationError) {
|
||||
rej(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
const api = `http://${server}/api/v1/users/token/create`;
|
||||
urllib.request(api, {
|
||||
method: "POST",
|
||||
data: {
|
||||
username: user.name,
|
||||
password: user.password
|
||||
}
|
||||
}).then((result) => {
|
||||
let newUser = Object.assign({}, user);
|
||||
const rawData = result.res.data.toString();
|
||||
if (rawData === rateLimited) {
|
||||
rej(`Error when logging into account: ${rateLimited}`);
|
||||
return;
|
||||
}
|
||||
const data = JSON.parse(rawData);
|
||||
if (result.res.statusCode !== 200) {
|
||||
rej(`Error when logging into account: ${data.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
newUser.token = data.token;
|
||||
newUser.permission = data.user.permissionLevel;
|
||||
newUser.role = data.user.role;
|
||||
newUser.color = data.user.color;
|
||||
newUser._id = data.user._id;
|
||||
|
||||
res(newUser);
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
rej(`Error when sending request to ${api}: ${error.code}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.categories = {
|
||||
create: (server, token, name) => {
|
||||
return new Promise((res, rej) => {
|
||||
const validationError = module.exports.valid.category(name);
|
||||
|
||||
const api = `http://${server}/api/v1/content/category/create`;
|
||||
urllib.request(api, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Cookie: `token=${token}`
|
||||
},
|
||||
data: {
|
||||
title: name
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result.res.statusCode === 200)
|
||||
res();
|
||||
else {
|
||||
const rawData = result.res.data.toString();
|
||||
if (rawData === rateLimited) {
|
||||
rej(`Error when creating category: ${rateLimited}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = JSON.parse(rawData);
|
||||
rej(`Error when creating category: ${data.message}`);
|
||||
}
|
||||
}).catch((error) => {
|
||||
rej(`Error when sending request to ${api}: ${error.code}`);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
list: (server, token, limit) => {
|
||||
return new Promise((res, rej) => {
|
||||
const api = `http://${server}/api/v1/content/category/list?count=${limit}`
|
||||
urllib.request(api, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Cookie: `token=${token}`
|
||||
}
|
||||
}).then((result) => {
|
||||
const rawData = result.res.data.toString();
|
||||
if (rawData === rateLimited) {
|
||||
rej(`Error while listing categories: ${rateLimited}`);
|
||||
return;
|
||||
}
|
||||
const data = JSON.parse(rawData);
|
||||
if (result.res.statusCode !== 200) {
|
||||
rej(`Error while listing categories: ${data.message}`);
|
||||
return;
|
||||
}
|
||||
res(data.categories);
|
||||
}).catch((error) => {
|
||||
rej(`Error while sending request to ${api}: ${error.code}`);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
client: (server, token) => {
|
||||
const socketURL = `ws://${server}/socket.io/?token=${token}&transport=websocket`;
|
||||
return new Promise((res, rej) => {
|
||||
const socket = new WebSocket(socketURL);
|
||||
|
||||
let categories = {};
|
||||
let messageCallbacks = [];
|
||||
let authenticated = false;
|
||||
|
||||
let sharedData = {
|
||||
connect: (categoriesID) => {
|
||||
return new Promise((res, rej) => {
|
||||
for (const categoryID of categoriesID) {
|
||||
categories[categoryID] = {
|
||||
updateCallback: res
|
||||
}
|
||||
}
|
||||
const data = [
|
||||
"subscribe",
|
||||
categoriesID
|
||||
];
|
||||
|
||||
socket.send(`42/gateway,${JSON.stringify(data)}`);
|
||||
});
|
||||
},
|
||||
|
||||
send: (categoryID, message) => {
|
||||
const data = [
|
||||
"message",
|
||||
{
|
||||
category: {
|
||||
_id: categoryID
|
||||
},
|
||||
content: message
|
||||
}
|
||||
];
|
||||
|
||||
socket.send(`42/gateway,${JSON.stringify(data)}`);
|
||||
},
|
||||
|
||||
onMessage: (callback) => {
|
||||
messageCallbacks[messageCallbacks.length] = callback;
|
||||
}
|
||||
};
|
||||
|
||||
socket.on("message", (rawMessage) => {
|
||||
const type = parseInt(rawMessage);
|
||||
const contents = rawMessage.substr(type.toString().length);
|
||||
|
||||
switch (type) {
|
||||
// Authentication
|
||||
case 0: {
|
||||
socket.send("40/gateway,");
|
||||
if (authenticated)
|
||||
console.error(`The server unexpectedly sent message type 0 multiple times${reportBug}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case 42: {
|
||||
const data = JSON.parse(contents.substr(("/gateway,").length));
|
||||
const dataType = data[0];
|
||||
|
||||
switch (dataType) {
|
||||
case "hello": {
|
||||
socket.send("42/gateway,[\"yoo\"]");
|
||||
if (authenticated)
|
||||
console.error(`The server unexpectedly sent hello through message type 42 multiple times${reportBug}`);
|
||||
else {
|
||||
authenticated = true;
|
||||
res(sharedData);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "clientListUpdate": {
|
||||
const categoryID = data[1].category._id;
|
||||
const callback = categories[categoryID].updateCallback;
|
||||
if (callback) {
|
||||
categories[categoryID].updateCallback = null;
|
||||
callback(categoryID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "message": {
|
||||
for (const callback in messageCallbacks) {
|
||||
messageCallbacks[callback](data[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Ping
|
||||
case 2: {
|
||||
socket.send("3");
|
||||
break;
|
||||
}
|
||||
|
||||
// Things I don't care about
|
||||
// More authentication messages
|
||||
case 40: break;
|
||||
|
||||
default: {
|
||||
console.error(`Got sent unknown type ${type}\nMessage contents: ${rawMessage}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
20
package.json
Normal file
20
package.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "brainlet-lib",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.hippoz.xyz/hiimgoodpack/brainlet-lib.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"urllib": "^2.36.1",
|
||||
"validator": "^13.5.2",
|
||||
"ws": "^7.4.2"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue