forked from cosmic/scythe
1
0
Fork 0

Compare commits

..

No commits in common. "cfb7b31b361651b63357e5a57f47355cf6a30984" and "7d1cf86a079b9a9cacdaf10a265a9dc1f0440bed" have entirely different histories.

24 changed files with 466 additions and 1139 deletions

View File

@ -2,10 +2,6 @@ FROM oven/bun:latest
WORKDIR /app
COPY package.json package.json
COPY . .
RUN bun i
CMD [ "./entrypoint.sh" ]

View File

@ -1,8 +0,0 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"javascript": {
"parser": {
"unsafeParameterDecoratorsEnabled": true
}
}
}

View File

@ -5,8 +5,8 @@
"private": true,
"scripts": {
"start": "bun run src/index.ts",
"migrate": "bunx drizzle-kit migrate",
"migrate:generate": "bunx drizzle-kit generate"
"migrate": "bun drizzle-kit migrate",
"migrate:generate": "bun drizzle-kit generate"
},
"devDependencies": {
"@types/bun": "^1.2.5",

View File

@ -1,284 +1,241 @@
import { Discord, Slash, SlashGroup, SlashOption } from "discordx";
import {
ApplicationCommandOptionType,
CommandInteraction,
EmbedBuilder,
GuildMember,
MessageFlags,
} from "discord.js";
import { ApplicationCommandOptionType, CommandInteraction, EmbedBuilder, GuildMember, MessageFlags } from "discord.js";
import db from "../db";
import { colonTable } from "../db/schema";
import { colonTable, type ColonThreeType } from "../db/schema";
import { eq, desc } from "drizzle-orm";
@Discord()
export class ColonThreeInit {
@Slash({
description: "Init all users for :3 Leaderboard",
name: "init_colonthree",
})
@Slash({ description: "Init all users for :3 Leaderboard", name: "init_colonthree" })
async init_colon(inter: CommandInteraction) {
if (Bun.env.OWNER != inter.user.id) {
await inter.reply({
content: "You're not allowed to run this.",
flags: MessageFlags.Ephemeral,
});
return;
}
if (Bun.env.OWNER != inter.user.id) {
await inter.reply({ content: "You're not allowed to run this.", flags: MessageFlags.Ephemeral })
return;
}
for (const userObject of await inter.guild!.members.cache) {
const user = userObject[1];
if (user.user.bot) {
continue;
}
for (const userObject of await inter.guild!.members.cache) {
const user = userObject[1];
if (user.user.bot) {
continue;
}
const check = await db
.select()
.from(colonTable)
.where(eq(colonTable.user, user.id));
const check = await db.select().from(colonTable).where(eq(colonTable.user, user.id));
if (check.length >= 1) {
continue;
}
if (check.length >= 1) {
continue;
}
await db.insert(colonTable).values({
user: user.id,
amount: 0,
messages_count: 0,
});
}
await db.insert(colonTable).values({
user: user.id,
amount: 0,
messages_count: 0
});
}
await inter.reply({
content: "All users have been initalized",
flags: MessageFlags.Ephemeral,
});
await inter.reply({ content: "All users have been initalized", flags: MessageFlags.Ephemeral });
}
}
@Discord()
@SlashGroup({
description: "The :3 Commands",
name: "colonthree",
description: "The :3 Commands",
name: "colonthree"
})
@SlashGroup("colonthree")
export class ColonThree {
@Slash({ description: "Stats" })
async stats(
@SlashOption({
description: "Get stats from user",
name: "user",
required: false,
type: ApplicationCommandOptionType.User,
})
user: GuildMember,
inter: CommandInteraction,
) {
const statsUser = (
await db
.select()
.from(colonTable)
.where(eq(colonTable.user, user?.id || inter.user.id))
)[0];
@Slash({ description: "Stats" })
async stats(
@SlashOption({
description: "Get stats from user",
name: "user",
required: false,
type: ApplicationCommandOptionType.User,
})
user: GuildMember,
inter: CommandInteraction
) {
const statsUser = (
await db
.select()
.from(colonTable)
.where(eq(colonTable.user, (user?.id || inter.user.id)))
)[0];
if (!statsUser) {
await inter.reply({
content: `Failed to get <@${user?.id || inter.user.id}>'s stats.`,
flags: MessageFlags.Ephemeral,
});
return;
}
if (!statsUser) {
await inter.reply({ content: `Failed to get <@${user?.id || inter.user.id}>'s stats.`, flags: MessageFlags.Ephemeral });
return;
}
const userObject = inter.client.users.cache.get(statsUser.user);
if (!userObject) {
await inter.reply({
content: "Something went wrong...",
flags: MessageFlags.Ephemeral,
});
return;
}
const userObject = inter.client.users.cache.get(statsUser.user);
if (!userObject) {
await inter.reply({ content: "Something went wrong...", flags: MessageFlags.Ephemeral });
return;
}
const embed = new EmbedBuilder()
.setTitle(`${userObject.username}'s Stats`)
.setAuthor({
name: userObject.username,
iconURL: userObject.avatarURL()!,
})
.addFields(
{
name: "**Total :3 Sent**",
value: `► **${statsUser.amount}** times`,
inline: true,
},
{
name: "**Average :3 per Message**",
value: `► **${(statsUser.amount / statsUser.messages_count).toFixed(2)}**`,
inline: true,
},
{
name: "**Messages Count**",
value: `► **${statsUser.messages_count}**`,
inline: true,
},
);
const embed = new EmbedBuilder()
.setTitle(`${userObject.username}'s Stats`)
.setAuthor({ name: userObject.username, iconURL: userObject.avatarURL()! })
.addFields(
{
name: '**Total :3 Sent**',
value: `► **${statsUser.amount}** times`,
inline: true
},
{
name: '**Average :3 per Message**',
value: `► **${(statsUser.amount / statsUser.messages_count).toFixed(2)}**`,
inline: true
},
{
name: '**Messages Count**',
value: `► **${statsUser.messages_count}**`,
inline: true
}
);
await inter.reply({ embeds: [embed], flags: MessageFlags.Ephemeral });
}
await inter.reply( { embeds: [embed], flags: MessageFlags.Ephemeral });
}
@Slash({ description: "Compare" })
async compare(
@SlashOption({
description: "Get stats from user",
name: "x",
required: true,
type: ApplicationCommandOptionType.User,
})
x: GuildMember,
@SlashOption({
description: "Get stats from user",
name: "y",
required: true,
type: ApplicationCommandOptionType.User,
})
y: GuildMember,
inter: CommandInteraction,
) {
const xStats = (
await db.select().from(colonTable).where(eq(colonTable.user, x.id))
)[0];
@Slash({ description: "Compare" })
async compare(
@SlashOption({
description: "Get stats from user",
name: "x",
required: true,
type: ApplicationCommandOptionType.User,
})
x: GuildMember,
@SlashOption({
description: "Get stats from user",
name: "y",
required: true,
type: ApplicationCommandOptionType.User,
})
y: GuildMember,
inter: CommandInteraction
) {
const xStats = (
await db
.select()
.from(colonTable)
.where(eq(colonTable.user, x.id))
)[0];
if (!xStats) {
await inter.reply({
content: `Failed to get <@${x.id}>'s stats.`,
flags: MessageFlags.Ephemeral,
});
return;
}
if (!xStats) {
await inter.reply({ content: `Failed to get <@${x.id}>'s stats.`, flags: MessageFlags.Ephemeral });
return;
}
const yStats = (
await db.select().from(colonTable).where(eq(colonTable.user, y.id))
)[0];
const yStats = (
await db
.select()
.from(colonTable)
.where(eq(colonTable.user, y.id))
)[0];
if (!yStats) {
await inter.reply({
content: `Failed to get <@${y.id}>'s stats.`,
flags: MessageFlags.Ephemeral,
});
return;
}
if (!yStats) {
await inter.reply({ content: `Failed to get <@${y.id}>'s stats.`, flags: MessageFlags.Ephemeral });
return;
}
const winner = xStats.amount > yStats.amount ? x : y;
const winner = xStats.amount > yStats.amount ? x : y;
const embed = new EmbedBuilder()
.setTitle(`🎉 ${winner.user.username} is using :3 more!`)
.addFields(
{
name: `📊 ${x.user.username}'s Stats`,
value: `► Sent **${xStats.amount}** :3\n► Avg: **${(xStats.amount / xStats.messages_count).toFixed(2)}** :3 per message`,
inline: true,
},
{
name: `📊 ${y.user.username}'s Stats`,
value: `► Sent **${yStats.amount}** :3\n► Avg: **${(yStats.amount / yStats.messages_count).toFixed(2)}** :3 per message`,
inline: true,
},
);
const embed = new EmbedBuilder()
.setTitle(`🎉 ${winner.user.username} is using :3 more!`)
.addFields(
{
name: `📊 ${x.user.username}'s Stats`,
value: `► Sent **${xStats.amount}** :3\n► Avg: **${(xStats.amount / xStats.messages_count).toFixed(2)}** :3 per message`,
inline: true
},
{
name: `📊 ${y.user.username}'s Stats`,
value: `► Sent **${yStats.amount}** :3\n► Avg: **${(yStats.amount / yStats.messages_count).toFixed(2)}** :3 per message`,
inline: true
}
)
await inter.reply({ embeds: [embed] });
}
await inter.reply({ embeds: [embed] });
}
@Slash({ description: "Leaderboard" })
@Slash({ description: "Leaderboard" })
async board(inter: CommandInteraction) {
const theColonThreeLeaders = await db
.select()
.from(colonTable)
.orderBy(desc(colonTable.amount));
const theColonThreeLeaders = await db.select()
.from(colonTable)
.orderBy(desc(colonTable.amount));
const topTen = theColonThreeLeaders
.slice(0, 10)
.filter((user) => user.amount >= 1);
const topTen = theColonThreeLeaders
.slice(0, 10)
.filter(user => user.amount >= 1);
const leaderboardText = topTen.map((user, index) => {
const rank = index + 1;
const avg = (user.amount / user.messages_count).toFixed(2);
const leaderboardText = topTen.map((user, index) => {
const rank = index + 1;
const avg = (user.amount / user.messages_count).toFixed(2);
const medal =
rank === 1
? "🥇"
: rank === 2
? "🥈"
: rank === 3
? "🥉"
: `**${rank}.**`;
const medal = rank === 1 ? "🥇" : rank === 2 ? "🥈" : rank === 3 ? "🥉" : `**${rank}.**`;
return `${medal} <@${user.user}> » **${user.amount}** :3 (avg: **${avg}**)`;
});
return `${medal} <@${user.user}> » **${user.amount}** :3 (avg: **${avg}**)`;
});
const embed = new EmbedBuilder()
.setTitle(`🏆 :3 Leaderboard`)
.setDescription(leaderboardText.join("\n") || "*No data yet!*")
.addFields({
name: "Stats",
value: `► **Tracking users:** ${theColonThreeLeaders.length}`,
inline: false,
})
.setFooter({ text: "Keep using :3!!!!!!" })
.setTimestamp();
const embed = new EmbedBuilder()
.setTitle(`🏆 :3 Leaderboard`)
.setDescription(leaderboardText.join("\n") || "*No data yet!*")
.addFields({
name: 'Stats',
value: `► **Tracking users:** ${theColonThreeLeaders.length}`,
inline: false
})
.setFooter({ text: 'Keep using :3!!!!!!' })
.setTimestamp();
await inter.reply({ embeds: [embed] });
await inter.reply({ embeds: [embed] });
}
@Slash({ description: "Stop tracking and delete my data" })
@Slash({ description: "Stop tracking and delete my data" })
async delete(
@SlashOption({
description: "Type confirm to delete",
name: "confirmation",
required: true,
type: ApplicationCommandOptionType.String,
})
confirmation: string,
inter: CommandInteraction,
) {
if (confirmation === "confirm") {
await db.delete(colonTable).where(eq(colonTable.user, inter.user.id));
await inter.reply({
content: "All of data was deleted, you will not be tracked again.",
flags: MessageFlags.Ephemeral,
});
return;
}
@SlashOption({
description: "Type confirm to delete",
name: "confirmation",
required: true,
type: ApplicationCommandOptionType.String,
})
confirmation: string,
inter: CommandInteraction
) {
if (confirmation === "confirm") {
await db.delete(colonTable).where(eq(colonTable.user, inter.user.id));
await inter.reply({ content: "All of data was deleted, you will not be tracked again.", flags: MessageFlags.Ephemeral });
return;
}
await inter.reply({
content: 'You need to type "confirm" to delete.',
flags: MessageFlags.Ephemeral,
});
}
await inter.reply({ content: 'You need to type "confirm" to delete.', flags: MessageFlags.Ephemeral });
}
@Slash({ description: "Start tracking" })
async start(inter: CommandInteraction) {
const check = (
await db
.select()
.from(colonTable)
.where(eq(colonTable.user, inter.user.id))
)[0];
@Slash({ description: "Start tracking" })
async start(
inter: CommandInteraction
) {
const check = (
await db
.select()
.from(colonTable)
.where(eq(colonTable.user, inter.user.id)
)
)[0];
if (check) {
await inter.reply({
content: "Scythe already tracks your :3 data.",
flags: MessageFlags.Ephemeral,
});
return;
}
if (check) {
await inter.reply({ content: "Scythe already tracks your :3 data.", flags: MessageFlags.Ephemeral });
return;
}
await db.insert(colonTable).values({
user: inter.user.id,
amount: 0,
messages_count: 0,
});
await inter.reply({
content: "Scythe starts tracking your :3 data again.",
flags: MessageFlags.Ephemeral,
});
}
await db.insert(colonTable).values({
user: inter.user.id,
amount: 0,
messages_count: 0
});
await inter.reply({ content: "Scythe starts tracking your :3 data again.", flags: MessageFlags.Ephemeral });
}
}

View File

@ -1,103 +0,0 @@
import {
ApplicationCommandOptionType,
MessageFlags,
TextChannel,
type CommandInteraction,
} from "discord.js";
import { Discord, Slash, SlashOption } from "discordx";
import db from "../db";
import { byeTable, greetsTable } from "../db/schema";
import { sql } from "drizzle-orm";
@Discord()
export class GreetCmds {
@Slash({ name: "hello-setup", description: "set up the hello message :3" })
async helloSetup(
@SlashOption({
name: "channel",
description: "channel to send in",
required: true,
type: ApplicationCommandOptionType.Channel,
})
channel: TextChannel,
@SlashOption({
name: "message",
description: "message to send",
required: true,
type: ApplicationCommandOptionType.String,
})
message: string,
inter: CommandInteraction,
) {
if (inter.user.id != inter.guild?.ownerId) {
return await inter.reply({
content: "you cannot use this command u goober!",
flags: MessageFlags.Ephemeral,
});
}
if (!inter.guildId) {
return await inter.reply("you can't use this in DMs silly!");
}
await db
.insert(greetsTable)
.values({
guild: inter.guildId,
channel: channel.id,
message,
})
.onConflictDoUpdate({
target: greetsTable.guild,
set: { message, channel: channel.id },
setWhere: sql`guild = ${inter.guildId}`,
});
await inter.reply({
content: "setup done! :3",
flags: MessageFlags.Ephemeral,
});
}
@Slash({ name: "bye-setup", description: "set up the bye message :3" })
async byeSetup(
@SlashOption({
name: "channel",
description: "channel to send in",
required: true,
type: ApplicationCommandOptionType.Channel,
})
channel: TextChannel,
@SlashOption({
name: "message",
description: "message to send",
required: true,
type: ApplicationCommandOptionType.String,
})
message: string,
inter: CommandInteraction,
) {
if (inter.user.id != inter.guild?.ownerId) {
return await inter.reply({
content: "you cannot use this command u goober!",
flags: MessageFlags.Ephemeral,
});
}
if (!inter.guildId) {
return await inter.reply("you can't use this in DMs silly!");
}
await db
.insert(byeTable)
.values({
guild: inter.guildId,
channel: channel.id,
message,
})
.onConflictDoUpdate({
target: byeTable.guild,
set: { message, channel: channel.id },
setWhere: sql`guild = ${inter.guildId}`,
});
await inter.reply({
content: "setup done! :3",
flags: MessageFlags.Ephemeral,
});
}
}

View File

@ -1,38 +0,0 @@
import {
ApplicationCommandOptionType,
CommandInteraction,
MessageFlags,
type TextChannel,
} from "discord.js";
import { Discord, Slash, SlashOption } from "discordx";
import db from "../db";
import { introTable } from "../db/schema";
import { sql } from "drizzle-orm";
@Discord()
export class Intro {
@Slash({ name: "intro-purge", description: "sets up intro purge" })
async introPurge(
@SlashOption({
name: "channel",
description: "channel to check",
required: true,
type: ApplicationCommandOptionType.Channel,
})
channel: TextChannel,
inter: CommandInteraction,
) {
await db
.insert(introTable)
.values({
guild: inter.guildId!,
channel: channel.id,
})
.onConflictDoUpdate({
target: introTable.guild,
set: { channel: channel.id },
setWhere: sql`guild = ${inter.guildId}`,
});
await inter.reply({content: "intro setup done!", flags: MessageFlags.Ephemeral});
}
}

View File

@ -1,48 +1,37 @@
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
EmbedBuilder,
MessageFlags,
type CommandInteraction,
type TextChannel,
} from "discord.js";
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, Message, MessageFlags, type CommandInteraction, type TextChannel } from "discord.js";
import { Discord, Slash } from "discordx";
@Discord()
export class TicketCmd {
@Slash({ name: "setup-ticket", description: "Setup the tickets" })
async snipe(inter: CommandInteraction) {
if (inter.user.id != Bun.env.OWNER) {
await inter.reply({
content: "You're not the owner",
flags: MessageFlags.Ephemeral,
});
return;
}
@Slash({ name: "setup-ticket", description: "Setup the tickets" })
async snipe(inter: CommandInteraction) {
if (inter.user.id != Bun.env.OWNER) {
await inter.reply({ content: "You're not the owner", flags: MessageFlags.Ephemeral });
return;
}
const channel = inter.client.channels.cache.get(
Bun.env.TICKET_CHANNEL,
) as TextChannel;
const channel = inter.client.channels.cache.get(Bun.env.TICKET_CHANNEL) as TextChannel;
const embed = new EmbedBuilder()
.setTitle(`Tickets`)
.setDescription(`Click the button to make a ticket! :3`);
const embed = new EmbedBuilder()
.setTitle(`Tickets`)
.setDescription(
`Click the button to make a ticket! :3`,
);
const createButton = new ButtonBuilder()
.setCustomId("createTicket")
.setLabel("Create Ticket")
const createButton = new ButtonBuilder()
.setCustomId('createTicket')
.setLabel('Create Ticket')
.setStyle(ButtonStyle.Primary);
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
createButton,
);
await channel.send({
embeds: [embed],
components: [row],
});
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents(createButton);
await inter.reply({ content: "Done", flags: MessageFlags.Ephemeral });
}
await channel.send({
embeds: [embed],
components: [row]
});
await inter.reply({ content: "Done", flags: MessageFlags.Ephemeral });
}
}

View File

@ -1,37 +1,19 @@
import {
ApplicationCommandOptionType,
type CommandInteraction,
type TextChannel,
} from "discord.js";
import { Discord, Slash, SlashOption } from "discordx";
import db from "../db";
import { uptimeTable } from "../db/schema";
import type { CommandInteraction, TextChannel } from "discord.js";
import { Discord, Slash } from "discordx";
@Discord()
export class UptimeCmd {
@Slash({ name: "uptime-setup", description: "set up uptime cmd" })
async uptimeCmd(
@SlashOption({
name: "channel",
description: "channel to send in",
required: true,
type: ApplicationCommandOptionType.Channel,
})
channel: TextChannel,
inter: CommandInteraction,
) {
if (inter.user.id != inter.guild?.ownerId) {
return await inter.reply("you cannot run this command :p");
async uptimeCmd(inter: CommandInteraction) {
if (inter.user.id != Bun.env.OWNER) {
await inter.reply("you cannot run this command :p");
return;
}
const msg = await channel.send(
`bot is up, last ping: <t:${Math.floor(Date.now() / 1000)}:f> | if the ping was more than 1 minute ago, cosmic needs to check the bot lol :3`,
const channel = inter.client.channels.cache.get(
Bun.env.SCYTHE_CHANNEL,
) as TextChannel;
await channel.send(
`bot is up, last ping: <t:${Math.floor(Date.now()/1000)}:f> | if the ping was more than 1 minute ago, cosmic needs to check the bot lol :3`,
);
await db.insert(uptimeTable).values({
guild: inter.guildId!,
channel: channel.id,
message: msg.id,
});
}
}

View File

@ -1,13 +1,13 @@
import {
ActionRowBuilder,
ButtonBuilder,
ButtonInteraction,
ButtonStyle,
CategoryChannel,
ChannelType,
EmbedBuilder,
MessageFlags,
PermissionsBitField,
ActionRowBuilder,
ButtonBuilder,
ButtonInteraction,
ButtonStyle,
CategoryChannel,
ChannelType,
EmbedBuilder,
MessageFlags,
PermissionsBitField
} from "discord.js";
import { ButtonComponent, Discord } from "discordx";
@ -20,17 +20,14 @@ import { eq } from "drizzle-orm";
export class TicketComponenets {
@ButtonComponent({ id: "createTicket" })
async createHandler(inter: ButtonInteraction): Promise<void> {
const check = await db
.select()
.from(ticketsTable)
.where(eq(ticketsTable.user, inter.user.id));
if (check.length >= 1) {
await inter.reply({
content: "You already have a ticket open.",
flags: MessageFlags.Ephemeral,
});
return;
}
const check = await db.select().from(ticketsTable).where(eq(ticketsTable.user, inter.user.id));
if (check.length >= 1) {
await inter.reply({
content: "You already have a ticket open.",
flags: MessageFlags.Ephemeral
});
return;
}
let channel = await inter.guild!.channels.create({
type: ChannelType.GuildText,
@ -42,58 +39,51 @@ export class TicketComponenets {
},
{
id: inter.user.id,
allow: [
PermissionsBitField.Flags.ViewChannel,
PermissionsBitField.Flags.SendMessages,
],
allow: [PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages],
},
{
id: Bun.env.MOD_ROLE,
allow: [
PermissionsBitField.Flags.ViewChannel,
PermissionsBitField.Flags.SendMessages,
],
},
],
allow: [PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages],
}
]
});
const category = inter.guild?.channels.cache.get(
Bun.env.TICKET_CATEGORY,
) as CategoryChannel;
channel = await channel.setParent(category);
const category = inter.guild?.channels.cache.get(Bun.env.TICKET_CATEGORY) as CategoryChannel;
channel = await channel.setParent(category);
const embed = new EmbedBuilder()
.setTitle(`Tickets`)
.setDescription(`<@${inter.user.id}> here's your ticket!`);
const embed = new EmbedBuilder()
.setTitle(`Tickets`)
.setDescription(
`<@${inter.user.id}> here's your ticket!`,
);
const deleteButton = new ButtonBuilder()
.setCustomId("deleteTicket")
.setLabel("Delete Ticket")
.setStyle(ButtonStyle.Primary);
const deleteButton = new ButtonBuilder()
.setCustomId('deleteTicket')
.setLabel('Delete Ticket')
.setStyle(ButtonStyle.Primary);
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
deleteButton,
);
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents(deleteButton);
await channel.send({
embeds: [embed],
components: [row],
});
embeds: [embed],
components: [row]
});
await inter.reply({
content: `<#${channel.id}>`,
flags: MessageFlags.Ephemeral,
});
await inter.reply({
content: `<#${channel.id}>`,
flags: MessageFlags.Ephemeral
})
await db.insert(ticketsTable).values({
user: inter.user.id,
channel: channel.id,
});
await db.insert(ticketsTable).values({
user: inter.user.id,
channel: channel.id
});
}
@ButtonComponent({ id: "deleteTicket" })
async deleteHandler(inter: ButtonInteraction): Promise<void> {
await inter.channel?.delete(`<@${inter.user.id}> has deleted the ticket.`);
await db.delete(ticketsTable).where(eq(ticketsTable.user, inter.user.id));
}
@ButtonComponent({ id: "deleteTicket" })
async deleteHandler(inter: ButtonInteraction): Promise<void> {
await inter.channel?.delete(`<@${inter.user.id}> has deleted the ticket.`);
await db.delete(ticketsTable).where(eq(ticketsTable.user, inter.user.id));
}
}

View File

@ -1,3 +1,3 @@
import { drizzle } from "drizzle-orm/libsql";
import { drizzle } from 'drizzle-orm/libsql';
export default drizzle(Bun.env.DB);

View File

@ -1,11 +0,0 @@
CREATE TABLE `bye` (
`guild` text PRIMARY KEY NOT NULL,
`channel` text NOT NULL,
`message` text NOT NULL
);
--> statement-breakpoint
CREATE TABLE `greets` (
`guild` text PRIMARY KEY NOT NULL,
`channel` text NOT NULL,
`message` text NOT NULL
);

View File

@ -1,10 +0,0 @@
CREATE TABLE `intro` (
`guild` text PRIMARY KEY NOT NULL,
`channel` text NOT NULL
);
--> statement-breakpoint
CREATE TABLE `uptime` (
`guild` text PRIMARY KEY NOT NULL,
`channel` text NOT NULL,
`message` text NOT NULL
);

View File

@ -1,42 +1,42 @@
{
"version": "6",
"dialect": "sqlite",
"id": "fa98c7d2-c794-4f03-887b-f6f5c1b3891d",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"tickets": {
"name": "tickets",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
"version": "6",
"dialect": "sqlite",
"id": "fa98c7d2-c794-4f03-887b-f6f5c1b3891d",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"tickets": {
"name": "tickets",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -1,73 +1,73 @@
{
"version": "6",
"dialect": "sqlite",
"id": "a3e18d09-b7d4-48e3-8920-1fa55c43ee70",
"prevId": "fa98c7d2-c794-4f03-887b-f6f5c1b3891d",
"tables": {
"colonthree": {
"name": "colonthree",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"messages_count": {
"name": "messages_count",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tickets": {
"name": "tickets",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
"version": "6",
"dialect": "sqlite",
"id": "a3e18d09-b7d4-48e3-8920-1fa55c43ee70",
"prevId": "fa98c7d2-c794-4f03-887b-f6f5c1b3891d",
"tables": {
"colonthree": {
"name": "colonthree",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"messages_count": {
"name": "messages_count",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tickets": {
"name": "tickets",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -1,135 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "2218a8ed-3583-479c-b142-997455b0c7dc",
"prevId": "a3e18d09-b7d4-48e3-8920-1fa55c43ee70",
"tables": {
"bye": {
"name": "bye",
"columns": {
"guild": {
"name": "guild",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"message": {
"name": "message",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"colonthree": {
"name": "colonthree",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"messages_count": {
"name": "messages_count",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"greets": {
"name": "greets",
"columns": {
"guild": {
"name": "guild",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"message": {
"name": "message",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tickets": {
"name": "tickets",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -1,190 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "fb3b9c6d-7d3b-4651-b230-e163bf08b586",
"prevId": "2218a8ed-3583-479c-b142-997455b0c7dc",
"tables": {
"bye": {
"name": "bye",
"columns": {
"guild": {
"name": "guild",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"message": {
"name": "message",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"colonthree": {
"name": "colonthree",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"messages_count": {
"name": "messages_count",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"greets": {
"name": "greets",
"columns": {
"guild": {
"name": "guild",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"message": {
"name": "message",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"intro": {
"name": "intro",
"columns": {
"guild": {
"name": "guild",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tickets": {
"name": "tickets",
"columns": {
"user": {
"name": "user",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"uptime": {
"name": "uptime",
"columns": {
"guild": {
"name": "guild",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"message": {
"name": "message",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -1,34 +1,20 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1742570954158,
"tag": "0000_nervous_komodo",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1742844574438,
"tag": "0001_warm_ego",
"breakpoints": true
},
{
"idx": 2,
"version": "6",
"when": 1742864217240,
"tag": "0002_colorful_colleen_wing",
"breakpoints": true
},
{
"idx": 3,
"version": "6",
"when": 1742927150677,
"tag": "0003_clumsy_mephistopheles",
"breakpoints": true
}
]
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1742570954158,
"tag": "0000_nervous_komodo",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1742844574438,
"tag": "0001_warm_ego",
"breakpoints": true
}
]
}

View File

@ -3,37 +3,14 @@ import { text, sqliteTable, int } from "drizzle-orm/sqlite-core";
import type { InferSelectModel } from "drizzle-orm";
export const ticketsTable = sqliteTable("tickets", {
user: text().primaryKey().notNull(),
channel: text().notNull(),
});
user: text().primaryKey().notNull(),
channel: text().notNull()
})
export const colonTable = sqliteTable("colonthree", {
user: text().primaryKey().notNull(),
amount: int().notNull(),
messages_count: int().notNull(),
});
export const greetsTable = sqliteTable("greets", {
guild: text().primaryKey().notNull(),
channel: text().notNull(),
message: text().notNull(),
});
export const byeTable = sqliteTable("bye", {
guild: text().primaryKey().notNull(),
channel: text().notNull(),
message: text().notNull(),
});
export const uptimeTable = sqliteTable("uptime", {
guild: text().primaryKey(),
channel: text().notNull(),
message: text().notNull(),
});
export const introTable = sqliteTable("intro", {
guild: text().primaryKey(),
channel: text().notNull(),
});
user: text().primaryKey().notNull(),
amount: int().notNull(),
messages_count: int().notNull()
})
export type ColonThreeType = InferSelectModel<typeof colonTable>;

View File

@ -2,63 +2,39 @@ import type { TextChannel } from "discord.js";
import { Client, Discord, On, type ArgsOf } from "discordx";
import db from "../db";
import { byeTable, colonTable, greetsTable } from "../db/schema";
import { colonTable } from "../db/schema";
import { eq } from "drizzle-orm";
@Discord()
export class MemberEvents {
@On({ event: "guildMemberAdd" })
async memberAdd([member]: ArgsOf<"guildMemberAdd">, client: Client) {
const greetRes = await db
.select()
.from(greetsTable)
.where(eq(greetsTable.guild, member.guild.id));
if (greetRes.length > 0) {
const channel = client.channels.cache.get(
greetRes[0].channel,
) as TextChannel;
await channel.send(
greetRes[0].message.replace("{user}", `<@${member.user.id}>`),
);
}
const channel = client.channels.cache.get(Bun.env.WELCOME!) as TextChannel;
await channel.send(
`Welcome to **${member.guild.name}** <@${member.user.id}> ! You are member #${member.guild.memberCount}! Get a color role and Operating System role(s) in the Channels & Roles section if you want and enjoy your stay <3`,
);
if (member.user.bot) {
const botRole = member.guild.roles.cache.get(Bun.env.BOT_ROLE!);
if (botRole) {
await member.roles.add(botRole);
}
} else {
if(Date.now() - member.user.createdAt.getTime() < 1000 * 60 * 60 * 24 * 7) {
try {
await member.send("to protect against raids, bots, and other disturbances, accounts under a week old are kicked upon joining. please wait for your account to mature before rejoining.")
} catch(_) {}
await member.kick("account less than week old")
return;
}
await db.insert(colonTable).values({
user: member.id,
amount: 0,
messages_count: 0,
messages_count: 0
});
}
}
@On({ event: "guildMemberRemove" })
async memberRemove([member]: ArgsOf<"guildMemberRemove">, client: Client) {
const byeRes = await db
.select()
.from(byeTable)
.where(eq(byeTable.guild, member.guild.id));
if (byeRes.length > 0) {
const channel = client.channels.cache.get(
byeRes[0].channel,
) as TextChannel;
await channel.send(
byeRes[0].message.replace("{user}", `${member.user.username}`),
);
}
const channel = client.channels.cache.get(Bun.env.GOODBYE!) as TextChannel;
await channel.send(`We're sad to see you go, ${member.user.username}.`);
await db.delete(colonTable).where(eq(colonTable.user, member.id));
await db
.delete(colonTable)
.where(eq(colonTable.user, member.id));
}
@On({ event: "guildMemberUpdate" })

View File

@ -46,20 +46,14 @@ export class MessageEvents {
if (cThreeRegex.test(msg.content)) {
let colonThrees = msg.content.match(cThreeRegex);
await db
.update(colonTable)
.set({
amount: sql`${colonTable.amount} + ${colonThrees?.length}`,
messages_count: sql`${colonTable.messages_count} + 1`,
})
.where(eq(colonTable.user, msg.author.id));
await db.update(colonTable).set({
amount: sql`${colonTable.amount} + ${colonThrees?.length}`,
messages_count: sql`${colonTable.messages_count} + 1`,
}).where(eq(colonTable.user, msg.author.id));
} else {
await db
.update(colonTable)
.set({
messages_count: sql`${colonTable.messages_count} + 1`,
})
.where(eq(colonTable.user, msg.author.id));
await db.update(colonTable).set({
messages_count: sql`${colonTable.messages_count} + 1`,
}).where(eq(colonTable.user, msg.author.id));
}
}
}

View File

@ -1,8 +1,8 @@
import { Client, Discord, On, type ArgsOf } from "discordx";
import { underageCheck } from "../utils/underage";
import { bumpRemind } from "../utils/bump";
import {stat, mkdir} from "fs/promises";
import { uptimeLoop } from "../utils/uptime-loop";
import { introCheck } from "../utils/intro-check";
@Discord()
export class Ready {
@ -12,6 +12,9 @@ export class Ready {
await client.initApplicationCommands();
await client.guilds.fetch();
const members = client.guilds.cache.get(Bun.env.GUILD!)?.members;
if(!((await stat("data")).isDirectory())) {
await mkdir("data")
}
if (!(await Bun.file("bump.json").exists())) {
await Bun.write("bump.json", "{}");
}
@ -20,6 +23,5 @@ export class Ready {
}
bumpRemind(client);
uptimeLoop(client);
introCheck(client);
}
}

View File

@ -22,9 +22,7 @@ const client = new Client({
client.on("error", console.error);
const run = async () => {
await importx(
`${dirname(import.meta.url)}/{events,commands,components}/**/*.ts`,
);
await importx(`${dirname(import.meta.url)}/{events,commands,components}/**/*.ts`);
client.login(Bun.env.TOKEN!);
};

View File

@ -1,22 +0,0 @@
import type { Client } from "discordx";
import db from "../db";
import { introTable } from "../db/schema";
import type { TextChannel } from "discord.js";
import { sleep } from "./underage";
export const introCheck = async (client: Client) => {
while (true) {
const channelRes = await db.select().from(introTable);
for (const c of channelRes) {
const channel = client.channels.cache.get(c.channel) as TextChannel;
const guild = client.guilds.cache.get(c.guild);
const msgs = await channel.messages.fetch({ limit: 10 });
for (const m of msgs) {
if (!guild?.members.cache.has(m[1].author.id)) {
await m[1].delete();
}
}
}
await sleep(1000 * 60 * 20);
}
};

View File

@ -1,19 +1,16 @@
import type { Client } from "discordx";
import { sleep } from "./underage";
import type { TextChannel } from "discord.js";
import db from "../db";
import { uptimeTable } from "../db/schema";
export const uptimeLoop = async (client: Client) => {
while (true) {
await sleep(30 * 1000);
const uptimeRes = await db.select().from(uptimeTable);
for (const i of uptimeRes) {
const channel = client.channels.cache.get(i.channel) as TextChannel;
const msg = await channel.messages.fetch(i.message);
await msg.edit(
`bot is up, last ping: <t:${Math.floor(Date.now() / 1000)}:f> | if the ping was more than 1 minute ago, cosmic needs to check the bot lol :3`,
);
}
const channel = client.channels.cache.get(
Bun.env.SCYTHE_CHANNEL,
) as TextChannel;
const msg = await channel.messages.fetch(Bun.env.UPTIME_MESSAGE);
await msg.edit(
`bot is up, last ping: <t:${Math.floor(Date.now()/1000)}:f> | if the ping was more than 1 minute ago, cosmic needs to check the bot lol :3`,
);
}
};