1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-11 05:02:37 +01:00

Reactions

This commit is contained in:
Flam3rboy 2021-05-22 17:40:28 +02:00
parent f72156a40d
commit 159ff75944
6 changed files with 186 additions and 184422 deletions

File diff suppressed because it is too large Load Diff

30
package-lock.json generated
View File

@ -10,7 +10,7 @@
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"@fosscord/server-util": "^1.2.2",
"@fosscord/server-util": "^1.2.4",
"@types/jest": "^26.0.22",
"bcrypt": "^5.0.1",
"body-parser": "^1.19.0",
@ -18,12 +18,12 @@
"express": "^4.17.1",
"express-validator": "^6.9.2",
"i18next": "^19.8.5",
"i18next-http-middleware": "^3.1.1",
"i18next-http-middleware": "^3.1.3",
"i18next-node-fs-backend": "^2.1.3",
"jsonwebtoken": "^8.5.1",
"lambert-server": "^1.2.2",
"missing-native-js-functions": "^1.2.6",
"mongodb": "^3.6.4",
"mongodb": "^3.6.5",
"mongoose": "^5.12.3",
"mongoose-autopopulate": "^0.12.3",
"mongoose-long": "^0.3.2",
@ -493,9 +493,9 @@
}
},
"node_modules/@fosscord/server-util": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@fosscord/server-util/-/server-util-1.2.2.tgz",
"integrity": "sha512-0lcOnN+I+6VXdY118lVsnj539cGv6XElChzzk9W/50sqBI3VZVq2w80GYJdcrT1POrpxJlEEJNxkNYRGGaYpeA==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@fosscord/server-util/-/server-util-1.2.4.tgz",
"integrity": "sha512-szj/JQBYtAjWbagv+T5YbvqzmtHl6C+WyE3us4fHtF7aIuFu0sV4HsPCHKxF9e64sUn/ayn/5nOzFJWKYjFEaA==",
"dependencies": {
"@types/jsonwebtoken": "^8.5.0",
"@types/mongoose-autopopulate": "^0.10.1",
@ -5100,9 +5100,9 @@
}
},
"node_modules/i18next-http-middleware": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.1.tgz",
"integrity": "sha512-MKS2+iac5qO/95tvlpOGDtqWOqp4bOEBHLoqZNS6wQBO7fu/rd2G7IO3R+Vq0xahXkH/Jh/UoG+vHSko2VitYw=="
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.3.tgz",
"integrity": "sha512-IVj5w2tJcnSZ3ZG8L/ylFdy/VD1tiyCG798Lw3mFI7hVxMOa0t1eTSzbk48ruS+EqDePQRc/18nzgg+Jbekdmw=="
},
"node_modules/i18next-node-fs-backend": {
"version": "2.1.3",
@ -12549,9 +12549,9 @@
}
},
"@fosscord/server-util": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@fosscord/server-util/-/server-util-1.2.2.tgz",
"integrity": "sha512-0lcOnN+I+6VXdY118lVsnj539cGv6XElChzzk9W/50sqBI3VZVq2w80GYJdcrT1POrpxJlEEJNxkNYRGGaYpeA==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@fosscord/server-util/-/server-util-1.2.4.tgz",
"integrity": "sha512-szj/JQBYtAjWbagv+T5YbvqzmtHl6C+WyE3us4fHtF7aIuFu0sV4HsPCHKxF9e64sUn/ayn/5nOzFJWKYjFEaA==",
"requires": {
"@types/jsonwebtoken": "^8.5.0",
"@types/mongoose-autopopulate": "^0.10.1",
@ -16469,9 +16469,9 @@
}
},
"i18next-http-middleware": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.1.tgz",
"integrity": "sha512-MKS2+iac5qO/95tvlpOGDtqWOqp4bOEBHLoqZNS6wQBO7fu/rd2G7IO3R+Vq0xahXkH/Jh/UoG+vHSko2VitYw=="
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.3.tgz",
"integrity": "sha512-IVj5w2tJcnSZ3ZG8L/ylFdy/VD1tiyCG798Lw3mFI7hVxMOa0t1eTSzbk48ruS+EqDePQRc/18nzgg+Jbekdmw=="
},
"i18next-node-fs-backend": {
"version": "2.1.3",

View File

@ -30,7 +30,7 @@
},
"homepage": "https://github.com/fosscord/fosscord-api#readme",
"dependencies": {
"@fosscord/server-util": "^1.2.2",
"@fosscord/server-util": "^1.2.4",
"@types/jest": "^26.0.22",
"bcrypt": "^5.0.1",
"body-parser": "^1.19.0",
@ -38,12 +38,12 @@
"express": "^4.17.1",
"express-validator": "^6.9.2",
"i18next": "^19.8.5",
"i18next-http-middleware": "^3.1.1",
"i18next-http-middleware": "^3.1.3",
"i18next-node-fs-backend": "^2.1.3",
"jsonwebtoken": "^8.5.1",
"lambert-server": "^1.2.2",
"missing-native-js-functions": "^1.2.6",
"mongodb": "^3.6.4",
"mongodb": "^3.6.5",
"mongoose": "^5.12.3",
"mongoose-autopopulate": "^0.12.3",
"mongoose-long": "^0.3.2",

View File

@ -37,13 +37,17 @@ export class FosscordServer extends Server {
}
async setupSchema() {
await db.collection("users").createIndex({ id: 1 }, { unique: true });
await db.collection("messages").createIndex({ id: 1 }, { unique: true });
await db.collection("channels").createIndex({ id: 1 }, { unique: true });
await db.collection("guilds").createIndex({ id: 1 }, { unique: true });
await db.collection("members").createIndex({ id: 1, guild_id: 1 }, { unique: true });
await db.collection("roles").createIndex({ id: 1 }, { unique: true });
await db.collection("emojis").createIndex({ id: 1 }, { unique: true });
return Promise.all([
db.collection("users").createIndex({ id: 1 }, { unique: true }),
db.collection("messages").createIndex({ id: 1 }, { unique: true }),
db.collection("channels").createIndex({ id: 1 }, { unique: true }),
db.collection("guilds").createIndex({ id: 1 }, { unique: true }),
db.collection("members").createIndex({ id: 1, guild_id: 1 }, { unique: true }),
db.collection("roles").createIndex({ id: 1 }, { unique: true }),
db.collection("emojis").createIndex({ id: 1 }, { unique: true }),
db.collection("invites").createIndex({ code: 1 }, { unique: true }),
db.collection("invites").createIndex({ expires_at: 1 }, { expireAfterSeconds: 0 }) // after 0 seconds of expires_at the invite will get delete
]);
}
async start() {
@ -70,9 +74,9 @@ export class FosscordServer extends Server {
fallbackLng: "en",
ns,
backend: {
loadPath: __dirname + "/../locales/{{lng}}/{{ns}}.json",
loadPath: __dirname + "/../locales/{{lng}}/{{ns}}.json"
},
load: "all",
load: "all"
});
this.app.use(i18nextMiddleware.handle(i18next, {}));
@ -92,8 +96,8 @@ export class FosscordServer extends Server {
const response = await fetch(`https://discord.com/assets/${req.params.file}`, {
// @ts-ignore
headers: {
...req.headers,
},
...req.headers
}
});
const buffer = await response.buffer();
@ -107,7 +111,7 @@ export class FosscordServer extends Server {
"transfer-encoding",
"expect-ct",
"access-control-allow-origin",
"content-encoding",
"content-encoding"
].includes(name.toLowerCase())
) {
return;

View File

@ -1,6 +1,144 @@
import { Router } from "express";
import {
ChannelModel,
EmojiModel,
getPermission,
MemberModel,
MessageModel,
MessageReactionAddEvent,
MessageReactionRemoveEvent,
PartialEmoji,
PublicUserProjection,
toObject,
UserModel
} from "@fosscord/server-util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../../../util/Event";
const router = Router();
// TODO:
// TODO: check if emoji is really an unicode emoji or a prperly encoded external emoji
function getEmoji(emoji: string): PartialEmoji {
emoji = decodeURIComponent(emoji);
const parts = emoji.includes(":") && emoji.split(":");
if (parts)
return {
name: parts[0],
id: parts[1]
};
return {
id: undefined,
name: emoji
};
}
router.get("/:emoji", async (req, res) => {
const { message_id, channel_id } = req.params;
const emoji = getEmoji(req.params.emoji);
const message = await MessageModel.findOne({ id: message_id, channel_id }).exec();
if (!message) throw new HTTPError("Message not found", 404);
const reaction = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
if (!reaction) throw new HTTPError("Reaction not found", 404);
const permissions = await getPermission(req.user_id, undefined, channel_id);
permissions.hasThrow("VIEW_CHANNEL");
const users = await UserModel.find({ id: { $in: reaction.user_ids } }, PublicUserProjection).exec();
res.json(toObject(users));
});
router.put("/:emoji/:user_id", async (req, res) => {
const { message_id, channel_id, user_id } = req.params;
if (user_id !== "@me") throw new HTTPError("Invalid user");
const emoji = getEmoji(req.params.emoji);
const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec();
if (!channel) throw new HTTPError("Channel not found", 404);
const message = await MessageModel.findOne({ id: message_id, channel_id }).exec();
if (!message) throw new HTTPError("Message not found", 404);
const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
const permissions = await getPermission(req.user_id, undefined, channel_id);
permissions.hasThrow("READ_MESSAGE_HISTORY");
if (!already_added) permissions.hasThrow("ADD_REACTIONS");
if (emoji.id) {
const external_emoji = await EmojiModel.findOne({ id: emoji.id }).exec();
if (!external_emoji) throw new HTTPError("Emoji not found", 404);
if (!already_added) permissions.hasThrow("USE_EXTERNAL_EMOJIS");
emoji.animated = external_emoji.animated;
emoji.name = external_emoji.name;
}
if (already_added) {
if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
already_added.count++;
} else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] });
await MessageModel.updateOne({ id: message_id, channel_id }, message).exec();
const member = channel.guild_id && (await MemberModel.findOne({ id: req.user_id }).exec());
await emitEvent({
event: "MESSAGE_REACTION_ADD",
channel_id,
guild_id: channel.guild_id,
data: {
user_id: req.user_id,
channel_id,
message_id,
guild_id: channel.guild_id,
emoji,
member
}
} as MessageReactionAddEvent);
res.sendStatus(204);
});
router.delete("/:emoji/:user_id", async (req, res) => {
var { message_id, channel_id, user_id } = req.params;
const emoji = getEmoji(req.params.emoji);
const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec();
if (!channel) throw new HTTPError("Channel not found", 404);
const message = await MessageModel.findOne({ id: message_id, channel_id }).exec();
if (!message) throw new HTTPError("Message not found", 404);
const permissions = await getPermission(req.user_id, undefined, channel_id);
if (user_id === "@me") user_id = req.user_id;
else permissions.hasThrow("MANAGE_MESSAGES");
const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
if (!already_added || !already_added.user_ids.includes(user_id)) throw new HTTPError("Reaction not found", 404);
already_added.count--;
if (already_added.count <= 0) message.reactions.remove(already_added);
await MessageModel.updateOne({ id: message_id, channel_id }, message).exec();
await emitEvent({
event: "MESSAGE_REACTION_REMOVE",
channel_id,
guild_id: channel.guild_id,
data: {
user_id: req.user_id,
channel_id,
message_id,
guild_id: channel.guild_id,
emoji
}
} as MessageReactionRemoveEvent);
res.sendStatus(204);
});
export default router;

View File

@ -76,7 +76,18 @@ router.get("/", async (req, res) => {
const messages = await query.limit(limit).exec();
return res.json(toObject(messages));
return res.json(
toObject(messages).map((x) => {
(x.reactions || []).forEach((x) => {
// @ts-ignore
if ((x.user_ids || []).includes(req.user_id)) x.me = true;
// @ts-ignore
delete x.user_ids;
});
return x;
})
);
});
// TODO: config max upload size