1+ # __init__.py
2+ import discord
3+ from discord .ext import commands
4+ from discord import app_commands
5+ from typing import Optional , Callable , Any , List
6+ import PyBot .utils .token_utils as token_utils
7+ import PyBot .utils .embed_utils as embed_utils
8+ import PyBot .utils .webhook_utils as webhook_utils
9+
10+ token = None # Placeholder for the token, to be set later
11+
12+ class PyBot (commands .Bot ):
13+ def __init__ (self , command_prefix = '!' ):
14+ if command_prefix == "/" :
15+ raise ValueError ("Command prefix cannot be '/' as it is reserved for slash commands. Use a different prefix or no prefix at all. it is buggy and not recommended." )
16+ intents = discord .Intents .default ()
17+ intents .message_content = True
18+ super ().__init__ (command_prefix = command_prefix , intents = intents )
19+ self .bot = self # For compatibility with older code
20+
21+ if not self .tree :
22+ # discord.py 2.0+ requires a CommandTree for slash commands
23+ self .tree = app_commands .CommandTree (self )
24+ self ._token = None
25+
26+ # === Lifecycle Events ===
27+ async def setup_hook (self ) -> None :
28+ """Initial setup after the bot is ready."""
29+ await self .tree .sync ()
30+ print ("Commands synced with Discord!" )
31+ if hasattr (self , 'hook' ) and self .hook .on_setup_hook :
32+ await self .hook .on_setup_hook ()
33+
34+ async def on_ready (self ):
35+ print (f'Logged in as { self .user } (ID: { self .user .id } )' )
36+ print ('------' )
37+ if hasattr (self , 'hook' ) and self .hook .on_ready :
38+ await self .hook .on_ready ()
39+
40+ async def on_command_error (self , ctx , error ):
41+ await ctx .send (f"An error occurred: { error } " )
42+ if hasattr (self , 'hook' ) and self .hook .on_command_error :
43+ await self .hook .on_command_error (ctx , error )
44+
45+ async def on_interaction (self , interaction : discord .Interaction ):
46+ """Handle interactions, such as slash commands."""
47+ if interaction .type == discord .InteractionType .application_command :
48+ await self .tree .process_interaction (interaction )
49+ if hasattr (self , 'hook' ) and self .hook .on_interaction :
50+ await self .hook .on_interaction (interaction )
51+
52+ async def on_message (self , message : discord .Message ):
53+ """Handle messages sent in channels."""
54+ if message .author == self .user :
55+ return
56+ await self .process_commands (message )
57+ if hasattr (self , 'hook' ) and self .hook .on_message :
58+ await self .hook .on_message (message )
59+
60+ async def on_disconnect (self ):
61+ """Handle bot disconnection."""
62+ print ("Bot has disconnected." )
63+ if hasattr (self , 'hook' ) and self .hook .on_disconnect :
64+ await self .hook .on_disconnect ()
65+
66+ async def on_connect (self ):
67+ """Handle bot connection."""
68+ print ("Bot has connected." )
69+ if hasattr (self , 'hook' ) and self .hook .on_connect :
70+ await self .hook .on_connect ()
71+
72+ def run (self , new_token : Optional [str ] = None ):
73+ """Run the bot with the provided token."""
74+ global token
75+
76+ # Priority: passed token > instance token > global token
77+ running_token = new_token or self ._token or token
78+
79+ if running_token is None :
80+ raise ValueError ("Token is not set. Please load a token before running the bot." )
81+
82+ # Update all token references
83+ self ._token = running_token
84+ token = running_token
85+
86+ super ().run (running_token )
87+
88+ def reboot_bot (self ):
89+ """Reboot the bot."""
90+ print ("Rebooting bot..." )
91+ self .loop .create_task (self .close ())
92+ self .loop .run_until_complete (self .start (self ._token ))
93+
94+ # === Command Methods ===
95+ def add_slash_command (self , name : str , description : str , function : Callable [..., Any ], options : Optional [list ] = None ):
96+ """Create a slash command.
97+
98+ Args:
99+ name: The name of the command
100+ description: The description of the command
101+ function: The function to execute when the command is called
102+ options: Optional list of app_commands.Option for additional parameters
103+ """
104+ @app_commands .command (name = name , description = description )
105+ async def slash_command (interaction : discord .Interaction ):
106+ await function (interaction )
107+
108+ if options :
109+ for option in options :
110+ slash_command .add_option (option )
111+
112+ self .tree .add_command (slash_command )
113+ return slash_command
114+
115+
116+ def add_slash_group (self , name : str , description : str ):
117+ """Create a slash command group.
118+
119+ Args:
120+ name: The name of the command group
121+ description: The description of the group
122+ """
123+ return app_commands .Group (name = name , description = description )
124+
125+ def add_custom_command (self , name , function ):
126+ """Add a custom prefix command to the bot."""
127+ self .add_command (commands .Command (function , name = name , help = function .__doc__ ))
128+
129+ # === Token Methods (delegated to utils) ===
130+ @staticmethod
131+ def load_token_from_string (token_string ):
132+ global token
133+ token = token_utils .load_token_from_string (token_string )
134+ return token
135+
136+ @staticmethod
137+ def load_token_from_file (file_path ):
138+ global token
139+ token = token_utils .load_token_from_file (file_path )
140+ return token
141+
142+ @staticmethod
143+ def load_token_from_environment (env_var ):
144+ global token
145+ token = token_utils .load_token_from_environment (env_var )
146+ return token
147+
148+ @staticmethod
149+ def load_token_from_config (config_file , token_key ):
150+ global token
151+ token = token_utils .load_token_from_config (config_file , token_key )
152+ return token
153+
154+ @staticmethod
155+ def load_token_from_secret (secret_name ):
156+ global token
157+ token = token_utils .load_token_from_secret (secret_name )
158+ return token
159+
160+ class WebhookUtils :
161+ @staticmethod
162+ def add_webhook (url ):
163+ return webhook_utils .add_webhook (url )
164+
165+ @staticmethod
166+ def send_webhook (webhook , content ):
167+ return webhook_utils .send_webhook (webhook , content )
168+
169+ @staticmethod
170+ def send_embed (webhook , embed ):
171+ return webhook_utils .send_embed (webhook , embed )
172+
173+ # === Embed Utility Methods ===
174+ @staticmethod
175+ def create_embed (title , description = None , color = 0x000000 ):
176+ return embed_utils .create_embed (title , description , color )
177+
178+ @staticmethod
179+ def set_embed_title (embed , title ):
180+ return embed_utils .set_embed_title (embed , title )
181+
182+ @staticmethod
183+ def set_embed_description (embed , description ):
184+ return embed_utils .set_embed_description (embed , description )
185+
186+ @staticmethod
187+ def set_embed_color (embed , color ):
188+ return embed_utils .set_embed_color (embed , color )
189+
190+ @staticmethod
191+ def set_embed_url (embed , url ):
192+ return embed_utils .set_embed_url (embed , url )
193+
194+ @staticmethod
195+ def set_embed_timestamp (embed , timestamp = None ):
196+ return embed_utils .set_embed_timestamp (embed , timestamp )
197+
198+ @staticmethod
199+ def set_embed_fields (embed , fields ):
200+ return embed_utils .set_embed_fields (embed , fields )
201+
202+ @staticmethod
203+ def add_embed_field (embed , name , value , inline = True ):
204+ return embed_utils .add_embed_field (embed , name , value , inline )
205+
206+ @staticmethod
207+ def set_embed_footer (embed , text , icon_url = None ):
208+ return embed_utils .set_embed_footer (embed , text , icon_url )
209+
210+ @staticmethod
211+ def set_embed_footer_text (embed , text ):
212+ return embed_utils .set_embed_footer_text (embed , text )
213+
214+ @staticmethod
215+ def set_embed_footer_icon (embed , icon_url ):
216+ return embed_utils .set_embed_footer_icon (embed , icon_url )
217+
218+ @staticmethod
219+ def set_embed_thumbnail (embed , url ):
220+ return embed_utils .set_embed_thumbnail (embed , url )
221+
222+ @staticmethod
223+ def set_embed_image (embed , url ):
224+ return embed_utils .set_embed_image (embed , url )
225+
226+ @staticmethod
227+ def set_embed_author (embed , name , url = None , icon_url = None ):
228+ return embed_utils .set_embed_author (embed , name , url , icon_url )
229+
230+ @staticmethod
231+ def set_embed_author_name (embed , name ):
232+ return embed_utils .set_embed_author_name (embed , name )
233+
234+ @staticmethod
235+ def set_embed_author_url (embed , url ):
236+ return embed_utils .set_embed_author_url (embed , url )
237+
238+ @staticmethod
239+ def set_embed_author_icon (embed , icon_url ):
240+ return embed_utils .set_embed_author_icon (embed , icon_url )
0 commit comments