mirror of
https://gitlab.com/pancakes1234/wdiscordbotserver.git
synced 2025-06-16 07:14:21 -06:00
Merge branch 'main' of https://gitlab.com/pancakes1234/wdiscordbotserver
This commit is contained in:
commit
121e0d9955
10
.gitlab-ci.yml
Normal file
10
.gitlab-ci.yml
Normal file
@ -0,0 +1,10 @@
|
||||
# The Docker image that will be used to build your app
|
||||
image: node:lts
|
||||
create-pages:
|
||||
pages:
|
||||
# The folder that contains the files to be exposed at the Page URL
|
||||
publish: website
|
||||
rules:
|
||||
# This ensures that only pushes to the default branch will trigger
|
||||
# a pages deploy
|
||||
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
@ -49,7 +49,7 @@ WDiscordBot Rules and Security Policies
|
||||
3. **Attribution Requirements:**
|
||||
- Any derivative work or modification of the bot’s code must include a clear credit line to the original developers:
|
||||
```
|
||||
Original Code Developed by Zacarias Posey, Izzy Perez, Milly, and JW.
|
||||
Original Code Developed by Zacarias Posey, Izzy Perez, Milly, Lillian, and JW.
|
||||
```
|
||||
- Failure to include attribution may result in removal requests, legal action, or a copyright takedown notice.
|
||||
|
||||
|
@ -455,8 +455,9 @@ Server Rules:
|
||||
{SERVER_RULES}
|
||||
---
|
||||
|
||||
Channel Context:
|
||||
You will be provided with the following information about the channel where the message was sent:
|
||||
User and Channel Context:
|
||||
You will be provided with the following information:
|
||||
- User's Server Role: The role of the user in the server (e.g., "Server Owner", "Admin", "Moderator", "Member").
|
||||
- Channel Category: The name of the category the channel belongs to (e.g., "General", "NSFW Zone", "Gaming").
|
||||
- Channel Age-Restricted (Discord Setting): A boolean (true/false) indicating if the channel is marked as age-restricted (NSFW) in Discord's settings.
|
||||
|
||||
@ -521,6 +522,20 @@ Example Response (Suicidal Content):
|
||||
}}
|
||||
"""
|
||||
|
||||
member = message.author # This is a discord.Member object
|
||||
server_role_str = "Member" # Default
|
||||
|
||||
if member == message.guild.owner:
|
||||
server_role_str = "Server Owner"
|
||||
elif member.guild_permissions.administrator:
|
||||
server_role_str = "Admin"
|
||||
else:
|
||||
# Check for generic moderator permissions if not owner or admin
|
||||
perms = member.guild_permissions
|
||||
if perms.manage_messages or perms.kick_members or perms.ban_members or perms.moderate_members:
|
||||
server_role_str = "Moderator"
|
||||
# else, it remains "Member"
|
||||
|
||||
user_prompt_content_list = [
|
||||
{
|
||||
"type": "text",
|
||||
@ -531,6 +546,7 @@ Example Response (Suicidal Content):
|
||||
|
||||
Message Details:
|
||||
- Author: {message.author.name} (ID: {message.author.id})
|
||||
- Server Role: {server_role_str}
|
||||
- Channel: #{message.channel.name} (ID: {message.channel.id})
|
||||
- Channel Category: {message.channel.category.name if message.channel.category else "No Category"}
|
||||
- Channel Age-Restricted (Discord Setting): {message.channel.is_nsfw()}
|
||||
@ -654,7 +670,7 @@ Now, analyze the provided message content based on the rules and instructions gi
|
||||
try:
|
||||
mod_log_api_secret = os.getenv("MOD_LOG_API_SECRET")
|
||||
if mod_log_api_secret:
|
||||
post_url = f"https://slipstreamm.dev/dashboard/api/guilds/{guild_id}/ai-moderation-action"
|
||||
post_url = f"https://slipstreamm.dev/dashboard/api/guilds/{guild_id}/ai-moderation-action" #will be replaceing later with the Learnhelp API
|
||||
payload = {
|
||||
"timestamp": current_timestamp_iso,
|
||||
"guild_id": guild_id,
|
||||
@ -705,7 +721,7 @@ Now, analyze the provided message content based on the rules and instructions gi
|
||||
msg_content = message.content if message.content else "*No text content*"
|
||||
notification_embed.add_field(name="Message Content", value=msg_content[:1024], inline=False)
|
||||
# Use the model_used variable that was defined earlier
|
||||
notification_embed.set_footer(text=f"AI Model: {model_used}")
|
||||
notification_embed.set_footer(text=f"AI Model: {model_used}. Learnhelp AI Moderation.")
|
||||
notification_embed.timestamp = discord.utils.utcnow() # Using discord.utils.utcnow() which is still supported
|
||||
|
||||
action_taken_message = "" # To append to the notification
|
||||
|
93
cogs/commits.py
Normal file
93
cogs/commits.py
Normal file
@ -0,0 +1,93 @@
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
import aiohttp
|
||||
import urllib.parse
|
||||
|
||||
# A view with a button to display commit diffs.
|
||||
class DiffView(discord.ui.View):
|
||||
def __init__(self, diffs):
|
||||
super().__init__(timeout=60) # Button active for 60 seconds.
|
||||
self.diffs = diffs
|
||||
|
||||
@discord.ui.button(label="Show Diffs", style=discord.ButtonStyle.primary)
|
||||
async def show_diffs(self, button: discord.ui.Button, interaction: discord.Interaction):
|
||||
diff_message = ""
|
||||
# Format each file's diff info.
|
||||
for diff in self.diffs:
|
||||
file_path = diff.get("new_path", "Unknown file")
|
||||
diff_text = diff.get("diff", "No diff available")
|
||||
diff_message += f"**File:** {file_path}\n```diff\n{diff_text}\n```\n"
|
||||
# Truncate if too long.
|
||||
if len(diff_message) > 1900:
|
||||
diff_message = diff_message[:1900] + "\n...[truncated]"
|
||||
await interaction.response.send_message(diff_message, ephemeral=True)
|
||||
|
||||
# The cog defining the /changes command.
|
||||
class GitlabChanges(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
self.repo = "pancakes1234/wdiscordbotserver"
|
||||
# URL-encode the repository identifier for the GitLab API.
|
||||
self.project_id = urllib.parse.quote_plus(self.repo)
|
||||
|
||||
@app_commands.command(
|
||||
name="changes",
|
||||
description="Display details of the latest commit for the bot"
|
||||
)
|
||||
async def changes(self, interaction: discord.Interaction):
|
||||
# Defer the response to allow for asynchronous API calls.
|
||||
await interaction.response.defer()
|
||||
|
||||
# Fetch the list of commits via GitLab's API.
|
||||
async with aiohttp.ClientSession() as session:
|
||||
commits_url = f"https://gitlab.com/api/v4/projects/{self.project_id}/repository/commits"
|
||||
async with session.get(commits_url) as resp:
|
||||
if resp.status != 200:
|
||||
await interaction.followup.send("Failed to fetch commits from GitLab.")
|
||||
return
|
||||
commits = await resp.json()
|
||||
|
||||
if not commits:
|
||||
await interaction.followup.send("No commits found.")
|
||||
return
|
||||
|
||||
# Retrieve the most recent commit.
|
||||
latest_commit = commits[0]
|
||||
commit_hash = latest_commit.get("id", "N/A")
|
||||
commit_message = latest_commit.get("message", "No commit message provided.")
|
||||
author_name = latest_commit.get("author_name", "Unknown author")
|
||||
# Construct a clickable link for the commit.
|
||||
commit_url = f"https://gitlab.com/{self.repo}/-/commit/{commit_hash}"
|
||||
|
||||
# Fetch diff details for the commit.
|
||||
async with aiohttp.ClientSession() as session:
|
||||
diff_url = f"https://gitlab.com/api/v4/projects/{self.project_id}/repository/commits/{commit_hash}/diff"
|
||||
async with session.get(diff_url) as diff_resp:
|
||||
if diff_resp.status != 200:
|
||||
diffs = []
|
||||
files_field = "Could not fetch diff details."
|
||||
else:
|
||||
diffs = await diff_resp.json()
|
||||
file_list = [diff.get("new_path", "Unknown file") for diff in diffs]
|
||||
files_field = "\n".join(file_list) if file_list else "No files changed."
|
||||
|
||||
# Build the embed with commit details.
|
||||
embed = discord.Embed(
|
||||
title="Latest Commit",
|
||||
description=f"Latest commit from **{self.repo}**",
|
||||
color=discord.Color.blurple()
|
||||
)
|
||||
embed.add_field(name="Commit Hash", value=commit_hash, inline=False)
|
||||
embed.add_field(name="Author", value=author_name, inline=False)
|
||||
embed.add_field(name="Description", value=commit_message, inline=False)
|
||||
embed.add_field(name="Files Edited", value=files_field, inline=False)
|
||||
embed.add_field(name="Commit Link", value=f"[View Commit]({commit_url})", inline=False)
|
||||
|
||||
# Attach the diff button view so the user can check diffs on demand.
|
||||
view = DiffView(diffs)
|
||||
await interaction.followup.send(embed=embed, view=view)
|
||||
|
||||
# Standard setup function for loading the cog.
|
||||
async def setup(bot: commands.Bot):
|
||||
await bot.add_cog(GitlabChanges(bot))
|
158
cogs/letsgogambling.py
Normal file
158
cogs/letsgogambling.py
Normal file
@ -0,0 +1,158 @@
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
import random
|
||||
from typing import Optional
|
||||
|
||||
# A sample shop inventory. Here the keys are item names (in lowercase)
|
||||
# and the values are the cost. When selling, we assume you get about half the price.
|
||||
SHOP_ITEMS = {
|
||||
"pen": 20,
|
||||
"notebook": 50,
|
||||
"backpack": 100,
|
||||
"headphones": 200,
|
||||
"Temu Iphone 69326": 75,
|
||||
"Temu Macbook water": 100,
|
||||
"phone": 150,
|
||||
"Dell Optiplex 3020 M": 200,
|
||||
"Asus Vivobook 14": 175,
|
||||
"Kasane Teto plushie": 50,
|
||||
"miku plushie": 50,
|
||||
"RARE Golden Teto Plusie": 20000,
|
||||
}
|
||||
|
||||
class Fun(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
# A simple in-memory economy store where each user starts with $100 and an empty inventory.
|
||||
self.economy = {} # {user_id: {"balance": int, "inventory": list}}
|
||||
|
||||
def get_account(self, user_id: int) -> dict:
|
||||
"""Creates an account for the user if one does not exist and returns it."""
|
||||
if user_id not in self.economy:
|
||||
self.economy[user_id] = {"balance": 100, "inventory": []}
|
||||
return self.economy[user_id]
|
||||
|
||||
# Create a slash command group called "fun"
|
||||
fun_group = app_commands.Group(name="fun", description="Fun economy commands")
|
||||
|
||||
@fun_group.command(name="work", description="Work a job and earn money!")
|
||||
async def work(self, interaction: discord.Interaction):
|
||||
account = self.get_account(interaction.user.id)
|
||||
earned = random.randint(50, 150)
|
||||
job = random.choice(["barista", "cashier", "developer", "bartender", "freelancer"])
|
||||
account["balance"] += earned
|
||||
await interaction.response.send_message(
|
||||
f"You worked as a {job} and earned ${earned}.\nYour new balance is ${account['balance']}."
|
||||
)
|
||||
|
||||
@fun_group.command(name="sell", description="Sell an item from your inventory.")
|
||||
@app_commands.describe(item="The item you wish to sell.")
|
||||
async def sell(self, interaction: discord.Interaction, item: str):
|
||||
account = self.get_account(interaction.user.id)
|
||||
# Find the item in your inventory (ignoring case)
|
||||
item_in_inventory = None
|
||||
for inv_item in account["inventory"]:
|
||||
if inv_item.lower() == item.lower():
|
||||
item_in_inventory = inv_item
|
||||
break
|
||||
|
||||
if not item_in_inventory:
|
||||
await interaction.response.send_message("You don't have that item in your inventory!")
|
||||
return
|
||||
|
||||
# Remove the item and determine its selling price.
|
||||
account["inventory"].remove(item_in_inventory)
|
||||
# Use the shop price if known, otherwise default to $20; you get roughly half when selling.
|
||||
sold_price = SHOP_ITEMS.get(item_in_inventory.lower(), 20) // 2
|
||||
account["balance"] += sold_price
|
||||
await interaction.response.send_message(
|
||||
f"You sold your {item_in_inventory} for ${sold_price}.\nYour new balance is ${account['balance']}."
|
||||
)
|
||||
|
||||
@fun_group.command(name="steal", description="Attempt to steal money from another user!")
|
||||
@app_commands.describe(target="The member you want to steal from.")
|
||||
async def steal(self, interaction: discord.Interaction, target: discord.Member):
|
||||
# Prevent stealing from oneself
|
||||
if target.id == interaction.user.id:
|
||||
await interaction.response.send_message("You can't steal from yourself!")
|
||||
return
|
||||
|
||||
thief = self.get_account(interaction.user.id)
|
||||
victim = self.get_account(target.id)
|
||||
if victim["balance"] < 50:
|
||||
await interaction.response.send_message(f"{target.display_name} doesn't have enough money to steal!")
|
||||
return
|
||||
|
||||
# Simulate theft with a 30% chance of success.
|
||||
if random.random() < 0.3:
|
||||
# On success: steal a random amount between $10 and up to one-third of the victim's balance (max $100).
|
||||
stolen = random.randint(10, min(100, victim["balance"] // 3))
|
||||
victim["balance"] -= stolen
|
||||
thief["balance"] += stolen
|
||||
await interaction.response.send_message(
|
||||
f"You successfully stole ${stolen} from {target.display_name}!\nYour new balance is ${thief['balance']}."
|
||||
)
|
||||
else:
|
||||
# On failure, the thief is fined a small amount.
|
||||
fine = random.randint(5, 20)
|
||||
thief["balance"] = max(0, thief["balance"] - fine)
|
||||
await interaction.response.send_message(
|
||||
f"You got caught trying to steal from {target.display_name}!\nYou were fined ${fine}.\nYour new balance is ${thief['balance']}."
|
||||
)
|
||||
|
||||
@fun_group.command(name="shop", description="View shop items or buy an item.")
|
||||
@app_commands.describe(item="The item you wish to buy (optional). Leave empty to view available items.")
|
||||
async def shop(self, interaction: discord.Interaction, item: Optional[str] = None):
|
||||
account = self.get_account(interaction.user.id)
|
||||
if item is None:
|
||||
# List all available shop items.
|
||||
items_list = "\n".join([f"{name.title()} - ${price}" for name, price in SHOP_ITEMS.items()])
|
||||
response = f"**Available Items:**\n{items_list}\n\nTo buy an item, use `/fun shop <item>`."
|
||||
await interaction.response.send_message(response)
|
||||
else:
|
||||
item_key = item.lower()
|
||||
if item_key not in SHOP_ITEMS:
|
||||
await interaction.response.send_message("That item is not available in the shop!")
|
||||
return
|
||||
price = SHOP_ITEMS[item_key]
|
||||
if account["balance"] < price:
|
||||
await interaction.response.send_message("You don't have enough money to buy that item!")
|
||||
return
|
||||
# Deduct the price and add the item to the user's inventory.
|
||||
account["balance"] -= price
|
||||
account["inventory"].append(item_key)
|
||||
await interaction.response.send_message(
|
||||
f"You bought a {item.title()} for ${price}.\nYour new balance is ${account['balance']}."
|
||||
)
|
||||
|
||||
@fun_group.command(name="gamble", description="Gamble a certain amount of money in a coin flip!")
|
||||
@app_commands.describe(amount="The amount of money you want to gamble.")
|
||||
async def gamble(self, interaction: discord.Interaction, amount: int):
|
||||
if amount <= 0:
|
||||
await interaction.response.send_message("You must gamble a positive amount!")
|
||||
return
|
||||
|
||||
account = self.get_account(interaction.user.id)
|
||||
if account["balance"] < amount:
|
||||
await interaction.response.send_message("You don't have that much money!")
|
||||
return
|
||||
|
||||
# Simple coin flip: win doubles your bet, lose subtracts it.
|
||||
if random.choice([True, False]):
|
||||
account["balance"] += amount
|
||||
await interaction.response.send_message(
|
||||
f"You won! You earned an extra ${amount}.\nYour new balance is ${account['balance']}."
|
||||
)
|
||||
else:
|
||||
account["balance"] -= amount
|
||||
await interaction.response.send_message(
|
||||
f"You lost the gamble and lost ${amount}.\nYour new balance is ${account['balance']}."
|
||||
)
|
||||
|
||||
# The setup function to add this cog and register the command group.
|
||||
async def setup(bot: commands.Bot):
|
||||
cog = Fun(bot)
|
||||
await bot.add_cog(cog)
|
||||
# Register the entire /fun group to the bot's command tree.
|
||||
bot.tree.add_command(Fun.fun_group)
|
161
cogs/serverconfig.py
Normal file
161
cogs/serverconfig.py
Normal file
@ -0,0 +1,161 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord import app_commands
|
||||
import os
|
||||
import json
|
||||
|
||||
# Path to the JSON config file
|
||||
CONFIG_FILE = "/home/server/serverconfig.json"
|
||||
|
||||
def load_config() -> dict:
|
||||
"""Load the server configuration from file.
|
||||
If the file does not exist or is invalid, create a new empty configuration."""
|
||||
if not os.path.exists(CONFIG_FILE):
|
||||
with open(CONFIG_FILE, "w") as f:
|
||||
json.dump({}, f)
|
||||
return {}
|
||||
try:
|
||||
with open(CONFIG_FILE, "r") as f:
|
||||
return json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
return {}
|
||||
|
||||
def save_config(data: dict) -> None:
|
||||
"""Save the configuration JSON to file."""
|
||||
with open(CONFIG_FILE, "w") as f:
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
async def global_disabled_check(interaction: discord.Interaction) -> bool:
|
||||
"""
|
||||
Global check for all app (slash) commands.
|
||||
If the command (except for serverconfig itself) is marked as disabled in this server’s config,
|
||||
send an ephemeral message and prevent execution.
|
||||
"""
|
||||
# If interaction comes from a DM, allow it.
|
||||
if interaction.guild is None:
|
||||
return True
|
||||
|
||||
# Always allow the serverconfig command so admins can change settings.
|
||||
if interaction.command and interaction.command.name == "serverconfig":
|
||||
return True
|
||||
|
||||
config = load_config()
|
||||
guild_id = str(interaction.guild.id)
|
||||
disabled_commands = config.get(guild_id, [])
|
||||
|
||||
if interaction.command and interaction.command.name in disabled_commands:
|
||||
if not interaction.response.is_done():
|
||||
await interaction.response.send_message(
|
||||
"This command has been disabled by server admins.", ephemeral=True
|
||||
)
|
||||
# Raising a CheckFailure prevents the command from running.
|
||||
raise app_commands.CheckFailure("Command disabled.")
|
||||
return True
|
||||
|
||||
class ServerConfigCog(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
|
||||
@app_commands.command(
|
||||
name="serverconfig",
|
||||
description="Enable or disable a command in this server."
|
||||
)
|
||||
@app_commands.describe(
|
||||
command="The name of the command to configure",
|
||||
enabled="Type 'yes' to enable or 'no' to disable."
|
||||
)
|
||||
async def serverconfig(
|
||||
self,
|
||||
interaction: discord.Interaction,
|
||||
command: str,
|
||||
enabled: str
|
||||
):
|
||||
# Check if the user has admin permissions.
|
||||
if not interaction.user.guild_permissions.administrator:
|
||||
await interaction.response.send_message(
|
||||
"You do not have permission to use this command.",
|
||||
ephemeral=True
|
||||
)
|
||||
return
|
||||
|
||||
# Normalize the enabled flag.
|
||||
enabled_flag = enabled.lower()
|
||||
if enabled_flag not in ["yes", "no"]:
|
||||
await interaction.response.send_message(
|
||||
"Invalid 'enabled' option. Please use 'yes' or 'no'.",
|
||||
ephemeral=True
|
||||
)
|
||||
return
|
||||
|
||||
# Verify that the provided command exists.
|
||||
found = False
|
||||
# Check the classic text commands.
|
||||
for cmd in self.bot.commands:
|
||||
if cmd.name == command:
|
||||
found = True
|
||||
break
|
||||
# Also check application (slash) commands from the tree.
|
||||
if not found:
|
||||
for cmd in self.bot.tree.get_commands():
|
||||
if cmd.name == command:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
await interaction.response.send_message(
|
||||
f"The command '{command}' was not found.",
|
||||
ephemeral=True
|
||||
)
|
||||
return
|
||||
|
||||
# Load the configuration.
|
||||
config = load_config()
|
||||
guild_id = str(interaction.guild.id)
|
||||
if guild_id not in config:
|
||||
config[guild_id] = []
|
||||
|
||||
if enabled_flag == "no":
|
||||
# Add the command to the disabled list if not already present.
|
||||
if command not in config[guild_id]:
|
||||
config[guild_id].append(command)
|
||||
save_config(config)
|
||||
await interaction.response.send_message(
|
||||
f"Command '{command}' has been **disabled** in this server.",
|
||||
ephemeral=True
|
||||
)
|
||||
else: # enabled_flag == "yes"
|
||||
# Remove the command from the disabled list if present.
|
||||
if command in config[guild_id]:
|
||||
config[guild_id].remove(command)
|
||||
save_config(config)
|
||||
await interaction.response.send_message(
|
||||
f"Command '{command}' has been **enabled** in this server.",
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
@serverconfig.autocomplete("command")
|
||||
async def command_autocomplete(
|
||||
self, interaction: discord.Interaction, current: str
|
||||
) -> list[app_commands.Choice[str]]:
|
||||
"""
|
||||
Autocomplete for the 'command' parameter.
|
||||
It searches both classic and slash commands for matches.
|
||||
"""
|
||||
choices = set()
|
||||
# Get names of text commands.
|
||||
for cmd in self.bot.commands:
|
||||
choices.add(cmd.name)
|
||||
# Get names of app commands.
|
||||
for cmd in self.bot.tree.get_commands():
|
||||
choices.add(cmd.name)
|
||||
# Filter and send at most 25 matching choices.
|
||||
filtered = [
|
||||
app_commands.Choice(name=cmd, value=cmd)
|
||||
for cmd in choices
|
||||
if current.lower() in cmd.lower()
|
||||
]
|
||||
return filtered[:25]
|
||||
|
||||
async def setup(bot: commands.Bot):
|
||||
# Register the global check – it will run for every application (slash) command.
|
||||
bot.tree.add_check(global_disabled_check)
|
||||
await bot.add_cog(ServerConfigCog(bot))
|
0
website/.gitkeep
Normal file
0
website/.gitkeep
Normal file
Loading…
x
Reference in New Issue
Block a user