summary refs log tree commit diff
path: root/api
diff options
context:
space:
mode:
authorMaddy <ty.carlier@gmail.com>2021-10-13 16:07:33 +1100
committerMaddy <ty.carlier@gmail.com>2021-10-13 19:19:46 +1100
commitd3853a3d7af1ec2127cb23eb6211c598347ec0be (patch)
treea83a770d7ee1fe5c38f5ef46f8ed35a881024b38 /api
parentMerge pull request #443 from Mr2u/master (diff)
downloadserver-d3853a3d7af1ec2127cb23eb6211c598347ec0be.tar.xz
added route /guilds/:id/prune GET count and POST prune.
Edited Member entity to include last_message_id to avoid searching for every members last message when pruning
When a message is sent through /channels/:id/messages last_message_id of sending Member is updated.
Diffstat (limited to 'api')
-rw-r--r--api/src/routes/channels/#channel_id/messages/index.ts10
-rw-r--r--api/src/routes/guilds/#guild_id/prune.ts74
2 files changed, 83 insertions, 1 deletions
diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts
index 26bb9e5d..399fa7bb 100644
--- a/api/src/routes/channels/#channel_id/messages/index.ts
+++ b/api/src/routes/channels/#channel_id/messages/index.ts
@@ -10,7 +10,8 @@ import {
 	getPermission,
 	Message,
 	MessageCreateEvent,
-	uploadFile
+	uploadFile,
+	Member,
 } from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { handleMessage, postHandleMessage, route } from "@fosscord/api";
@@ -188,6 +189,13 @@ router.post(
 
 		await channel.assign({ last_message_id: message.id }).save();
 
+		//gosh
+		var member = await Member.findOneOrFail({
+			where: { id: req.user_id },
+		});
+		await member.assign({ last_message_id: message.id })
+		await member.save();	//why does member.assign here return void?
+
 		if (channel.isDm()) {
 			const channel_dto = await DmChannelDTO.from(channel);
 
diff --git a/api/src/routes/guilds/#guild_id/prune.ts b/api/src/routes/guilds/#guild_id/prune.ts
new file mode 100644
index 00000000..cddbcee7
--- /dev/null
+++ b/api/src/routes/guilds/#guild_id/prune.ts
@@ -0,0 +1,74 @@
+import { Router, Request, Response } from "express";

+import { Guild, Member, Snowflake } from "@fosscord/util";

+import { HTTPError } from "lambert-server";

+import { LessThan } from "typeorm";

+import { route } from "@fosscord/api";

+const router = Router();

+

+//Returns all inactive members, respecting role hierarchy

+export const inactiveMembers = async (guild_id: string, user_id: string, days: number, roles: string[] = []) => {

+	var date = new Date();

+	date.setDate(date.getDate() - days);

+	//Snowflake should have `generateFromTime` method? Or similar?

+	var minId = BigInt(date.valueOf() - Snowflake.EPOCH) << BigInt(22);

+

+	var members = await Member.find({

+		where: [

+			{

+				guild_id,

+				last_message_id: LessThan(minId.toString()),

+			},

+		],

+		relations: ["roles"]

+	});

+	if (!members.length) return [];

+

+	//I'm sure I can do this in the above db query ( and it would probably be better to do so ), but oh well.

+	if (roles.length && members.length)

+		members = members.filter(user => user.roles?.some(role => roles.includes(role.id)));

+

+	const me = await Member.findOne({ id: user_id, guild_id }, { relations: ["roles"] });

+	if (!me) throw new HTTPError("You are not member of this guild", 403);

+	const myHighestRole = Math.max(...(me.roles?.map(x => x.position) || []))

+

+	const guild = await Guild.findOneOrFail({ where: { id: guild_id } })

+

+	members = members.filter(member =>

+		member.id !== guild.owner_id &&			//can't kick owner

+		member.roles?.some(role =>

+			role.position < myHighestRole ||	//roles higher than me can't be kicked

+			me.id === guild.owner_id			//owner can kick anyone

+		)

+	)

+

+	return members;

+}

+

+router.get("/", async (req: Request, res: Response) => {

+	const days = parseInt(req.query.days as string);

+

+	var roles = req.query.include_roles;

+	if (typeof roles === 'string') roles = [roles]; //express will return array otherwise

+

+	const members = await inactiveMembers(req.params.guild_id, req.user_id, days, roles as string[]);

+

+	res.send({ pruned: members.length });

+});

+

+router.post("/", route({ permission: "KICK_MEMBERS" }), async (req: Request, res: Response) => {

+	const days = parseInt(req.body.days);

+

+	var roles = req.query.include_roles;

+	if (typeof roles === 'string') roles = [roles];

+

+	const { guild_id } = req.params;

+	const members = await inactiveMembers(guild_id, req.user_id, days, roles as string[]);

+

+	for (var curr of members) {

+		await Member.removeFromGuild(curr.id, guild_id);

+	}

+

+	res.send({ purged: members.length });

+})

+

+export default router;