mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-23 10:52:30 +01:00
commit
1bbf789a7e
@ -514,6 +514,12 @@
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"sticker_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@ -5341,6 +5347,308 @@
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"ModifyGuildStickerSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"minLength": 2,
|
||||
"maxLength": 30,
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"maxLength": 100,
|
||||
"type": "string"
|
||||
},
|
||||
"tags": {
|
||||
"maxLength": 200,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"tags"
|
||||
],
|
||||
"definitions": {
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"image": {
|
||||
"$ref": "#/definitions/EmbedImage"
|
||||
},
|
||||
"thumbnail": {
|
||||
"$ref": "#/definitions/EmbedImage"
|
||||
},
|
||||
"video": {
|
||||
"$ref": "#/definitions/EmbedImage"
|
||||
},
|
||||
"provider": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"icon_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxy_icon_url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"inline": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"EmbedImage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"proxy_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"width": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChannelModifySchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"maxLength": 100,
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6
|
||||
],
|
||||
"type": "number"
|
||||
},
|
||||
"topic": {
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"type": [
|
||||
"null",
|
||||
"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": "string"
|
||||
},
|
||||
"deny": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UserPublic": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"discriminator": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_flags": {
|
||||
"type": "integer"
|
||||
},
|
||||
"avatar": {
|
||||
"type": "string"
|
||||
},
|
||||
"accent_color": {
|
||||
"type": "integer"
|
||||
},
|
||||
"banner": {
|
||||
"type": "string"
|
||||
},
|
||||
"bio": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"bio",
|
||||
"bot",
|
||||
"discriminator",
|
||||
"id",
|
||||
"public_flags",
|
||||
"username"
|
||||
]
|
||||
},
|
||||
"PublicConnectedAccount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"verifie": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"type",
|
||||
"verifie"
|
||||
]
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
},
|
||||
"TemplateCreateSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -37,6 +37,7 @@
|
||||
HTML_TIMESTAMP: Date.now(),
|
||||
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0"
|
||||
};
|
||||
GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST;
|
||||
const localStorage = window.localStorage;
|
||||
// TODO: remote auth
|
||||
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
|
||||
@ -105,7 +106,8 @@
|
||||
}
|
||||
|
||||
const settings = JSON.parse(localStorage.getItem("UserSettingsStore"));
|
||||
if (settings && settings.locale === "en") {
|
||||
if (settings && settings.locale.length <= 2) {
|
||||
// fix client locale wrong and client not loading at all
|
||||
settings.locale = "en-US";
|
||||
localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ export class FosscordServer extends Server {
|
||||
api.use("*", (error: any, req: Request, res: Response, next: NextFunction) => {
|
||||
if (error) return next(error);
|
||||
res.status(404).json({
|
||||
message: "404: Not Found",
|
||||
message: "404 endpoint not found",
|
||||
code: 0
|
||||
});
|
||||
next();
|
||||
|
@ -64,6 +64,7 @@ export interface MessageCreateSchema {
|
||||
payload_json?: string;
|
||||
file?: any;
|
||||
attachments?: any[]; //TODO we should create an interface for attachments
|
||||
sticker_ids?: string[];
|
||||
}
|
||||
|
||||
// https://discord.com/developers/docs/resources/channel#create-message
|
||||
|
@ -40,17 +40,14 @@ router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_A
|
||||
const { guild_id } = req.params;
|
||||
const body = req.body as EmojiCreateSchema;
|
||||
|
||||
const id = Snowflake.generate();
|
||||
const emoji_count = await Emoji.count({ guild_id: guild_id });
|
||||
const { maxEmojis } = Config.get().limits.guild;
|
||||
|
||||
if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis);
|
||||
|
||||
const id = Snowflake.generate();
|
||||
|
||||
if (body.require_colons == null) body.require_colons = true;
|
||||
|
||||
const user = await User.findOneOrFail({ id: req.user_id });
|
||||
|
||||
body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
|
||||
|
||||
const emoji = await new Emoji({
|
||||
|
10
api/src/routes/guilds/#guild_id/premium.ts
Normal file
10
api/src/routes/guilds/#guild_id/premium.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
const router = Router();
|
||||
|
||||
router.get("/subscriptions", route({}), async (req: Request, res: Response) => {
|
||||
// TODO:
|
||||
res.json([]);
|
||||
});
|
||||
|
||||
export default router;
|
135
api/src/routes/guilds/#guild_id/stickers.ts
Normal file
135
api/src/routes/guilds/#guild_id/stickers.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import {
|
||||
emitEvent,
|
||||
GuildStickersUpdateEvent,
|
||||
handleFile,
|
||||
Member,
|
||||
Snowflake,
|
||||
Sticker,
|
||||
StickerFormatType,
|
||||
StickerType,
|
||||
uploadFile
|
||||
} from "@fosscord/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
import multer from "multer";
|
||||
import { HTTPError } from "lambert-server";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
res.json(await Sticker.find({ guild_id }));
|
||||
});
|
||||
|
||||
const bodyParser = multer({
|
||||
limits: {
|
||||
fileSize: 1024 * 1024 * 100,
|
||||
fields: 10,
|
||||
files: 1
|
||||
},
|
||||
storage: multer.memoryStorage()
|
||||
}).single("file");
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
bodyParser,
|
||||
route({ permission: "MANAGE_EMOJIS_AND_STICKERS", body: "ModifyGuildStickerSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
if (!req.file) throw new HTTPError("missing file");
|
||||
|
||||
const { guild_id } = req.params;
|
||||
const body = req.body as ModifyGuildStickerSchema;
|
||||
const id = Snowflake.generate();
|
||||
|
||||
const [sticker] = await Promise.all([
|
||||
new Sticker({
|
||||
...body,
|
||||
guild_id,
|
||||
id,
|
||||
type: StickerType.GUILD,
|
||||
format_type: getStickerFormat(req.file.mimetype),
|
||||
available: true
|
||||
}).save(),
|
||||
uploadFile(`/stickers/${id}`, req.file)
|
||||
]);
|
||||
|
||||
await sendStickerUpdateEvent(guild_id);
|
||||
|
||||
res.json(sticker);
|
||||
}
|
||||
);
|
||||
|
||||
export function getStickerFormat(mime_type: string) {
|
||||
switch (mime_type) {
|
||||
case "image/apng":
|
||||
return StickerFormatType.APNG;
|
||||
case "application/json":
|
||||
return StickerFormatType.LOTTIE;
|
||||
case "image/png":
|
||||
return StickerFormatType.PNG;
|
||||
case "image/gif":
|
||||
return StickerFormatType.GIF;
|
||||
default:
|
||||
throw new HTTPError("invalid sticker format: must be png, apng or lottie");
|
||||
}
|
||||
}
|
||||
|
||||
router.get("/:sticker_id", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id, sticker_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
res.json(await Sticker.findOneOrFail({ guild_id, id: sticker_id }));
|
||||
});
|
||||
|
||||
export interface ModifyGuildStickerSchema {
|
||||
/**
|
||||
* @minLength 2
|
||||
* @maxLength 30
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* @maxLength 100
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* @maxLength 200
|
||||
*/
|
||||
tags: string;
|
||||
}
|
||||
|
||||
router.patch(
|
||||
"/:sticker_id",
|
||||
route({ body: "ModifyGuildStickerSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, sticker_id } = req.params;
|
||||
const body = req.body as ModifyGuildStickerSchema;
|
||||
|
||||
const sticker = await new Sticker({ ...body, guild_id, id: sticker_id }).save();
|
||||
await sendStickerUpdateEvent(guild_id);
|
||||
|
||||
return res.json(sticker);
|
||||
}
|
||||
);
|
||||
|
||||
async function sendStickerUpdateEvent(guild_id: string) {
|
||||
return emitEvent({
|
||||
event: "GUILD_STICKERS_UPDATE",
|
||||
guild_id: guild_id,
|
||||
data: {
|
||||
guild_id: guild_id,
|
||||
stickers: await Sticker.find({ guild_id: guild_id })
|
||||
}
|
||||
} as GuildStickersUpdateEvent);
|
||||
}
|
||||
|
||||
router.delete("/:sticker_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => {
|
||||
const { guild_id, sticker_id } = req.params;
|
||||
|
||||
await Sticker.delete({ guild_id, id: sticker_id });
|
||||
await sendStickerUpdateEvent(guild_id);
|
||||
|
||||
return res.sendStatus(204);
|
||||
});
|
||||
|
||||
export default router;
|
@ -1,19 +0,0 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
res.json({
|
||||
id: "",
|
||||
stickers: [],
|
||||
name: "",
|
||||
sku_id: "",
|
||||
cover_sticker_id: "",
|
||||
description: "",
|
||||
banner_asset_id: ""
|
||||
}).status(200);
|
||||
});
|
||||
|
||||
export default router;
|
@ -1,11 +1,13 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
import { StickerPack } from "@fosscord/util";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
res.json({ sticker_packs: [] }).status(200);
|
||||
const sticker_packs = await StickerPack.find({ relations: ["stickers"] });
|
||||
|
||||
res.json({ sticker_packs });
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
12
api/src/routes/stickers/#sticker_id/index.ts
Normal file
12
api/src/routes/stickers/#sticker_id/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Sticker } from "@fosscord/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { sticker_id } = req.params;
|
||||
|
||||
res.json(await Sticker.find({ id: sticker_id }));
|
||||
});
|
||||
|
||||
export default router;
|
@ -1,10 +1,11 @@
|
||||
//TODO: this is a template for a generic route
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
res.send({});
|
||||
router.get("/",route({}), async (req: Request, res: Response) => {
|
||||
res.json({});
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
@ -24,7 +24,7 @@ import fetch from "node-fetch";
|
||||
import cheerio from "cheerio";
|
||||
import { MessageCreateSchema } from "../routes/channels/#channel_id/messages";
|
||||
|
||||
// TODO: check webhook, application, system author
|
||||
// TODO: check webhook, application, system author, stickers
|
||||
// TODO: embed gifs/videos/images
|
||||
|
||||
const LINK_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
|
||||
@ -46,6 +46,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
|
||||
const message = new Message({
|
||||
...opts,
|
||||
sticker_items: opts.sticker_ids?.map((x) => ({ id: x })),
|
||||
guild_id: channel.guild_id,
|
||||
channel_id: opts.channel_id,
|
||||
attachments: opts.attachments || [],
|
||||
@ -82,7 +83,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
}
|
||||
|
||||
// TODO: stickers/activity
|
||||
if (!opts.content && !opts.embeds?.length && !opts.attachments?.length) {
|
||||
if (!opts.content && !opts.embeds?.length && !opts.attachments?.length && !opts.sticker_ids?.length) {
|
||||
throw new HTTPError("Empty messages are not allowed", 50006);
|
||||
}
|
||||
|
||||
|
@ -67,8 +67,7 @@
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@fosscord/api": ["src/index"],
|
||||
"@fosscord/api/*": ["src/*"]
|
||||
"@fosscord/api": ["src/index"]
|
||||
},
|
||||
"plugins": [{ "transform": "@zerollup/ts-transform-paths" }],
|
||||
"experimentalDecorators": true
|
||||
|
@ -3,7 +3,7 @@ const WebSocket = require("ws");
|
||||
const Constants = require("./dist/util/Constants");
|
||||
|
||||
// const ws = new WebSocket("ws://127.0.0.1:8080");
|
||||
const ws = new WebSocket("wss://gateway.discord.gg");
|
||||
const ws = new WebSocket("wss://dev.fosscord.com");
|
||||
|
||||
ws.on("open", () => {
|
||||
// ws.send(JSON.stringify({ req_type: "new_auth" }));
|
||||
|
@ -179,6 +179,8 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
x.guild_hashes = {}; // @ts-ignore
|
||||
x.guild_scheduled_events = []; // @ts-ignore
|
||||
x.threads = [];
|
||||
x.premium_subscription_count = 30;
|
||||
x.premium_tier = 3;
|
||||
return x;
|
||||
}),
|
||||
guild_experiments: [], // TODO
|
||||
|
@ -127,7 +127,7 @@ export class Message extends BaseClass {
|
||||
mention_channels: Channel[];
|
||||
|
||||
@JoinTable({ name: "message_stickers" })
|
||||
@ManyToMany(() => Sticker)
|
||||
@ManyToMany(() => Sticker, { cascade: true, onDelete: "CASCADE" })
|
||||
sticker_items?: Sticker[];
|
||||
|
||||
@OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, {
|
||||
|
18
util/src/entities/Migration.ts
Normal file
18
util/src/entities/Migration.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Column, Entity, ObjectIdColumn, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { BaseClassWithoutId } from ".";
|
||||
|
||||
export const PrimaryIdAutoGenerated = process.env.DATABASE?.startsWith("mongodb")
|
||||
? ObjectIdColumn
|
||||
: PrimaryGeneratedColumn;
|
||||
|
||||
@Entity("migrations")
|
||||
export class Migration extends BaseClassWithoutId {
|
||||
@PrimaryIdAutoGenerated()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
timestamp: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
@ -32,13 +32,8 @@ export class ReadState extends BaseClass {
|
||||
user: User;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((read_state: ReadState) => read_state.last_message)
|
||||
last_message_id: string;
|
||||
|
||||
@JoinColumn({ name: "last_message_id" })
|
||||
@ManyToOne(() => Message, { nullable: true })
|
||||
last_message?: Message;
|
||||
|
||||
@Column({ nullable: true })
|
||||
last_pin_timestamp?: Date;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import { User } from "./User";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Guild } from "./Guild";
|
||||
|
||||
@ -8,6 +9,7 @@ export enum StickerType {
|
||||
}
|
||||
|
||||
export enum StickerFormatType {
|
||||
GIF = 0, // gif is a custom format type and not in discord spec
|
||||
PNG = 1,
|
||||
APNG = 2,
|
||||
LOTTIE = 3,
|
||||
@ -21,11 +23,22 @@ export class Sticker extends BaseClass {
|
||||
@Column({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@Column()
|
||||
tags: string;
|
||||
@Column({ nullable: true })
|
||||
available?: boolean;
|
||||
|
||||
@Column()
|
||||
pack_id: string;
|
||||
@Column({ nullable: true })
|
||||
tags?: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((sticker: Sticker) => sticker.pack)
|
||||
pack_id?: string;
|
||||
|
||||
@JoinColumn({ name: "pack_id" })
|
||||
@ManyToOne(() => require("./StickerPack").StickerPack, {
|
||||
onDelete: "CASCADE",
|
||||
nullable: true,
|
||||
})
|
||||
pack: import("./StickerPack").StickerPack;
|
||||
|
||||
@Column({ nullable: true })
|
||||
guild_id?: string;
|
||||
@ -36,6 +49,15 @@ export class Sticker extends BaseClass {
|
||||
})
|
||||
guild?: Guild;
|
||||
|
||||
@Column({ nullable: true })
|
||||
user_id?: string;
|
||||
|
||||
@JoinColumn({ name: "user_id" })
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE",
|
||||
})
|
||||
user?: User;
|
||||
|
||||
@Column({ type: "int" })
|
||||
type: StickerType;
|
||||
|
||||
|
31
util/src/entities/StickerPack.ts
Normal file
31
util/src/entities/StickerPack.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm";
|
||||
import { Sticker } from ".";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
|
||||
@Entity("sticker_packs")
|
||||
export class StickerPack extends BaseClass {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
banner_asset_id?: string;
|
||||
|
||||
@OneToMany(() => Sticker, (sticker: Sticker) => sticker.pack, {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete",
|
||||
})
|
||||
stickers: Sticker[];
|
||||
|
||||
// sku_id: string
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((pack: StickerPack) => pack.cover_sticker)
|
||||
cover_sticker_id?: string;
|
||||
|
||||
@ManyToOne(() => Sticker, { nullable: true })
|
||||
@JoinColumn()
|
||||
cover_sticker?: Sticker;
|
||||
}
|
@ -11,6 +11,7 @@ export * from "./Guild";
|
||||
export * from "./Invite";
|
||||
export * from "./Member";
|
||||
export * from "./Message";
|
||||
export * from "./Migration";
|
||||
export * from "./RateLimit";
|
||||
export * from "./ReadState";
|
||||
export * from "./Recipient";
|
||||
@ -18,6 +19,7 @@ export * from "./Relationship";
|
||||
export * from "./Role";
|
||||
export * from "./Session";
|
||||
export * from "./Sticker";
|
||||
export * from "./StickerPack";
|
||||
export * from "./Team";
|
||||
export * from "./TeamMember";
|
||||
export * from "./Template";
|
||||
|
@ -12,6 +12,7 @@ import { Interaction } from "./Interaction";
|
||||
import { ConnectedAccount } from "../entities/ConnectedAccount";
|
||||
import { Relationship, RelationshipType } from "../entities/Relationship";
|
||||
import { Presence } from "./Presence";
|
||||
import { Sticker } from "..";
|
||||
|
||||
export interface Event {
|
||||
guild_id?: string;
|
||||
@ -193,6 +194,14 @@ export interface GuildEmojisUpdateEvent extends Event {
|
||||
};
|
||||
}
|
||||
|
||||
export interface GuildStickersUpdateEvent extends Event {
|
||||
event: "GUILD_STICKERS_UPDATE";
|
||||
data: {
|
||||
guild_id: string;
|
||||
stickers: Sticker[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface GuildIntegrationUpdateEvent extends Event {
|
||||
event: "GUILD_INTEGRATIONS_UPDATE";
|
||||
data: {
|
||||
@ -553,6 +562,7 @@ export type EVENT =
|
||||
| "GUILD_BAN_ADD"
|
||||
| "GUILD_BAN_REMOVE"
|
||||
| "GUILD_EMOJIS_UPDATE"
|
||||
| "GUILD_STICKERS_UPDATE"
|
||||
| "GUILD_INTEGRATIONS_UPDATE"
|
||||
| "GUILD_MEMBER_ADD"
|
||||
| "GUILD_MEMBER_REMOVE"
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class VanityInvite1633881705509 implements MigrationInterface {
|
||||
name = "VanityInvite1633881705509";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
try {
|
||||
await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN vanity_url_code`);
|
||||
|
66
util/src/migrations/1634308884591-Stickers.ts
Normal file
66
util/src/migrations/1634308884591-Stickers.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { MigrationInterface, QueryRunner, Table, TableColumn, TableForeignKey } from "typeorm";
|
||||
|
||||
export class Stickers1634308884591 implements MigrationInterface {
|
||||
name = "Stickers1634308884591";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropForeignKey("read_states", "FK_6f255d873cfbfd7a93849b7ff74");
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"tags",
|
||||
new TableColumn({ name: "tags", type: "varchar", isNullable: true })
|
||||
);
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"pack_id",
|
||||
new TableColumn({ name: "pack_id", type: "varchar", isNullable: true })
|
||||
);
|
||||
await queryRunner.changeColumn("stickers", "type", new TableColumn({ name: "type", type: "integer" }));
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"format_type",
|
||||
new TableColumn({ name: "format_type", type: "integer" })
|
||||
);
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"available",
|
||||
new TableColumn({ name: "available", type: "boolean", isNullable: true })
|
||||
);
|
||||
await queryRunner.changeColumn(
|
||||
"stickers",
|
||||
"user_id",
|
||||
new TableColumn({ name: "user_id", type: "boolean", isNullable: true })
|
||||
);
|
||||
await queryRunner.createForeignKey(
|
||||
"stickers",
|
||||
new TableForeignKey({
|
||||
name: "FK_8f4ee73f2bb2325ff980502e158",
|
||||
columnNames: ["user_id"],
|
||||
referencedColumnNames: ["id"],
|
||||
referencedTableName: "users",
|
||||
onDelete: "CASCADE",
|
||||
})
|
||||
);
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: "sticker_packs",
|
||||
columns: [
|
||||
new TableColumn({ name: "id", type: "varchar", isPrimary: true }),
|
||||
new TableColumn({ name: "name", type: "varchar" }),
|
||||
new TableColumn({ name: "description", type: "varchar", isNullable: true }),
|
||||
new TableColumn({ name: "banner_asset_id", type: "varchar", isNullable: true }),
|
||||
new TableColumn({ name: "cover_sticker_id", type: "varchar", isNullable: true }),
|
||||
],
|
||||
foreignKeys: [
|
||||
new TableForeignKey({
|
||||
columnNames: ["cover_sticker_id"],
|
||||
referencedColumnNames: ["id"],
|
||||
referencedTableName: "stickers",
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
@ -2,6 +2,7 @@ import path from "path";
|
||||
import "reflect-metadata";
|
||||
import { Connection, createConnection } from "typeorm";
|
||||
import * as Models from "../entities";
|
||||
import { Migration } from "../entities/Migration";
|
||||
import { yellow, green } from "nanocolors";
|
||||
|
||||
// UUID extension option is only supported with postgres
|
||||
@ -33,10 +34,27 @@ export function initDatabase(): Promise<Connection> {
|
||||
bigNumberStrings: false,
|
||||
supportBigNumbers: true,
|
||||
name: "default",
|
||||
migrations: [path.join(__dirname, "..", "migrations", "*.js")],
|
||||
});
|
||||
|
||||
promise.then((connection) => {
|
||||
promise.then(async (connection: Connection) => {
|
||||
dbConnection = connection;
|
||||
|
||||
// run migrations, and if it is a new fresh database, set it to the last migration
|
||||
if (connection.migrations.length) {
|
||||
if (!(await Migration.findOne({}))) {
|
||||
let i = 0;
|
||||
|
||||
await Migration.insert(
|
||||
connection.migrations.map((x) => ({
|
||||
id: i++,
|
||||
name: x.name,
|
||||
timestamp: Date.now(),
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
await connection.runMigrations();
|
||||
console.log(`[Database] ${green("connected")}`);
|
||||
});
|
||||
|
||||
|
@ -4,7 +4,9 @@ import fetch from "node-fetch";
|
||||
import { Config } from "./Config";
|
||||
import multer from "multer";
|
||||
|
||||
export async function uploadFile(path: string, file: Express.Multer.File) {
|
||||
export async function uploadFile(path: string, file?: Express.Multer.File) {
|
||||
if (!file?.buffer) throw new HTTPError("Missing file in body");
|
||||
|
||||
const form = new FormData();
|
||||
form.append("file", file.buffer, {
|
||||
contentType: file.mimetype,
|
||||
|
Loading…
Reference in New Issue
Block a user