@@ -21,14 +21,28 @@ async def flow(self):
2121 return self .attachments
2222
2323 async def build_attachment (self ):
24+ is_spoiler = self ._is_spoiler ()
25+
2426 if self .attachments .content_type is not None :
2527 if "image" in self .attachments .content_type :
26- return await self .image ()
28+ await self .image ()
29+ if is_spoiler :
30+ self ._mark_spoiler ()
31+ return
2732 elif "video" in self .attachments .content_type :
28- return await self .video ()
33+ await self .video ()
34+ if is_spoiler :
35+ self ._mark_spoiler ()
36+ return
2937 elif "audio" in self .attachments .content_type :
30- return await self .audio ()
38+ await self .audio ()
39+ if is_spoiler :
40+ self ._mark_spoiler ()
41+ return
42+
3143 await self .file ()
44+ if is_spoiler :
45+ self ._mark_spoiler ()
3246
3347 async def image (self ):
3448 self .attachments = await fill_out (self .guild , img_attachment , [
@@ -76,6 +90,14 @@ def get_file_size(file_size):
7690 return "%s %s" % (s , size_name [i ])
7791
7892 async def get_file_icon (self ) -> str :
93+ return self .resolve_file_icon (
94+ name = str (getattr (self .attachments , "filename" , "" ) or "" ),
95+ content_type = str (getattr (self .attachments , "content_type" , "" ) or "" ),
96+ url = str (getattr (self .attachments , "proxy_url" , "" ) or "" )
97+ )
98+
99+ @staticmethod
100+ def resolve_file_icon (name : str = "" , content_type : str = "" , url : str = "" ) -> str :
79101 acrobat_types = "pdf"
80102 webcode_types = "html" , "htm" , "css" , "rss" , "xhtml" , "xml"
81103 code_types = "py" , "cgi" , "pl" , "gadget" , "jar" , "msi" , "wsf" , "bat" , "php" , "js"
@@ -84,23 +106,79 @@ async def get_file_icon(self) -> str:
84106 "sxi" , "sxc" , "sxd" , "stw"
85107 )
86108 archive_types = (
87- "br" , "rpm" , "dcm" , "epub" , "zip" , "tar" , "rar" , "gz" , "bz2" , "7x" , "deb " , "ar " , "Z " , "lzo " , "lz " , "lz4 " ,
88- "arj " , "pkg " , "z "
109+ "br" , "rpm" , "dcm" , "epub" , "zip" , "tar" , "rar" , "gz" , "bz2" , "7x" , "7z " , "deb " , "ar " , "z " , "lzo " , "lz " ,
110+ "lz4 " , "arj " , "pkg "
89111 )
90112
91- for tmp in [self .attachments .proxy_url , self .attachments .filename ]:
92- if not tmp :
93- continue
94- extension = tmp .rsplit ('.' , 1 )[- 1 ]
95- if extension in acrobat_types :
96- return DiscordUtils .file_attachment_acrobat
97- elif extension in webcode_types :
98- return DiscordUtils .file_attachment_webcode
99- elif extension in code_types :
100- return DiscordUtils .file_attachment_code
101- elif extension in document_types :
102- return DiscordUtils .file_attachment_document
103- elif extension in archive_types :
104- return DiscordUtils .file_attachment_archive
105-
113+ content_type = (content_type or "" ).lower ()
114+ if content_type .startswith ("audio/" ):
115+ return DiscordUtils .file_attachment_audio
116+
117+ def _extension_from (value : str ) -> str :
118+ if not value :
119+ return ""
120+ cleaned = str (value ).split ("?" , 1 )[0 ].split ("#" , 1 )[0 ]
121+ if "." not in cleaned :
122+ return ""
123+ return cleaned .rsplit ("." , 1 )[- 1 ].lower ()
124+
125+ extension = ""
126+ for candidate in (name , url ):
127+ extension = _extension_from (candidate )
128+ if extension :
129+ break
130+
131+ if not extension and content_type :
132+ if "html" in content_type :
133+ extension = "html"
134+ elif "pdf" in content_type :
135+ extension = "pdf"
136+
137+ if extension in acrobat_types :
138+ return DiscordUtils .file_attachment_acrobat
139+ elif extension in webcode_types :
140+ return DiscordUtils .file_attachment_webcode
141+ elif extension in code_types :
142+ return DiscordUtils .file_attachment_code
143+ elif extension in document_types :
144+ return DiscordUtils .file_attachment_document
145+ elif extension in archive_types :
146+ return DiscordUtils .file_attachment_archive
147+
106148 return DiscordUtils .file_attachment_unknown
149+
150+ def _is_spoiler (self ) -> bool :
151+ """Check if an attachment is marked as a spoiler."""
152+ attachment = self .attachments
153+ spoiler_attr = getattr (attachment , "spoiler" , None )
154+ if callable (spoiler_attr ):
155+ try :
156+ return bool (spoiler_attr ())
157+ except Exception :
158+ pass
159+ if spoiler_attr is not None :
160+ return bool (spoiler_attr )
161+
162+ is_spoiler_method = getattr (attachment , "is_spoiler" , None )
163+ if callable (is_spoiler_method ):
164+ try :
165+ return bool (is_spoiler_method ())
166+ except Exception :
167+ return False
168+
169+ return False
170+
171+ def _mark_spoiler (self ):
172+ """Add spoiler styling class to the rendered attachment HTML."""
173+ if not isinstance (self .attachments , str ):
174+ return
175+
176+ replacements = (
177+ ('<div class=chatlog__attachment>' , '<div class="chatlog__attachment chatlog__attachment-spoiler">' ),
178+ ('class="chatlog__attachment"' , 'class="chatlog__attachment chatlog__attachment-spoiler"' ),
179+ )
180+
181+ for target , replacement in replacements :
182+ if target in self .attachments :
183+ self .attachments = self .attachments .replace (target , replacement , 1 )
184+ break
0 commit comments