DE ▾
API-Schlüssel erhalten

Erstelle einen Discord- oder Telegram-Bot auf einer uneingeschränkten KI-API

Ein Chat-Bot ist der schnellste Weg, ein unzensiertes Modell einer breiten Öffentlichkeit zugänglich zu machen. Diese Anleitung erstellt sowohl einen discord.py-Bot als auch einen python-telegram-bot-Bot auf Basis eines gemeinsamen Moduls, das API-Aufrufe verarbeitet. So werden Verlauf pro Kanal, Ratenbegrenzung, Wiederholungen und Altersprüfung einmal geschrieben und wiederverwendet.

Aktualisiert

Wichtige Punkte

  1. Isoliere die API-Logik in einem Modul und halte jeden Plattform-Adapter klein.
  2. Halte einen separaten, begrenzten Verlauf pro Kanal oder Chat, damit sich Unterhaltungen nicht vermischen.
  3. Drossle in drei Schichten: Abklingzeit pro Nutzer, ein globaler Gleitzeitfenster unter 300 Anfragen pro Minute und eine Begrenzung der Parallelität.
  4. Erwachsene Nutzung einschränken: Nur NSFW-markierte Discord-Kanäle und explizite /adult-Bestätigung auf Telegram.

Was wir bauen

Zwei kleine Bots, ein gemeinsamer Kern. Eine Datei namens llm.py übernimmt alles, was die API betrifft: die Persona, den Ratenbegrenzer und die Wiederholungslogik. Ein Discord-Bot und ein Telegram-Bot machen dann nichts anderes, als Plattform-Ereignisse in eine Liste von Nachrichten und zurück zu übersetzen. Wenn du später eine dritte Plattform hinzufügst, schreibst du nur noch den Adapter.

Installiere die Abhängigkeiten mit pip install httpx discord.py python-telegram-bot. Exportiere API_KEY von deiner Kontoseite, sowie DISCORD_TOKEN und/oder TELEGRAM_TOKEN aus der jeweiligen Bot-Einrichtung. Beide Bots setzen Python 3.10 oder neuer voraus.

Das Anfrageformat entspricht der einfachen OpenAI-Chat-Completion-Struktur, die in der mehrsprachigen Schnellstartanleitung beschrieben ist. Hierfür wird kein SDK benötigt.

Der gemeinsame Kern: Persona, Limiter und Wiederholungen

Platziere den kritischen Teil an einer zentralen Stelle. Das untenstehende Modul hält ein Gleitfenster von Anfrage-Zeitstempeln und wartet, wenn es 240 Aufrufe pro Minute erreicht, was eine bewusste Pufferung unter den pro Schlüssel erlaubten 300 Anfragen pro Minute ist. Ein Semaphore begrenzt die Parallelität auf vier, damit ein aktiver Kanal nicht zwanzig Anfragen gleichzeitig auslöst.

# llm.py - shared by both bots
import asyncio
import os
import time
from collections import deque

import httpx

URL = "https://api.unrestrictedaiapi.com/v1/chat/completions"
PERSONA = "You are Pip, a sardonic bar-room storyteller. Keep replies under 150 words."

_stamps = deque()            # timestamps of recent calls
_gate = asyncio.Semaphore(4) # at most 4 requests in flight
_client = httpx.AsyncClient(timeout=60.0)

async def _wait_for_slot(limit=240, window=60.0):
    # Stay below the 300 requests/minute per-key limit with headroom.
    while True:
        now = time.monotonic()
        while _stamps and now - _stamps[0] > window:
            _stamps.popleft()
        if len(_stamps) < limit:
            _stamps.append(now)
            return
        await asyncio.sleep(window - (now - _stamps[0]) + 0.05)

async def chat(history):
    """history: list of {"role","content"} dicts, oldest first."""
    messages = [{"role": "system", "content": PERSONA}] + list(history)
    async with _gate:
        for attempt in range(3):
            await _wait_for_slot()
            r = await _client.post(
                URL,
                headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
                json={"model": "uncensored", "messages": messages, "max_tokens": 400},
            )
            if r.status_code in (429, 503):
                await asyncio.sleep(2 ** attempt)
                continue
            if r.status_code == 403:
                return "I can't continue with that."
            r.raise_for_status()
            return r.json()["choices"][0]["message"]["content"]
    return "The service is busy. Try again in a moment."

Schau dir die Fehlerbehandlung an. Bei einem 429 oder 503 wartest du 1, dann 2 und schließlich 4 Sekunden und versuchst es erneut. Ein 403 bedeutet, dass der Inhaltsfilter ausgelöst hat; das Bot antwortet dann mit einer neutralen Zeile statt zu wiederholen. Jeder andere Fehler wird ausgelöst, was deine Plattformbibliothek protokolliert. Wenn du ein 402 siehst, ist dein Guthaben leer oder das Testguthaben ist abgelaufen; lade das Prepaid-Guthaben auf.

Der Discord-Adapter

Discord-Bots benötigen die Aktivierung der message-content-Intention im Entwicklerportal sowie im Code, sonst kommt msg.content leer an. Der Bot antwortet nur bei Erwähnung, was verhindert, dass er jede Nachricht in einem aktiven Server liest.

# discord_bot.py
import os
import time
from collections import defaultdict, deque

import discord

from llm import chat

intents = discord.Intents.default()
intents.message_content = True      # also enable it in the developer portal
bot = discord.Client(intents=intents)

HISTORY_TURNS = 10                   # user+assistant messages kept per channel
history = defaultdict(lambda: deque(maxlen=HISTORY_TURNS))
last_use = {}                        # user id -> last request time
COOLDOWN = 5.0                       # seconds between requests per user

def chunks(text, size=1900):
    return [text[i:i + size] for i in range(0, len(text), size)] or [""]

@bot.event
async def on_message(msg: discord.Message):
    if msg.author.bot or bot.user not in msg.mentions:
        return
    # Age gate: guild text channels must be flagged NSFW. No DMs.
    if not isinstance(msg.channel, discord.TextChannel) or not msg.channel.is_nsfw():
        await msg.reply("I only chat in channels marked age-restricted (NSFW).")
        return
    now = time.monotonic()
    if now - last_use.get(msg.author.id, 0) < COOLDOWN:
        await msg.add_reaction("\u23f3")
        return
    last_use[msg.author.id] = now

    text = msg.clean_content.replace(f"@{bot.user.display_name}", "").strip()
    if not text:
        return
    h = history[msg.channel.id]
    h.append({"role": "user", "content": f"{msg.author.display_name}: {text}"})
    async with msg.channel.typing():
        answer = await chat(h)
    h.append({"role": "assistant", "content": answer})
    for part in chunks(answer):
        await msg.channel.send(part)

bot.run(os.environ["DISCORD_TOKEN"])

Der Verlauf ist ein deque mit maxlen=10, key-basiert nach Kanal-ID, sodass jeder Kanal eine eigene Unterhaltung ist und alte Nachrichten automatisch abfallen. Das Voranstellen des Anzeigenamens jedes Nutzers ermöglicht es dem Modell, Personen in einem gemeinsamen Raum zu unterscheiden. Zehn Nachrichten sind ein konservativer Standardwert; das Kontextfenster umfasst 100.000 Token, sodass du diesen Wert deutlich erhöhen kannst, bevor dies relevant wird.

Der Telegram-Adapter

python-telegram-bot v20 und höher ist asynchron, was sich natürlich mit dem gemeinsamen Kern kombinieren lässt. chat_data ist ein pro Chat gespeichertes Wörterbuch der Bibliothek, sodass Verlauf und Abklingzeit-Zustand keinen zusätzlichen Speichercode benötigen.

# telegram_bot.py  (python-telegram-bot v20+)
import os
import time
from collections import deque

from telegram import Update
from telegram.ext import (ApplicationBuilder, CommandHandler, ContextTypes,
                          MessageHandler, filters)

from llm import chat

async def adult(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.chat_data["adult"] = True
    await update.message.reply_text("Noted. Say something.")

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(
        "This bot produces adult fiction. Send /adult only if you are 18 or older."
    )

async def talk(update: Update, context: ContextTypes.DEFAULT_TYPE):
    cd = context.chat_data
    if not cd.get("adult"):
        await update.message.reply_text("Send /adult to confirm you are 18+.")
        return
    if time.monotonic() - cd.get("last", 0) < 4:
        return                                   # per-chat cooldown
    cd["last"] = time.monotonic()

    h = cd.setdefault("history", deque(maxlen=10))
    h.append({"role": "user", "content": update.message.text})
    await context.bot.send_chat_action(update.effective_chat.id, "typing")
    answer = await chat(h)
    h.append({"role": "assistant", "content": answer})
    for i in range(0, len(answer), 4000):        # Telegram limit is 4096 chars
        await update.message.reply_text(answer[i:i + 4000])

app = ApplicationBuilder().token(os.environ["TELEGRAM_TOKEN"]).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("adult", adult))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, talk))
app.run_polling()

run_polling() ist die einfachste Startmethode und benötigt keine öffentliche URL. Beachte, dass chat_data im Speicher lebt, also wird es bei einem Neustart gelöscht. Wenn der Verlauf auch Neustarts überleben soll, speichere die Deque-Inhalte selbst in einer Datei oder Datenbank.

Altersprüfung und die NSFW-Kanalregel

Diese API ist nur für Erwachsene, und alles, was du darauf aufbaust, übernimmt diese Verantwortung. Betrachte die Altersprüfung als Feature, nicht als Nachgedanke.

  • Discord: antworte nur in Guild-Kanälen, die als altersbeschränkt markiert sind. Die Prüfung channel.is_nsfw() kostet nichts und der Bot verweigert andernorts, auch in Direktnachrichten.
  • Beide: biete den Bot nicht in Bereichen an, die auf Jugendliche abzielen, und entziehe den Zugang schnell, wenn du erfährst, dass ein Nutzer unter 18 ist.

Ein Bestätigungsbefehl ist eine Schranke, kein Altersnachweis. Sexuelle Inhalte mit Minderjährigen werden von der API immer mit einem 403 blockiert, einschließlich Fiktion und Rollenspiel, und dein Bot sollte niemals versuchen, diese Antwort zu umgehen.

Ausführen und Testen ohne große Nutzerzahl

  1. Erwähne den Bot in einem Nicht-NSFW-Kanal und bestätige, dass er sich weigert. Dies beweist, dass das Tor funktioniert.
  2. Erwähne ihn in einem markierten Kanal und sende schnell zwei Nachrichten. Die zweite sollte die Sanduhr-Reaktion auslösen, was beweist, dass die Abklingzeit funktioniert.
  3. Führe eine Vier-Nachrichten-Konversation, und beziehe dich dann auf die erste Nachricht. Wenn sich der Bot erinnert, ist der Verlauf korrekt verdrahtet.
  4. Setze den API-Schlüssel vorübergehend auf einen falschen Wert und bestätige, dass der Fehler in deinen Logs erscheint, anstatt stillschweigend zu verschwinden.
  5. Füge einen langen Textblock ein und beobachte, wie die Antwort in mehrere Nachrichten unterhalb der plattformspezifischen Längengrenze aufgeteilt wird.

Wenn du bereit zum Deployen bist, reicht jeder dauerhaft laufende Prozessmanager. Die Bots halten eine langfristige Verbindung zu ihrer Plattform und stellen kurze ausgehende HTTPS-Anfragen an die API, daher benötigen sie keine eingehenden Ports und fast keinen Speicher. Füge eine Neustart-Richtlinie hinzu, damit ein Absturz nicht dazu führt, dass deine Community mit einem stillen Bot spricht.

Ratenlimits, Verlaufgröße und Kosten

Absicherung vor der Veröffentlichung

Fragen und Antworten

Kann ein einzelner API-Schlüssel mehrere Bots bedienen?

Ja, aber sie teilen sich das Ratenlimit von 300 Anfragen pro Minute für diesen Schlüssel. Verteile deine interne Drosselung daher auf die einzelnen Prozesse.

Erinnert sich der Bot an Gespräche nach einem Neustart?

In diesen Beispielen nicht; der Kontext wird nur im Arbeitsspeicher gehalten. Speichere die Inhalte der Warteschlange in einer Datei oder Datenbank, wenn du Kontinuität benötigst.

Wie schütze ich Minderjährige?

Beschränke Discord-Antworten auf altersbeschränkte Kanäle, fordere einen Bestätigungsbefehl in Telegram an und vermeide jugendorientierte Bereiche. Dies sind Kontrollen, keine Beweise, also reagiere schnell auf jede Meldung.

Was passiert, wenn das Guthaben aufgebraucht ist?

Die API gibt 402 mit dem Code no_credit zurück. Fange dies im Kernmodul ab und teile deinen Nutzern mit, dass der Bot pausiert ist.

Dein Schlüssel ist nur ein Formular entfernt

Erstelle ein Konto, kopiere den Schlüssel und ändere die Base URL. Das ist die gesamte Einrichtung.

API-Schlüssel erhalten