Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8bb6681c7 | |||
| c0d5bff873 | |||
| ff7b33b952 | |||
| 702e62973a | |||
| c3dd3fa7b0 | |||
| b4954b082e | |||
| 54713ed346 | |||
| 673fc0deaf | |||
| f80cc18294 | |||
| e03b26da0f | |||
| 9eef597c8d | |||
| 57acaef1b2 | |||
| aa2382f3b4 | |||
| a8d83c8768 | |||
| e7be5553cc | |||
| 895b17390d | |||
| 9145ac1bb4 | |||
| 44d0315059 | |||
| b3045bf724 | |||
| 90c97d3f54 | |||
| 6bf61fdbf0 | |||
| b2c2d4bcc2 | |||
| 1ef60193a0 | |||
| 3fbc996de1 | |||
| 8819489a7d | |||
| 3c979d855e | |||
| 5d42ab65e6 | |||
| fa70c064cf | |||
| dfaf4ee8f3 | |||
| ed3cd1df57 | |||
| 059761fb29 |
@ -15,12 +15,12 @@ jobs:
|
||||
GITHUB: ${{ secrets.GITHUB }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@ -85,7 +85,7 @@ jobs:
|
||||
|
||||
# Ergebnisse als Artefakte speichern
|
||||
- name: Upload results as artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: quality-reports
|
||||
path: /tmp/results
|
||||
|
||||
@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Verify Docker installation
|
||||
run: docker --version
|
||||
|
||||
20
.github/renovate.json
vendored
Normal file
20
.github/renovate.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"baseBranches": [
|
||||
"dev"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch",
|
||||
"pin",
|
||||
"digest"
|
||||
],
|
||||
"automerge": false
|
||||
}
|
||||
]
|
||||
}
|
||||
52
README.md
52
README.md
@ -1,2 +1,52 @@
|
||||
# novelai-bot
|
||||
# NovelAI Discord Bot
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Simple bot which registers a command to generate images via the NovelAI API.
|
||||
|
||||
## Usage
|
||||
|
||||
Just enter `/generate` in the desired channel.
|
||||
|
||||
```text
|
||||
/generate
|
||||
*prompt: string
|
||||
undesired_prompt: string
|
||||
orientation: landscape or portrait
|
||||
seed: integer
|
||||
```
|
||||
|
||||
`*: required`
|
||||
|
||||
## Run
|
||||
|
||||
You might create environvent vars:
|
||||
|
||||
- **NOVELAI_API_TOKEN**: your NovelAI user API key
|
||||
- **DISCORD_BOT_TOKEN**: token of your Discord bot
|
||||
- **DISCORD_CHANNEL_ID**: ID of the channel to enable this bot
|
||||
|
||||
Don't forget to install the requirements in [requirements.txt](src/requirements.txt)
|
||||
|
||||
## Docker Compose
|
||||
|
||||
Or simply run it via **docker-compose**:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bot:
|
||||
restart: unless-stopped
|
||||
image: git.0day.agency/wirehack7/novelai-bot:latest
|
||||
environment:
|
||||
- NOVELAI_API_TOKEN=
|
||||
- DISCORD_BOT_TOKEN=
|
||||
- DISCORD_CHANNEL_ID=
|
||||
networks: {}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
To create a Discord bot go to [Discord Developers](https://discord.com/developers/).
|
||||
You should make the bot private.
|
||||
@ -1,4 +1,4 @@
|
||||
FROM python:3.13.3-slim
|
||||
FROM python:3.14.2-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
42
src/main.py
42
src/main.py
@ -10,8 +10,8 @@ import sys
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
import aiohttp
|
||||
import discord
|
||||
import requests
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
|
||||
@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
if not all([NOVELAI_API_TOKEN, BOT_TOKEN, ALLOWED_CHANNEL_ID]):
|
||||
raise ValueError(
|
||||
"NOVELAI_API_TOKEN, DISCORD_BOT_TOKEN und DISCORD_CHANNEL_ID müssen gesetzt sein."
|
||||
"NOVELAI_API_TOKEN, DISCORD_BOT_TOKEN and DISCORD_CHANNEL_ID have to be set."
|
||||
)
|
||||
|
||||
intents = discord.Intents.default()
|
||||
@ -43,9 +43,9 @@ async def on_ready():
|
||||
print(f"Bot gestartet als {bot.user}")
|
||||
try:
|
||||
synced = await bot.tree.sync()
|
||||
logger.info("%d Slash-Commands synchronisiert.", len(synced))
|
||||
logger.info("%d Slash-Commands synchronized.", len(synced))
|
||||
except Exception as err: # pylint: disable=W0718
|
||||
logger.error("Fehler beim Synchronisieren der Commands: %s", err)
|
||||
logger.error("error while syncing the command: %s", err)
|
||||
|
||||
activity = discord.Game(name="generating juicy NovelAI images 🥵")
|
||||
await bot.change_presence(status=discord.Status.online, activity=activity)
|
||||
@ -58,7 +58,7 @@ async def on_ready():
|
||||
orientation="portrait or landscape (Standard: portrait)",
|
||||
seed="Optional seed (integer). If empty, a random one will be generated",
|
||||
)
|
||||
# pylint: disable=too-many-locals,too-many-return-statements
|
||||
# pylint: disable=too-many-locals,too-many-return-statements,too-many-statements
|
||||
async def generate(
|
||||
interaction: discord.Interaction,
|
||||
prompt: str,
|
||||
@ -159,17 +159,25 @@ async def generate(
|
||||
}
|
||||
|
||||
start_time = time.monotonic()
|
||||
response = requests.post(
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
"https://image.novelai.net/ai/generate-image",
|
||||
json=payload,
|
||||
headers=headers,
|
||||
timeout=120,
|
||||
)
|
||||
duration = time.monotonic() - start_time
|
||||
logger.info("Bildgenerierung dauerte %.2f Sekunden", duration)
|
||||
timeout=aiohttp.ClientTimeout(total=120),
|
||||
) as response:
|
||||
response_bytes = await response.read()
|
||||
status_code = response.status
|
||||
except aiohttp.TimeoutError:
|
||||
await interaction.followup.send("Error: the request timed out.")
|
||||
except aiohttp.ClientError as e:
|
||||
await interaction.followup.send(f"Error: {e}")
|
||||
|
||||
if response.status_code == 200:
|
||||
response_bytes = response.content
|
||||
duration = time.monotonic() - start_time
|
||||
logger.info("Image creation took %.2f seconds", duration)
|
||||
|
||||
if status_code == 200:
|
||||
param_text = (
|
||||
f"```Prompt: {prompt}\n"
|
||||
f"Undesired prompt: {negative_prompt}\n"
|
||||
@ -206,13 +214,13 @@ async def generate(
|
||||
return
|
||||
|
||||
try:
|
||||
error_data = response.json()
|
||||
error_data = response_bytes.decode("utf-8")
|
||||
except Exception: # pylint: disable=W0718
|
||||
error_data = {"error": response.text}
|
||||
error_data = "error message unreadable"
|
||||
|
||||
error_message = f"Error {response.status_code} at API-request.\n"
|
||||
for key, value in error_data.items():
|
||||
error_message += f"**{key}**: {value}\n"
|
||||
error_message = (
|
||||
f"Error {status_code} at API-request.\n" f"Answer: {error_data}\n"
|
||||
)
|
||||
|
||||
await interaction.followup.send(error_message)
|
||||
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
discord
|
||||
requests
|
||||
aiohttp
|
||||
Loading…
x
Reference in New Issue
Block a user