├── apps ├── bot │ ├── src │ │ ├── lib │ │ │ ├── utils │ │ │ │ ├── misc │ │ │ │ │ └── index.ts │ │ │ │ ├── guards │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── arrays.ts │ │ │ │ │ ├── snowflakes.ts │ │ │ │ │ └── channels.ts │ │ │ │ └── formatting │ │ │ │ │ ├── index.ts │ │ │ │ │ └── strings.ts │ │ │ ├── enums.ts │ │ │ ├── env.ts │ │ │ ├── db.ts │ │ │ ├── clients │ │ │ │ ├── webhooks │ │ │ │ │ └── alert.ts │ │ │ │ ├── twitch.ts │ │ │ │ └── phase.ts │ │ │ ├── logs.ts │ │ │ ├── tw.ts │ │ │ ├── trpc.ts │ │ │ ├── stores.ts │ │ │ └── blacklist.ts │ │ ├── types │ │ │ ├── declarations │ │ │ │ ├── ts-reset.d.ts │ │ │ │ ├── assets.d.ts │ │ │ │ └── phase.d.ts │ │ │ ├── db.ts │ │ │ ├── builders.ts │ │ │ └── custom-ids.ts │ │ ├── assets │ │ │ ├── emojis │ │ │ │ ├── edit.png │ │ │ │ ├── play.png │ │ │ │ ├── plus.png │ │ │ │ ├── stop.png │ │ │ │ ├── delete.png │ │ │ │ ├── minus.png │ │ │ │ ├── muted.png │ │ │ │ ├── pause.png │ │ │ │ ├── repeat.png │ │ │ │ ├── shuffle.png │ │ │ │ ├── users.png │ │ │ │ ├── lock-open.png │ │ │ │ ├── transfer.png │ │ │ │ ├── lock-closed.png │ │ │ │ ├── next-track.png │ │ │ │ ├── connect4-empty.png │ │ │ │ ├── previous-track.png │ │ │ │ ├── connect4-player1.png │ │ │ │ └── connect4-player2.png │ │ │ ├── fonts │ │ │ │ ├── geist-bold.otf │ │ │ │ ├── geist-medium.otf │ │ │ │ ├── geist-regular.otf │ │ │ │ └── geist-semibold.otf │ │ │ ├── load.ts │ │ │ └── fonts.ts │ │ ├── structures │ │ │ ├── games │ │ │ │ └── tictactoe │ │ │ │ │ └── index.ts │ │ │ ├── builders │ │ │ │ ├── index.ts │ │ │ │ ├── ActionRowBuilder.ts │ │ │ │ └── ModalActionRowBuilder.ts │ │ │ └── stores │ │ │ │ └── InviteStore.ts │ │ ├── prestart.ts │ │ ├── main.ts │ │ ├── app │ │ │ ├── commands │ │ │ │ ├── (info) │ │ │ │ │ └── whois │ │ │ │ │ │ └── _utils.ts │ │ │ │ ├── (utility) │ │ │ │ │ ├── id │ │ │ │ │ │ ├── server.ts │ │ │ │ │ │ ├── user.ts │ │ │ │ │ │ ├── role.ts │ │ │ │ │ │ └── channel.ts │ │ │ │ │ ├── echo.ts │ │ │ │ │ └── avatar │ │ │ │ │ │ └── _options.ts │ │ │ │ ├── (voice) │ │ │ │ │ └── music │ │ │ │ │ │ └── _utils.ts │ │ │ │ ├── (module) │ │ │ │ │ └── tag │ │ │ │ │ │ └── list.ts │ │ │ │ └── (fun) │ │ │ │ │ ├── games │ │ │ │ │ ├── tictactoe.ts │ │ │ │ │ └── rps.ts │ │ │ │ │ └── coinflip.ts │ │ │ ├── crons │ │ │ │ └── (internal) │ │ │ │ │ └── status.ts │ │ │ └── events │ │ │ │ └── (modules) │ │ │ │ ├── invite-logs │ │ │ │ ├── invite-create.ts │ │ │ │ ├── ready.ts │ │ │ │ └── invite-delete.ts │ │ │ │ ├── self-roles │ │ │ │ └── _utils.ts │ │ │ │ └── auto-roles │ │ │ │ ├── pendings.ts │ │ │ │ └── joins.ts │ │ └── context.ts │ ├── eslint.config.js │ ├── scripts │ │ ├── dev.sh │ │ ├── check.sh │ │ ├── start.sh │ │ └── lint.sh │ ├── tsconfig.json │ ├── .env.development │ └── .env.template ├── dash │ ├── eslint.config.js │ ├── src │ │ ├── lib │ │ │ ├── env.ts │ │ │ ├── schemas.ts │ │ │ ├── trpc.ts │ │ │ └── utils.ts │ │ ├── types │ │ │ ├── declarations │ │ │ │ ├── ts-reset.d.ts │ │ │ │ ├── csstype.d.ts │ │ │ │ └── slate.d.ts │ │ │ ├── props.ts │ │ │ ├── auth.ts │ │ │ └── dashboard.ts │ │ ├── app │ │ │ ├── guilds │ │ │ │ ├── [id] │ │ │ │ │ ├── commands │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── settings │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── layout.tsx │ │ │ │ ├── loading.tsx │ │ │ │ └── layout.tsx │ │ │ └── auth │ │ │ │ ├── signin │ │ │ │ ├── actions.ts │ │ │ │ └── page.tsx │ │ │ │ ├── layout.tsx │ │ │ │ └── internal │ │ │ │ └── [...authjs] │ │ │ │ └── route.ts │ │ ├── styles │ │ │ └── globals.css │ │ ├── components │ │ │ ├── auth │ │ │ │ └── or-separator.tsx │ │ │ ├── navigation │ │ │ │ └── index.tsx │ │ │ ├── context.tsx │ │ │ ├── client-only.tsx │ │ │ ├── modules │ │ │ │ ├── beta-alert.tsx │ │ │ │ └── module-tags.tsx │ │ │ ├── link.tsx │ │ │ ├── form │ │ │ │ └── field │ │ │ │ │ └── wrapper.tsx │ │ │ ├── guilds │ │ │ │ └── guild-card-search.tsx │ │ │ └── richtext │ │ │ │ └── utils.ts │ │ ├── middleware.ts │ │ └── hooks │ │ │ ├── use-dashboard-context.ts │ │ │ └── use-element-size.ts │ ├── postcss.config.cjs │ ├── tsconfig.json │ ├── next.config.ts │ ├── .env.development │ └── .env.template ├── www │ ├── eslint.config.js │ ├── src │ │ ├── lib │ │ │ └── env.ts │ │ ├── types │ │ │ ├── declarations │ │ │ │ └── ts-reset.d.ts │ │ │ └── props.ts │ │ ├── app │ │ │ ├── favicon.ico │ │ │ ├── (legal) │ │ │ │ └── layout.tsx │ │ │ ├── contact │ │ │ │ ├── layout.tsx │ │ │ │ └── bug-report │ │ │ │ │ └── actions.ts │ │ │ └── not-found.tsx │ │ ├── styles │ │ │ └── globals.css │ │ └── components │ │ │ ├── header.tsx │ │ │ └── link.tsx │ ├── public │ │ ├── apple.png │ │ ├── phase.png │ │ └── banner.png │ ├── postcss.config.cjs │ ├── .env.template │ ├── tsconfig.json │ └── .env.development └── docs │ ├── src │ ├── lib │ │ └── env.ts │ ├── types │ │ ├── props.ts │ │ └── sidebar.ts │ ├── components │ │ ├── astro │ │ │ ├── Header.astro │ │ │ ├── FAQ.astro │ │ │ └── ModuleVariables.astro │ │ └── react │ │ │ ├── header.tsx │ │ │ └── link.tsx │ ├── styles │ │ └── globals.css │ ├── pages │ │ ├── 404.mdx │ │ ├── bot │ │ │ └── [...id].astro │ │ ├── packages │ │ │ └── [...id].astro │ │ └── index.mdx │ └── content │ │ ├── packages │ │ └── index.mdx │ │ └── bot │ │ ├── modules │ │ └── index.mdx │ │ └── index.mdx │ ├── public │ └── fonts │ │ ├── geist-mono.woff2 │ │ └── geist-sans.woff2 │ ├── vercel.json │ ├── tsconfig.json │ ├── .env.development │ ├── eslint.config.js │ ├── astro.config.ts │ └── package.json ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── lint.yml ├── assets ├── logo.png ├── moon.png ├── banner-bg.png ├── logo-halloween.png └── banner-with-text.png ├── packages ├── @repo │ ├── ui │ │ ├── src │ │ │ ├── components │ │ │ │ ├── lucide-icon │ │ │ │ │ └── index.tsx │ │ │ │ ├── slot │ │ │ │ │ └── index.tsx │ │ │ │ ├── simple-icon │ │ │ │ │ └── index.tsx │ │ │ │ ├── skeleton │ │ │ │ │ └── index.tsx │ │ │ │ ├── button-group │ │ │ │ │ └── index.tsx │ │ │ │ ├── label │ │ │ │ │ └── index.tsx │ │ │ │ ├── loading │ │ │ │ │ └── index.tsx │ │ │ │ ├── emoji-picker │ │ │ │ │ └── emoji-types.ts │ │ │ │ ├── codeblock │ │ │ │ │ └── index.tsx │ │ │ │ └── spinner │ │ │ │ │ └── index.tsx │ │ │ └── types │ │ │ │ └── overrides.d.ts │ │ ├── eslint.config.js │ │ ├── tailwind.config.css │ │ └── tsconfig.json │ ├── db │ │ ├── eslint.config.js │ │ ├── tsconfig.json │ │ ├── src │ │ │ ├── postgres │ │ │ │ ├── custom.ts │ │ │ │ ├── schemas │ │ │ │ │ ├── (modules) │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── welcome-messages.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── afks.ts │ │ │ │ │ ├── giveaways.ts │ │ │ │ │ ├── guilds.ts │ │ │ │ │ └── modules.ts │ │ │ │ └── index.ts │ │ │ └── mongo │ │ │ │ ├── models │ │ │ │ ├── guilds │ │ │ │ │ ├── modules │ │ │ │ │ │ ├── warnings.ts │ │ │ │ │ │ ├── join-to-creates.ts │ │ │ │ │ │ ├── bump-reminders.ts │ │ │ │ │ │ ├── auto-roles.ts │ │ │ │ │ │ ├── counters.ts │ │ │ │ │ │ ├── twitch-notifications.ts │ │ │ │ │ │ ├── welcome-messages.ts │ │ │ │ │ │ ├── reaction-roles.ts │ │ │ │ │ │ ├── audit-logs.ts │ │ │ │ │ │ ├── auto-messages.ts │ │ │ │ │ │ └── levels.ts │ │ │ │ │ ├── commands │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── afks.ts │ │ │ │ ├── otps.ts │ │ │ │ ├── tags.ts │ │ │ │ ├── levels.ts │ │ │ │ └── join-to-creates.ts │ │ │ │ ├── genkey.ts │ │ │ │ └── utils.ts │ │ └── drizzle.config.ts │ ├── env │ │ ├── eslint.config.js │ │ ├── tsconfig.json │ │ ├── src │ │ │ ├── merge.ts │ │ │ ├── presets │ │ │ │ ├── database.ts │ │ │ │ ├── twitch.ts │ │ │ │ ├── discord.ts │ │ │ │ ├── authjs.ts │ │ │ │ ├── apps │ │ │ │ │ ├── docs.ts │ │ │ │ │ ├── www.ts │ │ │ │ │ ├── dash.ts │ │ │ │ │ └── bot.ts │ │ │ │ ├── base.ts │ │ │ │ └── trpc.ts │ │ │ ├── index.ts │ │ │ └── lib │ │ │ │ └── constants.ts │ │ └── package.json │ ├── trpc │ │ ├── eslint.config.js │ │ ├── src │ │ │ ├── types │ │ │ │ ├── declarations │ │ │ │ │ └── ts-reset.d.ts │ │ │ │ └── modules.ts │ │ │ ├── server │ │ │ │ ├── router │ │ │ │ │ └── guilds │ │ │ │ │ │ └── modules │ │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── context.ts │ │ │ └── client │ │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── utils │ │ ├── eslint.config.js │ │ ├── src │ │ │ ├── modules │ │ │ │ ├── types │ │ │ │ │ ├── tags.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── constants │ │ │ │ │ ├── tags.ts │ │ │ │ │ ├── schemas │ │ │ │ │ │ ├── join-to-creates.ts │ │ │ │ │ │ ├── auto-roles.ts │ │ │ │ │ │ ├── audit-logs.ts │ │ │ │ │ │ ├── counters.ts │ │ │ │ │ │ ├── twitch-notifications.ts │ │ │ │ │ │ ├── welcome-messages.ts │ │ │ │ │ │ └── warnings.ts │ │ │ │ │ ├── definitions │ │ │ │ │ │ ├── forms.ts │ │ │ │ │ │ ├── tickets.ts │ │ │ │ │ │ ├── warnings.ts │ │ │ │ │ │ ├── auto-roles.ts │ │ │ │ │ │ ├── audit-logs.ts │ │ │ │ │ │ ├── join-to-creates.ts │ │ │ │ │ │ ├── reaction-roles.ts │ │ │ │ │ │ ├── self-roles.ts │ │ │ │ │ │ ├── auto-messages.ts │ │ │ │ │ │ ├── bump-reminders.ts │ │ │ │ │ │ └── twitch-notifications.ts │ │ │ │ │ └── ids.ts │ │ │ │ ├── index.ts │ │ │ │ └── structures │ │ │ │ │ ├── Variable.ts │ │ │ │ │ └── VariableGroup.ts │ │ │ ├── site.ts │ │ │ └── ms.ts │ │ └── tsconfig.json │ ├── zod │ │ ├── lib │ │ │ └── v3 │ │ │ │ ├── types.d.ts │ │ │ │ ├── index.js │ │ │ │ └── index.d.ts │ │ ├── eslint.config.js │ │ ├── tsconfig.json │ │ └── package.json │ ├── style │ │ ├── tsconfig.json │ │ ├── eslint │ │ │ ├── types │ │ │ │ ├── exports.d.ts │ │ │ │ └── declarations.d.ts │ │ │ ├── astro.js │ │ │ ├── bun.js │ │ │ ├── nextjs.js │ │ │ └── plugins │ │ │ │ └── react.js │ │ └── typescript │ │ │ ├── react.json │ │ │ ├── nextjs.json │ │ │ └── base.json │ └── config │ │ ├── tsconfig.json │ │ ├── package.json │ │ └── src │ │ └── site │ │ ├── index.cts │ │ ├── index.mts │ │ └── config.ts ├── @phasejs │ ├── builders │ │ ├── eslint.config.js │ │ ├── src │ │ │ ├── types │ │ │ │ ├── utils.ts │ │ │ │ └── builders.ts │ │ │ ├── lib │ │ │ │ ├── constants.ts │ │ │ │ └── resolvers.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── package.json │ ├── core │ │ ├── eslint.config.js │ │ ├── src │ │ │ ├── lib │ │ │ │ ├── factories.ts │ │ │ │ └── constants.ts │ │ │ ├── types │ │ │ │ ├── prestart.ts │ │ │ │ ├── utils.ts │ │ │ │ ├── client.ts │ │ │ │ ├── crons.ts │ │ │ │ ├── plugins.ts │ │ │ │ ├── middleware.ts │ │ │ │ ├── events.ts │ │ │ │ ├── context.ts │ │ │ │ └── commands.ts │ │ │ ├── structures │ │ │ │ ├── abstracts │ │ │ │ │ └── Base.ts │ │ │ │ └── BotPlugin.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── package.json │ ├── image │ │ ├── eslint.config.js │ │ ├── src │ │ │ ├── types │ │ │ │ └── image.ts │ │ │ ├── index.ts │ │ │ └── structures │ │ │ │ └── Image.ts │ │ ├── tsconfig.json │ │ └── package.json │ ├── logs │ │ ├── eslint.config.js │ │ ├── tsconfig.json │ │ └── package.json │ ├── stores │ │ ├── eslint.config.js │ │ ├── tsconfig.json │ │ ├── src │ │ │ ├── types │ │ │ │ ├── stores.ts │ │ │ │ └── declarations │ │ │ │ │ └── client.ts │ │ │ └── structures │ │ │ │ ├── BaseBotStore.ts │ │ │ │ └── BaseBotKVStore.ts │ │ └── package.json │ └── loaders │ │ ├── eslint.config.js │ │ ├── tsconfig.json │ │ ├── src │ │ ├── lib │ │ │ └── utils.ts │ │ ├── types │ │ │ └── app.ts │ │ └── index.ts │ │ └── package.json └── @plugin │ ├── emoji-sync │ ├── src │ │ ├── types │ │ │ ├── utils.ts │ │ │ ├── outdated.ts │ │ │ └── emojis.ts │ │ ├── lib │ │ │ ├── utils.ts │ │ │ ├── compare.ts │ │ │ └── sync.ts │ │ └── index.ts │ ├── eslint.config.js │ ├── tsconfig.json │ └── package.json │ ├── music │ ├── eslint.config.js │ ├── tsconfig.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ └── utils.ts │ └── package.json │ ├── theme │ ├── eslint.config.js │ ├── tsconfig.json │ ├── src │ │ ├── lib │ │ │ └── utils.ts │ │ ├── index.ts │ │ └── structures │ │ │ └── Theme.ts │ └── package.json │ ├── voice │ ├── eslint.config.js │ ├── src │ │ ├── lib │ │ │ ├── ffmpeg.ts │ │ │ └── utils.ts │ │ └── index.ts │ └── tsconfig.json │ └── blacklist │ ├── eslint.config.js │ ├── tsconfig.json │ ├── src │ ├── lib │ │ └── utils.ts │ ├── types │ │ ├── declarations │ │ │ ├── client.ts │ │ │ └── events.ts │ │ └── plugin.ts │ └── structures │ │ └── Entry.ts │ └── package.json ├── .editorconfig ├── .vscode ├── extensions.json └── launch.json ├── .gitignore ├── railway.json └── README.md /apps/bot/src/lib/utils/misc/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./hidden-content" 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: "mikaeladev" 2 | ko_fi: mikaelareid 3 | -------------------------------------------------------------------------------- /apps/bot/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/bun.js" 2 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/assets/logo.png -------------------------------------------------------------------------------- /assets/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/assets/moon.png -------------------------------------------------------------------------------- /apps/bot/scripts/dev.sh: -------------------------------------------------------------------------------- 1 | export NODE_ENV=development 2 | 3 | bun run src/main.ts 4 | -------------------------------------------------------------------------------- /apps/bot/src/lib/enums.ts: -------------------------------------------------------------------------------- 1 | export enum PhaseColour { 2 | Primary = "#f8f8f8", 3 | } 4 | -------------------------------------------------------------------------------- /apps/bot/src/types/declarations/ts-reset.d.ts: -------------------------------------------------------------------------------- 1 | import "@total-typescript/ts-reset" 2 | -------------------------------------------------------------------------------- /apps/dash/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/nextjs.js" 2 | -------------------------------------------------------------------------------- /apps/www/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/nextjs.js" 2 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/lucide-icon/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "lucide-react" 2 | -------------------------------------------------------------------------------- /apps/bot/src/lib/env.ts: -------------------------------------------------------------------------------- 1 | import { bot } from "@repo/env" 2 | 3 | export const env = bot() 4 | -------------------------------------------------------------------------------- /apps/bot/src/types/db.ts: -------------------------------------------------------------------------------- 1 | export type * from "@repo/db" 2 | export type * from "mongoose" 3 | -------------------------------------------------------------------------------- /apps/www/src/lib/env.ts: -------------------------------------------------------------------------------- 1 | import { www } from "@repo/env" 2 | 3 | export const env = www() 4 | -------------------------------------------------------------------------------- /assets/banner-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/assets/banner-bg.png -------------------------------------------------------------------------------- /packages/@phasejs/builders/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint" 2 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/src/types/utils.ts: -------------------------------------------------------------------------------- 1 | export type Awaitable = T | Promise 2 | -------------------------------------------------------------------------------- /packages/@repo/db/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@repo/env/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@repo/trpc/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@repo/ui/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/bun.js" 2 | -------------------------------------------------------------------------------- /apps/dash/src/lib/env.ts: -------------------------------------------------------------------------------- 1 | import { dash } from "@repo/env" 2 | 3 | export const env = dash() 4 | -------------------------------------------------------------------------------- /apps/docs/src/lib/env.ts: -------------------------------------------------------------------------------- 1 | import { docs } from "@repo/env" 2 | 3 | export const env = docs() 4 | -------------------------------------------------------------------------------- /apps/www/src/types/declarations/ts-reset.d.ts: -------------------------------------------------------------------------------- 1 | import type {} from "@total-typescript/ts-reset" 2 | -------------------------------------------------------------------------------- /packages/@phasejs/core/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@phasejs/image/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@phasejs/logs/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@phasejs/stores/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@plugin/music/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@plugin/theme/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@plugin/voice/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@repo/trpc/src/types/declarations/ts-reset.d.ts: -------------------------------------------------------------------------------- 1 | import "@total-typescript/ts-reset" 2 | -------------------------------------------------------------------------------- /packages/@repo/utils/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@repo/zod/lib/v3/types.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./index.d.ts" 2 | export as namespace Zod 3 | -------------------------------------------------------------------------------- /apps/bot/scripts/check.sh: -------------------------------------------------------------------------------- 1 | tsc --noEmit --incremental --tsBuildInfoFile .phase/cache/.tsbuildinfo 2 | -------------------------------------------------------------------------------- /apps/bot/scripts/start.sh: -------------------------------------------------------------------------------- 1 | export NODE_ENV=production 2 | 3 | cd .phase 4 | bun run --smol bot.js 5 | -------------------------------------------------------------------------------- /apps/bot/src/lib/utils/guards/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./channels" 2 | export * from "./snowflakes" 3 | -------------------------------------------------------------------------------- /apps/dash/src/types/declarations/ts-reset.d.ts: -------------------------------------------------------------------------------- 1 | import type {} from "@total-typescript/ts-reset" 2 | -------------------------------------------------------------------------------- /apps/www/public/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/www/public/apple.png -------------------------------------------------------------------------------- /apps/www/public/phase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/www/public/phase.png -------------------------------------------------------------------------------- /assets/logo-halloween.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/assets/logo-halloween.png -------------------------------------------------------------------------------- /packages/@phasejs/loaders/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@plugin/blacklist/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from "@repo/style/eslint/base.js" 2 | -------------------------------------------------------------------------------- /apps/www/public/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/www/public/banner.png -------------------------------------------------------------------------------- /apps/www/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/www/src/app/favicon.ico -------------------------------------------------------------------------------- /assets/banner-with-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/assets/banner-with-text.png -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/slot/index.tsx: -------------------------------------------------------------------------------- 1 | export { Slot, type SlotProps } from "@radix-ui/react-slot" 2 | -------------------------------------------------------------------------------- /packages/@repo/ui/tailwind.config.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "@repo/config/tailwind/site.css"; 3 | -------------------------------------------------------------------------------- /apps/docs/src/types/props.ts: -------------------------------------------------------------------------------- 1 | export interface Frontmatter { 2 | title: string 3 | description: string 4 | } 5 | -------------------------------------------------------------------------------- /packages/@repo/style/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./typescript/base.json", 3 | "include": ["**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/edit.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/play.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/plus.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/stop.png -------------------------------------------------------------------------------- /packages/@phasejs/image/src/types/image.ts: -------------------------------------------------------------------------------- 1 | import type { Font } from "satori" 2 | 3 | export type ImageFont = Font 4 | -------------------------------------------------------------------------------- /packages/@repo/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | end_of_line = lf 8 | -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/delete.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/minus.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/muted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/muted.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/pause.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/repeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/repeat.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/shuffle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/shuffle.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/users.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/lock-open.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/transfer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/transfer.png -------------------------------------------------------------------------------- /apps/bot/src/assets/fonts/geist-bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/fonts/geist-bold.otf -------------------------------------------------------------------------------- /apps/bot/src/lib/utils/formatting/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./dates" 2 | export * from "./numbers" 3 | export * from "./strings" 4 | -------------------------------------------------------------------------------- /apps/docs/public/fonts/geist-mono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/docs/public/fonts/geist-mono.woff2 -------------------------------------------------------------------------------- /apps/docs/public/fonts/geist-sans.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/docs/public/fonts/geist-sans.woff2 -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/lock-closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/lock-closed.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/next-track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/next-track.png -------------------------------------------------------------------------------- /apps/bot/src/assets/fonts/geist-medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/fonts/geist-medium.otf -------------------------------------------------------------------------------- /apps/bot/src/assets/fonts/geist-regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/fonts/geist-regular.otf -------------------------------------------------------------------------------- /packages/@phasejs/core/src/lib/factories.ts: -------------------------------------------------------------------------------- 1 | export function constantFactory(value: T): () => T { 2 | return () => value 3 | } 4 | -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/connect4-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/connect4-empty.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/previous-track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/previous-track.png -------------------------------------------------------------------------------- /apps/bot/src/assets/fonts/geist-semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/fonts/geist-semibold.otf -------------------------------------------------------------------------------- /packages/@repo/zod/lib/v3/index.js: -------------------------------------------------------------------------------- 1 | import * as z from "./lib.js" 2 | 3 | export * from "./lib.js" 4 | export { z } 5 | export default z 6 | -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/connect4-player1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/connect4-player1.png -------------------------------------------------------------------------------- /apps/bot/src/assets/emojis/connect4-player2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaeladev/phase/HEAD/apps/bot/src/assets/emojis/connect4-player2.png -------------------------------------------------------------------------------- /apps/dash/src/types/props.ts: -------------------------------------------------------------------------------- 1 | export interface LayoutProps { 2 | children: React.ReactNode 3 | params: Promise> 4 | } 5 | -------------------------------------------------------------------------------- /apps/www/src/types/props.ts: -------------------------------------------------------------------------------- 1 | export interface LayoutProps { 2 | children: React.ReactNode 3 | params: Promise> 4 | } 5 | -------------------------------------------------------------------------------- /packages/@repo/zod/lib/v3/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as z from "./lib.d.ts" 2 | 3 | export * from "./lib.d.ts" 4 | export { z } 5 | export default z 6 | -------------------------------------------------------------------------------- /apps/dash/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | } 6 | 7 | module.exports = config 8 | -------------------------------------------------------------------------------- /apps/dash/src/app/guilds/[id]/commands/page.tsx: -------------------------------------------------------------------------------- 1 | export default function CommandsPage() { 2 | return
Commands config is coming soon
3 | } 4 | -------------------------------------------------------------------------------- /apps/dash/src/app/guilds/[id]/settings/page.tsx: -------------------------------------------------------------------------------- 1 | export default function SettingsPage() { 2 | return
Settings config is coming soon
3 | } 4 | -------------------------------------------------------------------------------- /apps/www/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | } 6 | 7 | module.exports = config 8 | -------------------------------------------------------------------------------- /apps/bot/scripts/lint.sh: -------------------------------------------------------------------------------- 1 | export SKIP_ENV_VALIDATION=true 2 | 3 | eslint . --cache --cache-strategy content --cache-location .phase/cache/.eslintcache 4 | -------------------------------------------------------------------------------- /apps/bot/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { Database } from "@repo/db" 2 | 3 | export const db = new Database({ 4 | autoIndex: true, 5 | debug: false, 6 | }) 7 | -------------------------------------------------------------------------------- /apps/bot/src/types/builders.ts: -------------------------------------------------------------------------------- 1 | export type BuilderOrBuilderFunction = 2 | | TReturn 3 | | ((builder: TBuilder) => TReturn) 4 | -------------------------------------------------------------------------------- /apps/docs/src/components/astro/Header.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Header as ReactHeader } from "~/components/react/header" 3 | --- 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/dash/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "@repo/config/tailwind/site.css"; 3 | 4 | @source "../../../../node_modules/@repo/ui/dist/**/*.js"; 5 | -------------------------------------------------------------------------------- /apps/docs/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "@repo/config/tailwind/site.css"; 3 | 4 | @source "../../../../node_modules/@repo/ui/dist/**/*.js"; 5 | -------------------------------------------------------------------------------- /apps/www/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "@repo/config/tailwind/site.css"; 3 | 4 | @source "../../../../node_modules/@repo/ui/dist/**/*.js"; 5 | -------------------------------------------------------------------------------- /packages/@phasejs/builders/src/types/utils.ts: -------------------------------------------------------------------------------- 1 | export type PartialWithRequired< 2 | T extends object, 3 | K extends keyof T, 4 | > = Partial & Required> 5 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/simple-icon/index.tsx: -------------------------------------------------------------------------------- 1 | export { 2 | SiDiscord as DiscordIcon, 3 | SiGithub as GithubIcon, 4 | } from "@icons-pack/react-simple-icons" 5 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/types/tags.ts: -------------------------------------------------------------------------------- 1 | import type { moduleTags } from "~/modules/constants/tags" 2 | 3 | export type ModuleTag = (typeof moduleTags)[number] 4 | -------------------------------------------------------------------------------- /apps/docs/src/pages/404.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "~/components/astro/Layout.astro" 3 | title: "Not Found" 4 | description: "The page you're looking for doesn't exist." 5 | --- 6 | -------------------------------------------------------------------------------- /apps/docs/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { "source": "/docs/:path*", "destination": "/:path*" }, 4 | { "source": "/docs", "destination": "/" } 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /packages/@phasejs/builders/src/types/builders.ts: -------------------------------------------------------------------------------- 1 | export type BuilderOrBuilderFunction = 2 | | TReturn 3 | | ((builder: TBuilder) => TReturn) 4 | -------------------------------------------------------------------------------- /packages/@repo/style/eslint/types/exports.d.ts: -------------------------------------------------------------------------------- 1 | import type { ConfigArray } from "typescript-eslint" 2 | 3 | const configArray: ConfigArray 4 | export default configArray 5 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/prestart.ts: -------------------------------------------------------------------------------- 1 | import type { DjsClient } from "~/types/client" 2 | 3 | export type BotPrestart = (client: DjsClient) => void | Promise 4 | -------------------------------------------------------------------------------- /packages/@repo/env/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@repo/trpc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /apps/bot/src/structures/games/tictactoe/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./shared" 2 | export * from "./TicTacToe" 3 | export * from "./TicTacToeMessage" 4 | export * from "./TicTacToePlayer" 5 | -------------------------------------------------------------------------------- /packages/@phasejs/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@phasejs/image/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@phasejs/logs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@phasejs/stores/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@plugin/music/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@plugin/theme/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@repo/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /apps/bot/src/prestart.ts: -------------------------------------------------------------------------------- 1 | import { db } from "~/lib/db" 2 | import { env } from "~/lib/env" 3 | 4 | export default async function prestart() { 5 | await db.connect(env.MONGODB_URI) 6 | } 7 | -------------------------------------------------------------------------------- /packages/@phasejs/builders/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@phasejs/loaders/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@plugin/blacklist/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/src/content/packages/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Overview" 3 | description: "Learn about the Phase packages and how to use them." 4 | --- 5 | 6 | This page is still being worked on! 7 | -------------------------------------------------------------------------------- /packages/@repo/db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "drizzle.config.ts", "eslint.config.js"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs/src/components/astro/FAQ.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { FAQ as ReactFAQ } from "~/components/react/faq" 3 | --- 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/utils.ts: -------------------------------------------------------------------------------- 1 | export type Optional = T | undefined 2 | export type Awaitable = T | Promise 3 | 4 | export type Prettify = { 5 | [K in keyof T]: T[K] 6 | } & {} 7 | -------------------------------------------------------------------------------- /packages/@plugin/voice/src/lib/ffmpeg.ts: -------------------------------------------------------------------------------- 1 | import ffmpegPath from "ffmpeg-static" 2 | import ffmpeg, { setFfmpegPath } from "fluent-ffmpeg" 3 | 4 | setFfmpegPath(ffmpegPath!) 5 | 6 | export { ffmpeg } 7 | -------------------------------------------------------------------------------- /packages/@plugin/voice/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["src/**/*", "eslint.config.js", "postinstall.ts"], 4 | "compilerOptions": { "baseUrl": "." } 5 | } 6 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/tags.ts: -------------------------------------------------------------------------------- 1 | export const moduleTags = [ 2 | "Moderation", 3 | "Engagement", 4 | "Utility", 5 | "Notifications", 6 | "New", 7 | "Beta", 8 | ] as const 9 | -------------------------------------------------------------------------------- /packages/@repo/zod/eslint.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from "@repo/style/eslint/base.js" 2 | import tseslint from "typescript-eslint" 3 | 4 | tseslint.config(baseConfig, { ignores: ["lib/v3/lib.js"] }) 5 | -------------------------------------------------------------------------------- /apps/bot/src/main.ts: -------------------------------------------------------------------------------- 1 | import { loadApp } from "@phasejs/loaders" 2 | 3 | import { phaseClient } from "~/lib/clients/phase" 4 | 5 | const app = await loadApp(phaseClient) 6 | 7 | await phaseClient.init(app) 8 | -------------------------------------------------------------------------------- /packages/@phasejs/image/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/structures/Image" 2 | export * from "~/structures/ImageBuilder" 3 | 4 | export * from "~/types/image" 5 | 6 | export { version } from "../package.json" 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 💬 General Support 4 | url: https://phasebot.xyz/redirect/discord 5 | about: Please ask general questions here 6 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/custom.ts: -------------------------------------------------------------------------------- 1 | import { customType } from "drizzle-orm/pg-core" 2 | 3 | export const snowflake = customType<{ data: string }>({ 4 | dataType() { 5 | return "varchar(19)" 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /apps/bot/src/lib/clients/webhooks/alert.ts: -------------------------------------------------------------------------------- 1 | import { WebhookClient } from "discord.js" 2 | 3 | import { env } from "~/lib/env" 4 | 5 | export const alertWebhook = new WebhookClient({ 6 | url: env.WEBHOOK_ALERT, 7 | }) 8 | -------------------------------------------------------------------------------- /apps/bot/src/types/declarations/assets.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.png" { 2 | const path: string 3 | export default path 4 | } 5 | 6 | declare module "*.otf" { 7 | const path: string 8 | export default path 9 | } 10 | -------------------------------------------------------------------------------- /packages/@plugin/blacklist/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { version as pkgVersion } from "~/../package.json" 2 | 3 | import type { BotPluginVersion } from "@phasejs/core" 4 | 5 | export const version = pkgVersion as BotPluginVersion 6 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { version as pkgVersion } from "~/../package.json" 2 | 3 | import type { BotPluginVersion } from "@phasejs/core" 4 | 5 | export const version = pkgVersion as BotPluginVersion 6 | -------------------------------------------------------------------------------- /packages/@plugin/theme/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { version as pkgVersion } from "~/../package.json" 2 | 3 | import type { BotPluginVersion } from "@phasejs/core" 4 | 5 | export const pluginVersion = pkgVersion as BotPluginVersion 6 | -------------------------------------------------------------------------------- /packages/@plugin/voice/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { version as pkgVersion } from "~/../package.json" 2 | 3 | import type { BotPluginVersion } from "@phasejs/core" 4 | 5 | export const pluginVersion = pkgVersion as BotPluginVersion 6 | -------------------------------------------------------------------------------- /packages/@repo/zod/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "include": ["lib/**/*", "eslint.config.js"], 4 | "exclude": ["lib/v3/lib.js"], 5 | "compilerOptions": { 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/@phasejs/stores/src/types/stores.ts: -------------------------------------------------------------------------------- 1 | import type { BaseBotKVStore } from "~/structures/BaseBotKVStore" 2 | import type { BaseBotStore } from "~/structures/BaseBotStore" 3 | 4 | export type BotStore = BaseBotStore | BaseBotKVStore 5 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/structures/EmojiSync" 2 | export * from "~/lib/utils" 3 | 4 | export type * from "~/types/emojis" 5 | export type * from "~/types/outdated" 6 | export type * from "~/types/utils" 7 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/schemas/(modules)/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/postgres/schemas/(modules)/join-to-creates" 2 | export * from "~/postgres/schemas/(modules)/levels" 3 | export * from "~/postgres/schemas/(modules)/welcome-messages" 4 | -------------------------------------------------------------------------------- /apps/bot/src/lib/logs.ts: -------------------------------------------------------------------------------- 1 | import { version } from "../../package.json" 2 | 3 | import type { LogsPluginOptions } from "@phasejs/logs" 4 | 5 | export const logsConfig = { 6 | name: "Phase", 7 | version: version, 8 | } satisfies LogsPluginOptions 9 | -------------------------------------------------------------------------------- /apps/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript/react.json", 3 | "exclude": ["dist"], 4 | "include": [ 5 | ".astro/types.d.ts", 6 | "src/**/*", 7 | "eslint.config.js", 8 | "astro.config.ts" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/src/types/outdated.ts: -------------------------------------------------------------------------------- 1 | import type { BotEmoji, BotEmojiWithId } from "~/types/emojis" 2 | 3 | export interface OutdatedBotEmojis { 4 | create: BotEmoji[] 5 | delete: BotEmojiWithId[] 6 | update: BotEmojiWithId[] 7 | } 8 | -------------------------------------------------------------------------------- /packages/@repo/style/typescript/react.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./base.json", 4 | "compilerOptions": { 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "jsx": "react-jsx" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/bot/src/assets/load.ts: -------------------------------------------------------------------------------- 1 | import { isAbsolute, join } from "node:path" 2 | 3 | export function loadAsset(path: string): Bun.BunFile { 4 | const absolutePath = isAbsolute(path) ? path : join(import.meta.dir, path) 5 | return Bun.file(absolutePath) 6 | } 7 | -------------------------------------------------------------------------------- /apps/docs/src/components/react/header.tsx: -------------------------------------------------------------------------------- 1 | import { Header as ReactHeader } from "@repo/ui/header" 2 | import { Link } from "~/components/react/link.tsx" 3 | 4 | export function Header() { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /packages/@repo/style/eslint/types/declarations.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@next/eslint-plugin-next" { 2 | export const flatConfig: Record< 3 | "recommended" | "coreWebVitals", 4 | import("@typescript-eslint/utils").TSESLint.FlatConfig.Config 5 | > 6 | } 7 | -------------------------------------------------------------------------------- /packages/@repo/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript/react.json", 3 | "exclude": ["dist"], 4 | "include": [ 5 | "src/**/*", 6 | "tailwind.config.css", 7 | "eslint.config.js", 8 | "vite.config.ts" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/docs/src/content/bot/modules/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Overview" 3 | description: "Explains what modules are and links to their documentation." 4 | metadata: 5 | moduleId: null 6 | sidebarPriority: 0 7 | --- 8 | 9 | This page is still being worked on! 10 | -------------------------------------------------------------------------------- /packages/@plugin/blacklist/src/types/declarations/client.ts: -------------------------------------------------------------------------------- 1 | import type { Blacklist } from "~/structures/Blacklist" 2 | 3 | import type {} from "discord.js" 4 | 5 | declare module "discord.js" { 6 | interface Client { 7 | blacklist: Blacklist 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/bot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript", 3 | "exclude": [".phase"], 4 | "include": ["src/**/*", "scripts/**/*", "eslint.config.js"], 5 | "compilerOptions": { 6 | "baseUrl": ".", 7 | "paths": { "~/*": ["src/*"] } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/schemas/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/postgres/schemas/afks" 2 | export * from "~/postgres/schemas/giveaways" 3 | export * from "~/postgres/schemas/guilds" 4 | export * from "~/postgres/schemas/modules" 5 | export * from "~/postgres/schemas/themes" 6 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/structures/abstracts/Base.ts: -------------------------------------------------------------------------------- 1 | import type { BotClient } from "~/structures/BotClient" 2 | 3 | export abstract class Base { 4 | public readonly phase: BotClient 5 | 6 | constructor(phase: BotClient) { 7 | this.phase = phase 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "astro-build.astro-vscode", 4 | "bradlc.vscode-tailwindcss", 5 | "unifiedjs.vscode-mdx", 6 | "dbaeumer.vscode-eslint", 7 | "esbenp.prettier-vscode", 8 | "editorconfig.editorconfig" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/dash/src/app/guilds/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Loading } from "@repo/ui/loading" 2 | 3 | export default function LoadingPage() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/schemas/afks.ts: -------------------------------------------------------------------------------- 1 | import { pgTable, varchar } from "drizzle-orm/pg-core" 2 | 3 | import { snowflake } from "~/postgres/custom" 4 | 5 | export const afks = pgTable("afks", { 6 | userId: snowflake().unique().primaryKey(), 7 | reason: varchar({ length: 256 }), 8 | }) 9 | -------------------------------------------------------------------------------- /apps/bot/src/lib/utils/guards/arrays.ts: -------------------------------------------------------------------------------- 1 | import type { TupleOf } from "@repo/utils/types" 2 | 3 | export function isLength( 4 | array: TArrayValue[], 5 | length: TLength, 6 | ): array is TupleOf { 7 | return array.length === length 8 | } 9 | -------------------------------------------------------------------------------- /apps/dash/src/lib/schemas.ts: -------------------------------------------------------------------------------- 1 | import { formModuleSchemas, trpcModuleSchemas } from "@repo/utils/modules" 2 | import { z } from "@repo/zod" 3 | 4 | export const modulesFormSchema = z.object(formModuleSchemas).partial() 5 | export const modulesTrpcSchema = z.object(trpcModuleSchemas).nullablePartial() 6 | -------------------------------------------------------------------------------- /packages/@repo/style/typescript/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": ["./base.json", "./react.json"], 4 | "compilerOptions": { 5 | "jsx": "preserve", 6 | "plugins": [{ "name": "next" }], 7 | "incremental": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/www/src/app/(legal)/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { LayoutProps } from "~/types/props" 2 | 3 | export default function LegalLayout({ children }: LayoutProps) { 4 | return ( 5 |
6 | {children} 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /apps/www/src/app/contact/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { LayoutProps } from "~/types/props" 2 | 3 | export default function ContactLayout({ children }: LayoutProps) { 4 | return ( 5 |
6 | {children} 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(info)/whois/_utils.ts: -------------------------------------------------------------------------------- 1 | export const formatDate = (date: Date) => 2 | date.toLocaleString("en-GB", { 3 | weekday: "short", 4 | year: "numeric", 5 | month: "short", 6 | day: "numeric", 7 | hour: "numeric", 8 | minute: "numeric", 9 | hour12: false, 10 | }) 11 | -------------------------------------------------------------------------------- /apps/dash/src/types/declarations/csstype.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/consistent-indexed-object-style */ 2 | 3 | import type {} from "react" 4 | 5 | declare module "react" { 6 | // add css variable support 7 | interface CSSProperties { 8 | [key: `--${string}`]: string | number 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/client.ts: -------------------------------------------------------------------------------- 1 | import type { BotClient } from "~/structures/BotClient" 2 | import type { Client } from "discord.js" 3 | 4 | declare module "discord.js" { 5 | interface Client { 6 | phase: BotClient 7 | } 8 | } 9 | 10 | export type DjsClient = Client 11 | -------------------------------------------------------------------------------- /apps/bot/src/structures/builders/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/structures/builders/ActionRowBuilder" 2 | export * from "~/structures/builders/EmbedBuilder" 3 | export * from "~/structures/builders/MessageActionRowBuilder" 4 | export * from "~/structures/builders/MessageBuilder" 5 | export * from "~/structures/builders/ModalActionRowBuilder" 6 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/crons.ts: -------------------------------------------------------------------------------- 1 | import type { BotCronContext } from "~/structures/BotCron" 2 | import type { DjsClient } from "~/types/client" 3 | 4 | export type BotCronPattern = string 5 | 6 | export type BotCronExecute = ( 7 | client: DjsClient, 8 | context: BotCronContext, 9 | ) => void | Promise 10 | -------------------------------------------------------------------------------- /packages/@repo/zod/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@repo/zod", 4 | "version": "0.0.0", 5 | "type": "module", 6 | "exports": { 7 | ".": { 8 | "types": "./lib/v3/types.d.ts", 9 | "default": "./lib/v3/index.js" 10 | } 11 | }, 12 | "dependencies": { 13 | "zod": "3.25.x" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/bot/.env.development: -------------------------------------------------------------------------------- 1 | # Security Warning! Do not include any secrets in this file! 2 | # 3 | # This file is used to configure the local development environment and is 4 | # intended to be shared with other developers. 5 | # 6 | # Please place any secrets in `.env.local` instead. 7 | 8 | TRPC_PORT=4000 9 | BASE_URL="http://localhost:3000" 10 | -------------------------------------------------------------------------------- /apps/www/.env.template: -------------------------------------------------------------------------------- 1 | # Security Warning! Do not include any secrets in this file! 2 | # 3 | # This is a template file for `.env.local` and is intended to be shared with 4 | # other developers. 5 | # 6 | # Please place any secrets in `.env.local` instead. 7 | 8 | # generate with openssl: 9 | # $ openssl rand -base64 32 10 | TRPC_TOKEN="" 11 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/types/overrides.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/consistent-indexed-object-style */ 2 | 3 | import "@total-typescript/ts-reset" 4 | import "react" 5 | 6 | declare module "react" { 7 | // add css variable support 8 | interface CSSProperties { 9 | [key: `--${string}`]: string | number 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/bot/src/lib/clients/twitch.ts: -------------------------------------------------------------------------------- 1 | import { ApiClient } from "@twurple/api" 2 | import { AppTokenAuthProvider } from "@twurple/auth" 3 | 4 | import { env } from "~/lib/env" 5 | 6 | export const twitchAPI = new ApiClient({ 7 | authProvider: new AppTokenAuthProvider( 8 | env.TWITCH_CLIENT_ID, 9 | env.TWITCH_CLIENT_SECRET, 10 | ), 11 | }) 12 | -------------------------------------------------------------------------------- /apps/dash/src/components/auth/or-separator.tsx: -------------------------------------------------------------------------------- 1 | export function OrSeparator() { 2 | return ( 3 |
4 |
5 | Or 6 |
7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /packages/@repo/trpc/src/server/router/guilds/modules/index.ts: -------------------------------------------------------------------------------- 1 | import { toggleProcedures } from "~/server/router/guilds/modules/toggle" 2 | import { updateProcedures } from "~/server/router/guilds/modules/update" 3 | import { router } from "~/server/trpc" 4 | 5 | export const modulesRouter = router({ 6 | ...toggleProcedures, 7 | ...updateProcedures, 8 | }) 9 | -------------------------------------------------------------------------------- /packages/@phasejs/builders/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export { version } from "~/../package.json" 2 | 3 | export const COMMAND_BUILDER_TAG = Symbol("COMMAND_BUILDER_TAG") 4 | export const SUBCOMMAND_BUILDER_TAG = Symbol("SUBCOMMAND_BUILDER_TAG") 5 | export const CRON_BUILDER_TAG = Symbol("CRON_BUILDER_TAG") 6 | export const EVENT_BUILDER_TAG = Symbol("EVENT_BUILDER_TAG") 7 | -------------------------------------------------------------------------------- /packages/@plugin/blacklist/src/types/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { BotClient } from "@phasejs/core" 2 | import type { Entry } from "~/structures/Entry" 3 | import type { Snowflake } from "discord.js" 4 | 5 | export interface BlacklistPluginOptions { 6 | entries?: Iterable<[Snowflake, Entry]> 7 | populate?: (phase: BotClient) => Iterable<[Snowflake, Entry]> 8 | } 9 | -------------------------------------------------------------------------------- /apps/dash/src/types/declarations/slate.d.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ChannelElement, 3 | MentionElement, 4 | TextElement, 5 | TextLeaf, 6 | } from "~/types/slate" 7 | 8 | import type {} from "slate" 9 | 10 | declare module "slate" { 11 | interface CustomTypes { 12 | Text: TextLeaf 13 | Element: TextElement | ChannelElement | MentionElement 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/@repo/env/src/merge.ts: -------------------------------------------------------------------------------- 1 | import type { UnionToIntersection } from "@repo/utils/types" 2 | 3 | type Env = Readonly> 4 | type MergeEnvs = UnionToIntersection 5 | 6 | export function mergeEnvs(...envs: T) { 7 | return Object.fromEntries(envs.flatMap(Object.entries)) as MergeEnvs 8 | } 9 | -------------------------------------------------------------------------------- /apps/bot/src/lib/tw.ts: -------------------------------------------------------------------------------- 1 | import tailwindConfig from "@repo/config/tailwind/base.ts" 2 | import { tailwindToCSS } from "tw-to-css" 3 | 4 | const { twj } = tailwindToCSS({ 5 | config: tailwindConfig, 6 | options: { 7 | ignoreMediaQueries: true, 8 | }, 9 | }) 10 | 11 | export const tw = twj as ( 12 | ...content: TemplateStringsArray[] 13 | ) => React.CSSProperties 14 | -------------------------------------------------------------------------------- /apps/bot/src/types/declarations/phase.d.ts: -------------------------------------------------------------------------------- 1 | import type {} from "@phasejs/core" 2 | 3 | import type { PermissionResolvable } from "discord.js" 4 | 5 | declare module "@phasejs/core" { 6 | interface BotCommandMetadata { 7 | dmPermission?: boolean 8 | requiredBotPermissions?: PermissionResolvable 9 | requiredUserPermissions?: PermissionResolvable 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/docs/.env.development: -------------------------------------------------------------------------------- 1 | # Security Warning! Do not include any secrets in this file! 2 | # 3 | # This file is used to configure the local development environment and is 4 | # intended to be shared with other developers. 5 | # 6 | # Please place any secrets in `.env.local` instead. 7 | 8 | PORT=3002 9 | 10 | PUBLIC_BASE_URL="http://localhost:3000" 11 | PUBLIC_BASE_PATH="/docs" 12 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | import { constantFactory } from "~/lib/factories" 2 | 3 | import type { BotContextCreators } from "~/types/context" 4 | 5 | export const baseBotContextCreators: BotContextCreators = { 6 | commands: constantFactory({}), 7 | crons: constantFactory({}), 8 | events: constantFactory({}), 9 | } 10 | 11 | export { version } from "~/../package.json" 12 | -------------------------------------------------------------------------------- /packages/@repo/db/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "drizzle-kit" 2 | 3 | if (!process.env.POSTGRES_URI) { 4 | throw new Error("POSTGRES_URI is not set.") 5 | } 6 | 7 | export default defineConfig({ 8 | out: "./drizzle", 9 | schema: "./src/postgres/schemas/**/*", 10 | dialect: "postgresql", 11 | dbCredentials: { 12 | url: process.env.POSTGRES_URI, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /apps/dash/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript/nextjs.json", 3 | "exclude": [".next"], 4 | "include": [ 5 | ".next/types/**/*.ts", 6 | "src/**/*", 7 | "eslint.config.js", 8 | "next-env.d.ts", 9 | "next.config.ts", 10 | "postcss.config.cjs" 11 | ], 12 | "compilerOptions": { 13 | "baseUrl": ".", 14 | "paths": { "~/*": ["./src/*"] } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/www/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/style/typescript/nextjs.json", 3 | "exclude": [".next"], 4 | "include": [ 5 | ".next/types/**/*.ts", 6 | "src/**/*", 7 | "eslint.config.js", 8 | "next-env.d.ts", 9 | "next.config.ts", 10 | "postcss.config.cjs" 11 | ], 12 | "compilerOptions": { 13 | "baseUrl": ".", 14 | "paths": { "~/*": ["./src/*"] } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/database.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import type { BaseOptions } from "~/lib/constants" 5 | 6 | export function database(baseOptions: BaseOptions) { 7 | return createEnv({ 8 | ...baseOptions, 9 | server: { 10 | MONGODB_URI: z.string(), 11 | POSTGRES_URI: z.string(), 12 | }, 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/schemas/join-to-creates.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | 3 | import { moduleSchema, moduleSchemaExport } from "./_utils" 4 | 5 | const baseSchema = moduleSchema({ 6 | channel: z.string().snowflake("Channel is required"), 7 | category: z.string().snowflake("Category is required"), 8 | }) 9 | 10 | export const joinToCreates = moduleSchemaExport(baseSchema) 11 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/forms.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const forms = { 6 | id: ModuleId.Forms, 7 | name: "Forms", 8 | description: "Creates forms in your server for your members to fill out.", 9 | tags: ["Utility"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/twitch.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import type { BaseOptions } from "~/lib/constants" 5 | 6 | export function twitch(baseOptions: BaseOptions) { 7 | return createEnv({ 8 | ...baseOptions, 9 | server: { 10 | TWITCH_CLIENT_SECRET: z.string(), 11 | TWITCH_CLIENT_ID: z.string(), 12 | }, 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/tickets.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const tickets = { 6 | id: ModuleId.Tickets, 7 | name: "Tickets", 8 | description: "Enables the creation of private ticket-style channels.", 9 | tags: ["Utility"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/warnings.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface Warnings { 4 | enabled: boolean 5 | warnings: string[] 6 | } 7 | 8 | export const warningsSchema = new Schema( 9 | { 10 | enabled: { type: Schema.Types.Boolean, required: true }, 11 | warnings: { type: [Schema.Types.String], required: true }, 12 | }, 13 | { _id: false }, 14 | ) 15 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/warnings.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const warnings = { 6 | id: ModuleId.Warnings, 7 | name: "Warnings", 8 | description: "Helps moderators add, remove, and track member warnings.", 9 | tags: ["Moderation"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/auto-roles.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const autoRoles = { 6 | id: ModuleId.AutoRoles, 7 | name: "Auto Roles", 8 | description: "Automatically assigns roles to new members of your server.", 9 | tags: ["Utility"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /apps/www/src/components/header.tsx: -------------------------------------------------------------------------------- 1 | import { Header as BaseHeader } from "@repo/ui/header" 2 | import { Link } from "~/components/link" 3 | 4 | import type { HeaderProps as BaseHeaderProps } from "@repo/ui/header" 5 | 6 | export interface HeaderProps 7 | extends Omit {} 8 | 9 | export function Header(props: HeaderProps) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/afks.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | import { defineModel } from "~/mongo/utils" 4 | 5 | export interface AFK { 6 | user: string 7 | reason?: string 8 | } 9 | 10 | const afkSchema = new Schema({ 11 | user: { type: Schema.Types.String, required: true, unique: true }, 12 | reason: { type: Schema.Types.String }, 13 | }) 14 | 15 | export const afks = defineModel("AFKs", afkSchema) 16 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/audit-logs.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const auditLogs = { 6 | id: ModuleId.AuditLogs, 7 | name: "Audit Logs", 8 | description: "Sends detailed audit log entries to the channels you specify.", 9 | tags: ["Moderation"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/modules/constants/definitions" 2 | export * from "~/modules/constants/ids" 3 | export * from "~/modules/constants/tags" 4 | export * from "~/modules/constants/schemas" 5 | 6 | export * from "~/modules/structures/Variable" 7 | export * from "~/modules/structures/VariableGroup" 8 | 9 | export type * from "~/modules/types/definitions" 10 | export type * from "~/modules/types/tags" 11 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/discord.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import type { BaseOptions } from "~/lib/constants" 5 | 6 | export function discord(baseOptions: BaseOptions) { 7 | return createEnv({ 8 | ...baseOptions, 9 | server: { 10 | DISCORD_TOKEN: z.string(), 11 | DISCORD_SECRET: z.string(), 12 | DISCORD_ID: z.string(), 13 | }, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/join-to-creates.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const joinToCreates = { 6 | id: ModuleId.JoinToCreates, 7 | name: "Join to Creates", 8 | description: "Enables the creation of temporary voice channels.", 9 | tags: ["Utility"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/reaction-roles.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const reactionRoles = { 6 | id: ModuleId.ReactionRoles, 7 | name: "Reaction Roles", 8 | description: "Enables members to self-assign roles via reactions.", 9 | tags: ["Utility"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/self-roles.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const selfRoles = { 6 | id: ModuleId.SelfRoles, 7 | name: "Self Roles", 8 | description: "Enables members to self-assign roles through message methods.", 9 | tags: ["Utility", "Beta"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/types/utils.ts: -------------------------------------------------------------------------------- 1 | export type CapitalizeWords = 2 | T extends `${infer FirstWord} ${infer Rest}` 3 | ? `${Capitalize}${CapitalizeWords}` 4 | : Capitalize 5 | 6 | export type RemoveSpaces = T extends `${infer A} ${infer B}` 7 | ? RemoveSpaces<`${A}${B}`> 8 | : T 9 | 10 | export type FormatName = RemoveSpaces> 11 | -------------------------------------------------------------------------------- /apps/bot/src/lib/utils/guards/snowflakes.ts: -------------------------------------------------------------------------------- 1 | import { SnowflakeUtil } from "discord.js" // from @sapphire/snowflake 2 | 3 | import type { Snowflake } from "discord.js" 4 | 5 | export function isSnowflake(id: unknown): id is Snowflake { 6 | try { 7 | if (typeof id !== "string" || typeof id !== "bigint") return false 8 | return SnowflakeUtil.deconstruct(id).timestamp > SnowflakeUtil.epoch 9 | } catch { 10 | return false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/@phasejs/stores/src/structures/BaseBotStore.ts: -------------------------------------------------------------------------------- 1 | import { Base } from "@phasejs/core" 2 | 3 | import type { BotClient } from "@phasejs/core" 4 | 5 | export abstract class BaseBotStore extends Base { 6 | protected _init = false 7 | 8 | constructor(phase: BotClient) { 9 | super(phase) 10 | } 11 | 12 | public async init() { 13 | if (this._init) return this 14 | 15 | this._init = true 16 | return this 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/authjs.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import type { BaseOptions } from "~/lib/constants" 5 | 6 | export function authjs(baseOptions: BaseOptions) { 7 | return createEnv({ 8 | ...baseOptions, 9 | server: { 10 | AUTH_COOKIE_SECRET: z.string().base64().max(44), 11 | AUTH_OTP_SECRET: z.string().base64().max(44), 12 | }, 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/auto-messages.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const autoMessages = { 6 | id: ModuleId.AutoMessages, 7 | name: "Auto Messages", 8 | description: "Automatically sends messages on a set interval.", 9 | tags: ["Utility", "Engagement"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/apps/docs.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import { astroBaseOptions } from "~/lib/constants" 5 | 6 | import { base } from "~/presets/base" 7 | 8 | export function docs() { 9 | return createEnv({ 10 | ...astroBaseOptions, 11 | extends: [base(astroBaseOptions)], 12 | client: { 13 | PUBLIC_BASE_URL: z.string().url(), 14 | }, 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/skeleton/index.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@repo/utils/site" 2 | 3 | export interface SkeletonProps extends React.HTMLAttributes {} 4 | 5 | export function Skeleton({ className, ...props }: SkeletonProps) { 6 | return ( 7 |
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /apps/www/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { NotFound } from "@repo/ui/not-found" 2 | 3 | import type { Metadata } from "next" 4 | 5 | export const metadata: Metadata = { 6 | title: "Not Found", 7 | description: "The page you're looking for doesn't exist or has been moved.", 8 | } 9 | 10 | export default function NotFoundPage() { 11 | return ( 12 |
13 | 14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # vercel 7 | .vercel 8 | 9 | # build 10 | .turbo 11 | .nixpacks 12 | .astro 13 | .phase 14 | .next 15 | dist 16 | 17 | # development 18 | next-env.d.ts 19 | 20 | # debug 21 | *.tsbuildinfo 22 | 23 | # local env files 24 | .env 25 | .env.local 26 | .env.development.local 27 | .env.test.local 28 | .env.production.local 29 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(utility)/id/server.ts: -------------------------------------------------------------------------------- 1 | import { BotSubcommandBuilder } from "@phasejs/builders" 2 | 3 | export default new BotSubcommandBuilder() 4 | .setName("server") 5 | .setDescription("Gets the ID of the server.") 6 | .setMetadata({ dmPermission: false }) 7 | .setExecute((interaction) => { 8 | const id = interaction.guildId! 9 | 10 | void interaction.reply({ 11 | content: id, 12 | ephemeral: true, 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/@phasejs/builders/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/lib/constants" 2 | export * from "~/lib/resolvers" 3 | 4 | export * from "~/structures/abstracts/BotCommandBuilderBase" 5 | export * from "~/structures/BotCommandBuilder" 6 | export * from "~/structures/BotSubcommandBuilder" 7 | export * from "~/structures/BotEventBuilder" 8 | export * from "~/structures/BotCronBuilder" 9 | 10 | export type * from "~/types/builders" 11 | export type * from "~/types/utils" 12 | -------------------------------------------------------------------------------- /apps/dash/src/components/navigation/index.tsx: -------------------------------------------------------------------------------- 1 | import { DesktopNavigation } from "~/components/navigation/desktop" 2 | import { MobileNavigation } from "~/components/navigation/mobile" 3 | 4 | export function Navigation() { 5 | return ( 6 | <> 7 | 10 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/plugins.ts: -------------------------------------------------------------------------------- 1 | import type { BotClient } from "~/structures/BotClient" 2 | import type { Awaitable } from "~/types/utils" 3 | 4 | export type BotPluginVersion = `${number}.${number}.${number}` 5 | 6 | export type BotPluginLoadTrigger = "init" | "startup" | "ready" 7 | 8 | export type BotPluginLoadFunction = ( 9 | client: BotClient, 10 | ) => Awaitable 11 | -------------------------------------------------------------------------------- /packages/@repo/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@repo/config", 4 | "version": "0.0.0", 5 | "type": "module", 6 | "exports": { 7 | "./*": "./src/*", 8 | "./site": { 9 | "import": "./src/site/index.mts", 10 | "require": "./src/site/index.cts" 11 | } 12 | }, 13 | "dependencies": { 14 | "tailwindcss-animate": "^1.0.7" 15 | }, 16 | "peerDependencies": { 17 | "tailwindcss": "catalog:web" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/base.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import type { BaseOptions } from "~/lib/constants" 5 | 6 | export function base(baseOptions: BaseOptions) { 7 | return createEnv({ 8 | ...baseOptions, 9 | shared: { 10 | NODE_ENV: z.enum(["development", "production"]).default("development"), 11 | SKIP_ENV_VALIDATION: z.boolean().optional(), 12 | }, 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/bump-reminders.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const bumpReminders = { 6 | id: ModuleId.BumpReminders, 7 | name: "Bump Reminders", 8 | description: "Reminds members to bump your server after the cooldown period.", 9 | tags: ["Utility", "Engagement"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /apps/dash/next.config.ts: -------------------------------------------------------------------------------- 1 | import { siteConfig } from "@repo/config/site" 2 | 3 | import type { NextConfig } from "next" 4 | 5 | const protocolRegex = /^https?:\/\// 6 | const proxyOrigin = siteConfig.baseUrl.replace(protocolRegex, "") 7 | 8 | export default { 9 | transpilePackages: ["@repo/ui"], 10 | basePath: siteConfig.basePath, 11 | experimental: { 12 | serverActions: { 13 | allowedOrigins: [proxyOrigin], 14 | }, 15 | }, 16 | } satisfies NextConfig 17 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/definitions/twitch-notifications.ts: -------------------------------------------------------------------------------- 1 | import { ModuleId } from "~/modules/constants/ids" 2 | 3 | import type { ModuleDefinition } from "~/modules/types/definitions" 4 | 5 | export const twitchNotifications = { 6 | id: ModuleId.TwitchNotifications, 7 | name: "Twitch Notifications", 8 | description: "Notifies your server when a Twitch streamer goes live.", 9 | tags: ["Notifications"], 10 | } as const satisfies ModuleDefinition 11 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/schemas/auto-roles.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | 3 | import { moduleSchema, moduleSchemaExport } from "./_utils" 4 | 5 | const baseSchema = moduleSchema({ 6 | roles: z 7 | .object({ 8 | id: z.string().snowflake("Role is required"), 9 | target: z.enum(["everyone", "members", "bots"]), 10 | }) 11 | .array() 12 | .max(10), 13 | }) 14 | 15 | export const autoRoles = moduleSchemaExport(baseSchema) 16 | -------------------------------------------------------------------------------- /apps/dash/src/app/guilds/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Navigation } from "~/components/navigation" 2 | 3 | import type { LayoutProps } from "~/types/props" 4 | 5 | export default function Layout({ children }: LayoutProps) { 6 | return ( 7 |
8 | 9 |
10 | {children} 11 |
12 |
13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /apps/dash/src/components/context.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { DashboardContext } from "~/hooks/use-dashboard-context" 4 | 5 | import type { DashboardData } from "~/types/dashboard" 6 | 7 | export function DashboardProvider({ 8 | value, 9 | children, 10 | }: { 11 | value: DashboardData 12 | children: React.ReactNode 13 | }) { 14 | return ( 15 | 16 | {children} 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /apps/dash/src/lib/trpc.ts: -------------------------------------------------------------------------------- 1 | import { createClient as _createClient } from "@repo/trpc/client" 2 | 3 | import { env } from "~/lib/env" 4 | 5 | export type TRPCClient = ReturnType 6 | 7 | export function createClient(auth: { adminId?: string; guildId?: string }) { 8 | return _createClient({ 9 | url: env.TRPC_URL, 10 | auth: { 11 | token: env.TRPC_TOKEN, 12 | adminId: auth.adminId, 13 | guildId: auth.guildId, 14 | }, 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /apps/www/.env.development: -------------------------------------------------------------------------------- 1 | # Security Warning! Do not include any secrets in this file! 2 | # 3 | # This file is used to configure the local development environment and is 4 | # intended to be shared with other developers. 5 | # 6 | # Please place any secrets in `.env.local` instead. 7 | 8 | PORT=3000 9 | 10 | DASH_URL="http://localhost:3001/dashboard" 11 | DOCS_URL="http://localhost:3002/docs" 12 | TRPC_URL="http://localhost:4000" 13 | 14 | NEXT_PUBLIC_BASE_URL="http://localhost:3000" 15 | -------------------------------------------------------------------------------- /packages/@repo/env/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/lib/constants" 2 | 3 | export * from "~/presets/apps/bot" 4 | export * from "~/presets/apps/dash" 5 | export * from "~/presets/apps/docs" 6 | export * from "~/presets/apps/www" 7 | 8 | export * from "~/presets/authjs" 9 | export * from "~/presets/base" 10 | export * from "~/presets/database" 11 | export * from "~/presets/discord" 12 | export * from "~/presets/trpc" 13 | export * from "~/presets/twitch" 14 | 15 | export * from "~/merge" 16 | -------------------------------------------------------------------------------- /packages/@repo/trpc/src/types/modules.ts: -------------------------------------------------------------------------------- 1 | import type { GuildModules } from "@repo/db" 2 | import type { ModuleId } from "@repo/utils/modules" 3 | 4 | export type ExtraModuleFormData = { 5 | [ModuleId.TwitchNotifications]: { 6 | streamerNames: Record 7 | } 8 | } 9 | 10 | export type GuildModulesWithExtraData = Partial<{ 11 | [K in ModuleId]: K extends keyof ExtraModuleFormData 12 | ? GuildModules[K] & { _data: ExtraModuleFormData[K] } 13 | : GuildModules[K] 14 | }> 15 | -------------------------------------------------------------------------------- /apps/bot/src/structures/builders/ActionRowBuilder.ts: -------------------------------------------------------------------------------- 1 | import { Mixin } from "ts-mixer" 2 | 3 | import { MessageActionRowBuilder } from "~/structures/builders/MessageActionRowBuilder" 4 | import { ModalActionRowBuilder } from "~/structures/builders/ModalActionRowBuilder" 5 | 6 | export type ActionRowBuilderReturnType = 7 | | MessageActionRowBuilder 8 | | ModalActionRowBuilder 9 | 10 | export class ActionRowBuilder extends Mixin( 11 | MessageActionRowBuilder, 12 | ModalActionRowBuilder, 13 | ) {} 14 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/middleware.ts: -------------------------------------------------------------------------------- 1 | import type { BotCommandContext } from "~/structures/BotCommand" 2 | import type { BotCommandExecute } from "~/types/commands" 3 | import type { ChatInputCommandInteraction } from "discord.js" 4 | 5 | export type BotCommandMiddleware = ( 6 | interaction: ChatInputCommandInteraction, 7 | context: BotCommandContext, 8 | execute: BotCommandExecute, 9 | ) => unknown 10 | 11 | export interface BotMiddleware { 12 | commands?: BotCommandMiddleware 13 | } 14 | -------------------------------------------------------------------------------- /apps/docs/eslint.config.js: -------------------------------------------------------------------------------- 1 | import astroConfig from "@repo/style/eslint/astro.js" 2 | import tseslint from "typescript-eslint" 3 | 4 | // projectService isn't supported by astro-eslint-parser right now: 5 | // https://github.com/ota-meshi/eslint-plugin-astro/issues/447 6 | 7 | export default tseslint.config(astroConfig, { 8 | languageOptions: { 9 | parserOptions: { 10 | project: true, 11 | projectService: false, 12 | tsconfigRootDir: import.meta.dirname, 13 | }, 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /apps/dash/.env.development: -------------------------------------------------------------------------------- 1 | # Security Warning! Do not include any secrets in this file! 2 | # 3 | # This file is used to configure the local development environment and is 4 | # intended to be shared with other developers. 5 | # 6 | # Please place any secrets in `.env.local` instead. 7 | 8 | PORT=3001 9 | 10 | AUTH_URL="$NEXT_PUBLIC_BASE_URL$NEXT_PUBLIC_BASE_PATH/auth/internal" 11 | 12 | TRPC_URL="http://localhost:4000" 13 | 14 | NEXT_PUBLIC_BASE_URL="http://localhost:3000" 15 | NEXT_PUBLIC_BASE_PATH="/dashboard" 16 | -------------------------------------------------------------------------------- /packages/@repo/style/eslint/astro.js: -------------------------------------------------------------------------------- 1 | import astroPlugin from "eslint-plugin-astro" 2 | import tseslint from "typescript-eslint" 3 | 4 | import baseConfig from "./base.js" 5 | import reactPluginConfig from "./plugins/react.js" 6 | 7 | export default tseslint.config( 8 | baseConfig, 9 | reactPluginConfig, 10 | astroPlugin.configs["flat/recommended"], 11 | { 12 | name: "phase/astro", 13 | rules: { 14 | "import-x/no-unresolved": ["error", { ignore: ["^astro(:\\w+)?$"] }], 15 | }, 16 | }, 17 | ) 18 | -------------------------------------------------------------------------------- /packages/@plugin/blacklist/src/types/declarations/events.ts: -------------------------------------------------------------------------------- 1 | import type { Entry } from "~/structures/Entry" 2 | import type { Guild } from "discord.js" 3 | 4 | import type {} from "@phasejs/core" 5 | 6 | declare module "@phasejs/core" { 7 | interface BotClientEvents { 8 | // entry events 9 | "blacklist.entryCreate": Entry 10 | "blacklist.entryDelete": Entry 11 | "blacklist.entryEdit": Entry 12 | // join events 13 | "blacklist.joinPrevented": Entry 14 | "blacklist.joinSuccess": Guild 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/join-to-creates.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface JoinToCreates { 4 | enabled: boolean 5 | channel: string 6 | category: string 7 | } 8 | 9 | export const joinToCreatesSchema = new Schema( 10 | { 11 | enabled: { type: Schema.Types.Boolean, required: true }, 12 | channel: { type: Schema.Types.String, required: true }, 13 | category: { type: Schema.Types.String, required: true }, 14 | }, 15 | { _id: false }, 16 | ) 17 | -------------------------------------------------------------------------------- /packages/@repo/style/eslint/bun.js: -------------------------------------------------------------------------------- 1 | import globals from "globals" 2 | import tseslint from "typescript-eslint" 3 | 4 | import baseConfig from "./base.js" 5 | import reactPluginConfig from "./plugins/react.js" 6 | 7 | export default tseslint.config(baseConfig, reactPluginConfig, { 8 | name: "phase/bun", 9 | languageOptions: { 10 | globals: { 11 | ...globals.node, 12 | Bun: false, 13 | }, 14 | }, 15 | rules: { 16 | "import-x/no-unresolved": ["error", { ignore: ["^bun(:\\w+)?$"] }], 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(utility)/echo.ts: -------------------------------------------------------------------------------- 1 | import { BotCommandBuilder } from "@phasejs/builders" 2 | 3 | export default new BotCommandBuilder() 4 | .setName("echo") 5 | .setDescription("Echoes the text you give it.") 6 | .addStringOption((option) => 7 | option 8 | .setName("text") 9 | .setDescription("The text to echo.") 10 | .setRequired(true), 11 | ) 12 | .setExecute(async (interaction) => { 13 | const text = interaction.options.getString("text", true) 14 | void interaction.reply(text) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/schemas/(modules)/welcome-messages.ts: -------------------------------------------------------------------------------- 1 | import { boolean, pgTable, varchar } from "drizzle-orm/pg-core" 2 | 3 | import { snowflake } from "~/postgres/custom" 4 | 5 | export const welcomeMessages = pgTable("welcome_messages", { 6 | guildId: snowflake().unique().primaryKey(), 7 | enabled: boolean().notNull(), 8 | channelId: snowflake().notNull(), 9 | content: varchar({ length: 2000 }).notNull(), 10 | mention: boolean().default(false).notNull(), 11 | card: boolean().default(false).notNull(), 12 | }) 13 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(voice)/music/_utils.ts: -------------------------------------------------------------------------------- 1 | export function createProgressBar( 2 | percentage: number, 3 | status: "paused" | "resumed", 4 | ) { 5 | const barLength = 10 6 | const mediaControlSymbol = status === "paused" ? " ❚❚ " : " ► " 7 | const position = Math.round(percentage * barLength) 8 | 9 | let progressBar = "▬".repeat(barLength) 10 | 11 | progressBar = 12 | progressBar.substring(0, position) + 13 | mediaControlSymbol + 14 | progressBar.substring(position + 1) 15 | 16 | return progressBar 17 | } 18 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/button-group/index.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@repo/utils/site" 2 | 3 | export interface ButtonGroupProps extends React.ComponentPropsWithRef<"div"> {} 4 | 5 | export function ButtonGroup({ className, ...props }: ButtonGroupProps) { 6 | return ( 7 |
*:first-child]:rounded-l [&>*:last-child]:rounded-r [&>*:not(:first-child)]:border-l-0", 10 | className, 11 | )} 12 | {...props} 13 | /> 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/ids.ts: -------------------------------------------------------------------------------- 1 | export enum ModuleId { 2 | AuditLogs = "2uA4lWg9gW", 3 | AutoMessages = "2Cwqwmk84t", 4 | AutoRoles = "56Fd2OVE6Q", 5 | BumpReminders = "7ImdmYll1t", 6 | Counters = "5QvEVG03KK", 7 | Forms = "1uWxuEkesd", 8 | JoinToCreates = "67NXpiGedU", 9 | Levels = "23OmPKCPG0", 10 | ReactionRoles = "3MOVeYkTa1", 11 | SelfRoles = "5pZqVNWucH", 12 | Tickets = "7i5YEGu2Fj", 13 | TwitchNotifications = "1q4EDddac2", 14 | Warnings = "3aqDA1m91r", 15 | WelcomeMessages = "44Xaf7KFo1", 16 | } 17 | -------------------------------------------------------------------------------- /apps/www/src/app/contact/bug-report/actions.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { createClient } from "@repo/trpc/client" 4 | 5 | import { env } from "~/lib/env" 6 | 7 | type TRPCClient = ReturnType 8 | type BugReportInput = Parameters[0] 9 | 10 | export async function createBugReport(input: BugReportInput) { 11 | const client = createClient({ 12 | url: env.TRPC_URL, 13 | auth: { token: env.TRPC_TOKEN }, 14 | }) 15 | 16 | return await client.createBugReport.mutate(input) 17 | } 18 | -------------------------------------------------------------------------------- /apps/dash/src/types/auth.ts: -------------------------------------------------------------------------------- 1 | import type { APIUser } from "discord-api-types/v10" 2 | import type { Profile, Session, User } from "next-auth" 3 | import type { JWT } from "next-auth/jwt" 4 | 5 | declare module "next-auth" { 6 | interface Profile extends APIUser {} 7 | 8 | interface Session { 9 | user: { id: string } 10 | } 11 | 12 | interface User { 13 | userId: string 14 | } 15 | } 16 | 17 | declare module "next-auth/jwt" { 18 | interface JWT { 19 | userId: string 20 | } 21 | } 22 | 23 | export type { Profile, Session, User, JWT } 24 | -------------------------------------------------------------------------------- /apps/dash/src/components/client-only.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useIsClient } from "@uidotdev/usehooks" 4 | 5 | /** 6 | * Hack to work around next.js hydration 7 | * 8 | * @see https://github.com/uidotdev/usehooks/issues/218 9 | */ 10 | 11 | interface ClientOnlyProps { 12 | children: React.ReactNode 13 | } 14 | 15 | export const ClientOnly: React.FC = ({ children }) => { 16 | const isClient = useIsClient() 17 | 18 | // render children if on client side, otherwise return null 19 | return isClient ? <>{children} : null 20 | } 21 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/genkey.ts: -------------------------------------------------------------------------------- 1 | function randomString(length: number) { 2 | const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 3 | let value = "" 4 | for (let i = 0; i < length - 1; i++) { 5 | value += chars.charAt(Math.floor(Math.random() * chars.length)) 6 | } 7 | return value 8 | } 9 | 10 | function main() { 11 | const length = process.argv[2] 12 | if (!length) throw new Error("No length provided") 13 | 14 | const value = randomString(+length) 15 | console.log(`Random key: ${value}`) 16 | } 17 | 18 | main() 19 | -------------------------------------------------------------------------------- /railway.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://railway.app/railway.schema.json", 3 | "build": { 4 | "builder": "RAILPACK", 5 | "watchPatterns": [ 6 | "/apps/bot", 7 | "/packages/**", 8 | "/scripts/**", 9 | "bun.lock", 10 | "bunfig.toml", 11 | "package.json", 12 | "railway.json", 13 | "turbo.json" 14 | ] 15 | }, 16 | "deploy": { 17 | "runtime": "V2", 18 | "numReplicas": 1, 19 | "sleepApplication": false, 20 | "restartPolicyMaxRetries": 10, 21 | "restartPolicyType": "ALWAYS" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/bot/src/lib/utils/guards/channels.ts: -------------------------------------------------------------------------------- 1 | import { PermissionFlagsBits } from "discord.js" 2 | 3 | import type { GuildBasedChannel, GuildTextBasedChannel } from "discord.js" 4 | 5 | export function isSendableChannel( 6 | channel: GuildBasedChannel, 7 | ): channel is GuildTextBasedChannel { 8 | const requiredPermissions = [ 9 | PermissionFlagsBits.ViewChannel, 10 | PermissionFlagsBits.SendMessages, 11 | ] 12 | 13 | return ( 14 | channel.isSendable() && 15 | channel.permissionsFor(channel.guild.members.me!).has(requiredPermissions) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /apps/docs/src/types/sidebar.ts: -------------------------------------------------------------------------------- 1 | export interface SidebarNavCategory { 2 | type: "category" 3 | label: string 4 | children: SidebarNavCategoryChild[] 5 | } 6 | 7 | export interface SidebarNavSubcategory { 8 | type: "subcategory" 9 | label: string 10 | href: string 11 | children: SidebarNavPage[] 12 | } 13 | 14 | export interface SidebarNavPage { 15 | type: "page" 16 | label: string 17 | href: string 18 | } 19 | 20 | export type SidebarNavCategoryChild = SidebarNavSubcategory | SidebarNavPage 21 | 22 | export type SidebarNavItems = SidebarNavCategory[] 23 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/commands/index.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface GuildCommand { 4 | disabled: boolean 5 | allow: (`user:${string}` | `role:${string}`)[] 6 | deny: (`user:${string}` | `role:${string}`)[] 7 | } 8 | 9 | export const commandSchema = new Schema( 10 | { 11 | disabled: { type: Schema.Types.Boolean, required: true }, 12 | allow: { type: [Schema.Types.String], required: true }, 13 | deny: { type: [Schema.Types.String], required: true }, 14 | }, 15 | { _id: false }, 16 | ) 17 | -------------------------------------------------------------------------------- /apps/docs/src/components/react/link.tsx: -------------------------------------------------------------------------------- 1 | import { BaseLink, baseLinkVariants } from "@repo/ui/base-link" 2 | 3 | import type { BaseLinkProps } from "@repo/ui/base-link" 4 | 5 | export const linkVariants = baseLinkVariants 6 | 7 | export interface LinkProps extends Omit {} 8 | 9 | export function Link({ href, ...props }: LinkProps) { 10 | return ( 11 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /apps/bot/src/lib/trpc.ts: -------------------------------------------------------------------------------- 1 | import { BotPlugin } from "@phasejs/core" 2 | 3 | import { createRequestHandler } from "@repo/trpc/server" 4 | 5 | import { db } from "~/lib/db" 6 | import { env } from "~/lib/env" 7 | 8 | export function trpcPlugin() { 9 | return new BotPlugin({ 10 | name: "TRPC", 11 | trigger: "ready", 12 | version: "0.0.0", 13 | onLoad(phase) { 14 | Bun.serve({ 15 | port: env.TRPC_PORT, 16 | fetch(req) { 17 | return createRequestHandler(req, { db, env, phase }) 18 | }, 19 | }) 20 | }, 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /apps/bot/src/types/custom-ids.ts: -------------------------------------------------------------------------------- 1 | export type CustomID = string 2 | 3 | export type CustomIDWithAction = `${TPrefix}.${string}` 4 | 5 | export type CustomIDWithActionAndInput = 6 | `${TPrefix}.${string}.${string}` 7 | 8 | export type ExtractCustomIDParts = 9 | T extends `${infer TPrefix}.${infer TRest}` 10 | ? [TPrefix, ...ExtractCustomIDParts] 11 | : [T] 12 | 13 | export type ExtractCustomIDAction> = 14 | T extends `${string}.${infer TAction}` ? TAction : never 15 | -------------------------------------------------------------------------------- /packages/@phasejs/stores/src/types/declarations/client.ts: -------------------------------------------------------------------------------- 1 | import type { BotStores } from "~/index" 2 | import type { BotStore } from "~/types/stores" 3 | 4 | import type {} from "discord.js" 5 | import type {} from "@phasejs/core" 6 | 7 | declare module "discord.js" { 8 | interface Client { 9 | /** @deprecated Access from `phase.stores` instead */ 10 | stores: BotStores 11 | } 12 | } 13 | 14 | declare module "@phasejs/core" { 15 | interface BotClient { 16 | stores: BotStores 17 | } 18 | 19 | interface BotClientEvents { 20 | storeInit: BotStore 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/index.ts: -------------------------------------------------------------------------------- 1 | import { drizzle } from "drizzle-orm/node-postgres" 2 | 3 | import * as schemas from "~/postgres/schemas" 4 | 5 | interface DatabaseConfig { 6 | uri: string 7 | } 8 | 9 | export class Database { 10 | public readonly uri 11 | public readonly drizzle 12 | 13 | constructor(config: DatabaseConfig) { 14 | this.uri = config.uri 15 | this.drizzle = this.init() 16 | } 17 | 18 | private init() { 19 | return drizzle({ 20 | connection: this.uri, 21 | schema: schemas, 22 | casing: "snake_case", 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/@repo/style/eslint/nextjs.js: -------------------------------------------------------------------------------- 1 | import { flatConfig as nextPluginConfigs } from "@next/eslint-plugin-next" 2 | import tseslint from "typescript-eslint" 3 | 4 | import baseConfig from "./base.js" 5 | import reactPluginConfig from "./plugins/react.js" 6 | 7 | export default tseslint.config( 8 | baseConfig, 9 | reactPluginConfig, 10 | nextPluginConfigs.recommended, 11 | nextPluginConfigs.coreWebVitals, 12 | { 13 | name: "phase/nextjs", 14 | rules: { 15 | "@next/next/no-img-element": "off", 16 | "@next/next/no-duplicate-head": "off", 17 | }, 18 | }, 19 | ) 20 | -------------------------------------------------------------------------------- /packages/@phasejs/stores/src/structures/BaseBotKVStore.ts: -------------------------------------------------------------------------------- 1 | import { Collection } from "discord.js" 2 | 3 | import type { Base, BotClient } from "@phasejs/core" 4 | 5 | export abstract class BaseBotKVStore 6 | extends Collection 7 | implements Base 8 | { 9 | protected _init = false 10 | 11 | public readonly phase: BotClient 12 | 13 | constructor(phase: BotClient) { 14 | super() 15 | this.phase = phase 16 | } 17 | 18 | public async init() { 19 | if (this._init) return this 20 | 21 | this._init = true 22 | return this 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(utility)/id/user.ts: -------------------------------------------------------------------------------- 1 | import { BotSubcommandBuilder } from "@phasejs/builders" 2 | 3 | export default new BotSubcommandBuilder() 4 | .setName("user") 5 | .setDescription("Gets the ID of a user.") 6 | .addUserOption((option) => 7 | option 8 | .setName("user") 9 | .setDescription("The user to get the ID of.") 10 | .setRequired(true), 11 | ) 12 | .setExecute((interaction) => { 13 | const id = interaction.options.getUser("user", true).id 14 | 15 | void interaction.reply({ 16 | content: id, 17 | ephemeral: true, 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /apps/dash/.env.template: -------------------------------------------------------------------------------- 1 | # Security Warning! Do not include any secrets in this file! 2 | # 3 | # This is a template file for `.env.local` and is intended to be shared with 4 | # other developers. 5 | # 6 | # Please place any secrets in `.env.local` instead. 7 | 8 | # make a discord bot: 9 | # https://discord.com/developers/applications 10 | DISCORD_TOKEN="" 11 | DISCORD_SECRET="" 12 | DISCORD_ID="" 13 | 14 | # generate with openssl: 15 | # $ openssl rand -base64 32 16 | AUTH_COOKIE_SECRET="" 17 | AUTH_OTP_SECRET="" 18 | 19 | # generate with openssl: 20 | # $ openssl rand -base64 32 21 | TRPC_TOKEN="" 22 | -------------------------------------------------------------------------------- /apps/dash/src/components/modules/beta-alert.tsx: -------------------------------------------------------------------------------- 1 | import { Alert, AlertDescription, AlertTitle } from "@repo/ui/alert" 2 | import { Icon } from "@repo/ui/icon" 3 | import { TriangleAlertIcon } from "@repo/ui/lucide-icon" 4 | 5 | export function BetaAlert() { 6 | return ( 7 | 8 | } /> 9 | Beta Module 10 | 11 | This module is still in beta and may not be fully functional yet. Use at 12 | your own risk. 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /apps/dash/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import type { MiddlewareConfig } from "next/server" 2 | 3 | export { auth as middleware } from "~/lib/auth" 4 | 5 | export const config: MiddlewareConfig = { 6 | matcher: [ 7 | /* 8 | * match all request paths except for the ones starting with: 9 | * - _next/static (static files) 10 | * - _next/image (image optimization files) 11 | * - favicon.ico, sitemap.xml, robots.txt (metadata files) 12 | * - auth/error (auth error page) 13 | */ 14 | "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|auth/error).*)", 15 | ], 16 | } 17 | -------------------------------------------------------------------------------- /apps/bot/src/structures/builders/ModalActionRowBuilder.ts: -------------------------------------------------------------------------------- 1 | import { ActionRowBuilder, TextInputBuilder } from "discord.js" 2 | 3 | import type { BuilderOrBuilderFunction } from "~/types/builders" 4 | import type { ModalActionRowComponentBuilder } from "discord.js" 5 | 6 | export class ModalActionRowBuilder extends ActionRowBuilder { 7 | public addTextInput(builder: BuilderOrBuilderFunction) { 8 | return super.addComponents( 9 | typeof builder === "function" ? builder(new TextInputBuilder()) : builder, 10 | ) as ModalActionRowBuilder 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/schemas/giveaways.ts: -------------------------------------------------------------------------------- 1 | import { integer, pgTable, timestamp, uuid, varchar } from "drizzle-orm/pg-core" 2 | 3 | import { snowflake } from "~/postgres/custom" 4 | 5 | export const giveaways = pgTable("giveaways", { 6 | id: uuid().unique().primaryKey(), 7 | prize: varchar({ length: 512 }).notNull(), 8 | hostId: snowflake().notNull(), 9 | guildId: snowflake().notNull(), 10 | channelId: snowflake().notNull(), 11 | messageId: snowflake().notNull(), 12 | maxWinners: integer().notNull(), 13 | createdAt: timestamp().notNull().defaultNow(), 14 | endsAt: timestamp().notNull(), 15 | }) 16 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/schemas/audit-logs.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | 3 | import { moduleSchema, moduleSchemaExport } from "./_utils" 4 | 5 | const baseSchema = moduleSchema({ 6 | channels: z.object({ 7 | server: z.string().snowflake().optional(), 8 | messages: z.string().snowflake().optional(), 9 | voice: z.string().snowflake().optional(), 10 | invites: z.string().snowflake().optional(), 11 | members: z.string().snowflake().optional(), 12 | punishments: z.string().snowflake().optional(), 13 | }), 14 | }) 15 | 16 | export const auditLogs = moduleSchemaExport(baseSchema) 17 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/apps/www.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import { nextBaseOptions } from "~/lib/constants" 5 | 6 | import { base } from "~/presets/base" 7 | import { trpcClient } from "~/presets/trpc" 8 | 9 | export function www() { 10 | return createEnv({ 11 | ...nextBaseOptions, 12 | extends: [base(nextBaseOptions), trpcClient(nextBaseOptions)], 13 | server: { 14 | DASH_URL: z.string().url(), 15 | DOCS_URL: z.string().url(), 16 | }, 17 | client: { 18 | NEXT_PUBLIC_BASE_URL: z.string().url(), 19 | }, 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /apps/bot/src/assets/fonts.ts: -------------------------------------------------------------------------------- 1 | import geistBold from "~/assets/fonts/geist-bold.otf" with { type: "file" } 2 | import geistMedium from "~/assets/fonts/geist-medium.otf" with { type: "file" } 3 | import geistRegular from "~/assets/fonts/geist-regular.otf" with { type: "file" } 4 | import geistSemibold from "~/assets/fonts/geist-semibold.otf" with { type: "file" } 5 | import { loadAsset } from "~/assets/load" 6 | 7 | export const geistBoldFile = loadAsset(geistBold) 8 | export const geistSemiboldFile = loadAsset(geistSemibold) 9 | export const geistMediumFile = loadAsset(geistMedium) 10 | export const geistRegularFile = loadAsset(geistRegular) 11 | -------------------------------------------------------------------------------- /apps/bot/src/structures/stores/InviteStore.ts: -------------------------------------------------------------------------------- 1 | import { BaseBotKVStore } from "@phasejs/stores" 2 | 3 | import type { Collection, Snowflake } from "discord.js" 4 | 5 | export interface TrackedInvite { 6 | guildId: Snowflake 7 | inviterId: Snowflake | null 8 | code: string 9 | uses: number 10 | maxUses: number 11 | maxAge: number 12 | deleted: boolean 13 | createdTimestamp: number 14 | deletedTimestamp: number | null 15 | } 16 | 17 | export type TrackedInviteCollection = Collection 18 | 19 | export class InviteStore extends BaseBotKVStore< 20 | Snowflake, 21 | TrackedInviteCollection 22 | > {} 23 | -------------------------------------------------------------------------------- /apps/dash/src/app/auth/signin/actions.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { z } from "@repo/zod" 4 | 5 | import { signIn } from "~/lib/auth" 6 | 7 | export async function signInWithDiscord() { 8 | await signIn("discord", { redirectTo: "/dashboard/guilds" }) 9 | } 10 | 11 | export async function signInWithOTP(unsafeData: string) { 12 | const dataParseResult = z 13 | .string() 14 | .regex(/^[a-zA-Z0-9]{6}$/) 15 | .safeParse(unsafeData) 16 | 17 | if (dataParseResult.success) { 18 | await signIn("otp", { code: dataParseResult.data, redirect: false }) 19 | } else { 20 | throw new Error("Invalid data") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "bun", 6 | "request": "launch", 7 | "name": "Debug Bun", 8 | "program": "${file}", 9 | "args": [], 10 | "cwd": "${workspaceFolder}", 11 | "env": {}, 12 | "strictEnv": false, 13 | "watchMode": false, 14 | "stopOnEntry": false, 15 | "noDebug": false, 16 | "runtime": "bun", 17 | "runtimeArgs": [] 18 | }, 19 | { 20 | "type": "bun", 21 | "request": "attach", 22 | "name": "Attach to Bun", 23 | "url": "ws://localhost:6499/" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/schemas/counters.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | 3 | import { moduleSchema, moduleSchemaExport } from "./_utils" 4 | 5 | const baseSchema = moduleSchema({ 6 | counters: z 7 | .object({ 8 | // name: z.string().nonempty("Name is required"), 9 | channel: z.string().snowflake("Channel is required"), 10 | content: z 11 | .string() 12 | .nonempty("Content is required") 13 | .max(100, "Content cannot be longer than 100 characters"), 14 | }) 15 | .array() 16 | .max(10), 17 | }) 18 | 19 | export const counters = moduleSchemaExport(baseSchema) 20 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(utility)/id/role.ts: -------------------------------------------------------------------------------- 1 | import { BotSubcommandBuilder } from "@phasejs/builders" 2 | 3 | export default new BotSubcommandBuilder() 4 | .setName("role") 5 | .setDescription("Gets the ID of a role.") 6 | .addRoleOption((option) => 7 | option 8 | .setName("role") 9 | .setDescription("The role to get the ID of.") 10 | .setRequired(true), 11 | ) 12 | .setMetadata({ dmPermission: false }) 13 | .setExecute((interaction) => { 14 | const id = interaction.options.getRole("role", true).id 15 | 16 | void interaction.reply({ 17 | content: id, 18 | ephemeral: true, 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/src/types/emojis.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Base64Resolvable, 3 | BufferResolvable, 4 | ImageExtension, 5 | } from "discord.js" 6 | 7 | export type BotEmojiId = string 8 | export type BotEmojiName = string 9 | export type BotEmojiType = Exclude 10 | export type BotEmojiData = BufferResolvable | Base64Resolvable 11 | 12 | export interface BotEmoji { 13 | id?: BotEmojiId 14 | name: BotEmojiName 15 | type: BotEmojiType 16 | data: BotEmojiData 17 | } 18 | 19 | export type BotEmojiWithId = Required 20 | 21 | export type BotEmojiString = `<${"a" | ""}:${BotEmojiName}:${BotEmojiId}>` 22 | -------------------------------------------------------------------------------- /packages/@phasejs/loaders/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { isAbsolute, join } from "node:path" 2 | 3 | import type { AppConfig } from "~/types/app" 4 | 5 | export function resolveRootDir(config: AppConfig) { 6 | return join(process.cwd(), config.rootDir ?? "src") 7 | } 8 | 9 | export function regexFilter(pattern: string) { 10 | return (path: string) => new RegExp(pattern).test(path) 11 | } 12 | 13 | export async function loadFile(filePath: string, config: AppConfig) { 14 | const basePath = resolveRootDir(config) 15 | const fullPath = isAbsolute(filePath) ? filePath : join(basePath, filePath) 16 | return (await import(fullPath)) as Record 17 | } 18 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(utility)/id/channel.ts: -------------------------------------------------------------------------------- 1 | import { BotSubcommandBuilder } from "@phasejs/builders" 2 | 3 | export default new BotSubcommandBuilder() 4 | .setName("channel") 5 | .setDescription("Gets the ID of a channel.") 6 | .addChannelOption((option) => 7 | option 8 | .setName("channel") 9 | .setDescription("The channel to get the ID of.") 10 | .setRequired(true), 11 | ) 12 | .setMetadata({ dmPermission: false }) 13 | .setExecute((interaction) => { 14 | const id = interaction.options.getChannel("channel", true).id 15 | 16 | void interaction.reply({ 17 | content: id, 18 | ephemeral: true, 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/@plugin/blacklist/src/structures/Entry.ts: -------------------------------------------------------------------------------- 1 | export enum EntryType { 2 | Guild = "guild", 3 | User = "user", 4 | } 5 | 6 | export interface EntryCreateOptions { 7 | id: string 8 | type: EntryType 9 | reason?: string 10 | } 11 | 12 | export interface EntryEditOptions { 13 | reason?: string 14 | } 15 | 16 | export class Entry { 17 | public readonly id: EntryCreateOptions["id"] 18 | public readonly type: EntryCreateOptions["type"] 19 | public reason?: EntryCreateOptions["reason"] 20 | 21 | constructor(options: EntryCreateOptions) { 22 | this.id = options.id 23 | this.type = options.type 24 | this.reason = options.reason 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/@repo/config/src/site/index.cts: -------------------------------------------------------------------------------- 1 | // this just exists for next.config.ts which doesnt support import.meta.env 2 | 3 | import { createSiteConfig } from "./config" 4 | 5 | const port = process.env.PORT ?? "3000" 6 | 7 | const baseURL = 8 | process.env.NEXT_PUBLIC_BASE_URL ?? 9 | (process.env.SKIP_ENV_VALIDATION ? "http://localhost:3000" : undefined) 10 | 11 | if (!baseURL) { 12 | throw new Error("Missing '{PUBLIC_PREFIX}_BASE_URL' environment variable") 13 | } 14 | 15 | const basePath = process.env.NEXT_PUBLIC_BASE_PATH 16 | 17 | const siteConfig = createSiteConfig({ port, baseURL, basePath }) 18 | 19 | export default siteConfig 20 | export { siteConfig } 21 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/schemas/twitch-notifications.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | 3 | import { moduleSchema, moduleSchemaExport } from "./_utils" 4 | 5 | const baseSchema = moduleSchema({ 6 | streamers: z 7 | .object({ 8 | username: z 9 | .string() 10 | .min(4, "Username must be at least 4 characters") 11 | .max(25, "Username must be less than 25 characters"), 12 | channel: z.string().snowflake("You must select a channel"), 13 | mention: z.string().mention().optional(), 14 | }) 15 | .array() 16 | .max(5), 17 | }) 18 | 19 | export const twitchNotifications = moduleSchemaExport(baseSchema) 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Please describe the changes this PR makes and why it should be merged:** 2 | 3 | **Status and versioning classification:** 4 | 5 | 13 | -------------------------------------------------------------------------------- /apps/docs/src/pages/bot/[...id].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection, render } from "astro:content" 3 | 4 | import Layout from "~/components/astro/Layout.astro" 5 | 6 | import type { AnyEntryMap } from "astro:content" 7 | 8 | export async function getStaticPaths() { 9 | const entries = await getCollection("bot") 10 | return entries.map((entry) => ({ 11 | params: { id: entry.id }, 12 | props: { entry }, 13 | })) 14 | } 15 | 16 | interface Props { 17 | entry: AnyEntryMap["bot"][string] 18 | } 19 | 20 | const { entry } = Astro.props 21 | const { Content } = await render(entry) 22 | --- 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/@repo/env/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const baseOptions = (runtimeEnv: NodeJS.ProcessEnv) => ({ 2 | emptyStringAsUndefined: true, 3 | runtimeEnv, 4 | skipValidation: !!runtimeEnv.SKIP_ENV_VALIDATION, 5 | server: {}, 6 | }) 7 | 8 | export type BaseOptions = ReturnType 9 | 10 | export const botBaseOptions = { 11 | ...baseOptions(process.env), 12 | } 13 | 14 | export const nextBaseOptions = { 15 | ...baseOptions(process.env), 16 | clientPrefix: "NEXT_PUBLIC", 17 | client: {}, 18 | } as const 19 | 20 | export const astroBaseOptions = { 21 | ...baseOptions(process.env), 22 | clientPrefix: "PUBLIC", 23 | client: {}, 24 | } as const 25 | -------------------------------------------------------------------------------- /apps/dash/src/app/auth/layout.tsx: -------------------------------------------------------------------------------- 1 | import { OrbitingDots } from "@repo/ui/orbiting-dots" 2 | 3 | import type { LayoutProps } from "~/types/props" 4 | 5 | export default function Layout({ children }: LayoutProps) { 6 | return ( 7 |
8 |
9 | 10 |
11 |
12 | {children} 13 |
14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /apps/dash/src/components/link.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import NextLink from "next/link" 4 | 5 | import { BaseLink, baseLinkVariants } from "@repo/ui/base-link" 6 | 7 | import type { BaseLinkProps } from "@repo/ui/base-link" 8 | 9 | export const linkVariants = baseLinkVariants 10 | 11 | export interface LinkProps extends Omit {} 12 | 13 | export function Link({ href, ...props }: LinkProps) { 14 | const Anchor = 15 | props.disabled || (!props.mfe && props.external) 16 | ? "a" 17 | : (NextLink as BaseLinkProps["anchor"]) 18 | 19 | return ( 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /apps/docs/src/components/astro/ModuleVariables.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { ModuleDefinitions } from "@repo/utils/modules" 3 | 4 | import type { ModuleId } from "@repo/utils/modules" 5 | 6 | interface Props { 7 | moduleId: ModuleId 8 | } 9 | 10 | const { moduleId } = Astro.props 11 | 12 | const definition = ModuleDefinitions[moduleId] 13 | const variables = "variables" in definition ? definition.variables : undefined 14 | --- 15 | 16 |
    17 | { 18 | variables?.variables.map((variable) => { 19 | return ( 20 |
  • 21 | {`{${variable.name}}`} - {variable.description} 22 |
  • 23 | ) 24 | }) ?? null 25 | } 26 |
27 | -------------------------------------------------------------------------------- /apps/www/src/components/link.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import NextLink from "next/link" 4 | 5 | import { BaseLink, baseLinkVariants } from "@repo/ui/base-link" 6 | 7 | import type { BaseLinkProps } from "@repo/ui/base-link" 8 | 9 | export const linkVariants = baseLinkVariants 10 | 11 | export interface LinkProps extends Omit {} 12 | 13 | export function Link({ href, ...props }: LinkProps) { 14 | const Anchor = 15 | props.disabled || (!props.mfe && props.external) 16 | ? "a" 17 | : (NextLink as BaseLinkProps["anchor"]) 18 | 19 | return ( 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/trpc.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import type { BaseOptions } from "~/lib/constants" 5 | 6 | export function trpcClient(baseOptions: BaseOptions) { 7 | return createEnv({ 8 | ...baseOptions, 9 | server: { 10 | TRPC_URL: z.string().url(), 11 | TRPC_TOKEN: z.string().base64().max(44), 12 | }, 13 | }) 14 | } 15 | 16 | export function trpcServer(baseOptions: BaseOptions) { 17 | return createEnv({ 18 | ...baseOptions, 19 | server: { 20 | TRPC_PORT: z.string().transform(Number), 21 | TRPC_TOKEN: z.string().base64().max(44), 22 | }, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /apps/docs/src/pages/packages/[...id].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection, render } from "astro:content" 3 | 4 | import Layout from "~/components/astro/Layout.astro" 5 | 6 | import type { AnyEntryMap } from "astro:content" 7 | 8 | export async function getStaticPaths() { 9 | const entries = await getCollection("packages") 10 | return entries.map((entry) => ({ 11 | params: { id: entry.id }, 12 | props: { entry }, 13 | })) 14 | } 15 | 16 | interface Props { 17 | entry: AnyEntryMap["packages"][string] 18 | } 19 | 20 | const { entry } = Astro.props 21 | const { Content } = await render(entry) 22 | --- 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/src/lib/compare.ts: -------------------------------------------------------------------------------- 1 | import perceptualHash from "sharp-phash" 2 | import hashDistance from "sharp-phash/distance" 3 | 4 | import type { BotEmoji } from "~/types/emojis" 5 | 6 | export async function isSameEmoji( 7 | emoji1: BotEmoji, 8 | emoji2: BotEmoji, 9 | maxDistance = 5, 10 | ): Promise { 11 | const [phash1, phash2] = await Promise.all([ 12 | await perceptualHash(emoji1.data, { animated: emoji1.type === "gif" }), 13 | await perceptualHash(emoji2.data, { animated: emoji2.type === "gif" }), 14 | ]) 15 | 16 | const distance = hashDistance(phash1, phash2) 17 | const isSame = distance <= maxDistance 18 | 19 | return isSame 20 | } 21 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/structures/Variable.ts: -------------------------------------------------------------------------------- 1 | export class Variable< 2 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 3 | TArgs extends [any, ...any[]] = [any, ...any[]], 4 | > { 5 | readonly name: string 6 | readonly description: string 7 | readonly syntax: RegExp 8 | readonly parse: (value: string, ...args: TArgs) => string 9 | 10 | constructor(data: { 11 | name: string 12 | description: string 13 | syntax: RegExp 14 | parse: (value: string, ...args: TArgs) => string 15 | }) { 16 | this.name = data.name 17 | this.description = data.description 18 | this.syntax = data.syntax 19 | this.parse = data.parse 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/structures/VariableGroup.ts: -------------------------------------------------------------------------------- 1 | import type { Variable } from "~/modules/structures/Variable" 2 | 3 | export class VariableGroup< 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | TArgs extends [any, ...any[]] = [any, ...any[]], 6 | > { 7 | readonly variables: Variable[] 8 | readonly parse: (value: string, ...args: TArgs) => string 9 | 10 | constructor(variables: Variable[]) { 11 | this.variables = variables 12 | this.parse = (value: string, ...args: TArgs) => { 13 | return variables.reduce((acc, variable) => { 14 | return variable.parse(acc, ...args) 15 | }, value) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/otps.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | import { defineModel } from "~/mongo/utils" 4 | 5 | export interface Otp { 6 | otp: string 7 | userId: string 8 | guildId: string 9 | createdAt: Date 10 | } 11 | 12 | const otpSchema = new Schema({ 13 | otp: { type: Schema.Types.String, required: true, unique: true }, 14 | userId: { type: Schema.Types.String, required: true }, 15 | guildId: { type: Schema.Types.String, required: true }, 16 | createdAt: { 17 | type: Schema.Types.Date, 18 | expires: "1m", 19 | required: true, 20 | default: Date.now, 21 | }, 22 | }) 23 | 24 | export const otps = defineModel("Otps", otpSchema) 25 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/bump-reminders.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface BumpReminders { 4 | enabled: boolean 5 | time: number 6 | initialMessage: string 7 | reminderMessage: string 8 | mention?: string 9 | } 10 | 11 | export const bumpRemindersSchema = new Schema( 12 | { 13 | enabled: { type: Schema.Types.Boolean, required: true }, 14 | time: { type: Schema.Types.Number, required: true }, 15 | initialMessage: { type: Schema.Types.String, required: true }, 16 | reminderMessage: { type: Schema.Types.String, required: true }, 17 | mention: { type: Schema.Types.String }, 18 | }, 19 | { _id: false }, 20 | ) 21 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/tags.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | import { defineModel } from "~/mongo/utils" 4 | 5 | export interface Tag { 6 | guild: string 7 | tags: { 8 | name: string 9 | value: string 10 | }[] 11 | } 12 | 13 | const tagSchema = new Schema({ 14 | guild: { type: Schema.Types.String, required: true, index: true }, 15 | tags: { 16 | type: [ 17 | new Schema({ 18 | name: { type: Schema.Types.String, required: true }, 19 | value: { type: Schema.Types.String, required: true }, 20 | }), 21 | ], 22 | required: true, 23 | }, 24 | }) 25 | 26 | export const tags = defineModel("Tags", tagSchema) 27 | -------------------------------------------------------------------------------- /packages/@plugin/voice/src/index.ts: -------------------------------------------------------------------------------- 1 | import { BotPlugin } from "@phasejs/core" 2 | 3 | import { pluginVersion } from "~/lib/utils" 4 | 5 | import { VoiceManager } from "~/structures/VoiceManager" 6 | 7 | import type {} from "discord.js" 8 | 9 | declare module "discord.js" { 10 | interface Client { 11 | voices: VoiceManager 12 | } 13 | } 14 | 15 | export function voicePlugin() { 16 | return new BotPlugin({ 17 | name: "VoiceManager", 18 | version: pluginVersion, 19 | trigger: "init", 20 | onLoad: (phase) => { 21 | phase.client.voices = new VoiceManager(phase.client) 22 | }, 23 | }) 24 | } 25 | 26 | export * from "~/structures/Voice" 27 | export * from "~/structures/VoiceManager" 28 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/levels.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | import { defineModel } from "~/mongo/utils" 4 | 5 | export interface Level { 6 | guild: string 7 | user: string 8 | level: number 9 | xp: number 10 | } 11 | 12 | const levelsSchema = new Schema({ 13 | guild: { type: Schema.Types.String, required: true }, 14 | user: { type: Schema.Types.String, required: true }, 15 | level: { type: Schema.Types.Number, required: true }, 16 | xp: { type: Schema.Types.Number, required: true }, 17 | }) 18 | 19 | levelsSchema.index({ guild: 1, level: -1, xp: -1 }) 20 | levelsSchema.index({ guild: 1, user: 1 }) 21 | 22 | export const levels = defineModel("Levels", levelsSchema) 23 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/apps/dash.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import { nextBaseOptions } from "~/lib/constants" 5 | 6 | import { authjs } from "~/presets/authjs" 7 | import { base } from "~/presets/base" 8 | import { discord } from "~/presets/discord" 9 | import { trpcClient } from "~/presets/trpc" 10 | 11 | export function dash() { 12 | return createEnv({ 13 | ...nextBaseOptions, 14 | extends: [ 15 | base(nextBaseOptions), 16 | trpcClient(nextBaseOptions), 17 | discord(nextBaseOptions), 18 | authjs(nextBaseOptions), 19 | ], 20 | client: { 21 | NEXT_PUBLIC_BASE_URL: z.string().url(), 22 | }, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /apps/dash/src/components/form/field/wrapper.tsx: -------------------------------------------------------------------------------- 1 | import { cn, cva } from "~/lib/utils" 2 | 3 | import type { VariantProps } from "~/lib/utils" 4 | 5 | const wrapperVariants = cva("", { 6 | variants: { 7 | type: { 8 | default: "space-y-6", 9 | array: "space-y-4", 10 | }, 11 | }, 12 | defaultVariants: { 13 | type: "default", 14 | }, 15 | }) 16 | 17 | export interface FormFieldWrapperProps 18 | extends VariantProps, 19 | React.ComponentPropsWithRef<"div"> {} 20 | 21 | export function FormFieldWrapper({ 22 | className, 23 | type, 24 | ...props 25 | }: FormFieldWrapperProps) { 26 | return
27 | } 28 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/label/index.tsx: -------------------------------------------------------------------------------- 1 | import * as LabelPrimitive from "@radix-ui/react-label" 2 | import { cn, cva } from "@repo/utils/site" 3 | 4 | import type { VariantProps } from "@repo/utils/site" 5 | 6 | export const labelVariants = cva( 7 | "text-sm leading-none font-semibold peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 8 | ) 9 | 10 | export interface LabelProps 11 | extends React.ComponentPropsWithRef, 12 | VariantProps {} 13 | 14 | export function Label({ className, ...props }: LabelProps) { 15 | return ( 16 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /apps/docs/src/pages/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "~/components/astro/Layout.astro" 3 | title: "Introduction" 4 | description: "Welcome to the Phase documentation!" 5 | --- 6 | 7 | import FAQ from "~/components/astro/FAQ.astro" 8 | 9 | Phase is a free to use, open source Discord bot built by 10 | [mikaeladev](https://phasebot.xyz/redirect/developer). It aims to be the 11 | all-in-one solution for as many servers as possible, with a large variety of 12 | different modules and commands. 13 | 14 | Whether you're seeking to enhance a small server shared with friends or 15 | overseeing a bustling community, Phase is your solution for effectively 16 | managing, moderating, and improving your Discord servers. 17 | 18 | ## FAQ 19 | 20 | 21 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/schemas/welcome-messages.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | 3 | import { moduleSchema, moduleSchemaExport } from "./_utils" 4 | 5 | const baseSchema = moduleSchema({ 6 | channel: z.string().snowflake("Channel is required"), 7 | message: z 8 | .string() 9 | .nonempty("Message is required") 10 | .max(2000, "Message must be less than 2000 characters"), 11 | mention: z.boolean(), 12 | card: z.object({ 13 | enabled: z.boolean(), 14 | background: z 15 | .string() 16 | .url() 17 | .max(256, "Background URL cannot be longer than 256 characters") 18 | .optional(), 19 | }), 20 | }) 21 | 22 | export const welcomeMessages = moduleSchemaExport(baseSchema) 23 | -------------------------------------------------------------------------------- /packages/@repo/config/src/site/index.mts: -------------------------------------------------------------------------------- 1 | import { createSiteConfig } from "./config" 2 | 3 | const port = process.env.PORT ?? "3000" 4 | 5 | const baseURL = 6 | import.meta.env?.PUBLIC_BASE_URL ?? 7 | process.env.PUBLIC_BASE_URL ?? 8 | process.env.NEXT_PUBLIC_BASE_URL ?? 9 | (process.env.SKIP_ENV_VALIDATION ? "http://localhost:3000" : undefined) 10 | 11 | if (!baseURL) { 12 | throw new Error("Missing '{PUBLIC_PREFIX}_BASE_URL' environment variable") 13 | } 14 | 15 | const basePath = 16 | import.meta.env?.PUBLIC_BASE_PATH ?? 17 | process.env.PUBLIC_BASE_PATH ?? 18 | process.env.NEXT_PUBLIC_BASE_PATH 19 | 20 | const siteConfig = createSiteConfig({ port, baseURL, basePath }) 21 | 22 | export default siteConfig 23 | export { siteConfig } 24 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/loading/index.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@repo/utils/site" 2 | 3 | import { Moon } from "~/components/moon" 4 | import { OrbitingDots } from "~/components/orbiting-dots" 5 | 6 | export interface LoadingProps extends React.ComponentPropsWithRef<"div"> {} 7 | 8 | export function Loading({ className, ...props }: LoadingProps) { 9 | return ( 10 |
17 | 18 | 19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /packages/@plugin/music/src/index.ts: -------------------------------------------------------------------------------- 1 | import { BotPlugin } from "@phasejs/core" 2 | 3 | import { pluginVersion } from "~/lib/utils" 4 | 5 | import { Music } from "~/structures/Music" 6 | 7 | import type {} from "discord.js" 8 | 9 | declare module "discord.js" { 10 | interface Client { 11 | music: Music 12 | } 13 | } 14 | 15 | export function musicPlugin() { 16 | return new BotPlugin({ 17 | name: "Music", 18 | version: pluginVersion, 19 | trigger: "init", 20 | onLoad: (phase) => { 21 | phase.client.music = new Music(phase.client) 22 | }, 23 | }) 24 | } 25 | 26 | export * from "~/structures/Music" 27 | export * from "~/structures/Queue" 28 | export * from "~/structures/QueueManager" 29 | export * from "~/structures/Song" 30 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/auto-roles.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface AutoRoles { 4 | enabled: boolean 5 | roles: { 6 | id: string 7 | target: "everyone" | "members" | "bots" 8 | }[] 9 | } 10 | 11 | export const autoRolesSchema = new Schema( 12 | { 13 | enabled: { type: Schema.Types.Boolean, required: true }, 14 | roles: { 15 | type: [ 16 | new Schema( 17 | { 18 | id: { type: Schema.Types.String, required: true }, 19 | target: { type: Schema.Types.String, required: true }, 20 | }, 21 | { _id: false }, 22 | ), 23 | ], 24 | required: true, 25 | }, 26 | }, 27 | { _id: false }, 28 | ) 29 | -------------------------------------------------------------------------------- /apps/bot/src/lib/utils/formatting/strings.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Truncates a string to a specified length and appends an ellipsis (`...`) at 3 | * the end, replacing the last three characters if truncation is necessary. 4 | * 5 | * @param str The string to be truncated. 6 | * @param maxLength The maximum length of the string. 7 | */ 8 | export function truncateString(str: string, maxLength: number) { 9 | if (str.length <= maxLength) return str 10 | return str.slice(0, maxLength - 3) + "..." 11 | } 12 | 13 | /** 14 | * Wraps a string in a specified wrapper. 15 | * 16 | * @param text The string to be wrapped. 17 | * @param wrapper The wrapper to use. 18 | */ 19 | export function wrapText(text: string, wrapper: string) { 20 | return `${wrapper}${text}${wrapper}` 21 | } 22 | -------------------------------------------------------------------------------- /apps/bot/src/lib/stores.ts: -------------------------------------------------------------------------------- 1 | import { ConfigStore } from "~/structures/stores/ConfigStore" 2 | import { GuildStore } from "~/structures/stores/GuildStore" 3 | import { InviteStore } from "~/structures/stores/InviteStore" 4 | import { StreamerStore } from "~/structures/stores/StreamerStore" 5 | 6 | import type { StoresPluginOptions } from "@phasejs/stores" 7 | 8 | declare module "@phasejs/stores" { 9 | interface BotStores { 10 | config: ConfigStore 11 | guilds: GuildStore 12 | invites: InviteStore 13 | streamers: StreamerStore 14 | } 15 | } 16 | 17 | export const storesConfig = { 18 | stores: { 19 | config: ConfigStore, 20 | guilds: GuildStore, 21 | invites: InviteStore, 22 | streamers: StreamerStore, 23 | }, 24 | } satisfies StoresPluginOptions 25 | -------------------------------------------------------------------------------- /apps/dash/src/hooks/use-dashboard-context.ts: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | 5 | import type { DashboardData } from "~/types/dashboard" 6 | 7 | export const DashboardContext = React.createContext(null) 8 | 9 | export function useDashboardContext(): DashboardData 10 | export function useDashboardContext( 11 | noThrow?: boolean, 12 | ): DashboardData | undefined 13 | 14 | export function useDashboardContext(noThrow?: boolean) { 15 | const dashboardContext = React.use(DashboardContext) 16 | 17 | if (!dashboardContext) { 18 | if (noThrow) return undefined 19 | 20 | throw new Error( 21 | "useDashboardContext has to be used within ", 22 | ) 23 | } 24 | 25 | return dashboardContext 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | 15 | env: 16 | TURBO_TEAM: ${{ secrets.TURBO_TEAM }} 17 | TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} 18 | SKIP_ENV_VALIDATION: true 19 | NEXT_PUBLIC_BASE_URL: https://phasebot.xyz 20 | 21 | steps: 22 | - name: Checkout code 23 | uses: actions/checkout@v4 24 | 25 | - name: Setup Bun 26 | uses: oven-sh/setup-bun@v2 27 | with: 28 | bun-version: latest 29 | 30 | - name: Install dependencies 31 | run: bun install 32 | 33 | - name: Lint code 34 | run: bun run turbo run lint 35 | -------------------------------------------------------------------------------- /apps/bot/src/app/crons/(internal)/status.ts: -------------------------------------------------------------------------------- 1 | import { BotCronBuilder } from "@phasejs/builders" 2 | import { ActivityType } from "discord.js" 3 | 4 | export default new BotCronBuilder() 5 | .setPattern("*/30 * * * * *") // every 30 seconds 6 | .setExecute(async (client, ctx) => { 7 | const statusType = ctx.phase.stores.config.status.type 8 | const statusText = ctx.phase.stores.config.status.text 9 | 10 | const botStatusType = client.user.presence.status 11 | const botStatusText = client.user.presence.activities[0]?.state 12 | 13 | if (botStatusType !== statusType) { 14 | client.user.setStatus(statusType) 15 | } 16 | 17 | if (botStatusText !== statusText) { 18 | client.user.setActivity(statusText, { type: ActivityType.Custom }) 19 | } 20 | }) 21 | -------------------------------------------------------------------------------- /packages/@repo/env/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@repo/env", 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "default": "./dist/index.js" 15 | } 16 | }, 17 | "dependencies": { 18 | "@repo/utils": "workspace:*", 19 | "@repo/zod": "workspace:*", 20 | "@t3-oss/env-core": "^0.11.1" 21 | }, 22 | "tsup": { 23 | "clean": true, 24 | "dts": true, 25 | "minify": true, 26 | "splitting": true, 27 | "sourcemap": true, 28 | "format": "esm", 29 | "entry": [ 30 | "./src/index.ts" 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/@phasejs/builders/src/lib/resolvers.ts: -------------------------------------------------------------------------------- 1 | import type { JSONEncodable } from "discord.js" 2 | 3 | export function resolveBuilder< 4 | Builder extends JSONEncodable, 5 | BuilderData extends Record, 6 | >( 7 | builder: Builder | BuilderData | ((builder: Builder) => Builder), 8 | Constructor: new (data?: BuilderData) => Builder, 9 | ): Builder { 10 | const isBuilder = >( 11 | builder: unknown, 12 | Constructor: new () => Builder, 13 | ): builder is Builder => { 14 | return builder instanceof Constructor 15 | } 16 | 17 | if (isBuilder(builder, Constructor)) return builder 18 | if (typeof builder === "function") return builder(new Constructor()) 19 | 20 | return new Constructor(builder) 21 | } 22 | -------------------------------------------------------------------------------- /packages/@phasejs/loaders/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@phasejs/loaders", 4 | "version": "0.4.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "default": "./dist/index.js" 15 | } 16 | }, 17 | "dependencies": { 18 | "@phasejs/builders": "workspace:*", 19 | "@phasejs/core": "workspace:*", 20 | "fast-glob": "^3.3.3" 21 | }, 22 | "tsup": { 23 | "clean": true, 24 | "dts": true, 25 | "minify": true, 26 | "splitting": true, 27 | "sourcemap": true, 28 | "format": "esm", 29 | "entry": [ 30 | "./src/index.ts" 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/dash/src/app/auth/signin/page.tsx: -------------------------------------------------------------------------------- 1 | import { Codeblock } from "@repo/ui/codeblock" 2 | 3 | import { SignInMethods } from "~/app/auth/signin/components" 4 | 5 | export default function Page() { 6 | return ( 7 | <> 8 |
9 |

10 | Welcome back! 11 |

12 |

13 | To access the dashboard, either run the{" "} 14 | 15 | /bot login 16 | {" "} 17 | command, or click the button below to login with your Discord account. 18 |

19 |
20 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /packages/@phasejs/builders/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@phasejs/builders", 4 | "version": "0.4.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "default": "./dist/index.js" 15 | } 16 | }, 17 | "dependencies": { 18 | "@phasejs/core": "workspace:*" 19 | }, 20 | "peerDependencies": { 21 | "discord.js": "catalog:bot" 22 | }, 23 | "tsup": { 24 | "clean": true, 25 | "dts": true, 26 | "minify": true, 27 | "splitting": true, 28 | "sourcemap": true, 29 | "format": "esm", 30 | "entry": [ 31 | "./src/index.ts" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/join-to-creates.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | import { defineModel } from "~/mongo/utils" 4 | 5 | export interface JoinToCreate { 6 | guild: string 7 | channel: string 8 | owner: string 9 | createdAt: Date 10 | } 11 | 12 | const joinToCreateSchema = new Schema({ 13 | guild: { type: Schema.Types.String, required: true }, 14 | channel: { type: Schema.Types.String, required: true }, 15 | owner: { type: Schema.Types.String, required: true }, 16 | createdAt: { 17 | type: Schema.Types.Date, 18 | required: true, 19 | expires: "1d", 20 | default: Date.now, 21 | }, 22 | }) 23 | 24 | joinToCreateSchema.index({ guild: 1, channel: 1 }) 25 | 26 | export const joinToCreates = defineModel("JoinToCreates", joinToCreateSchema) 27 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/modules/constants/schemas/warnings.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | 3 | import { moduleSchema, moduleSchemaExport } from "./_utils" 4 | 5 | const baseSchema = moduleSchema({ 6 | warnings: z 7 | .object({ role: z.string().snowflake("Role is required") }) 8 | .array() 9 | .max(10), 10 | }) 11 | 12 | const transformedSchema = z.object({ 13 | ...baseSchema.shape, 14 | warnings: z.string().array().max(10), 15 | }) 16 | 17 | function transform({ 18 | warnings, 19 | ...data 20 | }: z.TypeOf): z.TypeOf { 21 | return { 22 | ...data, 23 | warnings: warnings.map(({ role }) => role), 24 | } 25 | } 26 | 27 | export const warnings = moduleSchemaExport( 28 | baseSchema, 29 | transformedSchema, 30 | transform, 31 | ) 32 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/schemas/guilds.ts: -------------------------------------------------------------------------------- 1 | import { relations } from "drizzle-orm" 2 | import { pgTable } from "drizzle-orm/pg-core" 3 | 4 | import { snowflake } from "~/postgres/custom" 5 | import { modules } from "~/postgres/schemas/modules" 6 | import { themes } from "~/postgres/schemas/themes" 7 | 8 | // tables // 9 | 10 | export const guilds = pgTable("guilds", { 11 | id: snowflake().unique().primaryKey(), 12 | adminIds: snowflake().array().notNull(), 13 | themeId: snowflake(), 14 | }) 15 | 16 | // relations // 17 | 18 | export const guildsRelations = relations(guilds, ({ one }) => ({ 19 | modules: one(modules, { 20 | fields: [guilds.id], 21 | references: [modules.guildId], 22 | }), 23 | theme: one(themes, { 24 | fields: [guilds.themeId], 25 | references: [themes.id], 26 | }), 27 | })) 28 | -------------------------------------------------------------------------------- /packages/@phasejs/stores/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@phasejs/stores", 4 | "version": "0.1.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "default": "./dist/index.js" 15 | } 16 | }, 17 | "dependencies": { 18 | "@phasejs/core": "workspace:*", 19 | "ts-mixer": "^6.0.4" 20 | }, 21 | "peerDependencies": { 22 | "discord.js": "catalog:bot" 23 | }, 24 | "tsup": { 25 | "clean": true, 26 | "dts": true, 27 | "minify": true, 28 | "splitting": true, 29 | "sourcemap": true, 30 | "format": "esm", 31 | "entry": [ 32 | "./src/index.ts" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/@plugin/theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@plugin/theme", 4 | "version": "0.1.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.js" 16 | } 17 | }, 18 | "dependencies": { 19 | "@phasejs/core": "workspace:*" 20 | }, 21 | "peerDependencies": { 22 | "discord.js": "catalog:bot" 23 | }, 24 | "tsup": { 25 | "clean": true, 26 | "dts": true, 27 | "minify": true, 28 | "splitting": true, 29 | "sourcemap": true, 30 | "format": "esm", 31 | "entry": [ 32 | "./src/index.ts" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /apps/dash/src/components/guilds/guild-card-search.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useQueryState } from "nuqs" 4 | 5 | import { Icon } from "@repo/ui/icon" 6 | import { Input } from "@repo/ui/input" 7 | import { SearchIcon } from "@repo/ui/lucide-icon" 8 | 9 | export function GuildCardSearch() { 10 | const [guildName, setGuildName] = useQueryState("name", { 11 | defaultValue: "", 12 | throttleMs: 500, 13 | }) 14 | 15 | return ( 16 |
17 | } 19 | className="text-muted-foreground absolute top-1/2 left-3 -translate-y-1/2" 20 | /> 21 | 27 |
28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/counters.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface Counters { 4 | enabled: boolean 5 | counters: { 6 | // name: string 7 | channel: string 8 | content: string 9 | }[] 10 | } 11 | 12 | export const countersSchema = new Schema( 13 | { 14 | enabled: { type: Schema.Types.Boolean, required: true }, 15 | counters: { 16 | type: [ 17 | new Schema( 18 | { 19 | // name: { type: Schema.Types.String, required: true }, 20 | channel: { type: Schema.Types.String, required: true }, 21 | content: { type: Schema.Types.String, required: true }, 22 | }, 23 | { _id: false }, 24 | ), 25 | ], 26 | required: true, 27 | }, 28 | }, 29 | { _id: false }, 30 | ) 31 | -------------------------------------------------------------------------------- /packages/@repo/trpc/src/server/index.ts: -------------------------------------------------------------------------------- 1 | import { fetchRequestHandler } from "@trpc/server/adapters/fetch" 2 | 3 | import { createContext } from "~/server/context" 4 | import { appRouter } from "~/server/router" 5 | 6 | import type { Database } from "@repo/db" 7 | import type { bot } from "@repo/env" 8 | import type { BotClientWithStores } from "~/types/bot" 9 | 10 | export interface RequestHandlerConfig { 11 | db: Database 12 | env: ReturnType 13 | phase: BotClientWithStores 14 | } 15 | 16 | export function createRequestHandler( 17 | request: Request, 18 | config: RequestHandlerConfig, 19 | ) { 20 | return fetchRequestHandler({ 21 | endpoint: "/", 22 | req: request, 23 | router: appRouter, 24 | createContext: (opts) => 25 | createContext({ 26 | ...opts, 27 | ...config, 28 | }), 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/emoji-picker/emoji-types.ts: -------------------------------------------------------------------------------- 1 | export interface EmojiMartData { 2 | categories: EmojiMartCategory[] 3 | emojis: Record 4 | aliases: Record 5 | sheet: EmojiMartSheet 6 | } 7 | 8 | export interface EmojiMartCategory { 9 | id: string 10 | emojis: string[] 11 | } 12 | 13 | export interface EmojiMartEmoji { 14 | id: string 15 | name: string 16 | keywords: string[] 17 | skins: EmojiMartSkin[] 18 | version: number 19 | emoticons?: string[] 20 | } 21 | 22 | export interface EmojiMartSkin { 23 | unified: string 24 | native: string 25 | x?: number 26 | y?: number 27 | } 28 | 29 | export interface EmojiMartSheet { 30 | cols: number 31 | rows: number 32 | } 33 | 34 | export interface Emoji { 35 | id: string 36 | name: string 37 | skin: EmojiMartSkin 38 | } 39 | -------------------------------------------------------------------------------- /apps/dash/src/hooks/use-element-size.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | export function useElementSize(ref: React.RefObject) { 4 | const [size, setSize] = React.useState<[number, number]>([0, 0]) 5 | 6 | React.useEffect(() => { 7 | const element = ref.current 8 | if (!element) return 9 | 10 | // Function to update width state 11 | const updateSize = () => { 12 | setSize([element.offsetWidth, element.offsetHeight]) 13 | } 14 | 15 | // Create a resize observer 16 | const resizeObserver = new ResizeObserver(() => updateSize()) 17 | resizeObserver.observe(element) 18 | 19 | // Initial width update 20 | updateSize() 21 | 22 | // Clean up observer on component unmount 23 | return () => { 24 | resizeObserver.disconnect() 25 | } 26 | }, [ref]) 27 | 28 | return size 29 | } 30 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/twitch-notifications.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface TwitchNotifications { 4 | enabled: boolean 5 | streamers: { 6 | id: string 7 | channel: string 8 | mention?: string 9 | }[] 10 | } 11 | 12 | export const twitchNotificationsSchema = new Schema( 13 | { 14 | enabled: { type: Schema.Types.Boolean, required: true }, 15 | streamers: { 16 | type: [ 17 | new Schema( 18 | { 19 | id: { type: Schema.Types.String, required: true }, 20 | channel: { type: Schema.Types.String, required: true }, 21 | mention: { type: Schema.Types.String }, 22 | }, 23 | { _id: false }, 24 | ), 25 | ], 26 | required: true, 27 | }, 28 | }, 29 | { _id: false }, 30 | ) 31 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/events.ts: -------------------------------------------------------------------------------- 1 | import type { BotEventContext } from "~/structures/BotEvent" 2 | import type { DjsClient } from "~/types/client" 3 | import type { Prettify } from "~/types/utils" 4 | import type { ClientEvents } from "discord.js" 5 | 6 | export type BotEvents = ClientEvents 7 | export type BotEventName = Prettify 8 | 9 | export type BotEventExecuteArgsMap = { 10 | [TName in BotEventName]: BotEvents[TName] extends [] 11 | ? [client: DjsClient, context: BotEventContext] 12 | : [client: DjsClient, ...args: BotEvents[TName], context: BotEventContext] 13 | } 14 | 15 | export type BotEventExecuteMap = { 16 | [TName in BotEventName]: (...args: BotEventExecuteArgsMap[TName]) => unknown 17 | } 18 | 19 | export type BotEventExecute = ( 20 | ...args: BotEventExecuteArgsMap[TName] 21 | ) => unknown 22 | -------------------------------------------------------------------------------- /packages/@phasejs/logs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@phasejs/logs", 4 | "version": "0.2.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.js" 16 | } 17 | }, 18 | "dependencies": { 19 | "@phasejs/core": "workspace:*", 20 | "chalk": "catalog:utils" 21 | }, 22 | "peerDependencies": { 23 | "discord.js": "catalog:bot" 24 | }, 25 | "tsup": { 26 | "clean": true, 27 | "dts": true, 28 | "minify": true, 29 | "splitting": true, 30 | "sourcemap": true, 31 | "format": "esm", 32 | "entry": [ 33 | "./src/index.ts" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "~/managers/CommandManager" 2 | export * from "~/managers/CronManager" 3 | export * from "~/managers/EventManager" 4 | 5 | export * from "~/structures/abstracts/Base" 6 | export * from "~/structures/BotClient" 7 | export * from "~/structures/BotCommand" 8 | export * from "~/structures/BotCron" 9 | export * from "~/structures/BotEvent" 10 | export * from "~/structures/BotPlugin" 11 | 12 | export * from "~/lib/constants" 13 | export * from "~/lib/factories" 14 | 15 | export type * from "~/types/client" 16 | export type * from "~/types/commands" 17 | export type * from "~/types/context" 18 | export type * from "~/types/crons" 19 | export type * from "~/types/events" 20 | export type * from "~/types/middleware" 21 | export type * from "~/types/plugins" 22 | export type * from "~/types/prestart" 23 | export type * from "~/types/utils" 24 | -------------------------------------------------------------------------------- /apps/bot/src/app/events/(modules)/invite-logs/invite-create.ts: -------------------------------------------------------------------------------- 1 | import { BotEventBuilder } from "@phasejs/builders" 2 | 3 | import { ModuleId } from "@repo/utils/modules" 4 | 5 | import { hasRequiredGuildPermissions, mapInvite } from "./_utils" 6 | 7 | export default new BotEventBuilder() 8 | .setName("inviteCreate") 9 | .setExecute(async (client, invite, ctx) => { 10 | const guild = client.guilds.resolve(invite) 11 | if (!guild || !hasRequiredGuildPermissions(guild)) return 12 | 13 | const guildDoc = ctx.phase.stores.guilds.get(guild.id) 14 | 15 | const moduleConfig = guildDoc?.modules?.[ModuleId.AuditLogs] 16 | if (!moduleConfig?.enabled || !moduleConfig.channels.invites) return 17 | 18 | const inviteStore = ctx.phase.stores.invites.get(guild.id) 19 | if (!inviteStore) return 20 | 21 | inviteStore.set(invite.code, mapInvite(invite)) 22 | }) 23 | -------------------------------------------------------------------------------- /apps/docs/astro.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "astro/config" 2 | import { loadEnv } from "vite" 3 | 4 | import mdx from "@astrojs/mdx" 5 | import react from "@astrojs/react" 6 | import sitemap from "@astrojs/sitemap" 7 | import { siteConfig } from "@repo/config/site" 8 | import tailwindcss from "@tailwindcss/vite" 9 | 10 | // loads environment variables 11 | process.env = loadEnv(process.env.NODE_ENV!, process.cwd(), "") 12 | 13 | // validates environment variables 14 | await import("./src/lib/env") 15 | 16 | export default defineConfig({ 17 | base: siteConfig.basePath, 18 | cacheDir: ".astro/cache/astro", 19 | integrations: [mdx(), react(), sitemap()], 20 | prefetch: true, 21 | site: siteConfig.url, 22 | server: { 23 | port: siteConfig.port, 24 | }, 25 | vite: { 26 | cacheDir: ".astro/cache/vite", 27 | plugins: [tailwindcss()], 28 | }, 29 | }) 30 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/structures/BotPlugin.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | BotPluginLoadFunction, 3 | BotPluginLoadTrigger, 4 | BotPluginVersion, 5 | } from "~/types/plugins" 6 | 7 | export interface BotPluginParams { 8 | name: string 9 | version: BotPluginVersion 10 | trigger: TTrigger 11 | onLoad: BotPluginLoadFunction 12 | } 13 | 14 | export class BotPlugin< 15 | TTrigger extends BotPluginLoadTrigger = BotPluginLoadTrigger, 16 | > implements Readonly> 17 | { 18 | public readonly name 19 | public readonly version 20 | public readonly trigger 21 | public readonly onLoad 22 | 23 | constructor(params: BotPluginParams) { 24 | this.name = params.name 25 | this.version = params.version 26 | this.trigger = params.trigger 27 | this.onLoad = params.onLoad 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/bot/src/lib/blacklist.ts: -------------------------------------------------------------------------------- 1 | import { Entry, EntryType } from "@plugin/blacklist" 2 | 3 | import type { BlacklistPluginOptions } from "@plugin/blacklist" 4 | import type { Snowflake } from "discord.js" 5 | 6 | export const blacklistConfig = { 7 | populate(phase) { 8 | const config = phase.stores.config.blacklist 9 | 10 | return [ 11 | ...config.guilds.map((entry): [Snowflake, Entry] => [ 12 | entry.id, 13 | new Entry({ 14 | id: entry.id, 15 | type: EntryType.Guild, 16 | reason: entry.reason, 17 | }), 18 | ]), 19 | ...config.users.map((entry): [Snowflake, Entry] => [ 20 | entry.id, 21 | new Entry({ 22 | id: entry.id, 23 | type: EntryType.User, 24 | reason: entry.reason, 25 | }), 26 | ]), 27 | ] 28 | }, 29 | } satisfies BlacklistPluginOptions 30 | -------------------------------------------------------------------------------- /apps/dash/src/components/modules/module-tags.tsx: -------------------------------------------------------------------------------- 1 | import { badgeVariants } from "@repo/ui/badge" 2 | 3 | import { cn } from "~/lib/utils" 4 | 5 | import type { ModuleTag } from "@repo/utils/modules" 6 | 7 | export interface ModuleTagsProps { 8 | tags: ModuleTag[] 9 | onSelect?: (tag: ModuleTag) => void 10 | } 11 | 12 | export function ModuleTags({ tags, onSelect }: ModuleTagsProps) { 13 | return ( 14 |
15 | {tags.map((tag) => ( 16 | 27 | ))} 28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /packages/@plugin/blacklist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@plugin/blacklist", 4 | "version": "0.1.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.js" 16 | } 17 | }, 18 | "dependencies": { 19 | "@phasejs/builders": "workspace:*", 20 | "@phasejs/core": "workspace:*" 21 | }, 22 | "peerDependencies": { 23 | "discord.js": "catalog:bot" 24 | }, 25 | "tsup": { 26 | "clean": true, 27 | "dts": true, 28 | "minify": true, 29 | "splitting": true, 30 | "sourcemap": true, 31 | "format": "esm", 32 | "entry": [ 33 | "./src/index.ts" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/site.ts: -------------------------------------------------------------------------------- 1 | import { siteConfig } from "@repo/config/site" 2 | import { clsx } from "clsx" 3 | import { extendTailwindMerge } from "tailwind-merge" 4 | 5 | import type { ClassValue } from "clsx" 6 | 7 | const twMerge = extendTailwindMerge({ 8 | extend: { 9 | classGroups: { 10 | shadow: [ 11 | { 12 | "shadow-glow": ["sm", "md", "lg"], 13 | }, 14 | ], 15 | }, 16 | }, 17 | }) 18 | 19 | export function cn(...inputs: ClassValue[]) { 20 | return twMerge(clsx(inputs)) 21 | } 22 | 23 | export function absoluteURL(path: string, withBasePath = true) { 24 | const basePath = withBasePath ? siteConfig.basePath : "" 25 | const fullPath = basePath + (path.startsWith("/") ? path : `/${path}`) 26 | return new URL(fullPath, siteConfig.baseUrl).href 27 | } 28 | 29 | export { cva, type VariantProps } from "class-variance-authority" 30 | -------------------------------------------------------------------------------- /apps/docs/src/content/bot/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Overview" 3 | description: "Learn about the Discord bot and its features." 4 | metadata: 5 | sidebarPriority: 0 6 | --- 7 | 8 | import FAQ from "~/components/astro/FAQ.astro" 9 | 10 | Phase is a free to use, open source Discord bot built by 11 | [mikaeladev][Developer]. It aims to be the all-in-one solution for as many 12 | servers as possible, with a large variety of different modules and commands. 13 | 14 | Whether you're seeking to enhance a small server shared with friends or 15 | overseeing a bustling community, Phase is your solution for effectively 16 | managing, moderating, and customising your Discord servers. 17 | 18 | These docs will be your guide to understanding how Phase works, how to configure 19 | it, and how to host it yourself. 20 | 21 | ## FAQ 22 | 23 | 24 | 25 | [Developer]: https://phasebot.xyz/redirect/developer 26 | -------------------------------------------------------------------------------- /packages/@plugin/theme/src/index.ts: -------------------------------------------------------------------------------- 1 | import { BotPlugin } from "@phasejs/core" 2 | 3 | import { pluginVersion } from "~/lib/utils" 4 | 5 | import { ThemeManager } from "~/structures/ThemeManager" 6 | 7 | import type { Theme } from "~/structures/Theme" 8 | 9 | import type {} from "discord.js" 10 | 11 | declare module "discord.js" { 12 | interface Client { 13 | themes: ThemeManager 14 | } 15 | } 16 | 17 | export function voicePlugin(themes: Theme[]) { 18 | return new BotPlugin({ 19 | name: "ThemeManager", 20 | version: pluginVersion, 21 | trigger: "init", 22 | onLoad: (phase) => { 23 | phase.client.themes = new ThemeManager(phase.client, { 24 | themes, 25 | guilds: [], 26 | }) 27 | }, 28 | }) 29 | } 30 | 31 | export * from "~/structures/Theme" 32 | export * from "~/structures/ThemeManager" 33 | 34 | export type * from "~/types/theme" 35 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@plugin/emoji-sync", 4 | "version": "0.1.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.js" 16 | } 17 | }, 18 | "dependencies": { 19 | "@phasejs/core": "workspace:*", 20 | "sharp": "catalog:web", 21 | "sharp-phash": "^2.2.0" 22 | }, 23 | "peerDependencies": { 24 | "discord.js": "catalog:bot" 25 | }, 26 | "tsup": { 27 | "clean": true, 28 | "dts": true, 29 | "minify": true, 30 | "splitting": true, 31 | "sourcemap": true, 32 | "format": "esm", 33 | "entry": [ 34 | "./src/index.ts" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/welcome-messages.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface WelcomeMessages { 4 | enabled: boolean 5 | channel: string 6 | message: string 7 | mention: boolean 8 | card: { 9 | enabled: boolean 10 | background?: string 11 | } 12 | } 13 | 14 | export const welcomeMessagesSchema = new Schema( 15 | { 16 | enabled: { type: Schema.Types.Boolean, required: true }, 17 | channel: { type: Schema.Types.String, required: true }, 18 | message: { type: Schema.Types.String, required: true }, 19 | mention: { type: Schema.Types.Boolean, required: true }, 20 | card: new Schema( 21 | { 22 | enabled: { type: Schema.Types.Boolean, required: true }, 23 | background: { type: Schema.Types.String }, 24 | }, 25 | { _id: false }, 26 | ), 27 | }, 28 | { _id: false }, 29 | ) 30 | -------------------------------------------------------------------------------- /packages/@repo/env/src/presets/apps/bot.ts: -------------------------------------------------------------------------------- 1 | import { z } from "@repo/zod" 2 | import { createEnv } from "@t3-oss/env-core" 3 | 4 | import { botBaseOptions } from "~/lib/constants" 5 | 6 | import { authjs } from "~/presets/authjs" 7 | import { base } from "~/presets/base" 8 | import { database } from "~/presets/database" 9 | import { discord } from "~/presets/discord" 10 | import { trpcServer } from "~/presets/trpc" 11 | import { twitch } from "~/presets/twitch" 12 | 13 | export function bot() { 14 | return createEnv({ 15 | ...botBaseOptions, 16 | extends: [ 17 | base(botBaseOptions), 18 | trpcServer(botBaseOptions), 19 | database(botBaseOptions), 20 | discord(botBaseOptions), 21 | twitch(botBaseOptions), 22 | authjs(botBaseOptions), 23 | ], 24 | server: { 25 | BASE_URL: z.string().url(), 26 | WEBHOOK_ALERT: z.string(), 27 | }, 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /apps/dash/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | export { cn, cva, absoluteURL, type VariantProps } from "@repo/utils/site" 2 | 3 | export const getOrdinal = (number: number): string => { 4 | if (number >= 11 && number <= 13) return number + "th" 5 | return ( 6 | number + 7 | (["th", "st", "nd", "rd"][number % 10] ?? ["th", "st", "nd", "rd"][0]!) 8 | ) 9 | } 10 | 11 | /** Prevents default browser behaviour. */ 12 | export function preventDefault(event: { preventDefault: () => void }) { 13 | event.preventDefault() 14 | } 15 | 16 | export type Keys = (keyof T)[] 17 | 18 | export function keys(obj: T) { 19 | return Object.keys(obj) as Keys 20 | } 21 | 22 | export type Entries = Required<{ 23 | [K in keyof T]: [K, Required[K]] 24 | }>[keyof T][] 25 | 26 | export function entries(obj: T) { 27 | return Object.entries(obj) as Entries 28 | } 29 | -------------------------------------------------------------------------------- /packages/@phasejs/loaders/src/types/app.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | BotCommandBuilder, 3 | BotCronBuilder, 4 | BotEventBuilder, 5 | BotSubcommandBuilder, 6 | } from "@phasejs/builders" 7 | import type { 8 | BotClientInitParams, 9 | BotMiddleware, 10 | BotPrestart, 11 | } from "@phasejs/core" 12 | 13 | export type AppConfig = { 14 | rootDir?: string 15 | } 16 | 17 | export type AppPaths = { 18 | prestart: string | undefined 19 | middlewares: string | undefined 20 | commands: string[] 21 | crons: string[] 22 | events: string[] 23 | } 24 | 25 | export type AppExports = { 26 | prestart: Record 27 | middlewares: Record 28 | commands: Record 29 | crons: Record 30 | events: Record 31 | } 32 | 33 | export type AppInitParams = BotClientInitParams 34 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/reaction-roles.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface ReactionRoles { 4 | enabled: boolean 5 | channel: string 6 | message: string 7 | reactions: { 8 | emoji: string 9 | role: string 10 | }[] 11 | } 12 | 13 | export const reactionRolesSchema = new Schema( 14 | { 15 | enabled: { type: Schema.Types.Boolean, required: true }, 16 | channel: { type: Schema.Types.String, required: true }, 17 | message: { type: Schema.Types.String, required: true }, 18 | reactions: { 19 | type: [ 20 | new Schema( 21 | { 22 | emoji: { type: Schema.Types.String, required: true }, 23 | role: { type: Schema.Types.String, required: true }, 24 | }, 25 | { _id: false }, 26 | ), 27 | ], 28 | required: true, 29 | }, 30 | }, 31 | { _id: false }, 32 | ) 33 | -------------------------------------------------------------------------------- /packages/@phasejs/image/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@phasejs/image", 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.js" 16 | } 17 | }, 18 | "dependencies": { 19 | "@resvg/resvg-js": "^2.6.2", 20 | "satori": "^0.12.2" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "catalog:web" 24 | }, 25 | "peerDependencies": { 26 | "discord.js": "catalog:bot" 27 | }, 28 | "tsup": { 29 | "clean": true, 30 | "dts": true, 31 | "minify": true, 32 | "splitting": true, 33 | "sourcemap": true, 34 | "format": "esm", 35 | "entry": [ 36 | "./src/index.ts" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/@repo/db/src/postgres/schemas/modules.ts: -------------------------------------------------------------------------------- 1 | import { relations } from "drizzle-orm" 2 | import { pgTable } from "drizzle-orm/pg-core" 3 | 4 | import { snowflake } from "~/postgres/custom" 5 | import { 6 | joinToCreates, 7 | levels, 8 | welcomeMessages, 9 | } from "~/postgres/schemas/(modules)" 10 | 11 | export const modules = pgTable("modules", { 12 | guildId: snowflake().unique().primaryKey(), 13 | }) 14 | 15 | export const modulesRelations = relations(modules, ({ one }) => ({ 16 | joinToCreates: one(joinToCreates, { 17 | fields: [modules.guildId], 18 | references: [joinToCreates.guildId], 19 | }), 20 | levels: one(levels, { 21 | fields: [modules.guildId], 22 | references: [levels.guildId], 23 | }), 24 | welcomeMessages: one(welcomeMessages, { 25 | fields: [modules.guildId], 26 | references: [welcomeMessages.guildId], 27 | }), 28 | })) 29 | 30 | export * from "~/postgres/schemas/(modules)" 31 | -------------------------------------------------------------------------------- /apps/bot/src/app/events/(modules)/self-roles/_utils.ts: -------------------------------------------------------------------------------- 1 | import type { GuildModules } from "@repo/db" 2 | import type { ModuleId } from "@repo/utils/modules" 3 | import type { GuildMember } from "discord.js" 4 | 5 | type SelfRolesMessage = GuildModules[ModuleId.SelfRoles]["messages"][number] 6 | 7 | export async function updateRoles( 8 | member: GuildMember, 9 | message: SelfRolesMessage, 10 | methodIndex: number, 11 | ) { 12 | const { roles } = message.methods[methodIndex]! 13 | 14 | const rolesToAdd = roles 15 | .filter((role) => role.action === "add") 16 | .map(({ id }) => id) 17 | 18 | const rolesToRemove = roles 19 | .filter((role) => role.action === "remove") 20 | .map(({ id }) => id) 21 | 22 | if (rolesToAdd.length) { 23 | await member.roles.add(rolesToAdd).catch(() => null) 24 | } 25 | 26 | if (rolesToRemove.length) { 27 | await member.roles.remove(rolesToRemove).catch(() => null) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/bot/src/context.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | BotClient, 3 | BotCommand, 4 | BotContextCreators, 5 | BotCron, 6 | BotEvent, 7 | } from "@phasejs/core" 8 | 9 | declare module "@phasejs/core" { 10 | interface BotCommandContext { 11 | phase: BotClient 12 | command: BotCommand 13 | } 14 | 15 | interface BotCronContext { 16 | phase: BotClient 17 | cron: BotCron 18 | } 19 | 20 | interface BotEventContext { 21 | phase: BotClient 22 | event: BotEvent 23 | } 24 | } 25 | 26 | export const contextCreators: BotContextCreators = { 27 | commands(params) { 28 | return { 29 | phase: params.phase, 30 | command: params.command, 31 | } 32 | }, 33 | crons(params) { 34 | return { 35 | phase: params.phase, 36 | cron: params.cron, 37 | } 38 | }, 39 | events(params) { 40 | return { 41 | phase: params.phase, 42 | event: params.event, 43 | } 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /packages/@phasejs/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@phasejs/core", 4 | "version": "0.21.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "default": "./dist/index.js" 15 | } 16 | }, 17 | "dependencies": { 18 | "croner": "^8.1.2", 19 | "deepmerge-ts": "^7.1.5", 20 | "emittery": "^1.1.0", 21 | "lodash": "^4.17.21" 22 | }, 23 | "devDependencies": { 24 | "@types/lodash": "^4.17.16" 25 | }, 26 | "peerDependencies": { 27 | "discord.js": "catalog:bot" 28 | }, 29 | "tsup": { 30 | "clean": true, 31 | "dts": true, 32 | "minify": true, 33 | "splitting": true, 34 | "sourcemap": true, 35 | "format": "esm", 36 | "entry": [ 37 | "./src/index.ts" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/context.ts: -------------------------------------------------------------------------------- 1 | import type { BotClient } from "~/structures/BotClient" 2 | import type { BotCommand, BotCommandContext } from "~/structures/BotCommand" 3 | import type { BotCron, BotCronContext } from "~/structures/BotCron" 4 | import type { BotEvent, BotEventContext } from "~/structures/BotEvent" 5 | import type { Awaitable } from "discord.js" 6 | 7 | export type BotCommandContextCreator = (params: { 8 | phase: BotClient 9 | command: BotCommand 10 | }) => Awaitable 11 | 12 | export type BotCronContextCreator = (params: { 13 | phase: BotClient 14 | cron: BotCron 15 | }) => Awaitable 16 | 17 | export type BotEventContextCreator = (params: { 18 | phase: BotClient 19 | event: BotEvent 20 | }) => Awaitable 21 | 22 | export type BotContextCreators = { 23 | commands: BotCommandContextCreator 24 | crons: BotCronContextCreator 25 | events: BotEventContextCreator 26 | } 27 | -------------------------------------------------------------------------------- /packages/@plugin/emoji-sync/src/lib/sync.ts: -------------------------------------------------------------------------------- 1 | import type { BotClient } from "@phasejs/core" 2 | import type { BotEmoji, BotEmojiWithId } from "~/types/emojis" 3 | import type { OutdatedBotEmojis } from "~/types/outdated" 4 | 5 | export async function syncEmojis( 6 | phase: BotClient, 7 | emojis: OutdatedBotEmojis, 8 | ) { 9 | const manager = phase.client.application.emojis 10 | 11 | const createEmoji = async (emoji: BotEmoji) => { 12 | await manager.create({ ...emoji, attachment: emoji.data }) 13 | } 14 | 15 | const deleteEmoji = async (emoji: BotEmojiWithId) => { 16 | await manager.delete(emoji.id) 17 | } 18 | 19 | const updateEmoji = async (emoji: BotEmojiWithId) => { 20 | await manager.delete(emoji.id) 21 | await createEmoji(emoji) 22 | } 23 | 24 | await Promise.all([ 25 | ...emojis.create.map(createEmoji), 26 | ...emojis.delete.map(deleteEmoji), 27 | ...emojis.update.map(updateEmoji), 28 | ]) 29 | } 30 | -------------------------------------------------------------------------------- /packages/@phasejs/core/src/types/commands.ts: -------------------------------------------------------------------------------- 1 | import type { BotCommand, BotCommandContext } from "~/structures/BotCommand" 2 | import type { 3 | RESTPostAPIChatInputApplicationCommandsJSONBody as BotCommandJSON, 4 | APIApplicationCommandSubcommandOption as BotSubcommandJSON, 5 | ChatInputCommandInteraction, 6 | } from "discord.js" 7 | 8 | type BotCommandBodyOmittedProperties = 9 | | "default_member_permissions" 10 | | "default_permission" 11 | | "nsfw" 12 | | "handler" 13 | | "required" 14 | 15 | export type BotCommandBody = T extends true 16 | ? Omit 17 | : Omit 18 | 19 | export type BotCommandNameResolvable = 20 | | string 21 | | BotCommand 22 | | ChatInputCommandInteraction 23 | 24 | export type BotCommandExecute = ( 25 | interaction: ChatInputCommandInteraction, 26 | context: BotCommandContext, 27 | ) => unknown 28 | -------------------------------------------------------------------------------- /packages/@repo/trpc/src/client/index.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCClient, httpBatchLink } from "@trpc/client" 2 | 3 | import type { AppRouter } from "~/server/router" 4 | 5 | export interface ClientConfig { 6 | url: string 7 | auth: { 8 | token: string 9 | adminId?: string 10 | guildId?: string 11 | } 12 | } 13 | 14 | export function createClient(config: ClientConfig) { 15 | const headers = new Headers() 16 | 17 | headers.set("Authorization", `Secret ${config.auth.token}`) 18 | 19 | if (config.auth.adminId) headers.set("X-Admin-ID", config.auth.adminId) 20 | if (config.auth.guildId) headers.set("X-Guild-ID", config.auth.guildId) 21 | 22 | return createTRPCClient({ 23 | links: [ 24 | httpBatchLink({ 25 | url: config.url, 26 | headers, 27 | }), 28 | ], 29 | }) 30 | } 31 | 32 | export { isTRPCClientError } from "@trpc/client" 33 | export type { TRPCClientError } from "@trpc/client" 34 | export type * from "~/types/trpc" 35 | -------------------------------------------------------------------------------- /apps/dash/src/components/richtext/utils.ts: -------------------------------------------------------------------------------- 1 | import { Element, Text } from "slate" 2 | 3 | import type { RichtextFlags } from "./index" 4 | import type { TextElement } from "~/types/slate" 5 | 6 | // default flags // 7 | 8 | export const defaultRichtextFlags = { 9 | input: { 10 | multiline: false, 11 | decorations: true, 12 | variables: true, 13 | }, 14 | textarea: { 15 | multiline: true, 16 | decorations: true, 17 | mentions: true, 18 | channels: true, 19 | variables: true, 20 | }, 21 | } satisfies Record 22 | 23 | // type guards // 24 | 25 | export function isText(descendant: unknown): descendant is Text { 26 | return Text.isText(descendant) 27 | } 28 | 29 | export function isElement(descendant: unknown): descendant is Element { 30 | return Element.isElement(descendant) 31 | } 32 | 33 | export function isRootElement(descendant: unknown): descendant is TextElement { 34 | return isElement(descendant) && descendant.type === "text" 35 | } 36 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/audit-logs.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface AuditLogs { 4 | enabled: boolean 5 | channels: { 6 | server?: string 7 | messages?: string 8 | voice?: string 9 | invites?: string 10 | members?: string 11 | punishments?: string 12 | } 13 | } 14 | 15 | export const auditLogsSchema = new Schema( 16 | { 17 | enabled: { type: Schema.Types.Boolean, required: true }, 18 | channels: { 19 | type: new Schema( 20 | { 21 | server: { type: Schema.Types.String }, 22 | messages: { type: Schema.Types.String }, 23 | voice: { type: Schema.Types.String }, 24 | invites: { type: Schema.Types.String }, 25 | members: { type: Schema.Types.String }, 26 | punishments: { type: Schema.Types.String }, 27 | }, 28 | { _id: false }, 29 | ), 30 | required: true, 31 | }, 32 | }, 33 | { _id: false }, 34 | ) 35 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(module)/tag/list.ts: -------------------------------------------------------------------------------- 1 | import { BotSubcommandBuilder } from "@phasejs/builders" 2 | 3 | import { db } from "~/lib/db" 4 | 5 | import { MessageBuilder } from "~/structures/builders/MessageBuilder" 6 | 7 | export default new BotSubcommandBuilder() 8 | .setName("list") 9 | .setDescription("Lists all the tags in the server.") 10 | .setExecute(async (interaction) => { 11 | await interaction.deferReply({ ephemeral: true }) 12 | 13 | const tagDoc = await db.tags.findOne({ 14 | guild: interaction.guildId, 15 | }) 16 | 17 | return void interaction.editReply( 18 | new MessageBuilder().setEmbeds((embed) => { 19 | return embed 20 | .setColor("Primary") 21 | .setTitle(`Tag List (${tagDoc?.tags.length ?? 0})`) 22 | .setDescription( 23 | tagDoc?.tags.length 24 | ? tagDoc.tags.map(({ name }) => name).join(", ") 25 | : "No tags found.", 26 | ) 27 | }), 28 | ) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/@repo/style/typescript/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "exclude": ["node_modules"], 4 | "compilerOptions": { 5 | // Enable latest features 6 | "lib": ["esnext"], 7 | "target": "esnext", 8 | "module": "esnext", 9 | "moduleDetection": "force", 10 | "jsx": "react-jsx", 11 | "allowJs": true, 12 | "resolveJsonModule": true, 13 | 14 | // Bundler mode 15 | "moduleResolution": "bundler", 16 | "allowImportingTsExtensions": true, 17 | "noEmit": true, 18 | 19 | // Paths 20 | "baseUrl": "${configDir}", 21 | "paths": { "~/*": ["./src/*"] }, 22 | 23 | // Best practices 24 | "strict": true, 25 | "checkJs": true, 26 | "skipLibCheck": true, 27 | "noFallthroughCasesInSwitch": true, 28 | "noUncheckedIndexedAccess": true, 29 | "isolatedModules": true, 30 | "esModuleInterop": true, 31 | 32 | // Some stricter flags 33 | "forceConsistentCasingInFileNames": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(fun)/games/tictactoe.ts: -------------------------------------------------------------------------------- 1 | import { BotSubcommandBuilder } from "@phasejs/builders" 2 | 3 | import { TicTacToe } from "~/structures/games/tictactoe" 4 | 5 | // this command only starts the game, it doesn't handle any game logic, 6 | // see app/events/games/tictactoe.ts for that 7 | 8 | export default new BotSubcommandBuilder() 9 | .setName("tictactoe") 10 | .setDescription("Starts a game of tic-tac-toe.") 11 | .addUserOption((option) => { 12 | return option 13 | .setName("opponent") 14 | .setDescription("The user you want to play against.") 15 | .setRequired(true) 16 | }) 17 | .setMetadata({ dmPermission: false }) 18 | .setExecute(async (interaction) => { 19 | const user1 = interaction.user 20 | const user2 = interaction.options.getUser("opponent", true) 21 | 22 | const tictactoe = new TicTacToe(interaction.client, [user1, user2]) 23 | const message = tictactoe.message.createMessage() 24 | 25 | void interaction.reply(message) 26 | }) 27 | -------------------------------------------------------------------------------- /apps/bot/src/app/events/(modules)/invite-logs/ready.ts: -------------------------------------------------------------------------------- 1 | import { BotEventBuilder } from "@phasejs/builders" 2 | import { BotEventListenerType } from "@phasejs/core" 3 | 4 | import { ModuleId } from "@repo/utils/modules" 5 | 6 | import { hasRequiredGuildPermissions, mapInvite } from "./_utils" 7 | 8 | export default new BotEventBuilder() 9 | .setName("ready") 10 | .setListenerType(BotEventListenerType.ONCE) 11 | .setExecute(async (client, _, ctx) => { 12 | const guildDocs = ctx.phase.stores.guilds.filter((guildDoc) => { 13 | const moduleConfig = guildDoc.modules?.[ModuleId.AuditLogs] 14 | return (moduleConfig?.enabled && moduleConfig.channels.invites) ?? false 15 | }) 16 | 17 | for (const guild of client.guilds.cache.values()) { 18 | if (!guildDocs.has(guild.id)) continue 19 | if (!hasRequiredGuildPermissions(guild)) continue 20 | const invites = await guild.invites.fetch() 21 | ctx.phase.stores.invites.set(guild.id, invites.mapValues(mapInvite)) 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /apps/bot/src/lib/clients/phase.ts: -------------------------------------------------------------------------------- 1 | import { BotClient } from "@phasejs/core" 2 | import { logsPlugin } from "@phasejs/logs" 3 | import { storesPlugin } from "@phasejs/stores" 4 | 5 | import { blacklistPlugin } from "@plugin/blacklist" 6 | import { musicPlugin } from "@plugin/music" 7 | import { voicePlugin } from "@plugin/voice" 8 | 9 | import { blacklistConfig } from "~/lib/blacklist" 10 | import { discordClient } from "~/lib/clients/discord" 11 | import { emojiSyncPlugin } from "~/lib/emojis" 12 | import { logsConfig } from "~/lib/logs" 13 | import { storesConfig } from "~/lib/stores" 14 | import { trpcPlugin } from "~/lib/trpc" 15 | 16 | import { contextCreators } from "~/context" 17 | 18 | export const phaseClient = new BotClient(discordClient, { 19 | contextCreators, 20 | plugins: [ 21 | logsPlugin(logsConfig), 22 | storesPlugin(storesConfig), 23 | blacklistPlugin(blacklistConfig), 24 | emojiSyncPlugin(), 25 | voicePlugin(), 26 | musicPlugin(), 27 | trpcPlugin(), 28 | ], 29 | }) 30 | -------------------------------------------------------------------------------- /apps/bot/src/app/events/(modules)/auto-roles/pendings.ts: -------------------------------------------------------------------------------- 1 | import { BotEventBuilder } from "@phasejs/builders" 2 | 3 | import { ModuleId } from "@repo/utils/modules" 4 | 5 | export default new BotEventBuilder() 6 | .setName("guildMemberUpdate") 7 | .setExecute(async (_, oldMember, newMember, ctx) => { 8 | if (!oldMember.pending || (oldMember.pending && newMember.pending)) return 9 | 10 | const guildDoc = ctx.phase.stores.guilds.get(newMember.guild.id) 11 | const autoRolesModule = guildDoc?.modules?.[ModuleId.AutoRoles] 12 | 13 | if (!autoRolesModule?.enabled) return 14 | 15 | for (const role of autoRolesModule.roles) { 16 | if (role.target === "bots" && !newMember.user.bot) continue 17 | if (role.target === "members" && newMember.user.bot) continue 18 | 19 | if ( 20 | newMember.guild.roles.cache.get(role.id) && 21 | !newMember.roles.cache.has(role.id) 22 | ) { 23 | await newMember.roles.add(role.id).catch(() => null) 24 | } 25 | } 26 | }) 27 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/auto-messages.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface AutoMessages { 4 | enabled: boolean 5 | messages: { 6 | name: string 7 | channel: string 8 | message: string 9 | interval: number 10 | mention?: string 11 | }[] 12 | } 13 | 14 | export const autoMessagesSchema = new Schema( 15 | { 16 | enabled: { type: Schema.Types.Boolean, required: true }, 17 | messages: { 18 | type: [ 19 | new Schema( 20 | { 21 | name: { type: Schema.Types.String, required: true }, 22 | channel: { type: Schema.Types.String, required: true }, 23 | message: { type: Schema.Types.String, required: true }, 24 | interval: { type: Number, required: true }, 25 | mention: { type: Schema.Types.String }, 26 | }, 27 | { _id: false }, 28 | ), 29 | ], 30 | required: true, 31 | }, 32 | }, 33 | { _id: false }, 34 | ) 35 | -------------------------------------------------------------------------------- /packages/@repo/style/eslint/plugins/react.js: -------------------------------------------------------------------------------- 1 | import reactPlugin from "eslint-plugin-react" 2 | import hooksPlugin from "eslint-plugin-react-hooks" 3 | import globals from "globals" 4 | import tseslint from "typescript-eslint" 5 | 6 | export default tseslint.config({ 7 | name: "phase/react", 8 | files: ["**/*.tsx", "**/*.jsx"], 9 | plugins: { 10 | react: reactPlugin, 11 | ["react-hooks"]: hooksPlugin, 12 | }, 13 | languageOptions: { 14 | parserOptions: { 15 | ...reactPlugin.configs.recommended.parserOptions, 16 | ...reactPlugin.configs["jsx-runtime"].parserOptions, 17 | }, 18 | globals: { 19 | ...globals.browser, 20 | }, 21 | }, 22 | rules: { 23 | ...reactPlugin.configs.recommended.rules, 24 | ...reactPlugin.configs["jsx-runtime"].rules, 25 | ...hooksPlugin.configs["recommended-latest"].rules, 26 | "react/no-unescaped-entities": "off", 27 | "react/prop-types": "off", 28 | }, 29 | settings: { 30 | react: { 31 | version: "detect", 32 | }, 33 | }, 34 | }) 35 | -------------------------------------------------------------------------------- /packages/@phasejs/image/src/structures/Image.ts: -------------------------------------------------------------------------------- 1 | import { AttachmentBuilder } from "discord.js" 2 | 3 | import type { AttachmentData } from "discord.js" 4 | 5 | export class Image { 6 | public readonly buffer: Buffer 7 | public readonly width: number 8 | public readonly height: number 9 | 10 | constructor(buffer: Buffer, width: number, height: number) { 11 | if (!this.isBuffer(buffer)) throw new TypeError("Buffer is not a Buffer.") 12 | if (width <= 0) throw new TypeError("Width must be a positive integer.") 13 | if (height <= 0) throw new TypeError("Height must be a positive integer.") 14 | 15 | this.buffer = buffer 16 | this.width = width 17 | this.height = height 18 | } 19 | 20 | public toBlob() { 21 | return new Blob([this.buffer], { type: "image/png" }) 22 | } 23 | 24 | public toAttachment(data?: AttachmentData) { 25 | return new AttachmentBuilder(this.buffer, data) 26 | } 27 | 28 | private isBuffer(thing: unknown): thing is Buffer { 29 | return thing instanceof Buffer 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/@repo/trpc/src/server/context.ts: -------------------------------------------------------------------------------- 1 | import type { FetchCreateContextFnOptions } from "@trpc/server/adapters/fetch" 2 | import type { RequestHandlerConfig } from "~/server" 3 | import type { GuildDoc } from "~/types/bot" 4 | import type { Guild as GuildAPI } from "discord.js" 5 | 6 | interface CreateContextParams 7 | extends FetchCreateContextFnOptions, 8 | RequestHandlerConfig {} 9 | 10 | export function createContext(params: CreateContextParams) { 11 | return { 12 | req: params.req, 13 | db: params.db, 14 | env: params.env, 15 | phase: params.phase, 16 | } 17 | } 18 | 19 | export type Context = ReturnType 20 | 21 | export type ContextWithAdminAuth = Context & { 22 | auth: { 23 | adminId: string 24 | } 25 | } 26 | 27 | export type ContextWithGuildAuth = Context & { 28 | auth: { 29 | adminId: string 30 | guildId: string 31 | guildDoc: GuildDoc 32 | guildAPI: GuildAPI 33 | } 34 | } 35 | 36 | export type AnyContext = Context | ContextWithAdminAuth | ContextWithGuildAuth 37 | -------------------------------------------------------------------------------- /packages/@plugin/theme/src/structures/Theme.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ThemeBackground, 3 | ThemeButtons, 4 | ThemeColours, 5 | ThemeFont, 6 | ThemeId, 7 | } from "~/types/theme" 8 | 9 | export class Theme { 10 | public readonly id: ThemeId 11 | public readonly background: ThemeBackground 12 | public readonly buttons: ThemeButtons 13 | public readonly colours: ThemeColours 14 | public readonly font: ThemeFont 15 | 16 | constructor(params: { 17 | id: ThemeId 18 | background: ThemeBackground 19 | buttons: ThemeButtons 20 | colours: ThemeColours 21 | font: ThemeFont 22 | }) { 23 | this.id = params.id 24 | this.background = params.background 25 | this.buttons = params.buttons 26 | this.colours = params.colours 27 | this.font = params.font 28 | } 29 | 30 | static isTheme(theme: unknown): theme is Theme { 31 | return theme instanceof Theme 32 | } 33 | 34 | static isThemeId(id: unknown): id is ThemeId { 35 | return typeof id === "string" && /^\w+-\w+-\w+-\w+-\w+$/.test(id) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /apps/dash/src/app/guilds/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { ClientOnly } from "~/components/client-only" 2 | import { DashboardProvider } from "~/components/context" 3 | 4 | import { getSession } from "~/lib/auth" 5 | import { createClient } from "~/lib/trpc" 6 | 7 | import type { DashboardData } from "~/types/dashboard" 8 | import type { LayoutProps } from "~/types/props" 9 | 10 | interface GuildLayoutProps extends LayoutProps { 11 | params: Promise> 12 | } 13 | 14 | export default async function GuildLayout({ 15 | params: paramsPromise, 16 | children, 17 | }: GuildLayoutProps) { 18 | const params = await paramsPromise 19 | const session = await getSession() 20 | 21 | const client = createClient({ adminId: session.user.id, guildId: params.id }) 22 | 23 | const dashboardData: DashboardData = { 24 | session, 25 | guild: client.guilds.getCurrent.query(), 26 | } 27 | 28 | return ( 29 | 30 | {children} 31 | 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/codeblock/index.tsx: -------------------------------------------------------------------------------- 1 | import { cn, cva } from "@repo/utils/site" 2 | 3 | import type { VariantProps } from "@repo/utils/site" 4 | 5 | export const codeblockVariants = cva( 6 | "text-foreground bg-muted/50 border font-mono text-sm", 7 | { 8 | variants: { 9 | inline: { 10 | true: "mx-[0.25ch] inline-block rounded-[4px] px-1", 11 | false: "my-1 rounded px-3 py-2", 12 | }, 13 | }, 14 | defaultVariants: { 15 | inline: false, 16 | }, 17 | }, 18 | ) 19 | 20 | export interface CodeblockProps 21 | extends React.ComponentPropsWithRef<"pre">, 22 | VariantProps {} 23 | 24 | export function Codeblock({ 25 | className, 26 | children, 27 | inline, 28 | ...props 29 | }: CodeblockProps) { 30 | const Component = inline ? "span" : "pre" 31 | 32 | return ( 33 | 37 | {children} 38 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /apps/bot/src/app/events/(modules)/invite-logs/invite-delete.ts: -------------------------------------------------------------------------------- 1 | import { BotEventBuilder } from "@phasejs/builders" 2 | 3 | import { ModuleId } from "@repo/utils/modules" 4 | 5 | import { hasRequiredGuildPermissions } from "./_utils" 6 | 7 | export default new BotEventBuilder() 8 | .setName("inviteDelete") 9 | .setExecute(async (client, invite, ctx) => { 10 | const guild = client.guilds.resolve(invite) 11 | if (!guild || !hasRequiredGuildPermissions(guild)) return 12 | 13 | const guildDoc = ctx.phase.stores.guilds.get(guild.id) 14 | 15 | const moduleConfig = guildDoc?.modules?.[ModuleId.AuditLogs] 16 | if (!moduleConfig?.enabled || !moduleConfig.channels.invites) return 17 | 18 | const inviteStore = ctx.phase.stores.invites.get(guild.id) 19 | if (!inviteStore) return 20 | 21 | const trackedInvite = inviteStore.get(invite.code) 22 | if (!trackedInvite) return 23 | 24 | trackedInvite.deleted = true 25 | trackedInvite.deletedTimestamp = Date.now() 26 | 27 | inviteStore.set(invite.code, trackedInvite) 28 | }) 29 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/modules/levels.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | export interface Levels { 4 | enabled: boolean 5 | channel: string 6 | message: string 7 | background?: string 8 | mention: boolean 9 | roles: { 10 | level: number 11 | role: string 12 | }[] 13 | } 14 | 15 | export const levelsSchema = new Schema( 16 | { 17 | enabled: { type: Schema.Types.Boolean, required: true }, 18 | channel: { type: Schema.Types.String, required: true }, 19 | message: { type: Schema.Types.String, required: true }, 20 | mention: { type: Schema.Types.Boolean, required: true }, 21 | background: { type: Schema.Types.String }, 22 | roles: { 23 | type: [ 24 | new Schema( 25 | { 26 | level: { type: Schema.Types.Number, required: true }, 27 | role: { type: Schema.Types.String, required: true }, 28 | }, 29 | { _id: false }, 30 | ), 31 | ], 32 | required: true, 33 | }, 34 | }, 35 | { _id: false }, 36 | ) 37 | -------------------------------------------------------------------------------- /apps/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "docs", 4 | "type": "module", 5 | "scripts": { 6 | "build": "bun run --bun astro build", 7 | "dev": "bun run --bun astro dev", 8 | "lint": "eslint .", 9 | "preview": "bun run build && bun run start", 10 | "start": "bun run --bun astro preview" 11 | }, 12 | "dependencies": { 13 | "@astrojs/mdx": "^4.2.3", 14 | "@astrojs/react": "^4.2.3", 15 | "@astrojs/sitemap": "^3.3.0", 16 | "@repo/config": "workspace:*", 17 | "@repo/env": "workspace:*", 18 | "@repo/ui": "workspace:*", 19 | "@repo/utils": "workspace:*", 20 | "astro": "^5.6.1", 21 | "astro-seo": "^0.8.4", 22 | "react": "catalog:web", 23 | "react-dom": "catalog:web" 24 | }, 25 | "devDependencies": { 26 | "@tailwindcss/vite": "catalog:web", 27 | "@types/react": "catalog:web", 28 | "@types/react-dom": "catalog:web", 29 | "tailwindcss": "catalog:web" 30 | }, 31 | "peerDependencies": { 32 | "typescript-eslint": "^8.34.1", 33 | "vite": "catalog:web" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/@plugin/music/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@plugin/music", 4 | "version": "0.1.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsup", 8 | "dev": "tsup --watch", 9 | "lint": "SKIP_ENV_VALIDATION=true eslint ." 10 | }, 11 | "exports": { 12 | ".": { 13 | "types": "./dist/index.d.ts", 14 | "import": "./dist/index.js", 15 | "require": "./dist/index.js" 16 | } 17 | }, 18 | "dependencies": { 19 | "@distube/youtube": "^1.0.4", 20 | "@distube/ytdl-core": "catalog:bot", 21 | "@phasejs/core": "workspace:*" 22 | }, 23 | "devDependencies": { 24 | "@plugin/voice": "workspace:*", 25 | "distube": "^5.0.7" 26 | }, 27 | "peerDependencies": { 28 | "@discordjs/voice": "catalog:bot", 29 | "discord.js": "catalog:bot" 30 | }, 31 | "tsup": { 32 | "clean": true, 33 | "dts": true, 34 | "minify": true, 35 | "splitting": true, 36 | "sourcemap": true, 37 | "format": "esm", 38 | "entry": [ 39 | "./src/index.ts" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apps/dash/src/app/auth/internal/[...authjs]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest } from "next/server" 2 | 3 | import { siteConfig } from "@repo/config/site" 4 | 5 | import { handlers } from "~/lib/auth" 6 | 7 | const basePath = siteConfig.basePath ?? "" 8 | 9 | function rewriteRequest(request: NextRequest) { 10 | const { headers, nextUrl } = request 11 | const { protocol, host, pathname, search } = nextUrl 12 | 13 | const detectedHost = headers.get("x-forwarded-host") ?? host 14 | const detectedProtocol = headers.get("x-forwarded-proto") ?? protocol 15 | 16 | const _protocol = detectedProtocol.endsWith(":") 17 | ? detectedProtocol 18 | : detectedProtocol + ":" 19 | 20 | const url = new URL( 21 | _protocol + "//" + detectedHost + basePath + pathname + search, 22 | ) 23 | 24 | return new NextRequest(url, request) 25 | } 26 | 27 | export async function GET(req: NextRequest) { 28 | return await handlers.GET(rewriteRequest(req)) 29 | } 30 | 31 | export async function POST(req: NextRequest) { 32 | return await handlers.POST(rewriteRequest(req)) 33 | } 34 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(utility)/avatar/_options.ts: -------------------------------------------------------------------------------- 1 | import { SlashCommandNumberOption, SlashCommandStringOption } from "discord.js" 2 | 3 | const imageSizes = [16, 32, 64, 128, 256, 512, 1024, 2048, 4096] 4 | 5 | export const extensionOption = new SlashCommandStringOption() 6 | .setName("extension") 7 | .setDescription("The image extension (defaults to webp)") 8 | .setRequired(false) 9 | .setChoices( 10 | { 11 | name: "webp", 12 | value: "webp", 13 | }, 14 | { 15 | name: "png", 16 | value: "png", 17 | }, 18 | { 19 | name: "jpg", 20 | value: "jpg", 21 | }, 22 | { 23 | name: "jpeg", 24 | value: "jpeg", 25 | }, 26 | { 27 | name: "gif", 28 | value: "gif", 29 | }, 30 | ) 31 | 32 | export const sizeOption = new SlashCommandNumberOption() 33 | .setName("size") 34 | .setDescription("The image size (defaults to 512)") 35 | .setRequired(false) 36 | .setChoices( 37 | ...imageSizes.map((size) => ({ 38 | name: size.toString(), 39 | value: size, 40 | })), 41 | ) 42 | -------------------------------------------------------------------------------- /packages/@repo/utils/src/ms.ts: -------------------------------------------------------------------------------- 1 | import unsafeMs from "ms" 2 | 3 | import type { StringValue } from "ms" 4 | 5 | /** The options to format with. */ 6 | export type MsOptions = { long: boolean } 7 | 8 | /** 9 | * A safe version of `ms` that returns undefined if the value is invalid. 10 | * 11 | * @param value The value to parse. 12 | */ 13 | export function ms(value: string): number | undefined 14 | 15 | /** 16 | * A safe version of `ms` that returns undefined if the value is invalid. 17 | * 18 | * @param value The value to format. 19 | * @param options The options to format with. 20 | */ 21 | export function ms(value: number, options?: MsOptions): string | undefined 22 | 23 | export function ms(value: string | number, options?: MsOptions) { 24 | let parsedValue: number | string | undefined 25 | 26 | try { 27 | if (typeof value === "string") { 28 | parsedValue = unsafeMs(value as StringValue) 29 | } else { 30 | parsedValue = unsafeMs(value, options) 31 | } 32 | } catch { 33 | parsedValue = undefined 34 | } 35 | 36 | return parsedValue 37 | } 38 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(fun)/games/rps.ts: -------------------------------------------------------------------------------- 1 | import { BotSubcommandBuilder } from "@phasejs/builders" 2 | 3 | export default new BotSubcommandBuilder() 4 | .setName("rps") 5 | .setDescription("Starts a game of rock-paper-scissors.") 6 | .addStringOption((option) => 7 | option 8 | .setName("choice") 9 | .setDescription("Your move.") 10 | .setRequired(true) 11 | .setChoices( 12 | { name: "Rock", value: "rock" }, 13 | { name: "Paper", value: "paper" }, 14 | { name: "Scissors", value: "scissors" }, 15 | ), 16 | ) 17 | .setExecute((interaction) => { 18 | const choices = ["rock", "paper", "scissors"] 19 | const outcomes = [`It's a tie! GG. 🤝`, `You win! GG. 🤝`, `I win! GG. 🤝`] 20 | const choice = interaction.options.getString("choice", true) 21 | const move = Math.floor(Math.random() * 3) 22 | const outcomeIndex = (choices.indexOf(choice) - move + 3) % 3 23 | 24 | void interaction.reply( 25 | `You chose **${choice}** and I chose **${choices[move]}**.\n${outcomes[outcomeIndex]}`, 26 | ) 27 | }) 28 | -------------------------------------------------------------------------------- /apps/bot/src/app/events/(modules)/auto-roles/joins.ts: -------------------------------------------------------------------------------- 1 | import { BotEventBuilder } from "@phasejs/builders" 2 | import { GuildFeature } from "discord.js" 3 | 4 | import { ModuleId } from "@repo/utils/modules" 5 | 6 | const verificationGate = GuildFeature.MemberVerificationGateEnabled 7 | 8 | export default new BotEventBuilder() 9 | .setName("guildMemberAdd") 10 | .setExecute(async (_, member, ctx) => { 11 | if (member.guild.features.includes(verificationGate)) return 12 | 13 | const guildDoc = ctx.phase.stores.guilds.get(member.guild.id) 14 | const autoRolesModule = guildDoc?.modules?.[ModuleId.AutoRoles] 15 | 16 | if (!autoRolesModule?.enabled) return 17 | 18 | for (const role of autoRolesModule.roles) { 19 | if (role.target === "bots" && !member.user.bot) continue 20 | if (role.target === "members" && member.user.bot) continue 21 | 22 | if ( 23 | member.guild.roles.cache.get(role.id) && 24 | !member.roles.cache.has(role.id) 25 | ) { 26 | await member.roles.add(role.id).catch(() => null) 27 | } 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /packages/@phasejs/loaders/src/index.ts: -------------------------------------------------------------------------------- 1 | import { buildBuilders } from "~/build" 2 | import { getAppExports } from "~/exports" 3 | import { getAppPaths } from "~/paths" 4 | 5 | import type { BotClient } from "@phasejs/core" 6 | import type { AppConfig, AppExports, AppInitParams } from "~/types/app" 7 | 8 | export async function loadApp( 9 | phase: BotClient, 10 | config: AppConfig = {}, 11 | ): Promise { 12 | const paths = await getAppPaths(config) 13 | const exports = await getAppExports(paths, config) 14 | return getAppInitParams(phase, exports) 15 | } 16 | 17 | export function getAppInitParams( 18 | phase: BotClient, 19 | exports: AppExports, 20 | ): AppInitParams { 21 | return { 22 | prestart: Object.values(exports.prestart)[0], 23 | middlewares: Object.values(exports.middlewares)[0], 24 | ...buildBuilders(phase, exports), 25 | } 26 | } 27 | 28 | export * from "~/lib/constants" 29 | export * from "~/lib/utils" 30 | 31 | export * from "~/build" 32 | export * from "~/exports" 33 | export * from "~/paths" 34 | 35 | export type * from "~/types/app" 36 | -------------------------------------------------------------------------------- /packages/@plugin/music/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { version as pkgVersion } from "~/../package.json" 2 | 3 | import type { BotPluginVersion } from "@phasejs/core" 4 | 5 | export const pluginVersion = pkgVersion as BotPluginVersion 6 | 7 | type Duration = `${number}:${number}` | `${number}:${number}:${number}` 8 | 9 | /** 10 | * Formats a number to a duration string. 11 | * 12 | * @param number - Duration in seconds. 13 | */ 14 | export function numberToDuration(number: number): Duration { 15 | if (!number || !Number(number)) return "00:00" 16 | 17 | const seconds = Math.floor(number % 60) 18 | const minutes = Math.floor((number % 3600) / 60) 19 | const hours = Math.floor(number / 3600) 20 | 21 | const formatInt = (int: number) => (int < 10 ? `0${int}` : int.toString()) 22 | 23 | if (hours > 0) { 24 | return `${formatInt(hours)}:${formatInt(minutes)}:${formatInt(seconds)}` as Duration 25 | } else if (minutes > 0) { 26 | return `${formatInt(minutes)}:${formatInt(seconds)}` as Duration 27 | } else { 28 | return `00:${formatInt(seconds)}` as Duration 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/models/guilds/index.ts: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose" 2 | 3 | import { commandSchema } from "~/mongo/models/guilds/commands" 4 | import { modulesSchema } from "~/mongo/models/guilds/modules" 5 | import { defineModel } from "~/mongo/utils" 6 | 7 | import type { GuildCommand } from "~/mongo/models/guilds/commands" 8 | import type { GuildModules } from "~/mongo/models/guilds/modules" 9 | 10 | export type * from "~/mongo/models/guilds/commands" 11 | export type * from "~/mongo/models/guilds/modules" 12 | 13 | export interface Guild { 14 | id: string 15 | admins: string[] 16 | commands?: Map 17 | modules?: Partial 18 | } 19 | 20 | const guildsSchema = new Schema({ 21 | id: { type: Schema.Types.String, required: true }, 22 | admins: { type: [Schema.Types.String], required: true, default: [] }, 23 | commands: { type: Schema.Types.Map, of: commandSchema }, 24 | modules: { type: modulesSchema }, 25 | }) 26 | 27 | export const guilds = defineModel("Guilds", guildsSchema, { 28 | id: { unique: true }, 29 | }) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | Visit https://phasebot.xyz/docs to view the full documentation. 4 | 5 | ## Community 6 | 7 | The Phase Bot community can be found in our [Discord server], where you can ask 8 | questions, voice ideas, and share your projects. 9 | 10 | Our [Code of Conduct] applies to all Phase Bot community channels. 11 | 12 | ## Contributions 13 | 14 | To contribute you can create pull requests or open issues. There are issue 15 | templates available for creating feature requests and reporting bugs, please use 16 | them. 17 | 18 | ## Security 19 | 20 | If you believe you have found a security vulnerability in our code, we encourage 21 | you to responsibly disclose this and not open a public issue. We will 22 | investigate all legitimate reports. Contact the developers in a support ticket 23 | to disclose any security vulnerabilities. 24 | 25 | 26 | 27 | [Discord server]: https://phasebot.xyz/redirect/discord 28 | [Code of Conduct]: https://github.com/mikaeladev/phase/blob/main/.github/CODE_OF_CONDUCT.md 29 | 30 | 31 | -------------------------------------------------------------------------------- /apps/dash/src/types/dashboard.ts: -------------------------------------------------------------------------------- 1 | import type { TRPCGuild } from "@repo/trpc/client" 2 | import type { ModuleDefinitions, ModuleId } from "@repo/utils/modules" 3 | import type { Mutable, Prettify } from "@repo/utils/types" 4 | import type { z } from "@repo/zod" 5 | import type { modulesFormSchema, modulesTrpcSchema } from "~/lib/schemas" 6 | import type { Session } from "~/types/auth" 7 | import type { UseFormReturn } from "react-hook-form" 8 | 9 | export type ModulesTRPCSchemaType = z.TypeOf 10 | export type ModulesFormSchemaType = z.TypeOf 11 | 12 | export type ModulesFormReturn = UseFormReturn 13 | 14 | type ModuleDefinition = 15 | (typeof ModuleDefinitions)[T] 16 | 17 | export type ModuleDefinitionWithConfig = { 18 | [K in ModuleId]: Prettify< 19 | Mutable> & { 20 | config: ModulesFormSchemaType[K] 21 | } 22 | > 23 | }[T] 24 | 25 | export type DashboardData = { 26 | session: Session 27 | guild: Promise 28 | } 29 | -------------------------------------------------------------------------------- /packages/@repo/config/src/site/config.ts: -------------------------------------------------------------------------------- 1 | type ConfigVariables = { 2 | port: string 3 | baseURL: string 4 | basePath: string | undefined 5 | } 6 | 7 | export function createSiteConfig(variables: ConfigVariables) { 8 | const { port, baseURL, basePath } = variables 9 | 10 | return { 11 | title: 12 | basePath === "/dashboard" 13 | ? "Phase Dashboard" 14 | : basePath === "/docs" 15 | ? "Phase Docs" 16 | : "Phase Bot", 17 | port: Number(port ?? 3000), 18 | url: baseURL + (basePath ?? ""), 19 | baseUrl: baseURL, 20 | basePath, 21 | description: 22 | "Phase is a free to use, open source Discord bot that aims to be the all-in-one solution for as many servers as possible.", 23 | keywords: ["Discord", "Bot", "Phase", "Free"], 24 | developer: { 25 | name: "mikaela", 26 | url: "https://github.com/mikaeladev", 27 | }, 28 | images: { 29 | logo: new URL("/phase.png", baseURL), 30 | favicon: new URL("/favicon.ico", baseURL), 31 | apple: new URL("/apple.png", baseURL), 32 | }, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/bot/src/app/commands/(fun)/coinflip.ts: -------------------------------------------------------------------------------- 1 | import { BotCommandBuilder } from "@phasejs/builders" 2 | 3 | export default new BotCommandBuilder() 4 | .setName("coinflip") 5 | .setDescription("Flips a coin.") 6 | .setExecute(async (interaction) => { 7 | await interaction.deferReply() 8 | 9 | const commonResponses = ["It's **Heads**!", "It's **Tails**!"] 10 | 11 | const rareResponses = [ 12 | "The coin landed on its side :/", 13 | "The outcome was decided long before you flipped the coin...\n-# It's **Heads** though.", 14 | "Coin flips are just chaos wrapped in the illusion of choice...\n-# It’s **Tails** though.", 15 | "Nothing is ever truly random, it’s all just angles, force, and gravity...\n-# It’s **Heads** though.", 16 | ] 17 | 18 | const random = Math.floor(Math.random() * 250) 19 | 20 | const response = 21 | random < 10 22 | ? rareResponses[Math.floor(Math.random() * rareResponses.length)]! 23 | : commonResponses[Math.floor(Math.random() * commonResponses.length)]! 24 | 25 | return void interaction.editReply(response) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/@repo/ui/src/components/spinner/index.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@repo/utils/site" 2 | 3 | export interface SpinnerProps extends React.ComponentPropsWithRef<"div"> {} 4 | 5 | export function Spinner({ className, ...props }: SpinnerProps) { 6 | return ( 7 |
8 |
9 | {Array(12) 10 | .fill(0) 11 | .map((_, i) => ( 12 |
20 | ))} 21 |
22 | 28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /packages/@repo/db/src/mongo/utils.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose" 2 | 3 | import type { IndexOptions, Model, Schema, SortOrder } from "mongoose" 4 | 5 | type ModelIndexValue = 6 | | true 7 | | SortOrder 8 | | (IndexOptions & { sortOrder?: SortOrder }) 9 | 10 | /** 11 | * Defines a mongoose model with a given name and schema. 12 | * 13 | * @param name The name of the model. 14 | * @param schema The schema of the model. 15 | */ 16 | export function defineModel( 17 | name: string, 18 | schema: Schema, 19 | indexes?: { [key in keyof T]?: ModelIndexValue }, 20 | ) { 21 | for (const index of Object.entries(indexes ?? {})) { 22 | const [key, value] = index as [keyof T, ModelIndexValue] 23 | 24 | if (typeof value === "object") { 25 | const { sortOrder, ...options } = value 26 | schema.index({ [key]: sortOrder ?? "ascending" }, options) 27 | } else { 28 | const sortOrder = value === true ? "ascending" : value 29 | schema.index({ [key]: sortOrder }) 30 | } 31 | } 32 | 33 | return (mongoose.models[name] as Model) ?? mongoose.model(name, schema) 34 | } 35 | -------------------------------------------------------------------------------- /apps/bot/.env.template: -------------------------------------------------------------------------------- 1 | # Security Warning! Do not include any secrets in this file! 2 | # 3 | # This is a template file for `.env.local` and is intended to be shared with 4 | # other developers. 5 | # 6 | # Please place any secrets in `.env.local` instead. 7 | 8 | # make a mongodb database: 9 | # https://www.freecodecamp.org/news/get-started-with-mongodb-atlas/ 10 | MONGODB_URI="" 11 | 12 | # make a postgres database: 13 | # https://neon.tech/docs/get-started-with-neon/signing-up 14 | POSTGRES_URI="" 15 | 16 | # make a discord bot: 17 | # https://discord.com/developers/applications 18 | DISCORD_TOKEN="" 19 | DISCORD_SECRET="" 20 | DISCORD_ID="" 21 | 22 | # make a twitch app: 23 | # https://dev.twitch.tv/console/apps/create 24 | TWITCH_CLIENT_ID="" 25 | TWITCH_CLIENT_SECRET="" 26 | 27 | # generate with openssl: 28 | # $ openssl rand -base64 32 29 | AUTH_COOKIE_SECRET="" 30 | AUTH_OTP_SECRET="" 31 | 32 | # generate with openssl: 33 | # $ openssl rand -base64 32 34 | TRPC_TOKEN="" 35 | 36 | # make a discord webhook: 37 | # https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks 38 | WEBHOOK_ALERT="" 39 | --------------------------------------------------------------------------------