added bot commands, dailyLbWins

This commit is contained in:
lukew3 2021-06-03 14:05:35 -04:00
parent 6072bc4271
commit 047436311a
6 changed files with 196 additions and 36 deletions

View file

@ -0,0 +1,20 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const botCommandSchema = new Schema(
{
command: { type: String, required: true },
arguments: [{ type: Schema.Types.Mixed }],
executedTimestamp: { type: Date },
requestTimestamp: { type: Date },
executed: { type: Boolean, default: false },
status: { type: String },
},
{
timestamps: true,
}
);
const BotCommand = mongoose.model("BotCommand", botCommandSchema);
module.exports = { BotCommand };

View file

@ -17,6 +17,7 @@ const userSchema = new Schema(
},
name: { type: String, required: true },
uid: { type: String, required: true },
discordId: { type: String },
presets: [{ type: presetSchema, default: {} }],
tags: [{ type: tagSchema, default: {} }],
favouriteThemes: [],
@ -44,6 +45,10 @@ const userSchema = new Schema(
bananas: {
t60bananas: { type: Number, default: 0 },
},
dailyLbWins: {
time15: { type: Number },
time60: { type: Number },
},
},
{
timestamps: true,

View file

@ -7,14 +7,11 @@ Make sure that the branch is ready for deployment
- Add deploy script(s) to package.json
- Create a plan for apache/nginx server
- Api should be accessible via api.monkeytype.com
- Add helmet middleware to express api?
## Bugs
- Some methods in functions/index.js may be broken
- I think bot commands like lbUpdate and such
- Make sure discord can work
- Might just want to call the api from discord bot instead of firebase functions
- 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
### Minor/efficiency bugs
@ -28,7 +25,7 @@ Make sure that the branch is ready for deployment
- 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 get glb undefined error
- 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

View file

@ -6,6 +6,7 @@ const admin = require("firebase-admin");
const helmet = require("helmet");
const { User } = require("./models/user");
const { Leaderboard } = require("./models/leaderboard");
const { BotCommand } = require("./models/bot-command");
// Firebase admin setup
//currently uses account key in functions to prevent repetition
@ -14,6 +15,7 @@ const serviceAccount = require("../functions/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
// MIDDLEWARE & SETUP
const app = express();
app.use(cors());
@ -44,8 +46,24 @@ function clearDailyLeaderboards() {
setTimeout(() => {
Leaderboard.find({ type: "daily" }, (err, lbs) => {
lbs.forEach((lb) => {
lb.board = [];
lb.save();
User.findOne({ name: lb.board[0].name }, (err, user) => {
if (user) {
if (user.dailyLbWins === undefined) {
user.dailyLbWins = {
[lb.mode + lb.mode2]: 1,
};
} else if (user.dailyLbWins[lb.mode + lb.mode2] === undefined) {
user.dailyLbWins[lb.mode + lb.mode2] = 1;
} else {
user.dailyLbWins[lb.mode + lb.mode2]++;
}
user.save();
}
}).then(() => {
announceDailyLbResult(lb);
lb.board = [];
lb.save();
});
});
});
clearDailyLeaderboards();
@ -91,6 +109,36 @@ async function authenticateToken(req, res, next) {
// NON-ROUTE FUNCTIONS
function updateDiscordRole(discordId, wpm) {
newBotCommand = new BotCommand({
command: "updateRole",
arguments: [discordId, wpm],
executed: false,
requestTimestamp: Date.now(),
});
newBotCommand.save();
}
async function announceLbUpdate(discordId, pos, lb, wpm, raw, acc, con) {
newBotCommand = new BotCommand({
command: "sayLbUpdate",
arguments: [discordId, pos, lb, wpm, raw, acc, con],
executed: false,
requestTimestamp: Date.now(),
});
newBotCommand.save();
}
async function announceDailyLbResult(lbdata) {
newBotCommand = new BotCommand({
command: "announceDailyLbResult",
arguments: [lbdata],
executed: false,
requestTimestamp: Date.now(),
});
newBotCommand.save();
}
function validateResult(result) {
if (result.wpm > result.rawWpm) {
console.error(
@ -409,7 +457,6 @@ async function checkIfTagPB(obj, userdata) {
if (toUpdate) {
//push working pb array to user tags pbs
await User.findOne({ uid: userdata.uid }, (err, user) => {
//it might be more convenient if tags was an object with ids as the keys
for (let j = 0; j < user.tags.length; j++) {
if (user.tags[j]._id.toString() === dbtags[i]._id.toString()) {
user.tags[j].personalBests = pbs;
@ -612,27 +659,19 @@ app.post("/signUp", (req, res) => {
return;
});
app.post("/updateName", (req, res) => {
//this might be a put/patch request
//update the name of user with given uid
const uid = req.body.uid;
const name = req.body.name;
});
app.post("/passwordReset", (req, res) => {
const email = req.body.email;
//send email to the passed email requesting password reset
res.sendStatus(200);
app.post("/updateName", authenticateToken, (req, res) => {
User.findOne({ uid: req.uid }, (err, user) => {
user.name = req.body.name;
user.save();
});
res.status(200);
});
app.get("/fetchSnapshot", authenticateToken, (req, res) => {
/* Takes token and returns snap */
User.findOne({ uid: req.uid }, (err, user) => {
if (err) res.status(500).send({ error: err });
if (!user) res.status(200).send({ message: "No user found" }); //client doesn't do anything with this
//populate snap object with data from user document
let snap = user;
//return user data
res.send({ snap: snap });
return;
});
@ -1034,7 +1073,6 @@ app.post("/saveConfig", authenticateToken, (req, res) => {
User.findOne({ uid: req.uid }, (err, user) => {
if (err) res.status(500).send({ error: err });
user.config = obj;
//what does {merge: true} do in firebase
user.save();
})
.then(() => {
@ -1314,6 +1352,74 @@ app.post("/removeTag", authenticateToken, (req, res) => {
}
});
app.post("/verifyDiscord", authenticateToken, (req, res) => {
/* Not tested yet */
response.set("Access-Control-Allow-Origin", origin);
response.set("Access-Control-Allow-Headers", "*");
response.set("Access-Control-Allow-Credentials", "true");
if (request.method === "OPTIONS") {
// Send response to OPTIONS requests
response.set("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
response.set("Access-Control-Allow-Headers", "Authorization,Content-Type");
response.set("Access-Control-Max-Age", "3600");
response.status(204).send("");
return;
}
request = req.body.data;
if (request.uid == undefined) {
response.status(200).send({ status: -1, message: "Need to provide uid" });
return;
}
try {
return fetch("https://discord.com/api/users/@me", {
headers: {
authorization: `${request.tokenType} ${request.accessToken}`,
},
})
.then((res) => res.json())
.then(async (res2) => {
let did = res2.id;
User.findOne({ discordId: did }, (err, user) => {
if (user) {
res.status(200).send({
status: -1,
message:
"This Discord account is already paired to a different Monkeytype account",
});
return;
} else {
User.findOne({ uid: req.uid }, (err, user2) => {
user2.discordId = did;
user2.save();
newCommand = new BotCommand({
command: "verify",
arguments: [did, req.uid],
executed: false,
requestTimestamp: Date.now(),
});
newCommand.save();
res
.status(200)
.send({ status: 1, message: "Verified", did: did });
return;
});
}
});
})
.catch((e) => {
console.error(
"Something went wrong when trying to verify discord of user " +
e.message
);
response.status(200).send({ status: -1, message: e.message });
return;
});
} catch (e) {
response.status(200).send({ status: -1, message: e });
return;
}
});
app.post("/resetPersonalBests", authenticateToken, (req, res) => {
try {
User.findOne({ uid: req.uid }, (err, user) => {
@ -1437,13 +1543,41 @@ app.post("/attemptAddToLeaderboards", authenticateToken, (req, res) => {
lb.board.length < lb.size ||
result.wpm > lb.board.slice(-1)[0].wpm
) {
lb, (lbPosData = addToLeaderboard(lb, result, user.name)); //should uid be added instead of name?
lb, (lbPosData = addToLeaderboard(lb, result, user.name)); //should uid be added instead of name? //or together
console.log(user.lbMemory[lb.mode + lb.mode2][lb.type]);
//lbPosData.foundAt = user.lbMemory[lb.mode+lb.mode2][lb.type];
retData[lb.type] = lbPosData;
lb.save();
user.lbMemory[lb.mode + lb.mode2][lb.type] =
retData[lb.type].insertedAt;
//check if made global top 10 and send to discord if it did
if (lb.type == "global") {
let usr =
user.discordId != undefined ? user.discordId : user.name;
if (
retData.global !== null &&
retData.global.insertedAt >= 0 &&
retData.global.insertedAt <= 9 &&
retData.global.newBest
) {
let lbstring = `${result.mode} ${result.mode2} global`;
console.log(
`sending command to the bot to announce lb update ${usr} ${
retData.global.insertedAt + 1
} ${lbstring} ${result.wpm}`
);
announceLbUpdate(
usr,
retData.global.insertedAt + 1,
lbstring,
result.wpm,
result.rawWpm,
result.acc,
result.consistency
);
}
}
}
});
}

View file

@ -2,6 +2,7 @@ import * as CloudFunctions from "./cloud-functions";
import * as Notifications from "./notifications";
import * as Settings from "./settings";
import * as DB from "./db";
import axiosInstance from "./axios-instance";
export let data = null;
export function set(val) {
@ -11,13 +12,18 @@ export function set(val) {
export function verify(user) {
Notifications.add("Verifying", 0, 3);
data.uid = user.uid;
CloudFunctions.verifyUser(data).then((data) => {
if (data.data.status === 1) {
Notifications.add(data.data.message, 1);
DB.getSnapshot().discordId = data.data.did;
Settings.updateDiscordSection();
} else {
Notifications.add(data.data.message, -1);
}
});
axiosInstance
.post("/verifyDiscord", {
data: data,
})
.then((response) => {
if (response.data.status === 1) {
Notifications.add(response.data.message, 1);
DB.getSnapshot().discordId = response.data.did;
Settings.updateDiscordSection();
} else {
Notifications.add(response.data.message, -1);
}
});
}

View file

@ -6,9 +6,7 @@ import axiosInstance from "./axios-instance";
let dbSnapshot = null;
export function updateName(uid, name) {
//db.collection(`users`).doc(uid).set({ name: name }, { merge: true });
axiosInstance.post("/updateName", {
uid: uid,
name: name,
});
}