Skip to content

Commit c2b3328

Browse files
authored
Merge pull request #146 from FroostySnoowman/master
Components v2
2 parents b15a6ec + 18d7434 commit c2b3328

30 files changed

+1496
-330
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,7 @@ chat_exporter/config.py
169169
*.sqlite
170170

171171
.idea/*
172-
.idea\*
172+
.idea\*
173+
174+
# macOS
175+
.DS_Store

chat_exporter/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
AttachmentToWebhookHandler,
88
AttachmentToDiscordChannelHandler)
99

10-
__version__ = "2.9.0"
10+
__version__ = "3.0.0"
1111

1212
__all__ = (
1313
export,

chat_exporter/construct/assets/attachment.py

Lines changed: 98 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)