Skip to content

Commit e6f9963

Browse files
authored
Merge pull request #43 from rokybeast/master
2 parents 92d0a75 + 5e09d31 commit e6f9963

2 files changed

Lines changed: 224 additions & 1 deletion

File tree

bot.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ async def setup_hook(self) -> None:
104104
'cogs.tod',
105105
'cogs.daily_quests',
106106
'cogs.staff_applications',
107-
'cogs.tts'
107+
'cogs.tts',
108+
'cogs.chowkidar'
108109
]
109110

110111
for ext in feature_cogs:

cogs/chowkidar.py

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import discord
2+
from discord.ext import commands
3+
import aiosqlite
4+
from utils.helpers import EmbedBuilder
5+
6+
def is_staff():
7+
async def predicate(ctx):
8+
if ctx.author.id == ctx.bot.config.owner_id:
9+
return True
10+
return ctx.author.guild_permissions.view_audit_log
11+
return commands.check(predicate)
12+
13+
class Chowkidar(commands.Cog):
14+
def __init__(self, bot):
15+
self.bot = bot
16+
self.watched_users = set()
17+
self.log_channel_id = None
18+
19+
async def cog_load(self):
20+
async with aiosqlite.connect("botdata.db") as db:
21+
await db.execute("CREATE TABLE IF NOT EXISTS chowkidar_config (guild_id INTEGER PRIMARY KEY, channel_id INTEGER)")
22+
await db.execute("CREATE TABLE IF NOT EXISTS chowkidar_tracked (user_id INTEGER PRIMARY KEY)")
23+
await db.commit()
24+
25+
async with db.execute("SELECT channel_id FROM chowkidar_config LIMIT 1") as cursor:
26+
row = await cursor.fetchone()
27+
if row:
28+
self.log_channel_id = row[0]
29+
30+
async with db.execute("SELECT user_id FROM chowkidar_tracked") as cursor:
31+
rows = await cursor.fetchall()
32+
self.watched_users = {row[0] for row in rows}
33+
34+
async def send_log(self, embed: discord.Embed):
35+
if not self.log_channel_id:
36+
return
37+
channel = self.bot.get_channel(self.log_channel_id)
38+
if channel:
39+
await channel.send(embed=embed)
40+
41+
@commands.hybrid_command(name="setwlchannel", description="Set the current channel as the watchlog channel.")
42+
@is_staff()
43+
async def setwlchannel(self, ctx):
44+
if not isinstance(ctx.channel, discord.TextChannel):
45+
await ctx.send(embed=EmbedBuilder.error_embed("Invalid Channel", "This command can only be used in a standard text channel."))
46+
return
47+
48+
self.log_channel_id = ctx.channel.id
49+
async with aiosqlite.connect("botdata.db") as db:
50+
await db.execute("INSERT OR REPLACE INTO chowkidar_config (guild_id, channel_id) VALUES (?, ?)", (ctx.guild.id, ctx.channel.id))
51+
await db.commit()
52+
53+
await ctx.send(embed=EmbedBuilder.success_embed("Channel Configured", f"Watchlog channel has been set to {ctx.channel.mention}."))
54+
55+
@commands.hybrid_command(name="chowkidar", description="Start tracking a user.")
56+
@is_staff()
57+
async def chowkidar(self, ctx, user: discord.Member):
58+
if user.id == self.bot.user.id:
59+
await ctx.send(embed=EmbedBuilder.error_embed("Invalid Target", "The bot cannot be tracked."))
60+
return
61+
62+
if user.guild_permissions.view_audit_log and ctx.author.id != ctx.guild.owner_id:
63+
await ctx.send(embed=EmbedBuilder.error_embed("Invalid Target", "You cannot track another staff member."))
64+
return
65+
66+
self.watched_users.add(user.id)
67+
async with aiosqlite.connect("botdata.db") as db:
68+
await db.execute("INSERT OR IGNORE INTO chowkidar_tracked (user_id) VALUES (?)", (user.id,))
69+
await db.commit()
70+
71+
await ctx.send(embed=EmbedBuilder.success_embed("Tracking Initiated", f"Now tracking actions for {user.mention}."))
72+
73+
@commands.hybrid_command(name="endwl", description="Stop tracking a user.")
74+
@is_staff()
75+
async def endwl(self, ctx, user: discord.Member):
76+
self.watched_users.discard(user.id)
77+
async with aiosqlite.connect("botdata.db") as db:
78+
await db.execute("DELETE FROM chowkidar_tracked WHERE user_id = ?", (user.id,))
79+
await db.commit()
80+
81+
await ctx.send(embed=EmbedBuilder.success_embed("Tracking Terminated", f"Stopped tracking {user.mention}."))
82+
83+
@commands.hybrid_command(name="purgewl", description="Delete all watchlogs for a specific user.")
84+
@is_staff()
85+
async def purgewl(self, ctx, user: discord.Member):
86+
if not self.log_channel_id:
87+
await ctx.send(embed=EmbedBuilder.error_embed("Configuration Error", "Watchlog channel is not set."))
88+
return
89+
90+
log_channel = self.bot.get_channel(self.log_channel_id)
91+
if not log_channel:
92+
await ctx.send(embed=EmbedBuilder.error_embed("Configuration Error", "Watchlog channel could not be found."))
93+
return
94+
95+
await ctx.defer()
96+
to_delete = []
97+
98+
async for msg in log_channel.history(limit=1000):
99+
if msg.author == self.bot.user and msg.embeds:
100+
embed = msg.embeds[0]
101+
if embed.footer and embed.footer.text and f"ID: {user.id}" in embed.footer.text:
102+
to_delete.append(msg)
103+
104+
if to_delete:
105+
for i in range(0, len(to_delete), 100):
106+
await log_channel.delete_messages(to_delete[i:i+100])
107+
108+
await ctx.send(embed=EmbedBuilder.success_embed("Purge Complete", f"Deleted {len(to_delete)} log entries for {user.mention}."))
109+
110+
@commands.Cog.listener()
111+
async def on_message(self, message):
112+
if message.author.bot or message.author.id not in self.watched_users:
113+
return
114+
115+
action = "Message Replied" if message.reference else "Message Sent"
116+
embed = discord.Embed(title=action, description=message.content, color=discord.Color.blue(), timestamp=message.created_at)
117+
embed.set_author(name=str(message.author), icon_url=message.author.display_avatar.url)
118+
embed.add_field(name="Channel", value=message.channel.mention)
119+
embed.add_field(name="Message ID", value=str(message.id))
120+
embed.add_field(name="Message Link", value=f"[Jump to Message]({message.jump_url})", inline=False)
121+
embed.set_footer(text=f"User ID: {message.author.id}")
122+
123+
await self.send_log(embed)
124+
125+
@commands.Cog.listener()
126+
async def on_message_edit(self, before, after):
127+
if after.author.bot or after.author.id not in self.watched_users or before.content == after.content:
128+
return
129+
130+
embed = discord.Embed(title="Message Edited", color=discord.Color.yellow(), timestamp=discord.utils.utcnow())
131+
embed.set_author(name=str(after.author), icon_url=after.author.display_avatar.url)
132+
embed.add_field(name="Before", value=before.content or "None", inline=False)
133+
embed.add_field(name="After", value=after.content or "None", inline=False)
134+
embed.add_field(name="Channel", value=after.channel.mention)
135+
embed.add_field(name="Message ID", value=str(after.id))
136+
embed.add_field(name="Message Link", value=f"[Jump to Message]({after.jump_url})", inline=False)
137+
embed.set_footer(text=f"User ID: {after.author.id}")
138+
139+
await self.send_log(embed)
140+
141+
@commands.Cog.listener()
142+
async def on_message_delete(self, message):
143+
if message.author.bot or message.author.id not in self.watched_users:
144+
return
145+
146+
embed = discord.Embed(title="Message Deleted", description=message.content, color=discord.Color.red(), timestamp=discord.utils.utcnow())
147+
embed.set_author(name=str(message.author), icon_url=message.author.display_avatar.url)
148+
embed.add_field(name="Channel", value=message.channel.mention)
149+
embed.add_field(name="Message ID", value=str(message.id))
150+
embed.set_footer(text=f"User ID: {message.author.id}")
151+
152+
await self.send_log(embed)
153+
154+
@commands.Cog.listener()
155+
async def on_voice_state_update(self, member, before, after):
156+
if member.bot or member.id not in self.watched_users:
157+
return
158+
159+
embed = discord.Embed(color=discord.Color.purple(), timestamp=discord.utils.utcnow())
160+
embed.set_author(name=str(member), icon_url=member.display_avatar.url)
161+
embed.set_footer(text=f"User ID: {member.id}")
162+
163+
if before.channel is None and after.channel is not None:
164+
embed.title = "Joined Voice Channel"
165+
embed.add_field(name="Channel", value=after.channel.mention)
166+
elif before.channel is not None and after.channel is None:
167+
embed.title = "Left Voice Channel"
168+
embed.add_field(name="Channel", value=before.channel.mention)
169+
elif before.channel != after.channel:
170+
embed.title = "Moved Voice Channel"
171+
embed.add_field(name="From", value=before.channel.mention)
172+
embed.add_field(name="To", value=after.channel.mention)
173+
else:
174+
return
175+
176+
await self.send_log(embed)
177+
178+
@commands.Cog.listener()
179+
async def on_raw_reaction_add(self, payload):
180+
if payload.user_id not in self.watched_users:
181+
return
182+
183+
guild = self.bot.get_guild(payload.guild_id)
184+
if not guild:
185+
return
186+
187+
member = guild.get_member(payload.user_id)
188+
if not member or member.bot:
189+
return
190+
191+
channel = guild.get_channel(payload.channel_id)
192+
message_link = f"https://discord.com/channels/{payload.guild_id}/{payload.channel_id}/{payload.message_id}"
193+
194+
embed = discord.Embed(title="Reaction Added", color=discord.Color.teal(), timestamp=discord.utils.utcnow())
195+
embed.set_author(name=str(member), icon_url=member.display_avatar.url)
196+
embed.add_field(name="Emoji", value=str(payload.emoji))
197+
embed.add_field(name="Channel", value=channel.mention if channel else str(payload.channel_id))
198+
embed.add_field(name="Message ID", value=str(payload.message_id))
199+
embed.add_field(name="Message Link", value=f"[Jump to Message]({message_link})", inline=False)
200+
embed.set_footer(text=f"User ID: {member.id}")
201+
202+
await self.send_log(embed)
203+
204+
@commands.Cog.listener()
205+
async def on_member_remove(self, member):
206+
if member.id not in self.watched_users:
207+
return
208+
209+
embed = discord.Embed(title="Left Server", color=discord.Color.dark_grey(), timestamp=discord.utils.utcnow())
210+
embed.set_author(name=str(member), icon_url=member.display_avatar.url)
211+
embed.set_footer(text=f"User ID: {member.id}")
212+
213+
await self.send_log(embed)
214+
215+
self.watched_users.discard(member.id)
216+
async with aiosqlite.connect("botdata.db") as db:
217+
await db.execute("DELETE FROM chowkidar_tracked WHERE user_id = ?", (member.id,))
218+
await db.commit()
219+
220+
async def setup(bot):
221+
await bot.add_cog(Chowkidar(bot))
222+

0 commit comments

Comments
 (0)