mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2026-01-09 17:04:30 +08:00
Merge branch 'master' of https://github.com/Miodec/monkey-type
This commit is contained in:
commit
6cf76e28d6
9 changed files with 93 additions and 712 deletions
|
|
@ -1,110 +0,0 @@
|
|||
async function botAuth(req, res, next) {
|
||||
const authHeader = req.headers["authorization"];
|
||||
const token = await admin
|
||||
.auth()
|
||||
.verifyIdToken(req.headers.authorization.split(" ")[1]);
|
||||
if (token.isDiscordBot == null || token.isDiscordBot == false) {
|
||||
return res.sendStatus(401);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
app.get("/getBananas/:discordId", botAuth, (req, res) => {
|
||||
User.findOne({ discordId: req.params.discordId }, (err, user) => {
|
||||
if (user) {
|
||||
res.send({ t60bananas: user.bananas.t60bananas });
|
||||
} else {
|
||||
res.send({ t60bananas: 0, message: "User not found" });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/getUserDiscordData/:uid", botAuth, (req, res) => {
|
||||
//for announceDailyLbResult
|
||||
User.findOne({ uid: req.params.uid }, (err, user) => {
|
||||
res.send({ name: user.name, discordId: user.discordId });
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/getUserPbs/:discordId", botAuth, (req, res) => {
|
||||
//for fix wpm role
|
||||
User.findOne({ discordId: req.params.discordId }, (err, user) => {
|
||||
if (user) {
|
||||
res.send({ personalBests: user.personalBests });
|
||||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/getUserPbsByUid/:uid", botAuth, (req, res) => {
|
||||
//for verify
|
||||
User.findOne({ uid: req.params.uid }, (err, user) => {
|
||||
if (user) {
|
||||
res.send({ personalBests: user.personalBests });
|
||||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/getTimeLeaderboard/:mode2/:type", botAuth, (req, res) => {
|
||||
//for lb
|
||||
Leaderboard.findOne({
|
||||
mode: "time",
|
||||
mode2: req.params.mode2,
|
||||
type: req.params.type,
|
||||
}).then((err, lb) => {
|
||||
//get top 10 leaderboard
|
||||
lb.board.length = 10;
|
||||
res.send({ board: lb.board });
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/getUserByDiscordId/:discordId", botAuth, (req, res) => {
|
||||
//for lb
|
||||
User.findOne({ discordId: req.params.discordId }, (err, user) => {
|
||||
if (user) {
|
||||
res.send({ uid: user.uid });
|
||||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/getRecentScore/:discordId", botAuth, (req, res) => {
|
||||
User.findOne({ discordId: req.params.discordId }, (err, user) => {
|
||||
if (user) {
|
||||
if (user.results.length == 0) {
|
||||
res.send({ recentScore: -1 });
|
||||
} else {
|
||||
res.send({ recentScore: user.results[user.results.length - 1] });
|
||||
}
|
||||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/getUserStats/:discordId", botAuth, (req, res) => {
|
||||
//for stats
|
||||
User.findOne({ discordId: req.params.discordId }, (err, user) => {
|
||||
if (user) {
|
||||
res.send({ stats: user.globalStats });
|
||||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/newBotCommand", botAuth, (req, res) => {
|
||||
let newBotCommand = new BotCommand({
|
||||
command: req.body.command, //is always "updateRole"
|
||||
arguments: req.body.arguments,
|
||||
executed: req.body.executed, //is always false
|
||||
requestTimestamp: req.body.requestTimestamp,
|
||||
});
|
||||
newBotCommand.save();
|
||||
res.status(200);
|
||||
});
|
||||
|
|
@ -1,443 +0,0 @@
|
|||
const { config } = require("dotenv");
|
||||
const path = require("path");
|
||||
config({ path: path.join(__dirname, ".env") });
|
||||
const { mongoDB } = require("./init/mongodb");
|
||||
const { connectDB } = require("./init/mongodb");
|
||||
const { ObjectID } = require("mongodb");
|
||||
const { performance } = require("perf_hooks");
|
||||
const fs = require("fs");
|
||||
|
||||
// const { QuerySnapshotData } = require("firebase-firestore");
|
||||
|
||||
console.log(config());
|
||||
|
||||
const admin = require("firebase-admin");
|
||||
|
||||
// const { User } = require("./models/user");
|
||||
// const { Leaderboard } = require("./models/leaderboard");
|
||||
// const { BotCommand } = require("./models/bot-command");
|
||||
|
||||
const serviceAccount = require("./credentials/serviceAccountKey.json");
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount),
|
||||
});
|
||||
|
||||
var db = admin.firestore();
|
||||
var auth = admin.auth();
|
||||
|
||||
process.on("SIGTERM", async () => {
|
||||
console.info("SIGTERM signal received. Stopping after this user is done");
|
||||
await currentUserPromise;
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on("exit", async () => {
|
||||
console.info("exit signal received. Stopping after this user is done");
|
||||
await currentUserPromise;
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on("SIGINT", async () => {
|
||||
console.info("SIGINT signal received. Stopping after this user is done");
|
||||
await currentUserPromise;
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Database should be completely clear before this is ran in order to prevent overlapping documents
|
||||
// Migrate users
|
||||
|
||||
let currentUserPromise = null;
|
||||
|
||||
let resolveUser = null;
|
||||
|
||||
async function migrateUsers() {
|
||||
// let UIDOVERRIDE = "ugbG1GiSHxVEYMDmMeLV9byeukl2";
|
||||
let UIDOVERRIDE = undefined;
|
||||
let lastId;
|
||||
let usersSoFar = 0;
|
||||
let totalUsers = 330000;
|
||||
let totalCompletionTime = 0;
|
||||
let averageCompletionTime = 0;
|
||||
try {
|
||||
let migrationStats = JSON.parse(
|
||||
fs.readFileSync("./migrationStats.txt", "utf8")
|
||||
);
|
||||
lastId = migrationStats.uid;
|
||||
usersSoFar = migrationStats.usersSoFar;
|
||||
totalCompletionTime = migrationStats.totalCompletionTime;
|
||||
averageCompletionTime = migrationStats.averageCompletionTime;
|
||||
} catch (e) {}
|
||||
let querySnapshot;
|
||||
let limit = 1000;
|
||||
do {
|
||||
console.log("starting another loop, getting users");
|
||||
if (lastId) {
|
||||
let lastSnapshot = await db.collection("users").doc(lastId).get();
|
||||
querySnapshot = await db
|
||||
.collection("users")
|
||||
.where("banned", "==", true)
|
||||
.orderBy("name")
|
||||
.startAfter(lastSnapshot)
|
||||
.limit(limit)
|
||||
.get();
|
||||
} else {
|
||||
querySnapshot = await db
|
||||
.collection("users")
|
||||
.where("banned", "==", true)
|
||||
.orderBy("name")
|
||||
.limit(limit)
|
||||
.get();
|
||||
}
|
||||
// console.log('start of foreach');
|
||||
console.log(`migrating ${querySnapshot.docs.length} users`);
|
||||
let fulllog = false;
|
||||
for (const userDoc of querySnapshot.docs) {
|
||||
let userstart = performance.now();
|
||||
currentUserPromise = null;
|
||||
currentUserPromise = new Promise((resolve, reject) => {
|
||||
resolveUser = resolve;
|
||||
});
|
||||
let userData = userDoc.data();
|
||||
let uid = userDoc.id;
|
||||
try {
|
||||
let userAuth = await auth.getUser(uid);
|
||||
let email = userAuth.email;
|
||||
let userCreatedAt = new Date(userAuth.metadata.creationTime).getTime();
|
||||
|
||||
let mongoUser = {
|
||||
name: userData.name,
|
||||
email: email,
|
||||
addedAt: userCreatedAt,
|
||||
uid: UIDOVERRIDE ? UIDOVERRIDE : uid,
|
||||
oldTypingStats: {},
|
||||
};
|
||||
|
||||
if (userData.completedTests)
|
||||
mongoUser.oldTypingStats.completedTests = userData.completedTests;
|
||||
if (userData.discordId) mongoUser.discordId = userData.discordId;
|
||||
if (userData.banned) mongoUser.banned = userData.banned;
|
||||
if (userData.verified) mongoUser.verified = userData.verified;
|
||||
//banned
|
||||
//verified
|
||||
if (userData.startedTests)
|
||||
mongoUser.oldTypingStats.startedTests = userData.startedTests;
|
||||
if (userData.timeTyping)
|
||||
mongoUser.oldTypingStats.timeTyping = userData.timeTyping;
|
||||
|
||||
if (userData.personalBests)
|
||||
mongoUser.personalBests = userData.personalBests;
|
||||
|
||||
let tagPairs = {};
|
||||
|
||||
let mongoUserTags = [];
|
||||
|
||||
if (fulllog) console.log(`${uid} migrating tags`);
|
||||
let tagsSnapshot = await db.collection(`users/${uid}/tags`).get();
|
||||
await tagsSnapshot.forEach(async (tagDoc) => {
|
||||
let tagData = tagDoc.data();
|
||||
let tagId = tagDoc.id;
|
||||
let new_id = ObjectID();
|
||||
tagPairs[tagId] = new_id;
|
||||
let tagtopush = { _id: new_id, name: tagData.name };
|
||||
if (tagData.personalBests)
|
||||
tagtopush.personalBests = tagData.personalBests;
|
||||
mongoUserTags.push(tagtopush);
|
||||
});
|
||||
|
||||
mongoUser.tags = mongoUserTags;
|
||||
|
||||
// if (fulllog) console.log(`${uid} migrating config`);
|
||||
// if (userData.config) {
|
||||
// await mongoDB()
|
||||
// .collection("configs")
|
||||
// .updateOne(
|
||||
// { uid: UIDOVERRIDE ? UIDOVERRIDE : uid },
|
||||
// {
|
||||
// $set: {
|
||||
// uid: UIDOVERRIDE ? UIDOVERRIDE : uid,
|
||||
// config: userData.config,
|
||||
// },
|
||||
// },
|
||||
// { upsert: true }
|
||||
// );
|
||||
// }
|
||||
|
||||
// if (fulllog) console.log(`${uid} migrating presets`);
|
||||
// let presetsSnapshot = await db.collection(`users/${uid}/presets`).get();
|
||||
// await presetsSnapshot.forEach(async (presetDoc) => {
|
||||
// let presetData = presetDoc.data();
|
||||
// let newpreset = {
|
||||
// uid: UIDOVERRIDE ? UIDOVERRIDE : uid,
|
||||
// name: presetData.name,
|
||||
// };
|
||||
// if (presetData.config) newpreset.config = presetData.config;
|
||||
// await mongoDB().collection("presets").insertOne(newpreset);
|
||||
// });
|
||||
|
||||
// let lastcount = 0;
|
||||
// let limit = 1000;
|
||||
// let lastdoc = "start";
|
||||
// let total = 0;
|
||||
// let newStats = {
|
||||
// completedTests: 0,
|
||||
// startedTests: 0,
|
||||
// timeTyping: 0,
|
||||
// };
|
||||
// if (fulllog) console.log(`${uid} migrating results`);
|
||||
// do {
|
||||
// if (fulllog) console.log(`${total} so far`);
|
||||
// let resultsSnapshot;
|
||||
// if (lastdoc === "start") {
|
||||
// resultsSnapshot = await db
|
||||
// .collection(`users/${uid}/results`)
|
||||
// .orderBy("timestamp", "desc")
|
||||
// .limit(limit)
|
||||
// .get();
|
||||
// } else {
|
||||
// resultsSnapshot = await db
|
||||
// .collection(`users/${uid}/results`)
|
||||
// .orderBy("timestamp", "desc")
|
||||
// .startAfter(lastdoc)
|
||||
// .limit(limit)
|
||||
// .get();
|
||||
// }
|
||||
// await resultsSnapshot.forEach(async (resultDoc) => {
|
||||
// let resultData = resultDoc.data();
|
||||
// resultData.uid = UIDOVERRIDE ? UIDOVERRIDE : uid;
|
||||
// if (resultData.tags && resultData.tags.length > 0) {
|
||||
// resultData.tags = resultData.tags.map((tag) => tagPairs[tag]);
|
||||
// }
|
||||
// if (!resultData.charStats) {
|
||||
// resultData.charStats = [
|
||||
// resultData.correctChars,
|
||||
// resultData.incorrectChars,
|
||||
// ];
|
||||
// }
|
||||
// delete resultData.correctChars;
|
||||
// delete resultData.incorrectChars;
|
||||
// delete resultData.allChars;
|
||||
// delete resultData.keySpacing;
|
||||
// delete resultData.keyDuration;
|
||||
// delete resultData.theme;
|
||||
// delete resultData.name;
|
||||
|
||||
// //remove the default fields here
|
||||
// if (resultData.bailedOut === false) delete resultData.bailedOut;
|
||||
// if (resultData.blindMode === false) delete resultData.blindMode;
|
||||
// if (resultData.difficulty === "normal")
|
||||
// delete resultData.difficulty;
|
||||
// if (resultData.funbox === "none") delete resultData.funbox;
|
||||
// if (resultData.language === "english") delete resultData.language;
|
||||
// if (resultData.numbers === false) delete resultData.numbers;
|
||||
// if (resultData.punctuation === false) delete resultData.punctuation;
|
||||
|
||||
// if (resultData.mode !== "custom") delete resultData.customText;
|
||||
|
||||
// newStats.completedTests++;
|
||||
// if (resultData.restartCount) {
|
||||
// newStats.startedTests += resultData.restartCount + 1;
|
||||
// } else {
|
||||
// newStats.startedTests++;
|
||||
// }
|
||||
// if (resultData.testDuration) {
|
||||
// newStats.timeTyping += parseFloat(resultData.testDuration);
|
||||
// }
|
||||
// if (resultData.incompleteTestSeconds) {
|
||||
// newStats.timeTyping += resultData.incompleteTestSeconds;
|
||||
// }
|
||||
// await mongoDB().collection("results").insertOne(resultData);
|
||||
// });
|
||||
// lastcount = resultsSnapshot.docs.length;
|
||||
// lastdoc = resultsSnapshot.docs[resultsSnapshot.docs.length - 1];
|
||||
// total += lastcount;
|
||||
// } while (lastcount > 0);
|
||||
|
||||
// if (fulllog) console.log(`${uid} migrated ${total} results`);
|
||||
|
||||
// mongoUser.completedTests = newStats.completedTests;
|
||||
// mongoUser.startedTests = newStats.startedTests;
|
||||
// mongoUser.timeTyping = newStats.timeTyping;
|
||||
|
||||
if (fulllog) console.log(`${uid} migrating user doc`);
|
||||
await mongoDB()
|
||||
.collection("users")
|
||||
.updateOne(
|
||||
{ uid: UIDOVERRIDE ? UIDOVERRIDE : uid },
|
||||
{
|
||||
$set: mongoUser,
|
||||
},
|
||||
{ upsert: true }
|
||||
);
|
||||
|
||||
console.log(`${uid} migrated \t\t ${userData.name} \t\t`);
|
||||
fs.appendFileSync(
|
||||
"log_success.txt",
|
||||
`${uid}\t\t${userData.name}\t\t`,
|
||||
"utf8"
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(`${uid} failed`);
|
||||
console.log(err);
|
||||
fs.appendFileSync(
|
||||
"log_failed.txt",
|
||||
`${uid}\t\t${err.message}\n`,
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
lastId = uid;
|
||||
let userend = performance.now();
|
||||
let time = (userend - userstart) / 1000;
|
||||
totalCompletionTime += time;
|
||||
// console.log(`${uid} took ${time} seconds`);
|
||||
averageCompletionTime = totalCompletionTime / usersSoFar + 1;
|
||||
usersSoFar++;
|
||||
let estimateSecondsLeft =
|
||||
averageCompletionTime * (totalUsers - usersSoFar);
|
||||
console.log(
|
||||
`${usersSoFar}/${totalUsers} users | estimated ${secondsToString(
|
||||
estimateSecondsLeft,
|
||||
true
|
||||
)} left`
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
"migrationStats.txt",
|
||||
JSON.stringify({
|
||||
uid,
|
||||
usersSoFar,
|
||||
totalCompletionTime,
|
||||
averageCompletionTime,
|
||||
}),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
resolveUser();
|
||||
|
||||
// console.log(userData);
|
||||
// let newUser;
|
||||
// try{
|
||||
// let data = userDoc.data();
|
||||
// data._id = userDoc.id;
|
||||
// newUser = new User(data);
|
||||
// newUser.uid = userDoc.id;
|
||||
// newUser.globalStats = {
|
||||
// started: userDoc.data().startedTests,
|
||||
// completed: userDoc.data().completedTests,
|
||||
// time: userDoc.data().timeTyping,
|
||||
// };
|
||||
// let tagIdDict = {};
|
||||
// let tagsSnapshot = await db.collection(`users/${userDoc.id}/tags`).get();
|
||||
// tagsSnapshot.forEach((tagDoc) => {
|
||||
// let formattedTag = tagDoc.data();
|
||||
// formattedTag._id = mongoose.Types.ObjectId(); //generate new objectId
|
||||
// tagIdDict[tagDoc.id] = formattedTag._id; //save pair of ids in memory to determine what to set new id as in result tags
|
||||
// newUser.tags.push(formattedTag);
|
||||
// console.log(`Tag ${tagDoc.id} saved for user ${userCount}`);
|
||||
// });
|
||||
// let resultsSnapshot = await db.collection(`users/${userDoc.id}/results`).get();
|
||||
// let resCount = 1;
|
||||
// resultsSnapshot.forEach((result) => {
|
||||
// let formattedResult = result.data();
|
||||
// if(formattedResult.tags != undefined){
|
||||
// formattedResult.tags.forEach((tag, index) => {
|
||||
// if (tagIdDict[tag])
|
||||
// formattedResult.tags[index] = tagIdDict[tag];
|
||||
// });
|
||||
// }
|
||||
// newUser.results.push(formattedResult);
|
||||
// console.log(`Result ${resCount} saved for user ${userCount}`);
|
||||
// resCount++;
|
||||
// });
|
||||
// newUser.results.sort((a, b) => {
|
||||
// return a.timestamp - b.timestamp;
|
||||
// });
|
||||
// let presetsSnapshot = await db.collection(`users/${userDoc.id}/presets`).get();
|
||||
// presetsSnapshot.forEach((preset) => {
|
||||
// newUser.presets.push(preset.data());
|
||||
// });
|
||||
// await newUser.save();
|
||||
// console.log(`User ${userCount} (${newUser.uid}) saved`);
|
||||
// userCount++;
|
||||
// }catch(e){
|
||||
// // throw e;
|
||||
// console.log(`User ${userCount} (${newUser.uid}) failed: ${e.message}`);
|
||||
// userCount++;
|
||||
// }
|
||||
}
|
||||
} while (querySnapshot.docs.length > 0);
|
||||
|
||||
console.log("Migration complete");
|
||||
// console.log('end of foreach');
|
||||
}
|
||||
// //not tested because I can't get leaderboards to work on my fork for some reason
|
||||
// db.collection("leaderboards")
|
||||
// .get()
|
||||
// .then((leaderboardsSnapshot) => {
|
||||
// leaderboardsSnapshot.forEach((lbDoc) => {
|
||||
// let newLb = new Leaderboard(lbDoc.data());
|
||||
// newLb.save();
|
||||
// });
|
||||
// });
|
||||
|
||||
// //migrate bot-commands
|
||||
// db.collection("bot-commands")
|
||||
// .get()
|
||||
// .then((botCommandsSnapshot) => {
|
||||
// botCommandsSnapshot.forEach((bcDoc) => {
|
||||
// let newBotCommand = new BotCommand(bcDoc.data());
|
||||
// newBotCommand.save();
|
||||
// });
|
||||
// });
|
||||
|
||||
//migrate public stat
|
||||
async function migratePublicStats() {
|
||||
db.collection("public")
|
||||
.doc("stats")
|
||||
.get()
|
||||
.then((ret) => {
|
||||
let stats = ret.data();
|
||||
mongoDB()
|
||||
.collection("public")
|
||||
.updateOne(
|
||||
{ type: "stats" },
|
||||
{
|
||||
$set: {
|
||||
completedTests: stats.completedTests,
|
||||
startedTests: stats.startedTests,
|
||||
timeTyping: stats.timeTyping,
|
||||
},
|
||||
},
|
||||
{ upsert: true }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function init() {
|
||||
await connectDB();
|
||||
// await migratePublicStats();
|
||||
await migrateUsers();
|
||||
// process.exit(1);
|
||||
}
|
||||
|
||||
function secondsToString(sec, full = false) {
|
||||
const hours = Math.floor(sec / 3600);
|
||||
const minutes = Math.floor((sec % 3600) / 60);
|
||||
const seconds = Math.round((sec % 3600) % 60);
|
||||
let hoursString;
|
||||
let minutesString;
|
||||
let secondsString;
|
||||
hours < 10 ? (hoursString = "0" + hours) : (hoursString = hours);
|
||||
minutes < 10 ? (minutesString = "0" + minutes) : (minutesString = minutes);
|
||||
seconds < 10 && (minutes > 0 || hours > 0 || full)
|
||||
? (secondsString = "0" + seconds)
|
||||
: (secondsString = seconds);
|
||||
|
||||
let ret = "";
|
||||
if (hours > 0 || full) ret += hoursString + ":";
|
||||
if (minutes > 0 || hours > 0 || full) ret += minutesString + ":";
|
||||
ret += secondsString;
|
||||
return ret;
|
||||
}
|
||||
|
||||
init();
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# Mongo todo
|
||||
|
||||
## Todo
|
||||
|
||||
- Make sure that the branch is ready for deployment
|
||||
- Make sure that the bot can interact with the data on the express server
|
||||
- Would be optimal if the bot were to run on the same server as the express server, so that the bot wouldn't have to access data through api routes
|
||||
- Determine if generatePairingCode should be removed or migrated
|
||||
- This function was commented out in index.js but is used in frontend
|
||||
|
||||
## Bugs
|
||||
|
||||
- Make sure that the bot is able to interact with the mongo database
|
||||
- If bot is on same server, it could work with mongo directly, otherwise, more api routes are needed
|
||||
- Do names have to be made lowercase before checking if a duplicate name is found?(that is when a new user is created or username is changed)
|
||||
|
||||
### Minor/efficiency bugs
|
||||
|
||||
- Does clearDailyLeaderboards cause a memory leak?
|
||||
- Is filteredResults.reverse(); in account.js going to cause efficiency issues?
|
||||
- For loop in account could work backwards instead, but this would add complexity
|
||||
- Why does `if (page == "account") pageTransition = false;` get rid of endless account loading bug when accessing via url
|
||||
- Name is not passed in user token/auth().currentUser
|
||||
- Account button sometimes shows loading infinitely after a test
|
||||
- Can't navigate to user until page is refreshed
|
||||
- After refresh, pr is not saved
|
||||
- Can't induce this error and doesn't occur often so adding it as minor bug
|
||||
- lbmemory undefined if page not refreshed after user sign up?
|
||||
- If you are in first place and you place on the leaderboard but not above yourself, you may get glb undefined error
|
||||
- Might also occur if you are simply on the leaderboard and make the leaderboard but not above your current position
|
||||
- Doesn't happen all the time
|
||||
- Hidden property of leaderboard is unused
|
||||
- Verified property of user is unused, set at false by default
|
||||
- Can't find where the property would be set in the code
|
||||
- Is this discord verified, if so, why do you need discord verified to be on leaderboard?
|
||||
- Temporarily removed from leaderboard requirements
|
||||
|
||||
### Functions not found anywhere except for index.js
|
||||
|
||||
Might need to be migrated, might not. I'm not sure why these are in the file if they are not being used.
|
||||
|
||||
- getAllNames
|
||||
- getAllUsers
|
||||
- getPatreons
|
||||
- requestTest
|
||||
- incrementStartedTestCounter
|
||||
- incrementTestCounter
|
||||
|
||||
### Possibilities
|
||||
|
||||
- Might be worthwhile to use redis to store userdata up to a certain point
|
||||
- Users who have been requested in the last hour will be stored in the redis database so that their data can be sent again without having to search a large database
|
||||
- After an hour without a new request they can be removed from memory
|
||||
- Create a backup system to prevent loss of data
|
||||
- Users should be able to export their data themselves
|
||||
- Pretty much is just the user snap but without uid
|
||||
- Could split server.js into multiple files for easier code management
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
|
|
@ -10,7 +10,7 @@
|
|||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"axios": "^0.21.1",
|
||||
"axios": "^0.21.2",
|
||||
"chart.js": "^2.9.4",
|
||||
"chartjs-plugin-annotation": "^0.5.7",
|
||||
"chartjs-plugin-trendline": "^0.2.2",
|
||||
|
|
@ -2814,11 +2814,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"version": "0.21.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz",
|
||||
"integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
"follow-redirects": "^1.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-eslint": {
|
||||
|
|
@ -16580,11 +16580,11 @@
|
|||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"version": "0.21.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz",
|
||||
"integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
"follow-redirects": "^1.14.0"
|
||||
}
|
||||
},
|
||||
"babel-eslint": {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"axios": "^0.21.1",
|
||||
"axios": "^0.21.2",
|
||||
"chart.js": "^2.9.4",
|
||||
"chartjs-plugin-annotation": "^0.5.7",
|
||||
"chartjs-plugin-trendline": "^0.2.2",
|
||||
|
|
|
|||
|
|
@ -1,14 +1,3 @@
|
|||
/*
|
||||
TODO:
|
||||
Export replay as video
|
||||
Export replay as typing test file?
|
||||
.ttr file extension (stands for typing test record)
|
||||
Should just be json, but fields should be specified by some format
|
||||
metadata field with rules, website source, mode, name of typist
|
||||
data field should be a list of objects, like monkeytype replay uses
|
||||
signature or verfication field should be able to check file validity with server
|
||||
And add ability to upload file to watch replay
|
||||
*/
|
||||
import config from "./config";
|
||||
import * as Sound from "./sound";
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@
|
|||
<!-- make sure to update this date every time the policy is changed -->
|
||||
</p>
|
||||
|
||||
<p>Effective date: August 27, 2021</p>
|
||||
<p>Effective date: September 8, 2021</p>
|
||||
<p>
|
||||
Thanks for trusting Monkeytype ('Monkeytype', 'we', 'us', 'our') with
|
||||
your personal information, your code, and your projects! We take our
|
||||
|
|
@ -188,7 +188,7 @@
|
|||
<li>Change settings on the website</li>
|
||||
</ul>
|
||||
|
||||
<h1 id="Data_Usage">How do we use your data?</h1>
|
||||
<h1 id="Data_Usage">How will we use your data?</h1>
|
||||
<p>Monkeytype collects your data so that we can:</p>
|
||||
|
||||
<ul>
|
||||
|
|
@ -263,7 +263,7 @@
|
|||
</p>
|
||||
<p>In our case, this service is provided by Google Analytics.</p>
|
||||
|
||||
<h1 id="Cookies">What are Cookies?</h1>
|
||||
<h1 id="Cookies">What are cookies?</h1>
|
||||
<p>
|
||||
Cookies are text files placed on your computer to collect standard
|
||||
Internet log information and visitor behavior information. When you
|
||||
|
|
@ -274,8 +274,7 @@
|
|||
For further information, visit
|
||||
<a href="https://en.wikipedia.org/wiki/HTTP_cookie" target="_blank">
|
||||
www.wikipedia.org/wiki/HTTP_cookie
|
||||
</a>
|
||||
.
|
||||
</a>.
|
||||
</p>
|
||||
|
||||
<h1 id="Usage_of_Cookies">How do we use cookies?</h1>
|
||||
|
|
@ -290,12 +289,12 @@
|
|||
</ul>
|
||||
<h1 id="Types_of_Cookies_Used">What types of cookies do we use?</h1>
|
||||
<p>
|
||||
There are a number of different types of cookies, however, our website
|
||||
There are a number of different types of cookies; however, our website
|
||||
uses functionality cookies. Monkeytype uses these cookies so we
|
||||
recognize you on our website and remember your previously selected
|
||||
settings.
|
||||
</p>
|
||||
<h1 id="Managing_Cookies">How to manage cookies</h1>
|
||||
<h1 id="Managing_Cookies">How to manage your cookies</h1>
|
||||
<p>
|
||||
You can set your browser not to accept cookies, and the above website
|
||||
tells you how to remove cookies from your browser. However, in a few
|
||||
|
|
@ -324,7 +323,7 @@
|
|||
|
||||
<h1 id="Contact_Info">How to contact us</h1>
|
||||
<p>
|
||||
If you have any questions about Monkeytypes’s privacy policy, the data
|
||||
If you have any questions about Monkeytype’s privacy policy, the data
|
||||
we hold on you, or you would like to exercise one of your data
|
||||
protection rights, please do not hesitate to contact us.
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -129,19 +129,19 @@
|
|||
|
||||
<h1 id="Vulnerability_Disclosure">How to Disclose a Vulnerability</h1>
|
||||
<p>
|
||||
For vulnerabilities that impact the confidentiality, integrity and
|
||||
availability of monkeytype services, please send your disclosure via
|
||||
For vulnerabilities that impact the confidentiality, integrity, and
|
||||
availability of Monkeytype services, please send your disclosure via
|
||||
(1)
|
||||
<a href="mailto:jack@monkeytype.com">mail</a>
|
||||
, or (2) private discord chat to
|
||||
<a href="https://www.discord.gg/monkeytype">miodec</a>
|
||||
. For non-security related platform bugs, follow the bug submission
|
||||
<a href="mailto:jack@monkeytype.com">email</a>,
|
||||
or (2) private Discord chat to
|
||||
<a href="https://www.discord.gg/monkeytype">Miodec</a>.
|
||||
For non-security related platform bugs, follow the bug submission
|
||||
<a
|
||||
href="https://github.com/Miodec/monkeytype#bug-report-or-feature-request"
|
||||
>
|
||||
guidelines
|
||||
</a>
|
||||
.Include as much detail as possible to ensure reproducibility. At a
|
||||
</a>.
|
||||
Include as much detail as possible to ensure reproducibility. At a
|
||||
minimum, vulnerability disclosures should include:
|
||||
</p>
|
||||
<ul>
|
||||
|
|
|
|||
|
|
@ -109,33 +109,32 @@
|
|||
<div id="middle">
|
||||
<!-- make sure to update this date every time the tos is changed -->
|
||||
<p>
|
||||
The last updates made to this terms of service were made on August 27,
|
||||
2021
|
||||
These terms of service were last updated on September 8, 2021.
|
||||
</p>
|
||||
<h1>Agreement</h1>
|
||||
<p>
|
||||
By accessing this Website, accessible from monkeytype.com, you are
|
||||
agreeing to be bound by these Website Terms and Conditions of Use and
|
||||
agree that you are responsible for the agreement with any applicable
|
||||
local laws.
|
||||
By accessing this Website, accessible from monkeytype.com, you are
|
||||
agreeingto be bound by these Website Terms of Service and agree that
|
||||
you are responsible for the agreement in accordance with any
|
||||
applicable local laws.
|
||||
<strong>
|
||||
IF YOU DO NOT AGREE TO ALL THE TERMS AND CONDITIONS OF THIS
|
||||
AGREEMENT, YOU MAY NOT ACCESS OR USE OUR SERVICES.
|
||||
AGREEMENT, YOU ARE NOT PERMITTED TO ACCESS OR USE OUR SERVICES.
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
<h1>Limitations</h1>
|
||||
<p>
|
||||
You are responsible for your accounts security and all activities on
|
||||
You are responsible for your account's security and all activities on
|
||||
your account. You must not, in the use of this site, violate any
|
||||
applicable laws, including without limitation, copyright laws, or any
|
||||
other laws reguarding the security of your personal data, or otherwise
|
||||
applicable laws, including, without limitation, copyright laws, or any
|
||||
other laws regarding the security of your personal data, or otherwise
|
||||
misuse this site.
|
||||
</p>
|
||||
<p>
|
||||
Monkeytype reserves the right to remove or disable any account or any
|
||||
other content on this site at any time for any reason, without prior
|
||||
notice to you if we believe that you have violated this agreement.
|
||||
notice to you, if we believe that you have violated this agreement.
|
||||
</p>
|
||||
<p>
|
||||
You agree that you will not upload, post, host, or transmit any
|
||||
|
|
@ -148,26 +147,26 @@
|
|||
<li>is discriminatory or abusive toward any individual or group;</li>
|
||||
<li>
|
||||
is degrading to others on the basis of gender, race, class,
|
||||
ethnicity, national origin, religion, sexual preference, orientation
|
||||
or identity, disability, or other classification or otherwise
|
||||
represents or approves content that: is hate speech, discriminating,
|
||||
ethnicity, national origin, religion, sexual preference, orientation,
|
||||
or identity, disability, or other classification, or otherwise
|
||||
represents or condones content that: is hate speech, discriminating,
|
||||
threatening, or pornographic; incites violence; or contains nudity
|
||||
or graphic or gratuitous violence;
|
||||
</li>
|
||||
<li>
|
||||
is violative to any person's right to privacy or publicity or
|
||||
violates any person's right to privacy or publicity, or
|
||||
otherwise solicits, collects, or publishes data, including personal
|
||||
information and login information, about other Users without consent
|
||||
or for unlawful purposes in violation of any applicable
|
||||
international, federal, state or local law, statute, ordinance or
|
||||
regulation;
|
||||
international, federal, state, or local law, statute, ordinance, or
|
||||
regulation; or
|
||||
</li>
|
||||
<li>
|
||||
contains or installs any active malware or exploits/uses our
|
||||
platform for exploit delivery (such as part of a command or control
|
||||
system); or infringes on any proprietary right of any party,
|
||||
including patent, trademark, trade secret, copyright, right of
|
||||
publicity, or other rights
|
||||
publicity, or other rights.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
|
|
@ -191,34 +190,34 @@
|
|||
<li>
|
||||
falsely impersonate any person or entity, including any of our
|
||||
contributors, misrepresent your identity or the site's purpose, or
|
||||
falsely associate yourself with Monkeytype.;
|
||||
falsely associate yourself with Monkeytype;
|
||||
</li>
|
||||
>
|
||||
<li>
|
||||
violate the privacy of any third party, such as by posting another
|
||||
person's personal information without their consent;
|
||||
</li>
|
||||
<li>
|
||||
access (or attempt to access) any service on the Services by any
|
||||
means other than as permitted in this Agreement or operating the
|
||||
Services on any computers or accounts on which you do not have
|
||||
access or attempt to access any service on the Services by any
|
||||
means other than as permitted in this Agreement, or operating the
|
||||
Services on any computers or accounts which you do not have
|
||||
permission to operate;
|
||||
</li>
|
||||
<li>
|
||||
facilitate or encourage any violations of this Agreement or
|
||||
interfere with the operation, appearance, security, or functionality
|
||||
of the Services;
|
||||
of the Services; or
|
||||
</li>
|
||||
<li>
|
||||
use the Services in any manner that is harmful to minors. Without
|
||||
limiting the foregoing, you will not transmit or post any content
|
||||
anywhere on the Services that violate anys laws. Monkeytype
|
||||
absolutely does not tolerate engaging in activity that significantly
|
||||
harms our Users. We will resolve disputes in favor of protecting our
|
||||
Users as a whole.
|
||||
use the Services in any manner that is harmful to minors.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
Without limiting the foregoing, you will not transmit or post any
|
||||
content anywhere on the Services that violates any laws. Monkeytype
|
||||
absolutely does not tolerate engaging in activity that significantly
|
||||
harms our Users. We will resolve disputes in favor of protecting our
|
||||
Users as a whole.
|
||||
</p>
|
||||
<h1>Privacy Policy</h1>
|
||||
If you use our Services, you must abide by our Privacy Policy. You
|
||||
acknowledge that you have read our
|
||||
|
|
@ -241,15 +240,19 @@
|
|||
You shouldn't use bots or access our Services in malicious or
|
||||
un-permitted ways. While accessing or using the Services, you may not:
|
||||
<ol>
|
||||
<li>use bots, hacks, or cheats while using our site</li>
|
||||
<li>create manual requests to Monkeytype servers</li>
|
||||
<li>
|
||||
tamper with, or use non-public areas of the Services, or the
|
||||
use bots, hacks, or cheats while using our site;
|
||||
</li>
|
||||
<li>
|
||||
create manual requests to Monkeytype servers;
|
||||
</li>
|
||||
<li>
|
||||
tamper with or use non-public areas of the Services, or the
|
||||
computer or delivery systems of Monkeytype and/or its service
|
||||
providers;
|
||||
</li>
|
||||
<li>
|
||||
Probe, scan, or test any system or network (particularly for
|
||||
probe, scan, or test any system or network (particularly for
|
||||
vulnerabilities), or otherwise attempt to breach or circumvent any
|
||||
security or authentication measures, or search or attempt to access
|
||||
or search the Services by any means (automated or otherwise) other
|
||||
|
|
@ -261,23 +264,23 @@
|
|||
exclusion mechanisms;
|
||||
</li>
|
||||
<li>
|
||||
Scrape the Services, scrape Content from the Services, or use
|
||||
scrape the Services, scrape Content from the Services, or use
|
||||
automated means, including spiders, robots, crawlers, data mining
|
||||
tools, or the like to download data from the Services or otherwise
|
||||
access the Services;
|
||||
</li>
|
||||
<li>
|
||||
Employ misleading email or IP addresses, or forged headers or
|
||||
employ misleading email or IP addresses or forged headers or
|
||||
otherwise manipulated identifiers in order to disguise the origin of
|
||||
any content transmitted to or through the Services;
|
||||
</li>
|
||||
<li>
|
||||
Use the Services to send altered, deceptive, or false
|
||||
source-identifying information, including without limitation by
|
||||
use the Services to send altered, deceptive, or false
|
||||
source-identifying information, including, without limitation, by
|
||||
forging TCP-IP packet headers or e-mail headers; or
|
||||
</li>
|
||||
<li>
|
||||
Interfere with, or disrupt or attempt to interfere with or disrupt,
|
||||
interfere with, or disrupt or attempt to interfere with or disrupt,
|
||||
the access of any User, host, or network, including, without
|
||||
limitation, by sending a virus to, spamming, or overloading the
|
||||
Services, or by scripted use of the Services in such a manner as to
|
||||
|
|
@ -286,16 +289,16 @@
|
|||
</ol>
|
||||
<h1>Links</h1>
|
||||
Monkeytype is not responsible for the contents of any linked sites. The
|
||||
use of any linked website is at the user’s own risk.
|
||||
use of any linked website is at the user's own risk.
|
||||
|
||||
<h1>Changes</h1>
|
||||
Monkeytype may revise these Terms of Use for its Website at any time
|
||||
Monkeytype may revise these Terms of Service for its Website at any time
|
||||
without prior notice. By using this Website, you are agreeing to be
|
||||
bound by the current version of these Terms and Conditions of Use.
|
||||
bound by the current version of these Terms of Service.
|
||||
<h1>Disclaimer</h1>
|
||||
<p>
|
||||
Excluding the explicitly stated warranties within these Terms, we only
|
||||
offer our Services on an 'as-is' basis. YOUR ACCESS TO AND USE OF THE
|
||||
EXCLUDING THE EXPLICITLY STATED WARRANTIES WITHIN THESE TERMS, WE ONLY
|
||||
OFFER OUR SERVICES ON AN 'AS-IS' BASIS. YOUR ACCESS TO AND USE OF THE
|
||||
SERVICES OR ANY CONTENT IS AT YOUR OWN RISK. YOU UNDERSTAND AND AGREE
|
||||
THAT THE SERVICES AND CONTENT ARE PROVIDED TO YOU ON AN 'AS IS,' 'WITH
|
||||
ALL FAULTS,' AND 'AS AVAILABLE' BASIS. WITHOUT LIMITING THE FOREGOING,
|
||||
|
|
@ -304,39 +307,39 @@
|
|||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
|
||||
NON-INFRINGEMENT. TO THE EXTENT SUCH DISCLAIMER CONFLICTS WITH
|
||||
APPLICABLE LAW, THE SCOPE AND DURATION OF ANY APPLICABLE WARRANTY WILL
|
||||
BE THE MINIMUM PERMITTED UNDER SUCH LAW. MONKEYTYPE makes no
|
||||
representations, warranties, OR GUARANTEES AS TO THE RELIABILITY,
|
||||
BE THE MINIMUM PERMITTED UNDER SUCH LAW. MONKEYTYPE MAKES NO
|
||||
REPRESENTATIONS, WARRANTIES, OR GUARANTEES AS TO THE RELIABILITY,
|
||||
TIMELINESS, QUALITY, SUITABILITY, AVAILABILITY, ACCURACY, OR
|
||||
COMPLETENESS of any kind with respect to the Services, including any
|
||||
representation or warranty that the use of the Services will (a) be
|
||||
timely, uninterrupted, or error-free or operate in combination with
|
||||
any other hardware, software, system, or data, (b) meet your
|
||||
requirements or expectations, (c) be free from errors or that defects
|
||||
will be corrected, or (d) be free of viruses or other harmful
|
||||
components. Monkeytype also makes no representations or warranties of
|
||||
any kind with respect to Content; User Content is provided by and is
|
||||
solely the responsibility of the respective User providing that
|
||||
Content. No advice or information, whether oral or written, obtained
|
||||
from Monkeytype or through the Services, will create any warranty not
|
||||
expressly made herein. MONKEYTYPE DOES NOT warrant, endorse,
|
||||
guarantee, or assume responsibility for any user conteNt on the
|
||||
Services or any hyperlinked website or third-party service, and
|
||||
MONKEYTYPE will not be a party to or in any way be responsible for
|
||||
transactions betWeen you and third-parties. IF APPLICABLE LAW DOES NOT
|
||||
COMPLETENESS OF ANY KIND WITH RESPECT TO THE SERVICES, INCLUDING ANY
|
||||
REPRESENTATION OR WARRANTY THAT THE USE OF THE SERVICES WILL (A) BE
|
||||
TIMELY, UNINTERRUPTED, OR ERROR-FREE, OR OPERATE IN COMBINATION WITH
|
||||
ANY OTHER HARDWARE, SOFTWARE, SYSTEM, OR DATA, (B) MEET YOUR
|
||||
REQUIREMENTS OR EXPECTATIONS, (C) BE FREE FROM ERRORS OR THAT DEFECTS
|
||||
WILL BE CORRECTED, OR (D) BE FREE OF VIRUSES OR OTHER HARMFUL
|
||||
COMPONENTS. MONKEYTYPE ALSO MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND WITH RESPECT TO CONTENT; USER CONTENT IS PROVIDED BY AND IS
|
||||
SOLELY THE RESPONSIBILITY OF THE RESPECTIVE USER PROVIDING THAT
|
||||
CONTENT. NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED
|
||||
FROM MONKEYTYPE OR THROUGH THE SERVICES, WILL CREATE ANY WARRANTY NOT
|
||||
EXPRESSLY MADE HEREIN. MONKEYTYPE DOES NOT WARRANT, ENDORSE,
|
||||
GUARANTEE, OR ASSUME RESPONSIBILITY FOR ANY USER CONTENT ON THE
|
||||
SERVICES OR ANY HYPERLINKED WEBSITE OR THIRD-PARTY SERVICE, AND
|
||||
MONKEYTYPE WILL NOT BE A PARTY TO OR IN ANY WAY BE RESPONSIBLE FOR
|
||||
TRANSACTIONS BETWEEN YOU AND THIRD PARTIES. IF APPLICABLE LAW DOES NOT
|
||||
ALLOW THE EXCLUSION OF SOME OR ALL OF THE ABOVE IMPLIED OR STATUTORY
|
||||
WARRANTIES TO APPLY TO YOU, THE ABOVE EXCLUSIONS WILL APPLY TO YOU TO
|
||||
THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
</p>
|
||||
<h1>Contact</h1>
|
||||
<p>
|
||||
If you have any questions about Monkeytypes’s privacy policy, the data
|
||||
If you have any questions about Monkeytype’s privacy policy, the data
|
||||
we hold on you, or you would like to exercise one of your data
|
||||
protection rights, please do not hesitate to contact us.
|
||||
</p>
|
||||
<p>
|
||||
Email:
|
||||
<a
|
||||
href="mailto: jack@monkeytype.com"
|
||||
href="mailto:jack@monkeytype.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue