2222"""
2323from __future__ import annotations
2424
25+ from typing import TypeVar
26+
2527import discord
2628from discord .ext import commands
2729
2830import core
31+ from constants import GUILD_ID
2932
3033
31- class InformationEmbed (discord .Embed ):
32- """A subclass of discord.Embed.
34+ EntityT = TypeVar ("EntityT" , discord .Member , discord .User , discord .Role , discord .abc .GuildChannel , discord .Guild )
3335
34- This class allows to automatically get information within the class instead of recreating the embed each time
3536
36- :param author: The embed author. Expects discord.Member or discord.User
37- :param entity: The Member, User, Role, TextChannel, or Guild to get information about.
38- """
37+ class NoPermissions (commands .CommandError ):
38+ pass
3939
40- def __init__ (
41- self ,
42- * ,
43- author : discord .Member | discord .User ,
44- entity : discord .Member | discord .User | discord .Role | discord .TextChannel | discord .Guild ,
45- ) -> None :
46- super ().__init__ ()
47- created_at : str = f"{ discord .utils .format_dt (entity .created_at )} ({ discord .utils .format_dt (entity .created_at , 'R' )} )"
48- if isinstance (entity , discord .Member ) and entity .joined_at :
49- joined_at = f"\n \n Joined At: { discord .utils .format_dt (entity .joined_at )} ({ discord .utils .format_dt (entity .joined_at , 'R' )} )"
50- else :
51- joined_at = ""
5240
53- description = f"Name: { entity .name } \n \n ID: { entity .id } \n \n Created At: { created_at } { joined_at } "
54- if isinstance (entity , discord .Member ):
55- self .set_thumbnail (url = entity .guild_avatar or entity .display_avatar or None )
56- elif isinstance (entity , discord .User ):
57- self .set_thumbnail (url = entity .display_avatar or None )
41+ class Information (core .Cog ):
42+ """Information commands which allows you to get information about users, the guild, roles, and channels."""
43+
44+ def __init__ (self , bot : core .Bot ) -> None :
45+ self .bot : core .Bot = bot
46+
47+ async def cog_command_error (self , ctx : core .Context , error : commands .CommandError ) -> None : # type: ignore # bad lib types.
48+ error = getattr (error , "original" , error )
49+
50+ if isinstance (error , NoPermissions ):
51+ await ctx .send ("Sorry, you don't have permissions to view details on this object." )
52+ return
53+
54+ def _embed_factory (self , entity : EntityT ) -> discord .Embed :
55+ embed = discord .Embed (title = f"Info on { entity .name } !" , colour = discord .Colour .random ())
56+ embed .add_field (name = "ID:" , value = entity .id )
57+ embed .timestamp = discord .utils .utcnow ()
58+
59+ if isinstance (entity , discord .User ):
60+ return self ._user_info (entity , embed = embed )
61+ elif isinstance (entity , discord .Member ):
62+ embed = self ._user_info (entity , embed = embed ) # type: ignore # superclass
63+ return self ._member_info (entity , embed = embed )
5864 elif isinstance (entity , discord .Role ):
59- description += f"\n \n Hoisted: { entity .hoist } \n \n Mentionable: { entity .mentionable } \n \n "
60- elif isinstance (entity , discord .TextChannel ):
61- description += f"\n \n Category: { entity .category } \n \n NSFW: { entity .nsfw } "
62- else : # Change to elif when other types are added
63- description += f"\n \n Owner: { entity .owner } "
64- self .set_thumbnail (url = entity .icon or None )
65+ return self ._role_info (entity , embed = embed )
66+ elif isinstance (entity , discord .abc .GuildChannel ):
67+ return self ._channel_info (entity , embed = embed )
68+ else :
69+ return self ._guild_info (entity , embed = embed )
6570
66- self .description = description
67- self .set_author (name = author .name , icon_url = author .display_avatar )
68- self .color = 0x7289DA
71+ def _member_info (self , member : discord .Member , / , * , embed : discord .Embed ) -> discord .Embed :
72+ if member .joined_at :
73+ joined_at_fmt = (
74+ discord .utils .format_dt (member .joined_at , "F" ) + "\n " f"({ discord .utils .format_dt (member .joined_at , 'R' )} )"
75+ )
76+ embed .add_field (name = "Member joined the guild on:" , value = joined_at_fmt )
6977
78+ roles = [role .mention for role in member .roles [1 :]]
79+ roles .reverse ()
80+ embed .add_field (name = "Member's top 5 roles:-" , value = "\n " .join (roles [:5 ]), inline = False )
81+ embed .colour = member .colour or embed .colour
7082
71- class Information (core .Cog ):
72- """Information commands which allows you to get information about users, the guild, roles, and channels."""
83+ return embed
7384
74- def __init__ (self , bot : core .Bot ) -> None :
75- self .bot = bot
85+ def _user_info (self , user : discord .User , / , * , embed : discord .Embed ) -> discord .Embed :
86+ embed = discord .Embed (title = f"Info on { user .display_name } !" , colour = discord .Colour .random ())
87+ embed .set_author (name = user .name )
88+ embed .set_image (url = user .display_avatar .url )
89+ created_at_fmt = (
90+ discord .utils .format_dt (user .created_at , "F" ) + "\n " f"({ discord .utils .format_dt (user .created_at , 'R' )} )"
91+ )
92+ embed .add_field (name = "Account was created on:" , value = created_at_fmt )
93+
94+ embed .timestamp = discord .utils .utcnow ()
95+
96+ return embed
97+
98+ def _role_info (self , role : discord .Role , / , * , embed : discord .Embed ) -> discord .Embed :
99+ embed .colour = role .colour or embed .colour
100+ embed .add_field (name = "Mentionable?" , value = role .mentionable )
101+ embed .add_field (name = "Hoisted?" , value = role .hoist )
102+ embed .add_field (name = "Member count:" , value = len (role .members ))
103+ embed .add_field (name = "Created on:" , value = discord .utils .format_dt (role .created_at , "F" ))
104+
105+ return embed
106+
107+ def _channel_info (self , channel : discord .abc .GuildChannel , / , * , embed : discord .Embed ) -> discord .Embed :
108+ sneaky_role = channel .guild .default_role
109+ permissions = channel .permissions_for (sneaky_role )
110+
111+ allowed_to_read = discord .Permissions (read_messages = True , view_channel = True )
112+
113+ if not permissions .is_strict_superset (allowed_to_read ):
114+ # They cannot read this channel
115+ raise NoPermissions ("Cannot read this channel." )
116+
117+ embed .url = channel .jump_url
118+
119+ embed .add_field (name = "Channel type:" , value = channel .type .name , inline = False )
120+
121+ embed .add_field (name = "Created on:" , value = discord .utils .format_dt (channel .created_at , "F" ), inline = False )
122+
123+ is_private = not (permissions .view_channel or permissions .read_messages )
124+ embed .add_field (name = "Private Channel?" , value = is_private )
125+
126+ return embed
127+
128+ def _guild_info (self , guild : discord .Guild , / , * , embed : discord .Embed ) -> discord .Embed :
129+ if guild .id != GUILD_ID :
130+ raise NoPermissions ("Unreachable but better safe than sorry." )
131+
132+ embed .add_field (name = "Created on:" , value = discord .utils .format_dt (guild .created_at , "F" ))
133+ embed .set_thumbnail (url = (guild .icon and guild .icon .url ))
134+
135+ return embed
76136
77137 @commands .group (
78138 name = "information" ,
@@ -81,18 +141,21 @@ def __init__(self, bot: core.Bot) -> None:
81141 invoke_without_command = True ,
82142 )
83143 async def info (
84- self , ctx : core .Context , entity : discord .Member | discord .User | discord .Role | discord .TextChannel = commands .Author
144+ self ,
145+ ctx : core .Context ,
146+ * ,
147+ entity : discord .Member
148+ | discord .User
149+ | discord .Role
150+ | discord .abc .GuildChannel
151+ | discord .Guild = commands .CurrentGuild ,
85152 ) -> None :
86- """Get information about a object
87- Args:
88- entity: The user, role, or TextChannel to get information about"""
89- embed = InformationEmbed (author = ctx .author , entity = entity )
90- await ctx .send (embed = embed )
91-
92- @info .command (name = "guild" , brief = "Get the current guild's information." )
93- async def guild_info (self , ctx : core .GuildContext ):
94- embed = InformationEmbed (author = ctx .author , entity = ctx .guild )
95- await ctx .reply (embed = embed )
153+ """Get information about a specific Pythonista related object.
154+
155+ entity: Accepts a Person's ID, a Role ID or a Channel ID. Defaults to showing info on the Guild.
156+ """
157+ embed = self ._embed_factory (entity ) # type: ignore # we ignore because converter sadness
158+ await ctx .reply (embed = embed , mention_author = False )
96159
97160
98161async def setup (bot : core .Bot ) -> None :
0 commit comments