This commit is contained in:
Jack 2021-09-09 13:47:24 +01:00
commit 6cf76e28d6
9 changed files with 93 additions and 712 deletions

View file

@ -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);
});

View file

@ -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();

View file

@ -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
View file

@ -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": {

View file

@ -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",

View file

@ -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";

View file

@ -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 Monkeytypess privacy policy, the data
If you have any questions about Monkeytypes 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>

View file

@ -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>

View file

@ -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 users 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 Monkeytypess privacy policy, the data
If you have any questions about Monkeytypes 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"
>