diff --git a/functions/index.js b/functions/index.js
index 0e3b6f69f..1b0a51aef 100644
--- a/functions/index.js
+++ b/functions/index.js
@@ -374,11 +374,19 @@ function checkIfPB(uid, obj) {
});
}
+function stdDev(array) {
+ const n = array.length;
+ const mean = array.reduce((a, b) => a + b) / n;
+ return Math.sqrt(
+ array.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n
+ );
+}
+
exports.testCompleted = functions.https.onCall((request, response) => {
try {
if (request.uid === undefined || request.obj === undefined) {
console.error(`error saving result for ${request.uid} - missing input`);
- return { resultCode: -1 };
+ return { resultCode: -999 };
}
let obj = request.obj;
@@ -407,52 +415,105 @@ exports.testCompleted = functions.https.onCall((request, response) => {
return { resultCode: -1 };
}
+ let keySpacing = {
+ average:
+ obj.keySpacing.reduce((previous, current) => (current += previous)) /
+ obj.keySpacing.length,
+ sd: stdDev(obj.keySpacing),
+ };
+
+ let keyDuration = {
+ average:
+ obj.keyDuration.reduce((previous, current) => (current += previous)) /
+ obj.keyDuration.length,
+ sd: stdDev(obj.keyDuration),
+ };
+
return admin
.firestore()
- .collection(`users/${request.uid}/results`)
- .add(obj)
- .then((e) => {
- return Promise.all([
- checkLeaderboards(request.obj, "global"),
- checkLeaderboards(request.obj, "daily"),
- checkIfPB(request.uid, request.obj),
- ]).then((values) => {
- let globallb = values[0];
- let dailylb = values[1];
- let ispb = values[2];
- // console.log(values);
+ .collection("users")
+ .doc(request.uid)
+ .get()
+ .then((doc) => {
+ let docdata = doc.data();
+ let banned = docdata.banned === undefined ? false : docdata.banned;
+ let verified =
+ docdata.verified === undefined ? false : docdata.verified;
- let returnobj = {
- resultCode: null,
- globalLeaderboard: globallb,
- dailyLeaderboard: dailylb,
- };
- if (ispb) {
- console.log(
- `saved result for ${request.uid} (new PB) - ${JSON.stringify(
- request.obj
- )}`
+ //check keyspacing and duration here
+ if (!verified) {
+ if (
+ keySpacing.sd < 15 ||
+ keyDuration.sd < 15 ||
+ keyDuration.average < 15
+ ) {
+ console.error(
+ `possible bot detected by user ${request.uid} ${
+ docdata.name
+ } - ${JSON.stringify(keySpacing)} ${JSON.stringify(keyDuration)}`
);
- returnobj.resultCode = 2;
- } else {
- console.log(
- `saved result for ${request.uid} - ${JSON.stringify(request.obj)}`
- );
- returnobj.resultCode = 1;
+ return { resultCode: -2 };
}
- // console.log(returnobj);
- return returnobj;
- });
+ }
+
+ return admin
+ .firestore()
+ .collection(`users/${request.uid}/results`)
+ .add(obj)
+ .then((e) => {
+ return Promise.all([
+ checkLeaderboards(request.obj, "global", banned),
+ checkLeaderboards(request.obj, "daily", banned),
+ checkIfPB(request.uid, request.obj),
+ ]).then((values) => {
+ let globallb = values[0].insertedAt;
+ let dailylb = values[1].insertedAt;
+ let ispb = values[2];
+ // console.log(values);
+
+ let returnobj = {
+ resultCode: null,
+ globalLeaderboard: globallb,
+ dailyLeaderboard: dailylb,
+ lbBanned: banned,
+ };
+ request.obj.keySpacing = "removed";
+ request.obj.keyDuration = "removed";
+ if (ispb) {
+ console.log(
+ `saved result for ${request.uid} (new PB) - ${JSON.stringify(
+ request.obj
+ )}`
+ );
+ returnobj.resultCode = 2;
+ } else {
+ console.log(
+ `saved result for ${request.uid} - ${JSON.stringify(
+ request.obj
+ )}`
+ );
+ returnobj.resultCode = 1;
+ }
+ // console.log(returnobj);
+ return returnobj;
+ });
+ })
+ .catch((e) => {
+ console.error(
+ `error saving result when checking for PB / checking leaderboards for ${request.uid} - ${e.message}`
+ );
+ return { resultCode: -999 };
+ });
})
.catch((e) => {
console.error(
- `error saving result when checking for PB for ${request.uid} - ${e.message}`
+ `error saving result when getting user data for ${request.uid} - ${e.message}`
);
- return { resultCode: -1 };
+ return { resultCode: -999 };
});
} catch (e) {
console.error(`error saving result for ${request.uid} - ${e}`);
- return { resultCode: -1 };
+ return { resultCode: -999 };
}
});
@@ -819,8 +880,13 @@ class Leaderboard {
}
}
-async function checkLeaderboards(resultObj, type) {
+async function checkLeaderboards(resultObj, type, banned) {
try {
+ if (banned)
+ return {
+ insertedAt: null,
+ banned: true,
+ };
if (
((resultObj.mode === "words" &&
["10", "100"].includes(String(resultObj.mode2))) ||
@@ -913,7 +979,9 @@ async function checkLeaderboards(resultObj, type) {
// console.log("board is the same");
}
- return insertResult;
+ return {
+ insertedAt: insertResult,
+ };
}
}
} catch (e) {
@@ -946,8 +1014,6 @@ exports.getLeaderboard = functions.https.onCall((request, response) => {
.doc(lbdata.board[i].uid)
.get()
.then((doc) => {
- console.log(lbdata.board[i].uid);
- console.log(request.uid);
if (
lbdata.board[i].uid !== null &&
lbdata.board[i].uid === request.uid
diff --git a/public/js/script.js b/public/js/script.js
index 35136ec1a..ea79c8df5 100644
--- a/public/js/script.js
+++ b/public/js/script.js
@@ -916,6 +916,9 @@ function showResult(difficultyFailed = false) {
// 'margin-bottom': 0
});
+ $("#result .stats .leaderboards .bottom").text("");
+ $("#result .stats .leaderboards").addClass("hidden");
+
let mode2 = "";
if (config.mode === "time") {
mode2 = config.time;
@@ -1039,6 +1042,8 @@ function showResult(difficultyFailed = false) {
blindMode: config.blindMode,
theme: config.theme,
tags: activeTags,
+ keySpacing: keypressStats.spacing.array,
+ keyDuration: keypressStats.duration.array,
};
if (
config.difficulty == "normal" ||
@@ -1113,9 +1118,16 @@ function showResult(difficultyFailed = false) {
obj: completedEvent,
}).then((e) => {
accountIconLoading(false);
- // console.log(e.data);
+ console.log(e.data);
if (e.data.resultCode === -1) {
showNotification("Could not save result", 3000);
+ } else if (e.data.resultCode === -2) {
+ showNotification(
+ "Possible bot detected. Result not saved.",
+ 4000
+ );
+ } else if (e.data.resultCode === -999) {
+ showNotification("Internal error. Result not saved.", 4000);
} else if (e.data.resultCode === 1 || e.data.resultCode === 2) {
dbSnapshot.results.unshift(completedEvent);
try {
@@ -1191,14 +1203,19 @@ function showResult(difficultyFailed = false) {
}
if (
e.data.dailyLeaderboard === null &&
- e.data.globalLeaderboard === null
+ e.data.globalLeaderboard === null &&
+ e.data.lbBanned === false
) {
$("#result .stats .leaderboards").addClass("hidden");
} else {
$("#result .stats .leaderboards").removeClass("hidden");
- $("#result .stats .leaderboards .bottom").html(
- globalLbString + "
" + dailyLbString
- );
+ if (e.data.lbBanned) {
+ $("#result .stats .leaderboards .bottom").html("banned");
+ } else {
+ $("#result .stats .leaderboards .bottom").html(
+ globalLbString + "
" + dailyLbString
+ );
+ }
}
if (e.data.resultCode === 2) {