16 Commits

Author SHA1 Message Date
pixeebot[bot]
5f63a24659 Sandbox URL Creation 2025-04-03 03:45:27 +00:00
pacnpal
2e7ed15038 Update birthday.py with status command 2025-03-12 14:54:14 -04:00
pacnpal
a472d1669b Create frogbot-scan-and-fix.yml 2025-02-12 22:23:21 -05:00
pacnpal
977a86b199 Create codacy.yml 2025-02-12 22:20:05 -05:00
pacnpal
0f634ed517 Merge pull request #11 from pacnpal/alert-autofix-3
Potential fix for code scanning alert no. 3: Clear-text logging of sensitive information
2025-02-12 22:17:57 -05:00
pacnpal
cbbfb0c318 Merge pull request #12 from pacnpal/alert-autofix-2
Potential fix for code scanning alert no. 2: Clear-text logging of sensitive information
2025-02-12 22:17:43 -05:00
pacnpal
c8f441eb8e Merge pull request #13 from pacnpal/alert-autofix-1
Potential fix for code scanning alert no. 1: Clear-text logging of sensitive information
2025-02-12 22:17:14 -05:00
pacnpal
4c6c7df1c8 Potential fix for code scanning alert no. 1: Clear-text logging of sensitive information
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-02-12 22:14:56 -05:00
pacnpal
104c9476b4 Potential fix for code scanning alert no. 2: Clear-text logging of sensitive information
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-02-12 22:14:43 -05:00
pacnpal
ce6138558f Potential fix for code scanning alert no. 3: Clear-text logging of sensitive information
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-02-12 22:14:28 -05:00
pacnpal
28177ed6ea Merge pull request #10 from pacnpal/alert-autofix-4
Potential fix for code scanning alert no. 4: Clear-text logging of sensitive information
2025-02-12 22:10:47 -05:00
pacnpal
113bb0b75b Merge pull request #9 from pacnpal/alert-autofix-5
Potential fix for code scanning alert no. 5: Clear-text logging of sensitive information
2025-02-12 22:10:28 -05:00
pacnpal
867cc1cd4e Potential fix for code scanning alert no. 4: Clear-text logging of sensitive information
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-02-12 22:08:52 -05:00
pacnpal
fdced4ad8b Potential fix for code scanning alert no. 5: Clear-text logging of sensitive information
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-02-12 22:08:36 -05:00
pacnpal
9fd6a2c7b8 Merge pull request #8 from pacnpal/pixeebot/drip-2025-02-05-pixee-python/fix-dataclass-defaults
Replace `dataclass` Mutable Default Values with Call to `field`
2025-02-12 22:05:02 -05:00
pixeebot[bot]
9deaa857bd Replace dataclass Mutable Default Values with Call to field 2025-02-05 03:06:36 +00:00
4 changed files with 190 additions and 22 deletions

61
.github/workflows/codacy.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow checks out code, performs a Codacy security scan
# and integrates the results with the
# GitHub Advanced Security code scanning feature. For more information on
# the Codacy security scan action usage and parameters, see
# https://github.com/codacy/codacy-analysis-cli-action.
# For more information on Codacy Analysis CLI in general, see
# https://github.com/codacy/codacy-analysis-cli.
name: Codacy Security Scan
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '36 1 * * 4'
permissions:
contents: read
jobs:
codacy-security-scan:
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
name: Codacy Security Scan
runs-on: ubuntu-latest
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout code
uses: actions/checkout@v4
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
verbose: true
output: results.sarif
format: sarif
# Adjust severity of non-security issues
gh-code-scanning-compat: true
# Force 0 exit code to allow SARIF file generation
# This will handover control about PR rejection to the GitHub side
max-allowed-issues: 2147483647
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif

View File

@@ -0,0 +1,65 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# Frogbot Scan and Fix does the following:
# Automatically creates pull requests with fixes for vulnerable project dependencies.
# Uses JFrog Xray to scan the project.
# Read more about Frogbot here - https://docs.jfrog-applications.jfrog.io/jfrog-applications/frogbot
# Some projects require creating a frogbot-config.yml file. Read more about it here - https://docs.jfrog-applications.jfrog.io/jfrog-applications/frogbot/setup-frogbot/frogbot-configuration
name: "Frogbot Scan and Fix"
on:
push:
branches: [ "main" ]
permissions:
contents: write
pull-requests: write
security-events: write
jobs:
create-fix-pull-requests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: jfrog/frogbot@5d9c42c30f1169d8be4ba5510b40e75ffcbbc2a9 # v2.21.2
env:
# [Mandatory if the two conditions below are met]
# 1. The project uses npm, yarn 2, NuGet or .NET to download its dependencies
# 2. The `installCommand` variable isn't set in your frogbot-config.yml file.
#
# The command that installs the project dependencies (e.g "npm i", "nuget restore" or "dotnet restore")
# JF_INSTALL_DEPS_CMD: ""
# [Mandatory]
# JFrog platform URL
JF_URL: ${{ secrets.JF_URL }}
# [Mandatory if JF_USER and JF_PASSWORD are not provided]
# JFrog access token with 'read' permissions on Xray service
JF_ACCESS_TOKEN: ${{ secrets.JF_ACCESS_TOKEN }}
# [Mandatory if JF_ACCESS_TOKEN is not provided]
# JFrog username with 'read' permissions for Xray. Must be provided with JF_PASSWORD
# JF_USER: ${{ secrets.JF_USER }}
# [Mandatory if JF_ACCESS_TOKEN is not provided]
# JFrog password. Must be provided with JF_USER
# JF_PASSWORD: ${{ secrets.JF_PASSWORD }}
# [Mandatory]
# The GitHub token automatically generated for the job
JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# [Optional]
# If the machine that runs Frogbot has no access to the internat, set the name of a remote repository
# in Artifactory, which proxies https://releases.jfrog.io/artifactory
# The 'frogbot' executable and other tools it needs will be downloaded through this repository.
# JF_RELEASES_REPO: ""
# [Optional]
# Frogbot will download the project dependencies, if they're not cached locally. To download the
# dependencies from a virtual repository in Artifactory, set the name of of the repository. There's no
# need to set this value, if it is set in the frogbot-config.yml file.
# JF_DEPS_REPO: ""

View File

@@ -37,7 +37,7 @@ async def birthday_context_menu(interaction: discord.Interaction, member: discor
birthday_role = interaction.guild.get_role(birthday_role_id) birthday_role = interaction.guild.get_role(birthday_role_id)
if not birthday_role: if not birthday_role:
logger.error(f"Birthday role {birthday_role_id} not found in guild {interaction.guild.id}") logger.error("Birthday role not found in the guild")
return await interaction.followup.send("The birthday role doesn't exist anymore. Please ask an admin to set it again.", ephemeral=True) return await interaction.followup.send("The birthday role doesn't exist anymore. Please ask an admin to set it again.", ephemeral=True)
# Assign the role, ignoring hierarchy # Assign the role, ignoring hierarchy
@@ -45,10 +45,10 @@ async def birthday_context_menu(interaction: discord.Interaction, member: discor
await member.add_roles(birthday_role, reason="Birthday role") await member.add_roles(birthday_role, reason="Birthday role")
logger.info(f"Birthday role assigned to {member.id} in guild {interaction.guild.id}") logger.info(f"Birthday role assigned to {member.id} in guild {interaction.guild.id}")
except discord.Forbidden: except discord.Forbidden:
logger.error(f"Failed to assign birthday role to {member.id} in guild {interaction.guild.id}: Insufficient permissions") logger.error("Failed to assign birthday role: Insufficient permissions")
return await interaction.followup.send("I don't have permission to assign that role.", ephemeral=True) return await interaction.followup.send("I don't have permission to assign that role.", ephemeral=True)
except discord.HTTPException as e: except discord.HTTPException as e:
logger.error(f"Failed to assign birthday role to {member.id} in guild {interaction.guild.id}: {str(e)}") logger.error(f"Failed to assign birthday role: {str(e)}")
return await interaction.followup.send("Failed to assign the birthday role due to a Discord error.", ephemeral=True) return await interaction.followup.send("Failed to assign the birthday role due to a Discord error.", ephemeral=True)
# Generate birthday message with random cakes (or pie) # Generate birthday message with random cakes (or pie)
@@ -63,7 +63,7 @@ async def birthday_context_menu(interaction: discord.Interaction, member: discor
if birthday_channel_id: if birthday_channel_id:
channel = interaction.client.get_channel(birthday_channel_id) channel = interaction.client.get_channel(birthday_channel_id)
if not channel: if not channel:
logger.warning(f"Birthday channel {birthday_channel_id} not found in guild {interaction.guild.id}") logger.warning("Birthday channel not found in the guild")
channel = interaction.channel channel = interaction.channel
else: else:
channel = interaction.channel channel = interaction.channel
@@ -76,7 +76,7 @@ async def birthday_context_menu(interaction: discord.Interaction, member: discor
try: try:
tz = ZoneInfo(timezone) tz = ZoneInfo(timezone)
except ZoneInfoNotFoundError: except ZoneInfoNotFoundError:
logger.warning(f"Invalid timezone {timezone} for guild {interaction.guild.id}, defaulting to UTC") logger.warning("Invalid timezone for the guild, defaulting to UTC")
await interaction.followup.send("Warning: Invalid timezone set. Defaulting to UTC.", ephemeral=True) await interaction.followup.send("Warning: Invalid timezone set. Defaulting to UTC.", ephemeral=True)
tz = ZoneInfo("UTC") tz = ZoneInfo("UTC")
@@ -196,12 +196,12 @@ class Birthday(commands.Cog):
birthday_role_id = await self.config.guild(ctx.guild).birthday_role() birthday_role_id = await self.config.guild(ctx.guild).birthday_role()
if not birthday_role_id: if not birthday_role_id:
logger.error(f"Birthday role not set for guild {ctx.guild.id}") logger.error("Birthday role not set for the guild")
return await ctx.send("The birthday role hasn't been set.", ephemeral=True) return await ctx.send("The birthday role hasn't been set.", ephemeral=True)
birthday_role = ctx.guild.get_role(birthday_role_id) birthday_role = ctx.guild.get_role(birthday_role_id)
if not birthday_role: if not birthday_role:
logger.error(f"Birthday role {birthday_role_id} not found in guild {ctx.guild.id}") logger.error("Birthday role not found in the guild")
return await ctx.send("The birthday role doesn't exist anymore.", ephemeral=True) return await ctx.send("The birthday role doesn't exist anymore.", ephemeral=True)
if birthday_role not in member.roles: if birthday_role not in member.roles:
@@ -209,12 +209,12 @@ class Birthday(commands.Cog):
try: try:
await member.remove_roles(birthday_role, reason="Birthday role manually removed") await member.remove_roles(birthday_role, reason="Birthday role manually removed")
logger.info(f"Birthday role manually removed from {member.id} in guild {ctx.guild.id}") logger.info(f"Birthday role manually removed from member {member.id}")
except discord.Forbidden: except discord.Forbidden:
logger.error(f"Failed to remove birthday role from {member.id} in guild {ctx.guild.id}: Insufficient permissions") logger.error(f"Failed to remove birthday role from member {member.id}: Insufficient permissions")
return await ctx.send("I don't have permission to remove that role.", ephemeral=True) return await ctx.send("I don't have permission to remove that role.", ephemeral=True)
except discord.HTTPException as e: except discord.HTTPException as e:
logger.error(f"Failed to remove birthday role from {member.id} in guild {ctx.guild.id}: {str(e)}") logger.error(f"Failed to remove birthday role from member {member.id} due to a Discord error: {str(e)}")
return await ctx.send("Failed to remove the birthday role due to a Discord error.", ephemeral=True) return await ctx.send("Failed to remove the birthday role due to a Discord error.", ephemeral=True)
# Remove scheduled task if it exists # Remove scheduled task if it exists
@@ -226,8 +226,50 @@ class Birthday(commands.Cog):
await ctx.send(f"Birthday role removed from {member.display_name}!", ephemeral=True) await ctx.send(f"Birthday role removed from {member.display_name}!", ephemeral=True)
except Exception as e: except Exception as e:
logger.error(f"Unexpected error in remove_birthday command: {str(e)}", exc_info=True) logger.error("Unexpected error in remove_birthday command", exc_info=True)
await ctx.send(f"An error occurred while removing the birthday role: {str(e)}", ephemeral=True) await ctx.send(f"An error occurred while removing the birthday role: {str(e)}", ephemeral=True)
@commands.hybrid_command(name="status")
@app_commands.guild_only()
async def status(self, ctx: commands.Context):
"""Show the status of the Birthday cog in the current server."""
try:
# Check if the user has permission to use this command
allowed_roles = await self.config.guild(ctx.guild).allowed_roles()
if not any(role.id in allowed_roles for role in ctx.author.roles):
logger.warning(f"User {ctx.author.id} attempted to use status command without permission")
return await ctx.send("You don't have permission to use this command.", ephemeral=True)
# Fetch configuration details
birthday_role_id = await self.config.guild(ctx.guild).birthday_role()
birthday_role = ctx.guild.get_role(birthday_role_id) if birthday_role_id else None
allowed_roles_ids = await self.config.guild(ctx.guild).allowed_roles()
allowed_roles = [ctx.guild.get_role(role_id) for role_id in allowed_roles_ids]
timezone = await self.config.guild(ctx.guild).timezone()
birthday_channel_id = await self.config.guild(ctx.guild).birthday_channel()
birthday_channel = ctx.guild.get_channel(birthday_channel_id) if birthday_channel_id else None
scheduled_tasks = await self.config.guild(ctx.guild).scheduled_tasks()
# Construct status message
status_message = f"**Birthday Cog Status for {ctx.guild.name}**\n"
status_message += f"**Birthday Role:** {birthday_role.name if birthday_role else 'Not Set'}\n"
status_message += f"**Allowed Roles:** {', '.join(role.name for role in allowed_roles if role) if allowed_roles else 'None'}\n"
status_message += f"**Timezone:** {timezone}\n"
status_message += f"**Birthday Channel:** {birthday_channel.mention if birthday_channel else 'Not Set'}\n"
status_message += f"**Scheduled Tasks:** {len(scheduled_tasks)}\n"
if scheduled_tasks:
status_message += "\n**Upcoming Tasks:**\n"
for member_id, task_info in scheduled_tasks.items():
member = ctx.guild.get_member(int(member_id))
role = ctx.guild.get_role(task_info["role_id"])
remove_at = datetime.fromisoformat(task_info["remove_at"]).replace(tzinfo=ZoneInfo(timezone))
status_message += f"- {member.display_name} ({role.name}) at {remove_at}\n"
await ctx.send(status_message, ephemeral=True)
except Exception as e:
logger.error(f"Unexpected error in status command: {str(e)}", exc_info=True)
await ctx.send(f"An error occurred while fetching the status: {str(e)}", ephemeral=True)
async def daily_cleanup(self): async def daily_cleanup(self):
"""Daily task to ensure all birthday roles are properly removed.""" """Daily task to ensure all birthday roles are properly removed."""
@@ -348,12 +390,12 @@ class Birthday(commands.Cog):
birthday_role_id = await self.config.guild(ctx.guild).birthday_role() birthday_role_id = await self.config.guild(ctx.guild).birthday_role()
if not birthday_role_id: if not birthday_role_id:
logger.error(f"Birthday role not set for guild {ctx.guild.id}") logger.error("Birthday role not set for the guild")
return await ctx.send("The birthday role hasn't been set. An admin needs to set it using `/setrole`.", ephemeral=True) return await ctx.send("The birthday role hasn't been set. An admin needs to set it using `/setrole`.", ephemeral=True)
birthday_role = ctx.guild.get_role(birthday_role_id) birthday_role = ctx.guild.get_role(birthday_role_id)
if not birthday_role: if not birthday_role:
logger.error(f"Birthday role {birthday_role_id} not found in guild {ctx.guild.id}") logger.error("Birthday role not found in the guild")
return await ctx.send("The birthday role doesn't exist anymore. Please ask an admin to set it again.", ephemeral=True) return await ctx.send("The birthday role doesn't exist anymore. Please ask an admin to set it again.", ephemeral=True)
# Assign the role, ignoring hierarchy # Assign the role, ignoring hierarchy
@@ -361,10 +403,10 @@ class Birthday(commands.Cog):
await member.add_roles(birthday_role, reason="Birthday role") await member.add_roles(birthday_role, reason="Birthday role")
logger.info(f"Birthday role assigned to {member.id} in guild {ctx.guild.id}") logger.info(f"Birthday role assigned to {member.id} in guild {ctx.guild.id}")
except discord.Forbidden: except discord.Forbidden:
logger.error(f"Failed to assign birthday role to {member.id} in guild {ctx.guild.id}: Insufficient permissions") logger.error("Failed to assign birthday role: Insufficient permissions")
return await ctx.send("I don't have permission to assign that role.", ephemeral=True) return await ctx.send("I don't have permission to assign that role.", ephemeral=True)
except discord.HTTPException as e: except discord.HTTPException as e:
logger.error(f"Failed to assign birthday role to {member.id} in guild {ctx.guild.id}: {str(e)}") logger.error(f"Failed to assign birthday role: {str(e)}")
return await ctx.send("Failed to assign the birthday role due to a Discord error.", ephemeral=True) return await ctx.send("Failed to assign the birthday role due to a Discord error.", ephemeral=True)
# Generate birthday message with random cakes (or pie) # Generate birthday message with random cakes (or pie)
@@ -379,7 +421,7 @@ class Birthday(commands.Cog):
if birthday_channel_id: if birthday_channel_id:
channel = self.bot.get_channel(birthday_channel_id) channel = self.bot.get_channel(birthday_channel_id)
if not channel: # If the set channel doesn't exist anymore if not channel: # If the set channel doesn't exist anymore
logger.warning(f"Birthday channel {birthday_channel_id} not found in guild {ctx.guild.id}") logger.warning("Birthday channel not found in the guild")
channel = ctx.channel channel = ctx.channel
else: else:
channel = ctx.channel channel = ctx.channel
@@ -392,7 +434,7 @@ class Birthday(commands.Cog):
try: try:
tz = ZoneInfo(timezone) tz = ZoneInfo(timezone)
except ZoneInfoNotFoundError: except ZoneInfoNotFoundError:
logger.warning(f"Invalid timezone {timezone} for guild {ctx.guild.id}, defaulting to UTC") logger.warning("Invalid timezone for the guild, defaulting to UTC")
await ctx.send("Warning: Invalid timezone set. Defaulting to UTC.", ephemeral=True) await ctx.send("Warning: Invalid timezone set. Defaulting to UTC.", ephemeral=True)
tz = ZoneInfo("UTC") tz = ZoneInfo("UTC")
@@ -401,7 +443,7 @@ class Birthday(commands.Cog):
await self.schedule_birthday_role_removal(ctx.guild, member, birthday_role, midnight) await self.schedule_birthday_role_removal(ctx.guild, member, birthday_role, midnight)
except Exception as e: except Exception as e:
logger.error(f"Unexpected error in birthday command: {str(e)}", exc_info=True) logger.error(f"Unexpected error in birthday command", exc_info=True)
await ctx.send(f"An error occurred: {str(e)}", ephemeral=True) await ctx.send(f"An error occurred: {str(e)}", ephemeral=True)
@commands.hybrid_command(name="bdaycheck") @commands.hybrid_command(name="bdaycheck")

View File

@@ -1,7 +1,7 @@
"""Constants for VideoProcessor""" """Constants for VideoProcessor"""
from typing import Dict, List, Union from typing import Dict, List, Union
from dataclasses import dataclass from dataclasses import field, dataclass
from enum import Enum from enum import Enum
class ReactionType(Enum): class ReactionType(Enum):
@@ -27,9 +27,9 @@ class ReactionEmojis:
@dataclass(frozen=True) @dataclass(frozen=True)
class ProgressEmojis: class ProgressEmojis:
"""Emoji sequences for progress indicators""" """Emoji sequences for progress indicators"""
NUMBERS: List[str] = ('1', '2', '3', '4', '5') NUMBERS: List[str] = field(default_factory=lambda: ('1', '2', '3', '4', '5'))
PROGRESS: List[str] = ('', '🟨', '🟩') PROGRESS: List[str] = field(default_factory=lambda: ('', '🟨', '🟩'))
DOWNLOAD: List[str] = ('0', '2', '4', '6', '8', '🔟') DOWNLOAD: List[str] = field(default_factory=lambda: ('0', '2', '4', '6', '8', '🔟'))
# Main reactions dictionary with type hints # Main reactions dictionary with type hints
REACTIONS: Dict[str, Union[str, List[str]]] = { REACTIONS: Dict[str, Union[str, List[str]]] = {