Rename greeptchat cog to assistant - generic AI chatbot branding

This commit is contained in:
2026-07-31 20:41:40 -05:00
parent 37a5682e3e
commit 7fa85d7dd9
6 changed files with 218 additions and 44 deletions
+174
View File
@@ -17,6 +17,13 @@ Homepage: https://homepage.kingstons-scrapyard.net/
- [Permissions Required](#permissions-required) - [Permissions Required](#permissions-required)
- [Scrapyard Cogworks Modifications](#scrapyard-cogworks-modifications) - [Scrapyard Cogworks Modifications](#scrapyard-cogworks-modifications)
**Assistant**
- [Assistant](#assistant)
- [Assistant Installation](#assistant-installation)
- [Assistant User Commands](#assistant-user-commands)
- [Assistant Admin/Owner Commands](#assistant-adminowner-commands)
- [Assistant Behavior](#assistant-behavior)
**TtsToy** **TtsToy**
- [TtsToy](#ttstoy) - [TtsToy](#ttstoy)
- [TtsToy User Commands](#user-commands-1) - [TtsToy User Commands](#user-commands-1)
@@ -560,6 +567,173 @@ The `[p]autoroom allow` command now accepts multiple users/roles in a single com
--- ---
--- ---
# Assistant
AI chatbot cog that responds in a dedicated channel or when the bot is @mentioned. Uses an OpenAI-compatible API backend. Designed for fun, unpredictable responses — crank the temperature and give it a wild system prompt.
### Features
- **Chat channel mode** — set a channel where the bot responds to every message
- **Ping mode** — responds when @mentioned in any channel
- **Custom system prompt** — control the bot's personality
- **Temperature control** — turn it up for chaotic/funny responses
- **Question mode** — optionally restrict responses to messages ending with `?`
- **Conversation history** — remembers the last 20 messages per channel (in memory only)
- **Long response handling** — automatically chunks replies over 2000 characters
### Requirements
- Python 3.11+
- Red-DiscordBot 3.5.0+
- Dependencies: `aiohttp`
---
## Assistant Installation
```
[p]cog install scrapyard assistant
[p]load assistant
```
---
## Assistant User Commands
The assistant responds automatically in the configured chat channel or when pinged. There are no user-invoked "chat" commands — just talk to it.
**In the chat channel:** Send any message and the bot replies.
**Anywhere else:** @mention the bot with your message.
```
@Bot what's the meaning of life?
```
If **question mode** is on, messages must end with `?` or they'll be ignored.
---
## Assistant Admin/Owner Commands
All commands are under the `[p]assistant` group.
---
### `[p]assistant setkey`
**Requires:** Bot Owner
Set the API key via a private modal dialog. The key is stored globally (shared across all servers the bot is in).
---
### `[p]assistant toggle`
Toggle the assistant on or off for the current server. Enabled by default.
---
### `[p]assistant channel [#channel]`
Set the dedicated chat channel where the bot responds to all messages.
- With a channel: sets that channel as the chat channel.
- Without a channel: clears the setting (bot only responds to @mentions).
**Examples:**
```
[p]assistant channel #bot-chat
[p]assistant channel
```
---
### `[p]assistant model <model>`
Set the AI model to use.
**Examples:**
```
[p]assistant model gemma4
[p]assistant model green-l
[p]assistant model green-r
```
---
### `[p]assistant temp <temperature>`
Set the response temperature.
- Range: `0.0` to `2.0`
- Lower values (0.1–0.5) = focused, predictable responses
- Higher values (1.2–2.0) = creative, chaotic, funny responses
**Examples:**
```
[p]assistant temp 0.3
[p]assistant temp 1.8
```
---
### `[p]assistant system <prompt>`
Set the system prompt that defines the bot's personality and behavior.
**Examples:**
```
[p]assistant system You are a helpful assistant.
[p]assistant system You are an unhinged gremlin who gives terrible life advice in the style of a 90s infomercial host. Everything is an emergency.
[p]assistant system You are a pirate who only speaks in nautical metaphors and refuses to acknowledge anything invented after 1750.
```
---
### `[p]assistant question`
Toggle question mode. When ON, the bot only responds to messages that end with `?`. Everything else is ignored.
This is a toggle — run again to disable.
---
### `[p]assistant clear`
Clear the conversation history for the current channel. The bot will forget all previous context.
---
### `[p]assistant settings`
Display the current configuration as an embed:
- Enabled (yes/no)
- Chat Channel
- API Key (set/not set)
- Model
- Temperature
- Question Mode (on/off)
- System Prompt (first 200 chars)
---
## Assistant Behavior
- **History:** The bot keeps the last 20 messages (user + assistant) per channel in memory. History is lost on cog reload/bot restart.
- **Typing indicator:** The bot shows "typing..." while waiting for the API response.
- **Long responses:** Replies over 2000 characters are split into multiple messages.
- **Error handling:** If the API is down or returns an error, the bot replies with the error info.
- **No response to bots:** The assistant ignores messages from other bots.
- **Per-guild settings:** Model, temperature, system prompt, channel, and question mode are all configured per server.
- **Global API key:** The API key is set once by the bot owner and applies to all servers.
---
---
---
# TtsToy # TtsToy
Multi-engine text-to-speech cog with voice cloning, emoji-triggered sound effects, inline voice switching, a TTS queue system, and a companion web UI. Plays audio in voice channels and uploads MP3 files to chat. Multi-engine text-to-speech cog with voice cloning, emoji-triggered sound effects, inline voice switching, a TTS queue system, and a companion web UI. Plays audio in voice channels and uploads MP3 files to chat.
+5
View File
@@ -0,0 +1,5 @@
from .assistant import Assistant
async def setup(bot):
await bot.add_cog(Assistant(bot))
@@ -6,12 +6,12 @@ from redbot.core.bot import Red
class APIKeyModal(discord.ui.Modal): class APIKeyModal(discord.ui.Modal):
def __init__(self, config): def __init__(self, config):
super().__init__(title="GreenPT API Key") super().__init__(title="API Key")
self.config = config self.config = config
self.key_input = discord.ui.TextInput( self.key_input = discord.ui.TextInput(
label="API Key", label="API Key",
style=discord.TextStyle.short, style=discord.TextStyle.short,
placeholder="Paste your GreenPT API key here", placeholder="Paste your API key here",
required=True, required=True,
) )
self.add_item(self.key_input) self.add_item(self.key_input)
@@ -21,8 +21,8 @@ class APIKeyModal(discord.ui.Modal):
await interaction.response.send_message("API key saved.", ephemeral=True) await interaction.response.send_message("API key saved.", ephemeral=True)
class GreeptChat(commands.Cog): class Assistant(commands.Cog):
"""AI chat via the GreenPT API. Set a chat channel or get responses when pinged.""" """AI chatbot assistant. Set a chat channel or get responses when the bot is pinged."""
BASE_URL = "https://greenpt.com/v1" BASE_URL = "https://greenpt.com/v1"
@@ -97,7 +97,7 @@ class GreeptChat(commands.Cog):
reply = await self._chat(guild, message.channel.id, content) reply = await self._chat(guild, message.channel.id, content)
if reply is None: if reply is None:
await message.reply("No response from GreenPT.") await message.reply("No response from the assistant.")
return return
if len(reply) <= 2000: if len(reply) <= 2000:
@@ -167,24 +167,24 @@ class GreeptChat(commands.Cog):
# Commands # Commands
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
greept = commands.Group("greept", "GreenPT chat settings.", guild_only=True) assistant = commands.Group("assistant", "AI assistant settings.", guild_only=True)
@greept.command(name="setkey") @assistant.command(name="setkey")
@checks.is_owner() @checks.is_owner()
async def greept_setkey(self, ctx: commands.Context): async def assistant_setkey(self, ctx: commands.Context):
"""Set the GreenPT API key (opens a private modal).""" """Set the API key (opens a private modal)."""
await ctx.send_modal(APIKeyModal(self.config)) await ctx.send_modal(APIKeyModal(self.config))
@greept.command(name="toggle") @assistant.command(name="toggle")
async def greept_toggle(self, ctx: commands.Context): async def assistant_toggle(self, ctx: commands.Context):
"""Toggle the cog on or off for this server.""" """Toggle the assistant on or off for this server."""
current = await self.config.guild(ctx.guild).enabled() current = await self.config.guild(ctx.guild).enabled()
await self.config.guild(ctx.guild).enabled.set(not current) await self.config.guild(ctx.guild).enabled.set(not current)
state = "enabled" if not current else "disabled" state = "enabled" if not current else "disabled"
await ctx.send(f"GreeptChat **{state}** for this server.") await ctx.send(f"Assistant **{state}** for this server.")
@greept.command(name="channel") @assistant.command(name="channel")
async def greept_channel(self, ctx: commands.Context, channel: discord.TextChannel | None = None): async def assistant_channel(self, ctx: commands.Context, channel: discord.TextChannel | None = None):
"""Set the dedicated chat channel. Omit to clear.""" """Set the dedicated chat channel. Omit to clear."""
if channel is None: if channel is None:
await self.config.guild(ctx.guild).chat_channel.set(None) await self.config.guild(ctx.guild).chat_channel.set(None)
@@ -193,14 +193,14 @@ class GreeptChat(commands.Cog):
await self.config.guild(ctx.guild).chat_channel.set(channel.id) await self.config.guild(ctx.guild).chat_channel.set(channel.id)
await ctx.send(f"Chat channel set to {channel.mention}.") await ctx.send(f"Chat channel set to {channel.mention}.")
@greept.command(name="model") @assistant.command(name="model")
async def greept_model(self, ctx: commands.Context, model: str): async def assistant_model(self, ctx: commands.Context, model: str):
"""Set the model (e.g. gemma4, green-l, green-r).""" """Set the model (e.g. gemma4, green-l, green-r)."""
await self.config.guild(ctx.guild).model.set(model) await self.config.guild(ctx.guild).model.set(model)
await ctx.send(f"Model set to **{model}**.") await ctx.send(f"Model set to **{model}**.")
@greept.command(name="temp") @assistant.command(name="temp")
async def greept_temp(self, ctx: commands.Context, temperature: float): async def assistant_temp(self, ctx: commands.Context, temperature: float):
"""Set the temperature (0.0 - 2.0).""" """Set the temperature (0.0 - 2.0)."""
if not 0.0 <= temperature <= 2.0: if not 0.0 <= temperature <= 2.0:
await ctx.send("Must be between 0.0 and 2.0.") await ctx.send("Must be between 0.0 and 2.0.")
@@ -208,29 +208,29 @@ class GreeptChat(commands.Cog):
await self.config.guild(ctx.guild).temperature.set(temperature) await self.config.guild(ctx.guild).temperature.set(temperature)
await ctx.send(f"Temperature set to **{temperature}**.") await ctx.send(f"Temperature set to **{temperature}**.")
@greept.command(name="system") @assistant.command(name="system")
async def greept_system(self, ctx: commands.Context, *, prompt: str): async def assistant_system(self, ctx: commands.Context, *, prompt: str):
"""Set the system prompt.""" """Set the system prompt."""
await self.config.guild(ctx.guild).system_prompt.set(prompt) await self.config.guild(ctx.guild).system_prompt.set(prompt)
await ctx.send("System prompt updated.") await ctx.send("System prompt updated.")
@greept.command(name="question") @assistant.command(name="question")
async def greept_question(self, ctx: commands.Context): async def assistant_question(self, ctx: commands.Context):
"""Toggle question mode (messages must end with ?).""" """Toggle question mode (messages must end with ?)."""
current = await self.config.guild(ctx.guild).question_mode() current = await self.config.guild(ctx.guild).question_mode()
await self.config.guild(ctx.guild).question_mode.set(not current) await self.config.guild(ctx.guild).question_mode.set(not current)
state = "ON" if not current else "OFF" state = "ON" if not current else "OFF"
await ctx.send(f"Question mode **{state}**.") await ctx.send(f"Question mode **{state}**.")
@greept.command(name="clear") @assistant.command(name="clear")
async def greept_clear(self, ctx: commands.Context): async def assistant_clear(self, ctx: commands.Context):
"""Clear conversation history for this channel.""" """Clear conversation history for this channel."""
key = (ctx.guild.id, ctx.channel.id) key = (ctx.guild.id, ctx.channel.id)
self.history.pop(key, None) self.history.pop(key, None)
await ctx.send("History cleared for this channel.") await ctx.send("History cleared for this channel.")
@greept.command(name="settings") @assistant.command(name="settings")
async def greept_settings(self, ctx: commands.Context): async def assistant_settings(self, ctx: commands.Context):
"""Show current settings.""" """Show current settings."""
cfg = self.config.guild(ctx.guild) cfg = self.config.guild(ctx.guild)
enabled = await cfg.enabled() enabled = await cfg.enabled()
@@ -243,7 +243,7 @@ class GreeptChat(commands.Cog):
channel_str = f"<#{chat_channel}>" if chat_channel else "None" channel_str = f"<#{chat_channel}>" if chat_channel else "None"
embed = discord.Embed(title="GreeptChat Settings", color=await ctx.embed_color()) embed = discord.Embed(title="Assistant Settings", color=await ctx.embed_color())
embed.add_field(name="Enabled", value=str(enabled), inline=True) embed.add_field(name="Enabled", value=str(enabled), inline=True)
embed.add_field(name="Chat Channel", value=channel_str, inline=True) embed.add_field(name="Chat Channel", value=channel_str, inline=True)
embed.add_field(name="API Key", value="Set" if has_key else "Not set", inline=True) embed.add_field(name="API Key", value="Set" if has_key else "Not set", inline=True)
+11
View File
@@ -0,0 +1,11 @@
{
"author": ["Scrapyard Cogworks"],
"install_msg": "Assistant cog installed. Use [p]assistant to configure.",
"name": "Assistant",
"short": "AI chatbot assistant with channel and ping modes.",
"description": "AI chatbot assistant using an OpenAI-compatible API. Set a dedicated chat channel or get responses when the bot is pinged. Supports temperature, model, system prompt, and question mode.",
"tags": ["ai", "chat", "assistant", "gpt", "llm"],
"requirements": ["aiohttp"],
"min_bot_version": "3.5.0",
"end_user_data_statement": "This cog stores conversation history per channel in memory only."
}
-5
View File
@@ -1,5 +0,0 @@
from .greeptchat import GreeptChat
async def setup(bot):
await bot.add_cog(GreeptChat(bot))
-11
View File
@@ -1,11 +0,0 @@
{
"author": ["Scrapyard Cogworks"],
"install_msg": "GreeptChat cog installed. Use [p]greept to configure.",
"name": "GreeptChat",
"short": "AI chat via GreenPT API with channel and ping modes.",
"description": "Connects to the GreenPT OpenAI-compatible API for AI chat. Set a dedicated chat channel or get responses when the bot is pinged. Supports temperature, model, system prompt, and question mode.",
"tags": ["ai", "chat", "greenpt", "gpt", "llm"],
"requirements": ["aiohttp"],
"min_bot_version": "3.5.0",
"end_user_data_statement": "This cog stores conversation history per channel in memory only."
}