Открытый Discord-бот с моделью без цензуры
Открытый Discord-бот, общающийся через LLM, занимает около восьмидесяти строк на Python. Это руководство даёт вам код, лимиты, которые нужно соблюдать, и стоимость ответа с использованием API, совместимого с OpenAI, и модели, которая не отказывает в законных запросах от взрослых.

Ключевые моменты
- discord.py и OpenAI SDK достаточно; укажите SDK на https://api.openrouternsfw.com/v1 и используйте модель uncensored.
- Включите доступ к содержимому сообщений в портале разработчика Discord, иначе бот будет видеть пустые сообщения.
- Используйте семафор со значением ниже 8, чтобы не превысить лимит параллельных запросов, и храните контент для взрослых в каналах с пометкой «18+».
- Ответ с 1500 входными и 300 выходными токенами стоит $0,000375, то есть около $3,75 за 10 000 ответов.
Карта не нужна. После регистрации получите $0,50 бесплатного пробного баланса в один клик (один раз на человека).
Получить API-ключЧто вы создаёте
Бот ниже отвечает, когда его упоминают. Он хранит краткую историю для каждого канала, отправляет её в конечную точку chat completions и публикует ответ. Это чистый Python, совместимый с лицензией MIT, без базы данных. Вы можете прочитать весь код за один присест, что и является целью открытого проекта: кто-то другой может сделать форк и понять его.
В основе лежит одна текстовая модель с открытыми весами, запущенная независимым провайдером и настроенная на ответы без отказов по контенту для законного использования взрослыми. Это подходит для сообществ Discord, построенных вокруг художественной литературы, ролевых игр или разговоров для взрослых. С самого начала действуют два правила. Во-первых, API запрещает сексуальный контент с участием несовершеннолетних, и его не должно быть в дизайне вашего бота. Во-вторых, у Discord есть свои правила и настройки каналов с возрастным ограничением, и вы несёте ответственность за их соблюдение. Ограничьте контент для взрослых каналами с соответствующей пометкой и серверами, где участники — взрослые.
Настройка Discord и ваших ключей
- Откройте портал разработчика Discord и создайте приложение, затем добавьте в него бота.
- В настройках бота включите доступ к содержимому сообщений. Без него бот будет получать сообщения с пустым текстом.
- Скопируйте токен бота. Относитесь к нему как к паролю.
- Используйте генератор URL OAuth2, чтобы пригласить бота с правами на чтение и отправку сообщений в выбранных вами каналах.
- Получите API-ключ на странице ключей. Новые пользователи могут получить $0,50 пробного баланса в один клик, без ввода данных карты, срок действия — 7 дней.
Сохраните оба секрета как переменные окружения, никогда не храните их в репозитории. Для проекта с открытым исходным кодом это особенно важно, потому что код является публичным.
pip install discord.py openai python-dotenv
printf 'DISCORD_TOKEN=your-discord-token
API_KEY=your-api-key
' > .env
echo ".env" >> .gitignore
Код бота
Сохраните это как bot.py. Комментарии объясняют решения, которые неочевидны.
import asyncio
import os
from collections import defaultdict, deque
import discord
from dotenv import load_dotenv
from openai import AsyncOpenAI
load_dotenv()
ai = AsyncOpenAI(
base_url="https://api.openrouternsfw.com/v1",
api_key=os.environ["API_KEY"],
max_retries=4, # the SDK retries 429 and honors Retry-After
)
SYSTEM = (
"You are Vex, a sharp-tongued storyteller in a Discord server for adults. "
"Stay in character, keep replies under 150 words, and never write "
"anything sexual involving minors."
)
intents = discord.Intents.default()
intents.message_content = True
bot = discord.Client(intents=intents)
slots = asyncio.Semaphore(6) # stay under 8 concurrent
history = defaultdict(lambda: deque(maxlen=12)) # last 12 turns per channel
async def ask(channel_id, user_text):
turns = history[channel_id]
turns.append({"role": "user", "content": user_text})
messages = [{"role": "system", "content": SYSTEM}] + list(turns)
async with slots:
resp = await ai.chat.completions.create(
model="uncensored",
messages=messages,
temperature=0.9,
max_tokens=400,
)
reply = resp.choices[0].message.content or ""
turns.append({"role": "assistant", "content": reply})
return reply
@bot.event
async def on_ready():
print(f"Logged in as {bot.user}")
@bot.event
async def on_message(message):
if message.author.bot or bot.user not in message.mentions:
return
text = message.clean_content.replace(f"@{bot.user.display_name}", "").strip()
if not text:
return
async with message.channel.typing():
try:
reply = await ask(message.channel.id, f"{message.author.display_name}: {text}")
except Exception as exc:
print("API error:", exc)
await message.reply("The model is busy. Try again in a few seconds.")
return
for i in range(0, len(reply), 1900): # Discord caps messages at 2000 chars
await message.reply(reply[i:i + 1900])
bot.run(os.environ["DISCORD_TOKEN"])
Запустите его с помощью python bot.py, затем упомяните бота в канале, к которому у него есть доступ на чтение. Первый ответ должен прийти через несколько секунд.
Если ничего не происходит, проверьте три вещи по порядку. Бот должен быть в сети в списке участников, значит, токен действителен. У него должны быть разрешения на чтение и отправку сообщений в этом канале. И упоминание должно быть настоящим упоминанием бота в Discord, выбранным из списка автозаполнения, потому что обычный текст с именем бота не появляется в message.mentions. В терминале выводятся ошибки API, поэтому неверный ключ отображается там как ошибка аутентификации.
Почему код устроен именно так
История намеренно небольшая
Контекстное окно рассчитано на 100 000 токенов, поэтому вы можете отправить много текста. Но каждый входной токен оплачивается при каждом вызове, а чат Discord состоит в основном из коротких строк. Двенадцать реплик сохраняют связность ответов, не превращая каждое сообщение в большой счёт. Увеличьте maxlen для каналов с историями, где важна непрерывность.
Семафор защищает ваш ключ
Полный ключ позволяет 8 параллельных запросов и 300 запросов в минуту. Пробный ключ позволяет только 2 и 60. Если десять человек одновременно упомянут бота и вы отправите десять запросов, дополнительные запросы получат HTTP 429 с кодом ошибки concurrency. Семафор формирует очередь в вашем собственном процессе. Установите значение 2, пока вы используете пробный ключ. Если вы когда-нибудь включите потоковую передачу, помните, что потоковый запрос удерживает свой слот до завершения потока.
Повторные попытки выполняет SDK
OpenAI Python SDK повторяет запросы при 429 и соблюдает заголовок Retry-After, который сервис отправляет как Retry-After: 5 при временной перегрузке. Установка max_retries в 4 покрывает кратковременные пики. Отказавшие или отклонённые запросы не оплачиваются, поэтому повторные запросы не расходуют токены, когда они завершаются неудачей.
Единая персона
Системный промпт стоит первым при каждом вызове. Персоны для каждого канала — это небольшое расширение: храните словарь от идентификатора канала к промпту и ищите его в ask.
Сделайте его хорошим открытым проектом
Код работает, но публичный репозиторий требует нескольких дополнений, прежде чем его смогут использовать незнакомцы.
- Лицензия. MIT или Apache 2.0 — обычные варианты для небольших ботов. Добавьте файл в корень репозитория.
- Файл .env.example. Перечислите
DISCORD_TOKENиAPI_KEYс фиктивными значениями, чтобы люди знали, что нужно установить. - Настраиваемый базовый URL. Считывайте
BASE_URLиMODELиз окружения, по умолчанию используя значения выше. Любой, кто сделает форк, сможет использовать любой OpenAI-совместимый сервер. - Dockerfile. Короткий файл на основе образа Python slim, копирующий
bot.py, устанавливающий три пакета и запускающий скрипт, позволяет хостить его где угодно. - README с предупреждением об интентах. Не включённый интент Message Content — самый частый вопрос в поддержку для любого бота Discord.
Держите свой токен и ключ вне истории Git. Если один из них утечёт, отзовите токен или ключ в портале Discord или на странице API-ключей и выпустите новый.
Хостинг и его стоимость
Бот — это один долгоживущий процесс с низкой нагрузкой на ЦП. Достаточно небольшого VPS, неиспользуемой машины или платформы контейнеризации. Открывать наружу веб-сервер не нужно, потому что бот сам подключается к Discord.
Стоимость API легко вычислить. Типичный ответ здесь использует 1500 входных токенов (системный промпт плюс двенадцать коротких реплик) и 300 выходных токенов.
- Входные: 1500 x $0,15 / 1M = $0,000225
- Выходные: 300 x $0,50 / 1M = $0,00015
- За ответ: $0,000375
Это делает 1000 ответов стоимостью $0,375, а 10 000 ответов — $3,75. Пробные $0,50 покрывают около 1333 ответов. Пополнение на $20 покрывает более 53 000. Пополнение — от $20 до $500, с +5% от $50, +10% от $100 и +15% от $200. Баланс не сгорает, подписки нет, поэтому тихий сервер почти ничего не стоит. Подробности см. на странице цен. Оплата только криптовалютой, с использованием USDT в сети TRC20 или USDC в сети Base.
Куда двигаться дальше
Три улучшения стоят усилий. Добавьте потоковую передачу, редактируя одно сообщение по мере поступления фрагментов, но учитывайте лимиты частоты редактирования Discord. Добавьте слэш-команду для сброса истории канала. Добавьте вызов функций, чтобы бот мог бросать кубики или искать лист персонажа; API поддерживает tools, tool_choice и результаты role: "tool" в формате OpenAI.
В документации API перечислены все поддерживаемые параметры, а в статье Hermes API описывается, как ведут себя модели uncensored внутри проекта. Промпты, отправленные в API, не используются для обучения, что стоит указать в правилах вашего сервера, если ваши участники спросят об этом.
Дополнительные руководства
Вопросы и ответы
Нужен ли боту доступ к содержимому сообщений?
Да. Включите его в портале разработчика Discord и в коде, установив intents.message_content в true. В противном случае бот видит пустые сообщения.
Сколько ответов бот может обрабатывать одновременно?
Полный ключ позволяет 8 параллельных запросов и 300 в минуту; пробный ключ позволяет 2 и 60. Семафор в боте не даёт вам превысить лимит.
Могу ли я опубликовать код бота?
Да, пока токен Discord и API-ключ остаются вне репозитория. Считывайте их из переменных окружения и поставляйте .env.example.
Разрешены ли темы для взрослых?
Только для законного использования взрослыми, в соответствии с правилами Discord и настройками каналов для взрослых. Сексуальный контент с участием несовершеннолетних никогда не допускается.
Сколько это стоит?
Ответ с 1 500 входными и 300 выходными токенами стоит $0,000375, то есть около $3,75 за 10 000 ответов, плюс стоимость размещения процесса.
Ваш ключ — всего в одной форме от вас
Создайте аккаунт, скопируйте ключ, измените базовый URL. Это вся настройка.
Получить API-ключ