Merge work into master
This commit is contained in:
commit
07e2beecf6
2241
cogs/aimod.py
2241
cogs/aimod.py
File diff suppressed because it is too large
Load Diff
2318
cogs/aimod_cog.py
Normal file
2318
cogs/aimod_cog.py
Normal file
File diff suppressed because it is too large
Load Diff
176
cogs/aimod_config.py
Normal file
176
cogs/aimod_config.py
Normal file
@ -0,0 +1,176 @@
|
||||
import os
|
||||
import json
|
||||
import asyncio
|
||||
import aiofiles
|
||||
from google.genai import types
|
||||
|
||||
# Vertex AI Configuration
|
||||
DEFAULT_VERTEX_AI_MODEL = "gemini-2.5-flash-preview-05-20"
|
||||
|
||||
# Define standard safety settings using google.generativeai types
|
||||
STANDARD_SAFETY_SETTINGS = [
|
||||
types.SafetySetting(
|
||||
category=types.HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold="BLOCK_NONE"
|
||||
),
|
||||
types.SafetySetting(
|
||||
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
||||
threshold="BLOCK_NONE",
|
||||
),
|
||||
types.SafetySetting(
|
||||
category=types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
||||
threshold="BLOCK_NONE",
|
||||
),
|
||||
types.SafetySetting(
|
||||
category=types.HarmCategory.HARM_CATEGORY_HARASSMENT, threshold="BLOCK_NONE"
|
||||
),
|
||||
]
|
||||
|
||||
MOD_LOG_API_SECRET_ENV_VAR = "MOD_LOG_API_SECRET"
|
||||
|
||||
GUILD_CONFIG_DIR = "data/"
|
||||
GUILD_CONFIG_PATH = os.path.join(GUILD_CONFIG_DIR, "guild_config.json")
|
||||
USER_INFRACTIONS_PATH = os.path.join(GUILD_CONFIG_DIR, "user_infractions.json")
|
||||
INFRACTION_BACKUP_DIR = os.path.join(GUILD_CONFIG_DIR, "infraction_backups")
|
||||
|
||||
os.makedirs(INFRACTION_BACKUP_DIR, exist_ok=True)
|
||||
os.makedirs(GUILD_CONFIG_DIR, exist_ok=True)
|
||||
|
||||
if not os.path.exists(GUILD_CONFIG_PATH):
|
||||
with open(GUILD_CONFIG_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump({}, f)
|
||||
try:
|
||||
with open(GUILD_CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
GUILD_CONFIG = json.load(f)
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"Failed to load per-guild config from {GUILD_CONFIG_PATH}: {e}")
|
||||
GUILD_CONFIG = {}
|
||||
|
||||
if not os.path.exists(USER_INFRACTIONS_PATH):
|
||||
with open(USER_INFRACTIONS_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump({}, f)
|
||||
try:
|
||||
with open(USER_INFRACTIONS_PATH, "r", encoding="utf-8") as f:
|
||||
USER_INFRACTIONS = json.load(f)
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"Failed to load user infractions from {USER_INFRACTIONS_PATH}: {e}")
|
||||
USER_INFRACTIONS = {}
|
||||
|
||||
CONFIG_LOCK = asyncio.Lock()
|
||||
|
||||
|
||||
async def save_guild_config():
|
||||
async with CONFIG_LOCK:
|
||||
try:
|
||||
async with aiofiles.open(GUILD_CONFIG_PATH, "w", encoding="utf-8") as f:
|
||||
await f.write(json.dumps(GUILD_CONFIG, indent=2))
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"Failed to save per-guild config: {e}")
|
||||
|
||||
|
||||
async def save_user_infractions():
|
||||
async with CONFIG_LOCK:
|
||||
try:
|
||||
async with aiofiles.open(USER_INFRACTIONS_PATH, "w", encoding="utf-8") as f:
|
||||
await f.write(json.dumps(USER_INFRACTIONS, indent=2))
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"Failed to save user infractions: {e}")
|
||||
|
||||
|
||||
def get_guild_config(guild_id: int, key: str, default=None):
|
||||
guild_str = str(guild_id)
|
||||
if guild_str in GUILD_CONFIG and key in GUILD_CONFIG[guild_str]:
|
||||
return GUILD_CONFIG[guild_str][key]
|
||||
return default
|
||||
|
||||
|
||||
async def set_guild_config(guild_id: int, key: str, value):
|
||||
guild_str = str(guild_id)
|
||||
if guild_str not in GUILD_CONFIG:
|
||||
GUILD_CONFIG[guild_str] = {}
|
||||
GUILD_CONFIG[guild_str][key] = value
|
||||
await save_guild_config()
|
||||
|
||||
|
||||
def get_user_infraction_history(guild_id: int, user_id: int) -> list:
|
||||
key = f"{guild_id}_{user_id}"
|
||||
return USER_INFRACTIONS.get(key, [])
|
||||
|
||||
|
||||
async def add_user_infraction(
|
||||
guild_id: int,
|
||||
user_id: int,
|
||||
rule_violated: str,
|
||||
action_taken: str,
|
||||
reasoning: str,
|
||||
timestamp: str,
|
||||
):
|
||||
key = f"{guild_id}_{user_id}"
|
||||
if key not in USER_INFRACTIONS:
|
||||
USER_INFRACTIONS[key] = []
|
||||
|
||||
infraction_record = {
|
||||
"timestamp": timestamp,
|
||||
"rule_violated": rule_violated,
|
||||
"action_taken": action_taken,
|
||||
"reasoning": reasoning,
|
||||
}
|
||||
USER_INFRACTIONS[key].append(infraction_record)
|
||||
USER_INFRACTIONS[key] = USER_INFRACTIONS[key][-10:]
|
||||
await save_user_infractions()
|
||||
|
||||
|
||||
SERVER_RULES = """
|
||||
# Server Rules
|
||||
|
||||
* **NSFW Content:**
|
||||
The only rule regarding NSFW content is that **real-life pornography is strictly prohibited**.
|
||||
Full-on pornographic images are permitted in designated NSFW channels.
|
||||
Stickers and emojis are NOT considered "full-on pornographic images" and are allowed in any channel.
|
||||
|
||||
* **Real-Life Pornography:** No real-life pornography is permitted.
|
||||
|
||||
* **Respectful Conduct & Edgy Humor:**
|
||||
* No harassment, genuine hate speech (defined as attacking protected groups OR malicious, targeted use of slurs against an individual), or genuine bullying.
|
||||
* Slurs used in general banter or edgy humor are permissible. However, using slurs in a genuinely hateful, targeted manner against an individual or group is considered hate speech and is forbidden.
|
||||
* *Context is key:* Edgy humor, dark jokes, and roasting are permitted and expected.
|
||||
* However, this does not excuse targeted, malicious personal attacks or harassment (including the aforementioned misuse of slurs), especially if the recipient is clearly not okay with it.
|
||||
* If it stops being a "joke" and becomes genuine harassment or hate speech, it's a rule violation.
|
||||
|
||||
* **No Discrimination:** Discrimination based on race, gender identity, sexual orientation, religion, nationality, disability, or other protected characteristics is prohibited.
|
||||
|
||||
* **AI-Generated Pornography:** Do not post AI-generated pornography.
|
||||
|
||||
* **Zero Tolerance for Pedophilia:** Any form of pedophilia, including lolicon and shotacon content, is strictly forbidden and will result in an immediate ban.
|
||||
|
||||
* **Channel Usage:** Please use channels for their intended purposes. Bot commands should primarily be used in `#bot-commands`, unless they are part of a bot-based game or event happening in another specific channel.
|
||||
|
||||
* **Gore:** Do not post gore or graphic real-life violence.
|
||||
|
||||
* **Suggestions:** We welcome your suggestions for the server! Please post them in the `#suggestions` channel.
|
||||
|
||||
---
|
||||
|
||||
**Reporting Violations:**
|
||||
If you witness someone breaking these rules, please ping an `@Moderator` with details.
|
||||
|
||||
---
|
||||
|
||||
**Moderator Applications:**
|
||||
Use the bot command `/modapp apply`
|
||||
"""
|
||||
|
||||
SUICIDAL_HELP_RESOURCES = """
|
||||
Hey, I'm really concerned to hear you're feeling this way. Please know that you're not alone and there are people who want to support you.
|
||||
Your well-being is important to us on this server.
|
||||
|
||||
Here are some immediate resources that can offer help right now:
|
||||
|
||||
- **National Crisis and Suicide Lifeline (US):** Call or text **988**. This is available 24/7, free, and confidential.
|
||||
- **Crisis Text Line (US):** Text **HOME** to **741741**. This is also a 24/7 free crisis counseling service.
|
||||
- **The Trevor Project (for LGBTQ youth):** Call **1-866-488-7386** or visit their website for chat/text options: <https://www.thetrevorproject.org/get-help/>
|
||||
- **The Jed Foundation (Mental Health Resource Center):** Provides resources for teens and young adults: <https://www.jedfoundation.org/>
|
||||
- **Find A Helpline (International):** If you're outside the US, this site can help you find resources in your country: <https://findahelpline.com/>
|
||||
|
||||
Please reach out to one of these. We've also alerted our server's support team so they are aware and can offer a listening ear or further guidance if you're comfortable.
|
||||
You matter, and help is available.
|
||||
"""
|
@ -152,9 +152,18 @@ class LoggingCog(commands.Cog):
|
||||
|
||||
def add_field(self, name: str, value: str, inline: bool = False) -> None:
|
||||
field = ui.TextDisplay(f"**{name}:** {value}")
|
||||
if hasattr(self.container, "children"):
|
||||
index = self.container.children.index(self.bottom_separator)
|
||||
self.container.children.insert(index, field)
|
||||
# Ensure the field is properly registered with the view by using
|
||||
# add_item first, then repositioning it before the bottom separator
|
||||
if hasattr(self.container, "_children"):
|
||||
self.container.add_item(field)
|
||||
try:
|
||||
children = self.container._children
|
||||
index = children.index(self.bottom_separator)
|
||||
children.remove(field)
|
||||
children.insert(index, field)
|
||||
except ValueError:
|
||||
# Fallback to default behaviour if the separator is missing
|
||||
pass
|
||||
else:
|
||||
self.content_container.add_item(field)
|
||||
|
||||
@ -1160,6 +1169,16 @@ class LoggingCog(commands.Cog):
|
||||
),
|
||||
inline=False,
|
||||
)
|
||||
if before.content:
|
||||
before_text = before.content[:1020] + (
|
||||
"..." if len(before.content) > 1020 else ""
|
||||
)
|
||||
embed.add_field(name="Before", value=before_text or "`Empty`", inline=False)
|
||||
if after.content:
|
||||
after_text = after.content[:1020] + (
|
||||
"..." if len(after.content) > 1020 else ""
|
||||
)
|
||||
embed.add_field(name="After", value=after_text or "`Empty`", inline=False)
|
||||
self._add_id_footer(embed, after, id_name="Message ID") # Add message ID
|
||||
await self._send_log_embed(guild, embed)
|
||||
|
||||
@ -1780,7 +1799,7 @@ class LoggingCog(commands.Cog):
|
||||
added = [r.mention for r in after_roles if r not in before_roles]
|
||||
removed = [r.mention for r in before_roles if r not in after_roles]
|
||||
if added or removed: # Only log if roles actually changed
|
||||
action_desc = f"{self._user_display(user)} updated roles for {self._user_display(target)} ({target.id}):"
|
||||
action_desc = f"{self._user_display(user)} updated roles for {self._user_display(target)}:"
|
||||
if added:
|
||||
action_desc += f"\n**Added:** {', '.join(added)}"
|
||||
if removed:
|
||||
@ -1802,10 +1821,10 @@ class LoggingCog(commands.Cog):
|
||||
timeout_duration = discord.utils.format_dt(
|
||||
after_timed_out, style="R"
|
||||
)
|
||||
action_desc = f"{self._user_display(user)} timed out {self._user_display(target)} ({target.id}) until {timeout_duration}"
|
||||
action_desc = f"{self._user_display(user)} timed out {self._user_display(target)} until {timeout_duration}"
|
||||
color = discord.Color.orange()
|
||||
else:
|
||||
action_desc = f"{self._user_display(user)} removed timeout from {self._user_display(target)} ({target.id})"
|
||||
action_desc = f"{self._user_display(user)} removed timeout from {self._user_display(target)}"
|
||||
color = discord.Color.green()
|
||||
# self._add_id_footer(embed, target, id_name="Target ID") # Footer set later
|
||||
else:
|
||||
@ -2124,7 +2143,7 @@ class LoggingCog(commands.Cog):
|
||||
title = "🛡️ Audit Log: Message Deleted" # Title adjusted for clarity
|
||||
channel = entry.extra.channel
|
||||
count = entry.extra.count
|
||||
action_desc = f"{user.mention} deleted {count} message(s) by {target.mention} ({target.id}) in {channel.mention}"
|
||||
action_desc = f"{user.mention} deleted {count} message(s) by {self._user_display(target)} in {channel.mention}"
|
||||
color = discord.Color.dark_grey()
|
||||
|
||||
elif entry.action == discord.AuditLogAction.message_bulk_delete:
|
||||
|
@ -25,8 +25,11 @@ async def load_all_cogs(bot: commands.Bot, skip_cogs: Optional[List[str]] = None
|
||||
and not filename.startswith("profile_updater")
|
||||
and not filename.startswith("neru")
|
||||
and not filename.endswith("_base_cog.py")
|
||||
and not filename.endswith("_config.py")
|
||||
and not filename == "aimod_cog.py" # Avoid double-loading via aimod.py
|
||||
and not filename.startswith("femdom")
|
||||
and not filename == "VoiceGatewayCog.py"
|
||||
and not filename == "rp_messages.py"
|
||||
):
|
||||
# Special check for welcome_cog.py
|
||||
if filename == "welcome_cog.py":
|
||||
|
Loading…
x
Reference in New Issue
Block a user