1+ # __init__.py
2+ import discord
3+ from discord .ext import commands
4+ from discord import app_commands
5+
6+ from packaging import version
7+
8+ from typing import *
9+
10+ # Import utility modules
11+ if __name__ != "__main__" :
12+ from .utils .token_utils import *
13+ from .utils .embed_utils import *
14+ from .utils .webhook_utils import *
15+ else :
16+ from utils .token_utils import *
17+ from utils .embed_utils import *
18+ from utils .webhook_utils import *
19+
20+ token = None # Placeholder for the token, to be set later
21+
22+ # Ensure discord.py version is compatible
23+ if version .parse (discord .__version__ ) < version .parse ("2.0.0" ):
24+ raise RuntimeError ("HyperCodePyBot requires discord.py version 2.0.0 or higher." )
25+
26+ class HyperCodePyBot (commands .Bot ):
27+ def __init__ (self , command_prefix = '!' ):
28+ if command_prefix == "/" : warn ("Command prefix cannot be '/' as it is reserved for slash commands. Use a different prefix or no prefix at all. it is buggy it not show autocompletion in chat and not recommended." )
29+ intents = discord .Intents .default ()
30+ intents .message_content = True
31+ super ().__init__ (command_prefix = command_prefix , intents = intents )
32+ self .bot = self # For compatibility with older code
33+
34+ if not self .tree :
35+ # discord.py 2.0+ requires a CommandTree for slash commands
36+ self .tree = app_commands .CommandTree (self )
37+ self ._token = None
38+
39+ # === Lifecycle Events ===
40+ async def setup_hook (self ) -> None :
41+ """Initial setup after the bot is ready."""
42+ await self .tree .sync ()
43+ print ("Commands synced with Discord!" )
44+ if hasattr (self , 'hook' ) and self .hook .on_setup_hook :
45+ await self .hook .on_setup_hook ()
46+
47+ async def on_ready (self ):
48+ print (f'Logged in as { self .user } (ID: { self .user .id } )' )
49+ print ('------' )
50+ if hasattr (self , 'hook' ) and self .hook .on_ready :
51+ await self .hook .on_ready ()
52+
53+ async def on_command_error (self , ctx , error ):
54+ await ctx .send (embed = embed_utils .create_embed ("Error: Command failed" , str (error ), color = 0xff0000 ))
55+ if hasattr (self , 'hook' ) and self .hook .on_command_error :
56+ await self .hook .on_command_error (ctx , error )
57+
58+ async def on_interaction (self , interaction : discord .Interaction ):
59+ """Handle interactions, such as slash commands."""
60+ if interaction .type == discord .InteractionType .application_command :
61+ await self .tree .process_interaction (interaction )
62+ if hasattr (self , 'hook' ) and self .hook .on_interaction :
63+ await self .hook .on_interaction (interaction )
64+
65+ async def on_message (self , message : discord .Message ):
66+ """Handle messages sent in channels."""
67+ if message .author == self .user :
68+ return
69+ await self .process_commands (message )
70+ if hasattr (self , 'hook' ) and self .hook .on_message :
71+ await self .hook .on_message (message )
72+
73+ async def on_disconnect (self ):
74+ """Handle bot disconnection."""
75+ print ("Bot has disconnected." )
76+ if hasattr (self , 'hook' ) and self .hook .on_disconnect :
77+ await self .hook .on_disconnect ()
78+
79+ async def on_connect (self ):
80+ """Handle bot connection."""
81+ print ("Bot has connected." )
82+ if hasattr (self , 'hook' ) and self .hook .on_connect :
83+ await self .hook .on_connect ()
84+
85+ def run (self , new_token : Optional [str ] = None ):
86+ """Run the bot with the provided token."""
87+ global token
88+
89+ # Priority: passed token > instance token > global token
90+ running_token = new_token or self ._token or token
91+
92+ if running_token is None :
93+ raise ValueError ("Token is not set. Please load a token before running the bot." )
94+
95+ # Update all token references
96+ self ._token = running_token
97+ token = running_token
98+
99+ super ().run (running_token )
100+
101+ def reboot_bot (self , callback : Optional [Callable [[], Any ]] = None ):
102+ """Reboot the bot."""
103+ print ("Rebooting bot..." )
104+ try :
105+ self .loop .run_until_complete (self .close ())
106+ self .loop .run_until_complete (self .start (self ._token ))
107+ callback (True , "Reboot successful" ) if callback else None
108+ except Exception as e :
109+ print (f"Error rebooting bot: { e } " )
110+ callback (False , str (e )) if callback else None
111+
112+ def add_custom_command (self , name , function ):
113+ """Add a custom prefix command to the bot."""
114+ self .add_command (commands .Command (function , name = name , help = function .__doc__ ))
115+
116+ # === Token Methods (delegated to utils) ===
117+ @staticmethod
118+ def load_token_from_string (token_string ):
119+ global token
120+ token = token_utils .load_token_from_string (token_string )
121+ return token
122+
123+ @staticmethod
124+ def load_token_from_file (file_path ):
125+ global token
126+ token = token_utils .load_token_from_file (file_path )
127+ return token
128+
129+ @staticmethod
130+ def load_token_from_environment (env_var ):
131+ global token
132+ token = token_utils .load_token_from_environment (env_var )
133+ return token
134+
135+ @staticmethod
136+ def load_token_from_config (config_file , token_key ):
137+ global token
138+ token = token_utils .load_token_from_config (config_file , token_key )
139+ return token
140+
141+ @staticmethod
142+ def load_token_from_secret (secret_name ):
143+ global token
144+ token = token_utils .load_token_from_secret (secret_name )
145+ return token
146+
147+ class Member :
148+ @staticmethod
149+ def get_member (guild : discord .Guild , member_id : int ):
150+ """Get a member by ID in a guild."""
151+ return guild .get_member (member_id )
152+
153+ @staticmethod
154+ def get_members (guild : discord .Guild ):
155+ """Get all members in a guild."""
156+ return guild .members
157+
158+ @staticmethod
159+ def get_member_roles (member : discord .Member ):
160+ """Get roles of a member."""
161+ return member .roles
162+
163+ @staticmethod
164+ def add_role (member : discord .Member , role : discord .Role ):
165+ """Add a role to a member."""
166+ return member .add_roles (role )
167+
168+ @staticmethod
169+ def remove_role (member : discord .Member , role : discord .Role ):
170+ """Remove a role from a member."""
171+ return member .remove_roles (role )
172+
173+ class Channel :
174+ @staticmethod
175+ def get_channel (guild : discord .Guild , channel_id : int ):
176+ """Get a channel by ID in a guild."""
177+ return guild .get_channel (channel_id )
178+
179+ @staticmethod
180+ def get_channels (guild : discord .Guild ):
181+ """Get all channels in a guild."""
182+ return guild .channels
183+
184+ @staticmethod
185+ def create_text_channel (guild : discord .Guild , name : str , category : Optional [discord .CategoryChannel ] = None ):
186+ """Create a text channel in a guild."""
187+ return guild .create_text_channel (name , category = category )
188+
189+ @staticmethod
190+ def create_voice_channel (guild : discord .Guild , name : str , category : Optional [discord .CategoryChannel ] = None ):
191+ """Create a voice channel in a guild."""
192+ return guild .create_voice_channel (name , category = category )
193+
194+ @staticmethod
195+ def delete_channel (channel : discord .abc .GuildChannel ):
196+ """Delete a channel."""
197+ return channel .delete ()
198+
199+ @staticmethod
200+ def edit_channel (channel : discord .abc .GuildChannel , ** kwargs ):
201+ """Edit a channel's properties."""
202+ return channel .edit (** kwargs )
203+
204+ def move_channel (channel : discord .abc .GuildChannel , position : int ):
205+ """Move a channel to a specific position."""
206+ return channel .edit (position = position )
207+
208+ class Role :
209+ @staticmethod
210+ def get_role (guild : discord .Guild , role_id : int ):
211+ """Get a role by ID in a guild."""
212+ return guild .get_role (role_id )
213+
214+ @staticmethod
215+ def get_roles (guild : discord .Guild ):
216+ """Get all roles in a guild."""
217+ return guild .roles
218+
219+ @staticmethod
220+ def create_role (guild : discord .Guild , name : str , ** kwargs ):
221+ """Create a role in a guild."""
222+ return guild .create_role (name = name , ** kwargs )
223+
224+ @staticmethod
225+ def delete_role (role : discord .Role ):
226+ """Delete a role."""
227+ return role .delete ()
228+
229+ @staticmethod
230+ def edit_role (role : discord .Role , ** kwargs ):
231+ """Edit a role's properties."""
232+ return role .edit (** kwargs )
233+
234+ class Guild :
235+ @staticmethod
236+ def get_guild (guild_id : int ):
237+ """Get a guild by ID."""
238+ return discord .utils .get (discord .Client .guilds , id = guild_id )
239+
240+ @staticmethod
241+ def get_guilds (client : discord .Client ):
242+ """Get all guilds the bot is in."""
243+ return client .guilds
244+
245+ @staticmethod
246+ def create_guild (name : str ):
247+ """Create a new guild."""
248+ return discord .Guild .create (name = name )
249+
250+ class VoiceChannel :
251+ @staticmethod
252+ def join_vc (channel : discord .VoiceChannel ):
253+ """Join a voice channel."""
254+ return channel .connect ()
255+ def leave_vc (channel : discord .VoiceChannel ):
256+ """Leave a voice channel."""
257+ return channel .disconnect ()
258+ def play_audio (channel : discord .VoiceChannel , source : str ):
259+ """Play audio in a voice channel."""
260+ if not channel .guild .voice_client :
261+ return channel .connect ()
262+ return channel .guild .voice_client .play (discord .FFmpegPCMAudio (source ))
263+ def stop_audio (channel : discord .VoiceChannel ):
264+ """Stop audio playback in a voice channel."""
265+ if channel .guild .voice_client and channel .guild .voice_client .is_playing ():
266+ return channel .guild .voice_client .stop ()
267+ return None
268+ def pause_audio (channel : discord .VoiceChannel ):
269+ """Pause audio playback in a voice channel."""
270+ if channel .guild .voice_client and channel .guild .voice_client .is_playing ():
271+ return channel .guild .voice_client .pause ()
272+ return None
273+ def resume_audio (channel : discord .VoiceChannel ):
274+ """Resume audio playback in a voice channel."""
275+ if channel .guild .voice_client and channel .guild .voice_client .is_paused ():
276+ return channel .guild .voice_client .resume ()
277+ return None
278+ def get_vc_status (channel : discord .VoiceChannel ):
279+ """Get the status of the voice channel."""
280+ if channel .guild .voice_client :
281+ return {
282+ "is_connected" : channel .guild .voice_client .is_connected (),
283+ "is_playing" : channel .guild .voice_client .is_playing (),
284+ "is_paused" : channel .guild .voice_client .is_paused (),
285+ "channel" : channel .guild .voice_client .channel .name if channel .guild .voice_client .channel else None
286+ }
287+ return {"is_connected" : False , "is_playing" : False , "is_paused" : False , "channel" : None }
288+ def get_vc_channel (channel : discord .VoiceChannel ):
289+ """Get the voice channel the bot is connected to."""
290+ if channel .guild .voice_client :
291+ return channel .guild .voice_client .channel
292+ return None
293+ def get_vc_members (channel : discord .VoiceChannel ):
294+ """Get the members in the voice channel."""
295+ if channel .guild .voice_client :
296+ return [member .name for member in channel .guild .voice_client .channel .members ]
297+ return []
298+ def kick_member (channel : discord .VoiceChannel , member : discord .Member ):
299+ """Kick a member from the voice channel."""
300+ if channel .guild .voice_client and member in channel .guild .voice_client .channel .members :
301+ return member .move_to (None )
302+ return None
303+ def move_member (channel : discord .VoiceChannel , member : discord .Member , target_channel : discord .VoiceChannel ):
304+ """Move a member to another voice channel."""
305+ if channel .guild .voice_client and member in channel .guild .voice_client .channel .members :
306+ return member .move_to (target_channel )
307+ return None
308+
309+ class WebhookUtils :
310+ @staticmethod
311+ def add_webhook (url ):
312+ return webhook_utils .add_webhook (url )
313+
314+ @staticmethod
315+ def send_webhook (webhook , content ):
316+ return webhook_utils .send_webhook (webhook , content )
317+
318+ @staticmethod
319+ def send_embed (webhook , embed ):
320+ return webhook_utils .send_embed (webhook , embed )
321+
322+ # === Embed Utility Methods ===
323+ @staticmethod
324+ def create_embed (title , description = None , color = 0x000000 ):
325+ return embed_utils .create_embed (title , description , color )
326+
327+ @staticmethod
328+ def set_embed_title (embed , title ):
329+ return embed_utils .set_embed_title (embed , title )
330+
331+ @staticmethod
332+ def set_embed_description (embed , description ):
333+ return embed_utils .set_embed_description (embed , description )
334+
335+ @staticmethod
336+ def set_embed_color (embed , color ):
337+ return embed_utils .set_embed_color (embed , color )
338+
339+ @staticmethod
340+ def set_embed_url (embed , url ):
341+ return embed_utils .set_embed_url (embed , url )
342+
343+ @staticmethod
344+ def set_embed_timestamp (embed , timestamp = None ):
345+ return embed_utils .set_embed_timestamp (embed , timestamp )
346+
347+ @staticmethod
348+ def set_embed_fields (embed , fields ):
349+ return embed_utils .set_embed_fields (embed , fields )
350+
351+ @staticmethod
352+ def add_embed_field (embed , name , value , inline = True ):
353+ return embed_utils .add_embed_field (embed , name , value , inline )
354+
355+ @staticmethod
356+ def set_embed_footer (embed , text , icon_url = None ):
357+ return embed_utils .set_embed_footer (embed , text , icon_url )
358+
359+ @staticmethod
360+ def set_embed_footer_text (embed , text ):
361+ return embed_utils .set_embed_footer_text (embed , text )
362+
363+ @staticmethod
364+ def set_embed_footer_icon (embed , icon_url ):
365+ return embed_utils .set_embed_footer_icon (embed , icon_url )
366+
367+ @staticmethod
368+ def set_embed_thumbnail (embed , url ):
369+ return embed_utils .set_embed_thumbnail (embed , url )
370+
371+ @staticmethod
372+ def set_embed_image (embed , url ):
373+ return embed_utils .set_embed_image (embed , url )
374+
375+ @staticmethod
376+ def set_embed_author (embed , name , url = None , icon_url = None ):
377+ return embed_utils .set_embed_author (embed , name , url , icon_url )
378+
379+ @staticmethod
380+ def set_embed_author_name (embed , name ):
381+ return embed_utils .set_embed_author_name (embed , name )
382+
383+ @staticmethod
384+ def set_embed_author_url (embed , url ):
385+ return embed_utils .set_embed_author_url (embed , url )
386+
387+ @staticmethod
388+ def set_embed_author_icon (embed , icon_url ):
389+ return embed_utils .set_embed_author_icon (embed , icon_url )
0 commit comments