Skip to content

Commit

Permalink
fix: cleanup + api
Browse files Browse the repository at this point in the history
  • Loading branch information
nicklvh committed Nov 26, 2023
1 parent f64ee94 commit 21791c9
Show file tree
Hide file tree
Showing 35 changed files with 275 additions and 78 deletions.
16 changes: 14 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
TOKEN=""
DATABASE_URL=""
# DISCORD BOT
TOKEN=''
CLIENT_ID=''

# API/DISCORD OAUTH
OAUTH_SECRET=''
OAUTH_REDIRECT=''
OAUTH_COOKIE=''
API_PORT='3000'
API_ORIGIN=''
API_PREFIX='/api/v1'

# DATABASE
DATABASE_URL='postgresql://user:pass@host:port/dbname'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.env
.env.development
node_modules
dist

Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@
"@sapphire/discord.js-utilities": "^7.1.2",
"@sapphire/fetch": "^2.4.1",
"@sapphire/framework": "^4.8.2",
"@sapphire/plugin-api": "^5.2.0",
"@sapphire/plugin-logger": "^3.0.7",
"@sapphire/plugin-subcommands": "^5.1.0",
"@sapphire/ratelimits": "^2.4.7",
"@sapphire/time-utilities": "^1.7.10",
"@sapphire/utilities": "^3.13.0",
"@skyra/env-utilities": "^1.2.1",
"discord-api-types": "^0.37.65",
"discord.js": "^14.14.1",
"dotenv": "^16.3.1",
"module-alias": "^2.2.3"
},
"devDependencies": {
Expand Down
3 changes: 1 addition & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ model Member {
modlogs Modlog[]
}

model Modlog {
caseId Int @id @default(autoincrement())
model Modlog {
memberId String
moderatorId String
reason String
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command } from '@sapphire/framework';
import { fetch, FetchResultTypes } from '@sapphire/fetch';
import { EmbedBuilder } from 'discord.js';
import type { APIPetResponse } from '@lib/index';
import type { APIPetResponse } from '@lib/types';
import { ApplyOptions } from '@sapphire/decorators';

@ApplyOptions<Command.Options>({
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command } from '@sapphire/framework';
import { fetch, FetchResultTypes } from '@sapphire/fetch';
import { EmbedBuilder } from 'discord.js';
import type { APIPetResponse } from '@lib/index';
import type { APIPetResponse } from '@lib/types';
import { ApplyOptions } from '@sapphire/decorators';

@ApplyOptions<Command.Options>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Command } from '@sapphire/framework';
import { fetch, FetchResultTypes } from '@sapphire/fetch';
import { EmbedBuilder } from 'discord.js';
import { ApplyOptions } from '@sapphire/decorators';
import type { APIPetInterface } from '@lib/index';
import type { APIPetInterface } from '@lib/types';

@ApplyOptions<Command.Options>({
name: 'duck',
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Command } from '@sapphire/framework';
import { PermissionFlagsBits } from 'discord.js';
import { ApplyOptions } from '@sapphire/decorators';
import { PaginatedMessage } from '@sapphire/discord.js-utilities';
import { capitaliseFirstLetter } from '@lib/utils';
import { chunk } from '@sapphire/utilities';
import { ModerationTypeStrings } from '@lib/types';

@ApplyOptions<Command.Options>({
name: 'infractions',
Expand Down Expand Up @@ -68,37 +69,55 @@ export class InfractionsCommand extends Command {
},
});

const guildInfractions = await this.container.prisma.modlog.findMany({
where: {
Guild: guildInDB,
guildId: interaction.guildId!,
},
});

if (!infractions || !infractions.length) {
return interaction.reply({
content: 'This user has no infractions',
ephemeral: true,
});
}

for (const infraction of infractions) {
for (const arr of chunk(infractions, 3)) {
message.addPageEmbed((embed) => {
return embed.setTitle(`Infraction #${infraction.caseId}`).addFields([
{
name: 'Moderator',
value: `<@${infraction.moderatorId}>`,
inline: true,
},
{
name: 'Reason',
value: infraction.reason,
inline: true,
},
{
name: 'Type',
value: capitaliseFirstLetter(infraction.type),
inline: false,
},
{
name: 'Date',
value: `<t:${infraction.createdAt.getMilliseconds()}:f>`,
inline: true,
},
]);
embed
.setAuthor({
name: `Infractions for ${user.tag}`,
iconURL: user.displayAvatarURL(),
})
.setColor('Blue')
.setTimestamp();

for (const infraction of arr) {
const moderator = this.container.client.users.cache.get(
infraction.moderatorId,
);

const id =
guildInfractions.findIndex(
(inf) => inf.createdAt === infraction.createdAt,
) + 1;

embed.addFields([
{
name: `${ModerationTypeStrings[infraction.type]} - Case #${id}`,
value: [
`**Moderator:** ${moderator!} (\`${moderator!.id}\`)`,
`**Reason:** \`${infraction.reason}\``,
`**Date:** <t:${(infraction.createdAt.valueOf() / 1000).toFixed(
0,
)}:f>`,
].join('\n'),
},
]);
}

return embed;
});
}

Expand Down
File renamed without changes.
File renamed without changes.
26 changes: 21 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { PrismaClient } from '@prisma/client';
import 'module-alias/register';
import '@sapphire/plugin-logger/register';
import '@sapphire/plugin-api/register';
import 'module-alias/register';
import { config } from 'dotenv';
config();
import { LogLevel, SapphireClient, container } from '@sapphire/framework';
import { join } from 'path';
import { GatewayIntentBits, Partials } from 'discord.js';
import { GatewayIntentBits, OAuth2Scopes, Partials } from 'discord.js';
import { ModerationManager } from '@lib/classes';
import { envParseNumber, envParseString, setup } from '@skyra/env-utilities';

setup();

const start = async () => {
const client = new SapphireClient({
Expand All @@ -17,13 +20,26 @@ const start = async () => {
GatewayIntentBits.GuildEmojisAndStickers,
],
partials: [Partials.GuildMember, Partials.Reaction, Partials.User],
baseUserDirectory: join(process.cwd(), 'dist', 'core'),
logger: {
level: LogLevel.Debug,
},
api: {
auth: {
id: envParseString('CLIENT_ID'),
cookie: envParseString('OAUTH_COOKIE'),
secret: envParseString('CLIENT_SECRET'),
scopes: [OAuth2Scopes.Identify, OAuth2Scopes.Guilds],
redirect: envParseString('OAUTH_REDIRECT'),
},
prefix: envParseString('API_PREFIX'),
origin: envParseString('API_ORIGIN'),
listenOptions: {
port: envParseNumber('API_PORT', 3000),
},
},
});

await client.login(process.env.TOKEN);
await client.login(envParseString('TOKEN'));

const prisma = new PrismaClient();
container.prisma = prisma;
Expand Down
48 changes: 48 additions & 0 deletions src/lib/api/decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { createFunctionPrecondition } from '@sapphire/decorators';
import { RateLimitManager } from '@sapphire/ratelimits';
import {
HttpCodes,
type ApiRequest,
type ApiResponse,
} from '@sapphire/plugin-api';

export const authenticated = () =>
createFunctionPrecondition(
(request: ApiRequest) => Boolean(request.auth?.token),
(_request: ApiRequest, response: ApiResponse) =>
response.error(HttpCodes.Unauthorized),
);

export function rateLimit(
time: number,
limit: number = 1,
auth: boolean = false,
) {
const manager = new RateLimitManager(time, limit);
return createFunctionPrecondition(
(request: ApiRequest, response: ApiResponse) => {
const id = (
auth
? request.auth!.id
: request.headers['x-api-key'] || request.socket.remoteAddress
) as string;
const bucket = manager.acquire(id);

response.setHeader('Date', new Date().toUTCString());
response.setHeader('X-RateLimit-Limit', time);
response.setHeader('X-RateLimit-Remaining', bucket.remaining.toString());
response.setHeader('X-RateLimit-Reset', bucket.remainingTime.toString());

if (bucket.limited) {
response.setHeader('Retry-After', bucket.remainingTime.toString());
return true;
}

try {
bucket.consume();
} catch {}

return false;
},
);
}
1 change: 1 addition & 0 deletions src/lib/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './decorators';
2 changes: 1 addition & 1 deletion src/lib/classes/ModerationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
EmbedBuilder,
ChannelType,
} from 'discord.js';
import { ModerationTypeNames } from '..';
import { ModerationTypeNames } from '../types';
import { container } from '@sapphire/framework';

export class ModerationManager {
Expand Down
2 changes: 0 additions & 2 deletions src/lib/index.ts

This file was deleted.

24 changes: 24 additions & 0 deletions src/lib/types/Augments.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { ModerationManager } from '@lib/classes';
import type { PrismaClient } from '@prisma/client';
import type { ArrayString, NumberString } from '@skyra/env-utilities';

declare module '@sapphire/pieces' {
interface Container {
prisma: PrismaClient;
moderationManager: ModerationManager;
}
}

declare module '@skyra/env-utilities' {
interface Env {
TOKEN: string;
CLIENT_ID: string;
CLIENT_SECRET: string;
OAUTH_REDIRECT: string;
OAUTH_SCOPE: ArrayString;
OAUTH_COOKIE: string;
API_PORT: NumberString;
API_ORIGIN: string;
API_PREFIX: string;
}
}
22 changes: 22 additions & 0 deletions src/lib/types/Util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ModerationType } from '@prisma/client';

export type APIPetResponse = Array<APIPetInterface>;

export interface APIPetInterface {
url: string;
message?: string;
}

export const ModerationTypeNames = {
[ModerationType.BAN]: 'Banned',
[ModerationType.KICK]: 'Kicked',
[ModerationType.MUTE]: 'Muted',
[ModerationType.WARN]: 'Warned',
};

export const ModerationTypeStrings = {
[ModerationType.BAN]: 'Ban',
[ModerationType.KICK]: 'Kick',
[ModerationType.MUTE]: 'Mute',
[ModerationType.WARN]: 'Warn',
};
24 changes: 1 addition & 23 deletions src/lib/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1 @@
import type { ModerationManager } from '@lib/classes';
import { type PrismaClient, ModerationType } from '@prisma/client';

export type APIPetResponse = Array<APIPetInterface>;

export interface APIPetInterface {
url: string;
message?: string;
}

export const ModerationTypeNames = {
[ModerationType.BAN]: 'Banned',
[ModerationType.KICK]: 'Kicked',
[ModerationType.MUTE]: 'Muted',
[ModerationType.WARN]: 'Warned',
};

declare module '@sapphire/pieces' {
interface Container {
prisma: PrismaClient;
moderationManager: ModerationManager;
}
}
export * from './Util';
12 changes: 0 additions & 12 deletions src/lib/utils/Functions.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/lib/utils/index.ts

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
23 changes: 23 additions & 0 deletions src/routes/hello-world.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { rateLimit } from '@lib/api';
import { ApplyOptions } from '@sapphire/decorators';
import {
methods,
Route,
type ApiRequest,
type ApiResponse,
} from '@sapphire/plugin-api';

@ApplyOptions<Route.Options>({
route: 'hello-world',
})
export class HelloWorldRoute extends Route {
@rateLimit(5000)
public [methods.GET](_request: ApiRequest, response: ApiResponse) {
response.json({ message: 'Hello World' });
}

@rateLimit(5000)
public [methods.POST](_request: ApiRequest, response: ApiResponse) {
response.json({ message: 'Hello World' });
}
}
Loading

0 comments on commit 21791c9

Please sign in to comment.