Compare commits
8 Commits
b71fb89832
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1baf37ae41 | |||
| efa258fdde | |||
| cab2eaa22c | |||
| 49a74cf4df | |||
| e0a99cc455 | |||
| 1214b7e98a | |||
| 369b3bf133 | |||
| d15eab2421 |
35
package.json
35
package.json
@@ -12,23 +12,23 @@
|
||||
"prisma-update": "yarn prisma db push"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/charts": "^3.30.0",
|
||||
"@chakra-ui/react": "^3.30.0",
|
||||
"@chakra-ui/charts": "^3.31.0",
|
||||
"@chakra-ui/react": "^3.31.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@escape.tech/graphql-armor": "^3.2.0",
|
||||
"@prisma/client": "^6.19.0",
|
||||
"@prisma/extension-accelerate": "^2.0.2",
|
||||
"@prisma/client": "^6.19.2",
|
||||
"@prisma/extension-accelerate": "^3.0.1",
|
||||
"@urql/next": "^2.0.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"graphql": "^16.12.0",
|
||||
"graphql-scalars": "^1.25.0",
|
||||
"graphql-yoga": "^5.18.0",
|
||||
"next": "16.1.1",
|
||||
"next": "16.1.5",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"react-icons": "^5.5.0",
|
||||
"recharts": "^3.6.0",
|
||||
"recharts": "^3.7.0",
|
||||
"rxjs": "^7.8.2",
|
||||
"urql": "^5.0.1"
|
||||
},
|
||||
@@ -36,23 +36,24 @@
|
||||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@iconify/react": "^6.0.2",
|
||||
"@types/node": "^24.10.4",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/node": "^25.0.10",
|
||||
"@types/react": "^19.2.10",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.51.0",
|
||||
"@typescript-eslint/parser": "^8.51.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^8.54.0",
|
||||
"baseline-browser-mapping": "^2.9.18",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-next": "16.1.1",
|
||||
"eslint-config-next": "16.1.5",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"prettier": "3.7.4",
|
||||
"prisma": "^6.19.0",
|
||||
"prettier": "3.8.1",
|
||||
"prisma": "^6.19.2",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.51.0"
|
||||
"typescript-eslint": "^8.54.0"
|
||||
},
|
||||
"packageManager": "yarn@4.12.0"
|
||||
}
|
||||
|
||||
57
src/app/CommandsListTable.tsx
Normal file
57
src/app/CommandsListTable.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { JSX } from "react";
|
||||
import { Checkmark, Heading, Table, VStack } from "@chakra-ui/react";
|
||||
import botCommands from "@/data/botCommands";
|
||||
|
||||
const CommandsListTable = (): JSX.Element => {
|
||||
return (
|
||||
<VStack gap={6} w={{ base: "90%", md: "100%" }}>
|
||||
<Heading as="h3" fontSize="3xl">
|
||||
{"Bot Commands"}
|
||||
</Heading>
|
||||
<Table.Root maxW="fit-content" interactive showColumnBorder>
|
||||
<Table.Caption>
|
||||
{"The bot will only respond to admins and moderators within groups."}
|
||||
</Table.Caption>
|
||||
<Table.Header>
|
||||
<Table.Row bg="cyan.emphasized">
|
||||
<Table.ColumnHeader> {"Command"}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader> {"Description"}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader> {"Private"}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader> {"Group"}</Table.ColumnHeader>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{botCommands.map((commandObj, index) => {
|
||||
const { command, description, groups } = commandObj;
|
||||
return (
|
||||
<Table.Row
|
||||
key={`${index}-${command}`}
|
||||
bg="cyan.muted"
|
||||
_hover={{ bg: "purple.emphasized" }}
|
||||
>
|
||||
<Table.Cell>{command}</Table.Cell>
|
||||
<Table.Cell>{description.replaceAll("\\", "")}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{groups ? (
|
||||
<Checkmark colorPalette="purple" checked />
|
||||
) : (
|
||||
<Checkmark colorPalette="purple" indeterminate />
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{commandObj.private ? (
|
||||
<Checkmark colorPalette="purple" checked />
|
||||
) : (
|
||||
<Checkmark colorPalette="purple" indeterminate />
|
||||
)}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommandsListTable;
|
||||
@@ -41,7 +41,7 @@ const StatsList = ({
|
||||
groups >= 0 ? (
|
||||
<SingleStatComponent
|
||||
loading={loading}
|
||||
title="Groups Bot"
|
||||
title="Groups Helped"
|
||||
error={error}
|
||||
stat={groups}
|
||||
/>
|
||||
|
||||
37
src/app/api/bot/route.ts
Normal file
37
src/app/api/bot/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { headers } from "next/headers";
|
||||
import botCommands from "@/data/botCommands";
|
||||
|
||||
const environment = process.env.NODE_ENV || "development";
|
||||
|
||||
const isValidApiKey = (apiKey: string): boolean => {
|
||||
const envApiKey =
|
||||
process.env.API_TOKEN || process.env.NEXT_PUBLIC_API_TOKEN || "";
|
||||
|
||||
return apiKey === envApiKey;
|
||||
};
|
||||
|
||||
export async function GET(/*request: Request*/) {
|
||||
const headersList = await headers();
|
||||
const apiKey = headersList.get("x-api-key");
|
||||
|
||||
if (environment === "production") {
|
||||
if (!apiKey || apiKey == null) {
|
||||
return new Response("No API Key provided", {
|
||||
status: 401,
|
||||
headers: headersList
|
||||
});
|
||||
}
|
||||
|
||||
if (apiKey !== null && !(await isValidApiKey(apiKey))) {
|
||||
return new Response("Invalid API Key", {
|
||||
status: 403,
|
||||
headers: headersList
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Response.json(botCommands, {
|
||||
status: 200,
|
||||
headers: headersList
|
||||
});
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import GetTotalGroupsQuery from "@/graphql/queries/getTotalGroups";
|
||||
import GetTotalStatsQuery from "@/graphql/queries/getTotalStats";
|
||||
import GetTodaysStatsQuery from "@/graphql/queries/getTodaysStats";
|
||||
import GetStatsRange from "@/graphql/queries/getStatsRange";
|
||||
import CommandsListTable from "./CommandsListTable";
|
||||
|
||||
export default function Home() {
|
||||
// * Total Groups * //
|
||||
@@ -95,9 +96,9 @@ export default function Home() {
|
||||
<Fragment>
|
||||
<VStack
|
||||
bg="cyan.950"
|
||||
minH="100vh"
|
||||
minH="100dvh"
|
||||
h="100%"
|
||||
py="5vh"
|
||||
py="5dvh"
|
||||
minW="fit-content"
|
||||
textAlign="center"
|
||||
>
|
||||
@@ -135,7 +136,7 @@ export default function Home() {
|
||||
textAlign="center"
|
||||
>
|
||||
{
|
||||
"A telegram bot that removes links and embeds to sites that align with the Fascist Right political agenda."
|
||||
"A Telegram bot that deletes links and embeds to sites that align with the Fascist Right political agenda or are otherwise a safety/privacy risk."
|
||||
}
|
||||
</Heading>
|
||||
</VStack>
|
||||
@@ -152,6 +153,7 @@ export default function Home() {
|
||||
</Text>
|
||||
</VStack>
|
||||
</VStack>
|
||||
<CommandsListTable />
|
||||
<VStack gap={10} w="100%">
|
||||
{totalGroups ? (
|
||||
<StatsList
|
||||
@@ -194,7 +196,7 @@ export default function Home() {
|
||||
/>
|
||||
)}
|
||||
</VStack>
|
||||
<VStack w="95%" gap="5vh">
|
||||
<VStack w="95%" gap="5dvh">
|
||||
<VStack gap={1}>
|
||||
<Heading as="h1" fontSize="3xl">{`30 Day Stats`}</Heading>
|
||||
<Text textAlign="center" fontSize="sm" color="whiteAlpha.800">
|
||||
@@ -240,7 +242,7 @@ export default function Home() {
|
||||
rgb(147, 40, 142)
|
||||
)`}
|
||||
w="100%"
|
||||
py="5vh"
|
||||
py="5dvh"
|
||||
textAlign="center"
|
||||
>
|
||||
<VStack
|
||||
@@ -263,17 +265,19 @@ export default function Home() {
|
||||
minW="fit-content"
|
||||
w={{ base: "100%", sm: "auto" }}
|
||||
bg="blackAlpha.600"
|
||||
maxW={{ base: "", sm: "62vw" }}
|
||||
maxW={{ base: "", sm: "62dvw" }}
|
||||
px={2}
|
||||
>
|
||||
{"Down with fascism! Fuck MAGA! Fuck Trump! Fuck Nazis!"}
|
||||
{
|
||||
"Down with fascism! Fuck MAGA! Fuck Trump! Fuck Nazis! Abolish ICE!"
|
||||
}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize="3xl"
|
||||
minW="fit-content"
|
||||
w={{ base: "100%", sm: "auto" }}
|
||||
bg="blackAlpha.600"
|
||||
maxW={{ base: "", sm: "62vw" }}
|
||||
maxW={{ base: "", sm: "62dvw" }}
|
||||
px={2}
|
||||
>
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ const LineChartComponent = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<Chart.Root maxH="xs" chart={chart} maxW="100vw">
|
||||
<Chart.Root maxH="xs" chart={chart} maxW="100dvw">
|
||||
<LineChart data={chart.data}>
|
||||
<CartesianGrid stroke={chart.color("border")} vertical={false} />
|
||||
<XAxis
|
||||
|
||||
56
src/data/botCommands.ts
Normal file
56
src/data/botCommands.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
// * Commands *
|
||||
|
||||
interface Commands {
|
||||
command: String;
|
||||
description: String;
|
||||
groups: boolean;
|
||||
private: boolean;
|
||||
}
|
||||
|
||||
const botInfo: Commands = {
|
||||
command: "/botInfo",
|
||||
description:
|
||||
"Lists information about the bot such as: creator, reason for my creation, purpose and goal, etc",
|
||||
groups: true,
|
||||
private: true
|
||||
};
|
||||
|
||||
const getGroupStats: Commands = {
|
||||
command: "/groupStats",
|
||||
description:
|
||||
"Displays the number of times the bot has deleted links in your group for the lifetime of the bot",
|
||||
groups: true,
|
||||
private: false
|
||||
};
|
||||
|
||||
const help: Commands = {
|
||||
command: "/help",
|
||||
description:
|
||||
"Lists information about how to setup the bot and the list of available commands",
|
||||
groups: true,
|
||||
private: true
|
||||
};
|
||||
|
||||
const registerGroup: Commands = {
|
||||
command: "/registerGroup",
|
||||
description: `Will add your group to the database, if your group doesn't exist already\\. This is used to calculate the "total groups" count on the bot stats website`,
|
||||
groups: true,
|
||||
private: true
|
||||
};
|
||||
|
||||
const botStatsSite: Commands = {
|
||||
command: "/botStats",
|
||||
description: "Provides the bot stats website link as an embed",
|
||||
groups: true,
|
||||
private: true
|
||||
};
|
||||
|
||||
const commands: Commands[] = [
|
||||
botInfo,
|
||||
getGroupStats,
|
||||
help,
|
||||
registerGroup,
|
||||
botStatsSite
|
||||
];
|
||||
|
||||
export default commands;
|
||||
@@ -243,7 +243,7 @@ export const resolvers = {
|
||||
increment: async (
|
||||
_parent: unknown,
|
||||
data: {
|
||||
link: boolean;
|
||||
link: number;
|
||||
command: boolean;
|
||||
trigger: boolean;
|
||||
mutationKey?: string;
|
||||
@@ -282,7 +282,7 @@ export const resolvers = {
|
||||
return await prisma.dailyStats.update({
|
||||
where: { createdAt: latestDailyStats.createdAt },
|
||||
data: {
|
||||
linksDeleted: { increment: link ? 1 : 0 },
|
||||
linksDeleted: { increment: link ? link : 0 },
|
||||
commandResponses: { increment: command ? 1 : 0 },
|
||||
timesTriggered: { increment: trigger ? 1 : 0 }
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const typeDefs = /* GraphQL */ `
|
||||
mutationKey: String
|
||||
): Groups!
|
||||
increment(
|
||||
link: Boolean
|
||||
link: Int
|
||||
command: Boolean
|
||||
trigger: Boolean
|
||||
mutationKey: String
|
||||
|
||||
Reference in New Issue
Block a user