Refactor environment variables and update systemd service configuration for ANewDawn
This commit is contained in:
parent
80e0637e8a
commit
195ca21947
7 changed files with 70 additions and 97 deletions
|
|
@ -1,4 +1,3 @@
|
|||
DISCORD_TOKEN=
|
||||
OPENAI_TOKEN=
|
||||
OLLAMA_API_KEY=
|
||||
OPENROUTER_API_KEY=
|
||||
5
.github/copilot-instructions.md
vendored
5
.github/copilot-instructions.md
vendored
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
ANewDawn is a Discord bot written in Python 3.13+ using the discord.py library and Pydantic AI for AI-powered chat capabilities. The bot includes features such as:
|
||||
|
||||
- AI-powered chat responses using OpenAI and Grok models
|
||||
- AI-powered chat responses using OpenAI models
|
||||
- Conversation memory with reset/undo functionality
|
||||
- Image enhancement using OpenCV
|
||||
- Web search integration via Ollama
|
||||
|
|
@ -66,13 +66,12 @@ ruff format --check --verbose
|
|||
|
||||
The main bot client is `LoviBotClient` which extends `discord.Client`. It handles:
|
||||
- Message events (`on_message`)
|
||||
- Slash commands (`/ask`, `/grok`, `/reset`, `/undo`)
|
||||
- Slash commands (`/ask`, `/reset`, `/undo`)
|
||||
- Context menus (image enhancement)
|
||||
|
||||
### AI Integration
|
||||
|
||||
- `chatgpt_agent` - Pydantic AI agent using OpenAI
|
||||
- `grok_it()` - Function for Grok model responses
|
||||
- Message history is stored in `recent_messages` dict per channel
|
||||
|
||||
### Memory Management
|
||||
|
|
|
|||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -37,7 +37,6 @@
|
|||
"numpy",
|
||||
"Ollama",
|
||||
"opencv",
|
||||
"OPENROUTER",
|
||||
"percpu",
|
||||
"phibiscarf",
|
||||
"plubplub",
|
||||
|
|
|
|||
27
README.md
27
README.md
|
|
@ -5,3 +5,30 @@
|
|||
</p>
|
||||
|
||||
A shit Discord bot.
|
||||
|
||||
## Running via systemd
|
||||
|
||||
This repo includes a systemd unit template under `systemd/anewdawn.service` that can be used to run the bot as a service.
|
||||
|
||||
### Quick setup
|
||||
|
||||
1. Copy and edit the environment file:
|
||||
```sh
|
||||
sudo mkdir -p /etc/ANewDawn
|
||||
sudo cp systemd/anewdawn.env.example /etc/ANewDawn/ANewDawn.env
|
||||
sudo chown -R lovinator:lovinator /etc/ANewDawn
|
||||
# Edit /etc/ANewDawn/ANewDawn.env and fill in your tokens.
|
||||
```
|
||||
|
||||
2. Install the systemd unit:
|
||||
```sh
|
||||
sudo cp systemd/anewdawn.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now anewdawn.service
|
||||
```
|
||||
|
||||
3. Check status / logs:
|
||||
```sh
|
||||
sudo systemctl status anewdawn.service
|
||||
sudo journalctl -u anewdawn.service -f
|
||||
```
|
||||
|
|
|
|||
92
main.py
92
main.py
|
|
@ -36,7 +36,6 @@ if TYPE_CHECKING:
|
|||
from discord.abc import MessageableChannel
|
||||
from discord.guild import GuildChannel
|
||||
from discord.interactions import InteractionChannel
|
||||
from openai.types.chat import ChatCompletion
|
||||
from pydantic_ai.run import AgentRunResult
|
||||
|
||||
load_dotenv(verbose=True)
|
||||
|
|
@ -82,39 +81,6 @@ chatgpt_agent: Agent[BotDependencies, str] = Agent(
|
|||
deps_type=BotDependencies,
|
||||
model_settings=openai_settings,
|
||||
)
|
||||
grok_client = openai.OpenAI(
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
api_key=os.getenv("OPENROUTER_API_KEY"),
|
||||
)
|
||||
|
||||
|
||||
def grok_it(
|
||||
message: discord.Message | None,
|
||||
user_message: str,
|
||||
) -> str | None:
|
||||
"""Chat with the bot using the Pydantic AI agent.
|
||||
|
||||
Args:
|
||||
user_message: The message from the user.
|
||||
message: The original Discord message object.
|
||||
|
||||
Returns:
|
||||
The bot's response as a string, or None if no response.
|
||||
"""
|
||||
allowed_users: list[str] = get_allowed_users()
|
||||
if message and message.author.name not in allowed_users:
|
||||
return None
|
||||
|
||||
response: ChatCompletion = grok_client.chat.completions.create(
|
||||
model="x-ai/grok-4-fast:free",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": user_message,
|
||||
},
|
||||
],
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
|
||||
|
||||
# MARK: reset_memory
|
||||
|
|
@ -711,7 +677,7 @@ class LoviBotClient(discord.Client):
|
|||
add_message_to_memory(str(message.channel.id), message.author.name, incoming_message)
|
||||
|
||||
lowercase_message: str = incoming_message.lower()
|
||||
trigger_keywords: list[str] = ["lovibot", "@lovibot", "<@345000831499894795>", "grok", "@grok"]
|
||||
trigger_keywords: list[str] = ["lovibot", "@lovibot", "<@345000831499894795>"]
|
||||
has_trigger_keyword: bool = any(trigger in lowercase_message for trigger in trigger_keywords)
|
||||
should_respond_flag: bool = has_trigger_keyword or should_respond_without_trigger(str(message.channel.id), message.author.name)
|
||||
|
||||
|
|
@ -853,62 +819,6 @@ async def ask(interaction: discord.Interaction, text: str, new_conversation: boo
|
|||
await send_response(interaction=interaction, text=text, response=display_response)
|
||||
|
||||
|
||||
# MARK: /grok command
|
||||
@client.tree.command(name="grok", description="Grok a question.")
|
||||
@app_commands.allowed_installs(guilds=True, users=True)
|
||||
@app_commands.allowed_contexts(guilds=True, dms=True, private_channels=True)
|
||||
@app_commands.describe(text="Grok a question.")
|
||||
async def grok(interaction: discord.Interaction, text: str) -> None:
|
||||
"""A command to ask the AI a question.
|
||||
|
||||
Args:
|
||||
interaction (discord.Interaction): The interaction object.
|
||||
text (str): The question or message to ask.
|
||||
"""
|
||||
await interaction.response.defer()
|
||||
|
||||
if not text:
|
||||
logger.error("No question or message provided.")
|
||||
await interaction.followup.send("You need to provide a question or message.", ephemeral=True)
|
||||
return
|
||||
|
||||
user_name_lowercase: str = interaction.user.name.lower()
|
||||
logger.info("Received command from: %s", user_name_lowercase)
|
||||
|
||||
# Only allow certain users to interact with the bot
|
||||
allowed_users: list[str] = get_allowed_users()
|
||||
if user_name_lowercase not in allowed_users:
|
||||
await send_response(interaction=interaction, text=text, response="You are not authorized to use this command.")
|
||||
return
|
||||
|
||||
# Get model response
|
||||
try:
|
||||
model_response: str | None = grok_it(message=interaction.message, user_message=text)
|
||||
except openai.OpenAIError as e:
|
||||
logger.exception("An error occurred while chatting with the AI model.")
|
||||
await send_response(interaction=interaction, text=text, response=f"An error occurred: {e}")
|
||||
return
|
||||
|
||||
truncated_text: str = truncate_user_input(text)
|
||||
|
||||
# Fallback if model provided no response
|
||||
if not model_response:
|
||||
logger.warning("No response from the AI model. Message: %s", text)
|
||||
model_response = "I forgor how to think 💀"
|
||||
|
||||
display_response: str = f"`{truncated_text}`\n\n{model_response}"
|
||||
logger.info("Responding to message: %s with: %s", text, display_response)
|
||||
|
||||
# If response is longer than 2000 characters, split it into multiple messages
|
||||
max_discord_message_length: int = 2000
|
||||
if len(display_response) > max_discord_message_length:
|
||||
for i in range(0, len(display_response), max_discord_message_length):
|
||||
await send_response(interaction=interaction, text=text, response=display_response[i : i + max_discord_message_length])
|
||||
return
|
||||
|
||||
await send_response(interaction=interaction, text=text, response=display_response)
|
||||
|
||||
|
||||
# MARK: /reset command
|
||||
@client.tree.command(name="reset", description="Reset the conversation memory.")
|
||||
@app_commands.allowed_installs(guilds=True, users=True)
|
||||
|
|
|
|||
11
systemd/anewdawn.env.example
Normal file
11
systemd/anewdawn.env.example
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Copy this file to /etc/ANewDawn/ANewDawn.env and fill in the required values.
|
||||
# Make sure the directory is owned by the user running the service (e.g., "lovinator").
|
||||
|
||||
# Discord bot token
|
||||
DISCORD_TOKEN=
|
||||
|
||||
# OpenAI token (for GPT-5 and other OpenAI models)
|
||||
OPENAI_TOKEN=
|
||||
|
||||
# Optional: additional env vars used by your bot
|
||||
# MY_CUSTOM_VAR=
|
||||
28
systemd/anewdawn.service
Normal file
28
systemd/anewdawn.service
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
[Unit]
|
||||
Description=ANewDawn Discord Bot
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Run the bot as the lovinator user (UID 1000) so it has appropriate permissions.
|
||||
# Update these values if you need a different system user/group.
|
||||
User=lovinator
|
||||
Group=lovinator
|
||||
|
||||
# The project directory containing main.py (update as needed).
|
||||
WorkingDirectory=/home/lovinator/Code/ANewDawn
|
||||
|
||||
# Load environment variables (see systemd/anewdawn.env.example).
|
||||
EnvironmentFile=/etc/ANewDawn/ANewDawn.env
|
||||
|
||||
# Use the python interpreter from your environment (system python is fine if dependencies are installed).
|
||||
ExecStart=/usr/bin/env python3 main.py
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Loading…
Add table
Add a link
Reference in a new issue