Skip to content

Discord bot + FastAPI service for creating, validating, assigning, extending, and deleting software license keys, backed by Supabase.

Notifications You must be signed in to change notification settings

DraxonV1/LicensingBot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Professional License Management System

GitHub stars GitHub forks

Banner Terminal Showcase

Discord bot + FastAPI service for creating, validating, assigning, extending, and deleting software license keys, backed by Supabase.

What You Get

  • Discord slash commands for admins/staff
  • REST API for your external tools/applications
  • API key auth on API routes
  • In-memory license file generation for Discord delivery (no local file persistence)
  • Supabase-backed storage for license groups and license records
  • Rate limiting, request logging, and basic HTTP hardening headers

Project Layout

.
|- app.py         # Runs bot + API in separate processes
|- bot.py         # Discord bot commands
|- api.py         # FastAPI endpoints
|- database.py    # Supabase operations
|- utils.py       # Key generation/duration/status helpers
|- config.py      # Environment/config loading + validation
|- schema.sql     # Database schema
|- requirements.txt
`- logs/

Requirements

  • Python 3.9+
  • Discord bot token
  • Supabase project (URL + key)

Setup

1. Install dependencies

pip install -r requirements.txt

2. Create Supabase schema

Run schema.sql in Supabase SQL Editor.

3. Configure environment variables

Create .env in project root:

DISCORD_BOT_TOKEN=your_discord_bot_token
DISCORD_GUILD_ID=your_discord_guild_id

SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your_supabase_key

API_HOST=0.0.0.0
API_PORT=8000
API_SECRET_KEY=replace_with_long_random_secret
API_KEY_HEADER_NAME=X-API-Key
API_ALLOWED_ORIGINS=http://localhost:3000
API_ALLOWED_HOSTS=localhost,127.0.0.1

RATE_LIMIT_ENABLED=true
RATE_LIMIT_PER_MINUTE=60
LOG_LEVEL=INFO
LOG_FILE=licensing_system.log

Important:

  • API_SECRET_KEY must not be change-this-secret-key or startup will fail.
  • API_ALLOWED_ORIGINS is comma-separated.
  • API_ALLOWED_HOSTS is comma-separated.

Running

Run both bot and API

python src/app.py

Run only bot

python src/bot.py

Run only API

python src/api.py

API docs:

  • Swagger: http://localhost:8000/docs
  • ReDoc: http://localhost:8000/redoc

Discord Bot Usage (Full Examples)

All commands are slash commands.

Create license group

Command:

/create type:licensegroup name:Premium

What it does:

  • Creates group PREMIUM if it does not exist.

Create licenses and receive file

Command:

/create type:license group:Premium amount:10 duration:30d

What it does:

  • Creates 10 licenses in PREMIUM
  • Expiration: now + 30 days
  • Sends 30d_licenses.txt as Discord attachment
  • File is generated in memory and sent immediately (not written to disk)

Supported duration formats:

  • 7d, 30d, 90d
  • 1m, 6m
  • 1y

Check license

Command:

/check key:OP-PREMIUM-TI-A8K9M2X5L7-C

What it returns:

  • status (valid/invalid)
  • active vs expired
  • created/expires timestamps
  • time remaining
  • assignment (if present)

List licenses in group

Command:

/list group:Premium

What it returns:

  • up to 20 entries in embed
  • active/expired counts
  • total count

Extend license

Command:

/extend key:OP-PREMIUM-TI-A8K9M2X5L7-C duration:30d

What it does:

  • Adds duration to license expiration

Assign license

Command:

/assign key:OP-PREMIUM-TI-A8K9M2X5L7-C user:john_doe

What it does:

  • Sets assigned_to = john_doe

Delete license

Command:

/delete type:license key:OP-PREMIUM-TI-A8K9M2X5L7-C

Delete license group (and all group licenses)

Command:

/delete type:licensegroup group:Premium

Bot info / stats

Command:

/botinfo

API Usage (Full Examples)

Base URL:

http://localhost:8000

Authentication:

  • Send API key in header on API calls:
X-API-Key: <your API_SECRET_KEY>

Notes:

  • /docs, /redoc, /openapi.json are public by default.
  • Main API routes require key.

Use this shell variable first:

API_KEY="replace_with_your_secret"
BASE_URL="http://localhost:8000"

1. Create licenses

curl -X POST "$BASE_URL/create/license/" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -d '{
    "group": "Premium",
    "amount": 5,
    "duration": "30d"
  }'

2. Check license

curl -X GET "$BASE_URL/check/license/?key=OP-PREMIUM-TI-A8K9M2X5L7-C" \
  -H "X-API-Key: $API_KEY"

3. Extend license

curl -X POST "$BASE_URL/extend/license/" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -d '{
    "key": "OP-PREMIUM-TI-A8K9M2X5L7-C",
    "duration": "30d"
  }'

4. Assign license

curl -X POST "$BASE_URL/assign/license/" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -d '{
    "key": "OP-PREMIUM-TI-A8K9M2X5L7-C",
    "user": "john_doe"
  }'

5. List licenses in group

curl -X GET "$BASE_URL/list/licenses/?group=Premium" \
  -H "X-API-Key: $API_KEY"

6. Delete license

curl -X DELETE "$BASE_URL/delete/license/?key=OP-PREMIUM-TI-A8K9M2X5L7-C" \
  -H "X-API-Key: $API_KEY"

7. Get statistics

curl -X GET "$BASE_URL/statistics/" \
  -H "X-API-Key: $API_KEY"

Integrating License Checks Into Your Tool

Recommended pattern:

  1. Validate local input format quickly.
  2. Call /check/license/ server-side (never expose API secret in frontend/public client).
  3. Fail closed on API/network errors.
  4. Cache short-term validation results (for example 30-120 seconds) if needed.

Python integration example

import os
import requests

BASE_URL = os.getenv("LICENSE_API_BASE", "http://localhost:8000")
API_KEY = os.getenv("LICENSE_API_KEY")

def check_license_or_raise(license_key: str) -> dict:
    if not API_KEY:
        raise RuntimeError("Missing LICENSE_API_KEY")

    response = requests.get(
        f"{BASE_URL}/check/license/",
        params={"key": license_key},
        headers={"X-API-Key": API_KEY},
        timeout=5,
    )

    if response.status_code == 404:
        raise ValueError("License not found")
    if response.status_code == 401:
        raise PermissionError("License API auth failed")
    if response.status_code >= 500:
        raise RuntimeError("License API server error")
    response.raise_for_status()

    data = response.json()
    if data.get("status") != "valid" or data.get("is_expired", True):
        raise ValueError("License is invalid or expired")

    return data

Node.js integration example

const BASE_URL = process.env.LICENSE_API_BASE || "http://localhost:8000";
const API_KEY = process.env.LICENSE_API_KEY;

async function assertLicenseValid(licenseKey) {
  if (!API_KEY) throw new Error("Missing LICENSE_API_KEY");

  const url = new URL("/check/license/", BASE_URL);
  url.searchParams.set("key", licenseKey);

  const res = await fetch(url, {
    method: "GET",
    headers: { "X-API-Key": API_KEY },
  });

  if (res.status === 404) throw new Error("License not found");
  if (res.status === 401) throw new Error("Unauthorized to license API");
  if (!res.ok) throw new Error(`License API error: ${res.status}`);

  const data = await res.json();
  if (data.status !== "valid" || data.is_expired) {
    throw new Error("Invalid or expired license");
  }

  return data;
}

Integration checklist

  • Keep LICENSE_API_KEY on backend only
  • Add request timeout and retry policy
  • Block access when API says invalid/expired
  • Log failed checks with request IDs/user IDs
  • Optionally store assigned_to and compare against current user/device

Security Notes

  • Rotate API_SECRET_KEY regularly.
  • Restrict API_ALLOWED_ORIGINS and API_ALLOWED_HOSTS in production.
  • Do not expose API key in browser/mobile clients.
  • Keep Discord bot permissions minimal.
  • Use HTTPS and reverse proxy (Nginx/Caddy/Cloudflare) in production.

Troubleshooting

API returns 401

  • Confirm X-API-Key header is present.
  • Confirm key equals API_SECRET_KEY from server env.
  • Check that there are no leading/trailing spaces in .env.

API startup fails due to config

  • Ensure all required env variables are set.
  • Ensure API_SECRET_KEY is not default placeholder.

Discord commands not visible

  • Verify bot has applications.commands scope.
  • If using global commands, allow time for propagation.
  • For faster updates, set DISCORD_GUILD_ID.

Supabase operation errors

  • Re-run schema.sql.
  • Validate SUPABASE_URL and SUPABASE_KEY.
  • Check logs in logs/.

Fork and Contribute

1. Fork and clone

  1. Fork the repository on GitHub.
  2. Clone your fork:
git clone https://github.com/DraxonV1/LicensingBot.git
cd LicensingBot

2. Add upstream remote

git remote add upstream https://github.com/DraxonV1/LicensingBot.git
git fetch upstream

3. Create feature branch

git checkout -b feature/<short-description>

4. Implement + verify

pip install -r requirements.txt
py -3 -m py_compile src\\api.py src\\bot.py src\\config.py src\\utils.py src\\database.py src\\app.py src\\terminal_logger.py

5. Commit and push

git add .
git commit -m "feat: <your change summary>"
git push origin feature/<short-description>

6. Open pull request

  1. Open PR from your fork branch to upstream/main.
  2. Include:
  • what changed
  • why it changed
  • how you tested
  • any breaking changes

Contribution guidelines

  • Keep changes focused and small where possible.
  • Update README and examples when behavior changes.
  • Preserve existing API shape unless intentionally versioning.
  • Add defensive error handling for external calls.

License

MIT (or project-defined license in repository settings).

About

Discord bot + FastAPI service for creating, validating, assigning, extending, and deleting software license keys, backed by Supabase.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published