Skip to content

Commit 9ce8998

Browse files
authored
Merge pull request #150 from FroostySnoowman/master
V3.0.0 Layout Fixes
2 parents 5a5e62e + 4d29e6c commit 9ce8998

File tree

5 files changed

+113
-16
lines changed

5 files changed

+113
-16
lines changed

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__ = "3.0.0"
10+
__version__ = "3.0.1"
1111

1212
__all__ = (
1313
export,

chat_exporter/construct/assets/embed.py

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import html
22

3+
from pytz import timezone
4+
35
from chat_exporter.ext.discord_import import discord
46

57
from chat_exporter.ext.html_generator import (
@@ -44,9 +46,11 @@ class Embed:
4446

4547
check_against = None
4648

47-
def __init__(self, embed, guild):
49+
def __init__(self, embed, guild, pytz_timezone=None, military_time=True):
4850
self.embed: discord.Embed = embed
4951
self.guild: discord.Guild = guild
52+
self.pytz_timezone = pytz_timezone
53+
self.military_time = military_time
5054

5155
async def flow(self):
5256
self.check_against = _gather_checker()
@@ -68,13 +72,45 @@ def build_colour(self):
6872
if self.embed.colour != self.check_against else (0x4A, 0x4A, 0x50)
6973
)
7074

75+
def _format_embed_timestamp(self) -> str:
76+
timestamp = getattr(self.embed, "timestamp", None)
77+
if not timestamp or timestamp == self.check_against:
78+
return ""
79+
80+
time_value = timestamp
81+
if not getattr(time_value, "tzinfo", None):
82+
time_value = timezone("UTC").localize(time_value)
83+
84+
tz_name = self.pytz_timezone or getattr(self.guild, "timezone", "UTC") or "UTC"
85+
try:
86+
tz = timezone(tz_name)
87+
except Exception:
88+
tz = timezone("UTC")
89+
90+
local_time = time_value.astimezone(tz)
91+
if self.military_time:
92+
return local_time.strftime("%d-%m-%Y %H:%M")
93+
return local_time.strftime("%d-%m-%Y %I:%M %p")
94+
7195
async def build_title(self):
72-
self.title = html.escape(self.embed.title) if self.embed.title != self.check_against else ""
96+
raw_title = html.escape(self.embed.title) if self.embed.title != self.check_against else ""
7397

74-
if self.title:
75-
self.title = await fill_out(self.guild, embed_title, [
76-
("EMBED_TITLE", self.title, PARSE_MODE_MARKDOWN)
77-
])
98+
if not raw_title:
99+
self.title = ""
100+
return
101+
102+
title_html = await fill_out(self.guild, "{{EMBED_TITLE}}", [
103+
("EMBED_TITLE", raw_title, PARSE_MODE_MARKDOWN)
104+
])
105+
106+
url_value = getattr(self.embed, "url", self.check_against)
107+
if url_value and url_value != self.check_against:
108+
safe_url = html.escape(str(url_value), quote=True)
109+
title_html = f'<a href="{safe_url}">{title_html}</a>'
110+
111+
self.title = await fill_out(self.guild, embed_title, [
112+
("EMBED_TITLE", title_html, PARSE_MODE_NONE)
113+
])
78114

79115
async def build_description(self):
80116
self.description = html.escape(self.embed.description) if self.embed.description != self.check_against else ""
@@ -136,25 +172,32 @@ async def build_thumbnail(self):
136172
if self.embed.thumbnail and self.embed.thumbnail.url != self.check_against else ""
137173

138174
async def build_footer(self):
139-
self.footer = html.escape(self.embed.footer.text) if (
175+
footer_text = html.escape(self.embed.footer.text) if (
140176
self.embed.footer and self.embed.footer.text != self.check_against
141177
) else ""
142178

143179
footer_icon = self.embed.footer.icon_url if (
144180
self.embed.footer and self.embed.footer.icon_url != self.check_against
145181
) else None
146182

147-
if not self.footer:
183+
timestamp_text = self._format_embed_timestamp()
184+
if footer_text and timestamp_text:
185+
footer_text = f"{footer_text} | {timestamp_text}"
186+
elif not footer_text and timestamp_text:
187+
footer_text = timestamp_text
188+
189+
if not footer_text:
190+
self.footer = ""
148191
return
149192

150193
if footer_icon is not None:
151194
self.footer = await fill_out(self.guild, embed_footer_icon, [
152-
("EMBED_FOOTER", self.footer, PARSE_MODE_NONE),
195+
("EMBED_FOOTER", footer_text, PARSE_MODE_NONE),
153196
("EMBED_FOOTER_ICON", footer_icon, PARSE_MODE_NONE)
154197
])
155198
else:
156199
self.footer = await fill_out(self.guild, embed_footer, [
157-
("EMBED_FOOTER", self.footer, PARSE_MODE_NONE)])
200+
("EMBED_FOOTER", footer_text, PARSE_MODE_NONE)])
158201

159202
async def build_embed(self):
160203
self.embed = await fill_out(self.guild, embed_body, [
@@ -168,4 +211,4 @@ async def build_embed(self):
168211
("EMBED_DESC", self.description, PARSE_MODE_NONE),
169212
("EMBED_FIELDS", self.fields, PARSE_MODE_NONE),
170213
("EMBED_FOOTER", self.footer, PARSE_MODE_NONE),
171-
])
214+
])

chat_exporter/construct/message.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,48 @@ def get_message_snapshots(self):
9494
return self.message.snapshots
9595
return []
9696

97+
@staticmethod
98+
def _collect_attachment_urls(attachment):
99+
urls = set()
100+
for attr in ("url", "proxy_url"):
101+
value = getattr(attachment, attr, None)
102+
if value:
103+
urls.add(str(value))
104+
return urls
105+
106+
@staticmethod
107+
def _embed_has_non_image_content(embed) -> bool:
108+
if getattr(embed, "title", None):
109+
return True
110+
if getattr(embed, "description", None):
111+
return True
112+
if getattr(embed, "fields", None):
113+
if len(embed.fields) > 0:
114+
return True
115+
author = getattr(embed, "author", None)
116+
if author and getattr(author, "name", None):
117+
return True
118+
footer = getattr(embed, "footer", None)
119+
if footer and getattr(footer, "text", None):
120+
return True
121+
thumbnail = getattr(embed, "thumbnail", None)
122+
if thumbnail and getattr(thumbnail, "url", None):
123+
return True
124+
return False
125+
126+
def _is_duplicate_image_embed(self, embed, attachment_urls) -> bool:
127+
if not attachment_urls:
128+
return False
129+
image = getattr(embed, "image", None)
130+
if not image:
131+
return False
132+
image_url = getattr(image, "proxy_url", None) or getattr(image, "url", None)
133+
if not image_url or str(image_url) not in attachment_urls:
134+
return False
135+
if self._embed_has_non_image_content(embed):
136+
return False
137+
return True
138+
97139
async def construct_message(
98140
self,
99141
) -> (str, dict):
@@ -308,8 +350,8 @@ async def build_sticker(self):
308350
])
309351

310352
async def build_assets(self):
311-
for e in self.message.embeds:
312-
self.embeds += await Embed(e, self.guild).flow()
353+
processed_attachments = []
354+
attachment_urls = set()
313355

314356
for snapshot in self.get_message_snapshots():
315357
if hasattr(snapshot, "embeds"):
@@ -320,6 +362,15 @@ async def build_assets(self):
320362
for a in self.message.attachments:
321363
if self.attachment_handler and isinstance(self.attachment_handler, AttachmentHandler):
322364
a = await self.attachment_handler.process_asset(a)
365+
processed_attachments.append(a)
366+
attachment_urls.update(self._collect_attachment_urls(a))
367+
368+
for e in self.message.embeds:
369+
if self._is_duplicate_image_embed(e, attachment_urls):
370+
continue
371+
self.embeds += await Embed(e, self.guild, self.pytz_timezone, self.military_time).flow()
372+
373+
for a in processed_attachments:
323374
self.attachments += await Attachment(a, self.guild).flow()
324375

325376
for snapshot in self.get_message_snapshots():

chat_exporter/html/base.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,9 @@
474474

475475
.chatlog__attachment.chatlog__attachment-spoiler {
476476
overflow: hidden;
477+
position: relative;
478+
display: block;
479+
width: fit-content;
477480
}
478481

479482
.chatlog__attachment.chatlog__attachment-spoiler::before {
@@ -554,7 +557,7 @@
554557

555558
.chatlog__embed {
556559
--embed-color: rgba(74, 74, 80, 1);
557-
display: inline-grid;
560+
display: grid;
558561
grid-template-columns: minmax(0, 1fr) auto;
559562
column-gap: 1rem;
560563
margin-top: 0.35em;

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55
[project]
66
name = "chat_exporter"
77
description = "A simple Discord chat exporter for Python Discord bots."
8-
version = "3.0.0"
8+
version = "3.0.1"
99
readme = "README.md"
1010
authors = [
1111
{ name="mahtoid", email="info@mahto.id" }

0 commit comments

Comments
 (0)