-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
768 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
-- CreateTable | ||
CREATE TABLE "guild" ( | ||
"id" BIGINT NOT NULL, | ||
"maximum_announcement_length" SMALLINT NOT NULL DEFAULT 0, | ||
"maximum_giveable_birthday_roles" SMALLINT NOT NULL DEFAULT 1, | ||
"maximum_birthday_list_amount" SMALLINT NOT NULL DEFAULT 10, | ||
|
||
CONSTRAINT "guild_pkey" PRIMARY KEY ("id") | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Please do not edit this file manually | ||
# It should be added in your version-control system (i.e. Git) | ||
provider = "postgresql" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,35 @@ | ||
generator client { | ||
provider = "prisma-client-js" | ||
provider = "prisma-client-js" | ||
} | ||
|
||
datasource db { | ||
provider = "postgresql" | ||
url = env("DATABASE_URL") | ||
} | ||
|
||
model User { | ||
// The user's Discord ID | ||
id BigInt @id | ||
// Whether or not they have currently subscribed to a premium plan | ||
premium Boolean @default(false) | ||
// The tier of the user | ||
tier Tiers @default(Free) | ||
// Maximum usage guild Premium | ||
// @tier 1 (Premium) | ||
// @tier 3 (Supporter) | ||
maximumPremium Int @default(0) @map("maximum_premium") @db.SmallInt | ||
createdAt DateTime @default(now()) @map("created_at") | ||
updatedAt DateTime @updatedAt @map("updated_at") | ||
transactions Transaction[] | ||
guild Guild[] | ||
@@index([id]) | ||
@@map("user") | ||
provider = "postgresql" | ||
url = env("DATABASE_URL") | ||
} | ||
|
||
model Guild { | ||
// The guild's Discord ID | ||
id BigInt @id | ||
// The guild's name on Discord | ||
name String? @db.Text | ||
// The Discord ID of the user who activated the premium | ||
userId BigInt @map("user_id") | ||
// The tier of the guild | ||
tier Tiers @default(Free) | ||
// Whether or not they have currently subscribed to a premium plan | ||
premium Boolean @default(false) | ||
createdAt DateTime @default(now()) @map("created_at") | ||
updatedAt DateTime @updatedAt @map("updated_at") | ||
user User @relation(fields: [userId], references: [id], onDelete: NoAction) | ||
@@index([id]) | ||
@@map("guild") | ||
} | ||
|
||
model Transaction { | ||
// This is the transaction ID from the payment provider | ||
id Int @id | ||
// The user's Discord ID | ||
userId BigInt @map("user_id") | ||
// The amount of money that was paid | ||
amount Int | ||
// The tier of the transaction | ||
tier Tiers @default(Free) | ||
createdAt DateTime @default(now()) @map("created_at") | ||
updatedAt DateTime @updatedAt @map("updated_at") | ||
user User @relation(fields: [userId], references: [id]) | ||
@@index([id]) | ||
@@map("transaction") | ||
} | ||
|
||
enum Tiers { | ||
Free | ||
Premium | ||
Supporter | ||
CustomBot | ||
// The guild's Discord ID | ||
id BigInt @id | ||
/// The maximum amount of characters for the announcement message | ||
/// @tier 1 (128) | ||
/// @tier 2 (256) | ||
/// @tier 3 (512) | ||
/// @default 0 | ||
maximumAnnouncementLength Int @default(0) @map("maximum_announcement_length") @db.SmallInt | ||
/// The maximum amount of roles that the bot can give to the user | ||
/// @tier 1 (3) | ||
/// @tier 2 (5) | ||
/// @tier 3 (10) | ||
/// @default 1 | ||
maximumGiveableBirthdayRoles Int @default(1) @map("maximum_giveable_birthday_roles") @db.SmallInt | ||
/// The maximum birthday amount that can be seen in advance with the /birthdayy list command | ||
/// @tier 1 (30) | ||
/// @tier 2 (50) | ||
/// @tier 3 (100) | ||
/// @default 10 | ||
maximumBirthdayListAmount Int @default(10) @map("maximum_birthday_list_amount") @db.SmallInt | ||
@@map("guild") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import '#api/routes/guilds/[...id]'; | ||
import '#api/routes/index'; | ||
import "#api/routes/webhooks/vote"; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { container } from '@sapphire/pieces'; | ||
import { isNullish, isNullishOrEmpty } from '@sapphire/utilities'; | ||
|
||
container.server.route({ | ||
url: '/guilds/:id', | ||
method: 'GET', | ||
handler: async (request, reply) => { | ||
if (isNullishOrEmpty(request.headers.authorization)) { | ||
return reply.code(401).send({ success: false, message: 'Missing authorization' }); | ||
} | ||
|
||
const mappings = getMappings(request.headers.authorization); | ||
if (!mappings) { | ||
return reply.code(403).send({ success: false, message: 'Missing access to this resource' }); | ||
} | ||
|
||
if (typeof request.params !== 'object' || isNullish(request.params) || !('id' in request.params)) { | ||
return reply.code(400).send({ success: false, message: 'Missing parameters' }); | ||
} | ||
|
||
let id: bigint; | ||
try { | ||
id = BigInt(request.params.id as string); | ||
} catch { | ||
return reply.code(400).send({ success: false, message: 'Invalid Guild ID' }); | ||
} | ||
|
||
const data = await container.prisma.guild.findFirst({ where: { id }, select: mappings.properties }); | ||
return reply.code(200).send(data ?? mappings.defaults); | ||
} | ||
}); | ||
|
||
const Mappings = { | ||
properties: { maximumAnnouncementLength: true, maximumGiveableBirthdayRoles: true, maximumBirthdayListAmount: true}, | ||
defaults: { maximumAnnouncementLength: 0, maximumGiveableBirthdayRoles: 1, maximumBirthdayListAmount: 10} | ||
} as const; | ||
|
||
function getMappings(token: string) { | ||
switch (token) { | ||
case process.env.INTERNAL_API_TOKEN: | ||
return Mappings; | ||
default: | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { container } from '@sapphire/pieces'; | ||
|
||
container.server.route({ | ||
url: '/', | ||
method: 'GET', | ||
handler: () => ({ data: 'Hello world' }) | ||
}); |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { SlashCommandIntegerOption, SlashCommandStringOption } from '@discordjs/builders'; | ||
import { codeBlock, isNullish } from '@sapphire/utilities'; | ||
import { envParseArray } from '@skyra/env-utilities'; | ||
import { Command, RegisterCommand, RegisterSubCommand } from '@skyra/http-framework'; | ||
import { blue, bold, red, yellow } from '@skyra/logger'; | ||
import { MessageFlags, PermissionFlagsBits } from 'discord-api-types/v10'; | ||
|
||
@RegisterCommand((builder) => | ||
builder | ||
.setName('config') | ||
.setDescription("Manage a guild's features") | ||
.setDMPermission(false) | ||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator) | ||
) | ||
export class UserCommand extends Command { | ||
@RegisterSubCommand((builder) => builder.setName('get').setDescription("Gets a guild's features").addStringOption(getGuildOption)) | ||
public async get(interaction: Command.ChatInputInteraction, options: Options) { | ||
if (!UserCommand.ClientOwners.includes(interaction.user.id)) { | ||
return interaction.reply({ content: 'You cannot use this command.', flags: MessageFlags.Ephemeral }); | ||
} | ||
|
||
const data = await this.container.prisma.guild.findFirst({ where: { id: BigInt(options.guild) } }); | ||
if (isNullish(data)) { | ||
return interaction.reply({ content: 'There is no data recorded for that guild.', flags: MessageFlags.Ephemeral }); | ||
} | ||
|
||
const lines = [ | ||
`${bold('Guild ID')}: ${bold(blue(data.id.toString().padStart(19, ' ')))}`, | ||
`${bold('Maximum Announcement Length')}: ${formatRange(data.maximumAnnouncementLength, 128, 512)}`, | ||
`${bold('Maximum Giveable Birthday Roles')}: ${formatRange(data.maximumGiveableBirthdayRoles, 1, 5)}`, | ||
`${bold('Maximum Birthday List Amount')}: ${formatRange(data.maximumBirthdayListAmount, 10, 50)}` | ||
]; | ||
return interaction.reply({ content: codeBlock('ansi', lines.join('\n')), flags: MessageFlags.Ephemeral }); | ||
} | ||
|
||
@RegisterSubCommand((builder) => | ||
builder | ||
.setName('set') | ||
.setDescription("Updates a guild's features") | ||
.addStringOption(getGuildOption) | ||
.addIntegerOption(getIntegerOption(128, 512, 'maximum-announcement-length', 'The maximum announcement length')) | ||
.addIntegerOption(getIntegerOption(1, 5, 'maximum-giveable-birthday-roles', 'The maximum giveable birthday roles')) | ||
.addIntegerOption(getIntegerOption(10, 50, 'maximum-birthday-list-amount', 'The maximum birthday list amount')) | ||
) | ||
public async set(interaction: Command.ChatInputInteraction, options: SetOptions) { | ||
if (!UserCommand.ClientOwners.includes(interaction.user.id)) { | ||
return interaction.reply({ content: 'You cannot use this command.', flags: MessageFlags.Ephemeral }); | ||
} | ||
|
||
const id = BigInt(options.guild); | ||
const data = { | ||
maximumAnnouncementLength: options['maximum-announcement-length'], | ||
maximumGiveableBirthdayRoles: options['maximum-giveable-birthday-roles'], | ||
maximumBirthdayListAmount: options['maximum-birthday-list-amount'] | ||
}; | ||
try { | ||
await this.container.prisma.guild.upsert({ | ||
where: { id }, | ||
create: { id, ...data }, | ||
update: data | ||
}); | ||
return interaction.reply({ content: 'Updated.', flags: MessageFlags.Ephemeral }); | ||
} catch (error) { | ||
this.container.logger.error(error); | ||
|
||
return interaction.reply({ | ||
content: 'I was not able to update the configuration, please check my logs and/or try again later.', | ||
flags: MessageFlags.Ephemeral | ||
}); | ||
} | ||
} | ||
|
||
@RegisterSubCommand((builder) => builder.setName('reset').setDescription("Resets a guild's features").addStringOption(getGuildOption)) | ||
public async reset(interaction: Command.ChatInputInteraction, options: Options) { | ||
if (!UserCommand.ClientOwners.includes(interaction.user.id)) { | ||
return interaction.reply({ content: 'You cannot use this command.', flags: MessageFlags.Ephemeral }); | ||
} | ||
|
||
const data = await this.container.prisma.guild.delete({ where: { id: BigInt(options.guild) } }); | ||
const content = isNullish(data) ? 'There is no data recorded for that guild.' : "Successfully deleted the specified guild's data."; | ||
return interaction.reply({ content, flags: MessageFlags.Ephemeral }); | ||
} | ||
|
||
private static readonly ClientOwners = envParseArray('CLIENT_OWNERS'); | ||
} | ||
|
||
interface Options { | ||
guild: string; | ||
} | ||
|
||
interface SetOptions extends Options { | ||
'maximum-announcement-length': number; | ||
'maximum-giveable-birthday-roles': number; | ||
'maximum-birthday-list-amount': number; | ||
} | ||
|
||
function getGuildOption() { | ||
return new SlashCommandStringOption() | ||
.setName('guild') | ||
.setDescription('The ID of the guild to manage') | ||
.setMinLength(17) | ||
.setMaxLength(19) | ||
.setRequired(true); | ||
} | ||
|
||
function getIntegerOption(min: number, max: number, name: string, description: string) { | ||
return new SlashCommandIntegerOption().setName(name).setDescription(`${description} (${min}-${max})`).setMinValue(min).setMaxValue(max); | ||
} | ||
|
||
function formatRange(value: number, min: number, max: number) { | ||
if (value === min) return `${blue(format(value))} → ${red(format(max))}`; | ||
if (value === max) return `${yellow(format(min))} ← ${blue(format(value))}`; | ||
return `${yellow(format(min))} ← ${blue(format(value))} → ${red(format(max))}`; | ||
} | ||
|
||
function format(value: number) { | ||
return value.toString().padStart(3, ' '); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,23 @@ | ||
import { setup as envRun } from '@skyra/env-utilities'; | ||
import { setup as envSetup } from '@skyra/env-utilities'; | ||
import { initializeSentry, setInvite, setRepository } from '@skyra/shared-http-pieces'; | ||
|
||
|
||
import '@skyra/shared-http-pieces/register'; | ||
|
||
envSetup(new URL('../../../src/.env', import.meta.url)); | ||
setInvite('948377113457745990', '326417868864'); | ||
setRepository('https://github.com/BirthdayyBot/util'); | ||
initializeSentry(); | ||
|
||
|
||
import '#lib/setup/api'; | ||
import '#lib/setup/fastify'; | ||
import '#lib/setup/logger'; | ||
import '#lib/setup/prisma'; | ||
import '@skyra/shared-http-pieces/register'; | ||
|
||
export function setup() { | ||
envRun(new URL('../../../src/.env', import.meta.url)); | ||
|
||
setRepository('iriss'); | ||
setInvite('948377113457745990', '326417868864'); | ||
initializeSentry(); | ||
|
||
export async function setup() { | ||
// Load all routes | ||
await import('#api/routes/_load'); | ||
} |
Oops, something went wrong.