Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
7d65475
Huge structural changes and command for posting verification button
Rufat00 Dec 16, 2025
7cde3ba
set up of global styles
MKorolyova Mar 21, 2026
2c45b43
small fixes in global styles
MKorolyova Mar 26, 2026
d6a1449
Merge pull request #1 from acmutsa/globals-css-set-up
deeg454 Mar 26, 2026
c5f90d2
partner section, partners must be updated later
MKorolyova Mar 30, 2026
6df4bf5
padding editing
MKorolyova Mar 31, 2026
62d36e1
about us component that works on all screen sizes
deeg454 Apr 1, 2026
a28d4d0
Merge pull request #2 from acmutsa/partner-section
deeg454 Apr 1, 2026
219c336
added some slight animation and fixed ratios
deeg454 Apr 1, 2026
0aaeee0
Merge pull request #3 from acmutsa/about-us
deeg454 Apr 1, 2026
e16ecbe
initial commit
deeg454 Apr 1, 2026
1173c1f
Merge pull request #4 from acmutsa/feat/faq
deeg454 Apr 1, 2026
664d482
added motion library
deeg454 Apr 2, 2026
4585476
Footer complete
vivtratx Apr 4, 2026
5cf5156
implement map section with dynamic pin positioning, create reusable …
MKorolyova Apr 21, 2026
45a9ab7
Finished hero.tsx, missing animations
William-Chen07 Apr 24, 2026
5abf92b
faq rendering fixes
deeg454 Apr 29, 2026
46c153a
about section fixes
deeg454 Apr 29, 2026
4aff508
initial commit
deeg454 May 1, 2026
f9327f3
font fixes and styling changes
deeg454 May 1, 2026
ffc1f8c
Merge pull request #5 from acmutsa/footer
deeg454 May 1, 2026
bc55416
Merge pull request #10 from acmutsa/map-section
deeg454 May 1, 2026
7c5595d
Merge pull request #11 from acmutsa/fix/navbar-hero-fixes
deeg454 May 1, 2026
a1fbce5
footer logo fix
deeg454 May 1, 2026
567a801
mlh badge fixes
deeg454 May 1, 2026
a999114
mlh badge for xl screens
deeg454 May 1, 2026
dd8cce9
fixed ui issues, thread working
Rufat00 Jun 20, 2026
fcb42ec
pre animation branch merge changes to ui
deeg454 Jun 21, 2026
9344088
hero and faq animations added from fix branch
deeg454 Jun 21, 2026
0d2b652
final ui fixes
deeg454 Jun 21, 2026
1c750c6
Mlh badge fix
deeg454 Jun 21, 2026
2dcd8f3
Merge pull request #14 from acmutsa/ui-fixes
deeg454 Jun 21, 2026
3f0ac41
Design fix
MKorolyova Jun 22, 2026
554c167
Merge remote-tracking branch 'origin/string' into assets-changes
Rufat00 Jun 22, 2026
5a1e5c6
Pin position and thread size fix for all screen types
MKorolyova Jun 24, 2026
edada7b
Deleting Work with as section from main page
MKorolyova Jun 24, 2026
b02e251
Change assets from png to webp
MKorolyova Jun 24, 2026
b657a85
removing Work with us from main page again
MKorolyova Jun 24, 2026
3b1b1c9
Merge branch 'dev' into assets-changes
Rufat00 Jun 24, 2026
6096df6
Merge pull request #18 from acmutsa/assets-changes
Rufat00 Jun 24, 2026
d2ae4e2
Organizers section with animation
MKorolyova Jun 25, 2026
699b58c
coderabbitai recomended fix
MKorolyova Jun 25, 2026
5e146fd
fixing env
Rufat00 Jun 25, 2026
6bffca2
Merge pull request #20 from acmutsa/organizers-section
Rufat00 Jun 25, 2026
17d268b
added cloudflare configuration
Rufat00 Jun 25, 2026
65f068a
changed build command
Rufat00 Jun 25, 2026
7b86a2f
added necessary flags
Rufat00 Jun 25, 2026
be8a4d7
fixed
Rufat00 Jun 25, 2026
f0f53c0
bug fix
Rufat00 Jun 25, 2026
a99c6c0
some minor fixes
Rufat00 Jun 25, 2026
08c0e8f
added wrangler to gitignore
Rufat00 Jun 25, 2026
8ec7a54
wrangler config
Rufat00 Jun 25, 2026
a65e652
cloudflare preview config fix
MKorolyova Jun 25, 2026
039fe5b
cloudflare upload config fix
MKorolyova Jun 25, 2026
96c9b2e
Merge pull request #22 from acmutsa/cloudflare-config
MKorolyova Jun 26, 2026
8896646
removed redis, nav links are static, registration toggle is through c…
Rufat00 Jun 26, 2026
d8ea41e
hackkit config update
MKorolyova Jun 26, 2026
48607a4
Merge branch 'dev' into deleting-redis
Rufat00 Jun 26, 2026
590fd94
Update hackkit.config.ts
Rufat00 Jun 26, 2026
03fab56
Merge pull request #23 from acmutsa/deleting-redis
Rufat00 Jun 26, 2026
8a3e9f8
fixed color theme
Rufat00 Jun 26, 2026
8020e8a
lock-file
Rufat00 Jun 26, 2026
8883207
FAQ Page
deeg454 Jun 27, 2026
1b753ef
faq hover link animation
deeg454 Jun 27, 2026
08d1234
smoother fade out transition
deeg454 Jun 27, 2026
5e9b63c
final fixes
deeg454 Jun 27, 2026
7dc441f
Registration closed message and Registration form fix
MKorolyova Jun 30, 2026
7e9c2e3
Merge pull request #25 from acmutsa/fixing-colors
deeg454 Jun 30, 2026
469ebb0
mlh requirements
deeg454 Jul 3, 2026
87147de
dashboard
MKorolyova Jul 8, 2026
5315fd8
xl screen fix
MKorolyova Jul 8, 2026
7d38eb9
dashboard navbar
MKorolyova Jul 8, 2026
eeaa336
dashboard navbar colour fix
MKorolyova Jul 9, 2026
7ce0e9a
Merge pull request #27 from acmutsa/xl-screen-fix
deeg454 Jul 10, 2026
899d52a
event-pass
MKorolyova Jul 11, 2026
2bd163b
Merge pull request #26 from acmutsa/dashboard
deeg454 Jul 11, 2026
dcd625d
faq landing animation changes
deeg454 Jul 11, 2026
be1df88
pin location changes
deeg454 Jul 13, 2026
639e498
smaller screen size pin fix
deeg454 Jul 13, 2026
9081737
move one pin
MKorolyova Jul 14, 2026
baedf56
Merge branch 'dev' into faq-page
MKorolyova Jul 14, 2026
06329cd
Merge pull request #24 from acmutsa/faq-page
MKorolyova Jul 14, 2026
4a8abb9
added volunteer form link and removed register link
deeg454 Jul 15, 2026
efd557c
Update link in Hero component to point to register page
MKorolyova Jul 15, 2026
a4e2965
Merge pull request #28 from acmutsa/hotfix
MKorolyova Jul 15, 2026
27f05b5
close registration
MKorolyova Jul 15, 2026
5347be2
profile page
MKorolyova Jul 17, 2026
6a3a757
Merge pull request #29 from acmutsa/profile-page
deeg454 Jul 17, 2026
afa2ae6
Merge branch 'dev' into bot_update
Rufat00 Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ DISCORD_DEV_VERIFY_CHANNEL_ID="52872856278050172988890"
DISCORD_PROD_SERVER_ID="528728562780501729853789"
DISCORD_PROD_VERIFY_CHANNEL_ID="5287378262780501729853789"
DISCORD_SECRET_TOKEN="FHBAHJABDhbHSUeyi7398.S72ljd.HKBVAfug73gifa-74gfwiyB-BHJBDHV07hAJf83"
NODE_ENV="development"
INTERNAL_AUTH_KEY="bwgybgidbsi-4784gyfgs-475hhkdsbfs-bwgybgidbsi-4784gyfgs-475hhkdsbfs-bwgybgidbsi-4784gyfgs-475hhkdsbfs-fbnauib4783-bhfbsjfbs"
SHARED_SECRET="bwgybgidbsi-4784gyfgs-475hhkdsbfs-bwgybgidbsi-4784gyfgs-475hhkdsbfs-bwgybgidbsi-4784gyfgs-475hhkdsbfs-fbnauib4783-bhfbsjfbs"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_YRGIBHSBIbabffjdhvbuYGI7BK"
NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in"
NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up"
Expand All @@ -39,5 +38,5 @@ R2_BUCKET_NAME="your-org-name-userdata"
R2_SECRET_ACCESS_KEY="279y45g79tgbjsbfhbsiufhbs89hg487hsiufs"
TURSO_AUTH_TOKEN="dhbBDSUGFBSUYGBSIUGRBIGBSIYBS7495w8y97w.bivsb7478wGYIFGDSUGFS48y39975y3gtyugysjgs7ugu.BHFBSYUFBSV476guysfuw78gfuy3buybfshbfYUFSGVBYFIBVEIUSBVJSBANIBA74y597bvs6gfusg&gsyjgbfshgbyIGBYUS"
TURSO_DATABASE_URL="libsql:hackkit-orgname.turso.io"
UPSTASH_REDIS_REST_TOKEN="BKFSHBSIY7g9347385gBJFDBSJYFGBSIYFUSIBISUYHGOSHIYtusvufdbsfiubfsibYBG"
UPSTASH_REDIS_REST_URL="https://giant-worm-78102.upstash.io"
# UPSTASH_REDIS_REST_TOKEN="BKFSHBSIY7g9347385gBJFDBSJYFGBSIYFUSIBISUYHGOSHIYtusvufdbsfiubfsibYBG"
# UPSTASH_REDIS_REST_URL="https://giant-worm-78102.upstash.io"
1 change: 1 addition & 0 deletions apps/bot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/dist
255 changes: 22 additions & 233 deletions apps/bot/bot.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
import { Client, Collection, GatewayIntentBits } from "discord.js";
import {
Client,
Collection,
Events,
GatewayIntentBits,
EmbedBuilder,
ButtonBuilder,
ButtonStyle,
ActionRowBuilder,
} from "discord.js";
import { readdirSync } from "node:fs";
import path from "node:path";
import { Hono } from "hono";
import { serve } from "bun";
import c from "config";
import { db } from "db";
import { eq } from "db/drizzle";
import { discordVerification } from "db/schema";
import { getHacker } from "db/functions";
import { nanoid } from "nanoid";
loadCommands,
loadEvents,
loadInteractions,
loadReceivers,
} from "./utils/loaders";
import express from "express";
import sharedSecretMiddleware from "./middleware";

/* DISCORD BOT */

Expand All @@ -31,224 +20,24 @@ const client = new Client({
});

client.commands = new Collection();
(client as any).interactions = new Collection();

const commandsPath = path.join(__dirname, "commands");
const commandFiles = readdirSync(commandsPath).filter((file) =>
file.endsWith(".ts"),
);
for (const file of commandFiles) {
console.log(`[Loading Command] ${file}`);
const filePath = path.join(commandsPath, file);
const command = require(filePath);
// Set a new item in the Collection with the key as the command name and the value as the exported module
if ("data" in command && "execute" in command) {
client.commands.set(command.data.name, command);
} else {
console.log(
`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`,
);
}
}
console.log(`Loaded ${client.commands.size} Commands`);
// Load commands and events and interactions from according folders.
loadCommands(client);
loadEvents(client);
loadInteractions(client);

client.on(Events.InteractionCreate, async (interaction) => {
if (interaction.isChatInputCommand()) {
const command = interaction.client.commands.get(
interaction.commandName,
);
// Start an Express server for webhooks so other applications can communicate with the bot.
const expressApp = express();
expressApp.use(express.json());
expressApp.use(sharedSecretMiddleware);

if (!command) {
console.error(
`No command matching ${interaction.commandName} was found.`,
);
return;
}
//Load receivers for bot's webhooks
loadReceivers(expressApp, client);

try {
await command.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({
content: "There was an error while executing this command!",
ephemeral: true,
});
} else {
await interaction.reply({
content: "There was an error while executing this command!",
ephemeral: true,
});
}
}
} else if (interaction.isButton()) {
if (interaction.customId === "verify") {
console.log("Button Pressed");
const user = interaction.member?.user;
if (!user) {
interaction.reply({
content: "There was an error while executing this command!",
ephemeral: true,
});
return;
}
const vCode = nanoid(20);
console.log(interaction.guildId);
const verification = await db
.insert(discordVerification)
.values({
code: vCode,
discordName: user.username,
discordProfilePhoto: user.avatar || "",
discordUserID: user.id as string,
discordUserTag: user.discriminator as string,
status: "pending",
guild: interaction.guildId as string,
})
.returning();

interaction.reply({
content: `Please click [this link](${c.siteUrl}/discord-verify?code=${vCode}) to verify your registration!`,
ephemeral: true,
});
}
}
const RECEIVERS_PORT = process.env.PORT ? parseInt(process.env.PORT) : 4000;
expressApp.listen(RECEIVERS_PORT, () => {
console.log(`Bot receivers listening on port ${RECEIVERS_PORT}`);
});

client.login(process.env.DISCORD_SECRET_TOKEN);

/* WEB SERVER */

const app = new Hono();
app.get("/postMsgToServer", (h) => {
const internalAuthKey = h.req.query("access");
const serverType: string | undefined | "dev" | "prod" = h.req.query("env");
if (!internalAuthKey || internalAuthKey != process.env.INTERNAL_AUTH_KEY) {
return h.text("access denied");
}
if (!serverType || (serverType !== "dev" && serverType !== "prod")) {
return h.text("invalid env");
}

const verifyBtn = new ButtonBuilder()
.setCustomId("verify")
.setLabel("Verify")
.setStyle(ButtonStyle.Primary);

const verifyEmbed = new EmbedBuilder()
.setColor(0x0099ff)
.setTitle("Verification")
.setURL(c.siteUrl)
.setAuthor({
name: c.botName,
iconURL: c.siteUrl + c.icon.md,
url: c.siteUrl,
})
.setDescription(
`**Verify your registration for ${c.hackathonName} ${c.itteration} to gain access to the rest of the server!**\n\nClick the "verify" button below to begin the verification process.\n\u200B`,
)
.setThumbnail(`${c.siteUrl}${c.icon.md}`)
.setFooter({
text: "Questions or issues? Contact an organizer :)",
iconURL: "https://static.acmutsa.org/Info_Simple.svg.png",
});

const channel = client.channels.cache.get(
serverType === "dev"
? (process.env.DISCORD_DEV_VERIFY_CHANNEL_ID as string)
: (process.env.DISCORD_PROD_VERIFY_CHANNEL_ID as string),
);

if (!channel || !channel.isTextBased()) {
return h.text("Invalid channel");
}

const row = new ActionRowBuilder<ButtonBuilder>().addComponents(verifyBtn);

channel.send({
embeds: [verifyEmbed],
components: [row],
});
return h.text(`Posted to channel!`);
});

app.get("/health", (h) => {
return h.text("ok");
});

app.post("/api/checkDiscordVerification", async (h) => {
const body = await h.req.json();
const internalAuthKey = h.req.query("access");
if (!internalAuthKey || internalAuthKey != process.env.INTERNAL_AUTH_KEY) {
console.log("denied access");
return h.text("access denied");
}

if (body.code === undefined || typeof body.code !== "string") {
console.log("failed cause of malformed body");
return h.json({ success: false });
}

console.log("got here 1");
const verification = await db.query.discordVerification.findFirst({
where: eq(discordVerification.code, body.code),
});
console.log("got here 1 with verification ", verification);

if (!verification || !verification.clerkID) {
console.log("failed cause of no verification or missing clerkID");
return h.json({ success: false });
}
console.log("got here 2");

const user = await getHacker(verification.clerkID);
console.log("got here 2 with user", user);
if (!user) {
console.log("failed cause of no user in db");
return h.json({ success: false });
}

const { discordRole: userGroupRoleName } = (
c.groups as Record<string, { discordRole: string }>
)[Object.keys(c.groups)[user.hackerData.group]];

const guild = client.guilds.cache.get(verification.guild);
if (!guild) {
console.log("failed cause of no guild on intereaction");
return h.json({ success: false });
}

const role = guild.roles.cache.find(
(role) => role.name === c.botParticipantRole,
);
const userGroupRole = guild.roles.cache.find(
(role) => role.name === userGroupRoleName,
);

if (!role || !userGroupRole) {
console.log(
"failed cause could not find a role, was looking for group " +
user.hackerData.group +
" called " +
userGroupRoleName,
);
return h.json({ success: false });
}

const member = guild.members.cache.get(verification.discordUserID);

if (!member) {
console.log("failed cause could not find member");
return h.json({ success: false });
}

await member.roles.add(role);
await member.roles.add(userGroupRole);
await member.setNickname(user.firstName + " " + user.lastName);

return h.json({ success: true });
});

serve({
fetch: app.fetch,
port: process.env.PORT ? parseInt(process.env.PORT) : 4000,
});
86 changes: 86 additions & 0 deletions apps/bot/commands/postVerify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
SlashCommandBuilder,
ChatInputCommandInteraction,
EmbedBuilder,
ButtonBuilder,
ButtonStyle,
ActionRowBuilder,
PermissionsBitField,
} from "discord.js";
import c from "config";

export const data = new SlashCommandBuilder()
.setName("post-verify")
.setDescription("Post the verification embed to this channel (admin only)");

export const execute = async (interaction: ChatInputCommandInteraction) => {
// Ensure command is used in a guild
if (!interaction.inGuild() || !interaction.guild) {
await interaction.reply({
content: "This command can only be used in a server.",
ephemeral: true,
});
return;
}

// Check for administrator permission
const hasAdmin = interaction.memberPermissions?.has(
PermissionsBitField.Flags.Administrator,
);
if (!hasAdmin) {
await interaction.reply({
content:
"You must have Administrator permissions to run this command.",
ephemeral: true,
});
return;
}

const channel = interaction.channel;
if (!channel || !channel.isTextBased()) {
await interaction.reply({
content: "This channel cannot receive messages from the bot.",
ephemeral: true,
});
return;
}

const verifyBtn = new ButtonBuilder()
.setCustomId("verify")
.setLabel("Verify")
.setStyle(ButtonStyle.Primary);

const verifyEmbed = new EmbedBuilder()
.setColor(0x0099ff)
.setTitle("Verification")
.setURL(c.siteUrl)
.setAuthor({
name: c.botName,
iconURL: c.siteUrl + c.icon.md,
url: c.siteUrl,
})
.setDescription(
`**Verify your registration for ${c.hackathonName} ${c.itteration} to gain access to the rest of the server!**\n\nClick the "verify" button below to begin the verification process.\n\u200B`,
)
.setThumbnail(`${c.siteUrl}${c.icon.md}`)
.setFooter({
text: "Questions or issues? Contact an organizer :)",
iconURL: "https://static.acmutsa.org/Info_Simple.svg.png",
});

const row = new ActionRowBuilder<ButtonBuilder>().addComponents(verifyBtn);

try {
await channel.send({ embeds: [verifyEmbed], components: [row] });
await interaction.reply({
content: "Posted verification message.",
ephemeral: true,
});
} catch (err) {
console.error("Failed to post verification message:", err);
await interaction.reply({
content: "Failed to post verification message.",
ephemeral: true,
});
}
};
Loading
Loading