Skydata001 commited on
Commit
588f5ad
ยท
verified ยท
1 Parent(s): a2f4144

Delete cogs

Browse files
Files changed (3) hide show
  1. cogs/admin.py +0 -210
  2. cogs/logs.py +0 -157
  3. cogs/welcome.py +0 -126
cogs/admin.py DELETED
@@ -1,210 +0,0 @@
1
- """
2
- cogs/admin.py
3
- Slash commands for the admin / owner control panel.
4
-
5
- All commands are restricted to ADMIN_CHANNEL_ID and require ADMIN_ROLE_ID.
6
- Owner-only commands additionally check that the user is the guild owner.
7
- """
8
-
9
- import discord
10
- from discord.ext import commands
11
- from discord import app_commands
12
- import logging
13
- from datetime import datetime, timezone
14
-
15
- from config import (
16
- ALLOWED_GUILD_ID,
17
- ADMIN_ROLE_ID,
18
- ADMIN_CHANNEL_ID,
19
- WELCOME_CHANNEL_ID,
20
- LOGS_CHANNEL_ID,
21
- )
22
- from stats import tracker
23
-
24
- logger = logging.getLogger("WelcomeBot.Admin")
25
-
26
-
27
- # โ”€โ”€ Permission checks โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
28
-
29
- def is_admin():
30
- async def predicate(interaction: discord.Interaction) -> bool:
31
- if interaction.guild_id != ALLOWED_GUILD_ID:
32
- await interaction.response.send_message("โŒ ู‡ุฐุง ุงู„ุฃู…ุฑ ูŠุนู…ู„ ูู‚ุท ููŠ ุงู„ุฎุงุฏู… ุงู„ู…ุฎุตุต.", ephemeral=True)
33
- return False
34
- if interaction.channel_id != ADMIN_CHANNEL_ID:
35
- await interaction.response.send_message(
36
- f"โŒ ู‡ุฐุง ุงู„ุฃู…ุฑ ูŠุนู…ู„ ูู‚ุท ููŠ <#{ADMIN_CHANNEL_ID}>.", ephemeral=True
37
- )
38
- return False
39
- role = interaction.guild.get_role(ADMIN_ROLE_ID)
40
- if role not in interaction.user.roles:
41
- await interaction.response.send_message("โŒ ู„ูŠุณ ู„ุฏูŠูƒ ุตู„ุงุญูŠุฉ ุงุณุชุฎุฏุงู… ู‡ุฐุง ุงู„ุฃู…ุฑ.", ephemeral=True)
42
- return False
43
- return True
44
- return app_commands.check(predicate)
45
-
46
-
47
- def is_owner():
48
- async def predicate(interaction: discord.Interaction) -> bool:
49
- if interaction.guild_id != ALLOWED_GUILD_ID:
50
- await interaction.response.send_message("โŒ ู‡ุฐุง ุงู„ุฃู…ุฑ ูŠุนู…ู„ ูู‚ุท ููŠ ุงู„ุฎุงุฏู… ุงู„ู…ุฎุตุต.", ephemeral=True)
51
- return False
52
- if interaction.channel_id != ADMIN_CHANNEL_ID:
53
- await interaction.response.send_message(
54
- f"โŒ ู‡ุฐุง ุงู„ุฃู…ุฑ ูŠุนู…ู„ ูู‚ุท ููŠ <#{ADMIN_CHANNEL_ID}>.", ephemeral=True
55
- )
56
- return False
57
- if interaction.user.id != interaction.guild.owner_id:
58
- await interaction.response.send_message("โŒ ู‡ุฐุง ุงู„ุฃู…ุฑ ู…ุฎุตุต ู„ู…ุงู„ูƒ ุงู„ุฎุงุฏู… ูู‚ุท.", ephemeral=True)
59
- return False
60
- return True
61
- return app_commands.check(predicate)
62
-
63
-
64
- # โ”€โ”€ Cog โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
65
-
66
- class AdminCog(commands.Cog):
67
- def __init__(self, bot: commands.Bot):
68
- self.bot = bot
69
- self._welcome_enabled = True # toggle welcome on/off
70
-
71
- # โ”€โ”€ /ุญุงู„ุฉ โ€” Bot & welcome status โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
72
- @app_commands.command(name="ุญุงู„ุฉ", description="ุนุฑุถ ุญุงู„ุฉ ุงู„ุจูˆุช ูˆุงู„ุฅุญุตุงุฆูŠุงุช ุงู„ูŠูˆู…ูŠุฉ")
73
- @app_commands.guilds(discord.Object(id=ALLOWED_GUILD_ID))
74
- @is_admin()
75
- async def status(self, interaction: discord.Interaction):
76
- daily = tracker.get_daily()
77
- totals = tracker.get_totals()
78
-
79
- embed = discord.Embed(
80
- title="๐Ÿค– ุญุงู„ุฉ ุงู„ุจูˆุช",
81
- color=0x7B2FBE,
82
- timestamp=datetime.now(timezone.utc),
83
- )
84
- embed.add_field(name="โœ… ุงู„ุชุฑุญูŠุจ", value="ู…ูุนู‘ู„" if self._welcome_enabled else "ู…ุนุทู‘ู„", inline=True)
85
- embed.add_field(name="โฑ๏ธ ุฒู…ู† ุงู„ุชุดุบูŠู„", value="ู…ุชุตู„", inline=True)
86
- embed.add_field(name="\u200b", value="\u200b", inline=True)
87
- embed.add_field(name="๐Ÿ“ฅ ุฏุฎูˆู„ ุงู„ูŠูˆู…", value=str(daily["joins"]), inline=True)
88
- embed.add_field(name="๐Ÿ“ค ุฎุฑูˆุฌ ุงู„ูŠูˆู…", value=str(daily["leaves"]), inline=True)
89
- embed.add_field(name="โš ๏ธ ุฃุฎุทุงุก ุงู„ูŠูˆู…", value=str(daily["errors"]), inline=True)
90
- embed.add_field(
91
- name="๐Ÿ“ฆ ุงู„ูƒู„ูŠ",
92
- value=(
93
- f"ุฏุฎูˆู„: {totals['total_joins']} | "
94
- f"ุฎุฑูˆุฌ: {totals['total_leaves']} | "
95
- f"ุฃุฎุทุงุก: {totals['total_errors']}"
96
- ),
97
- inline=False,
98
- )
99
- embed.set_footer(text=f"ุจุฏุก ุงู„ูŠูˆู…: {daily['day_start'][:10]}")
100
- await interaction.response.send_message(embed=embed, ephemeral=True)
101
-
102
- # โ”€โ”€ /ุชุฌุฑุจุฉ_ุชุฑุญูŠุจ โ€” Test welcome for a member โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
103
- @app_commands.command(name="ุชุฌุฑุจุฉ_ุชุฑุญูŠุจ", description="ุงุฎุชุจุงุฑ ุฑุณุงู„ุฉ ุงู„ุชุฑุญูŠุจ ู„ุนุถูˆ ู…ุนูŠู†")
104
- @app_commands.describe(ุนุถูˆ="ุงู„ุนุถูˆ ุงู„ุฐูŠ ุชุฑูŠุฏ ุชุฌุฑุจุฉ ุงู„ุชุฑุญูŠุจ ุจู‡")
105
- @app_commands.guilds(discord.Object(id=ALLOWED_GUILD_ID))
106
- @is_admin()
107
- async def test_welcome(self, interaction: discord.Interaction, ุนุถูˆ: discord.Member):
108
- await interaction.response.defer(ephemeral=True)
109
- try:
110
- welcome_cog = self.bot.cogs.get("WelcomeCog")
111
- if not welcome_cog:
112
- await interaction.followup.send("โŒ ูƒูˆุฌ ุงู„ุชุฑุญูŠุจ ุบูŠุฑ ู…ุญู…ู‘ู„.", ephemeral=True)
113
- return
114
- await welcome_cog.send_welcome(ุนุถูˆ, test_mode=True)
115
- await interaction.followup.send(
116
- f"โœ… ุชู… ุฅุฑุณุงู„ ุฑุณุงู„ุฉ ุชุฑุญูŠุจ ุชุฌุฑูŠุจูŠุฉ ู„ู€ {ุนุถูˆ.mention} ููŠ <#{WELCOME_CHANNEL_ID}>.",
117
- ephemeral=True,
118
- )
119
- except Exception as e:
120
- logger.error(f"test_welcome error: {e}")
121
- await interaction.followup.send(f"โŒ ุญุฏุซ ุฎุทุฃ: {e}", ephemeral=True)
122
-
123
- # โ”€โ”€ /ุชูุนูŠู„_ุชุฑุญูŠุจ โ€” Owner only โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
124
- @app_commands.command(name="ุชูุนูŠู„_ุชุฑุญูŠุจ", description="[ุงู„ู…ุงู„ูƒ] ุชูุนูŠู„ ุฃูˆ ุชุนุทูŠู„ ู†ุธุงู… ุงู„ุชุฑุญูŠุจ")
125
- @app_commands.describe(ุญุงู„ุฉ="ูุนู‘ู„ ุฃูˆ ุนุทู‘ู„ ุงู„ุชุฑุญูŠุจ")
126
- @app_commands.choices(ุญุงู„ุฉ=[
127
- app_commands.Choice(name="ุชูุนูŠู„", value="on"),
128
- app_commands.Choice(name="ุชุนุทูŠู„", value="off"),
129
- ])
130
- @app_commands.guilds(discord.Object(id=ALLOWED_GUILD_ID))
131
- @is_owner()
132
- async def toggle_welcome(self, interaction: discord.Interaction, ุญุงู„ุฉ: str):
133
- self._welcome_enabled = (ุญุงู„ุฉ == "on")
134
- # Propagate to welcome cog (monkey-patch the listener)
135
- welcome_cog = self.bot.cogs.get("WelcomeCog")
136
- status = "ู…ูุนู‘ู„ โœ…" if self._welcome_enabled else "ู…ุนุทู‘ู„ โŒ"
137
-
138
- embed = discord.Embed(
139
- title="โš™๏ธ ุชุญุฏูŠุซ ุฅุนุฏุงุฏุงุช ุงู„ุชุฑุญูŠุจ",
140
- description=f"ู†ุธุงู… ุงู„ุชุฑุญูŠุจ ุงู„ุขู†: **{status}**",
141
- color=0x57F287 if self._welcome_enabled else 0xED4245,
142
- )
143
- await interaction.response.send_message(embed=embed)
144
-
145
- # โ”€โ”€ /ุฅุญุตุงุฆูŠุงุช โ€” Detailed stats (admin) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
146
- @app_commands.command(name="ุฅุญุตุงุฆูŠุงุช", description="ุนุฑุถ ุฅุญุตุงุฆูŠุงุช ู…ูุตู‘ู„ุฉ ู„ู„ุฎุงุฏู…")
147
- @app_commands.guilds(discord.Object(id=ALLOWED_GUILD_ID))
148
- @is_admin()
149
- async def stats_cmd(self, interaction: discord.Interaction):
150
- history = tracker.get_history(7)
151
- totals = tracker.get_totals()
152
- daily = tracker.get_daily()
153
-
154
- embed = discord.Embed(
155
- title="๐Ÿ“Š ุฅุญุตุงุฆูŠุงุช ุงู„ุฎุงุฏู… (ุขุฎุฑ 7 ุฃูŠุงู…)",
156
- color=0x7B2FBE,
157
- timestamp=datetime.now(timezone.utc),
158
- )
159
-
160
- if history:
161
- lines = []
162
- for h in reversed(history):
163
- date = h["date"][:10]
164
- g = h["growth"]
165
- arrow = "๐Ÿ“ˆ" if g >= 0 else "๐Ÿ“‰"
166
- lines.append(f"`{date}` {arrow} ุฏุฎูˆู„: **{h['joins']}** ุฎุฑูˆุฌ: **{h['leaves']}** ู†ู…ูˆ: **{g:+d}**")
167
- embed.add_field(name="๐Ÿ“… ุงู„ุณุฌู„ ุงู„ูŠูˆู…ูŠ", value="\n".join(lines), inline=False)
168
- else:
169
- embed.add_field(name="๐Ÿ“… ุงู„ุณุฌู„ ุงู„ูŠูˆู…ูŠ", value="ู„ุง ุชูˆุฌุฏ ุจูŠุงู†ุงุช ุจุนุฏ.", inline=False)
170
-
171
- embed.add_field(name="๐Ÿ“ฅ ุฏุฎูˆู„ ุงู„ูŠูˆู…", value=str(daily["joins"]), inline=True)
172
- embed.add_field(name="๐Ÿ“ค ุฎุฑูˆุฌ ุงู„ูŠูˆู…", value=str(daily["leaves"]), inline=True)
173
- embed.add_field(name="๐Ÿ“ฆ ุฅุฌู…ุงู„ูŠ ุงู„ุฏุฎูˆู„ ุงู„ูƒู„ูŠ", value=str(totals["total_joins"]), inline=True)
174
-
175
- guild = interaction.guild
176
- if guild:
177
- embed.add_field(name="๐Ÿ‘ฅ ุงู„ุฃุนุถุงุก ุงู„ุญุงู„ูŠูˆู†", value=str(guild.member_count), inline=True)
178
-
179
- await interaction.response.send_message(embed=embed, ephemeral=True)
180
-
181
- # โ”€โ”€ /ู…ุณุงุนุฏุฉ_ุงุฏู…ู† โ€” Help panel โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
182
- @app_commands.command(name="ู…ุณุงุนุฏุฉ_ุงุฏู…ู†", description="ุนุฑุถ ู‚ุงุฆู…ุฉ ุงู„ุฃูˆุงู…ุฑ ุงู„ู…ุชุงุญุฉ ู„ู„ุฅุฏุงุฑุฉ")
183
- @app_commands.guilds(discord.Object(id=ALLOWED_GUILD_ID))
184
- @is_admin()
185
- async def admin_help(self, interaction: discord.Interaction):
186
- embed = discord.Embed(
187
- title="๐Ÿ“‹ ู„ูˆุญุฉ ุชุญูƒู… ุงู„ุฅุฏุงุฑุฉ โ€” ุงู„ุฃูˆุงู…ุฑ ุงู„ู…ุชุงุญุฉ",
188
- color=0x7B2FBE,
189
- )
190
- embed.add_field(
191
- name="๐Ÿ”ง ุฃูˆุงู…ุฑ ุงู„ุฅุฏุงุฑุฉ",
192
- value=(
193
- "`/ุญุงู„ุฉ` โ€” ุญุงู„ุฉ ุงู„ุจูˆุช ูˆุงู„ุฅุญุตุงุฆูŠุงุช ุงู„ูŠูˆู…ูŠุฉ\n"
194
- "`/ุชุฌุฑุจุฉ_ุชุฑุญูŠุจ @ุนุถูˆ` โ€” ุงุฎุชุจุงุฑ ุฑุณุงู„ุฉ ุชุฑุญูŠุจ ู„ุนุถูˆ ู…ุนูŠู†\n"
195
- "`/ุฅุญุตุงุฆูŠุงุช` โ€” ุฅุญุตุงุฆูŠุงุช ู…ูุตู‘ู„ุฉ (7 ุฃูŠุงู…)\n"
196
- "`/ู…ุณุงุนุฏุฉ_ุงุฏู…ู†` โ€” ู‡ุฐู‡ ุงู„ู‚ุงุฆู…ุฉ"
197
- ),
198
- inline=False,
199
- )
200
- embed.add_field(
201
- name="๐Ÿ‘‘ ุฃูˆุงู…ุฑ ุงู„ู…ุงู„ูƒ ูู‚ุท",
202
- value="`/ุชูุนูŠู„_ุชุฑุญูŠุจ` โ€” ุชูุนูŠู„ ุฃูˆ ุชุนุทูŠู„ ู†ุธุงู… ุงู„ุชุฑุญูŠุจ",
203
- inline=False,
204
- )
205
- embed.set_footer(text=f"ุฌู…ูŠุน ุงู„ุฃูˆุงู…ุฑ ุชุนู…ู„ ูู‚ุท ููŠ ู‡ุฐู‡ ุงู„ู‚ู†ุงุฉ")
206
- await interaction.response.send_message(embed=embed, ephemeral=True)
207
-
208
-
209
- async def setup(bot: commands.Bot):
210
- await bot.add_cog(AdminCog(bot))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cogs/logs.py DELETED
@@ -1,157 +0,0 @@
1
- """
2
- cogs/logs.py
3
- Posts to the logs channel:
4
- - Instant log on every join / leave / error
5
- - Daily summary every 24 h (growth stats, comparison)
6
- """
7
-
8
- import discord
9
- from discord.ext import commands, tasks
10
- import logging
11
- from datetime import datetime, timezone
12
-
13
- from config import ALLOWED_GUILD_ID, LOGS_CHANNEL_ID
14
- from stats import tracker
15
-
16
- logger = logging.getLogger("WelcomeBot.Logs")
17
-
18
-
19
- class LogsCog(commands.Cog):
20
- def __init__(self, bot: commands.Bot):
21
- self.bot = bot
22
- self.daily_report.start()
23
-
24
- def cog_unload(self):
25
- self.daily_report.cancel()
26
-
27
- def _guild(self) -> discord.Guild | None:
28
- return self.bot.get_guild(ALLOWED_GUILD_ID)
29
-
30
- async def _log_channel(self) -> discord.TextChannel | None:
31
- g = self._guild()
32
- if g:
33
- return g.get_channel(LOGS_CHANNEL_ID)
34
- return None
35
-
36
- # โ”€โ”€ Instant join log โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
37
- @commands.Cog.listener()
38
- async def on_member_join(self, member: discord.Member):
39
- if member.guild.id != ALLOWED_GUILD_ID:
40
- return
41
- ch = await self._log_channel()
42
- if not ch:
43
- return
44
- embed = discord.Embed(
45
- title="๐Ÿ“ฅ ุนุถูˆ ุฌุฏูŠุฏ",
46
- description=f"{member.mention} ุงู†ุถู… ุฅู„ู‰ ุงู„ุฎุงุฏู…",
47
- color=0x57F287,
48
- timestamp=datetime.now(timezone.utc),
49
- )
50
- embed.set_thumbnail(url=member.display_avatar.url)
51
- embed.add_field(name="ุงู„ู…ุนุฑู‘ู", value=str(member.id), inline=True)
52
- embed.add_field(name="ุงู„ุงุณู…", value=member.name, inline=True)
53
- embed.add_field(
54
- name="ุฅุฌู…ุงู„ูŠ ุงู„ุฃุนุถุงุก",
55
- value=str(member.guild.member_count),
56
- inline=True,
57
- )
58
- await ch.send(embed=embed)
59
-
60
- # โ”€โ”€ Instant leave log โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
61
- @commands.Cog.listener()
62
- async def on_member_remove(self, member: discord.Member):
63
- if member.guild.id != ALLOWED_GUILD_ID:
64
- return
65
- ch = await self._log_channel()
66
- if not ch:
67
- return
68
- embed = discord.Embed(
69
- title="๐Ÿ“ค ุนุถูˆ ุบุงุฏุฑ",
70
- description=f"**{member.name}** ุบุงุฏุฑ ุงู„ุฎุงุฏู…",
71
- color=0xED4245,
72
- timestamp=datetime.now(timezone.utc),
73
- )
74
- embed.set_thumbnail(url=member.display_avatar.url)
75
- embed.add_field(name="ุงู„ู…ุนุฑู‘ู", value=str(member.id), inline=True)
76
- guild = self._guild()
77
- if guild:
78
- embed.add_field(
79
- name="ุฅุฌู…ุงู„ูŠ ุงู„ุฃุนุถุงุก",
80
- value=str(guild.member_count),
81
- inline=True,
82
- )
83
- await ch.send(embed=embed)
84
-
85
- # โ”€โ”€ Error log helper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
86
- async def log_error(self, description: str):
87
- ch = await self._log_channel()
88
- if not ch:
89
- return
90
- embed = discord.Embed(
91
- title="โš ๏ธ ุฎุทุฃ",
92
- description=description,
93
- color=0xFEE75C,
94
- timestamp=datetime.now(timezone.utc),
95
- )
96
- await ch.send(embed=embed)
97
-
98
- # โ”€โ”€ Daily summary task (every 24 h) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
99
- @tasks.loop(hours=24)
100
- async def daily_report(self):
101
- ch = await self._log_channel()
102
- if not ch:
103
- return
104
-
105
- summary = tracker.reset_daily()
106
- joins = summary["joins"]
107
- leaves = summary["leaves"]
108
- errors = summary["errors"]
109
- growth = summary["growth"]
110
-
111
- totals = tracker.get_totals()
112
-
113
- # Growth percentage (avoid div/0)
114
- total_before = totals["total_joins"] - totals["total_leaves"] - growth
115
- if total_before > 0:
116
- pct = (growth / total_before) * 100
117
- pct_str = f"{pct:+.1f}%"
118
- else:
119
- pct_str = "โ€”"
120
-
121
- color = 0x57F287 if growth >= 0 else 0xED4245
122
- arrow = "๐Ÿ“ˆ" if growth >= 0 else "๐Ÿ“‰"
123
-
124
- embed = discord.Embed(
125
- title="๐Ÿ“Š ุชู‚ุฑูŠุฑ ูŠูˆู…ูŠ โ€” ุฅุญุตุงุฆูŠุงุช ุงู„ุฎุงุฏู…",
126
- color=color,
127
- timestamp=datetime.now(timezone.utc),
128
- )
129
- embed.add_field(name="๐Ÿ“ฅ ุฏุฎูˆู„ ุงู„ูŠูˆู…", value=str(joins), inline=True)
130
- embed.add_field(name="๐Ÿ“ค ุฎุฑูˆุฌ ุงู„ูŠูˆู…", value=str(leaves), inline=True)
131
- embed.add_field(name="โš ๏ธ ุฃุฎุทุงุก ุงู„ูŠูˆู…", value=str(errors), inline=True)
132
- embed.add_field(
133
- name=f"{arrow} ู†ู…ูˆ ุงู„ูŠูˆู…",
134
- value=f"**{growth:+d}** ุนุถูˆ ({pct_str})",
135
- inline=False,
136
- )
137
- embed.add_field(
138
- name="๐Ÿ“ฆ ุงู„ุฅุฌู…ุงู„ูŠ ุงู„ูƒู„ูŠ",
139
- value=(
140
- f"ุฏุฎูˆู„: {totals['total_joins']} | "
141
- f"ุฎุฑูˆุฌ: {totals['total_leaves']} | "
142
- f"ุฃุฎุทุงุก: {totals['total_errors']}"
143
- ),
144
- inline=False,
145
- )
146
- embed.set_footer(text="ูŠุชุฌุฏุฏ ู‡ุฐุง ุงู„ุชู‚ุฑูŠุฑ ูƒู„ 24 ุณุงุนุฉ")
147
-
148
- await ch.send(embed=embed)
149
- logger.info("Daily report posted.")
150
-
151
- @daily_report.before_loop
152
- async def before_daily_report(self):
153
- await self.bot.wait_until_ready()
154
-
155
-
156
- async def setup(bot: commands.Bot):
157
- await bot.add_cog(LogsCog(bot))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cogs/welcome.py DELETED
@@ -1,126 +0,0 @@
1
- """
2
- cogs/welcome.py
3
- Handles the on_member_join event:
4
- - Sends a professional embed to the welcome channel
5
- - Reacts with ๐Ÿ‘‹
6
- - Sends the personalized welcome card image (avatar composited on crown PNG)
7
- """
8
-
9
- import discord
10
- from discord.ext import commands
11
- from discord import app_commands
12
- import random
13
- import logging
14
- from datetime import datetime, timezone
15
-
16
- from config import ALLOWED_GUILD_ID, WELCOME_CHANNEL_ID
17
- from image_processor import generate_welcome_card
18
- from stats import tracker
19
-
20
- logger = logging.getLogger("WelcomeBot.Welcome")
21
-
22
- # โ”€โ”€ Welcome messages pool โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
23
- WELCOME_MESSAGES = [
24
- "ุฃู‡ู„ุงู‹ ูˆุณู‡ู„ุงู‹ ุจูƒ ููŠ ู…ุฌุชู…ุนู†ุง! ุณุนุฏุงุก ุจุงู†ุถู…ุงู…ูƒ ุฅู„ูŠู†ุง ๐ŸŽ‰",
25
- "ู…ุฑุญุจุงู‹ ุจูƒ ููŠ ุฎุงุฏู…ู†ุง! ู†ุชู…ู†ู‰ ุฃู† ุชุฌุฏ ูˆู‚ุชุงู‹ ู…ู…ุชุนุงู‹ ู…ุนู†ุง โœจ",
26
- "ูŠุณุนุฏู†ุง ูˆุฌูˆุฏูƒ ุจูŠู†ู†ุง! ุฃู‡ู„ุงู‹ ุจูƒ ููŠ ุนุงุฆู„ุชู†ุง ุงู„ุตุบูŠุฑุฉ ๐Ÿ’œ",
27
- "ูˆุตู„ ุนุถูˆ ุฌุฏูŠุฏ! ู…ุฑุญุจุงู‹ ุจูƒุŒ ู†ุชู…ู†ู‰ ู„ูƒ ุฅู‚ุงู…ุฉ ุทูŠุจุฉ ๐ŸŒŸ",
28
- "ุฃู‡ู„ุงู‹ ุจูƒ ุจูŠู†ู†ุง! ุงู„ุฎุงุฏู… ุฃุตุจุญ ุฃุฌู…ู„ ุจูˆุฌูˆุฏูƒ ๐Ÿ”ฎ",
29
- "ู…ุฑุญุจุงู‹ ุจูƒ ููŠ ู…ุฌุชู…ุนู†ุง ุงู„ู…ู…ูŠุฒ! ู†ุญู† ุณุนุฏุงุก ุจู‚ุฏูˆู…ูƒ ๐Ÿ’ซ",
30
- "ูˆุตู„ุช ููŠ ุงู„ูˆู‚ุช ุงู„ู…ู†ุงุณุจ! ู…ุฑุญุจุงู‹ ุจูƒ ููŠ ุฎุงุฏู…ู†ุง ๐ŸŽŠ",
31
- "ุฃู‡ู„ุงู‹ ูˆุณู‡ู„ุงู‹! ู†ุชู…ู†ู‰ ุฃู† ุชุดุนุฑ ุจุงู„ุฑุงุญุฉ ุจูŠู†ู†ุง ุฏุงุฆู…ุงู‹ ๐ŸŒ™",
32
- ]
33
-
34
-
35
- class WelcomeCog(commands.Cog):
36
- def __init__(self, bot: commands.Bot):
37
- self.bot = bot
38
-
39
- # โ”€โ”€ Guard: only run in the allowed guild โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
40
- def _is_allowed_guild(self, guild_id: int) -> bool:
41
- return guild_id == ALLOWED_GUILD_ID
42
-
43
- # โ”€โ”€ Core welcome sender โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
44
- async def send_welcome(self, member: discord.Member, test_mode: bool = False):
45
- """Send welcome embed + card to the welcome channel."""
46
- guild = member.guild
47
-
48
- if not self._is_allowed_guild(guild.id):
49
- logger.warning(f"Ignored member join from guild {guild.id}")
50
- return
51
-
52
- channel = guild.get_channel(WELCOME_CHANNEL_ID)
53
- if not channel:
54
- logger.error(f"Welcome channel {WELCOME_CHANNEL_ID} not found.")
55
- tracker.record_error()
56
- return
57
-
58
- welcome_msg = random.choice(WELCOME_MESSAGES)
59
- member_count = guild.member_count
60
- join_time = member.joined_at or datetime.now(timezone.utc)
61
-
62
- # โ”€โ”€ Embed โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
63
- embed = discord.Embed(
64
- title=f"โœจ ุนุถูˆ ุฌุฏูŠุฏ ุงู†ุถู… ุฅู„ูŠู†ุง!",
65
- description=f"**{welcome_msg}**",
66
- color=0x7B2FBE,
67
- timestamp=join_time,
68
- )
69
- embed.set_thumbnail(url=member.display_avatar.url)
70
- embed.add_field(
71
- name="๐Ÿ‘ค ุงู„ุนุถูˆ",
72
- value=f"{member.mention}\n`{member.name}`",
73
- inline=True,
74
- )
75
- embed.add_field(
76
- name="๐Ÿ“… ุชุงุฑูŠุฎ ุงู„ุงู†ุถู…ุงู…",
77
- value=f"<t:{int(join_time.timestamp())}:F>",
78
- inline=True,
79
- )
80
- embed.add_field(
81
- name="๐Ÿ‘ฅ ุนุฏุฏ ุงู„ุฃุนุถุงุก",
82
- value=f"ุฃุตุจุญ ุนุฏุฏ ุฃุนุถุงุฆู†ุง **{member_count}** ุนุถูˆุงู‹",
83
- inline=False,
84
- )
85
- embed.set_footer(
86
- text=f"{'[ูˆุถุน ุงู„ุชุฌุฑุจุฉ] ' if test_mode else ''}ุฎุงุฏู… {guild.name}",
87
- icon_url=guild.icon.url if guild.icon else None,
88
- )
89
-
90
- # โ”€โ”€ Send embed โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
91
- try:
92
- sent = await channel.send(embed=embed)
93
- await sent.add_reaction("๐Ÿ‘‹")
94
- except Exception as e:
95
- logger.error(f"Failed to send welcome embed: {e}")
96
- tracker.record_error()
97
- return
98
-
99
- # โ”€โ”€ Generate and send welcome card โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
100
- try:
101
- avatar_url = member.display_avatar.with_format("png").with_size(512).url
102
- card_bytes = await generate_welcome_card(avatar_url, member.name)
103
- file = discord.File(fp=card_bytes, filename="welcome.png")
104
- await channel.send(file=file)
105
- except Exception as e:
106
- logger.error(f"Failed to send welcome card image: {e}")
107
- tracker.record_error()
108
-
109
- if not test_mode:
110
- tracker.record_join()
111
-
112
- # โ”€โ”€ Event โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
113
- @commands.Cog.listener()
114
- async def on_member_join(self, member: discord.Member):
115
- logger.info(f"Member joined: {member} in guild {member.guild.id}")
116
- await self.send_welcome(member)
117
-
118
- @commands.Cog.listener()
119
- async def on_member_remove(self, member: discord.Member):
120
- if self._is_allowed_guild(member.guild.id):
121
- tracker.record_leave()
122
- logger.info(f"Member left: {member}")
123
-
124
-
125
- async def setup(bot: commands.Bot):
126
- await bot.add_cog(WelcomeCog(bot))