From cbf9c112a4b90d9521dca38cdbb79cd9eb96a379 Mon Sep 17 00:00:00 2001 From: Fretux Date: Tue, 27 Jan 2026 15:30:09 +0100 Subject: [PATCH] Revert "Add dashboard UI and secure API endpoints" --- .env.example | 15 - README.md | 119 ++++---- dashboard/index.html | 12 - dashboard/package.json | 19 -- dashboard/src/App.jsx | 576 --------------------------------------- dashboard/src/main.jsx | 11 - dashboard/src/styles.css | 265 ------------------ dashboard/vite.config.js | 9 - dashboardServer.js | 278 ++++++++----------- db.js | 20 +- package.json | 8 +- services/permissions.js | 42 +-- 12 files changed, 181 insertions(+), 1193 deletions(-) delete mode 100644 .env.example delete mode 100644 dashboard/index.html delete mode 100644 dashboard/package.json delete mode 100644 dashboard/src/App.jsx delete mode 100644 dashboard/src/main.jsx delete mode 100644 dashboard/src/styles.css delete mode 100644 dashboard/vite.config.js diff --git a/.env.example b/.env.example deleted file mode 100644 index bf94059..0000000 --- a/.env.example +++ /dev/null @@ -1,15 +0,0 @@ -# Backend configuration -DISCORD_CLIENT_ID= -DISCORD_CLIENT_SECRET= -DISCORD_REDIRECT_URI=http://localhost:3000/auth/discord/callback -DISCORD_BOT_TOKEN= -MONGODB_URI=mongodb://localhost:27017 -SESSION_SECRET=change-me -DASHBOARD_CLIENT_URL=http://localhost:5173 -DASHBOARD_PORT=3000 -DASHBOARD_SESSION_TTL_MS=86400000 -NODE_ENV=development - -# Frontend configuration (Vite) -VITE_API_BASE_URL=http://localhost:3000 -VITE_BOT_INVITE_URL= diff --git a/README.md b/README.md index 0dcfe6d..fbaebc5 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,80 @@ -# Discord Bot +Im going to make a To-do List for the features i want to implement for my Bot. -## Dashboard setup +### **To-Do List for My Discord Bot** -This repository includes a lightweight dashboard for configuring permissions and warnings without editing JSON. The dashboard uses Discord OAuth2 and the existing MongoDB collections. +#### **Core Features** +(I will try to make these tools optional, because alot of bots already feature them) -### Environment variables +1. **Basic Commands** + - [x] Add a `/help` command to display a list of available commands. + - [x] Implement a `/ping` command to check bot responsiveness. + - [x] Add an `/about` command to describe the bot's purpose. -Copy `.env.example` and fill in the values: +2. **Role Management** + - [ ] Set up a self-assignable role system (e.g., using reaction roles). + - [x] Allow admins to assign/remove roles with commands. -```bash -cp .env.example .env -``` - -Required backend variables: - -- `DISCORD_CLIENT_ID` -- `DISCORD_CLIENT_SECRET` -- `DISCORD_REDIRECT_URI` (example: `http://localhost:3000/auth/discord/callback`) -- `DISCORD_BOT_TOKEN` -- `MONGODB_URI` -- `SESSION_SECRET` -- `DASHBOARD_CLIENT_URL` (example: `http://localhost:5173`) - -Frontend variables (Vite): - -- `VITE_API_BASE_URL` (example: `http://localhost:3000`) -- `VITE_BOT_INVITE_URL` - -> Make sure the redirect URI is added in the Discord Developer Portal OAuth2 settings. - -### Running the backend - -```bash -node dashboardServer.js -``` - -The API will run on `DASHBOARD_PORT` (default `3000`). - -### Running the dashboard frontend - -```bash -cd dashboard -npm install -npm run dev -``` - -The frontend will run on `http://localhost:5173` by default. - -### API health check - -```bash -curl http://localhost:3000/health -``` +3. **Moderation Tools** + - [x] Add commands to mute, kick, and temp_ban users. + - [ ] Create an auto-moderation feature to delete spam or inappropriate content. + - [x] Implement a warning system with a strike counter. --- -## Existing feature notes +#### **Unique Features** +1. **Currency System** + - [x] Design a virtual currency system with basic commands: + - [x] `/balance` to check currency balance. + - [x] `/earn` to earn daily or periodic rewards. + - [x] `/transfer @user [amount]` to send currency to another user. + - [ ] Link the currency system to role management: + - [ ] Allow users to purchase roles using the currency with a command (e.g., `!buyrole [role name]`). -### Server-Specific Permission Configuration +2. **Datetime Converter** + (Still in Progress, maybe i'll add visual Menu) + - [x] Create a command to convert timezones: + - [x] `/timestamp [date]` to generate Discord timestamps. + - [x] Support multiple Discord timestamp formats. + - [ ] Include a command to display server-specific or user-specific timezones. -Moderation commands support per-server permission overrides stored in MongoDB. Each guild can define which users or roles are allowed to run specific commands, plus optional admin overrides that apply to all moderation commands. +3. **Minigames** + - [ ] Implement a Quiz Game: + - [x] Add a `/quiz` command to start the game. + - [x] Provide multiple-choice or true/false questions. + - [x] Award virtual currency for correct answers. + - [ ] Add a simple number-guessing game (e.g., `!guess [number]`). + - [ ] Create a leaderboard for game winners. + +--- + +#### **Utility Tools** +1. **Search and Lookups** + - [x] Add a `/wiki [topic]` command to fetch summaries from Wikipedia. + - [x] Include a `/weather [location]` command for weather updates. + +--- + +#### **Advanced and Fun Features (very optional)** +1. **AI Chat Integration** + - [ ] Integrate GPT for conversational AI: + - [ ] Respond to mentions with intelligent replies. + - [ ] Allow users to ask general questions with `!ask [question]`. + +--- + +#### **Deployment (will happen if im completly satisfied with the bot)** +1. **Hosting** + - [ ] Set up local hosting for development. + - [ ] Deploy the bot on a cloud platform (e.g., Heroku, AWS, or Railway). + +2. **Monitoring** + - [ ] Add logging to track errors and usage statistics. + - [ ] Implement uptime monitoring to ensure the bot stays online. + +--- + +### **Server-Specific Permission Configuration** +Moderation commands support per-server permission overrides stored in MongoDB. Each guild can define which users or roles are allowed to run specific commands, plus optional admin overrides that apply to all moderation commands. This is intended for future web-based configuration. Example document stored in the `discord.guild_permissions` collection: ```json diff --git a/dashboard/index.html b/dashboard/index.html deleted file mode 100644 index 3dcf396..0000000 --- a/dashboard/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Discord Bot Dashboard - - -
- - - diff --git a/dashboard/package.json b/dashboard/package.json deleted file mode 100644 index 19ed1d3..0000000 --- a/dashboard/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "discord-bot-dashboard", - "version": "0.1.0", - "private": true, - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "react": "^18.3.1", - "react-dom": "^18.3.1" - }, - "devDependencies": { - "@vitejs/plugin-react": "^4.3.1", - "vite": "^5.4.7" - } -} diff --git a/dashboard/src/App.jsx b/dashboard/src/App.jsx deleted file mode 100644 index 6329058..0000000 --- a/dashboard/src/App.jsx +++ /dev/null @@ -1,576 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; - -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; -const BOT_INVITE_URL = import.meta.env.VITE_BOT_INVITE_URL || ''; - -const apiFetch = async (path, options = {}) => { - const response = await fetch(`${API_BASE_URL}${path}`, { - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - ...options.headers, - }, - ...options, - }); - - const contentType = response.headers.get('content-type'); - const data = contentType && contentType.includes('application/json') - ? await response.json() - : null; - - if (!response.ok) { - const error = new Error(data?.error || 'Request failed'); - error.details = data?.details; - throw error; - } - - return data; -}; - -const usePath = () => { - const [path, setPath] = useState(window.location.pathname); - - useEffect(() => { - const handler = () => setPath(window.location.pathname); - window.addEventListener('popstate', handler); - return () => window.removeEventListener('popstate', handler); - }, []); - - const navigate = useCallback((nextPath) => { - window.history.pushState({}, '', nextPath); - setPath(nextPath); - }, []); - - return { path, navigate }; -}; - -const Toast = ({ message, type, onDismiss }) => { - if (!message) return null; - return ( -
- {message} - -
- ); -}; - -const Landing = () => ( -
-

Discord Bot Dashboard

-

Manage bot permissions and warnings without editing raw JSON.

-
- {BOT_INVITE_URL ? ( - - Add bot to server - - ) : ( - Set VITE_BOT_INVITE_URL to enable the invite link. - )} - - Login with Discord - -
-
-); - -const GuildPicker = ({ onManage }) => { - const [guilds, setGuilds] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(''); - - useEffect(() => { - let ignore = false; - setLoading(true); - apiFetch('/api/guilds') - .then((data) => { - if (!ignore) { - setGuilds(data.guilds || []); - setError(''); - } - }) - .catch((err) => { - if (!ignore) { - setError(err.message); - } - }) - .finally(() => { - if (!ignore) setLoading(false); - }); - - return () => { - ignore = true; - }; - }, []); - - return ( -
-

Your Guilds

-

Choose a server you manage to update permissions and warnings.

- {loading ?

Loading guilds…

: null} - {error ?

{error}

: null} -
- {guilds.map((guild) => ( -
- {guild.icon ? ( - - ) : ( -
- )} -
-

{guild.name}

- -
-
- ))} - {!loading && guilds.length === 0 ? ( -

No manageable guilds found for this account.

- ) : null} -
-
- ); -}; - -const AdminUserList = ({ value, onChange }) => { - const [input, setInput] = useState(''); - - const addUser = () => { - const trimmed = input.trim(); - if (!trimmed) return; - if (!value.includes(trimmed)) { - onChange([...value, trimmed]); - } - setInput(''); - }; - - return ( -
-
- {value.map((userId) => ( - - {userId} - - - ))} - {value.length === 0 ? No admin users yet. : null} -
-
- setInput(event.target.value)} - placeholder="Add user ID" - /> - -
-
- ); -}; - -const CommandPermissionEditor = ({ roles, value, onChange }) => { - const [newCommand, setNewCommand] = useState(''); - const commandEntries = Object.entries(value || {}); - - const updateCommand = (commandName, nextData) => { - onChange({ - ...value, - [commandName]: { - allowedRoleIds: nextData.allowedRoleIds, - allowedUserIds: nextData.allowedUserIds, - }, - }); - }; - - const removeCommand = (commandName) => { - const updated = { ...value }; - delete updated[commandName]; - onChange(updated); - }; - - const addCommand = () => { - const trimmed = newCommand.trim(); - if (!trimmed) return; - if (!value[trimmed]) { - onChange({ - ...value, - [trimmed]: { allowedRoleIds: [], allowedUserIds: [] }, - }); - } - setNewCommand(''); - }; - - return ( -
-
- setNewCommand(event.target.value)} - placeholder="Add command name" - /> - -
- {commandEntries.length === 0 ? ( -

No command overrides yet. Empty means commands are allowed for everyone.

- ) : null} - {commandEntries.map(([commandName, entry]) => ( -
-
-

{commandName}

- -
-

- If both lists are empty, this command remains available to everyone. -

-
- -
- {roles.map((role) => ( - - ))} - {roles.length === 0 ? No roles available. : null} -
-
-
- - updateCommand(commandName, { - ...entry, - allowedUserIds: nextUsers, - })} - /> -
-
- ))} -
- ); -}; - -const PermissionsTab = ({ guildId, onToast }) => { - const [roles, setRoles] = useState([]); - const [permissions, setPermissions] = useState(null); - const [loading, setLoading] = useState(true); - const [saving, setSaving] = useState(false); - const [error, setError] = useState(''); - - useEffect(() => { - let ignore = false; - setLoading(true); - Promise.all([ - apiFetch(`/api/guilds/${guildId}/roles`), - apiFetch(`/api/guilds/${guildId}/permissions`), - ]) - .then(([rolesData, permissionsData]) => { - if (ignore) return; - setRoles(rolesData.roles || []); - setPermissions(permissionsData.permissions || {}); - setError(''); - }) - .catch((err) => { - if (!ignore) setError(err.message); - }) - .finally(() => { - if (!ignore) setLoading(false); - }); - - return () => { - ignore = true; - }; - }, [guildId]); - - const toggleRole = (roleId) => { - if (!permissions) return; - const adminRoleIds = permissions.adminRoleIds || []; - const next = adminRoleIds.includes(roleId) - ? adminRoleIds.filter((id) => id !== roleId) - : [...adminRoleIds, roleId]; - setPermissions({ ...permissions, adminRoleIds: next }); - }; - - const savePermissions = async () => { - if (!permissions) return; - setSaving(true); - try { - const response = await apiFetch(`/api/guilds/${guildId}/permissions`, { - method: 'PATCH', - body: JSON.stringify(permissions), - }); - setPermissions(response.permissions); - onToast('Permissions saved.', 'success'); - } catch (err) { - onToast(err.message, 'error'); - } finally { - setSaving(false); - } - }; - - if (loading) return

Loading permissions…

; - if (error) return

{error}

; - if (!permissions) return

Failed to load permissions.

; - - return ( -
-
- -
- {roles.map((role) => ( - - ))} - {roles.length === 0 ? No roles available. : null} -
-
- -
- - setPermissions({ ...permissions, adminUserIds: nextUsers })} - /> -
- -
- - - setPermissions({ ...permissions, commandPermissions: nextCommands }) - } - /> -
- -
- -
-
- ); -}; - -const WarningsTab = ({ guildId, onToast }) => { - const [userId, setUserId] = useState(''); - const [warnings, setWarnings] = useState([]); - const [loading, setLoading] = useState(false); - const [reason, setReason] = useState(''); - const [error, setError] = useState(''); - - const fetchWarnings = async () => { - if (!userId.trim()) return; - setLoading(true); - try { - const data = await apiFetch(`/api/guilds/${guildId}/warnings/${userId.trim()}`); - setWarnings(data.warnings || []); - setError(''); - } catch (err) { - setError(err.message); - } finally { - setLoading(false); - } - }; - - const addWarning = async () => { - if (!userId.trim()) return; - try { - const data = await apiFetch(`/api/guilds/${guildId}/warnings/${userId.trim()}`, { - method: 'POST', - body: JSON.stringify({ reason }), - }); - setWarnings(data.warnings || []); - setReason(''); - onToast('Warning added.', 'success'); - } catch (err) { - onToast(err.message, 'error'); - } - }; - - return ( -
-
- -
- setUserId(event.target.value)} - placeholder="Enter user ID" - /> - -
-
- {error ?

{error}

: null} -
- {warnings.length === 0 ? ( -

No warnings found for this user.

- ) : ( - warnings.map((warning, index) => ( -
-

{warning.reason}

-
- Moderator: {warning.moderatorId || 'Unknown'} - {warning.createdAt ? new Date(warning.createdAt).toLocaleString() : ''} -
-
- )) - )} -
- -
- -