feat: add multiple discord bot cogs
Adds new cogs including DataManager, Hiring, KofiShop, Logging, ModMail, MORS, ServiceReview, StaffMsg, and Translator to enhance bot functionality for data management, hiring processes, logging events, and more.
This commit is contained in:
276
logging/logging.py
Normal file
276
logging/logging.py
Normal file
@@ -0,0 +1,276 @@
|
||||
import discord
|
||||
from redbot.core import commands, Config
|
||||
import datetime
|
||||
from typing import Dict, Optional, Union
|
||||
|
||||
class Logging(commands.Cog):
|
||||
"""
|
||||
A cog for comprehensive server event logging.
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=1234567893, force_registration=True)
|
||||
default_guild = {
|
||||
"log_channel": None,
|
||||
"logged_events": {}, # For DataManager
|
||||
"enabled_events": {
|
||||
"on_message_delete": True,
|
||||
"on_message_edit": True,
|
||||
"on_member_join": True,
|
||||
"on_member_remove": True,
|
||||
"on_member_update": True,
|
||||
"on_voice_state_update": True,
|
||||
"on_invite_create": True,
|
||||
},
|
||||
}
|
||||
self.config.register_guild(**default_guild)
|
||||
|
||||
async def _send_log(self, guild: discord.Guild, embed: discord.Embed, event_name: str):
|
||||
"""Helper function to send logs."""
|
||||
enabled_events = await self.config.guild(guild).enabled_events()
|
||||
if not enabled_events.get(event_name, False):
|
||||
return
|
||||
|
||||
log_channel_id = await self.config.guild(guild).log_channel()
|
||||
if not log_channel_id:
|
||||
return
|
||||
|
||||
log_channel = guild.get_channel(log_channel_id)
|
||||
if isinstance(log_channel, discord.TextChannel):
|
||||
try:
|
||||
log_message = await log_channel.send(embed=embed)
|
||||
if event_name in ["on_message_delete", "on_message_edit"]:
|
||||
async with self.config.guild(guild).logged_events() as events:
|
||||
events[str(log_message.id)] = datetime.datetime.now(
|
||||
datetime.timezone.utc
|
||||
).isoformat()
|
||||
except discord.Forbidden:
|
||||
pass # Bot lacks permissions
|
||||
|
||||
# --- SETTINGS COMMANDS ---
|
||||
@commands.group(aliases=["logset"]) # type: ignore
|
||||
@commands.guild_only()
|
||||
@commands.admin_or_permissions(manage_guild=True)
|
||||
async def loggingset(self, ctx: commands.Context):
|
||||
"""Configure logging settings."""
|
||||
pass
|
||||
|
||||
@loggingset.command(name="channel")
|
||||
async def set_log_channel(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
"""Set the channel where logs will be sent."""
|
||||
if not ctx.guild:
|
||||
return
|
||||
await self.config.guild(ctx.guild).log_channel.set(channel.id)
|
||||
await ctx.send(f"Log channel has been set to {channel.mention}")
|
||||
|
||||
@loggingset.command(name="toggle")
|
||||
async def toggle_log_event(self, ctx: commands.Context, event: str):
|
||||
"""Toggle logging for a specific event."""
|
||||
if not ctx.guild:
|
||||
return
|
||||
|
||||
valid_events = list((await self.config.guild(ctx.guild).enabled_events()).keys())
|
||||
if event not in valid_events:
|
||||
await ctx.send(f"Invalid event. Valid events are: `{'`, `'.join(valid_events)}`")
|
||||
return
|
||||
|
||||
async with self.config.guild(ctx.guild).enabled_events() as events:
|
||||
current_status = events.get(event, False)
|
||||
events[event] = not current_status
|
||||
new_status = "enabled" if not current_status else "disabled"
|
||||
await ctx.send(f"Logging for `{event}` has been {new_status}.")
|
||||
|
||||
# --- EVENT LISTENERS ---
|
||||
@commands.Cog.listener()
|
||||
async def on_message_delete(self, message: discord.Message):
|
||||
"""Log when a message is deleted."""
|
||||
if message.author.bot or not message.guild:
|
||||
return
|
||||
|
||||
location = ""
|
||||
if isinstance(message.channel, (discord.TextChannel, discord.Thread)):
|
||||
location = message.channel.mention
|
||||
elif isinstance(message.channel, discord.DMChannel):
|
||||
location = f"DM with {message.channel.recipient}"
|
||||
elif isinstance(message.channel, discord.GroupChannel):
|
||||
location = f"Group DM ({message.channel.id})"
|
||||
else:
|
||||
location = f"Channel ID: {message.channel.id}"
|
||||
|
||||
embed = discord.Embed(
|
||||
description=f"**Message deleted in {location}**\n{message.content or 'No text content.'}",
|
||||
color=discord.Color.red(),
|
||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||
)
|
||||
embed.set_author(
|
||||
name=f"{message.author.name} ({message.author.id})",
|
||||
icon_url=message.author.display_avatar.url,
|
||||
)
|
||||
await self._send_log(message.guild, embed, "on_message_delete")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message_edit(self, before: discord.Message, after: discord.Message):
|
||||
"""Log when a message is edited."""
|
||||
if before.author.bot or not before.guild or before.content == after.content:
|
||||
return
|
||||
|
||||
location = ""
|
||||
if isinstance(before.channel, (discord.TextChannel, discord.Thread)):
|
||||
location = before.channel.mention
|
||||
elif isinstance(before.channel, discord.DMChannel):
|
||||
location = f"DM with {before.channel.recipient}"
|
||||
elif isinstance(before.channel, discord.GroupChannel):
|
||||
location = f"Group DM ({before.channel.id})"
|
||||
else:
|
||||
location = f"Channel ID: {before.channel.id}"
|
||||
|
||||
embed = discord.Embed(
|
||||
description=f"**Message edited in {location}** [Jump to Message]({after.jump_url})",
|
||||
color=discord.Color.orange(),
|
||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||
)
|
||||
embed.set_author(
|
||||
name=f"{before.author.name} ({before.author.id})",
|
||||
icon_url=before.author.display_avatar.url,
|
||||
)
|
||||
embed.add_field(name="Before", value=before.content or "Empty", inline=False)
|
||||
embed.add_field(name="After", value=after.content or "Empty", inline=False)
|
||||
await self._send_log(before.guild, embed, "on_message_edit")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_join(self, member: discord.Member):
|
||||
"""Log when a user joins the server."""
|
||||
embed = discord.Embed(
|
||||
description=f"**{member.mention} has joined the server.**",
|
||||
color=discord.Color.green(),
|
||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||
)
|
||||
embed.set_author(
|
||||
name=f"{member.name} ({member.id})", icon_url=member.display_avatar.url
|
||||
)
|
||||
await self._send_log(member.guild, embed, "on_member_join")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_remove(self, member: discord.Member):
|
||||
"""Log when a user leaves the server."""
|
||||
embed = discord.Embed(
|
||||
description=f"**{member.mention} has left the server.**",
|
||||
color=discord.Color.dark_red(),
|
||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||
)
|
||||
embed.set_author(
|
||||
name=f"{member.name} ({member.id})", icon_url=member.display_avatar.url
|
||||
)
|
||||
await self._send_log(member.guild, embed, "on_member_remove")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
||||
"""Log when a member's roles or nickname changes."""
|
||||
guild = after.guild
|
||||
if before.nick != after.nick:
|
||||
embed = discord.Embed(
|
||||
description=f"**{after.mention} changed their nickname.**",
|
||||
color=discord.Color.blue(),
|
||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||
)
|
||||
embed.set_author(
|
||||
name=f"{after.name} ({after.id})", icon_url=after.display_avatar.url
|
||||
)
|
||||
embed.add_field(name="Before", value=before.nick or "None", inline=False)
|
||||
embed.add_field(name="After", value=after.nick or "None", inline=False)
|
||||
await self._send_log(guild, embed, "on_member_update")
|
||||
|
||||
if before.roles != after.roles:
|
||||
added_roles = [r.mention for r in after.roles if r not in before.roles]
|
||||
removed_roles = [r.mention for r in before.roles if r not in after.roles]
|
||||
if added_roles or removed_roles:
|
||||
embed = discord.Embed(
|
||||
description=f"**{after.mention}'s roles were updated.**",
|
||||
color=discord.Color.blue(),
|
||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||
)
|
||||
embed.set_author(
|
||||
name=f"{after.name} ({after.id})",
|
||||
icon_url=after.display_avatar.url,
|
||||
)
|
||||
if added_roles:
|
||||
embed.add_field(
|
||||
name="Added Roles", value=", ".join(added_roles), inline=False
|
||||
)
|
||||
if removed_roles:
|
||||
embed.add_field(
|
||||
name="Removed Roles",
|
||||
value=", ".join(removed_roles),
|
||||
inline=False,
|
||||
)
|
||||
await self._send_log(guild, embed, "on_member_update")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_voice_state_update(
|
||||
self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState
|
||||
):
|
||||
"""Log when a user's voice state changes."""
|
||||
if before.channel == after.channel:
|
||||
return # Ignore mutes, deafens, etc. for now
|
||||
|
||||
guild = member.guild
|
||||
action = ""
|
||||
if before.channel and not after.channel:
|
||||
action = f"left voice channel {before.channel.mention}."
|
||||
elif not before.channel and after.channel:
|
||||
action = f"joined voice channel {after.channel.mention}."
|
||||
elif before.channel and after.channel:
|
||||
action = f"moved from {before.channel.mention} to {after.channel.mention}."
|
||||
|
||||
if action:
|
||||
embed = discord.Embed(
|
||||
description=f"**{member.mention} {action}**",
|
||||
color=discord.Color.light_grey(),
|
||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||
)
|
||||
embed.set_author(
|
||||
name=f"{member.name} ({member.id})", icon_url=member.display_avatar.url
|
||||
)
|
||||
await self._send_log(guild, embed, "on_voice_state_update")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_invite_create(self, invite: discord.Invite):
|
||||
"""Log when an invite is created."""
|
||||
guild = invite.guild
|
||||
if not guild or not isinstance(guild, discord.Guild):
|
||||
return
|
||||
|
||||
inviter_str = "Unknown User"
|
||||
if invite.inviter and isinstance(invite.inviter, (discord.User, discord.Member)):
|
||||
inviter_str = invite.inviter.mention
|
||||
elif invite.inviter:
|
||||
inviter_str = f"User ({invite.inviter.id})"
|
||||
|
||||
channel_str = "Unknown Channel"
|
||||
if invite.channel and isinstance(invite.channel, (discord.abc.GuildChannel)):
|
||||
if hasattr(invite.channel, 'mention'):
|
||||
channel_str = invite.channel.mention
|
||||
else:
|
||||
channel_str = f"`{invite.channel.name}`"
|
||||
elif invite.channel:
|
||||
channel_str = f"Channel ID: {invite.channel.id}"
|
||||
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Invite Created",
|
||||
description=f"Invite `{invite.code}` to {channel_str} created by {inviter_str}.",
|
||||
color=discord.Color.teal(),
|
||||
timestamp=datetime.datetime.now(datetime.timezone.utc),
|
||||
)
|
||||
embed.add_field(name="Max Uses", value=invite.max_uses or "Unlimited")
|
||||
embed.add_field(
|
||||
name="Max Age",
|
||||
value=f"{invite.max_age // 3600} hours" if invite.max_age else "Permanent",
|
||||
)
|
||||
await self._send_log(guild, embed, "on_invite_create")
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Logging(bot))
|
||||
|
||||
Reference in New Issue
Block a user