Compare commits
No commits in common. "c3bf31b3d4b806a303c4c2250ae4d47e4a444acf" and "712affc83d6fa7ab1765e17edb2a0bab2056d4ca" have entirely different histories.
c3bf31b3d4
...
712affc83d
43 changed files with 3551 additions and 10944 deletions
|
@ -13,11 +13,6 @@ Authorization: Bearer {{$dotenv DISCORD_ACCESS_TOKEN}}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
GET https://discord.com/api/guilds/{{$dotenv DISCORD_GUILD_ID}}
|
|
||||||
Authorization: Bot {{$dotenv DISCORD_BOT_TOKEN}}
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
POST https://discord.com/api/oauth2/token/revoke
|
POST https://discord.com/api/oauth2/token/revoke
|
||||||
Content-Type: application/x-www-form-urlencoded
|
Content-Type: application/x-www-form-urlencoded
|
||||||
Authorization: Basic {{$dotenv DISCORD_CLIENT_ID}}:{{$dotenv DISCORD_CLIENT_SECRET}}
|
Authorization: Basic {{$dotenv DISCORD_CLIENT_ID}}:{{$dotenv DISCORD_CLIENT_SECRET}}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import "dotenv/config";
|
import "dotenv/config"
|
||||||
import type { Config } from "drizzle-kit";
|
import type { Config } from "drizzle-kit"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
schema: "./src/drizzle/schema.ts",
|
schema: "./src/drizzle/schema.ts",
|
||||||
|
@ -8,4 +8,4 @@ export default {
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
connectionString: process.env.DATABASE_URL ?? "",
|
connectionString: process.env.DATABASE_URL ?? "",
|
||||||
},
|
},
|
||||||
} satisfies Config;
|
} satisfies Config
|
||||||
|
|
24
package.json
24
package.json
|
@ -7,30 +7,20 @@
|
||||||
"start": "vinxi start",
|
"start": "vinxi start",
|
||||||
"lint": "eslint --fix \"**/*.{ts,tsx,js,jsx}\"",
|
"lint": "eslint --fix \"**/*.{ts,tsx,js,jsx}\"",
|
||||||
"push": "drizzle-kit push:pg",
|
"push": "drizzle-kit push:pg",
|
||||||
"discord-openapi-gen": "pnpm openapi-typescript https://raw.githubusercontent.com/discord/discord-api-spec/main/specs/openapi.json -o ./src/types/discord.d.ts",
|
|
||||||
"liljudd-openapi-gen": "pnpm openapi-typescript ./public/api/specs/liljudd.json -o ./src/types/liljudd.d.ts",
|
|
||||||
"typecheck": "tsc --noEmit --checkJs false --skipLibCheck"
|
"typecheck": "tsc --noEmit --checkJs false --skipLibCheck"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/core": "^0.19.0",
|
"@auth/core": "^0.19.0",
|
||||||
"@auth/drizzle-adapter": "^0.3.12",
|
"@auth/drizzle-adapter": "^0.3.12",
|
||||||
"@auth/solid-start": "0.1.2",
|
"@auth/solid-start": "0.1.2",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
"@solidjs/meta": "^0.29.3",
|
||||||
"@fortawesome/pro-duotone-svg-icons": "^6.5.1",
|
"@solidjs/router": "^0.10.5",
|
||||||
"@fortawesome/pro-light-svg-icons": "^6.5.1",
|
"@solidjs/start": "^0.4.2",
|
||||||
"@fortawesome/pro-regular-svg-icons": "^6.5.1",
|
|
||||||
"@fortawesome/pro-solid-svg-icons": "^6.5.1",
|
|
||||||
"@fortawesome/pro-thin-svg-icons": "^6.5.1",
|
|
||||||
"@fortawesome/sharp-solid-svg-icons": "^6.5.1",
|
|
||||||
"@solidjs/meta": "^0.29.2",
|
|
||||||
"@solidjs/router": "^0.10.9",
|
|
||||||
"@solidjs/start": "^0.4.9",
|
|
||||||
"drizzle-orm": "^0.29.2",
|
"drizzle-orm": "^0.29.2",
|
||||||
"moment-timezone": "^0.5.44",
|
|
||||||
"openapi-fetch": "^0.8.2",
|
|
||||||
"postgres": "^3.4.3",
|
"postgres": "^3.4.3",
|
||||||
"solid-js": "^1.8.11",
|
"solid-js": "^1.8.7",
|
||||||
"vinxi": "^0.1.2"
|
"solid-start": "^0.3.10",
|
||||||
|
"vinxi": "0.0.62"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||||
|
@ -40,12 +30,10 @@
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-solid": "^0.13.1",
|
"eslint-plugin-solid": "^0.13.1",
|
||||||
"openapi-typescript": "^6.7.3",
|
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"prettier-plugin-organize-imports": "^3.2.4",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"sass": "^1.69.6",
|
"sass": "^1.69.6",
|
||||||
"typescript": "^5.3.3",
|
|
||||||
"zod": "3.22.4"
|
"zod": "3.22.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
3721
pnpm-lock.yaml
3721
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,34 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="description" content="SwaggerUI" />
|
|
||||||
<title>SwaggerUI</title>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css"
|
|
||||||
/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="swagger-ui"></div>
|
|
||||||
<script
|
|
||||||
src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js"
|
|
||||||
crossorigin
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-standalone-preset.js"
|
|
||||||
crossorigin
|
|
||||||
></script>
|
|
||||||
<script>
|
|
||||||
window.onload = () => {
|
|
||||||
window.ui = SwaggerUIBundle({
|
|
||||||
url: "/api/specs/liljudd.json",
|
|
||||||
dom_id: "#swagger-ui",
|
|
||||||
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
|
|
||||||
layout: "StandaloneLayout",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,238 +0,0 @@
|
||||||
{
|
|
||||||
"openapi": "3.0.0",
|
|
||||||
"info": {
|
|
||||||
"title": "li'l Judd - OpenAPI 3.0",
|
|
||||||
"description": "None yet",
|
|
||||||
"termsOfService": "https://liljudd.ink/terms-of-service/",
|
|
||||||
"contact": {
|
|
||||||
"url": "https://liljudd.ink/contact/"
|
|
||||||
},
|
|
||||||
"version": "0.0.0"
|
|
||||||
},
|
|
||||||
"paths": {
|
|
||||||
"/api/config/{guildId}": {
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"Guild configs"
|
|
||||||
],
|
|
||||||
"summary": "Find guild config by ID",
|
|
||||||
"description": "Returns a single guild config",
|
|
||||||
"operationId": "getGuildById",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "guildId",
|
|
||||||
"in": "path",
|
|
||||||
"description": "ID of guild config to return",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "successful operation",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/guildConfig"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Invalid ID supplied"
|
|
||||||
},
|
|
||||||
"404": {
|
|
||||||
"description": "Guild not found"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"api_key": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"delete": {
|
|
||||||
"tags": [
|
|
||||||
"Guild configs"
|
|
||||||
],
|
|
||||||
"summary": "Deletes a guild config by ID",
|
|
||||||
"description": "Delete a guild's config",
|
|
||||||
"operationId": "deleteGuildById",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "guildId",
|
|
||||||
"in": "path",
|
|
||||||
"description": "ID of guild config to delete",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"204": {
|
|
||||||
"description": "successful operation"
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Invalid ID supplied"
|
|
||||||
},
|
|
||||||
"404": {
|
|
||||||
"description": "Guild not found"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"api_key": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/tp_messages/{guildId}": {
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"Time planning messages"
|
|
||||||
],
|
|
||||||
"summary": "Find guild by ID for it's tp_messages",
|
|
||||||
"description": "Returns tp_messages for a guild",
|
|
||||||
"operationId": "getTp_messagesOfGuildById",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "guildId",
|
|
||||||
"in": "path",
|
|
||||||
"description": "ID of guild's tp_messages to return",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "successful operation",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/guildConfig"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"204": {
|
|
||||||
"description": "Time planning not enabled for this guild"
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Invalid ID supplied"
|
|
||||||
},
|
|
||||||
"404": {
|
|
||||||
"description": "Guild not found"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"api_key": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"components": {
|
|
||||||
"schemas": {
|
|
||||||
"guildConfig": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"guildID": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)",
|
|
||||||
"example": "1234567890123456789"
|
|
||||||
},
|
|
||||||
"features": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"time_planning": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"channelID": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)",
|
|
||||||
"example": "1234567890123456789"
|
|
||||||
},
|
|
||||||
"cron": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "0 0 1 * * * 60o 1w"
|
|
||||||
},
|
|
||||||
"isAvailableRoleId": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)",
|
|
||||||
"example": "1234567890123456789"
|
|
||||||
},
|
|
||||||
"wantsToBeNotifieRoledId": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)",
|
|
||||||
"example": "1234567890123456789"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"matches": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/match"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"match": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"channelID": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)",
|
|
||||||
"example": "1234567890123456789"
|
|
||||||
},
|
|
||||||
"matchType": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(50)",
|
|
||||||
"example": "Scrim"
|
|
||||||
},
|
|
||||||
"createrId": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)",
|
|
||||||
"example": "1234567890123456789"
|
|
||||||
},
|
|
||||||
"roleId": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)",
|
|
||||||
"example": "1234567890123456789"
|
|
||||||
},
|
|
||||||
"opponentName": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(100)",
|
|
||||||
"example": "?"
|
|
||||||
},
|
|
||||||
"messsageId": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "varchar(19)",
|
|
||||||
"example": "1234567890123456789"
|
|
||||||
},
|
|
||||||
"cron": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "0 0 1 5 2 2023 60o"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"securitySchemes": {
|
|
||||||
"api_key": {
|
|
||||||
"type": "apiKey",
|
|
||||||
"name": "api_key",
|
|
||||||
"in": "header"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
@ -1,10 +1,12 @@
|
||||||
// @refresh reload
|
// @refresh reload
|
||||||
import "@fortawesome/fontawesome-svg-core/styles.css";
|
|
||||||
import { Meta, MetaProvider, Title } from "@solidjs/meta";
|
import { Meta, MetaProvider, Title } from "@solidjs/meta";
|
||||||
import { Router } from "@solidjs/router";
|
import { Router } from "@solidjs/router";
|
||||||
import { FileRoutes } from "@solidjs/start";
|
import { FileRoutes } from "@solidjs/start";
|
||||||
import { Suspense } from "solid-js";
|
import { Suspense } from "solid-js";
|
||||||
import "./styles/global.scss";
|
import Footer from "./components/Footer";
|
||||||
|
import NavBar from "./components/NavBar";
|
||||||
|
import "./styles/GlobalLayout.css";
|
||||||
|
import "./styles/Layout.scss";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -16,8 +18,9 @@ export default function App() {
|
||||||
content="The Splatoon Discord bot with unique features."
|
content="The Splatoon Discord bot with unique features."
|
||||||
/>
|
/>
|
||||||
<Title>li'l Judd - Your competitive Splatoon assistant</Title>
|
<Title>li'l Judd - Your competitive Splatoon assistant</Title>
|
||||||
|
<NavBar />
|
||||||
<Suspense>{props.children}</Suspense>
|
<Suspense>{props.children}</Suspense>
|
||||||
|
<Footer />
|
||||||
</MetaProvider>
|
</MetaProvider>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
import {
|
|
||||||
FaSymbol,
|
|
||||||
FlipProp,
|
|
||||||
IconDefinition,
|
|
||||||
IconProp,
|
|
||||||
PullProp,
|
|
||||||
RotateProp,
|
|
||||||
SizeProp,
|
|
||||||
Transform,
|
|
||||||
} from "@fortawesome/fontawesome-svg-core";
|
|
||||||
import { type JSX } from "solid-js";
|
|
||||||
|
|
||||||
export interface FontAwesomeIconProps
|
|
||||||
extends Omit<
|
|
||||||
JSX.SvgSVGAttributes<SVGSVGElement>,
|
|
||||||
"children" | "mask" | "transform"
|
|
||||||
> {
|
|
||||||
icon: IconDefinition;
|
|
||||||
mask?: IconProp;
|
|
||||||
maskId?: string;
|
|
||||||
color?: string;
|
|
||||||
spin?: boolean;
|
|
||||||
spinPulse?: boolean;
|
|
||||||
spinReverse?: boolean;
|
|
||||||
pulse?: boolean;
|
|
||||||
beat?: boolean;
|
|
||||||
fade?: boolean;
|
|
||||||
beatFade?: boolean;
|
|
||||||
bounce?: boolean;
|
|
||||||
shake?: boolean;
|
|
||||||
flash?: boolean;
|
|
||||||
border?: boolean;
|
|
||||||
fixedWidth?: boolean;
|
|
||||||
inverse?: boolean;
|
|
||||||
listItem?: boolean;
|
|
||||||
flip?: FlipProp;
|
|
||||||
size?: SizeProp;
|
|
||||||
pull?: PullProp;
|
|
||||||
rotation?: RotateProp;
|
|
||||||
transform?: string | Transform;
|
|
||||||
symbol?: FaSymbol;
|
|
||||||
style?: JSX.CSSProperties;
|
|
||||||
tabIndex?: number;
|
|
||||||
title?: string;
|
|
||||||
titleId?: string;
|
|
||||||
swapOpacity?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const idPool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
||||||
function nextUniqueId() {
|
|
||||||
let size = 12;
|
|
||||||
let id = "";
|
|
||||||
|
|
||||||
while (size-- > 0) {
|
|
||||||
id += idPool[(Math.random() * 62) | 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Path(props: { d: string | string[] }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{typeof props.d === "string" ? (
|
|
||||||
<path fill="currentColor" d={props.d} />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<path class="fa-secondary" fill="currentColor" d={props.d[0]} />
|
|
||||||
<path class="fa-primary" fill="currentColor" d={props.d[1]} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FontAwesomeIcon(props: FontAwesomeIconProps) {
|
|
||||||
const titleId = () =>
|
|
||||||
props.title
|
|
||||||
? "svg-inline--fa-title-".concat(props.titleId || nextUniqueId())
|
|
||||||
: undefined;
|
|
||||||
// Get CSS class list from the props object
|
|
||||||
function attributes() {
|
|
||||||
const defaultClasses = {
|
|
||||||
"svg-inline--fa": true,
|
|
||||||
[`fa-${props.icon.iconName}`]: true,
|
|
||||||
[props.class ?? ""]:
|
|
||||||
typeof props.class !== "undefined" && props.class !== null,
|
|
||||||
...props.classList,
|
|
||||||
};
|
|
||||||
|
|
||||||
// map of CSS class names to properties
|
|
||||||
const faClasses = {
|
|
||||||
"fa-beat": props.beat,
|
|
||||||
"fa-fade": props.fade,
|
|
||||||
"fa-beat-fade": props.beatFade,
|
|
||||||
"fa-bounce": props.bounce,
|
|
||||||
"fa-shake": props.shake,
|
|
||||||
"fa-flash": props.flash,
|
|
||||||
"fa-spin": props.spin,
|
|
||||||
"fa-spin-reverse": props.spinReverse,
|
|
||||||
"fa-spin-pulse": props.spinPulse,
|
|
||||||
"fa-pulse": props.pulse,
|
|
||||||
"fa-fw": props.fixedWidth,
|
|
||||||
"fa-inverse": props.inverse,
|
|
||||||
"fa-border": props.border,
|
|
||||||
"fa-li": props.listItem,
|
|
||||||
"fa-flip": typeof props.flip !== "undefined" && props.flip !== null,
|
|
||||||
"fa-flip-horizontal":
|
|
||||||
props.flip === "horizontal" || props.flip === "both",
|
|
||||||
"fa-flip-vertical": props.flip === "vertical" || props.flip === "both",
|
|
||||||
[`fa-${props.size}`]:
|
|
||||||
typeof props.size !== "undefined" && props.size !== null,
|
|
||||||
[`fa-rotate-${props.rotation}`]:
|
|
||||||
typeof props.rotation !== "undefined" && props.size !== null,
|
|
||||||
[`fa-pull-${props.pull}`]:
|
|
||||||
typeof props.pull !== "undefined" && props.pull !== null,
|
|
||||||
"fa-swap-opacity": props.swapOpacity,
|
|
||||||
};
|
|
||||||
|
|
||||||
const attributes = {
|
|
||||||
focusable: !!props.title,
|
|
||||||
"aria-hidden": !props.title,
|
|
||||||
role: "img",
|
|
||||||
xmlns: "http://www.w3.org/2000/svg",
|
|
||||||
"aria-labelledby": titleId(),
|
|
||||||
"data-prefix": props.icon.prefix,
|
|
||||||
"data-icon": props.icon.iconName,
|
|
||||||
"data-fa-transform": props.transform,
|
|
||||||
"data-fa-mask": props.mask,
|
|
||||||
"data-fa-mask-id": props.maskId,
|
|
||||||
"data-fa-symbol": props.symbol,
|
|
||||||
tabIndex: props.tabIndex,
|
|
||||||
classList: { ...defaultClasses, ...faClasses },
|
|
||||||
color: props.color,
|
|
||||||
style: props.style,
|
|
||||||
viewBox: `0 0 ${props.icon.icon[0]} ${props.icon.icon[1]}`,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// return the complete class list
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<svg {...attributes()}>
|
|
||||||
{/* <Show when={props.title}>
|
|
||||||
<title id={titleId()}>{props.title}</title>
|
|
||||||
</Show> */}
|
|
||||||
<Path d={props.icon.icon[4]} />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { JSX, Suspense } from "solid-js";
|
|
||||||
import "../styles/Layout.scss";
|
|
||||||
import Footer from "./Footer";
|
|
||||||
import NavBar from "./NavBar";
|
|
||||||
|
|
||||||
function Layout(props: { children: JSX.Element; site: string }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<NavBar />
|
|
||||||
<div class={props.site}>
|
|
||||||
<Suspense>{props.children}</Suspense>
|
|
||||||
</div>
|
|
||||||
<Footer />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Layout;
|
|
|
@ -1,53 +1,35 @@
|
||||||
import { faCirclePlus } from "@fortawesome/pro-regular-svg-icons";
|
|
||||||
import { JSX, Show, Suspense } from "solid-js";
|
|
||||||
import "../styles/components/NavBar.scss";
|
import "../styles/components/NavBar.scss";
|
||||||
import { FontAwesomeIcon } from "./FontAwesomeIcon";
|
|
||||||
import NavUser from "./NavUser";
|
|
||||||
|
|
||||||
export function Li(props: {
|
|
||||||
href: string;
|
|
||||||
action?: () => void;
|
|
||||||
name?: string;
|
|
||||||
children?: JSX.Element;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<li class="navElem flex-row thick">
|
|
||||||
<a
|
|
||||||
class="flex-row"
|
|
||||||
href={props.href}
|
|
||||||
onClick={() => props.action && props.action()}
|
|
||||||
>
|
|
||||||
{props.children ?? <></>}
|
|
||||||
<Show when={props.name}>
|
|
||||||
<span>{props.name}</span>
|
|
||||||
</Show>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function NavBar() {
|
function NavBar() {
|
||||||
return (
|
return (
|
||||||
<nav class="flex-row responsive">
|
<nav>
|
||||||
<ul class="flex-row responsive thick">
|
<ul>
|
||||||
<Li href="/" name="li'l Judd">
|
<li class="navElem">
|
||||||
<img src="/assets/logox256.png" alt="The Bots Logo" />
|
<a class="textBx" href="/">
|
||||||
</Li>
|
<img id="logo" src="/assets/logox256.png" alt="The Bots Logo" />
|
||||||
<Li href="/features" name="Features" />
|
li'l Judd
|
||||||
<Li href="/how-do-i" name="How do I...?" />
|
</a>
|
||||||
<Li href="/stack" name="The Stack" />
|
</li>
|
||||||
<Li href="/about" name="About" />
|
<li class="navElem">
|
||||||
</ul>
|
<a href="/features">Features</a>
|
||||||
<ul class="flex-row responsive thick">
|
</li>
|
||||||
<Li
|
<li class="navElem">
|
||||||
href="https://discord.com/api/oauth2/authorize?client_id=1024410658973941862&permissions=18977581952080&scope=bot"
|
<a href="/how-do-i">How do I...?</a>
|
||||||
name="Invite to your server"
|
</li>
|
||||||
>
|
<li class="navElem">
|
||||||
<FontAwesomeIcon class="lower" icon={faCirclePlus} size="xl" />
|
<a href="/stack">The Stack</a>
|
||||||
</Li>
|
</li>
|
||||||
<Suspense>
|
<li class="navElem">
|
||||||
<NavUser />
|
<a href="/about">About</a>
|
||||||
</Suspense>
|
</li>
|
||||||
|
<li class="navElem">
|
||||||
|
<a
|
||||||
|
href="https://discord.com/api/oauth2/authorize?client_id=1024410658973941862&permissions=18977581952080&scope=bot"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Invite to your server
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
import { getSession } from "@auth/solid-start";
|
|
||||||
import { signIn, signOut } from "@auth/solid-start/client";
|
|
||||||
import {
|
|
||||||
faArrowRightFromBracket,
|
|
||||||
faArrowRightToBracket,
|
|
||||||
faGear,
|
|
||||||
} from "@fortawesome/pro-regular-svg-icons";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { Show, createResource } from "solid-js";
|
|
||||||
import { getRequestEvent } from "solid-js/web";
|
|
||||||
import db from "~/drizzle";
|
|
||||||
import { users } from "~/drizzle/schema";
|
|
||||||
import { authOptions } from "~/server/auth";
|
|
||||||
import { FontAwesomeIcon } from "./FontAwesomeIcon";
|
|
||||||
import { Li } from "./NavBar";
|
|
||||||
|
|
||||||
const initialUser = {
|
|
||||||
id: "",
|
|
||||||
name: null as string | null,
|
|
||||||
email: "",
|
|
||||||
emailVerified: null as Date | null,
|
|
||||||
image: null as string | null,
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getUser() {
|
|
||||||
"use server";
|
|
||||||
|
|
||||||
const event = getRequestEvent();
|
|
||||||
if (!event)
|
|
||||||
return { success: false, message: "No request event!", ...initialUser };
|
|
||||||
|
|
||||||
const session = await getSession(event.request, authOptions);
|
|
||||||
if (!session?.user?.id)
|
|
||||||
return { success: false, message: "No user with id!", ...initialUser };
|
|
||||||
|
|
||||||
const user = (
|
|
||||||
await db
|
|
||||||
.selectDistinct()
|
|
||||||
.from(users)
|
|
||||||
.where(eq(users.id, session.user?.id))
|
|
||||||
.limit(1)
|
|
||||||
.execute()
|
|
||||||
)[0];
|
|
||||||
|
|
||||||
return { success: true, message: "", ...user };
|
|
||||||
}
|
|
||||||
|
|
||||||
function NavUser() {
|
|
||||||
const [user] = createResource(() =>
|
|
||||||
getUser().then((e) => {
|
|
||||||
if (!e.success) console.log(1, e.message);
|
|
||||||
console.log(2, e);
|
|
||||||
return e;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Show
|
|
||||||
when={user()?.id}
|
|
||||||
fallback={
|
|
||||||
<Li
|
|
||||||
href="#"
|
|
||||||
name="Login"
|
|
||||||
action={() => signIn("discord", { callbackUrl: "/config" })}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
class="secondary"
|
|
||||||
icon={faArrowRightToBracket}
|
|
||||||
size="xl"
|
|
||||||
/>
|
|
||||||
</Li>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Li href="/config">
|
|
||||||
<div class="swap lower">
|
|
||||||
<img class="primary" src={user()?.image ?? ""} alt="User pfp" />
|
|
||||||
<FontAwesomeIcon class="secondary" icon={faGear} size="xl" />
|
|
||||||
</div>
|
|
||||||
</Li>
|
|
||||||
<Li href="#" action={() => signOut({ callbackUrl: "/" })}>
|
|
||||||
<FontAwesomeIcon icon={faArrowRightFromBracket} size="xl" />
|
|
||||||
</Li>
|
|
||||||
</Show>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NavUser;
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { drizzle } from "drizzle-orm/postgres-js";
|
import { drizzle } from "drizzle-orm/postgres-js"
|
||||||
import postgres from "postgres";
|
import postgres from "postgres"
|
||||||
import * as schema from "./schema";
|
import * as schema from "./schema"
|
||||||
|
|
||||||
const queryClient = postgres(import.meta.env.VITE_DATABASE_URL ?? "");
|
const queryClient = postgres(process.env.DATABASE_URL ?? "")
|
||||||
const db = drizzle(queryClient, {
|
const db = drizzle(queryClient, {
|
||||||
schema,
|
schema,
|
||||||
});
|
})
|
||||||
|
|
||||||
export default db;
|
export default db
|
||||||
|
|
|
@ -12,22 +12,23 @@ import {
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
export const users = pgTable("user", {
|
export const users = pgTable("user", {
|
||||||
id: text("id").notNull().primaryKey(),
|
id: text("id").primaryKey(),
|
||||||
name: text("name"),
|
name: text("name"),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
emailVerified: timestamp("emailVerified", { mode: "date" }),
|
emailVerified: timestamp("email_verified", { mode: "date" }),
|
||||||
image: text("image"),
|
image: text("image"),
|
||||||
|
createdAt: timestamp("created_at").defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const accounts = pgTable(
|
export const accounts = pgTable(
|
||||||
"account",
|
"account",
|
||||||
{
|
{
|
||||||
userId: text("userId")
|
userId: text("user_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
||||||
provider: text("provider").notNull(),
|
provider: text("provider").notNull(),
|
||||||
providerAccountId: text("providerAccountId").notNull(),
|
providerAccountId: text("provider_account_id").notNull(),
|
||||||
refresh_token: text("refresh_token"),
|
refresh_token: text("refresh_token"),
|
||||||
access_token: text("access_token"),
|
access_token: text("access_token"),
|
||||||
expires_at: integer("expires_at"),
|
expires_at: integer("expires_at"),
|
||||||
|
@ -44,15 +45,15 @@ export const accounts = pgTable(
|
||||||
);
|
);
|
||||||
|
|
||||||
export const sessions = pgTable("session", {
|
export const sessions = pgTable("session", {
|
||||||
sessionToken: text("sessionToken").notNull().primaryKey(),
|
sessionToken: text("session_token").primaryKey(),
|
||||||
userId: text("userId")
|
userId: text("user_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const verificationTokens = pgTable(
|
export const verificationTokens = pgTable(
|
||||||
"verificationToken",
|
"verification_token",
|
||||||
{
|
{
|
||||||
identifier: text("identifier").notNull(),
|
identifier: text("identifier").notNull(),
|
||||||
token: text("token").notNull(),
|
token: text("token").notNull(),
|
||||||
|
@ -65,14 +66,14 @@ export const verificationTokens = pgTable(
|
||||||
|
|
||||||
export const matchPlannings = pgTable("match_planning", {
|
export const matchPlannings = pgTable("match_planning", {
|
||||||
id: serial("id").primaryKey(),
|
id: serial("id").primaryKey(),
|
||||||
channelId: varchar("channel_id", { length: 20 }).notNull(),
|
channelId: varchar("channel_id", { length: 19 }).notNull(),
|
||||||
matchtype: varchar("match_type", { length: 50 }).notNull(),
|
matchtype: varchar("matchtype", { length: 50 }).notNull(),
|
||||||
createrId: varchar("creater_id", { length: 20 }).notNull(),
|
createrId: varchar("creater_id", { length: 19 }).notNull(),
|
||||||
roleId: varchar("role_id", { length: 20 }).notNull(),
|
roleId: varchar("role_id", { length: 19 }).notNull(),
|
||||||
opponentName: varchar("opponent_name", { length: 100 }).notNull(),
|
opponentName: varchar("opponent_name", { length: 100 }).notNull(),
|
||||||
messageId: varchar("message_id", { length: 20 }).notNull(),
|
messageId: varchar("message_id", { length: 19 }).notNull(),
|
||||||
ts: timestamp("ts").notNull(),
|
plannedFor: timestamp("planned_for", { precision: 3 }).notNull(),
|
||||||
guildId: varchar("guild_id", { length: 20 })
|
guildId: varchar("guild_id", { length: 19 })
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => guilds.id, { onDelete: "cascade" }),
|
.references(() => guilds.id, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
@ -85,8 +86,7 @@ export const matchPlanningsRelations = relations(matchPlannings, ({ one }) => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const guilds = pgTable("guild", {
|
export const guilds = pgTable("guild", {
|
||||||
id: varchar("id", { length: 20 }).primaryKey(),
|
id: varchar("id", { length: 19 }).primaryKey(),
|
||||||
timezone: text("timezone").notNull(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const guildsRelations = relations(guilds, ({ one, many }) => ({
|
export const guildsRelations = relations(guilds, ({ one, many }) => ({
|
||||||
|
@ -99,42 +99,41 @@ export const guildsRelations = relations(guilds, ({ one, many }) => ({
|
||||||
|
|
||||||
export const timePlannings = pgTable("time_planning", {
|
export const timePlannings = pgTable("time_planning", {
|
||||||
id: serial("id").primaryKey(),
|
id: serial("id").primaryKey(),
|
||||||
guildId: varchar("guild_id", { length: 20 })
|
guildId: varchar("guild_id", { length: 19 })
|
||||||
.notNull()
|
|
||||||
.unique()
|
|
||||||
.references(() => guilds.id, {
|
.references(() => guilds.id, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
})
|
||||||
channelId: varchar("channel_id", { length: 20 }).notNull(),
|
.notNull()
|
||||||
target_interval: smallint("target_interval").notNull(),
|
.unique(),
|
||||||
isAvailableRoleId: varchar("is_available_role_id", { length: 20 }),
|
channelId: varchar("channel_id", { length: 19 }).notNull(),
|
||||||
|
targetWeekday: smallint("target_weekday").notNull(),
|
||||||
|
targetHour: smallint("target_hour").notNull(),
|
||||||
|
targetMinute: smallint("target_minute").notNull(),
|
||||||
|
isAvailableRoleId: varchar("is_available_role_id", { length: 19 }),
|
||||||
wantsToBeNotifieRoledId: varchar("wants_to_be_notified_role_id", {
|
wantsToBeNotifieRoledId: varchar("wants_to_be_notified_role_id", {
|
||||||
length: 20,
|
length: 19,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const timePlanningsRelations = relations(
|
export const timePlanningsRelations = relations(
|
||||||
timePlannings,
|
timePlannings,
|
||||||
({ one, many }) => ({
|
({ one, many }) => ({
|
||||||
guild: one(guilds, {
|
guild: one(tpMessages),
|
||||||
fields: [timePlannings.guildId],
|
|
||||||
references: [guilds.id],
|
|
||||||
}),
|
|
||||||
messages: many(tpMessages),
|
messages: many(tpMessages),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const tpMessages = pgTable("tp_message", {
|
export const tpMessages = pgTable("tp_message", {
|
||||||
messageId: varchar("message_id", { length: 20 }).primaryKey(),
|
messageId: varchar("message_id", { length: 19 }).primaryKey(),
|
||||||
day: smallint("day").notNull(),
|
day: smallint("day").notNull(),
|
||||||
planId: integer("plan_id")
|
planId: varchar("plan_id", { length: 19 })
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => timePlannings.id, { onDelete: "cascade" }),
|
.references(() => timePlannings.guildId, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const tpMessagesRelations = relations(tpMessages, ({ one }) => ({
|
export const tpMessagesRelations = relations(tpMessages, ({ one }) => ({
|
||||||
plan: one(timePlannings, {
|
timePlanning: one(timePlannings, {
|
||||||
fields: [tpMessages.planId],
|
fields: [tpMessages.messageId],
|
||||||
references: [timePlannings.id],
|
references: [timePlannings.channelId],
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { createHandler } from "@solidjs/start/entry";
|
import { createHandler, StartServer } from "@solidjs/start/server";
|
||||||
import { StartServer } from "@solidjs/start/server";
|
|
||||||
|
|
||||||
export default createHandler(() => (
|
export default createHandler(() => (
|
||||||
<StartServer
|
<StartServer
|
||||||
|
@ -8,7 +7,7 @@ export default createHandler(() => (
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/assets/favicon.ico" />
|
||||||
{assets}
|
{assets}
|
||||||
</head>
|
</head>
|
||||||
<body id="app">
|
<body id="app">
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { Title } from "@solidjs/meta";
|
import { Title } from "@solidjs/meta";
|
||||||
import { HttpStatusCode } from "@solidjs/start";
|
import { HttpStatusCode } from "@solidjs/start";
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/index.scss";
|
import "../styles/pages/index.scss";
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<Layout site="index">
|
<>
|
||||||
<Title>Not Found</Title>
|
<Title>Not Found</Title>
|
||||||
<HttpStatusCode code={404} />
|
<HttpStatusCode code={404} />
|
||||||
<section class="index">
|
<section class="index">
|
||||||
|
@ -18,6 +17,6 @@ export default function NotFound() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Layout>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,77 +1,78 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/about.scss";
|
import "../styles/pages/about.scss";
|
||||||
|
|
||||||
function about() {
|
function about() {
|
||||||
return (
|
return (
|
||||||
<Layout site="about">
|
<>
|
||||||
<h1>About</h1>
|
<div class="aboutdiv">
|
||||||
<section>
|
<h1>About</h1>
|
||||||
<h2>Why does this bot exist?</h2>
|
<section>
|
||||||
<p>
|
<h2>Why does this bot exist?</h2>
|
||||||
We had a person in our team, who sent{" "}
|
<p>
|
||||||
<a href="/assets/screenshots/oldplanningmsg.png" target="_blank">
|
We had a person in our team, who sent{" "}
|
||||||
these planning messages
|
<a href="/assets/screenshots/oldplanningmsg.png" target="_blank">
|
||||||
</a>{" "}
|
these planning messages
|
||||||
and I thought that this should be automated. Some time later the first
|
</a>{" "}
|
||||||
version of li'l Judd was born. Today the bot has more features and
|
and I thought that this should be automated. Some time later the
|
||||||
keeps getting more of them! It is designed to actually improve the
|
first version of li'l Judd was born. Today the bot has more features
|
||||||
Splatoon experience and not be the 10000th moderation and general
|
and keeps getting more of them! It is designed to actually improve
|
||||||
utility bot with the same features as all bots.
|
the Splatoon experience and not be the 10000th moderation and
|
||||||
</p>
|
general utility bot with the same features as all bots.
|
||||||
</section>
|
</p>
|
||||||
<section>
|
</section>
|
||||||
<h2>Who is behind this?</h2>
|
<section>
|
||||||
<p>
|
<h2>Who is behind this?</h2>
|
||||||
The bot is currently being developed by{" "}
|
<p>
|
||||||
<a href="/contact">moonleay</a> (hey that's me!) with occasional
|
The bot is currently being developed by{" "}
|
||||||
help from his friends!
|
<a href="/contact">moonleay</a> (hey that's me!) with
|
||||||
</p>
|
occasional help from his friends!
|
||||||
</section>
|
</p>
|
||||||
<section>
|
</section>
|
||||||
<h2>How can I trust you?</h2>
|
<section>
|
||||||
<p>
|
<h2>How can I trust you?</h2>
|
||||||
The bot only requests permissions, which are needed for it to work.
|
<p>
|
||||||
Additionally, if you want to check how the bot works under the hood,
|
The bot only requests permissions, which are needed for it to work.
|
||||||
you can
|
Additionally, if you want to check how the bot works under the hood,
|
||||||
<a href="https://git.moonleay.net/DiscordBots/lilJudd">
|
you can
|
||||||
read the code
|
<a href="https://git.moonleay.net/DiscordBots/lilJudd">
|
||||||
</a>
|
read the code
|
||||||
and if you still don't trust me, you can always host the bot yourself!
|
</a>
|
||||||
A guide on how to do that can be found in the README of the git
|
and if you still don't trust me, you can always host the bot
|
||||||
project.
|
yourself! A guide on how to do that can be found in the README of
|
||||||
</p>
|
the git project.
|
||||||
</section>
|
</p>
|
||||||
<section>
|
</section>
|
||||||
<h2>Where is my data stored?</h2>
|
<section>
|
||||||
<p>
|
<h2>Where is my data stored?</h2>
|
||||||
Your data is stored on a VPS from Contabo in Germany. The bot used to
|
<p>
|
||||||
be hosted on a server in my basement, but I moved it to a VPS, because
|
Your data is stored on a VPS from Contabo in Germany. The bot used
|
||||||
my internet connection was not stable enough.
|
to be hosted on a server in my basement, but I moved it to a VPS,
|
||||||
</p>
|
because my internet connection was not stable enough.
|
||||||
</section>
|
</p>
|
||||||
<section>
|
</section>
|
||||||
<h2>So whats in the future?</h2>
|
<section>
|
||||||
<p>
|
<h2>So whats in the future?</h2>
|
||||||
I plan on adding features, which are aimed to improve your and your
|
<p>
|
||||||
teams competitive experience! You can check out my public todo list{" "}
|
I plan on adding features, which are aimed to improve your and your
|
||||||
<a
|
teams competitive experience! You can check out my public todo list{" "}
|
||||||
href="https://todo.moonleay.net/share/OmisuzgPDdsrCAXKjGrTfYzWwqNDNclOMGJWeMsi/auth?view=kanban"
|
<a
|
||||||
target="_blank"
|
href="https://todo.moonleay.net/share/OmisuzgPDdsrCAXKjGrTfYzWwqNDNclOMGJWeMsi/auth?view=kanban"
|
||||||
>
|
target="_blank"
|
||||||
here
|
>
|
||||||
</a>
|
here
|
||||||
.
|
</a>
|
||||||
</p>
|
.
|
||||||
</section>
|
</p>
|
||||||
<section>
|
</section>
|
||||||
<h2>Hey, there is this really cool idea I have! Can you add it?</h2>
|
<section>
|
||||||
<p>
|
<h2>Hey, there is this really cool idea I have! Can you add it?</h2>
|
||||||
Just message me! I can't promise anything, but I am always open to new
|
<p>
|
||||||
ideas and improvements! You can find ways to contact me{" "}
|
Just message me! I can't promise anything, but I am always open to
|
||||||
<a href="/contact">here</a>.
|
new ideas and improvements! You can find ways to contact me{" "}
|
||||||
</p>
|
<a href="/contact">here</a>.
|
||||||
</section>
|
</p>
|
||||||
</Layout>
|
</section>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,195 +1,199 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/acknowledgements.scss";
|
import "../styles/pages/acknowledgements.scss";
|
||||||
|
|
||||||
function acknowledgements() {
|
function acknowledgements() {
|
||||||
return (
|
return (
|
||||||
<Layout site="acknowledgements">
|
<>
|
||||||
<h1>Acknowledgements</h1>
|
<div class="acknowledgements">
|
||||||
<section>
|
<h1>Acknowledgements</h1>
|
||||||
<table>
|
<section>
|
||||||
<thead>
|
<table>
|
||||||
<tr>
|
<thead>
|
||||||
<th>
|
<tr>
|
||||||
<p>tool</p>
|
<th>
|
||||||
</th>
|
<p>tool</p>
|
||||||
<th>
|
</th>
|
||||||
<p>license</p>
|
<th>
|
||||||
</th>
|
<p>license</p>
|
||||||
<th>
|
</th>
|
||||||
<p>page</p>
|
<th>
|
||||||
</th>
|
<p>page</p>
|
||||||
</tr>
|
</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
<tr>
|
<tbody>
|
||||||
<td>
|
<tr>
|
||||||
<p>Kotlin</p>
|
<td>
|
||||||
</td>
|
<p>Kotlin</p>
|
||||||
<td>
|
</td>
|
||||||
<a
|
<td>
|
||||||
href="https://kotlinlang.org/docs/faq.html#is-kotlin-free"
|
<a
|
||||||
target="_blank"
|
href="https://kotlinlang.org/docs/faq.html#is-kotlin-free"
|
||||||
>
|
target="_blank"
|
||||||
Apache license 2.0
|
>
|
||||||
</a>
|
Apache license 2.0
|
||||||
</td>
|
</a>
|
||||||
<td>
|
</td>
|
||||||
<a href="https://kotlinlang.org/" target="_blank">
|
<td>
|
||||||
website
|
<a href="https://kotlinlang.org/" target="_blank">
|
||||||
</a>
|
website
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<p>Kord</p>
|
<td>
|
||||||
</td>
|
<p>Kord</p>
|
||||||
<td>
|
</td>
|
||||||
<a
|
<td>
|
||||||
href="https://github.com/kordlib/kord/blob/main/LICENSE"
|
<a
|
||||||
target="_blank"
|
href="https://github.com/kordlib/kord/blob/main/LICENSE"
|
||||||
>
|
target="_blank"
|
||||||
MIT license
|
>
|
||||||
</a>
|
MIT license
|
||||||
</td>
|
</a>
|
||||||
<td>
|
</td>
|
||||||
<a href="https://kord.dev/" target="_blank">
|
<td>
|
||||||
website
|
<a href="https://kord.dev/" target="_blank">
|
||||||
</a>
|
website
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<p>Kord Extensions</p>
|
<td>
|
||||||
</td>
|
<p>Kord Extensions</p>
|
||||||
<td>
|
</td>
|
||||||
<a
|
<td>
|
||||||
href="https://github.com/Kord-Extensions/kord-extensions/blob/root/LICENSE"
|
<a
|
||||||
target="_blank"
|
href="https://github.com/Kord-Extensions/kord-extensions/blob/root/LICENSE"
|
||||||
>
|
target="_blank"
|
||||||
Mozilla Public License 2.0
|
>
|
||||||
</a>
|
Mozilla Public License 2.0
|
||||||
</td>
|
</a>
|
||||||
<td>
|
</td>
|
||||||
<a href="https://kordex.kotlindiscord.com/" target="_blank">
|
<td>
|
||||||
website
|
<a href="https://kordex.kotlindiscord.com/" target="_blank">
|
||||||
</a>
|
website
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<p>PostgreSQL</p>
|
<td>
|
||||||
</td>
|
<p>PostgreSQL</p>
|
||||||
<td>
|
</td>
|
||||||
<a
|
<td>
|
||||||
href="https://www.postgresql.org/about/licence/"
|
<a
|
||||||
target="_blank"
|
href="https://www.postgresql.org/about/licence/"
|
||||||
>
|
target="_blank"
|
||||||
PostgreSQL license
|
>
|
||||||
</a>
|
PostgreSQL license
|
||||||
</td>
|
</a>
|
||||||
<td>
|
</td>
|
||||||
<a href="https://www.postgresql.org/" target="_blank">
|
<td>
|
||||||
website
|
<a href="https://www.postgresql.org/" target="_blank">
|
||||||
</a>
|
website
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<p>kotlinx-coroutines-core</p>
|
<td>
|
||||||
</td>
|
<p>kotlinx-coroutines-core</p>
|
||||||
<td>
|
</td>
|
||||||
<a
|
<td>
|
||||||
href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/LICENSE.txt"
|
<a
|
||||||
target="_blank"
|
href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/LICENSE.txt"
|
||||||
>
|
target="_blank"
|
||||||
Apache license 2.0
|
>
|
||||||
</a>
|
Apache license 2.0
|
||||||
</td>
|
</a>
|
||||||
<td>
|
</td>
|
||||||
<a
|
<td>
|
||||||
href="https://github.com/Kotlin/kotlinx.coroutines"
|
<a
|
||||||
target="_blank"
|
href="https://github.com/Kotlin/kotlinx.coroutines"
|
||||||
>
|
target="_blank"
|
||||||
repo
|
>
|
||||||
</a>
|
repo
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<p>slf4j</p>
|
<td>
|
||||||
</td>
|
<p>slf4j</p>
|
||||||
<td>
|
</td>
|
||||||
<a
|
<td>
|
||||||
href="https://github.com/qos-ch/slf4j/blob/master/LICENSE.txt"
|
<a
|
||||||
target="_blank"
|
href="https://github.com/qos-ch/slf4j/blob/master/LICENSE.txt"
|
||||||
>
|
target="_blank"
|
||||||
MIT license
|
>
|
||||||
</a>
|
MIT license
|
||||||
</td>
|
</a>
|
||||||
<td>
|
</td>
|
||||||
<a href="https://www.slf4j.org/" target="_blank">
|
<td>
|
||||||
website
|
<a href="https://www.slf4j.org/" target="_blank">
|
||||||
</a>
|
website
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<p>Exposed</p>
|
<td>
|
||||||
</td>
|
<p>Exposed</p>
|
||||||
<td>
|
</td>
|
||||||
<a href="https://github.com/JetBrains/Exposed/blob/main/LICENSE.txt">
|
<td>
|
||||||
Apache license 2.0
|
<a href="https://github.com/JetBrains/Exposed/blob/main/LICENSE.txt">
|
||||||
</a>
|
Apache license 2.0
|
||||||
</td>
|
</a>
|
||||||
<td>
|
</td>
|
||||||
<a href="https://github.com/JetBrains/Exposed" target="_blank">
|
<td>
|
||||||
repo
|
<a
|
||||||
</a>
|
href="https://github.com/JetBrains/Exposed"
|
||||||
</td>
|
target="_blank"
|
||||||
</tr>
|
>
|
||||||
<tr>
|
repo
|
||||||
<td>
|
</a>
|
||||||
<p>Krontab</p>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<a
|
<td>
|
||||||
href="https://github.com/InsanusMokrassar/krontab/blob/master/LICENSE"
|
<p>Krontab</p>
|
||||||
target="_blank"
|
</td>
|
||||||
>
|
<td>
|
||||||
Apache license 2.0
|
<a
|
||||||
</a>
|
href="https://github.com/InsanusMokrassar/krontab/blob/master/LICENSE"
|
||||||
</td>
|
target="_blank"
|
||||||
<td>
|
>
|
||||||
<a
|
Apache license 2.0
|
||||||
href="https://github.com/InsanusMokrassar/krontab"
|
</a>
|
||||||
target="_blank"
|
</td>
|
||||||
>
|
<td>
|
||||||
repo
|
<a
|
||||||
</a>
|
href="https://github.com/InsanusMokrassar/krontab"
|
||||||
</td>
|
target="_blank"
|
||||||
</tr>
|
>
|
||||||
<tr>
|
repo
|
||||||
<td>
|
</a>
|
||||||
<p>Splatoon3.ink</p>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<a
|
<td>
|
||||||
href="https://github.com/misenhower/splatoon3.ink/blob/main/license.md"
|
<p>Splatoon3.ink</p>
|
||||||
target="_blank"
|
</td>
|
||||||
>
|
<td>
|
||||||
MIT License
|
<a
|
||||||
</a>
|
href="https://github.com/misenhower/splatoon3.ink/blob/main/license.md"
|
||||||
</td>
|
target="_blank"
|
||||||
<td>
|
>
|
||||||
<a href="https://splatoon3.ink/" target="_blank">
|
MIT License
|
||||||
website
|
</a>
|
||||||
</a>
|
</td>
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
<a href="https://splatoon3.ink/" target="_blank">
|
||||||
</tbody>
|
website
|
||||||
</table>
|
</a>
|
||||||
</section>
|
</td>
|
||||||
</Layout>
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
57
src/routes/config.tsx
Normal file
57
src/routes/config.tsx
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import "../styles/pages/config.scss";
|
||||||
|
|
||||||
|
function config() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3 class={"centered"}>Configure li'l Judd in</h3>
|
||||||
|
<div class={"config"}>
|
||||||
|
<div>
|
||||||
|
<div class={"horizontal centered"}>
|
||||||
|
<img class={"guildpfp"} src="https://cdn.discordapp.com/icons/1040502664506646548/bb5a51c4659cf47bdd942bb11e974da7.webp?size=240" alt="Server pfp" />
|
||||||
|
<h1>li'l Judds home base</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>Timezone</h2>
|
||||||
|
<p>Set the timezone for your server.</p>
|
||||||
|
<label>
|
||||||
|
<select>
|
||||||
|
<option value="-1">UTC-1:00</option>
|
||||||
|
<option value="0">UTC+0:00</option>
|
||||||
|
<option value="1">UTC+1:00</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</article>
|
||||||
|
<section>
|
||||||
|
<div class={"centered"}>
|
||||||
|
<h2>Features</h2>
|
||||||
|
<p>Configure the features of the bot</p>
|
||||||
|
</div>
|
||||||
|
<article>
|
||||||
|
<div class={"horizontal"}>
|
||||||
|
<h3>Time Planning</h3>
|
||||||
|
<input type="checkbox" id="time planning" />
|
||||||
|
</div>
|
||||||
|
<label class={"horizontal"}>
|
||||||
|
<p class={"marg_right_5px"}>Target channel:</p>
|
||||||
|
<select>
|
||||||
|
<option value="id">Channel 1</option>
|
||||||
|
<option value="id">Channel 2</option>
|
||||||
|
<option value="id">Channel 3</option>
|
||||||
|
<option value="id">Channel 4</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<div class={"horizontal"}>
|
||||||
|
<h4>Enable pingable Roles</h4>
|
||||||
|
<input type="checkbox" id="pingableroles" />
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
<button>Apply</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config;
|
|
@ -1,271 +0,0 @@
|
||||||
import { getSession } from "@auth/solid-start";
|
|
||||||
import { faToggleOff, faToggleOn } from "@fortawesome/pro-regular-svg-icons";
|
|
||||||
import { useNavigate, useParams } from "@solidjs/router";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import moment from "moment-timezone";
|
|
||||||
import createClient from "openapi-fetch";
|
|
||||||
import { Index, createEffect, createResource, createSignal } from "solid-js";
|
|
||||||
import { createStore } from "solid-js/store";
|
|
||||||
import { getRequestEvent } from "solid-js/web";
|
|
||||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
|
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import db from "~/drizzle";
|
|
||||||
import { accounts } from "~/drizzle/schema";
|
|
||||||
import { authOptions } from "~/server/auth";
|
|
||||||
import { paths } from "~/types/discord";
|
|
||||||
import "../../styles/pages/config.scss";
|
|
||||||
|
|
||||||
const guessTZ = () => Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
||||||
|
|
||||||
const initialValue = (params: ReturnType<typeof useParams>) => ({
|
|
||||||
success: null as boolean | null,
|
|
||||||
guild: {
|
|
||||||
id: params.guildId,
|
|
||||||
name: undefined as string | undefined,
|
|
||||||
icon: undefined as string | null | undefined,
|
|
||||||
},
|
|
||||||
tzNames: [guessTZ()],
|
|
||||||
});
|
|
||||||
|
|
||||||
const getPayload = async (
|
|
||||||
id: string,
|
|
||||||
): Promise<
|
|
||||||
| { success: false; message: string }
|
|
||||||
| (ReturnType<typeof initialValue> & { success: true })
|
|
||||||
> => {
|
|
||||||
"use server";
|
|
||||||
const event = getRequestEvent();
|
|
||||||
if (!event) return { success: false, message: "No request event!" };
|
|
||||||
|
|
||||||
const session = await getSession(event.request, authOptions);
|
|
||||||
if (!session?.user?.id)
|
|
||||||
return { success: false, message: "No user with id!" };
|
|
||||||
|
|
||||||
const { DISCORD_ACCESS_TOKEN } = (
|
|
||||||
await db
|
|
||||||
.selectDistinct({ DISCORD_ACCESS_TOKEN: accounts.access_token })
|
|
||||||
.from(accounts)
|
|
||||||
.where(eq(accounts.userId, session.user?.id))
|
|
||||||
.limit(1)
|
|
||||||
.execute()
|
|
||||||
)[0];
|
|
||||||
if (!DISCORD_ACCESS_TOKEN)
|
|
||||||
return { success: false, message: "No discord access token!" };
|
|
||||||
|
|
||||||
// const guilds = await fetch("https://discord.com/api/users/@me/guilds", {
|
|
||||||
// headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
|
|
||||||
// }).then((res) => res.json());
|
|
||||||
const { GET } = createClient<paths>({
|
|
||||||
baseUrl: "https://discord.com/api/v10",
|
|
||||||
});
|
|
||||||
const { data: guilds, error } = await GET("/users/@me/guilds", {
|
|
||||||
headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.log(error);
|
|
||||||
return { success: false, message: "Error on discord api request!" };
|
|
||||||
}
|
|
||||||
|
|
||||||
const guild = guilds?.find((e) => e.id === id);
|
|
||||||
|
|
||||||
if (!guild)
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: "User is in no such guild with requested id!",
|
|
||||||
};
|
|
||||||
if (!(parseInt(guild.permissions) & (1 << 5)))
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message:
|
|
||||||
"User is no MANAGE_GUILD permissions on this guild with requested id!",
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
guild: {
|
|
||||||
id: guild.id,
|
|
||||||
name: guild.name,
|
|
||||||
icon: guild.icon,
|
|
||||||
},
|
|
||||||
// guild: guilds
|
|
||||||
// .filter((e: any) => e.permissions & (1 << 5))
|
|
||||||
// .map((e: any) => e.name),
|
|
||||||
tzNames: moment.tz.names(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function config() {
|
|
||||||
const params = useParams();
|
|
||||||
const navigator = useNavigate();
|
|
||||||
let timezoneRef: HTMLInputElement;
|
|
||||||
let timePlanningRef: HTMLInputElement;
|
|
||||||
let pingableRolesRef: HTMLInputElement;
|
|
||||||
|
|
||||||
const [timezone, setTimezone] = createSignal(guessTZ());
|
|
||||||
const [payload] = createResource(params.guildId, async (id) => {
|
|
||||||
const payload = await getPayload(id);
|
|
||||||
|
|
||||||
if (!payload.success) {
|
|
||||||
console.log(payload.message, "No success");
|
|
||||||
navigator("/config", { replace: false });
|
|
||||||
return initialValue(params);
|
|
||||||
}
|
|
||||||
return payload;
|
|
||||||
});
|
|
||||||
const guild = () => payload()?.guild ?? initialValue(params).guild;
|
|
||||||
const tzNames = () => payload()?.tzNames ?? [];
|
|
||||||
const [config, setConfig] = createStore({
|
|
||||||
features: {
|
|
||||||
timePlanning: {
|
|
||||||
enabled: false,
|
|
||||||
pingableRoles: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createEffect(() => console.log(payload()));
|
|
||||||
createEffect(() => console.log("timezone", timezone()));
|
|
||||||
createEffect(() =>
|
|
||||||
console.log("timePlanning.enabled", config.features.timePlanning.enabled),
|
|
||||||
);
|
|
||||||
createEffect(() =>
|
|
||||||
console.log(
|
|
||||||
"timePlanning.pingableRoles",
|
|
||||||
config.features.timePlanning.pingableRoles,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
createEffect(() => (timezoneRef.value = timezone()));
|
|
||||||
createEffect(
|
|
||||||
() => (timePlanningRef.checked = config.features.timePlanning.enabled),
|
|
||||||
);
|
|
||||||
createEffect(
|
|
||||||
() =>
|
|
||||||
(pingableRolesRef.checked = config.features.timePlanning.pingableRoles),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout site="config">
|
|
||||||
<h3 class="text-center">Configure li'l Judd in</h3>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<div class="flex-row centered">
|
|
||||||
<img
|
|
||||||
class="guildpfp"
|
|
||||||
src={
|
|
||||||
guild()?.icon
|
|
||||||
? `https://cdn.discordapp.com/icons/${guild()?.id}/${guild()
|
|
||||||
?.icon}.webp?size=240`
|
|
||||||
: "https://cdn.discordapp.com/icons/1040502664506646548/bb5a51c4659cf47bdd942bb11e974da7.webp?size=240"
|
|
||||||
}
|
|
||||||
alt="Server pfp"
|
|
||||||
/>
|
|
||||||
<h1>{guild()?.name ?? "li'l Judds home base"}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Guild</h2>
|
|
||||||
<p>General settings for this guild.</p>
|
|
||||||
<div class="flex-row">
|
|
||||||
<label for="timezone">Timezone for your server:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
list="timezones"
|
|
||||||
id="timezone"
|
|
||||||
ref={timezoneRef!}
|
|
||||||
// disabled={!tzNames().find((e) => e === timezone())}
|
|
||||||
onInput={(e) => setTimezone(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<datalist id="timezones">
|
|
||||||
<Index each={tzNames()}>
|
|
||||||
{(zone) => <option value={zone()} />}
|
|
||||||
</Index>
|
|
||||||
</datalist>
|
|
||||||
|
|
||||||
<button
|
|
||||||
disabled={guessTZ() === timezone()}
|
|
||||||
title={"Detected: " + guessTZ()}
|
|
||||||
onClick={() => setTimezone(guessTZ())}
|
|
||||||
>
|
|
||||||
Auto-detect
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Features</h2>
|
|
||||||
<p>Configure the features of the bot</p>
|
|
||||||
<label for="timePlanning" class="flex-row">
|
|
||||||
<p>Time Planning </p>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={
|
|
||||||
config.features.timePlanning.enabled ? faToggleOn : faToggleOff
|
|
||||||
}
|
|
||||||
size="xl"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
hidden
|
|
||||||
type="checkbox"
|
|
||||||
id="timePlanning"
|
|
||||||
ref={timePlanningRef!}
|
|
||||||
onInput={(e) =>
|
|
||||||
setConfig("features", "timePlanning", "enabled", e.target.checked)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="sub"
|
|
||||||
classList={{ disabled: !config.features.timePlanning.enabled }}
|
|
||||||
>
|
|
||||||
<div class="flex-row">
|
|
||||||
<label>Target channel:</label>
|
|
||||||
<select value={timezone()}>
|
|
||||||
<optgroup label="--Select a Channel--">
|
|
||||||
<Index each={tzNames()}>
|
|
||||||
{(channel) => <option>{channel()}</option>}
|
|
||||||
</Index>
|
|
||||||
</optgroup>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="flex-row">
|
|
||||||
<label for="pingableRoles" class="flex-row">
|
|
||||||
<p>Enable pingable Roles:</p>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={
|
|
||||||
config.features.timePlanning.pingableRoles
|
|
||||||
? faToggleOn
|
|
||||||
: faToggleOff
|
|
||||||
}
|
|
||||||
size="xl"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
hidden
|
|
||||||
type="checkbox"
|
|
||||||
id="pingableRoles"
|
|
||||||
ref={pingableRolesRef!}
|
|
||||||
onInput={(e) =>
|
|
||||||
setConfig(
|
|
||||||
"features",
|
|
||||||
"timePlanning",
|
|
||||||
"pingableRoles",
|
|
||||||
e.target.checked,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<button>Apply</button>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default config;
|
|
|
@ -1,124 +0,0 @@
|
||||||
import { getSession } from "@auth/solid-start";
|
|
||||||
import {
|
|
||||||
faBadgeCheck,
|
|
||||||
faCircleExclamation,
|
|
||||||
faPlus,
|
|
||||||
} from "@fortawesome/pro-regular-svg-icons";
|
|
||||||
import { useNavigate } from "@solidjs/router";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import createClient from "openapi-fetch";
|
|
||||||
import { For, createResource } from "solid-js";
|
|
||||||
import { getRequestEvent } from "solid-js/web";
|
|
||||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
|
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import db from "~/drizzle";
|
|
||||||
import { accounts } from "~/drizzle/schema";
|
|
||||||
import { authOptions } from "~/server/auth";
|
|
||||||
import { paths } from "~/types/discord";
|
|
||||||
import "../../styles/pages/config.scss";
|
|
||||||
|
|
||||||
const initialValue = () => ({
|
|
||||||
success: null as boolean | null,
|
|
||||||
guilds: [] as {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
icon: string | null | undefined;
|
|
||||||
}[],
|
|
||||||
});
|
|
||||||
|
|
||||||
const getPayload = async (): Promise<
|
|
||||||
| { success: false; message: string }
|
|
||||||
| (ReturnType<typeof initialValue> & { success: true })
|
|
||||||
> => {
|
|
||||||
("use server");
|
|
||||||
const event = getRequestEvent();
|
|
||||||
if (!event) return { success: false, message: "No request event!" };
|
|
||||||
|
|
||||||
const session = await getSession(event.request, authOptions);
|
|
||||||
if (!session?.user?.id)
|
|
||||||
return { success: false, message: "No user with id!" };
|
|
||||||
|
|
||||||
const { DISCORD_ACCESS_TOKEN } = (
|
|
||||||
await db
|
|
||||||
.selectDistinct({ DISCORD_ACCESS_TOKEN: accounts.access_token })
|
|
||||||
.from(accounts)
|
|
||||||
.where(eq(accounts.userId, session.user?.id))
|
|
||||||
.limit(1)
|
|
||||||
.execute()
|
|
||||||
)[0];
|
|
||||||
if (!DISCORD_ACCESS_TOKEN)
|
|
||||||
return { success: false, message: "No discord access token!" };
|
|
||||||
|
|
||||||
const { GET } = createClient<paths>({
|
|
||||||
baseUrl: "https://discord.com/api/v10",
|
|
||||||
});
|
|
||||||
const { data: guilds, error } = await GET("/users/@me/guilds", {
|
|
||||||
headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("guilds", guilds);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.log(error);
|
|
||||||
return { success: false, message: "Error on discord api request!" };
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
guilds:
|
|
||||||
guilds
|
|
||||||
?.filter((e) => parseInt(e.permissions) & (1 << 5))
|
|
||||||
.map(({ id, name, icon }) => ({ id, name, icon })) ?? [],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function index() {
|
|
||||||
const navigator = useNavigate();
|
|
||||||
|
|
||||||
const [payload] = createResource(async () => {
|
|
||||||
const payload = await getPayload();
|
|
||||||
|
|
||||||
if (!payload.success) {
|
|
||||||
console.log(payload.message, "No success");
|
|
||||||
navigator("/", { replace: false });
|
|
||||||
return initialValue();
|
|
||||||
}
|
|
||||||
console.log("success");
|
|
||||||
return payload;
|
|
||||||
});
|
|
||||||
|
|
||||||
const icons = [faPlus, faCircleExclamation, faBadgeCheck];
|
|
||||||
const colors = [undefined, "orange", "green"];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout site="config">
|
|
||||||
<h3 class="text-center">Configure li'l Judd in</h3>
|
|
||||||
<div>
|
|
||||||
<For each={payload()?.guilds ?? []}>
|
|
||||||
{(guild, i) => (
|
|
||||||
<a href={`/config/${guild.id}`} class="flex-row centered">
|
|
||||||
<img
|
|
||||||
class="guildpfp"
|
|
||||||
src={
|
|
||||||
guild.icon
|
|
||||||
? `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.webp?size=240`
|
|
||||||
: "https://cdn.discordapp.com/icons/1040502664506646548/bb5a51c4659cf47bdd942bb11e974da7.webp?size=240"
|
|
||||||
}
|
|
||||||
alt="Server pfp"
|
|
||||||
/>
|
|
||||||
<h1>{guild.name}</h1>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
// beat={i() % 3 === 1}
|
|
||||||
color={colors[i() % 3]}
|
|
||||||
icon={icons[i() % 3]}
|
|
||||||
size="xl"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default index;
|
|
|
@ -1,25 +1,29 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/contact.scss";
|
import "../styles/pages/contact.scss";
|
||||||
|
|
||||||
function contact() {
|
function contact() {
|
||||||
return (
|
return (
|
||||||
<Layout site="contact">
|
<>
|
||||||
<h1>Contact</h1>
|
<div class="contact">
|
||||||
<section>
|
<h1>Contact</h1>
|
||||||
<a href="mailto:contact@moonleay.net" target="_blank">
|
<section class="contact">
|
||||||
<img src="/assets/icons/email.svg" alt="Email" />
|
<a href="mailto:contact@moonleay.net" target="_blank">
|
||||||
contact@moonleay.net
|
<img src="/assets/icons/email.svg" alt="Email" />
|
||||||
</a>
|
contact@moonleay.net
|
||||||
<a href="https://discord.com/users/372703841151614976" target="_blank">
|
</a>
|
||||||
<img src="/assets/icons/discord.svg" alt="Discord" />
|
<a
|
||||||
@moonleay
|
href="https://discord.com/users/372703841151614976"
|
||||||
</a>
|
target="_blank"
|
||||||
<a href="https://discord.gg/HTZRktfH4A" target="_blank">
|
>
|
||||||
<img src="/assets/icons/discord.svg" alt="discord" />
|
<img src="/assets/icons/discord.svg" alt="Discord" />
|
||||||
li'l Judd's home base
|
@moonleay
|
||||||
</a>
|
</a>
|
||||||
</section>
|
<a href="https://discord.gg/HTZRktfH4A" target="_blank">
|
||||||
</Layout>
|
<img src="/assets/icons/discord.svg" alt="discord" />
|
||||||
|
li'l Judd's home base
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,50 +1,51 @@
|
||||||
import ImageSection from "~/components/ImageSection";
|
import ImageSection from "~/components/ImageSection";
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/features.scss";
|
import "../styles/pages/features.scss";
|
||||||
|
|
||||||
function features() {
|
function features() {
|
||||||
return (
|
return (
|
||||||
<Layout site="features">
|
<>
|
||||||
<h1>Features</h1>
|
<div class="features">
|
||||||
<div class="gridlayout">
|
<h1 class="title">Features</h1>
|
||||||
<ImageSection
|
<div class="gridlayout">
|
||||||
imgUrl="/assets/screenshots/timeplanner.png"
|
<ImageSection
|
||||||
imgAlt="Screenshot of the time planning feature of li'l Judd"
|
imgUrl="/assets/screenshots/timeplanner.png"
|
||||||
title="Time Planning and Management"
|
imgAlt="Screenshot of the time planning feature of li'l Judd"
|
||||||
description="Helps you to see on which days your fellow team mates are available."
|
title="Time Planning and Management"
|
||||||
note="The bot can send these planning messages every monday at 3AM. Members can click the buttons on each message to communicate, if they have time on that day."
|
description="Helps you to see on which days your fellow team mates are available."
|
||||||
/>
|
note="The bot can send these planning messages every monday at 3AM. Members can click the buttons on each message to communicate, if they have time on that day."
|
||||||
<ImageSection
|
/>
|
||||||
imgUrl="/assets/screenshots/matchplanner.png"
|
<ImageSection
|
||||||
imgAlt="Screenshot of the match planning feature of li'l Judd"
|
imgUrl="/assets/screenshots/matchplanner.png"
|
||||||
title="Match Planner"
|
imgAlt="Screenshot of the match planning feature of li'l Judd"
|
||||||
description="Make sure that you know when your next match is and who will participate."
|
title="Match Planner"
|
||||||
note="The bot can send these planning messages, when the command /match is used. Members can click the buttons on each message to communicate, if they will participate in the match. Participating members will get a role until the match has started."
|
description="Make sure that you know when your next match is and who will participate."
|
||||||
/>
|
note="The bot can send these planning messages, when the command /match is used. Members can click the buttons on each message to communicate, if they will participate in the match. Participating members will get a role until the match has started."
|
||||||
<ImageSection
|
/>
|
||||||
imgUrl="/assets/screenshots/notifs.png"
|
<ImageSection
|
||||||
imgAlt="Screenshot of the notification feature of li'l Judd"
|
imgUrl="/assets/screenshots/notifs.png"
|
||||||
title="Notifications"
|
imgAlt="Screenshot of the notification feature of li'l Judd"
|
||||||
description="Make sure that you and your team members vote in the Time Planner."
|
title="Notifications"
|
||||||
note="The bot can add roles. The first one gets pinged, when the time planner sends the messages, the other one gets assigned to the available members of the day, so that it is possible to ping all available people."
|
description="Make sure that you and your team members vote in the Time Planner."
|
||||||
/>
|
note="The bot can add roles. The first one gets pinged, when the time planner sends the messages, the other one gets assigned to the available members of the day, so that it is possible to ping all available people."
|
||||||
<ImageSection
|
/>
|
||||||
imgUrl="/assets/screenshots/rotationstatus.png"
|
<ImageSection
|
||||||
imgAlt="Screenshot of the current x map rotation in li'l Judd's status"
|
imgUrl="/assets/screenshots/rotationstatus.png"
|
||||||
title="Rotation Status"
|
imgAlt="Screenshot of the current x map rotation in li'l Judd's status"
|
||||||
description="Li'l Judd can show you the current map rotation in his status."
|
title="Rotation Status"
|
||||||
note="The bot cycles through the current map and mode rotation. It updates every few seconds."
|
description="Li'l Judd can show you the current map rotation in his status."
|
||||||
/>
|
note="The bot cycles through the current map and mode rotation. It updates every few seconds."
|
||||||
<ImageSection
|
/>
|
||||||
span
|
<ImageSection
|
||||||
imgUrl="/assets/screenshots/unknown.png"
|
span
|
||||||
imgAlt="A Question Mark"
|
imgUrl="/assets/screenshots/unknown.png"
|
||||||
title="More to come.."
|
imgAlt="A Question Mark"
|
||||||
description="The bot is still in development. More features will be added."
|
title="More to come.."
|
||||||
note="If you have a specific feature request, you can contact me on Discord: @moonleay or email: contact at moonleay dot net"
|
description="The bot is still in development. More features will be added."
|
||||||
/>
|
note="If you have a specific feature request, you can contact me on Discord: @moonleay or email: contact at moonleay dot net"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/how-do-i.scss";
|
import "../styles/pages/how-do-i.scss";
|
||||||
|
|
||||||
function howDoI() {
|
function howDoI() {
|
||||||
return (
|
return (
|
||||||
<Layout site="how-do-i">
|
<>
|
||||||
<h1>How do I...?</h1>
|
<h1 class="hdi-title">How do I...?</h1>
|
||||||
<section>
|
<section class="hdi-section">
|
||||||
<h2>.. enable / disable certain features?</h2>
|
<h2>.. enable / disable certain features?</h2>
|
||||||
<p>
|
<p>
|
||||||
Features can be enabled and disables using the <code>/feature</code>
|
Features can be enabled and disables using the <code>/feature</code>
|
||||||
|
@ -27,7 +26,7 @@ function howDoI() {
|
||||||
{/* <p><code>/feature feature:Time Planning Feature set:Enable channel:#ich-kann-heute</code></p> */}
|
{/* <p><code>/feature feature:Time Planning Feature set:Enable channel:#ich-kann-heute</code></p> */}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section class="hdi-section">
|
||||||
<h2>.. create a match?</h2>
|
<h2>.. create a match?</h2>
|
||||||
<p>
|
<p>
|
||||||
You can create a match time using the <code>/match</code> command.
|
You can create a match time using the <code>/match</code> command.
|
||||||
|
@ -48,14 +47,14 @@ function howDoI() {
|
||||||
{/* <p><code>/match match:Ladder Match timestamp:24.12.2069 04:20 opponent:Forbidden</code></p> */}
|
{/* <p><code>/match match:Ladder Match timestamp:24.12.2069 04:20 opponent:Forbidden</code></p> */}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="note">
|
<section class="hdi-footernotesection">
|
||||||
<p>
|
<p>
|
||||||
Is something missing here?
|
Is something missing here?
|
||||||
<br />
|
<br />
|
||||||
Please <a href="/contact">contact me</a>!
|
Please <a href="/contact">contact me</a>!
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</Layout>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/imprint.scss";
|
import "../styles/pages/imprint.scss";
|
||||||
|
|
||||||
function imprint() {
|
function imprint() {
|
||||||
return (
|
return (
|
||||||
<Layout site="imprint">
|
<>
|
||||||
<section>
|
<section class="imprint">
|
||||||
<h1>Imprint</h1>
|
<h1>Imprint</h1>
|
||||||
<section>
|
<section>
|
||||||
<a href="/contact">
|
<a href="/contact">
|
||||||
|
@ -55,7 +54,7 @@ function imprint() {
|
||||||
</h5>
|
</h5>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</Layout>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/index.scss";
|
import "../styles/pages/index.scss";
|
||||||
|
|
||||||
function index() {
|
function index() {
|
||||||
return (
|
return (
|
||||||
<Layout site="index">
|
<>
|
||||||
<section>
|
<section class="index">
|
||||||
<h1>li'l Judd</h1>
|
<h1>li'l Judd</h1>
|
||||||
<h5>The competetive Splatoon Bot</h5>
|
<h5>The competetive Splatoon Bot</h5>
|
||||||
<div>
|
<div>
|
||||||
|
@ -14,7 +13,7 @@ function index() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Layout>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,177 +1,179 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/privacy-policy.scss";
|
import "../styles/pages/privacy-policy.scss";
|
||||||
|
|
||||||
function privacyPolicy() {
|
function privacyPolicy() {
|
||||||
return (
|
return (
|
||||||
<Layout site="privacyPolicy">
|
<>
|
||||||
<div>
|
<div class="privacyPolicy">
|
||||||
<h1>Privacy Policy for li'l Judd</h1>
|
<div>
|
||||||
<h4>Last updated: 2023-12-05</h4>
|
<h1>Privacy Policy for li'l Judd</h1>
|
||||||
|
<h4>Last updated: 2023-12-05</h4>
|
||||||
|
</div>
|
||||||
|
<section>
|
||||||
|
<h2>1. Introduction</h2>
|
||||||
|
<p>
|
||||||
|
Welcome to li'l Judd! This Privacy Policy explains how we
|
||||||
|
collect, use, disclose, and safeguard your personal information when
|
||||||
|
you use our Discord bot service.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>2. Data Controller</h2>
|
||||||
|
<p>
|
||||||
|
The data controller for the processing of your personal data is
|
||||||
|
moonleay.
|
||||||
|
<br />
|
||||||
|
Please note that "moonleay" is primarily used as a username and may
|
||||||
|
not directly reflect my legal or real-world identity.
|
||||||
|
<br />
|
||||||
|
You can reach me <a href="/contact">here</a>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>3. Information We Collect</h2>
|
||||||
|
<h3>3.1 Discord User Data</h3>
|
||||||
|
<p>
|
||||||
|
We may collect and process the following (personal) data related to
|
||||||
|
your Discord account & guilds:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>- Discord User ID</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- Discord username, discriminator and IDs of users</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- Guild name and ID</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- Channel names and IDs of channels with active features</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- Message IDs of the bot messages</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- Role IDs of created roles</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h3>3.2 Usage Data</h3>
|
||||||
|
<p>
|
||||||
|
We may collect information on how you interact with our bot,
|
||||||
|
including but not limited to:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>- Commands issued</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- Server and channel information</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- Timestamps of interactions</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>4. How we use your Information</h2>
|
||||||
|
<p>We process your personal data for the following purposes:</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>- To provide and maintain the bot service.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- To improve, customize, and optimize our bot.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- To respond to your requests, comments, or inquiries.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>- To comply with legal obligations.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>5. Legal Basis for Processing</h2>
|
||||||
|
<p>We process your personal data on the following legal bases:</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
- Consent: You have given your consent for the processing of
|
||||||
|
your personal data for one or more specific purposes.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
- Performance of a contract: The processing is necessary for the
|
||||||
|
performance of the agreement between you and us.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>6. Data Sharing</h2>
|
||||||
|
<p>
|
||||||
|
We do not sell, trade, or otherwise transfer your personal
|
||||||
|
information to third parties. However, we may share your information
|
||||||
|
with:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
- Third-party service providers involved in the operation and
|
||||||
|
maintenance of the bot.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>7. Data Security</h2>
|
||||||
|
<p>
|
||||||
|
We implement reasonable security measures to protect your personal
|
||||||
|
information from unauthorized access, disclosure, alteration, and
|
||||||
|
destruction.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>8. Your Rights</h2>
|
||||||
|
<p>You have the following rights regarding your personal data:</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
- Right to withdraw consent: You have the right to withdraw your
|
||||||
|
consent at any time. You can do this by contacting us at
|
||||||
|
contact@moonleay.net.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
- Right to rectification: You can request corrections to
|
||||||
|
inaccurate or incomplete information.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
- Right to erasure: You can request the deletion of your
|
||||||
|
personal data.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>9. Changes to this Privacy Policy</h2>
|
||||||
|
<p>
|
||||||
|
We may update this Privacy Policy to reflect changes in our
|
||||||
|
practices. The updated version will be posted on
|
||||||
|
https://liljudd.ink/privacy-policy.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>10. Contact Information</h2>
|
||||||
|
<p>
|
||||||
|
If you have any questions or concerns about this Privacy Policy,
|
||||||
|
please contact us at contact@moonleay.net.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<section>
|
</>
|
||||||
<h2>1. Introduction</h2>
|
|
||||||
<p>
|
|
||||||
Welcome to li'l Judd! This Privacy Policy explains how we
|
|
||||||
collect, use, disclose, and safeguard your personal information when
|
|
||||||
you use our Discord bot service.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>2. Data Controller</h2>
|
|
||||||
<p>
|
|
||||||
The data controller for the processing of your personal data is
|
|
||||||
moonleay.
|
|
||||||
<br />
|
|
||||||
Please note that "moonleay" is primarily used as a username and may
|
|
||||||
not directly reflect my legal or real-world identity.
|
|
||||||
<br />
|
|
||||||
You can reach me <a href="/contact">here</a>.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>3. Information We Collect</h2>
|
|
||||||
<h3>3.1 Discord User Data</h3>
|
|
||||||
<p>
|
|
||||||
We may collect and process the following (personal) data related to
|
|
||||||
your Discord account & guilds:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<p>- Discord User ID</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- Discord username, discriminator and IDs of users</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- Guild name and ID</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- Channel names and IDs of channels with active features</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- Message IDs of the bot messages</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- Role IDs of created roles</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h3>3.2 Usage Data</h3>
|
|
||||||
<p>
|
|
||||||
We may collect information on how you interact with our bot, including
|
|
||||||
but not limited to:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<p>- Commands issued</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- Server and channel information</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- Timestamps of interactions</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>4. How we use your Information</h2>
|
|
||||||
<p>We process your personal data for the following purposes:</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<p>- To provide and maintain the bot service.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- To improve, customize, and optimize our bot.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- To respond to your requests, comments, or inquiries.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>- To comply with legal obligations.</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>5. Legal Basis for Processing</h2>
|
|
||||||
<p>We process your personal data on the following legal bases:</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
- Consent: You have given your consent for the processing of your
|
|
||||||
personal data for one or more specific purposes.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
- Performance of a contract: The processing is necessary for the
|
|
||||||
performance of the agreement between you and us.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>6. Data Sharing</h2>
|
|
||||||
<p>
|
|
||||||
We do not sell, trade, or otherwise transfer your personal information
|
|
||||||
to third parties. However, we may share your information with:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
- Third-party service providers involved in the operation and
|
|
||||||
maintenance of the bot.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>7. Data Security</h2>
|
|
||||||
<p>
|
|
||||||
We implement reasonable security measures to protect your personal
|
|
||||||
information from unauthorized access, disclosure, alteration, and
|
|
||||||
destruction.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>8. Your Rights</h2>
|
|
||||||
<p>You have the following rights regarding your personal data:</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
- Right to withdraw consent: You have the right to withdraw your
|
|
||||||
consent at any time. You can do this by contacting us at
|
|
||||||
contact@moonleay.net.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
- Right to rectification: You can request corrections to
|
|
||||||
inaccurate or incomplete information.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
- Right to erasure: You can request the deletion of your personal
|
|
||||||
data.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>9. Changes to this Privacy Policy</h2>
|
|
||||||
<p>
|
|
||||||
We may update this Privacy Policy to reflect changes in our practices.
|
|
||||||
The updated version will be posted on
|
|
||||||
https://liljudd.ink/privacy-policy.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>10. Contact Information</h2>
|
|
||||||
<p>
|
|
||||||
If you have any questions or concerns about this Privacy Policy,
|
|
||||||
please contact us at contact@moonleay.net.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/stack.scss";
|
import "../styles/pages/stack.scss";
|
||||||
|
|
||||||
function stack() {
|
function stack() {
|
||||||
return (
|
return (
|
||||||
<Layout site="stack">
|
<>
|
||||||
<h1>The Stack</h1>
|
<h1 class="stack-title">The Stack</h1>
|
||||||
<section>
|
<section class="stack-section">
|
||||||
<img src="/assets/logos/kotlin.svg" alt="Kotlin 'K' logo" />
|
<img src="/assets/logos/kotlin.svg" alt="Kotlin 'K' logo" />
|
||||||
<div class="stackgrid_3 stackitm">
|
<div class="stackgrid_3 stackitm">
|
||||||
<h1>The Kotlin programming language</h1>
|
<h1>The Kotlin programming language</h1>
|
||||||
|
@ -15,14 +14,14 @@ function stack() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section class="stack-section">
|
||||||
<img src="/assets/logos/kord.png" alt="The Kord logo" />
|
<img src="/assets/logos/kord.png" alt="The Kord logo" />
|
||||||
<div class="stackgrid_3 stackitm">
|
<div class="stackgrid_3 stackitm">
|
||||||
<h1>The Kord library</h1>
|
<h1>The Kord library</h1>
|
||||||
<p>A Kotlin library for creating Discord bots. Pretty bare bones.</p>
|
<p>A Kotlin library for creating Discord bots. Pretty bare bones.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section class="stack-section">
|
||||||
<img
|
<img
|
||||||
src="/assets/logos/kordextensions.png"
|
src="/assets/logos/kordextensions.png"
|
||||||
alt="The Kord-Extensions logo"
|
alt="The Kord-Extensions logo"
|
||||||
|
@ -32,7 +31,7 @@ function stack() {
|
||||||
<p>A Kotlin library to improve the Kord experience.</p>
|
<p>A Kotlin library to improve the Kord experience.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section class="stack-section">
|
||||||
<img src="/assets/logos/pgelephant.png" alt="The PostgreSQL elephant" />
|
<img src="/assets/logos/pgelephant.png" alt="The PostgreSQL elephant" />
|
||||||
<div class="stackgrid_3 stackitm">
|
<div class="stackgrid_3 stackitm">
|
||||||
<h1>The PostgreSQL database</h1>
|
<h1>The PostgreSQL database</h1>
|
||||||
|
@ -48,7 +47,7 @@ function stack() {
|
||||||
<a href="/acknowledgements">Acknowledgements</a>.
|
<a href="/acknowledgements">Acknowledgements</a>.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</Layout>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,94 +1,96 @@
|
||||||
import Layout from "~/components/Layout";
|
|
||||||
import "../styles/pages/terms-of-service.scss";
|
import "../styles/pages/terms-of-service.scss";
|
||||||
|
|
||||||
function termsOfService() {
|
function termsOfService() {
|
||||||
return (
|
return (
|
||||||
<Layout site="termsOfService">
|
<>
|
||||||
<h1>Terms of Service</h1>
|
<div class="termsOfService">
|
||||||
<div>
|
<h1>Terms of Service</h1>
|
||||||
<h2>Usage Agreement</h2>
|
<div>
|
||||||
<p>
|
<h2>Usage Agreement</h2>
|
||||||
By inviting the bot and using its features (commands, planning system)
|
<p>
|
||||||
are you agreeing to the below mentioned Terms and Privacy Policy
|
By inviting the bot and using its features (commands, planning
|
||||||
(Policy) of the bot.
|
system) are you agreeing to the below mentioned Terms and Privacy
|
||||||
<br />
|
Policy (Policy) of the bot.
|
||||||
You acknowledge that you have the privilege to use the bot freely on
|
<br />
|
||||||
any Discord Server (Server) you share with it, that you can invite it
|
You acknowledge that you have the privilege to use the bot freely on
|
||||||
to any Server that you have "Manage Server" rights for and that this
|
any Discord Server (Server) you share with it, that you can invite
|
||||||
privilege might get revoked for you, if you're subject of breaking the
|
it to any Server that you have "Manage Server" rights for and that
|
||||||
terms and/or policy of this bot, or the{" "}
|
this privilege might get revoked for you, if you're subject of
|
||||||
<a href="https://discord.com/terms" target="_blank">
|
breaking the terms and/or policy of this bot, or the{" "}
|
||||||
Terms of Service
|
<a href="https://discord.com/terms" target="_blank">
|
||||||
</a>
|
Terms of Service
|
||||||
,{" "}
|
</a>
|
||||||
<a href="https://discord.com/privacy" target="_blank">
|
,{" "}
|
||||||
Privacy Policy
|
<a href="https://discord.com/privacy" target="_blank">
|
||||||
</a>{" "}
|
Privacy Policy
|
||||||
and/or{" "}
|
</a>{" "}
|
||||||
<a href="https://discord.com/guidelines" target="_blank">
|
and/or{" "}
|
||||||
Community Guidelines
|
<a href="https://discord.com/guidelines" target="_blank">
|
||||||
</a>{" "}
|
Community Guidelines
|
||||||
of{" "}
|
</a>{" "}
|
||||||
<a href="https://discord.com/" target="_blank">
|
of{" "}
|
||||||
Discord Inc
|
<a href="https://discord.com/" target="_blank">
|
||||||
</a>
|
Discord Inc
|
||||||
.<br />
|
</a>
|
||||||
Through Inviting the bot may it collect specific data as described in
|
.<br />
|
||||||
its Policy.
|
Through Inviting the bot may it collect specific data as described
|
||||||
<br />
|
in its Policy.
|
||||||
The intended usage of this data is for core functionalities of the bot
|
<br />
|
||||||
such as command handling, guild-specific settings and the
|
The intended usage of this data is for core functionalities of the
|
||||||
time-planning system.
|
bot such as command handling, guild-specific settings and the
|
||||||
<br />
|
time-planning system.
|
||||||
</p>
|
<br />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Intended Age</h2>
|
||||||
|
<p>
|
||||||
|
The bot may not be used by individuals under the minimal age
|
||||||
|
described in Discord's Terms of Service.
|
||||||
|
<br />
|
||||||
|
Doing so will be seen as a violation of these terms and will result
|
||||||
|
in a removal of the bot from any servers you own.
|
||||||
|
<br />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Affiliation</h2>
|
||||||
|
<p>
|
||||||
|
The Bot is not affiliated with, supported or made by Discord Inc.
|
||||||
|
<br />
|
||||||
|
Any direct connection to Discord or any of its Trademark objects is
|
||||||
|
purely coincidental. We do not claim to have the copyright ownership
|
||||||
|
of any of Discord's assets, trademarks or other intellectual
|
||||||
|
property.
|
||||||
|
<br />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Liability</h2>
|
||||||
|
<p>
|
||||||
|
The owner of the bot may not be made liable for individuals breaking
|
||||||
|
these Terms at any given time.
|
||||||
|
<br />
|
||||||
|
He has faith in the end users being truthfull about their
|
||||||
|
information and not misusing this bot or the services of Discord Inc
|
||||||
|
in a malicious way.
|
||||||
|
<br />
|
||||||
|
We reserve the right to update these terms at our own discretion,
|
||||||
|
giving you a 1-Week (7 days) period to opt out of these terms if
|
||||||
|
you're not agreeing with the new changes. You may opt out by
|
||||||
|
Removing the bot from any Server you have the rights for.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Contact</h2>
|
||||||
|
<p>
|
||||||
|
People may get in contact through e-mail at contact@moonleay.net, or
|
||||||
|
through the official Support Discord of the Bot. Other ways of
|
||||||
|
support may be provided but aren't guaranteed.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</>
|
||||||
<h2>Intended Age</h2>
|
|
||||||
<p>
|
|
||||||
The bot may not be used by individuals under the minimal age described
|
|
||||||
in Discord's Terms of Service.
|
|
||||||
<br />
|
|
||||||
Doing so will be seen as a violation of these terms and will result in
|
|
||||||
a removal of the bot from any servers you own.
|
|
||||||
<br />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2>Affiliation</h2>
|
|
||||||
<p>
|
|
||||||
The Bot is not affiliated with, supported or made by Discord Inc.
|
|
||||||
<br />
|
|
||||||
Any direct connection to Discord or any of its Trademark objects is
|
|
||||||
purely coincidental. We do not claim to have the copyright ownership
|
|
||||||
of any of Discord's assets, trademarks or other intellectual property.
|
|
||||||
<br />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2>Liability</h2>
|
|
||||||
<p>
|
|
||||||
The owner of the bot may not be made liable for individuals breaking
|
|
||||||
these Terms at any given time.
|
|
||||||
<br />
|
|
||||||
He has faith in the end users being truthfull about their information
|
|
||||||
and not misusing this bot or the services of Discord Inc in a
|
|
||||||
malicious way.
|
|
||||||
<br />
|
|
||||||
We reserve the right to update these terms at our own discretion,
|
|
||||||
giving you a 1-Week (7 days) period to opt out of these terms if
|
|
||||||
you're not agreeing with the new changes. You may opt out by Removing
|
|
||||||
the bot from any Server you have the rights for.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2>Contact</h2>
|
|
||||||
<p>
|
|
||||||
People may get in contact through e-mail at contact@moonleay.net, or
|
|
||||||
through the official Support Discord of the Bot. Other ways of support
|
|
||||||
may be provided but aren't guaranteed.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,23 +7,15 @@ export const authOptions: SolidAuthConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
...Discord({
|
...Discord({
|
||||||
clientId: import.meta.env.VITE_DISCORD_CLIENT_ID,
|
clientId: process.env.DISCORD_CLIENT_ID,
|
||||||
clientSecret: import.meta.env.VITE_DISCORD_CLIENT_SECRET,
|
clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
||||||
}),
|
}),
|
||||||
authorization:
|
authorization:
|
||||||
"https://discord.com/api/oauth2/authorize?scope=identify+email+guilds+guilds.members.read",
|
"https://discord.com/api/oauth2/authorize?scope=identify+email+guilds+guilds.members.read",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
adapter: DrizzleAdapter(db),
|
adapter: DrizzleAdapter(db),
|
||||||
secret: import.meta.env.VITE_AUTH_SECRET,
|
secret: process.env.AUTH_SECRET,
|
||||||
callbacks: {
|
|
||||||
session: ({ session, user }) => {
|
|
||||||
if (session?.user) {
|
|
||||||
session.user.id = user.id;
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pages: {
|
pages: {
|
||||||
// signIn: "/signin",
|
// signIn: "/signin",
|
||||||
// signOut: "/signout",
|
// signOut: "/signout",
|
||||||
|
@ -31,7 +23,4 @@ export const authOptions: SolidAuthConfig = {
|
||||||
// verifyRequest: '/auth/verify-request', // (used for check email message)
|
// verifyRequest: '/auth/verify-request', // (used for check email message)
|
||||||
// newUser: '/auth/new-user' // New users will be directed here on first sign in (leave the property out if not of interest)
|
// newUser: '/auth/new-user' // New users will be directed here on first sign in (leave the property out if not of interest)
|
||||||
},
|
},
|
||||||
redirectProxyUrl: import.meta.env.DEV
|
|
||||||
? import.meta.env.VITE_AUTH_REDIRECT_PROXY_URL
|
|
||||||
: undefined,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,24 +53,3 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-row {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
&.centered {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.thick {
|
|
||||||
align-items: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.responsive {
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,73 +5,46 @@ nav {
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
ul .navElem a {
|
ul {
|
||||||
color: white;
|
display: flex;
|
||||||
padding: 0 1rem;
|
flex-direction: column;
|
||||||
vertical-align: middle;
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
&:hover {
|
.navElem {
|
||||||
color: rgb(96 59 255) !important;
|
margin: 0.5rem 1rem;
|
||||||
|
transition: 0.5s;
|
||||||
|
color: white;
|
||||||
|
|
||||||
.swap {
|
&:hover {
|
||||||
svg path {
|
color: rgb(96 59 255) !important;
|
||||||
transition: color 0.5s 0.5s;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.primary {
|
.textBx {
|
||||||
opacity: 0;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
|
||||||
.secondary {
|
#logo {
|
||||||
opacity: 1;
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 100%;
|
||||||
|
max-width: initial;
|
||||||
|
max-height: initial;
|
||||||
|
margin: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img,
|
|
||||||
.swap,
|
|
||||||
.swap svg {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 100%;
|
|
||||||
max-width: initial;
|
|
||||||
max-height: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swap {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
transition: opacity 0.5s;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
span,
|
|
||||||
> svg path {
|
|
||||||
transition: color 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lower {
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
height: 72px;
|
flex-direction: row;
|
||||||
|
padding: 0 2rem;
|
||||||
|
|
||||||
|
.navElem:last-child {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.about {
|
.aboutdiv {
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,34 +1,38 @@
|
||||||
.config {
|
.guildpfp {
|
||||||
.text-center {
|
width: 50px;
|
||||||
text-align: center;
|
height: 50px;
|
||||||
}
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.guildpfp {
|
.horizontal {
|
||||||
width: 50px;
|
display: flex;
|
||||||
height: 50px;
|
flex-direction: row;
|
||||||
border-radius: 100%;
|
h1 {
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
p, h3, h4 {
|
||||||
label {
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
section,
|
.marg_right_5px {
|
||||||
a {
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered {
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config {
|
||||||
|
article {
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
.sub {
|
|
||||||
margin-left: 10px;
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
}
|
}
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
section {
|
.contact {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
h1 {
|
.title {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 1.2rem;
|
margin-bottom: 1.2rem;
|
||||||
|
|
|
@ -1,66 +1,64 @@
|
||||||
.how-do-i {
|
.hdi-title {
|
||||||
h1 {
|
font-size: 3rem;
|
||||||
font-size: 3rem;
|
text-align: center;
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hdi-section {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 1rem auto;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 1rem;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.imgwrapper {
|
||||||
|
margin: auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
.desktop {
|
||||||
max-width: 1100px;
|
display: none;
|
||||||
margin: 1rem auto;
|
}
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 1rem;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
.imgwrapper {
|
|
||||||
margin: auto;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
|
.mobile {
|
||||||
|
display: flex;
|
||||||
|
border-radius: 5px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
@media (min-width: 600px) {
|
||||||
.desktop {
|
.desktop {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
@media (min-width: 600px) {
|
|
||||||
.desktop {
|
|
||||||
display: flex;
|
|
||||||
border-radius: 5px;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile {
|
.mobile {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note {
|
|
||||||
max-width: 1100px;
|
|
||||||
margin: 1rem auto;
|
|
||||||
width: 100%;
|
|
||||||
justify-content: space-between;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 1rem;
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
a {
|
|
||||||
color: white;
|
|
||||||
text-decoration: underline;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
transition: 0.5s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: rgb(96 59 255) !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hdi-footernotesection {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 1rem auto;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 1rem;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
text-decoration: underline;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: 0.5s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(96 59 255) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,63 +1,61 @@
|
||||||
.stack {
|
.stack-title {
|
||||||
h1 {
|
font-size: 3rem;
|
||||||
font-size: 3rem;
|
text-align: center;
|
||||||
text-align: center;
|
margin-bottom: 1.2rem;
|
||||||
margin-bottom: 1.2rem;
|
}
|
||||||
|
|
||||||
|
.stack-section {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 1rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
@media (min-width: 1150px) {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 1rem auto;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
img {
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
max-width: 80%;
|
||||||
border-radius: 4px;
|
max-height: 200px;
|
||||||
margin: 1rem;
|
width: 200px;
|
||||||
padding: 1.5rem;
|
border-radius: 5px;
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
@media (min-width: 1150px) {
|
|
||||||
max-width: 1100px;
|
|
||||||
margin: 1rem auto;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 80%;
|
|
||||||
max-height: 200px;
|
|
||||||
width: 200px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stackgrid_3 {
|
|
||||||
//grid-column: span 3;
|
|
||||||
text-align: center;
|
|
||||||
align-self: center;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.note {
|
.stackgrid_3 {
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
//grid-column: span 3;
|
||||||
border-radius: 4px;
|
|
||||||
margin: 1rem;
|
|
||||||
|
|
||||||
@media (min-width: 1150px) {
|
|
||||||
width: 1100px;
|
|
||||||
margin: 1rem auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
padding: 1rem;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
.stack-note {
|
||||||
color: white;
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
text-decoration: underline;
|
border-radius: 4px;
|
||||||
font-size: 0.9rem;
|
margin: 1rem;
|
||||||
transition: 0.5s;
|
|
||||||
|
|
||||||
&:hover {
|
@media (min-width: 1150px) {
|
||||||
color: rgb(96 59 255) !important;
|
width: 1100px;
|
||||||
}
|
margin: 1rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
text-decoration: underline;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: 0.5s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(96 59 255) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
src/types/authjs.d.ts
vendored
15
src/types/authjs.d.ts
vendored
|
@ -1,15 +0,0 @@
|
||||||
import { DefaultSession as DSession } from "@auth/core/types"
|
|
||||||
|
|
||||||
declare module "@auth/core/types" {
|
|
||||||
/**
|
|
||||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
|
||||||
*/
|
|
||||||
interface Session extends DSession {
|
|
||||||
user?: {
|
|
||||||
id: string
|
|
||||||
name?: string | null
|
|
||||||
email?: string | null
|
|
||||||
image?: string | null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
7751
src/types/discord.d.ts
vendored
7751
src/types/discord.d.ts
vendored
File diff suppressed because it is too large
Load diff
16
src/types/env.d.ts
vendored
16
src/types/env.d.ts
vendored
|
@ -1,16 +0,0 @@
|
||||||
/// <reference types="vinxi/client" />
|
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
|
||||||
readonly VITE_DISCORD_CLIENT: string;
|
|
||||||
readonly VITE_DISCORD_CLIENT_SECRET: string;
|
|
||||||
readonly VITE_DISCORD_BOT_TOKEN: string;
|
|
||||||
|
|
||||||
readonly VITE_AUTH_SECRET: string;
|
|
||||||
readonly VITE_AUTH_REDIRECT_PROXY_URL: string | undefined;
|
|
||||||
|
|
||||||
readonly VITE_DATABASE_URL: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportMeta {
|
|
||||||
readonly env: ImportMetaEnv;
|
|
||||||
}
|
|
196
src/types/liljudd.d.ts
vendored
196
src/types/liljudd.d.ts
vendored
|
@ -1,196 +0,0 @@
|
||||||
/**
|
|
||||||
* This file was auto-generated by openapi-typescript.
|
|
||||||
* Do not make direct changes to the file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export interface paths {
|
|
||||||
"/api/config/{guildId}": {
|
|
||||||
/**
|
|
||||||
* Find guild config by ID
|
|
||||||
* @description Returns a single guild config
|
|
||||||
*/
|
|
||||||
get: operations["getGuildById"];
|
|
||||||
/**
|
|
||||||
* Deletes a guild config by ID
|
|
||||||
* @description Delete a guild's config
|
|
||||||
*/
|
|
||||||
delete: operations["deleteGuildById"];
|
|
||||||
};
|
|
||||||
"/api/tp_messages/{guildId}": {
|
|
||||||
/**
|
|
||||||
* Find guild by ID for it's tp_messages
|
|
||||||
* @description Returns tp_messages for a guild
|
|
||||||
*/
|
|
||||||
get: operations["getTp_messagesOfGuildById"];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type webhooks = Record<string, never>;
|
|
||||||
|
|
||||||
export interface components {
|
|
||||||
schemas: {
|
|
||||||
guildConfig: {
|
|
||||||
/**
|
|
||||||
* Format: varchar(19)
|
|
||||||
* @example 1234567890123456789
|
|
||||||
*/
|
|
||||||
guildID?: string;
|
|
||||||
features?: {
|
|
||||||
time_planning?: {
|
|
||||||
/**
|
|
||||||
* Format: varchar(19)
|
|
||||||
* @example 1234567890123456789
|
|
||||||
*/
|
|
||||||
channelID?: string;
|
|
||||||
/** @example 0 0 1 * * * 60o 1w */
|
|
||||||
cron?: string;
|
|
||||||
/**
|
|
||||||
* Format: varchar(19)
|
|
||||||
* @example 1234567890123456789
|
|
||||||
*/
|
|
||||||
isAvailableRoleId?: string;
|
|
||||||
/**
|
|
||||||
* Format: varchar(19)
|
|
||||||
* @example 1234567890123456789
|
|
||||||
*/
|
|
||||||
wantsToBeNotifieRoledId?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
matches?: components["schemas"]["match"][];
|
|
||||||
};
|
|
||||||
match: {
|
|
||||||
/**
|
|
||||||
* Format: varchar(19)
|
|
||||||
* @example 1234567890123456789
|
|
||||||
*/
|
|
||||||
channelID?: string;
|
|
||||||
/**
|
|
||||||
* Format: varchar(50)
|
|
||||||
* @example Scrim
|
|
||||||
*/
|
|
||||||
matchType?: string;
|
|
||||||
/**
|
|
||||||
* Format: varchar(19)
|
|
||||||
* @example 1234567890123456789
|
|
||||||
*/
|
|
||||||
createrId?: string;
|
|
||||||
/**
|
|
||||||
* Format: varchar(19)
|
|
||||||
* @example 1234567890123456789
|
|
||||||
*/
|
|
||||||
roleId?: string;
|
|
||||||
/**
|
|
||||||
* Format: varchar(100)
|
|
||||||
* @example ?
|
|
||||||
*/
|
|
||||||
opponentName?: string;
|
|
||||||
/**
|
|
||||||
* Format: varchar(19)
|
|
||||||
* @example 1234567890123456789
|
|
||||||
*/
|
|
||||||
messsageId?: string;
|
|
||||||
/** @example 0 0 1 5 2 2023 60o */
|
|
||||||
cron?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
responses: never;
|
|
||||||
parameters: never;
|
|
||||||
requestBodies: never;
|
|
||||||
headers: never;
|
|
||||||
pathItems: never;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type $defs = Record<string, never>;
|
|
||||||
|
|
||||||
export type external = Record<string, never>;
|
|
||||||
|
|
||||||
export interface operations {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find guild config by ID
|
|
||||||
* @description Returns a single guild config
|
|
||||||
*/
|
|
||||||
getGuildById: {
|
|
||||||
parameters: {
|
|
||||||
path: {
|
|
||||||
/** @description ID of guild config to return */
|
|
||||||
guildId: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
responses: {
|
|
||||||
/** @description successful operation */
|
|
||||||
200: {
|
|
||||||
content: {
|
|
||||||
"application/json": components["schemas"]["guildConfig"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/** @description Invalid ID supplied */
|
|
||||||
400: {
|
|
||||||
content: never;
|
|
||||||
};
|
|
||||||
/** @description Guild not found */
|
|
||||||
404: {
|
|
||||||
content: never;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Deletes a guild config by ID
|
|
||||||
* @description Delete a guild's config
|
|
||||||
*/
|
|
||||||
deleteGuildById: {
|
|
||||||
parameters: {
|
|
||||||
path: {
|
|
||||||
/** @description ID of guild config to delete */
|
|
||||||
guildId: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
responses: {
|
|
||||||
/** @description successful operation */
|
|
||||||
204: {
|
|
||||||
content: never;
|
|
||||||
};
|
|
||||||
/** @description Invalid ID supplied */
|
|
||||||
400: {
|
|
||||||
content: never;
|
|
||||||
};
|
|
||||||
/** @description Guild not found */
|
|
||||||
404: {
|
|
||||||
content: never;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Find guild by ID for it's tp_messages
|
|
||||||
* @description Returns tp_messages for a guild
|
|
||||||
*/
|
|
||||||
getTp_messagesOfGuildById: {
|
|
||||||
parameters: {
|
|
||||||
path: {
|
|
||||||
/** @description ID of guild's tp_messages to return */
|
|
||||||
guildId: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
responses: {
|
|
||||||
/** @description successful operation */
|
|
||||||
200: {
|
|
||||||
content: {
|
|
||||||
"application/json": components["schemas"]["guildConfig"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/** @description Time planning not enabled for this guild */
|
|
||||||
204: {
|
|
||||||
content: never;
|
|
||||||
};
|
|
||||||
/** @description Invalid ID supplied */
|
|
||||||
400: {
|
|
||||||
content: never;
|
|
||||||
};
|
|
||||||
/** @description Guild not found */
|
|
||||||
404: {
|
|
||||||
content: never;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,19 +1,19 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"jsxImportSource": "solid-js",
|
"jsxImportSource": "solid-js",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"types": ["vinxi/client"],
|
"types": ["vinxi/client"],
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"~/*": ["./src/*"]
|
"~/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue