🚧 webhook
This commit is contained in:
		
							parent
							
								
									8f862f0e5d
								
							
						
					
					
						commit
						df2b83ac15
					
				| @ -1770,10 +1770,6 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "additionalProperties": false, |         "additionalProperties": false, | ||||||
|         "required": [ |  | ||||||
|             "avatar", |  | ||||||
|             "name" |  | ||||||
|         ], |  | ||||||
|         "definitions": { |         "definitions": { | ||||||
|             "ChannelType": { |             "ChannelType": { | ||||||
|                 "enum": [ |                 "enum": [ | ||||||
| @ -7446,5 +7442,256 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "$schema": "http://json-schema.org/draft-07/schema#" |         "$schema": "http://json-schema.org/draft-07/schema#" | ||||||
|  |     }, | ||||||
|  |     "WebhookModifySchema": { | ||||||
|  |         "type": "object", | ||||||
|  |         "properties": { | ||||||
|  |             "name": { | ||||||
|  |                 "type": "string" | ||||||
|  |             }, | ||||||
|  |             "avatar": { | ||||||
|  |                 "type": "string" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "additionalProperties": false, | ||||||
|  |         "definitions": { | ||||||
|  |             "ChannelType": { | ||||||
|  |                 "enum": [ | ||||||
|  |                     0, | ||||||
|  |                     1, | ||||||
|  |                     10, | ||||||
|  |                     11, | ||||||
|  |                     12, | ||||||
|  |                     13, | ||||||
|  |                     2, | ||||||
|  |                     3, | ||||||
|  |                     4, | ||||||
|  |                     5, | ||||||
|  |                     6 | ||||||
|  |                 ], | ||||||
|  |                 "type": "number" | ||||||
|  |             }, | ||||||
|  |             "ChannelPermissionOverwriteType": { | ||||||
|  |                 "enum": [ | ||||||
|  |                     0, | ||||||
|  |                     1 | ||||||
|  |                 ], | ||||||
|  |                 "type": "number" | ||||||
|  |             }, | ||||||
|  |             "Embed": { | ||||||
|  |                 "type": "object", | ||||||
|  |                 "properties": { | ||||||
|  |                     "title": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "type": { | ||||||
|  |                         "enum": [ | ||||||
|  |                             "article", | ||||||
|  |                             "gifv", | ||||||
|  |                             "image", | ||||||
|  |                             "link", | ||||||
|  |                             "rich", | ||||||
|  |                             "video" | ||||||
|  |                         ], | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "description": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "url": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "timestamp": { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "format": "date-time" | ||||||
|  |                     }, | ||||||
|  |                     "color": { | ||||||
|  |                         "type": "integer" | ||||||
|  |                     }, | ||||||
|  |                     "footer": { | ||||||
|  |                         "type": "object", | ||||||
|  |                         "properties": { | ||||||
|  |                             "text": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             }, | ||||||
|  |                             "icon_url": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             }, | ||||||
|  |                             "proxy_icon_url": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             } | ||||||
|  |                         }, | ||||||
|  |                         "additionalProperties": false, | ||||||
|  |                         "required": [ | ||||||
|  |                             "text" | ||||||
|  |                         ] | ||||||
|  |                     }, | ||||||
|  |                     "image": { | ||||||
|  |                         "$ref": "#/definitions/EmbedImage" | ||||||
|  |                     }, | ||||||
|  |                     "thumbnail": { | ||||||
|  |                         "$ref": "#/definitions/EmbedImage" | ||||||
|  |                     }, | ||||||
|  |                     "video": { | ||||||
|  |                         "$ref": "#/definitions/EmbedImage" | ||||||
|  |                     }, | ||||||
|  |                     "provider": { | ||||||
|  |                         "type": "object", | ||||||
|  |                         "properties": { | ||||||
|  |                             "name": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             }, | ||||||
|  |                             "url": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             } | ||||||
|  |                         }, | ||||||
|  |                         "additionalProperties": false | ||||||
|  |                     }, | ||||||
|  |                     "author": { | ||||||
|  |                         "type": "object", | ||||||
|  |                         "properties": { | ||||||
|  |                             "name": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             }, | ||||||
|  |                             "url": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             }, | ||||||
|  |                             "icon_url": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             }, | ||||||
|  |                             "proxy_icon_url": { | ||||||
|  |                                 "type": "string" | ||||||
|  |                             } | ||||||
|  |                         }, | ||||||
|  |                         "additionalProperties": false | ||||||
|  |                     }, | ||||||
|  |                     "fields": { | ||||||
|  |                         "type": "array", | ||||||
|  |                         "items": { | ||||||
|  |                             "type": "object", | ||||||
|  |                             "properties": { | ||||||
|  |                                 "name": { | ||||||
|  |                                     "type": "string" | ||||||
|  |                                 }, | ||||||
|  |                                 "value": { | ||||||
|  |                                     "type": "string" | ||||||
|  |                                 }, | ||||||
|  |                                 "inline": { | ||||||
|  |                                     "type": "boolean" | ||||||
|  |                                 } | ||||||
|  |                             }, | ||||||
|  |                             "additionalProperties": false, | ||||||
|  |                             "required": [ | ||||||
|  |                                 "name", | ||||||
|  |                                 "value" | ||||||
|  |                             ] | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 "additionalProperties": false | ||||||
|  |             }, | ||||||
|  |             "EmbedImage": { | ||||||
|  |                 "type": "object", | ||||||
|  |                 "properties": { | ||||||
|  |                     "url": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "proxy_url": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "height": { | ||||||
|  |                         "type": "integer" | ||||||
|  |                     }, | ||||||
|  |                     "width": { | ||||||
|  |                         "type": "integer" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 "additionalProperties": false | ||||||
|  |             }, | ||||||
|  |             "ChannelModifySchema": { | ||||||
|  |                 "type": "object", | ||||||
|  |                 "properties": { | ||||||
|  |                     "name": { | ||||||
|  |                         "maxLength": 100, | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "type": { | ||||||
|  |                         "$ref": "#/definitions/ChannelType" | ||||||
|  |                     }, | ||||||
|  |                     "topic": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "bitrate": { | ||||||
|  |                         "type": "integer" | ||||||
|  |                     }, | ||||||
|  |                     "user_limit": { | ||||||
|  |                         "type": "integer" | ||||||
|  |                     }, | ||||||
|  |                     "rate_limit_per_user": { | ||||||
|  |                         "type": "integer" | ||||||
|  |                     }, | ||||||
|  |                     "position": { | ||||||
|  |                         "type": "integer" | ||||||
|  |                     }, | ||||||
|  |                     "permission_overwrites": { | ||||||
|  |                         "type": "array", | ||||||
|  |                         "items": { | ||||||
|  |                             "type": "object", | ||||||
|  |                             "properties": { | ||||||
|  |                                 "id": { | ||||||
|  |                                     "type": "string" | ||||||
|  |                                 }, | ||||||
|  |                                 "type": { | ||||||
|  |                                     "$ref": "#/definitions/ChannelPermissionOverwriteType" | ||||||
|  |                                 }, | ||||||
|  |                                 "allow": { | ||||||
|  |                                     "type": "bigint" | ||||||
|  |                                 }, | ||||||
|  |                                 "deny": { | ||||||
|  |                                     "type": "bigint" | ||||||
|  |                                 } | ||||||
|  |                             }, | ||||||
|  |                             "additionalProperties": false, | ||||||
|  |                             "required": [ | ||||||
|  |                                 "allow", | ||||||
|  |                                 "deny", | ||||||
|  |                                 "id", | ||||||
|  |                                 "type" | ||||||
|  |                             ] | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |                     "parent_id": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "id": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "nsfw": { | ||||||
|  |                         "type": "boolean" | ||||||
|  |                     }, | ||||||
|  |                     "rtc_region": { | ||||||
|  |                         "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "default_auto_archive_duration": { | ||||||
|  |                         "type": "integer" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 "additionalProperties": false, | ||||||
|  |                 "required": [ | ||||||
|  |                     "name", | ||||||
|  |                     "type" | ||||||
|  |                 ] | ||||||
|  |             }, | ||||||
|  |             "RelationshipType": { | ||||||
|  |                 "enum": [ | ||||||
|  |                     1, | ||||||
|  |                     2, | ||||||
|  |                     3, | ||||||
|  |                     4 | ||||||
|  |                 ], | ||||||
|  |                 "type": "number" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "$schema": "http://json-schema.org/draft-07/schema#" | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -11,7 +11,7 @@ | |||||||
| 			window.__OVERLAY__ = /overlay/.test(location.pathname); | 			window.__OVERLAY__ = /overlay/.test(location.pathname); | ||||||
| 			window.__BILLING_STANDALONE__ = /^\/billing/.test(location.pathname); | 			window.__BILLING_STANDALONE__ = /^\/billing/.test(location.pathname); | ||||||
| 			window.GLOBAL_ENV = { | 			window.GLOBAL_ENV = { | ||||||
| 				API_ENDPOINT: "/api", | 				API_ENDPOINT: `//${location.host}/api`, | ||||||
| 				API_VERSION: 9, | 				API_VERSION: 9, | ||||||
| 				GATEWAY_ENDPOINT: `${location.protocol === "https:" ? "wss://" : "ws://"}${location.hostname}:3002`, | 				GATEWAY_ENDPOINT: `${location.protocol === "https:" ? "wss://" : "ws://"}${location.hostname}:3002`, | ||||||
| 				WEBAPP_ENDPOINT: "", | 				WEBAPP_ENDPOINT: "", | ||||||
|  | |||||||
| @ -5,11 +5,11 @@ import { checkToken, Config } from "@fosscord/util"; | |||||||
| export const NO_AUTHORIZATION_ROUTES = [ | export const NO_AUTHORIZATION_ROUTES = [ | ||||||
| 	"/auth/login", | 	"/auth/login", | ||||||
| 	"/auth/register", | 	"/auth/register", | ||||||
| 	"/webhooks/", |  | ||||||
| 	"/ping", | 	"/ping", | ||||||
| 	"/gateway", | 	"/gateway", | ||||||
| 	"/experiments", | 	"/experiments", | ||||||
| 	/\/guilds\/\d+\/widget\.(json|png)/ | 	/\/guilds\/\d+\/widget\.(json|png)/, | ||||||
|  | 	/\/webhooks\/\d+\/\w+/ // only exclude webhook calls with webhook token
 | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| export const API_PREFIX = /^\/api(\/v\d+)?/; | export const API_PREFIX = /^\/api(\/v\d+)?/; | ||||||
|  | |||||||
| @ -1,9 +1,10 @@ | |||||||
| import { NextFunction, Request, Response } from "express"; | import { NextFunction, Request, Response } from "express"; | ||||||
| import { HTTPError } from "lambert-server"; | import { HTTPError } from "lambert-server"; | ||||||
| import { EntityNotFoundError } from "typeorm"; |  | ||||||
| import { FieldError } from "@fosscord/api"; | import { FieldError } from "@fosscord/api"; | ||||||
| import { ApiError } from "@fosscord/util"; | import { ApiError } from "@fosscord/util"; | ||||||
| 
 | 
 | ||||||
|  | const EntityNotFoundErrorRegex = /"(\w+)"/; | ||||||
|  | 
 | ||||||
| export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) { | export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) { | ||||||
| 	if (!error) return next(); | 	if (!error) return next(); | ||||||
| 
 | 
 | ||||||
| @ -18,8 +19,8 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne | |||||||
| 			code = error.code; | 			code = error.code; | ||||||
| 			message = error.message; | 			message = error.message; | ||||||
| 			httpcode = error.httpStatus; | 			httpcode = error.httpStatus; | ||||||
| 		} else if (error instanceof EntityNotFoundError) { | 		} else if (error.name === "EntityNotFoundError") { | ||||||
| 			message = `${(error as any).stringifyTarget || "Item"} could not be found`; | 			message = `${error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"} could not be found`; | ||||||
| 			code = 404; | 			code = 404; | ||||||
| 		} else if (error instanceof FieldError) { | 		} else if (error instanceof FieldError) { | ||||||
| 			code = Number(error.code); | 			code = Number(error.code); | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { | |||||||
| 	// ! this only works using SQL querys
 | 	// ! this only works using SQL querys
 | ||||||
| 	// TODO: implement this with default typeorm query
 | 	// TODO: implement this with default typeorm query
 | ||||||
| 	// const guilds = await Guild.find({ where: { features: "DISCOVERABLE" } }); //, take: Math.abs(Number(limit)) });
 | 	// const guilds = await Guild.find({ where: { features: "DISCOVERABLE" } }); //, take: Math.abs(Number(limit)) });
 | ||||||
| 	const guilds = await Guild.find({ where: `"features" LIKE 'COMMUNITY'`, take: Math.abs(Number(limit)) }); | 	const guilds = await Guild.find({ where: `"features" LIKE 'COMMUNITY'`, take: Math.abs(Number(limit) || 50) }); | ||||||
| 	res.send({ guilds: guilds }); | 	res.send({ guilds: guilds }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								api/src/routes/guilds/#guild_id/integrations.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								api/src/routes/guilds/#guild_id/integrations.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | import { route } from "@fosscord/api"; | ||||||
|  | import { Router, Request, Response } from "express"; | ||||||
|  | const router = Router(); | ||||||
|  | 
 | ||||||
|  | router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => { | ||||||
|  | 	// TODO: integrations (followed channels, youtube, twitch)
 | ||||||
|  | 	res.send([]); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export default router; | ||||||
| @ -4,7 +4,7 @@ import { Router, Request, Response } from "express"; | |||||||
| const router = Router(); | const router = Router(); | ||||||
| 
 | 
 | ||||||
| router.get("/", async (req: Request, res: Response) => { | router.get("/", async (req: Request, res: Response) => { | ||||||
| 	res.send({}); | 	res.json({}); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export default router; | export default router; | ||||||
|  | |||||||
							
								
								
									
										89
									
								
								api/src/routes/webhooks/#webhook_id/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								api/src/routes/webhooks/#webhook_id/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | |||||||
|  | import { Channel, Config, emitEvent, JWTOptions, Webhook, WebhooksUpdateEvent } from "@fosscord/util"; | ||||||
|  | import { route, Authentication, handleFile } from "@fosscord/api"; | ||||||
|  | import { Router, Request, Response, NextFunction } from "express"; | ||||||
|  | import jwt from "jsonwebtoken"; | ||||||
|  | import { HTTPError } from "lambert-server"; | ||||||
|  | const router = Router(); | ||||||
|  | 
 | ||||||
|  | export interface WebhookModifySchema { | ||||||
|  | 	name?: string; | ||||||
|  | 	avatar?: string; | ||||||
|  | 	// channel_id?: string; // TODO
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function validateWebhookToken(req: Request, res: Response, next: NextFunction) { | ||||||
|  | 	const { jwtSecret } = Config.get().security; | ||||||
|  | 
 | ||||||
|  | 	jwt.verify(req.params.token, jwtSecret, JWTOptions, async (err, decoded: any) => { | ||||||
|  | 		if (err) return next(new HTTPError("Invalid Token", 401)); | ||||||
|  | 		next(); | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | router.get("/", route({}), async (req: Request, res: Response) => { | ||||||
|  | 	res.json(await Webhook.findOneOrFail({ id: req.params.webhook_id })); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | router.get("/:token", route({}), validateWebhookToken, async (req: Request, res: Response) => { | ||||||
|  | 	res.json(await Webhook.findOneOrFail({ id: req.params.webhook_id })); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | router.patch("/", route({ body: "WebhookModifySchema", permission: "MANAGE_WEBHOOKS" }), (req: Request, res: Response) => { | ||||||
|  | 	return updateWebhook(req, res); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | router.patch("/:token", route({ body: "WebhookModifySchema" }), validateWebhookToken, (req: Request, res: Response) => { | ||||||
|  | 	return updateWebhook(req, res); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | async function updateWebhook(req: Request, res: Response) { | ||||||
|  | 	const webhook = await Webhook.findOneOrFail({ id: req.params.webhook_id }); | ||||||
|  | 	if (req.body.channel_id) await Channel.findOneOrFail({ id: req.body.channel_id, guild_id: webhook.guild_id }); | ||||||
|  | 
 | ||||||
|  | 	webhook.assign({ | ||||||
|  | 		...req.body, | ||||||
|  | 		avatar: await handleFile(`/icons/${req.params.webhook_id}`, req.body.avatar) | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	await Promise.all([ | ||||||
|  | 		emitEvent({ | ||||||
|  | 			event: "WEBHOOKS_UPDATE", | ||||||
|  | 			channel_id: webhook.channel_id, | ||||||
|  | 			data: { | ||||||
|  | 				channel_id: webhook.channel_id, | ||||||
|  | 				guild_id: webhook.guild_id | ||||||
|  | 			} | ||||||
|  | 		} as WebhooksUpdateEvent), | ||||||
|  | 		webhook.save() | ||||||
|  | 	]); | ||||||
|  | 
 | ||||||
|  | 	res.json(webhook); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | router.delete("/", route({ permission: "MANAGE_WEBHOOKS" }), async (req: Request, res: Response) => { | ||||||
|  | 	return deleteWebhook(req, res); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | router.delete("/:token", route({}), validateWebhookToken, (req: Request, res: Response) => { | ||||||
|  | 	return deleteWebhook(req, res); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | async function deleteWebhook(req: Request, res: Response) { | ||||||
|  | 	const webhook = await Webhook.findOneOrFail({ id: req.params.webhook_id }); | ||||||
|  | 
 | ||||||
|  | 	await Promise.all([ | ||||||
|  | 		emitEvent({ | ||||||
|  | 			event: "WEBHOOKS_UPDATE", | ||||||
|  | 			channel_id: webhook.channel_id, | ||||||
|  | 			data: { | ||||||
|  | 				channel_id: webhook.channel_id, | ||||||
|  | 				guild_id: webhook.guild_id | ||||||
|  | 			} | ||||||
|  | 		} as WebhooksUpdateEvent), | ||||||
|  | 		webhook.remove() | ||||||
|  | 	]); | ||||||
|  | 
 | ||||||
|  | 	res.sendStatus(204); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default router; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| import { DiscordApiErrors, Event, EventData, getPermission, PermissionResolvable, Permissions } from "@fosscord/util"; | import { DiscordApiErrors, Event, EventData, getPermission, PermissionResolvable, Permissions, Webhook } from "@fosscord/util"; | ||||||
| import { NextFunction, Request, Response } from "express"; | import { NextFunction, Request, Response } from "express"; | ||||||
| import fs from "fs"; | import fs from "fs"; | ||||||
| import path from "path"; | import path from "path"; | ||||||
| @ -54,9 +54,13 @@ export function route(opts: RouteOptions) { | |||||||
| 	return async (req: Request, res: Response, next: NextFunction) => { | 	return async (req: Request, res: Response, next: NextFunction) => { | ||||||
| 		if (opts.permission) { | 		if (opts.permission) { | ||||||
| 			const required = new Permissions(opts.permission); | 			const required = new Permissions(opts.permission); | ||||||
|  | 			if (req.params.webhook_id) { | ||||||
|  | 				const webhook = await Webhook.findOneOrFail({ id: req.params.webhook_id }); | ||||||
|  | 				req.params.channel_id = webhook.channel_id; | ||||||
|  | 				req.params.guild_id = webhook.guild_id; | ||||||
|  | 			} | ||||||
| 			const permission = await getPermission(req.user_id, req.params.guild_id, req.params.channel_id); | 			const permission = await getPermission(req.user_id, req.params.guild_id, req.params.channel_id); | ||||||
| 
 | 
 | ||||||
| 			// bitfield comparison: check if user lacks certain permission
 |  | ||||||
| 			if (!permission.has(required)) { | 			if (!permission.has(required)) { | ||||||
| 				throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(opts.permission as string); | 				throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(opts.permission as string); | ||||||
| 			} | 			} | ||||||
|  | |||||||
| @ -18,13 +18,13 @@ export class Webhook extends BaseClass { | |||||||
| 	@Column({ type: "simple-enum", enum: WebhookType }) | 	@Column({ type: "simple-enum", enum: WebhookType }) | ||||||
| 	type: WebhookType; | 	type: WebhookType; | ||||||
| 
 | 
 | ||||||
| 	@Column({ nullable: true }) | 	@Column() | ||||||
| 	name?: string; | 	name: string; | ||||||
| 
 | 
 | ||||||
| 	@Column({ nullable: true }) | 	@Column({ nullable: true }) | ||||||
| 	avatar?: string; | 	avatar?: string; | ||||||
| 
 | 
 | ||||||
| 	@Column({ nullable: true }) | 	@Column({ nullable: true, select: false }) | ||||||
| 	token?: string; | 	token?: string; | ||||||
| 
 | 
 | ||||||
| 	@Column({ nullable: true }) | 	@Column({ nullable: true }) | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| export const DOUBLE_WHITE_SPACE = /\s\s+/g; | export const DOUBLE_WHITE_SPACE = /\s\s+/g; | ||||||
| export const SPECIAL_CHAR = /[@#`:\r\n\t\f\v\p{C}]/gu; | export const SPECIAL_CHAR = /[@#\r\n\t\f\v]/gu; | ||||||
| export const CHANNEL_MENTION = /<#(\d+)>/g; | export const CHANNEL_MENTION = /<#(\d+)>/g; | ||||||
| export const USER_MENTION = /<@!?(\d+)>/g; | export const USER_MENTION = /<@!?(\d+)>/g; | ||||||
| export const ROLE_MENTION = /<@&(\d+)>/g; | export const ROLE_MENTION = /<@&(\d+)>/g; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Flam3rboy
						Flam3rboy