Compare commits

..

No commits in common. "main" and "fix/perms" have entirely different histories.

18 changed files with 16 additions and 840 deletions

3
env.d.ts vendored
View File

@ -1,12 +1,15 @@
module "bun" { module "bun" {
interface Env { interface Env {
TOKEN: string; TOKEN: string;
WELCOME: string;
GOODBYE: string;
GUILD: string; GUILD: string;
BAD_ROLE: string; BAD_ROLE: string;
BOT_ROLE: string; BOT_ROLE: string;
BOT_CHANNEL: string; BOT_CHANNEL: string;
OWNER: string; OWNER: string;
SCYTHE_CHANNEL: string; SCYTHE_CHANNEL: string;
UPTIME_MESSAGE: string;
TICKET_CATEGORY: string; TICKET_CATEGORY: string;
TICKET_CHANNEL: string; TICKET_CHANNEL: string;
MOD_ROLE: string; MOD_ROLE: string;

View File

@ -1,61 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1745930157,
"narHash": "sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ+5dck=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "46e634be05ce9dc6d4db8e664515ba10b78151ae",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,21 +0,0 @@
{
description = "Bun development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in {
devShells.default = pkgs.mkShell {
packages = with pkgs; [
bun
nodejs
];
};
});
}

View File

@ -1,88 +0,0 @@
import {
ApplicationCommandOptionType,
Attachment,
Colors,
CommandInteraction,
EmbedBuilder,
MessageFlags,
TextChannel,
} from "discord.js";
import { Discord, Slash, SlashOption } from "discordx";
import db from "../db";
import { confessTable } from "../db/schema";
import { eq } from "drizzle-orm";
@Discord()
export class Confession {
@Slash({ name: "confess-setup", description: "setup confessions" })
async confessSetup(
@SlashOption({
name: "log_channel",
description: "channel to send logs",
required: true,
type: ApplicationCommandOptionType.Channel,
})
channel: TextChannel,
inter: CommandInteraction,
) {
if (inter.guild?.ownerId != inter.user.id) {
return inter.reply("you aren't the owner silly!");
}
await db.insert(confessTable).values({
guild: inter.guildId!,
channel: channel.id,
});
await inter.reply("confess setup done!");
}
@Slash({
name: "confess",
description: "confessions are LOGGED for moderation purposes.",
})
async confess(
@SlashOption({
name: "message",
description: "message to confess",
required: true,
type: ApplicationCommandOptionType.String,
})
message: string,
@SlashOption({
name: "attachment",
description: "submit an image attachment with the message :3",
required: false,
type: ApplicationCommandOptionType.Attachment,
})
attachment: Attachment,
inter: CommandInteraction,
) {
const embed = new EmbedBuilder()
.setTitle("A Confession!")
.setDescription(message)
.setFooter({ text: "Confessions" })
.setColor(Colors.DarkRed);
const logEmbed = new EmbedBuilder()
.setTitle(`Confession Log from ${inter.user.username}`)
.setDescription(message)
.setFooter({ text: "Confession Log" });
if (attachment) {
embed.setImage(attachment.url);
logEmbed.setImage(attachment.url);
}
const channelRes = await db
.select()
.from(confessTable)
.where(eq(confessTable.guild, inter.guildId!));
const channel = inter.client.channels.cache.get(
channelRes[0].channel,
) as TextChannel;
await (inter.channel as TextChannel).send({ embeds: [embed] });
await inter.reply({
content: "Confession Sent",
flags: MessageFlags.Ephemeral,
});
await channel.send({ embeds: [logEmbed] });
}
}

View File

@ -33,9 +33,6 @@ export class Intro {
set: { channel: channel.id }, set: { channel: channel.id },
setWhere: sql`guild = ${inter.guildId}`, setWhere: sql`guild = ${inter.guildId}`,
}); });
await inter.reply({ await inter.reply({content: "intro setup done!", flags: MessageFlags.Ephemeral});
content: "intro setup done!",
flags: MessageFlags.Ephemeral,
});
} }
} }

View File

@ -1,71 +0,0 @@
import {
ApplicationCommandOptionType,
CommandInteraction,
MessageFlags,
TextChannel,
} from "discord.js";
import { Discord, Slash, SlashOption } from "discordx";
import db from "../db";
import { starboardSettingsTable } from "../db/schema";
import { eq } from "drizzle-orm";
@Discord()
export class StarboardCmds {
@Slash({ name: "starboard-setup", description: "setup the starboard" })
async starboardSetup(
@SlashOption({
name: "channel",
description: "channel to post starboard messages to",
type: ApplicationCommandOptionType.Channel,
required: true,
})
channel: TextChannel,
@SlashOption({
name: "threshold",
description: "number of reacts needed to post to starboard",
type: ApplicationCommandOptionType.Integer,
required: true,
})
threshold: number,
inter: CommandInteraction,
) {
if (inter.user.id !== inter.guild?.ownerId) {
return await inter.reply("can't use this one silly!");
}
await db.insert(starboardSettingsTable).values({
guild: inter.guildId!,
channel: channel.id,
threshold,
enabled: 1,
});
await inter.reply({ content: "Done", flags: MessageFlags.Ephemeral });
}
@Slash({
name: "starboard-enable",
description: "enable/disable the starboard",
})
async starboardEnable(
@SlashOption({
name: "setting",
description: "enable/disable",
type: ApplicationCommandOptionType.Boolean,
required: true,
})
setting: boolean,
inter: CommandInteraction,
) {
if (inter.user.id !== inter.guild?.ownerId) {
return await inter.reply("can't use this one silly!");
}
await db
.update(starboardSettingsTable)
.set({
enabled: setting ? 1 : 0,
})
.where(eq(starboardSettingsTable.guild, inter.guildId!));
await inter.reply({
content: "Setting Changed",
flags: MessageFlags.Ephemeral,
});
}
}

View File

@ -55,13 +55,13 @@ export class TicketComponenets {
PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.SendMessages,
], ],
}, },
{ {
id: inter.client.user.id, id: inter.client.user.id,
allow: [ allow: [
PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.ViewChannel,
PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.SendMessages
], ],
}, }
], ],
}); });
@ -69,7 +69,7 @@ export class TicketComponenets {
Bun.env.TICKET_CATEGORY, Bun.env.TICKET_CATEGORY,
) as CategoryChannel; ) as CategoryChannel;
channel = await channel.setParent(category, { lockPermissions: false }); channel = await channel.setParent(category);
const embed = new EmbedBuilder() const embed = new EmbedBuilder()
.setTitle(`Tickets`) .setTitle(`Tickets`)

View File

@ -1,4 +0,0 @@
CREATE TABLE `confess` (
`guild` text PRIMARY KEY NOT NULL,
`channel` text NOT NULL
);

View File

@ -1,10 +0,0 @@
CREATE TABLE `starboard_messages` (
`message` text PRIMARY KEY NOT NULL
);
--> statement-breakpoint
CREATE TABLE `starboard_settings` (
`guild` text PRIMARY KEY NOT NULL,
`threshold` integer NOT NULL,
`channel` text NOT NULL,
`enabled` integer NOT NULL
);

View File

@ -1,214 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "dea3a962-3fb8-4c45-90fa-baff2f53bfb2",
"prevId": "fb3b9c6d-7d3b-4651-b230-e163bf08b586",
"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": {}
},
"confess": {
"name": "confess",
"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": {}
},
"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,269 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "f14a1768-d64d-47dc-a9bd-ad3646ab3c6e",
"prevId": "dea3a962-3fb8-4c45-90fa-baff2f53bfb2",
"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": {}
},
"confess": {
"name": "confess",
"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": {}
},
"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": {}
},
"starboard_messages": {
"name": "starboard_messages",
"columns": {
"message": {
"name": "message",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"starboard_settings": {
"name": "starboard_settings",
"columns": {
"guild": {
"name": "guild",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"threshold": {
"name": "threshold",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"channel": {
"name": "channel",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"enabled": {
"name": "enabled",
"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": {}
},
"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

@ -29,20 +29,6 @@
"when": 1742927150677, "when": 1742927150677,
"tag": "0003_clumsy_mephistopheles", "tag": "0003_clumsy_mephistopheles",
"breakpoints": true "breakpoints": true
},
{
"idx": 4,
"version": "6",
"when": 1744606507294,
"tag": "0004_keen_bedlam",
"breakpoints": true
},
{
"idx": 5,
"version": "6",
"when": 1746568313383,
"tag": "0005_shallow_dragon_lord",
"breakpoints": true
} }
] ]
} }

View File

@ -36,20 +36,4 @@ export const introTable = sqliteTable("intro", {
channel: text().notNull(), channel: text().notNull(),
}); });
export const confessTable = sqliteTable("confess", {
guild: text().primaryKey(),
channel: text().notNull(),
});
export const starboardMessagesTable = sqliteTable("starboard_messages", {
message: text().primaryKey(),
});
export const starboardSettingsTable = sqliteTable("starboard_settings", {
guild: text().primaryKey(),
threshold: int().notNull(),
channel: text().notNull(),
enabled: int().notNull(),
});
export type ColonThreeType = InferSelectModel<typeof colonTable>; export type ColonThreeType = InferSelectModel<typeof colonTable>;

View File

@ -28,16 +28,11 @@ export class MemberEvents {
await member.roles.add(botRole); await member.roles.add(botRole);
} }
} else { } else {
if ( if(Date.now() - member.user.createdAt.getTime() < 1000 * 60 * 60 * 24 * 7) {
Date.now() - member.user.createdAt.getTime() <
1000 * 60 * 60 * 24 * 7
) {
try { try {
await member.send( 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.")
"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")
} catch (_) {}
await member.kick("account less than week old");
return; return;
} }
await db.insert(colonTable).values({ await db.insert(colonTable).values({

View File

@ -1,49 +0,0 @@
import { type Client, Discord, On, type ArgsOf } from "discordx";
import db from "../db";
import { starboardMessagesTable, starboardSettingsTable } from "../db/schema";
import { eq } from "drizzle-orm";
import { Colors, EmbedBuilder, type TextChannel } from "discord.js";
@Discord()
export class ReactionAdd {
@On({ event: "messageReactionAdd" })
async reactionAdd([re]: ArgsOf<"messageReactionAdd">, client: Client) {
const settings = await db
.select()
.from(starboardSettingsTable)
.where(eq(starboardSettingsTable.guild, re.message.guildId!));
if (
re.count === settings[0].threshold &&
re.emoji.name === "⭐" &&
re.message.channelId !== settings[0].channel &&
settings[0].enabled === 1
) {
await db
.insert(starboardMessagesTable)
.values({ message: re.message.id });
const author = client.guilds.cache
.get(re.message.guildId!)!
.members.cache.get(re.message.author!.id);
const embed = new EmbedBuilder()
.setAuthor({
name: author?.displayName!,
iconURL: author?.displayAvatarURL(),
})
.setTitle(`New message from ${author?.displayName}`)
.setURL(re.message.url)
// Sapphire returns ExpectedValidationError & ExpectedConstraintError
// without specifying null for whatever reason
// Even though .content is already typed as "string | null"
.setDescription(re.message.content || null)
.setImage(re.message.attachments.first()?.url || null)
.setFooter({ text: "\u{00002b50}" })
.setTimestamp(re.message.createdAt)
.setColor(Colors.Yellow);
const channel = client.channels.cache.get(
settings[0].channel,
) as TextChannel;
await channel.send({ embeds: [embed] });
}
}
}

View File

@ -2,6 +2,7 @@ import { Client, Discord, On, type ArgsOf } from "discordx";
import { underageCheck } from "../utils/underage"; import { underageCheck } from "../utils/underage";
import { bumpRemind } from "../utils/bump"; import { bumpRemind } from "../utils/bump";
import { uptimeLoop } from "../utils/uptime-loop"; import { uptimeLoop } from "../utils/uptime-loop";
import { introCheck } from "../utils/intro-check";
@Discord() @Discord()
export class Ready { export class Ready {
@ -19,6 +20,6 @@ export class Ready {
} }
bumpRemind(client); bumpRemind(client);
uptimeLoop(client); uptimeLoop(client);
//introCheck(client); introCheck(client);
} }
} }

View File

@ -15,8 +15,6 @@ const client = new Client({
IntentsBitField.Flags.GuildMessages, IntentsBitField.Flags.GuildMessages,
IntentsBitField.Flags.GuildMembers, IntentsBitField.Flags.GuildMembers,
IntentsBitField.Flags.MessageContent, IntentsBitField.Flags.MessageContent,
IntentsBitField.Flags.GuildMessageReactions,
IntentsBitField.Flags.DirectMessageReactions,
], ],
silent: false, silent: false,
}); });

View File

@ -10,7 +10,6 @@ export const introCheck = async (client: Client) => {
for (const c of channelRes) { for (const c of channelRes) {
const channel = client.channels.cache.get(c.channel) as TextChannel; const channel = client.channels.cache.get(c.channel) as TextChannel;
const guild = client.guilds.cache.get(c.guild); const guild = client.guilds.cache.get(c.guild);
await guild?.fetch();
const msgs = await channel.messages.fetch({ limit: 10 }); const msgs = await channel.messages.fetch({ limit: 10 });
for (const m of msgs) { for (const m of msgs) {
if (!guild?.members.cache.has(m[1].author.id)) { if (!guild?.members.cache.has(m[1].author.id)) {