guilds
This commit is contained in:
parent
10e1eb95ae
commit
c2ce88dee7
3071
assets/openapi.json
3071
assets/openapi.json
File diff suppressed because it is too large
Load Diff
93495
assets/schemas.json
93495
assets/schemas.json
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,17 @@ const router: Router = Router();
|
|||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
route({ permission: "BAN_MEMBERS" }),
|
route({
|
||||||
|
permission: "BAN_MEMBERS",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildBansResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
@ -73,7 +83,20 @@ router.get(
|
|||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:user",
|
"/:user",
|
||||||
route({ permission: "BAN_MEMBERS" }),
|
route({
|
||||||
|
permission: "BAN_MEMBERS",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "BanModeratorSchema",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const user_id = req.params.ban;
|
const user_id = req.params.ban;
|
||||||
@ -97,7 +120,21 @@ router.get(
|
|||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/:user_id",
|
"/:user_id",
|
||||||
route({ requestBody: "BanCreateSchema", permission: "BAN_MEMBERS" }),
|
route({
|
||||||
|
requestBody: "BanCreateSchema",
|
||||||
|
permission: "BAN_MEMBERS",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Ban",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const banned_user_id = req.params.user_id;
|
const banned_user_id = req.params.user_id;
|
||||||
@ -143,7 +180,20 @@ router.put(
|
|||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/@me",
|
"/@me",
|
||||||
route({ requestBody: "BanCreateSchema" }),
|
route({
|
||||||
|
requestBody: "BanCreateSchema",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Ban",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
@ -182,7 +232,18 @@ router.put(
|
|||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:user_id",
|
"/:user_id",
|
||||||
route({ permission: "BAN_MEMBERS" }),
|
route({
|
||||||
|
permission: "BAN_MEMBERS",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id, user_id } = req.params;
|
const { guild_id, user_id } = req.params;
|
||||||
|
|
||||||
|
@ -28,18 +28,39 @@ import { Request, Response, Router } from "express";
|
|||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
const channels = await Channel.find({ where: { guild_id } });
|
route({
|
||||||
|
responses: {
|
||||||
|
201: {
|
||||||
|
body: "GuildChannelsResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
const channels = await Channel.find({ where: { guild_id } });
|
||||||
|
|
||||||
res.json(channels);
|
res.json(channels);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
route({
|
route({
|
||||||
requestBody: "ChannelModifySchema",
|
requestBody: "ChannelModifySchema",
|
||||||
permission: "MANAGE_CHANNELS",
|
permission: "MANAGE_CHANNELS",
|
||||||
|
responses: {
|
||||||
|
201: {
|
||||||
|
body: "Channel",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
// creates a new guild channel https://discord.com/developers/docs/resources/guild#create-guild-channel
|
// creates a new guild channel https://discord.com/developers/docs/resources/guild#create-guild-channel
|
||||||
@ -60,6 +81,15 @@ router.patch(
|
|||||||
route({
|
route({
|
||||||
requestBody: "ChannelReorderSchema",
|
requestBody: "ChannelReorderSchema",
|
||||||
permission: "MANAGE_CHANNELS",
|
permission: "MANAGE_CHANNELS",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
// changes guild channel position
|
// changes guild channel position
|
||||||
|
@ -16,37 +16,51 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { emitEvent, GuildDeleteEvent, Guild } from "@spacebar/util";
|
|
||||||
import { Router, Request, Response } from "express";
|
|
||||||
import { HTTPError } from "lambert-server";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
|
import { Guild, GuildDeleteEvent, emitEvent } from "@spacebar/util";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
|
import { HTTPError } from "lambert-server";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
// discord prefixes this route with /delete instead of using the delete method
|
// discord prefixes this route with /delete instead of using the delete method
|
||||||
// docs are wrong https://discord.com/developers/docs/resources/guild#delete-guild
|
// docs are wrong https://discord.com/developers/docs/resources/guild#delete-guild
|
||||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
router.post(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
|
route({
|
||||||
const guild = await Guild.findOneOrFail({
|
responses: {
|
||||||
where: { id: guild_id },
|
204: {},
|
||||||
select: ["owner_id"],
|
401: {
|
||||||
});
|
body: "APIErrorResponse",
|
||||||
if (guild.owner_id !== req.user_id)
|
|
||||||
throw new HTTPError("You are not the owner of this guild", 401);
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
Guild.delete({ id: guild_id }), // this will also delete all guild related data
|
|
||||||
emitEvent({
|
|
||||||
event: "GUILD_DELETE",
|
|
||||||
data: {
|
|
||||||
id: guild_id,
|
|
||||||
},
|
},
|
||||||
guild_id: guild_id,
|
404: {
|
||||||
} as GuildDeleteEvent),
|
body: "APIErrorResponse",
|
||||||
]);
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
return res.sendStatus(204);
|
const guild = await Guild.findOneOrFail({
|
||||||
});
|
where: { id: guild_id },
|
||||||
|
select: ["owner_id"],
|
||||||
|
});
|
||||||
|
if (guild.owner_id !== req.user_id)
|
||||||
|
throw new HTTPError("You are not the owner of this guild", 401);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
Guild.delete({ id: guild_id }), // this will also delete all guild related data
|
||||||
|
emitEvent({
|
||||||
|
event: "GUILD_DELETE",
|
||||||
|
data: {
|
||||||
|
id: guild_id,
|
||||||
|
},
|
||||||
|
guild_id: guild_id,
|
||||||
|
} as GuildDeleteEvent),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return res.sendStatus(204);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -16,40 +16,50 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Router, Request, Response } from "express";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
// TODO:
|
route({
|
||||||
// Load from database
|
responses: {
|
||||||
// Admin control, but for now it allows anyone to be discoverable
|
200: {
|
||||||
|
body: "GuildDiscoveryRequirements",
|
||||||
res.send({
|
},
|
||||||
guild_id: guild_id,
|
|
||||||
safe_environment: true,
|
|
||||||
healthy: true,
|
|
||||||
health_score_pending: false,
|
|
||||||
size: true,
|
|
||||||
nsfw_properties: {},
|
|
||||||
protected: true,
|
|
||||||
sufficient: true,
|
|
||||||
sufficient_without_grace_period: true,
|
|
||||||
valid_rules_channel: true,
|
|
||||||
retention_healthy: true,
|
|
||||||
engagement_healthy: true,
|
|
||||||
age: true,
|
|
||||||
minimum_age: 0,
|
|
||||||
health_score: {
|
|
||||||
avg_nonnew_participators: 0,
|
|
||||||
avg_nonnew_communicators: 0,
|
|
||||||
num_intentful_joiners: 0,
|
|
||||||
perc_ret_w1_intentful: 0,
|
|
||||||
},
|
},
|
||||||
minimum_size: 0,
|
}),
|
||||||
});
|
async (req: Request, res: Response) => {
|
||||||
});
|
const { guild_id } = req.params;
|
||||||
|
// TODO:
|
||||||
|
// Load from database
|
||||||
|
// Admin control, but for now it allows anyone to be discoverable
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
guild_id: guild_id,
|
||||||
|
safe_environment: true,
|
||||||
|
healthy: true,
|
||||||
|
health_score_pending: false,
|
||||||
|
size: true,
|
||||||
|
nsfw_properties: {},
|
||||||
|
protected: true,
|
||||||
|
sufficient: true,
|
||||||
|
sufficient_without_grace_period: true,
|
||||||
|
valid_rules_channel: true,
|
||||||
|
retention_healthy: true,
|
||||||
|
engagement_healthy: true,
|
||||||
|
age: true,
|
||||||
|
minimum_age: 0,
|
||||||
|
health_score: {
|
||||||
|
avg_nonnew_participators: 0,
|
||||||
|
avg_nonnew_communicators: 0,
|
||||||
|
num_intentful_joiners: 0,
|
||||||
|
perc_ret_w1_intentful: 0,
|
||||||
|
},
|
||||||
|
minimum_size: 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -34,37 +34,77 @@ import { Request, Response, Router } from "express";
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildEmojisResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
const emojis = await Emoji.find({
|
const emojis = await Emoji.find({
|
||||||
where: { guild_id: guild_id },
|
where: { guild_id: guild_id },
|
||||||
relations: ["user"],
|
relations: ["user"],
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.json(emojis);
|
return res.json(emojis);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.get("/:emoji_id", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id, emoji_id } = req.params;
|
"/:emoji_id",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Emoji",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id, emoji_id } = req.params;
|
||||||
|
|
||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
const emoji = await Emoji.findOneOrFail({
|
const emoji = await Emoji.findOneOrFail({
|
||||||
where: { guild_id: guild_id, id: emoji_id },
|
where: { guild_id: guild_id, id: emoji_id },
|
||||||
relations: ["user"],
|
relations: ["user"],
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.json(emoji);
|
return res.json(emoji);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
route({
|
route({
|
||||||
requestBody: "EmojiCreateSchema",
|
requestBody: "EmojiCreateSchema",
|
||||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||||
|
responses: {
|
||||||
|
201: {
|
||||||
|
body: "Emoji",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
@ -115,6 +155,14 @@ router.patch(
|
|||||||
route({
|
route({
|
||||||
requestBody: "EmojiModifySchema",
|
requestBody: "EmojiModifySchema",
|
||||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Emoji",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { emoji_id, guild_id } = req.params;
|
const { emoji_id, guild_id } = req.params;
|
||||||
@ -141,7 +189,15 @@ router.patch(
|
|||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:emoji_id",
|
"/:emoji_id",
|
||||||
route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }),
|
route({
|
||||||
|
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { emoji_id, guild_id } = req.params;
|
const { emoji_id, guild_id } = req.params;
|
||||||
|
|
||||||
|
@ -34,28 +34,61 @@ import { HTTPError } from "lambert-server";
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
body: "GuildResponse",
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
const [guild, member] = await Promise.all([
|
const [guild, member] = await Promise.all([
|
||||||
Guild.findOneOrFail({ where: { id: guild_id } }),
|
Guild.findOneOrFail({ where: { id: guild_id } }),
|
||||||
Member.findOne({ where: { guild_id: guild_id, id: req.user_id } }),
|
Member.findOne({ where: { guild_id: guild_id, id: req.user_id } }),
|
||||||
]);
|
]);
|
||||||
if (!member)
|
if (!member)
|
||||||
throw new HTTPError(
|
throw new HTTPError(
|
||||||
"You are not a member of the guild you are trying to access",
|
"You are not a member of the guild you are trying to access",
|
||||||
401,
|
401,
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.send({
|
return res.send({
|
||||||
...guild,
|
...guild,
|
||||||
joined_at: member?.joined_at,
|
joined_at: member?.joined_at,
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "GuildUpdateSchema", permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
requestBody: "GuildUpdateSchema",
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
body: "GuildUpdateSchema",
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const body = req.body as GuildUpdateSchema;
|
const body = req.body as GuildUpdateSchema;
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
|
@ -16,15 +16,22 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Invite, PublicInviteRelation } from "@spacebar/util";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
|
import { Invite, PublicInviteRelation } from "@spacebar/util";
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
route({ permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildInvitesResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
|
@ -16,17 +16,27 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Router, Request, Response } from "express";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
// TODO: member verification
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
// TODO: member verification
|
||||||
|
|
||||||
res.status(404).json({
|
res.status(404).json({
|
||||||
message: "Unknown Guild Member Verification Form",
|
message: "Unknown Guild Member Verification Form",
|
||||||
code: 10068,
|
code: 10068,
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -34,20 +34,52 @@ import { Request, Response, Router } from "express";
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id, member_id } = req.params;
|
"/",
|
||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Member",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id, member_id } = req.params;
|
||||||
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
const member = await Member.findOneOrFail({
|
const member = await Member.findOneOrFail({
|
||||||
where: { id: member_id, guild_id },
|
where: { id: member_id, guild_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.json(member);
|
return res.json(member);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "MemberChangeSchema" }),
|
route({
|
||||||
|
requestBody: "MemberChangeSchema",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Member",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const member_id =
|
const member_id =
|
||||||
@ -119,54 +151,81 @@ router.patch(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put("/", route({}), async (req: Request, res: Response) => {
|
router.put(
|
||||||
// TODO: Lurker mode
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "MemberJoinGuildResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
// TODO: Lurker mode
|
||||||
|
|
||||||
const rights = await getRights(req.user_id);
|
const rights = await getRights(req.user_id);
|
||||||
|
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
let { member_id } = req.params;
|
let { member_id } = req.params;
|
||||||
if (member_id === "@me") {
|
if (member_id === "@me") {
|
||||||
member_id = req.user_id;
|
member_id = req.user_id;
|
||||||
rights.hasThrow("JOIN_GUILDS");
|
rights.hasThrow("JOIN_GUILDS");
|
||||||
} else {
|
} else {
|
||||||
// TODO: join others by controller
|
// TODO: join others by controller
|
||||||
}
|
}
|
||||||
|
|
||||||
const guild = await Guild.findOneOrFail({
|
const guild = await Guild.findOneOrFail({
|
||||||
where: { id: guild_id },
|
where: { id: guild_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
const emoji = await Emoji.find({
|
const emoji = await Emoji.find({
|
||||||
where: { guild_id: guild_id },
|
where: { guild_id: guild_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
const roles = await Role.find({
|
const roles = await Role.find({
|
||||||
where: { guild_id: guild_id },
|
where: { guild_id: guild_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
const stickers = await Sticker.find({
|
const stickers = await Sticker.find({
|
||||||
where: { guild_id: guild_id },
|
where: { guild_id: guild_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
await Member.addToGuild(member_id, guild_id);
|
await Member.addToGuild(member_id, guild_id);
|
||||||
res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers });
|
res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers });
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.delete("/", route({}), async (req: Request, res: Response) => {
|
router.delete(
|
||||||
const { guild_id, member_id } = req.params;
|
"/",
|
||||||
const permission = await getPermission(req.user_id, guild_id);
|
route({
|
||||||
const rights = await getRights(req.user_id);
|
responses: {
|
||||||
if (member_id === "@me" || member_id === req.user_id) {
|
204: {},
|
||||||
// TODO: unless force-joined
|
403: {
|
||||||
rights.hasThrow("SELF_LEAVE_GROUPS");
|
body: "APIErrorResponse",
|
||||||
} else {
|
},
|
||||||
rights.hasThrow("KICK_BAN_MEMBERS");
|
},
|
||||||
permission.hasThrow("KICK_MEMBERS");
|
}),
|
||||||
}
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id, member_id } = req.params;
|
||||||
|
const permission = await getPermission(req.user_id, guild_id);
|
||||||
|
const rights = await getRights(req.user_id);
|
||||||
|
if (member_id === "@me" || member_id === req.user_id) {
|
||||||
|
// TODO: unless force-joined
|
||||||
|
rights.hasThrow("SELF_LEAVE_GROUPS");
|
||||||
|
} else {
|
||||||
|
rights.hasThrow("KICK_BAN_MEMBERS");
|
||||||
|
permission.hasThrow("KICK_MEMBERS");
|
||||||
|
}
|
||||||
|
|
||||||
await Member.removeFromGuild(member_id, guild_id);
|
await Member.removeFromGuild(member_id, guild_id);
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -24,7 +24,18 @@ const router = Router();
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "MemberNickChangeSchema" }),
|
route({
|
||||||
|
requestBody: "MemberNickChangeSchema",
|
||||||
|
responses: {
|
||||||
|
200: {},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
let permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
|
let permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
|
||||||
|
@ -16,15 +16,23 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Member } from "@spacebar/util";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
|
import { Member } from "@spacebar/util";
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/",
|
"/",
|
||||||
route({ permission: "MANAGE_ROLES" }),
|
route({
|
||||||
|
permission: "MANAGE_ROLES",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id, role_id, member_id } = req.params;
|
const { guild_id, role_id, member_id } = req.params;
|
||||||
|
|
||||||
@ -35,7 +43,13 @@ router.delete(
|
|||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/",
|
"/",
|
||||||
route({ permission: "MANAGE_ROLES" }),
|
route({
|
||||||
|
permission: "MANAGE_ROLES",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
403: {},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id, role_id, member_id } = req.params;
|
const { guild_id, role_id, member_id } = req.params;
|
||||||
|
|
||||||
|
@ -16,35 +16,58 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Request, Response, Router } from "express";
|
|
||||||
import { Member, PublicMemberProjection } from "@spacebar/util";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import { MoreThan } from "typeorm";
|
import { Member, PublicMemberProjection } from "@spacebar/util";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
|
import { MoreThan } from "typeorm";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
// TODO: send over websocket
|
// TODO: send over websocket
|
||||||
// TODO: check for GUILD_MEMBERS intent
|
// TODO: check for GUILD_MEMBERS intent
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
const limit = Number(req.query.limit) || 1;
|
route({
|
||||||
if (limit > 1000 || limit < 1)
|
query: {
|
||||||
throw new HTTPError("Limit must be between 1 and 1000");
|
limit: {
|
||||||
const after = `${req.query.after}`;
|
type: "number",
|
||||||
const query = after ? { id: MoreThan(after) } : {};
|
description:
|
||||||
|
"max number of members to return (1-1000). default 1",
|
||||||
|
},
|
||||||
|
after: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildMembersResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
const limit = Number(req.query.limit) || 1;
|
||||||
|
if (limit > 1000 || limit < 1)
|
||||||
|
throw new HTTPError("Limit must be between 1 and 1000");
|
||||||
|
const after = `${req.query.after}`;
|
||||||
|
const query = after ? { id: MoreThan(after) } : {};
|
||||||
|
|
||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
const members = await Member.find({
|
const members = await Member.find({
|
||||||
where: { guild_id, ...query },
|
where: { guild_id, ...query },
|
||||||
select: PublicMemberProjection,
|
select: PublicMemberProjection,
|
||||||
take: limit,
|
take: limit,
|
||||||
order: { id: "ASC" },
|
order: { id: "ASC" },
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.json(members);
|
return res.json(members);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -18,140 +18,159 @@
|
|||||||
|
|
||||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
|
|
||||||
import { Request, Response, Router } from "express";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import { getPermission, FieldErrors, Message, Channel } from "@spacebar/util";
|
import { Channel, FieldErrors, Message, getPermission } from "@spacebar/util";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { FindManyOptions, In, Like } from "typeorm";
|
import { FindManyOptions, In, Like } from "typeorm";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const {
|
"/",
|
||||||
channel_id,
|
route({
|
||||||
content,
|
responses: {
|
||||||
// include_nsfw, // TODO
|
200: {
|
||||||
offset,
|
body: "GuildMessagesSearchResponse",
|
||||||
sort_order,
|
},
|
||||||
// sort_by, // TODO: Handle 'relevance'
|
403: {
|
||||||
limit,
|
body: "APIErrorResponse",
|
||||||
author_id,
|
},
|
||||||
} = req.query;
|
422: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
const parsedLimit = Number(limit) || 50;
|
|
||||||
if (parsedLimit < 1 || parsedLimit > 100)
|
|
||||||
throw new HTTPError("limit must be between 1 and 100", 422);
|
|
||||||
|
|
||||||
if (sort_order) {
|
|
||||||
if (
|
|
||||||
typeof sort_order != "string" ||
|
|
||||||
["desc", "asc"].indexOf(sort_order) == -1
|
|
||||||
)
|
|
||||||
throw FieldErrors({
|
|
||||||
sort_order: {
|
|
||||||
message: "Value must be one of ('desc', 'asc').",
|
|
||||||
code: "BASE_TYPE_CHOICES",
|
|
||||||
},
|
|
||||||
}); // todo this is wrong
|
|
||||||
}
|
|
||||||
|
|
||||||
const permissions = await getPermission(
|
|
||||||
req.user_id,
|
|
||||||
req.params.guild_id,
|
|
||||||
channel_id as string | undefined,
|
|
||||||
);
|
|
||||||
permissions.hasThrow("VIEW_CHANNEL");
|
|
||||||
if (!permissions.has("READ_MESSAGE_HISTORY"))
|
|
||||||
return res.json({ messages: [], total_results: 0 });
|
|
||||||
|
|
||||||
const query: FindManyOptions<Message> = {
|
|
||||||
order: {
|
|
||||||
timestamp: sort_order
|
|
||||||
? (sort_order.toUpperCase() as "ASC" | "DESC")
|
|
||||||
: "DESC",
|
|
||||||
},
|
|
||||||
take: parsedLimit || 0,
|
|
||||||
where: {
|
|
||||||
guild: {
|
|
||||||
id: req.params.guild_id,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relations: [
|
}),
|
||||||
"author",
|
async (req: Request, res: Response) => {
|
||||||
"webhook",
|
const {
|
||||||
"application",
|
channel_id,
|
||||||
"mentions",
|
content,
|
||||||
"mention_roles",
|
// include_nsfw, // TODO
|
||||||
"mention_channels",
|
offset,
|
||||||
"sticker_items",
|
sort_order,
|
||||||
"attachments",
|
// sort_by, // TODO: Handle 'relevance'
|
||||||
],
|
limit,
|
||||||
skip: offset ? Number(offset) : 0,
|
author_id,
|
||||||
};
|
} = req.query;
|
||||||
//@ts-ignore
|
|
||||||
if (channel_id) query.where.channel = { id: channel_id };
|
|
||||||
else {
|
|
||||||
// get all channel IDs that this user can access
|
|
||||||
const channels = await Channel.find({
|
|
||||||
where: { guild_id: req.params.guild_id },
|
|
||||||
select: ["id"],
|
|
||||||
});
|
|
||||||
const ids = [];
|
|
||||||
|
|
||||||
for (const channel of channels) {
|
const parsedLimit = Number(limit) || 50;
|
||||||
const perm = await getPermission(
|
if (parsedLimit < 1 || parsedLimit > 100)
|
||||||
req.user_id,
|
throw new HTTPError("limit must be between 1 and 100", 422);
|
||||||
req.params.guild_id,
|
|
||||||
channel.id,
|
if (sort_order) {
|
||||||
);
|
if (
|
||||||
if (!perm.has("VIEW_CHANNEL") || !perm.has("READ_MESSAGE_HISTORY"))
|
typeof sort_order != "string" ||
|
||||||
continue;
|
["desc", "asc"].indexOf(sort_order) == -1
|
||||||
ids.push(channel.id);
|
)
|
||||||
|
throw FieldErrors({
|
||||||
|
sort_order: {
|
||||||
|
message: "Value must be one of ('desc', 'asc').",
|
||||||
|
code: "BASE_TYPE_CHOICES",
|
||||||
|
},
|
||||||
|
}); // todo this is wrong
|
||||||
}
|
}
|
||||||
|
|
||||||
//@ts-ignore
|
const permissions = await getPermission(
|
||||||
query.where.channel = { id: In(ids) };
|
req.user_id,
|
||||||
}
|
req.params.guild_id,
|
||||||
//@ts-ignore
|
channel_id as string | undefined,
|
||||||
if (author_id) query.where.author = { id: author_id };
|
);
|
||||||
//@ts-ignore
|
permissions.hasThrow("VIEW_CHANNEL");
|
||||||
if (content) query.where.content = Like(`%${content}%`);
|
if (!permissions.has("READ_MESSAGE_HISTORY"))
|
||||||
|
return res.json({ messages: [], total_results: 0 });
|
||||||
|
|
||||||
const messages: Message[] = await Message.find(query);
|
const query: FindManyOptions<Message> = {
|
||||||
|
order: {
|
||||||
const messagesDto = messages.map((x) => [
|
timestamp: sort_order
|
||||||
{
|
? (sort_order.toUpperCase() as "ASC" | "DESC")
|
||||||
id: x.id,
|
: "DESC",
|
||||||
type: x.type,
|
|
||||||
content: x.content,
|
|
||||||
channel_id: x.channel_id,
|
|
||||||
author: {
|
|
||||||
id: x.author?.id,
|
|
||||||
username: x.author?.username,
|
|
||||||
avatar: x.author?.avatar,
|
|
||||||
avatar_decoration: null,
|
|
||||||
discriminator: x.author?.discriminator,
|
|
||||||
public_flags: x.author?.public_flags,
|
|
||||||
},
|
},
|
||||||
attachments: x.attachments,
|
take: parsedLimit || 0,
|
||||||
embeds: x.embeds,
|
where: {
|
||||||
mentions: x.mentions,
|
guild: {
|
||||||
mention_roles: x.mention_roles,
|
id: req.params.guild_id,
|
||||||
pinned: x.pinned,
|
},
|
||||||
mention_everyone: x.mention_everyone,
|
},
|
||||||
tts: x.tts,
|
relations: [
|
||||||
timestamp: x.timestamp,
|
"author",
|
||||||
edited_timestamp: x.edited_timestamp,
|
"webhook",
|
||||||
flags: x.flags,
|
"application",
|
||||||
components: x.components,
|
"mentions",
|
||||||
hit: true,
|
"mention_roles",
|
||||||
},
|
"mention_channels",
|
||||||
]);
|
"sticker_items",
|
||||||
|
"attachments",
|
||||||
|
],
|
||||||
|
skip: offset ? Number(offset) : 0,
|
||||||
|
};
|
||||||
|
//@ts-ignore
|
||||||
|
if (channel_id) query.where.channel = { id: channel_id };
|
||||||
|
else {
|
||||||
|
// get all channel IDs that this user can access
|
||||||
|
const channels = await Channel.find({
|
||||||
|
where: { guild_id: req.params.guild_id },
|
||||||
|
select: ["id"],
|
||||||
|
});
|
||||||
|
const ids = [];
|
||||||
|
|
||||||
return res.json({
|
for (const channel of channels) {
|
||||||
messages: messagesDto,
|
const perm = await getPermission(
|
||||||
total_results: messages.length,
|
req.user_id,
|
||||||
});
|
req.params.guild_id,
|
||||||
});
|
channel.id,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!perm.has("VIEW_CHANNEL") ||
|
||||||
|
!perm.has("READ_MESSAGE_HISTORY")
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
ids.push(channel.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
query.where.channel = { id: In(ids) };
|
||||||
|
}
|
||||||
|
//@ts-ignore
|
||||||
|
if (author_id) query.where.author = { id: author_id };
|
||||||
|
//@ts-ignore
|
||||||
|
if (content) query.where.content = Like(`%${content}%`);
|
||||||
|
|
||||||
|
const messages: Message[] = await Message.find(query);
|
||||||
|
|
||||||
|
const messagesDto = messages.map((x) => [
|
||||||
|
{
|
||||||
|
id: x.id,
|
||||||
|
type: x.type,
|
||||||
|
content: x.content,
|
||||||
|
channel_id: x.channel_id,
|
||||||
|
author: {
|
||||||
|
id: x.author?.id,
|
||||||
|
username: x.author?.username,
|
||||||
|
avatar: x.author?.avatar,
|
||||||
|
avatar_decoration: null,
|
||||||
|
discriminator: x.author?.discriminator,
|
||||||
|
public_flags: x.author?.public_flags,
|
||||||
|
},
|
||||||
|
attachments: x.attachments,
|
||||||
|
embeds: x.embeds,
|
||||||
|
mentions: x.mentions,
|
||||||
|
mention_roles: x.mention_roles,
|
||||||
|
pinned: x.pinned,
|
||||||
|
mention_everyone: x.mention_everyone,
|
||||||
|
tts: x.tts,
|
||||||
|
timestamp: x.timestamp,
|
||||||
|
edited_timestamp: x.edited_timestamp,
|
||||||
|
flags: x.flags,
|
||||||
|
components: x.components,
|
||||||
|
hit: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
messages: messagesDto,
|
||||||
|
total_results: messages.length,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -31,7 +31,20 @@ const router = Router();
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:member_id",
|
"/:member_id",
|
||||||
route({ requestBody: "MemberChangeProfileSchema" }),
|
route({
|
||||||
|
requestBody: "MemberChangeProfileSchema",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Member",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
// const member_id =
|
// const member_id =
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Router, Request, Response } from "express";
|
|
||||||
import { Guild, Member, Snowflake } from "@spacebar/util";
|
|
||||||
import { LessThan, IsNull } from "typeorm";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
|
import { Guild, Member, Snowflake } from "@spacebar/util";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
|
import { IsNull, LessThan } from "typeorm";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
//Returns all inactive members, respecting role hierarchy
|
//Returns all inactive members, respecting role hierarchy
|
||||||
@ -80,25 +80,46 @@ export const inactiveMembers = async (
|
|||||||
return members;
|
return members;
|
||||||
};
|
};
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const days = parseInt(req.query.days as string);
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
body: "GuildPruneResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const days = parseInt(req.query.days as string);
|
||||||
|
|
||||||
let roles = req.query.include_roles;
|
let roles = req.query.include_roles;
|
||||||
if (typeof roles === "string") roles = [roles]; //express will return array otherwise
|
if (typeof roles === "string") roles = [roles]; //express will return array otherwise
|
||||||
|
|
||||||
const members = await inactiveMembers(
|
const members = await inactiveMembers(
|
||||||
req.params.guild_id,
|
req.params.guild_id,
|
||||||
req.user_id,
|
req.user_id,
|
||||||
days,
|
days,
|
||||||
roles as string[],
|
roles as string[],
|
||||||
);
|
);
|
||||||
|
|
||||||
res.send({ pruned: members.length });
|
res.send({ pruned: members.length });
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
route({ permission: "KICK_MEMBERS", right: "KICK_BAN_MEMBERS" }),
|
route({
|
||||||
|
permission: "KICK_MEMBERS",
|
||||||
|
right: "KICK_BAN_MEMBERS",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildPurgeResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const days = parseInt(req.body.days);
|
const days = parseInt(req.body.days);
|
||||||
|
|
||||||
|
@ -16,22 +16,35 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getIpAdress, getVoiceRegions, route } from "@spacebar/api";
|
||||||
import { Guild } from "@spacebar/util";
|
import { Guild } from "@spacebar/util";
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { getVoiceRegions, route, getIpAdress } from "@spacebar/api";
|
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
route({
|
||||||
//TODO we should use an enum for guild's features and not hardcoded strings
|
responses: {
|
||||||
return res.json(
|
200: {
|
||||||
await getVoiceRegions(
|
body: "GuildVoiceRegionsResponse",
|
||||||
getIpAdress(req),
|
},
|
||||||
guild.features.includes("VIP_REGIONS"),
|
404: {
|
||||||
),
|
body: "APIErrorResponse",
|
||||||
);
|
},
|
||||||
});
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
|
//TODO we should use an enum for guild's features and not hardcoded strings
|
||||||
|
return res.json(
|
||||||
|
await getVoiceRegions(
|
||||||
|
getIpAdress(req),
|
||||||
|
guild.features.includes("VIP_REGIONS"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -31,16 +31,48 @@ import { HTTPError } from "lambert-server";
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id, role_id } = req.params;
|
"/",
|
||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
route({
|
||||||
const role = await Role.findOneOrFail({ where: { guild_id, id: role_id } });
|
responses: {
|
||||||
return res.json(role);
|
200: {
|
||||||
});
|
body: "Role",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id, role_id } = req.params;
|
||||||
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
const role = await Role.findOneOrFail({
|
||||||
|
where: { guild_id, id: role_id },
|
||||||
|
});
|
||||||
|
return res.json(role);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/",
|
"/",
|
||||||
route({ permission: "MANAGE_ROLES" }),
|
route({
|
||||||
|
permission: "MANAGE_ROLES",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id, role_id } = req.params;
|
const { guild_id, role_id } = req.params;
|
||||||
if (role_id === guild_id)
|
if (role_id === guild_id)
|
||||||
@ -69,7 +101,24 @@ router.delete(
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "RoleModifySchema", permission: "MANAGE_ROLES" }),
|
route({
|
||||||
|
requestBody: "RoleModifySchema",
|
||||||
|
permission: "MANAGE_ROLES",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Role",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { role_id, guild_id } = req.params;
|
const { role_id, guild_id } = req.params;
|
||||||
const body = req.body as RoleModifySchema;
|
const body = req.body as RoleModifySchema;
|
||||||
|
@ -21,7 +21,6 @@ import {
|
|||||||
Config,
|
Config,
|
||||||
DiscordApiErrors,
|
DiscordApiErrors,
|
||||||
emitEvent,
|
emitEvent,
|
||||||
getPermission,
|
|
||||||
GuildRoleCreateEvent,
|
GuildRoleCreateEvent,
|
||||||
GuildRoleUpdateEvent,
|
GuildRoleUpdateEvent,
|
||||||
Member,
|
Member,
|
||||||
@ -47,7 +46,21 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "RoleModifySchema", permission: "MANAGE_ROLES" }),
|
route({
|
||||||
|
requestBody: "RoleModifySchema",
|
||||||
|
permission: "MANAGE_ROLES",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Role",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const guild_id = req.params.guild_id;
|
const guild_id = req.params.guild_id;
|
||||||
const body = req.body as RoleModifySchema;
|
const body = req.body as RoleModifySchema;
|
||||||
@ -104,14 +117,25 @@ router.post(
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "RolePositionUpdateSchema" }),
|
route({
|
||||||
|
requestBody: "RolePositionUpdateSchema",
|
||||||
|
permission: "MANAGE_ROLES",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildRolesResponse",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const body = req.body as RolePositionUpdateSchema;
|
const body = req.body as RolePositionUpdateSchema;
|
||||||
|
|
||||||
const perms = await getPermission(req.user_id, guild_id);
|
|
||||||
perms.hasThrow("MANAGE_ROLES");
|
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
body.map(async (x) =>
|
body.map(async (x) =>
|
||||||
Role.update({ guild_id, id: x.id }, { position: x.position }),
|
Role.update({ guild_id, id: x.id }, { position: x.position }),
|
||||||
|
@ -33,12 +33,25 @@ import { HTTPError } from "lambert-server";
|
|||||||
import multer from "multer";
|
import multer from "multer";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildStickersResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
res.json(await Sticker.find({ where: { guild_id } }));
|
res.json(await Sticker.find({ where: { guild_id } }));
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const bodyParser = multer({
|
const bodyParser = multer({
|
||||||
limits: {
|
limits: {
|
||||||
@ -55,6 +68,17 @@ router.post(
|
|||||||
route({
|
route({
|
||||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||||
requestBody: "ModifyGuildStickerSchema",
|
requestBody: "ModifyGuildStickerSchema",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Sticker",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
if (!req.file) throw new HTTPError("missing file");
|
if (!req.file) throw new HTTPError("missing file");
|
||||||
@ -98,20 +122,46 @@ export function getStickerFormat(mime_type: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
router.get("/:sticker_id", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id, sticker_id } = req.params;
|
"/:sticker_id",
|
||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Sticker",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id, sticker_id } = req.params;
|
||||||
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
res.json(
|
res.json(
|
||||||
await Sticker.findOneOrFail({ where: { guild_id, id: sticker_id } }),
|
await Sticker.findOneOrFail({
|
||||||
);
|
where: { guild_id, id: sticker_id },
|
||||||
});
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:sticker_id",
|
"/:sticker_id",
|
||||||
route({
|
route({
|
||||||
requestBody: "ModifyGuildStickerSchema",
|
requestBody: "ModifyGuildStickerSchema",
|
||||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Sticker",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id, sticker_id } = req.params;
|
const { guild_id, sticker_id } = req.params;
|
||||||
@ -141,7 +191,15 @@ async function sendStickerUpdateEvent(guild_id: string) {
|
|||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:sticker_id",
|
"/:sticker_id",
|
||||||
route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }),
|
route({
|
||||||
|
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id, sticker_id } = req.params;
|
const { guild_id, sticker_id } = req.params;
|
||||||
|
|
||||||
|
@ -40,19 +40,46 @@ const TemplateGuildProjection: (keyof Guild)[] = [
|
|||||||
"icon",
|
"icon",
|
||||||
];
|
];
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildTemplatesResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
const templates = await Template.find({
|
const templates = await Template.find({
|
||||||
where: { source_guild_id: guild_id },
|
where: { source_guild_id: guild_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.json(templates);
|
return res.json(templates);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "TemplateCreateSchema", permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
requestBody: "TemplateCreateSchema",
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "Template",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const guild = await Guild.findOneOrFail({
|
const guild = await Guild.findOneOrFail({
|
||||||
@ -80,7 +107,13 @@ router.post(
|
|||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:code",
|
"/:code",
|
||||||
route({ permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
200: { body: "Template" },
|
||||||
|
403: { body: "APIErrorResponse" },
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { code, guild_id } = req.params;
|
const { code, guild_id } = req.params;
|
||||||
|
|
||||||
@ -95,7 +128,13 @@ router.delete(
|
|||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/:code",
|
"/:code",
|
||||||
route({ permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
200: { body: "Template" },
|
||||||
|
403: { body: "APIErrorResponse" },
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { code, guild_id } = req.params;
|
const { code, guild_id } = req.params;
|
||||||
const guild = await Guild.findOneOrFail({
|
const guild = await Guild.findOneOrFail({
|
||||||
@ -114,7 +153,14 @@ router.put(
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:code",
|
"/:code",
|
||||||
route({ requestBody: "TemplateModifySchema", permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
requestBody: "TemplateModifySchema",
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
200: { body: "Template" },
|
||||||
|
403: { body: "APIErrorResponse" },
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { code, guild_id } = req.params;
|
const { code, guild_id } = req.params;
|
||||||
const { name, description } = req.body;
|
const { name, description } = req.body;
|
||||||
|
@ -33,7 +33,20 @@ const InviteRegex = /\W/g;
|
|||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
route({ permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildVanityUrlResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
@ -60,7 +73,21 @@ router.get(
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "VanityUrlSchema", permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
requestBody: "VanityUrlSchema",
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildVanityUrlCreateResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const body = req.body as VanityUrlSchema;
|
const body = req.body as VanityUrlSchema;
|
||||||
|
@ -34,7 +34,21 @@ const router = Router();
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "VoiceStateUpdateSchema" }),
|
route({
|
||||||
|
requestBody: "VoiceStateUpdateSchema",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const body = req.body as VoiceStateUpdateSchema;
|
const body = req.body as VoiceStateUpdateSchema;
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
|
@ -23,20 +23,42 @@ import { HTTPError } from "lambert-server";
|
|||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const guild_id = req.params.guild_id;
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildWelcomeScreen",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const guild_id = req.params.guild_id;
|
||||||
|
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
res.json(guild.welcome_screen);
|
res.json(guild.welcome_screen);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({
|
route({
|
||||||
requestBody: "GuildUpdateWelcomeScreenSchema",
|
requestBody: "GuildUpdateWelcomeScreenSchema",
|
||||||
permission: "MANAGE_GUILD",
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
204: {},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const guild_id = req.params.guild_id;
|
const guild_id = req.params.guild_id;
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Request, Response, Router } from "express";
|
|
||||||
import { Permissions, Guild, Invite, Channel, Member } from "@spacebar/util";
|
|
||||||
import { HTTPError } from "lambert-server";
|
|
||||||
import { random, route } from "@spacebar/api";
|
import { random, route } from "@spacebar/api";
|
||||||
|
import { Channel, Guild, Invite, Member, Permissions } from "@spacebar/util";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
|
import { HTTPError } from "lambert-server";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
@ -32,77 +32,90 @@ const router: Router = Router();
|
|||||||
|
|
||||||
// https://discord.com/developers/docs/resources/guild#get-guild-widget
|
// https://discord.com/developers/docs/resources/guild#get-guild-widget
|
||||||
// TODO: Cache the response for a guild for 5 minutes regardless of response
|
// TODO: Cache the response for a guild for 5 minutes regardless of response
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildWidgetJsonResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404);
|
if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404);
|
||||||
|
|
||||||
// Fetch existing widget invite for widget channel
|
// Fetch existing widget invite for widget channel
|
||||||
let invite = await Invite.findOne({
|
let invite = await Invite.findOne({
|
||||||
where: { channel_id: guild.widget_channel_id },
|
where: { channel_id: guild.widget_channel_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (guild.widget_channel_id && !invite) {
|
if (guild.widget_channel_id && !invite) {
|
||||||
// Create invite for channel if none exists
|
// Create invite for channel if none exists
|
||||||
// TODO: Refactor invite create code to a shared function
|
// TODO: Refactor invite create code to a shared function
|
||||||
const max_age = 86400; // 24 hours
|
const max_age = 86400; // 24 hours
|
||||||
const expires_at = new Date(max_age * 1000 + Date.now());
|
const expires_at = new Date(max_age * 1000 + Date.now());
|
||||||
|
|
||||||
invite = await Invite.create({
|
invite = await Invite.create({
|
||||||
code: random(),
|
code: random(),
|
||||||
temporary: false,
|
temporary: false,
|
||||||
uses: 0,
|
uses: 0,
|
||||||
max_uses: 0,
|
max_uses: 0,
|
||||||
max_age: max_age,
|
max_age: max_age,
|
||||||
expires_at,
|
expires_at,
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
guild_id,
|
guild_id,
|
||||||
channel_id: guild.widget_channel_id,
|
channel_id: guild.widget_channel_id,
|
||||||
}).save();
|
}).save();
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch voice channels, and the @everyone permissions object
|
|
||||||
const channels: { id: string; name: string; position: number }[] = [];
|
|
||||||
|
|
||||||
(
|
|
||||||
await Channel.find({
|
|
||||||
where: { guild_id: guild_id, type: 2 },
|
|
||||||
order: { position: "ASC" },
|
|
||||||
})
|
|
||||||
).filter((doc) => {
|
|
||||||
// Only return channels where @everyone has the CONNECT permission
|
|
||||||
if (
|
|
||||||
doc.permission_overwrites === undefined ||
|
|
||||||
Permissions.channelPermission(
|
|
||||||
doc.permission_overwrites,
|
|
||||||
Permissions.FLAGS.CONNECT,
|
|
||||||
) === Permissions.FLAGS.CONNECT
|
|
||||||
) {
|
|
||||||
channels.push({
|
|
||||||
id: doc.id,
|
|
||||||
name: doc.name ?? "Unknown channel",
|
|
||||||
position: doc.position ?? 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Fetch members
|
// Fetch voice channels, and the @everyone permissions object
|
||||||
// TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file)
|
const channels: { id: string; name: string; position: number }[] = [];
|
||||||
const members = await Member.find({ where: { guild_id: guild_id } });
|
|
||||||
|
|
||||||
// Construct object to respond with
|
(
|
||||||
const data = {
|
await Channel.find({
|
||||||
id: guild_id,
|
where: { guild_id: guild_id, type: 2 },
|
||||||
name: guild.name,
|
order: { position: "ASC" },
|
||||||
instant_invite: invite?.code,
|
})
|
||||||
channels: channels,
|
).filter((doc) => {
|
||||||
members: members,
|
// Only return channels where @everyone has the CONNECT permission
|
||||||
presence_count: guild.presence_count,
|
if (
|
||||||
};
|
doc.permission_overwrites === undefined ||
|
||||||
|
Permissions.channelPermission(
|
||||||
|
doc.permission_overwrites,
|
||||||
|
Permissions.FLAGS.CONNECT,
|
||||||
|
) === Permissions.FLAGS.CONNECT
|
||||||
|
) {
|
||||||
|
channels.push({
|
||||||
|
id: doc.id,
|
||||||
|
name: doc.name ?? "Unknown channel",
|
||||||
|
position: doc.position ?? 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
res.set("Cache-Control", "public, max-age=300");
|
// Fetch members
|
||||||
return res.json(data);
|
// TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file)
|
||||||
});
|
const members = await Member.find({ where: { guild_id: guild_id } });
|
||||||
|
|
||||||
|
// Construct object to respond with
|
||||||
|
const data = {
|
||||||
|
id: guild_id,
|
||||||
|
name: guild.name,
|
||||||
|
instant_invite: invite?.code,
|
||||||
|
channels: channels,
|
||||||
|
members: members,
|
||||||
|
presence_count: guild.presence_count,
|
||||||
|
};
|
||||||
|
|
||||||
|
res.set("Cache-Control", "public, max-age=300");
|
||||||
|
return res.json(data);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -18,11 +18,11 @@
|
|||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
import { Request, Response, Router } from "express";
|
|
||||||
import { Guild } from "@spacebar/util";
|
|
||||||
import { HTTPError } from "lambert-server";
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
|
import { Guild } from "@spacebar/util";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
import { HTTPError } from "lambert-server";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
@ -31,130 +31,178 @@ const router: Router = Router();
|
|||||||
|
|
||||||
// https://discord.com/developers/docs/resources/guild#get-guild-widget-image
|
// https://discord.com/developers/docs/resources/guild#get-guild-widget-image
|
||||||
// TODO: Cache the response
|
// TODO: Cache the response
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
if (!guild.widget_enabled) throw new HTTPError("Unknown Guild", 404);
|
if (!guild.widget_enabled) throw new HTTPError("Unknown Guild", 404);
|
||||||
|
|
||||||
// Fetch guild information
|
// Fetch guild information
|
||||||
const icon = guild.icon;
|
const icon = guild.icon;
|
||||||
const name = guild.name;
|
const name = guild.name;
|
||||||
const presence = guild.presence_count + " ONLINE";
|
const presence = guild.presence_count + " ONLINE";
|
||||||
|
|
||||||
// Fetch parameter
|
// Fetch parameter
|
||||||
const style = req.query.style?.toString() || "shield";
|
const style = req.query.style?.toString() || "shield";
|
||||||
if (
|
if (
|
||||||
!["shield", "banner1", "banner2", "banner3", "banner4"].includes(style)
|
!["shield", "banner1", "banner2", "banner3", "banner4"].includes(
|
||||||
) {
|
style,
|
||||||
throw new HTTPError(
|
)
|
||||||
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
) {
|
||||||
400,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup canvas
|
|
||||||
const { createCanvas } = require("canvas");
|
|
||||||
const { loadImage } = require("canvas");
|
|
||||||
const sizeOf = require("image-size");
|
|
||||||
|
|
||||||
// TODO: Widget style templates need Spacebar branding
|
|
||||||
const source = path.join(
|
|
||||||
__dirname,
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"assets",
|
|
||||||
"widget",
|
|
||||||
`${style}.png`,
|
|
||||||
);
|
|
||||||
if (!fs.existsSync(source)) {
|
|
||||||
throw new HTTPError("Widget template does not exist.", 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create base template image for parameter
|
|
||||||
const { width, height } = await sizeOf(source);
|
|
||||||
const canvas = createCanvas(width, height);
|
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
const template = await loadImage(source);
|
|
||||||
ctx.drawImage(template, 0, 0);
|
|
||||||
|
|
||||||
// Add the guild specific information to the template asset image
|
|
||||||
switch (style) {
|
|
||||||
case "shield":
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
73,
|
|
||||||
13,
|
|
||||||
"#FFFFFF",
|
|
||||||
"thin 10px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "banner1":
|
|
||||||
if (icon) await drawIcon(ctx, 20, 27, 50, icon);
|
|
||||||
await drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22);
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
83,
|
|
||||||
66,
|
|
||||||
"#C9D2F0FF",
|
|
||||||
"thin 11px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "banner2":
|
|
||||||
if (icon) await drawIcon(ctx, 13, 19, 36, icon);
|
|
||||||
await drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15);
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
62,
|
|
||||||
49,
|
|
||||||
"#C9D2F0FF",
|
|
||||||
"thin 11px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "banner3":
|
|
||||||
if (icon) await drawIcon(ctx, 20, 20, 50, icon);
|
|
||||||
await drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27);
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
83,
|
|
||||||
58,
|
|
||||||
"#C9D2F0FF",
|
|
||||||
"thin 11px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "banner4":
|
|
||||||
if (icon) await drawIcon(ctx, 21, 136, 50, icon);
|
|
||||||
await drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27);
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
84,
|
|
||||||
171,
|
|
||||||
"#C9D2F0FF",
|
|
||||||
"thin 12px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new HTTPError(
|
throw new HTTPError(
|
||||||
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
||||||
400,
|
400,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return final image
|
// Setup canvas
|
||||||
const buffer = canvas.toBuffer("image/png");
|
const { createCanvas } = require("canvas");
|
||||||
res.set("Content-Type", "image/png");
|
const { loadImage } = require("canvas");
|
||||||
res.set("Cache-Control", "public, max-age=3600");
|
const sizeOf = require("image-size");
|
||||||
return res.send(buffer);
|
|
||||||
});
|
// TODO: Widget style templates need Spacebar branding
|
||||||
|
const source = path.join(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"..",
|
||||||
|
"assets",
|
||||||
|
"widget",
|
||||||
|
`${style}.png`,
|
||||||
|
);
|
||||||
|
if (!fs.existsSync(source)) {
|
||||||
|
throw new HTTPError("Widget template does not exist.", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create base template image for parameter
|
||||||
|
const { width, height } = await sizeOf(source);
|
||||||
|
const canvas = createCanvas(width, height);
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
const template = await loadImage(source);
|
||||||
|
ctx.drawImage(template, 0, 0);
|
||||||
|
|
||||||
|
// Add the guild specific information to the template asset image
|
||||||
|
switch (style) {
|
||||||
|
case "shield":
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
73,
|
||||||
|
13,
|
||||||
|
"#FFFFFF",
|
||||||
|
"thin 10px Verdana",
|
||||||
|
presence,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "banner1":
|
||||||
|
if (icon) await drawIcon(ctx, 20, 27, 50, icon);
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
83,
|
||||||
|
51,
|
||||||
|
"#FFFFFF",
|
||||||
|
"12px Verdana",
|
||||||
|
name,
|
||||||
|
22,
|
||||||
|
);
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
83,
|
||||||
|
66,
|
||||||
|
"#C9D2F0FF",
|
||||||
|
"thin 11px Verdana",
|
||||||
|
presence,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "banner2":
|
||||||
|
if (icon) await drawIcon(ctx, 13, 19, 36, icon);
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
62,
|
||||||
|
34,
|
||||||
|
"#FFFFFF",
|
||||||
|
"12px Verdana",
|
||||||
|
name,
|
||||||
|
15,
|
||||||
|
);
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
62,
|
||||||
|
49,
|
||||||
|
"#C9D2F0FF",
|
||||||
|
"thin 11px Verdana",
|
||||||
|
presence,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "banner3":
|
||||||
|
if (icon) await drawIcon(ctx, 20, 20, 50, icon);
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
83,
|
||||||
|
44,
|
||||||
|
"#FFFFFF",
|
||||||
|
"12px Verdana",
|
||||||
|
name,
|
||||||
|
27,
|
||||||
|
);
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
83,
|
||||||
|
58,
|
||||||
|
"#C9D2F0FF",
|
||||||
|
"thin 11px Verdana",
|
||||||
|
presence,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "banner4":
|
||||||
|
if (icon) await drawIcon(ctx, 21, 136, 50, icon);
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
84,
|
||||||
|
156,
|
||||||
|
"#FFFFFF",
|
||||||
|
"13px Verdana",
|
||||||
|
name,
|
||||||
|
27,
|
||||||
|
);
|
||||||
|
await drawText(
|
||||||
|
ctx,
|
||||||
|
84,
|
||||||
|
171,
|
||||||
|
"#C9D2F0FF",
|
||||||
|
"thin 12px Verdana",
|
||||||
|
presence,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new HTTPError(
|
||||||
|
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return final image
|
||||||
|
const buffer = canvas.toBuffer("image/png");
|
||||||
|
res.set("Content-Type", "image/png");
|
||||||
|
res.set("Cache-Control", "public, max-age=3600");
|
||||||
|
return res.send(buffer);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
async function drawIcon(
|
async function drawIcon(
|
||||||
canvas: any,
|
canvas: any,
|
||||||
|
@ -23,21 +23,48 @@ import { Request, Response, Router } from "express";
|
|||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
// https://discord.com/developers/docs/resources/guild#get-guild-widget-settings
|
// https://discord.com/developers/docs/resources/guild#get-guild-widget-settings
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { guild_id } = req.params;
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "GuildWidgetSettingsResponse",
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
enabled: guild.widget_enabled || false,
|
enabled: guild.widget_enabled || false,
|
||||||
channel_id: guild.widget_channel_id || null,
|
channel_id: guild.widget_channel_id || null,
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// https://discord.com/developers/docs/resources/guild#modify-guild-widget
|
// https://discord.com/developers/docs/resources/guild#modify-guild-widget
|
||||||
router.patch(
|
router.patch(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "WidgetModifySchema", permission: "MANAGE_GUILD" }),
|
route({
|
||||||
|
requestBody: "WidgetModifySchema",
|
||||||
|
permission: "MANAGE_GUILD",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "WidgetModifySchema",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const body = req.body as WidgetModifySchema;
|
const body = req.body as WidgetModifySchema;
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
|
@ -33,7 +33,21 @@ const router: Router = Router();
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
route({ requestBody: "GuildCreateSchema", right: "CREATE_GUILDS" }),
|
route({
|
||||||
|
requestBody: "GuildCreateSchema",
|
||||||
|
right: "CREATE_GUILDS",
|
||||||
|
responses: {
|
||||||
|
201: {
|
||||||
|
body: "GuildCreateResponse",
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const body = req.body as GuildCreateSchema;
|
const body = req.body as GuildCreateSchema;
|
||||||
|
|
||||||
|
@ -31,53 +31,72 @@ import { Request, Response, Router } from "express";
|
|||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
router.get("/:code", route({}), async (req: Request, res: Response) => {
|
router.get(
|
||||||
const { allowDiscordTemplates, allowRaws, enabled } =
|
"/:code",
|
||||||
Config.get().templates;
|
route({
|
||||||
if (!enabled)
|
responses: {
|
||||||
res.json({
|
200: {
|
||||||
code: 403,
|
body: "GuildTemplate",
|
||||||
message: "Template creation & usage is disabled on this instance.",
|
|
||||||
}).sendStatus(403);
|
|
||||||
|
|
||||||
const { code } = req.params;
|
|
||||||
|
|
||||||
if (code.startsWith("discord:")) {
|
|
||||||
if (!allowDiscordTemplates)
|
|
||||||
return res
|
|
||||||
.json({
|
|
||||||
code: 403,
|
|
||||||
message:
|
|
||||||
"Discord templates cannot be used on this instance.",
|
|
||||||
})
|
|
||||||
.sendStatus(403);
|
|
||||||
const discordTemplateID = code.split("discord:", 2)[1];
|
|
||||||
|
|
||||||
const discordTemplateData = await fetch(
|
|
||||||
`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`,
|
|
||||||
{
|
|
||||||
method: "get",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
},
|
},
|
||||||
);
|
403: {
|
||||||
return res.json(await discordTemplateData.json());
|
body: "APIErrorResponse",
|
||||||
}
|
},
|
||||||
|
404: {
|
||||||
|
body: "APIErrorResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { allowDiscordTemplates, allowRaws, enabled } =
|
||||||
|
Config.get().templates;
|
||||||
|
if (!enabled)
|
||||||
|
res.json({
|
||||||
|
code: 403,
|
||||||
|
message:
|
||||||
|
"Template creation & usage is disabled on this instance.",
|
||||||
|
}).sendStatus(403);
|
||||||
|
|
||||||
if (code.startsWith("external:")) {
|
const { code } = req.params;
|
||||||
if (!allowRaws)
|
|
||||||
return res
|
|
||||||
.json({
|
|
||||||
code: 403,
|
|
||||||
message: "Importing raws is disabled on this instance.",
|
|
||||||
})
|
|
||||||
.sendStatus(403);
|
|
||||||
|
|
||||||
return res.json(code.split("external:", 2)[1]);
|
if (code.startsWith("discord:")) {
|
||||||
}
|
if (!allowDiscordTemplates)
|
||||||
|
return res
|
||||||
|
.json({
|
||||||
|
code: 403,
|
||||||
|
message:
|
||||||
|
"Discord templates cannot be used on this instance.",
|
||||||
|
})
|
||||||
|
.sendStatus(403);
|
||||||
|
const discordTemplateID = code.split("discord:", 2)[1];
|
||||||
|
|
||||||
const template = await Template.findOneOrFail({ where: { code: code } });
|
const discordTemplateData = await fetch(
|
||||||
res.json(template);
|
`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`,
|
||||||
});
|
{
|
||||||
|
method: "get",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return res.json(await discordTemplateData.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code.startsWith("external:")) {
|
||||||
|
if (!allowRaws)
|
||||||
|
return res
|
||||||
|
.json({
|
||||||
|
code: 403,
|
||||||
|
message: "Importing raws is disabled on this instance.",
|
||||||
|
})
|
||||||
|
.sendStatus(403);
|
||||||
|
|
||||||
|
return res.json(code.split("external:", 2)[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const template = await Template.findOneOrFail({
|
||||||
|
where: { code: code },
|
||||||
|
});
|
||||||
|
res.json(template);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:code",
|
"/:code",
|
||||||
|
@ -24,7 +24,7 @@ import {
|
|||||||
OneToMany,
|
OneToMany,
|
||||||
RelationId,
|
RelationId,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { Config, handleFile, Snowflake } from "..";
|
import { Config, GuildWelcomeScreen, handleFile, Snowflake } from "..";
|
||||||
import { Ban } from "./Ban";
|
import { Ban } from "./Ban";
|
||||||
import { BaseClass } from "./BaseClass";
|
import { BaseClass } from "./BaseClass";
|
||||||
import { Channel } from "./Channel";
|
import { Channel } from "./Channel";
|
||||||
@ -270,16 +270,7 @@ export class Guild extends BaseClass {
|
|||||||
verification_level?: number;
|
verification_level?: number;
|
||||||
|
|
||||||
@Column({ type: "simple-json" })
|
@Column({ type: "simple-json" })
|
||||||
welcome_screen: {
|
welcome_screen: GuildWelcomeScreen;
|
||||||
enabled: boolean;
|
|
||||||
description: string;
|
|
||||||
welcome_channels: {
|
|
||||||
description: string;
|
|
||||||
emoji_id?: string;
|
|
||||||
emoji_name?: string;
|
|
||||||
channel_id: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@RelationId((guild: Guild) => guild.widget_channel)
|
@RelationId((guild: Guild) => guild.widget_channel)
|
||||||
|
10
src/util/interfaces/GuildWelcomeScreen.ts
Normal file
10
src/util/interfaces/GuildWelcomeScreen.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface GuildWelcomeScreen {
|
||||||
|
enabled: boolean;
|
||||||
|
description: string;
|
||||||
|
welcome_channels: {
|
||||||
|
description: string;
|
||||||
|
emoji_id?: string;
|
||||||
|
emoji_name?: string;
|
||||||
|
channel_id: string;
|
||||||
|
}[];
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
export * from "./Activity";
|
export * from "./Activity";
|
||||||
export * from "./ConnectedAccount";
|
export * from "./ConnectedAccount";
|
||||||
export * from "./Event";
|
export * from "./Event";
|
||||||
|
export * from "./GuildWelcomeScreen";
|
||||||
export * from "./Interaction";
|
export * from "./Interaction";
|
||||||
export * from "./Presence";
|
export * from "./Presence";
|
||||||
export * from "./Status";
|
export * from "./Status";
|
||||||
|
10
src/util/schemas/responses/GuildBansResponse.ts
Normal file
10
src/util/schemas/responses/GuildBansResponse.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface GuildBansResponse {
|
||||||
|
reason: string;
|
||||||
|
user: {
|
||||||
|
username: string;
|
||||||
|
discriminator: string;
|
||||||
|
id: string;
|
||||||
|
avatar: string | null;
|
||||||
|
public_flags: number;
|
||||||
|
};
|
||||||
|
}
|
3
src/util/schemas/responses/GuildChannelsResponse.ts
Normal file
3
src/util/schemas/responses/GuildChannelsResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Channel } from "../../entities";
|
||||||
|
|
||||||
|
export type GuildChannelsResponse = Channel[];
|
3
src/util/schemas/responses/GuildCreateResponse.ts
Normal file
3
src/util/schemas/responses/GuildCreateResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface GuildCreateResponse {
|
||||||
|
id: string;
|
||||||
|
}
|
23
src/util/schemas/responses/GuildDiscoveryRequirements.ts
Normal file
23
src/util/schemas/responses/GuildDiscoveryRequirements.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export interface GuildDiscoveryRequirements {
|
||||||
|
uild_id: string;
|
||||||
|
safe_environment: boolean;
|
||||||
|
healthy: boolean;
|
||||||
|
health_score_pending: boolean;
|
||||||
|
size: boolean;
|
||||||
|
nsfw_properties: unknown;
|
||||||
|
protected: boolean;
|
||||||
|
sufficient: boolean;
|
||||||
|
sufficient_without_grace_period: boolean;
|
||||||
|
valid_rules_channel: boolean;
|
||||||
|
retention_healthy: boolean;
|
||||||
|
engagement_healthy: boolean;
|
||||||
|
age: boolean;
|
||||||
|
minimum_age: number;
|
||||||
|
health_score: {
|
||||||
|
avg_nonnew_participators: number;
|
||||||
|
avg_nonnew_communicators: number;
|
||||||
|
num_intentful_joiners: number;
|
||||||
|
perc_ret_w1_intentful: number;
|
||||||
|
};
|
||||||
|
minimum_size: number;
|
||||||
|
}
|
3
src/util/schemas/responses/GuildEmojisResponse.ts
Normal file
3
src/util/schemas/responses/GuildEmojisResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Emoji } from "../../entities";
|
||||||
|
|
||||||
|
export type GuildEmojisResponse = Emoji[];
|
3
src/util/schemas/responses/GuildInvitesResponse.ts
Normal file
3
src/util/schemas/responses/GuildInvitesResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Invite } from "../../entities";
|
||||||
|
|
||||||
|
export type GuildInvitesResponse = Invite[];
|
3
src/util/schemas/responses/GuildMembersResponse.ts
Normal file
3
src/util/schemas/responses/GuildMembersResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Member } from "../../entities";
|
||||||
|
|
||||||
|
export type GuildMembersResponse = Member[];
|
32
src/util/schemas/responses/GuildMessagesSearchResponse.ts
Normal file
32
src/util/schemas/responses/GuildMessagesSearchResponse.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
Attachment,
|
||||||
|
Embed,
|
||||||
|
MessageType,
|
||||||
|
PublicUser,
|
||||||
|
Role,
|
||||||
|
} from "../../entities";
|
||||||
|
|
||||||
|
export interface GuildMessagesSearchMessage {
|
||||||
|
id: string;
|
||||||
|
type: MessageType;
|
||||||
|
content?: string;
|
||||||
|
channel_id: string;
|
||||||
|
author: PublicUser;
|
||||||
|
attachments: Attachment[];
|
||||||
|
embeds: Embed[];
|
||||||
|
mentions: PublicUser[];
|
||||||
|
mention_roles: Role[];
|
||||||
|
pinned: boolean;
|
||||||
|
mention_everyone?: boolean;
|
||||||
|
tts: boolean;
|
||||||
|
timestamp: string;
|
||||||
|
edited_timestamp: string | null;
|
||||||
|
flags: number;
|
||||||
|
components: unknown[];
|
||||||
|
hit: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GuildMessagesSearchResponse {
|
||||||
|
messages: GuildMessagesSearchMessage[];
|
||||||
|
total_results: number;
|
||||||
|
}
|
7
src/util/schemas/responses/GuildPruneResponse.ts
Normal file
7
src/util/schemas/responses/GuildPruneResponse.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface GuildPruneResponse {
|
||||||
|
pruned: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GuildPurgeResponse {
|
||||||
|
purged: number;
|
||||||
|
}
|
3
src/util/schemas/responses/GuildResponse.ts
Normal file
3
src/util/schemas/responses/GuildResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Guild } from "../../entities";
|
||||||
|
|
||||||
|
export type GuildResponse = Guild & { joined_at: string };
|
3
src/util/schemas/responses/GuildRolesResponse.ts
Normal file
3
src/util/schemas/responses/GuildRolesResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Role } from "../../entities";
|
||||||
|
|
||||||
|
export type GuildRolesResponse = Role[];
|
3
src/util/schemas/responses/GuildStickersResponse.ts
Normal file
3
src/util/schemas/responses/GuildStickersResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Sticker } from "../../entities";
|
||||||
|
|
||||||
|
export type GuildStickersResponse = Sticker[];
|
3
src/util/schemas/responses/GuildTemplatesResponse.ts
Normal file
3
src/util/schemas/responses/GuildTemplatesResponse.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Template } from "../../entities";
|
||||||
|
|
||||||
|
export type GuildTemplatesResponse = Template[];
|
17
src/util/schemas/responses/GuildVanityUrl.ts
Normal file
17
src/util/schemas/responses/GuildVanityUrl.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface GuildVanityUrl {
|
||||||
|
code: string;
|
||||||
|
uses: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GuildVanityUrlNoInvite {
|
||||||
|
code: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GuildVanityUrlResponse =
|
||||||
|
| GuildVanityUrl
|
||||||
|
| GuildVanityUrl[]
|
||||||
|
| GuildVanityUrlNoInvite;
|
||||||
|
|
||||||
|
export interface GuildVanityUrlCreateResponse {
|
||||||
|
code: string;
|
||||||
|
}
|
9
src/util/schemas/responses/GuildVoiceRegionsResponse.ts
Normal file
9
src/util/schemas/responses/GuildVoiceRegionsResponse.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export interface GuildVoiceRegion {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
custom: boolean;
|
||||||
|
deprecated: boolean;
|
||||||
|
optimal: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GuildVoiceRegionsResponse = GuildVoiceRegion[];
|
21
src/util/schemas/responses/GuildWidgetJsonResponse.ts
Normal file
21
src/util/schemas/responses/GuildWidgetJsonResponse.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { ClientStatus } from "../../interfaces";
|
||||||
|
|
||||||
|
export interface GuildWidgetJsonResponse {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
instant_invite: string;
|
||||||
|
channels: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
position: number;
|
||||||
|
}[];
|
||||||
|
members: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
discriminator: string;
|
||||||
|
avatar: string | null;
|
||||||
|
status: ClientStatus;
|
||||||
|
avatar_url: string;
|
||||||
|
}[];
|
||||||
|
presence_count: number;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import { Snowflake } from "../../util";
|
||||||
|
|
||||||
|
export interface GuildWidgetSettingsResponse {
|
||||||
|
enabled: boolean;
|
||||||
|
channel_id: Snowflake | null;
|
||||||
|
}
|
8
src/util/schemas/responses/MemberJoinGuildResponse.ts
Normal file
8
src/util/schemas/responses/MemberJoinGuildResponse.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Emoji, Guild, Role, Sticker } from "../../entities";
|
||||||
|
|
||||||
|
export interface MemberJoinGuildResponse {
|
||||||
|
guild: Guild;
|
||||||
|
emojis: Emoji[];
|
||||||
|
roles: Role[];
|
||||||
|
stickers: Sticker[];
|
||||||
|
}
|
@ -12,7 +12,25 @@ export * from "./ChannelWebhooksResponse";
|
|||||||
export * from "./GatewayBotResponse";
|
export * from "./GatewayBotResponse";
|
||||||
export * from "./GatewayResponse";
|
export * from "./GatewayResponse";
|
||||||
export * from "./GenerateRegistrationTokensResponse";
|
export * from "./GenerateRegistrationTokensResponse";
|
||||||
|
export * from "./GuildBansResponse";
|
||||||
|
export * from "./GuildChannelsResponse";
|
||||||
|
export * from "./GuildCreateResponse";
|
||||||
|
export * from "./GuildDiscoveryRequirements";
|
||||||
|
export * from "./GuildEmojisResponse";
|
||||||
|
export * from "./GuildInvitesResponse";
|
||||||
|
export * from "./GuildMembersResponse";
|
||||||
|
export * from "./GuildMessagesSearchResponse";
|
||||||
|
export * from "./GuildPruneResponse";
|
||||||
|
export * from "./GuildResponse";
|
||||||
|
export * from "./GuildRolesResponse";
|
||||||
|
export * from "./GuildStickersResponse";
|
||||||
|
export * from "./GuildTemplatesResponse";
|
||||||
|
export * from "./GuildVanityUrl";
|
||||||
|
export * from "./GuildVoiceRegionsResponse";
|
||||||
|
export * from "./GuildWidgetJsonResponse";
|
||||||
|
export * from "./GuildWidgetSettingsResponse";
|
||||||
export * from "./LocationMetadataResponse";
|
export * from "./LocationMetadataResponse";
|
||||||
|
export * from "./MemberJoinGuildResponse";
|
||||||
export * from "./Tenor";
|
export * from "./Tenor";
|
||||||
export * from "./TokenResponse";
|
export * from "./TokenResponse";
|
||||||
export * from "./UserProfileResponse";
|
export * from "./UserProfileResponse";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user