webhook fixes & username/avatar property for msg

This commit is contained in:
TomatoCake 2024-07-18 12:42:07 +02:00
parent 99d9bf563f
commit e7a98b6c46
9 changed files with 1459 additions and 76 deletions

View File

@ -1127,12 +1127,19 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"channel_ordering": {
"type": "array",
"items": {
"type": "string"
}
},
"id": { "id": {
"type": "string" "type": "string"
} }
}, },
"required": [ "required": [
"bans", "bans",
"channel_ordering",
"channels", "channels",
"emojis", "emojis",
"features", "features",
@ -1203,9 +1210,6 @@
"default_auto_archive_duration": { "default_auto_archive_duration": {
"type": "integer" "type": "integer"
}, },
"position": {
"type": "integer"
},
"permission_overwrites": { "permission_overwrites": {
"type": "array", "type": "array",
"items": { "items": {
@ -1272,6 +1276,10 @@
"type": "integer", "type": "integer",
"default": 0 "default": 0
}, },
"position": {
"description": "Must be calculated Channel.calculatePosition",
"type": "integer"
},
"id": { "id": {
"type": "string" "type": "string"
} }
@ -1280,11 +1288,11 @@
"created_at", "created_at",
"default_thread_rate_limit_per_user", "default_thread_rate_limit_per_user",
"flags", "flags",
"guild",
"id", "id",
"nsfw", "nsfw",
"owner", "owner",
"parent_id", "parent_id",
"position",
"type" "type"
] ]
}, },
@ -2138,7 +2146,7 @@
"attachments": { "attachments": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/components/schemas/Attachment_1" "$ref": "#/components/schemas/Attachment"
} }
}, },
"embeds": { "embeds": {
@ -2324,7 +2332,6 @@
} }
}, },
"required": [ "required": [
"avatar",
"banner", "banner",
"bio", "bio",
"communication_disabled_until", "communication_disabled_until",
@ -2889,7 +2896,7 @@
], ],
"type": "number" "type": "number"
}, },
"Attachment_1": { "Attachment": {
"type": "object", "type": "object",
"properties": { "properties": {
"filename": { "filename": {
@ -3394,7 +3401,7 @@
"attachments": { "attachments": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/components/schemas/Attachment_1" "$ref": "#/components/schemas/Attachment"
} }
}, },
"embeds": { "embeds": {
@ -3473,6 +3480,9 @@
"type": "string", "type": "string",
"format": "date-time" "format": "date-time"
}, },
"avatar": {
"type": "string"
},
"username": { "username": {
"type": "string" "type": "string"
}, },
@ -3482,9 +3492,6 @@
"public_flags": { "public_flags": {
"type": "integer" "type": "integer"
}, },
"avatar": {
"type": "string"
},
"accent_color": { "accent_color": {
"type": "integer" "type": "integer"
}, },
@ -3721,6 +3728,9 @@
"banner": { "banner": {
"type": "string" "type": "string"
}, },
"description": {
"type": "string"
},
"unavailable": { "unavailable": {
"type": "boolean" "type": "boolean"
}, },
@ -3757,9 +3767,6 @@
"default_message_notifications": { "default_message_notifications": {
"type": "integer" "type": "integer"
}, },
"description": {
"type": "string"
},
"discovery_splash": { "discovery_splash": {
"type": "string" "type": "string"
}, },
@ -3852,12 +3859,19 @@
}, },
"premium_progress_bar_enabled": { "premium_progress_bar_enabled": {
"type": "boolean" "type": "boolean"
},
"channel_ordering": {
"type": "array",
"items": {
"type": "string"
}
} }
}, },
"required": [ "required": [
"_do_validate", "_do_validate",
"assign", "assign",
"bans", "bans",
"channel_ordering",
"channels", "channels",
"emojis", "emojis",
"features", "features",
@ -5081,6 +5095,9 @@
"content": { "content": {
"type": "string" "type": "string"
}, },
"mobile_network_type": {
"type": "string"
},
"nonce": { "nonce": {
"type": "string" "type": "string"
}, },
@ -5191,6 +5208,10 @@
"items": { "items": {
"type": "string" "type": "string"
} }
},
"components": {
"type": "array",
"items": {}
} }
} }
}, },
@ -5218,6 +5239,9 @@
"content": { "content": {
"type": "string" "type": "string"
}, },
"mobile_network_type": {
"type": "string"
},
"nonce": { "nonce": {
"type": "string" "type": "string"
}, },
@ -5310,6 +5334,10 @@
"items": { "items": {
"type": "string" "type": "string"
} }
},
"components": {
"type": "array",
"items": {}
} }
} }
}, },
@ -5834,6 +5862,16 @@
"UserSettingsSchema": { "UserSettingsSchema": {
"type": "object", "type": "object",
"properties": { "properties": {
"status": {
"enum": [
"dnd",
"idle",
"invisible",
"offline",
"online"
],
"type": "string"
},
"afk_timeout": { "afk_timeout": {
"type": "integer" "type": "integer"
}, },
@ -5931,16 +5969,6 @@
"show_current_game": { "show_current_game": {
"type": "boolean" "type": "boolean"
}, },
"status": {
"enum": [
"dnd",
"idle",
"invisible",
"offline",
"online"
],
"type": "string"
},
"stream_notifications_enabled": { "stream_notifications_enabled": {
"type": "boolean" "type": "boolean"
}, },
@ -6181,6 +6209,107 @@
"name" "name"
] ]
}, },
"WebhookExecuteSchema": {
"type": "object",
"properties": {
"content": {
"type": "string"
},
"username": {
"type": "string"
},
"avatar_url": {
"type": "string"
},
"tts": {
"type": "boolean"
},
"embeds": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Embed"
}
},
"allowed_mentions": {
"type": "object",
"properties": {
"parse": {
"type": "array",
"items": {
"type": "string"
}
},
"roles": {
"type": "array",
"items": {
"type": "string"
}
},
"users": {
"type": "array",
"items": {
"type": "string"
}
},
"replied_user": {
"type": "boolean"
}
},
"additionalProperties": false
},
"components": {
"type": "array",
"items": {}
},
"file": {
"type": "object",
"properties": {
"filename": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"filename"
]
},
"payload_json": {
"type": "string"
},
"attachments": {
"description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"filename": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"filename",
"id"
]
}
},
"flags": {
"type": "integer"
},
"thread_name": {
"type": "string"
},
"applied_tags": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"WidgetModifySchema": { "WidgetModifySchema": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -6960,6 +7089,9 @@
"type": "string", "type": "string",
"format": "date-time" "format": "date-time"
}, },
"avatar": {
"type": "string"
},
"verified": { "verified": {
"type": "boolean" "type": "boolean"
}, },
@ -6972,9 +7104,6 @@
"public_flags": { "public_flags": {
"type": "integer" "type": "integer"
}, },
"avatar": {
"type": "string"
},
"accent_color": { "accent_color": {
"type": "integer" "type": "integer"
}, },
@ -7077,6 +7206,9 @@
"type": "string", "type": "string",
"format": "date-time" "format": "date-time"
}, },
"avatar": {
"type": "string"
},
"verified": { "verified": {
"type": "boolean" "type": "boolean"
}, },
@ -7089,9 +7221,6 @@
"public_flags": { "public_flags": {
"type": "integer" "type": "integer"
}, },
"avatar": {
"type": "string"
},
"accent_color": { "accent_color": {
"type": "integer" "type": "integer"
}, },
@ -7305,6 +7434,9 @@
"premium_since": { "premium_since": {
"type": "integer" "type": "integer"
}, },
"avatar": {
"type": "string"
},
"user": { "user": {
"$ref": "#/components/schemas/PublicUser" "$ref": "#/components/schemas/PublicUser"
}, },
@ -7533,12 +7665,19 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"channel_ordering": {
"type": "array",
"items": {
"type": "string"
}
},
"id": { "id": {
"type": "string" "type": "string"
} }
}, },
"required": [ "required": [
"bans", "bans",
"channel_ordering",
"channels", "channels",
"emojis", "emojis",
"features", "features",
@ -7737,6 +7876,9 @@
"premium_since": { "premium_since": {
"type": "integer" "type": "integer"
}, },
"avatar": {
"type": "string"
},
"user": { "user": {
"$ref": "#/components/schemas/PublicUser" "$ref": "#/components/schemas/PublicUser"
}, },
@ -8037,9 +8179,175 @@
}, },
{ {
"name": "voice" "name": "voice"
},
{
"name": "webhooks"
} }
], ],
"paths": { "paths": {
"/webhooks/{webhook_id}/": {
"get": {
"security": [
{
"bearer": []
}
],
"description": "Returns a webhook object for the given id.",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIWebhook"
}
}
}
},
"404": {
"description": "No description available"
}
},
"parameters": [
{
"name": "webhook_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "webhook_id"
}
],
"tags": [
"webhooks"
]
}
},
"/webhooks/{webhook_id}/{token}/": {
"get": {
"security": [
{
"bearer": []
}
],
"description": "Returns a webhook object for the given id.",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIWebhook"
}
}
}
},
"404": {
"description": "No description available"
}
},
"parameters": [
{
"name": "webhook_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "webhook_id"
},
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "token"
}
],
"tags": [
"webhooks"
]
},
"post": {
"security": [
{
"bearer": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WebhookExecuteSchema"
}
}
}
},
"responses": {
"204": {
"description": "No description available"
},
"400": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIErrorResponse"
}
}
}
},
"404": {
"description": "No description available"
}
},
"parameters": [
{
"name": "webhook_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "webhook_id"
},
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "token"
},
{
"name": "wait",
"in": "query",
"required": false,
"schema": {
"type": "boolean"
},
"description": "waits for server confirmation of message send before response, and returns the created message body"
},
{
"name": "thread_id",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "Send a message to the specified thread within a webhook's channel."
}
],
"tags": [
"webhooks"
]
}
},
"/voice/regions/": { "/voice/regions/": {
"get": { "get": {
"security": [ "security": [
@ -10189,6 +10497,15 @@
} }
} }
}, },
"parameters": [
{
"name": "client_id",
"in": "query",
"schema": {
"type": "string"
}
}
],
"tags": [ "tags": [
"oauth2" "oauth2"
] ]
@ -10265,6 +10582,30 @@
] ]
} }
}, },
"/oauth2/applications/@me/": {
"get": {
"security": [
{
"bearer": []
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Application"
}
}
}
}
},
"tags": [
"oauth2"
]
}
},
"/invites/{code}": { "/invites/{code}": {
"get": { "get": {
"security": [ "security": [
@ -10879,14 +11220,23 @@
}, },
"/guilds/{guild_id}/webhooks/": { "/guilds/{guild_id}/webhooks/": {
"get": { "get": {
"x-permission-required": "MANAGE_WEBHOOKS",
"security": [ "security": [
{ {
"bearer": [] "bearer": []
} }
], ],
"description": "Returns a list of guild webhook objects. Requires the MANAGE_WEBHOOKS permission.",
"responses": { "responses": {
"default": { "200": {
"description": "No description available" "description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/APIWebhookArray"
}
}
}
} }
}, },
"parameters": [ "parameters": [
@ -14363,11 +14713,13 @@
}, },
"/channels/{channel_id}/webhooks/": { "/channels/{channel_id}/webhooks/": {
"get": { "get": {
"x-permission-required": "MANAGE_WEBHOOKS",
"security": [ "security": [
{ {
"bearer": [] "bearer": []
} }
], ],
"description": "Returns a list of channel webhook objects. Requires the MANAGE_WEBHOOKS permission.",
"responses": { "responses": {
"200": { "200": {
"description": "", "description": "",

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,14 @@
import { handleMessage, route } from "@spacebar/api"; import { handleMessage, postHandleMessage, route } from "@spacebar/api";
import { import {
Attachment, Attachment,
Config, Config,
DiscordApiErrors, DiscordApiErrors,
FieldErrors, FieldErrors,
Message, Message,
MessageCreateEvent,
Webhook, Webhook,
WebhookExecuteSchema, WebhookExecuteSchema,
emitEvent,
uploadFile, uploadFile,
} from "@spacebar/util"; } from "@spacebar/util";
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
@ -93,7 +95,11 @@ router.post(
}, },
}), }),
async (req: Request, res: Response) => { async (req: Request, res: Response) => {
const { wait, thread_id } = req.query;
if (!wait) return res.status(204).send();
const { webhook_id, token } = req.params; const { webhook_id, token } = req.params;
const body = req.body as WebhookExecuteSchema; const body = req.body as WebhookExecuteSchema;
const attachments: Attachment[] = []; const attachments: Attachment[] = [];
@ -200,6 +206,7 @@ router.post(
webhook_id: webhook.id, webhook_id: webhook.id,
application_id: webhook.application?.id, application_id: webhook.application?.id,
embeds, embeds,
// TODO: Support thread_id/thread_name once threads are implemented
channel_id: webhook.channel_id, channel_id: webhook.channel_id,
attachments, attachments,
timestamp: new Date(), timestamp: new Date(),
@ -209,6 +216,22 @@ router.post(
message.edited_timestamp = null; message.edited_timestamp = null;
webhook.channel.last_message_id = message.id; webhook.channel.last_message_id = message.id;
await Promise.all([
message.save(),
emitEvent({
event: "MESSAGE_CREATE",
channel_id: webhook.channel_id,
data: message,
} as MessageCreateEvent),
]);
// no await as it shouldnt block the message send function and silently catch error
postHandleMessage(message).catch((e) =>
console.error("[Message] post-message handler failed", e),
);
return res.json(message);
}, },
); );

View File

@ -1,17 +1,17 @@
/* /*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend. Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
@ -41,11 +41,13 @@ import {
Sticker, Sticker,
MessageCreateSchema, MessageCreateSchema,
EmbedCache, EmbedCache,
handleFile,
} from "@spacebar/util"; } from "@spacebar/util";
import { HTTPError } from "lambert-server"; import { HTTPError } from "lambert-server";
import { In } from "typeorm"; import { In } from "typeorm";
import { EmbedHandlers } from "@spacebar/api"; import { EmbedHandlers } from "@spacebar/api";
import * as Sentry from "@sentry/node"; import * as Sentry from "@sentry/node";
import fetch from "node-fetch";
const allow_empty = false; const allow_empty = false;
// TODO: check webhook, application, system author, stickers // TODO: check webhook, application, system author, stickers
// TODO: embed gifs/videos/images // TODO: embed gifs/videos/images
@ -92,44 +94,89 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
where: { id: opts.application_id }, where: { id: opts.application_id },
}); });
} }
let permission: any;
if (opts.webhook_id) { if (opts.webhook_id) {
message.webhook = await Webhook.findOneOrFail({ message.webhook = await Webhook.findOneOrFail({
where: { id: opts.webhook_id }, where: { id: opts.webhook_id },
}); });
}
const permission = await getPermission( message.author = (await User.findOne({
opts.author_id, where: { id: opts.webhook_id },
channel.guild_id, })) || undefined;
opts.channel_id,
);
permission.hasThrow("SEND_MESSAGES");
if (permission.cache.member) {
message.member = permission.cache.member;
}
if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES"); if (!message.author) {
if (opts.message_reference) { message.author = User.create({
permission.hasThrow("READ_MESSAGE_HISTORY"); id: opts.webhook_id,
// code below has to be redone when we add custom message routing username: message.webhook.name,
if (message.guild_id !== null) { discriminator: "0000",
const guild = await Guild.findOneOrFail({ avatar: message.webhook.avatar,
where: { id: channel.guild_id }, public_flags: 0,
premium: false,
premium_type: 0,
bot: true,
created_at: new Date(),
verified: true,
rights: "0",
data: {
valid_tokens_since: new Date(),
},
}); });
if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) {
if (opts.message_reference.guild_id !== channel.guild_id) await message.author.save();
throw new HTTPError( }
"You can only reference messages from this guild",
); if (opts.username) {
if (opts.message_reference.channel_id !== opts.channel_id) message.username = opts.username;
throw new HTTPError( message.author.username = message.username;
"You can only reference messages from this channel", }
); if (opts.avatar_url) {
} const avatarData = await fetch(opts.avatar_url);
const base64 = await avatarData.buffer().then((x) => x.toString("base64"));
const dataUri = "data:" + avatarData.headers.get("content-type") + ";base64," + base64;
message.avatar = await handleFile(
`/avatars/${opts.webhook_id}`,
dataUri as string,
);
console.log(message.avatar);
message.author.avatar = message.avatar;
}
} else {
permission = await getPermission(
opts.author_id,
channel.guild_id,
opts.channel_id,
);
permission.hasThrow("SEND_MESSAGES");
if (permission.cache.member) {
message.member = permission.cache.member;
}
if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES");
if (opts.message_reference) {
permission.hasThrow("READ_MESSAGE_HISTORY");
// code below has to be redone when we add custom message routing
if (message.guild_id !== null) {
const guild = await Guild.findOneOrFail({
where: { id: channel.guild_id },
});
if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) {
if (opts.message_reference.guild_id !== channel.guild_id)
throw new HTTPError(
"You can only reference messages from this guild",
);
if (opts.message_reference.channel_id !== opts.channel_id)
throw new HTTPError(
"You can only reference messages from this channel",
);
}
}
/** Q: should be checked if the referenced message exists? ANSWER: NO
otherwise backfilling won't work **/
message.type = MessageType.REPLY;
} }
/** Q: should be checked if the referenced message exists? ANSWER: NO
otherwise backfilling won't work **/
message.type = MessageType.REPLY;
} }
// TODO: stickers/activity // TODO: stickers/activity
@ -172,14 +219,14 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
const role = await Role.findOneOrFail({ const role = await Role.findOneOrFail({
where: { id: mention, guild_id: channel.guild_id }, where: { id: mention, guild_id: channel.guild_id },
}); });
if (role.mentionable || permission.has("MANAGE_ROLES")) { if (role.mentionable || (opts.webhook_id || permission.has("MANAGE_ROLES"))) {
mention_role_ids.push(mention); mention_role_ids.push(mention);
} }
}, },
), ),
); );
if (permission.has("MENTION_EVERYONE")) { if (opts.webhook_id || permission.has("MENTION_EVERYONE")) {
mention_everyone = mention_everyone =
!!content.match(EVERYONE_MENTION) || !!content.match(EVERYONE_MENTION) ||
!!content.match(HERE_MENTION); !!content.match(HERE_MENTION);
@ -302,4 +349,6 @@ interface MessageOptions extends MessageCreateSchema {
attachments?: Attachment[]; attachments?: Attachment[];
edited_timestamp?: Date; edited_timestamp?: Date;
timestamp?: Date; timestamp?: Date;
username?: string;
avatar_url?: string;
} }

View File

@ -1,17 +1,17 @@
/* /*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend. Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
@ -218,6 +218,12 @@ export class Message extends BaseClass {
@Column({ type: "simple-json", nullable: true }) @Column({ type: "simple-json", nullable: true })
components?: MessageComponent[]; components?: MessageComponent[];
@Column({ nullable: true })
username?: string;
@Column({ nullable: true })
avatar?: string;
toJSON(): Message { toJSON(): Message {
return { return {
...this, ...this,
@ -234,7 +240,12 @@ export class Message extends BaseClass {
reactions: this.reactions ?? undefined, reactions: this.reactions ?? undefined,
sticker_items: this.sticker_items ?? undefined, sticker_items: this.sticker_items ?? undefined,
message_reference: this.message_reference ?? undefined, message_reference: this.message_reference ?? undefined,
author: this.author?.toPublicUser() ?? undefined, author: {
...this.author?.toPublicUser() ?? undefined,
// Webhooks
username: this.username ?? this.author?.username,
avatar: this.avatar ?? this.author?.avatar,
},
activity: this.activity ?? undefined, activity: this.activity ?? undefined,
application: this.application ?? undefined, application: this.application ?? undefined,
components: this.components ?? undefined, components: this.components ?? undefined,

View File

@ -1,17 +1,17 @@
/* /*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend. Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
@ -35,7 +35,7 @@ export class Webhook extends BaseClass {
type: WebhookType; type: WebhookType;
@Column({ nullable: true }) @Column({ nullable: true })
name?: string; name: string;
@Column({ nullable: true }) @Column({ nullable: true })
avatar?: string; avatar?: string;

View File

@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class WebhookMessageProperties1721298824927 implements MigrationInterface {
name = "WebhookMessageProperties1721298824927";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query("ALTER TABLE `messages` ADD `username` text NULL");
await queryRunner.query("ALTER TABLE `messages` ADD `avatar` text NULL");
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `username`");
await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `avatar`");
}
}

View File

@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class WebhookMessageProperties1721298824927 implements MigrationInterface {
name = "WebhookMessageProperties1721298824927";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query("ALTER TABLE `messages` ADD `username` text NULL");
await queryRunner.query("ALTER TABLE `messages` ADD `avatar` text NULL");
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `username`");
await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `avatar`");
}
}

View File

@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class WebhookMessageProperties1721298824927 implements MigrationInterface {
name = "WebhookMessageProperties1721298824927";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query("ALTER TABLE messages ADD username text NULL");
await queryRunner.query("ALTER TABLE messages ADD avatar text NULL");
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query("ALTER TABLE messages DROP COLUMN username");
await queryRunner.query("ALTER TABLE messages DROP COLUMN avatar");
}
}