@@ -106,6 +106,51 @@ async def save_stats_to_mongodb(self):
106106 # Calculate total user count
107107 total_users = sum (guild .member_count or 0 for guild in self .bot .guilds )
108108
109+ # Get detailed guild information
110+ guild_details = []
111+ guild_basic_list = []
112+ for guild in self .bot .guilds :
113+ try :
114+ basic_info = {
115+ "id" : str (guild .id ),
116+ "name" : guild .name ,
117+ "member_count" : guild .member_count or 0
118+ }
119+ guild_basic_list .append (basic_info )
120+
121+ detailed_info = {
122+ "id" : str (guild .id ),
123+ "name" : guild .name ,
124+ "member_count" : guild .member_count or 0 ,
125+ "owner_id" : str (guild .owner_id ) if guild .owner_id else None ,
126+ "created_at" : guild .created_at .isoformat () if guild .created_at else None ,
127+ "features" : list (guild .features ) if guild .features else [],
128+ "verification_level" : str (guild .verification_level ) if guild .verification_level else "none" ,
129+ "premium_tier" : guild .premium_tier if hasattr (guild , 'premium_tier' ) else 0 ,
130+ "premium_subscription_count" : guild .premium_subscription_count if hasattr (guild , 'premium_subscription_count' ) else 0 ,
131+ "large" : guild .large if hasattr (guild , 'large' ) else False ,
132+ "icon" : guild .icon .url if guild .icon else None ,
133+ "banner" : guild .banner .url if guild .banner else None ,
134+ "description" : guild .description ,
135+ "region" : str (guild .preferred_locale ) if hasattr (guild , 'preferred_locale' ) else None ,
136+ "channels_count" : len (guild .channels ) if guild .channels else 0 ,
137+ "roles_count" : len (guild .roles ) if guild .roles else 0 ,
138+ "emojis_count" : len (guild .emojis ) if guild .emojis else 0 ,
139+ "joined_at" : guild .me .joined_at .isoformat () if guild .me and guild .me .joined_at else None ,
140+ "permissions" : guild .me .guild_permissions .value if guild .me else 0 ,
141+ "last_updated" : datetime .now ().isoformat ()
142+ }
143+ guild_details .append (detailed_info )
144+ except Exception as e :
145+ logging .warning (f"Error getting details for guild { guild .id } : { e } " )
146+ basic_info = {
147+ "id" : str (guild .id ),
148+ "name" : getattr (guild , 'name' , 'Unknown' ),
149+ "member_count" : getattr (guild , 'member_count' , 0 ) or 0
150+ }
151+ guild_basic_list .append (basic_info )
152+ guild_details .append (basic_info )
153+
109154 # Prepare comprehensive stats document
110155 stats_doc = {
111156 "command_count" : self .command_count ,
@@ -122,9 +167,19 @@ async def save_stats_to_mongodb(self):
122167 "cpu_usage" : round (cpu_usage , 2 ),
123168 "cached_users" : len (self .bot .users ),
124169
125- # Guild information
170+ # Guild information - comprehensive
126171 "guild_count" : len (self .bot .guilds ),
127172 "guild_list" : [str (guild .id ) for guild in self .bot .guilds ],
173+ "guild_details" : guild_details , # Full detailed information
174+ "guild_basic" : guild_basic_list , # Basic information for quick access
175+
176+ # Guild statistics
177+ "largest_guild" : max (guild_details , key = lambda g : g .get ('member_count' , 0 )) if guild_details else None ,
178+ "total_channels" : sum (g .get ('channels_count' , 0 ) for g in guild_details ),
179+ "total_roles" : sum (g .get ('roles_count' , 0 ) for g in guild_details ),
180+ "total_emojis" : sum (g .get ('emojis_count' , 0 ) for g in guild_details ),
181+ "premium_guilds" : len ([g for g in guild_details if g .get ('premium_tier' , 0 ) > 0 ]),
182+ "large_guilds" : len ([g for g in guild_details if g .get ('large' , False )]),
128183
129184 # Uptime information
130185 "uptime_start" : self .start_time .timestamp (),
@@ -144,9 +199,37 @@ async def save_stats_to_mongodb(self):
144199 upsert = True
145200 )
146201
202+ # Also save individual guild documents for detailed tracking
203+ try :
204+ for guild_info in guild_details :
205+ await self .db .db .guild_cache .update_one (
206+ {"_id" : guild_info ["id" ]},
207+ {"$set" : {
208+ ** guild_info ,
209+ "bot_joined_at" : guild_info .get ("joined_at" ),
210+ "last_seen" : datetime .now ().isoformat (),
211+ "is_active" : True
212+ }},
213+ upsert = True
214+ )
215+
216+ # Mark guilds we're no longer in as inactive
217+ current_guild_ids = [g ["id" ] for g in guild_details ]
218+ await self .db .db .guild_cache .update_many (
219+ {"_id" : {"$nin" : current_guild_ids }, "is_active" : True },
220+ {"$set" : {
221+ "is_active" : False ,
222+ "left_at" : datetime .now ().isoformat ()
223+ }}
224+ )
225+
226+ logging .debug (f"Updated { len (guild_details )} guild cache entries" )
227+ except Exception as e :
228+ logging .error (f"Error updating guild cache: { e } " )
229+
147230 success = result .modified_count > 0 or result .upserted_id is not None
148231 if success :
149- logging .debug ("Stats saved to MongoDB successfully" )
232+ logging .debug ("Stats and guild data saved to MongoDB successfully" )
150233 else :
151234 logging .warning ("Failed to save stats to MongoDB" )
152235
@@ -546,6 +629,200 @@ def check(reaction, user):
546629 except TimeoutError :
547630 await ctx .send ("⏰ Stats reset timed out." )
548631
632+ @commands .command (name = 'guildstats' , aliases = ['serverstats' ])
633+ @commands .is_owner ()
634+ async def guild_stats (self , ctx ):
635+ """Show comprehensive guild statistics"""
636+ guild_count = len (self .bot .guilds )
637+ total_users = sum (guild .member_count or 0 for guild in self .bot .guilds )
638+
639+ # Calculate guild statistics
640+ large_guilds = len ([g for g in self .bot .guilds if getattr (g , 'large' , False )])
641+ premium_guilds = len ([g for g in self .bot .guilds if getattr (g , 'premium_tier' , 0 ) > 0 ])
642+
643+ # Find largest and smallest guilds
644+ guilds_with_members = [(g .name , g .member_count or 0 ) for g in self .bot .guilds if g .member_count ]
645+ largest_guild = max (guilds_with_members , key = lambda x : x [1 ]) if guilds_with_members else ("None" , 0 )
646+ smallest_guild = min (guilds_with_members , key = lambda x : x [1 ]) if guilds_with_members else ("None" , 0 )
647+
648+ embed = discord .Embed (
649+ title = "🏰 Guild Statistics" ,
650+ color = discord .Color .gold ()
651+ )
652+
653+ embed .add_field (
654+ name = "📊 Overview" ,
655+ value = f"**Total Guilds:** { guild_count :,} \n "
656+ f"**Total Users:** { total_users :,} \n "
657+ f"**Average Users/Guild:** { total_users // guild_count if guild_count > 0 else 0 :,} " ,
658+ inline = True
659+ )
660+
661+ embed .add_field (
662+ name = "🏆 Guild Types" ,
663+ value = f"**Large Guilds:** { large_guilds :,} \n "
664+ f"**Premium Guilds:** { premium_guilds :,} \n "
665+ f"**Regular Guilds:** { guild_count - large_guilds - premium_guilds :,} " ,
666+ inline = True
667+ )
668+
669+ embed .add_field (
670+ name = "📈 Size Range" ,
671+ value = f"**Largest:** { largest_guild [0 ][:20 ]} { '...' if len (largest_guild [0 ]) > 20 else '' } ({ largest_guild [1 ]:,} )\n "
672+ f"**Smallest:** { smallest_guild [0 ][:20 ]} { '...' if len (smallest_guild [0 ]) > 20 else '' } ({ smallest_guild [1 ]:,} )" ,
673+ inline = False
674+ )
675+
676+ # Guild distribution by size
677+ size_ranges = {
678+ "Tiny (1-50)" : len ([g for g in self .bot .guilds if 1 <= (g .member_count or 0 ) <= 50 ]),
679+ "Small (51-200)" : len ([g for g in self .bot .guilds if 51 <= (g .member_count or 0 ) <= 200 ]),
680+ "Medium (201-1000)" : len ([g for g in self .bot .guilds if 201 <= (g .member_count or 0 ) <= 1000 ]),
681+ "Large (1001+)" : len ([g for g in self .bot .guilds if (g .member_count or 0 ) > 1000 ])
682+ }
683+
684+ size_distribution = "\n " .join ([f"**{ size } :** { count } " for size , count in size_ranges .items () if count > 0 ])
685+ embed .add_field (
686+ name = "📏 Size Distribution" ,
687+ value = size_distribution or "No data available" ,
688+ inline = False
689+ )
690+
691+ embed .timestamp = datetime .utcnow ()
692+ await ctx .send (embed = embed )
693+
694+ @commands .command (name = 'guildlist' , aliases = ['serverlist' ])
695+ @commands .is_owner ()
696+ async def guild_list (self , ctx , page : int = 1 ):
697+ """Show paginated list of guilds with details"""
698+ guilds_per_page = 10
699+ guild_list = sorted (self .bot .guilds , key = lambda g : g .member_count or 0 , reverse = True )
700+ total_pages = (len (guild_list ) + guilds_per_page - 1 ) // guilds_per_page
701+
702+ if page < 1 or page > total_pages :
703+ await ctx .send (f"❌ Invalid page number. Pages available: 1-{ total_pages } " )
704+ return
705+
706+ start_idx = (page - 1 ) * guilds_per_page
707+ end_idx = start_idx + guilds_per_page
708+ page_guilds = guild_list [start_idx :end_idx ]
709+
710+ embed = discord .Embed (
711+ title = f"🏰 Guild List (Page { page } /{ total_pages } )" ,
712+ color = discord .Color .blue ()
713+ )
714+
715+ for i , guild in enumerate (page_guilds , start = start_idx + 1 ):
716+ premium_indicator = "👑" if getattr (guild , 'premium_tier' , 0 ) > 0 else ""
717+ large_indicator = "🏢" if getattr (guild , 'large' , False ) else ""
718+
719+ embed .add_field (
720+ name = f"{ i } . { guild .name } { premium_indicator } { large_indicator } " ,
721+ value = f"**ID:** `{ guild .id } `\n "
722+ f"**Members:** { guild .member_count or 0 :,} \n "
723+ f"**Owner:** <@{ guild .owner_id } >" if guild .owner_id else "Unknown" ,
724+ inline = True
725+ )
726+
727+ embed .set_footer (text = f"Total: { len (guild_list )} guilds • Use .guildlist <page> to navigate" )
728+ await ctx .send (embed = embed )
729+
730+ @commands .command (name = 'guildinfo' , aliases = ['serverinfo' ])
731+ @commands .is_owner ()
732+ async def guild_info (self , ctx , * , guild_query : str ):
733+ """Get detailed information about a specific guild"""
734+ # Try to find guild by ID first, then by name
735+ guild = None
736+ if guild_query .isdigit ():
737+ guild = self .bot .get_guild (int (guild_query ))
738+
739+ if not guild :
740+ # Search by name (case insensitive)
741+ guild = discord .utils .find (
742+ lambda g : guild_query .lower () in g .name .lower (),
743+ self .bot .guilds
744+ )
745+
746+ if not guild :
747+ await ctx .send (f"❌ Guild not found: `{ guild_query } `" )
748+ return
749+
750+ embed = discord .Embed (
751+ title = f"🏰 { guild .name } " ,
752+ color = discord .Color .green ()
753+ )
754+
755+ if guild .icon :
756+ embed .set_thumbnail (url = guild .icon .url )
757+
758+ embed .add_field (
759+ name = "📊 Basic Info" ,
760+ value = f"**ID:** `{ guild .id } `\n "
761+ f"**Owner:** <@{ guild .owner_id } >\n "
762+ f"**Created:** <t:{ int (guild .created_at .timestamp ())} :R>\n "
763+ f"**Joined:** <t:{ int (guild .me .joined_at .timestamp ())} :R>" if guild .me .joined_at else "Unknown" ,
764+ inline = True
765+ )
766+
767+ embed .add_field (
768+ name = "👥 Members & Channels" ,
769+ value = f"**Members:** { guild .member_count or 0 :,} \n "
770+ f"**Channels:** { len (guild .channels ):,} \n "
771+ f"**Roles:** { len (guild .roles ):,} \n "
772+ f"**Emojis:** { len (guild .emojis ):,} " ,
773+ inline = True
774+ )
775+
776+ embed .add_field (
777+ name = "🎯 Features" ,
778+ value = f"**Verification:** { guild .verification_level } \n "
779+ f"**Premium Tier:** { guild .premium_tier if hasattr (guild , 'premium_tier' ) else 0 } \n "
780+ f"**Large Guild:** { 'Yes' if getattr (guild , 'large' , False ) else 'No' } \n "
781+ f"**Features:** { len (guild .features )} special" ,
782+ inline = True
783+ )
784+
785+ if guild .description :
786+ embed .add_field (
787+ name = "📝 Description" ,
788+ value = guild .description [:500 ] + ("..." if len (guild .description ) > 500 else "" ),
789+ inline = False
790+ )
791+
792+ if guild .features :
793+ features_str = ", " .join (guild .features [:10 ])
794+ if len (guild .features ) > 10 :
795+ features_str += f" (+{ len (guild .features ) - 10 } more)"
796+ embed .add_field (
797+ name = "✨ Special Features" ,
798+ value = f"`{ features_str } `" ,
799+ inline = False
800+ )
801+
802+ # Bot permissions in the guild
803+ if guild .me :
804+ perms = guild .me .guild_permissions
805+ important_perms = []
806+ if perms .administrator :
807+ important_perms .append ("Administrator" )
808+ if perms .manage_guild :
809+ important_perms .append ("Manage Server" )
810+ if perms .manage_channels :
811+ important_perms .append ("Manage Channels" )
812+ if perms .kick_members :
813+ important_perms .append ("Kick Members" )
814+ if perms .ban_members :
815+ important_perms .append ("Ban Members" )
816+
817+ embed .add_field (
818+ name = "🔐 Bot Permissions" ,
819+ value = ", " .join (important_perms [:5 ]) if important_perms else "Basic permissions" ,
820+ inline = False
821+ )
822+
823+ embed .timestamp = datetime .utcnow ()
824+ await ctx .send (embed = embed )
825+
549826 def get_stats_summary (self ) -> Dict :
550827 """Get a summary of current stats"""
551828 uptime = datetime .now () - self .start_time
0 commit comments