Skip to content

Commit

Permalink
Merge pull request #24 from N3aar/feat/scheduled-events
Browse files Browse the repository at this point in the history
Feat/scheduled events
  • Loading branch information
N3aar authored Jun 10, 2024
2 parents 717a52c + c860b48 commit 6fc1198
Show file tree
Hide file tree
Showing 16 changed files with 189 additions and 24 deletions.
5 changes: 5 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ RUN pnpm build

FROM node:20-alpine as runner

ENV TZ America/Sao_Paulo

RUN apk add --no-cache tzdata
RUN ln -s /usr/share/zoneinfo/${TZ} /etc/localtime

WORKDIR /app

COPY --from=builder /app/package.json ./
Expand Down
5 changes: 3 additions & 2 deletions docker/Dockerfile.backup
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
FROM alpine:latest

RUN apk add --no-cache sqlite curl tzdata tar
ENV TZ America/Sao_Paulo

ENV TZ=America/Sao_Paulo
RUN apk add --no-cache sqlite curl tzdata tar
RUN ln -s /usr/share/zoneinfo/${TZ} /etc/localtime

COPY scripts/backup.sh /usr/local/bin/backup.sh
COPY scripts/crontab /etc/crontabs/root
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@
"typescript": "^5.4.5"
},
"dependencies": {
"prisma": "^5.14.0",
"@prisma/client": "5.14.0",
"@sapphire/discord.js-utilities": "^7.2.1",
"@sapphire/framework": "^5.2.1",
"@sapphire/pieces": "^4.2.2",
"cron": "^3.1.7",
"dayjs": "^1.11.11",
"discord.js": "14.x"
"discord.js": "14.x",
"prisma": "^5.14.0"
}
}
22 changes: 22 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Warnings:
- You are about to drop the column `date` on the `Events` table. All the data in the column will be lost.
- Added the required column `day` to the `Events` table without a default value. This is not possible if the table is not empty.
- Added the required column `month` to the `Events` table without a default value. This is not possible if the table is not empty.
*/
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Events" (
"id" TEXT NOT NULL PRIMARY KEY,
"description" TEXT NOT NULL,
"month" INTEGER NOT NULL,
"day" INTEGER NOT NULL,
"repeat" BOOLEAN NOT NULL DEFAULT false,
"guild_id" TEXT,
"created_by" TEXT,
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" DATETIME NOT NULL,
CONSTRAINT "Events_guild_id_fkey" FOREIGN KEY ("guild_id") REFERENCES "Guild" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "Events_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "User" ("discord_id") ON DELETE SET NULL ON UPDATE CASCADE
);
INSERT INTO "new_Events" ("created_at", "created_by", "description", "guild_id", "id", "repeat", "updated_at") SELECT "created_at", "created_by", "description", "guild_id", "id", "repeat", "updated_at" FROM "Events";
DROP TABLE "Events";
ALTER TABLE "new_Events" RENAME TO "Events";
PRAGMA foreign_key_check("Events");
PRAGMA foreign_keys=ON;
27 changes: 27 additions & 0 deletions prisma/migrations/20240610010812_event_type/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Warnings:
- Added the required column `type` to the `Events` table without a default value. This is not possible if the table is not empty.
*/
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Events" (
"id" TEXT NOT NULL PRIMARY KEY,
"description" TEXT NOT NULL,
"month" INTEGER NOT NULL,
"day" INTEGER NOT NULL,
"type" TEXT NOT NULL,
"repeat" BOOLEAN NOT NULL DEFAULT false,
"guild_id" TEXT,
"created_by" TEXT,
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" DATETIME NOT NULL,
CONSTRAINT "Events_guild_id_fkey" FOREIGN KEY ("guild_id") REFERENCES "Guild" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "Events_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "User" ("discord_id") ON DELETE SET NULL ON UPDATE CASCADE
);
INSERT INTO "new_Events" ("created_at", "created_by", "day", "description", "guild_id", "id", "month", "repeat", "updated_at") SELECT "created_at", "created_by", "day", "description", "guild_id", "id", "month", "repeat", "updated_at" FROM "Events";
DROP TABLE "Events";
ALTER TABLE "new_Events" RENAME TO "Events";
PRAGMA foreign_key_check("Events");
PRAGMA foreign_keys=ON;
16 changes: 9 additions & 7 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@ model User {
xp Int @default(0) @map("xp")
level Int @default(1) @map("level")
birthday DateTime? @map("birthday")
Events Events[] @relation("UserEvents")
events Events[] @relation("user_events")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
}

model Events {
id String @id @default(cuid()) @map("id")
description String @map("description")
date DateTime @map("date")
month Int @map("month")
day Int @map("day")
type String @map("type")
repeat Boolean @default(false) @map("repeat")
guildId String? @map("guild_id")
Guild Guild? @relation("GuildEvents", fields: [guildId], references: [id])
guild Guild? @relation("guild_events", fields: [guildId], references: [id])
createdBy String? @map("created_by")
User User? @relation("UserEvents", fields: [createdBy], references: [discordId])
user User? @relation("user_events", fields: [createdBy], references: [discordId])
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
}
Expand All @@ -39,15 +41,15 @@ model Daily {
day Int @default(0) @map("day")
afternoon Int @default(0) @map("afternoon")
night Int @default(0) @map("night")
Guild Guild[]
guild Guild[]
}

model Guild {
id String @id @default(cuid()) @map("id")
discordId String @unique @map("discord_id")
mainChannel String? @map("main_channel")
Events Events[] @relation("GuildEvents")
Daily Daily? @relation(fields: [dailyId], references: [id])
events Events[] @relation("guild_events")
daily Daily? @relation(fields: [dailyId], references: [id])
dailyId String? @map("daily_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
Expand Down
3 changes: 0 additions & 3 deletions src/commands/user/avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ export class AvatarCommand extends Command {
};

const userType = interaction.options.getString("type");
const avatarType =
userType === "user" || !member.avatar ? "Usuário" : "Servidor";
const avatar =
userType === "user"
? member.user.displayAvatarURL(config)
Expand All @@ -68,7 +66,6 @@ export class AvatarCommand extends Command {
const embed = new EmbedBuilder()
.setTitle(nickname)
.setImage(avatar)
.setFooter({ text: avatarType })
.setColor(color);

await interaction.reply({
Expand Down
58 changes: 58 additions & 0 deletions src/events/announceScheduledEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { container } from "@sapphire/pieces";
import type { TextChannel } from "discord.js";

type ChannelMessage = {
channel: TextChannel;
message: string[];
};

export default async function announceScheduledEvent() {
const today = new Date();
const events = await container.db.events.findMany({
where: {
month: today.getMonth() + 1,
day: today.getDate(),
type: "DEFAULT",
},
include: {
guild: true,
},
});

if (!events || events.length <= 0) return;

const guilds = container.client.guilds.cache;
const channelMessages = new Map<string, ChannelMessage>();

for (const event of events) {
if (!event.guildId || !event.guild?.mainChannel) continue;

const guild = guilds.get(event.guild.discordId);
const channel = guild?.channels.cache.get(event.guild.mainChannel) as
| TextChannel
| undefined;

if (!channel || !guild) continue;

if (channelMessages.has(channel.id)) {
const messages = channelMessages.get(channel.id)?.message;
messages?.push(event.description);
} else {
channelMessages.set(channel.id, {
channel,
message: [event.description],
});
}

if (!event.repeat) {
await container.db.events.delete({ where: { id: event.id } });
}
}

for (const channelMessage of channelMessages.values()) {
const channel = channelMessage.channel;
channel.send(
`# Eventos\n${channelMessage.message.map((m) => `- ${m}`).join("\n")}`,
);
}
}
7 changes: 4 additions & 3 deletions src/events/giveXp.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { container } from "@sapphire/pieces";
import type { Message } from "discord.js";
import type { Message, TextChannel } from "discord.js";

export default function giveXp(message: Message) {
const member = message.member;
const guild = message.guild;
const channel = message.channel as TextChannel;

if (!member || !guild) return;
if (!member || !guild || !channel) return;

container.expHandler.addExp(member, guild);
container.expHandler.addExp(member, guild, channel);
}
15 changes: 15 additions & 0 deletions src/events/startTimers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defaultTimeZone } from "@/utils/contants.js";
import { CronJob } from "cron";
import announceScheduledEvent from "./announceScheduledEvent.js";

export default function startTimers() {
new CronJob(
"* * * * *",
() => {
announceScheduledEvent();
},
null,
true,
defaultTimeZone,
);
}
2 changes: 1 addition & 1 deletion src/events/supressUrlEmbeds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Message } from "discord.js";
export default async function suppressUrlEmbeds(message: Message) {
const content = message.content;

if (!message.embeds.length) return;
if (message.embeds.length <= 0) return;

for (const url in urlFixers) {
if (content.includes(url)) {
Expand Down
5 changes: 4 additions & 1 deletion src/listeners/ready.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import startTimers from "@/events/startTimers.js";
import { Listener } from "@sapphire/framework";
import type { Client } from "discord.js";

Expand All @@ -13,5 +14,7 @@ export class ReadyListener extends Listener {
});
}

public run(client: Client) {}
public run(client: Client) {
startTimers();
}
}
7 changes: 5 additions & 2 deletions src/structures/xpHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ExpValues } from "@/utils/contants.js";
import { container } from "@sapphire/pieces";
import type { Guild, GuildMember } from "discord.js";
import type { Guild, GuildMember, TextChannel } from "discord.js";

export type ExpStats = {
exp: number;
Expand Down Expand Up @@ -53,7 +53,7 @@ export default class ExpHandler {
};
}

public async addExp(member: GuildMember, guild: Guild) {
public async addExp(member: GuildMember, guild: Guild, channel: TextChannel) {
const expStats = await this.getStats(member, guild);

if (!expStats?.canGetExp) return;
Expand All @@ -67,6 +67,9 @@ export default class ExpHandler {
if (expStats.exp >= xpRequired) {
expStats.level++;
expStats.exp = Math.max(newXp - xpRequired, 0);
channel.send(
`${member.toString()} subiu para o nível ${expStats.level}!`,
);
}

await container.db.user.upsert({
Expand Down
2 changes: 2 additions & 0 deletions src/utils/contants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export const EmbedColors = {
anilist: 0x3db4f2,
};

export const defaultTimeZone = "America/Sao_Paulo";

export const leaderboardIcon = "https://i.imgur.com/qpb2q9S.png";

export const leaderboardEmojis = ["🥇", "🥈", "🥉"];
Expand Down
6 changes: 3 additions & 3 deletions src/utils/dateFormat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export function dateToCron(dateStr: string) {

export function formatDate(date: DateObject): string {
const parts = [
String(date.day).padStart(2, "0"),
String(date.month).padStart(2, "0"),
String(date.year),
date.day ? String(date.day).padStart(2, "0") : undefined,
date.month ? String(date.month).padStart(2, "0") : undefined,
date.year ? String(date.year) : undefined,
].filter((part) => part);

return parts.join("/");
Expand Down

0 comments on commit 6fc1198

Please sign in to comment.