mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-07 11:22:35 +01:00
✨ Reactions
This commit is contained in:
parent
f72156a40d
commit
159ff75944
184389
assets/passwords.txt
184389
assets/passwords.txt
File diff suppressed because it is too large
Load Diff
30
package-lock.json
generated
30
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user