diff --git a/slackdump2html.py b/slackdump2html.py
index a803220..9cd487d 100644
--- a/slackdump2html.py
+++ b/slackdump2html.py
@@ -18,13 +18,17 @@ def get_input_file_path() -> str:
input_file = get_input_file_path()
data_cleaner = SlackDataCleaner()
- print("Reading slack dump...", flush=True)
+ data_dir = None
+ if input_file.endswith(".json") and os.path.exists(input_file[:-5]):
+ data_dir = input_file[:-5]
+
+ print(f"Reading slack dump...", flush=True)
reader = SlackDumpReader(data_cleaner)
- slack_data = reader.read(input_file)
+ slack_data = reader.read(input_file, data_dir)
print("Cleaning data...", flush=True)
data_cleaner.replace_names(slack_data)
print("Printing output file...", flush=True)
- html_printer = HtmlPrinter(slack_data)
+ html_printer = HtmlPrinter(slack_data, data_dir)
html_printer.print()
diff --git a/src/HtmlPrinter.py b/src/HtmlPrinter.py
index 3ff1692..0c894b1 100644
--- a/src/HtmlPrinter.py
+++ b/src/HtmlPrinter.py
@@ -1,11 +1,15 @@
import os
import re
+import shutil
+import urllib
from datetime import datetime
+from html import escape
+from pathlib import Path
import emoji
from src.SlackDataCleaner import SlackDataCleaner
-from src.SlackDumpReader import SlackData, SlackMessage, SlackThreadMessage
+from src.SlackDumpReader import SlackData, SlackFile, SlackMessage, SlackThreadMessage
class HtmlPrinter:
@@ -13,9 +17,11 @@ class HtmlPrinter:
standard_emojis: dict[str, str] = dict()
used_custom_emojis: set[str] = set()
data_cleaner = SlackDataCleaner()
+ data_dir: str | None
- def __init__(self, slack_data: SlackData):
+ def __init__(self, slack_data: SlackData, data_dir: str | None):
self.slack_data = slack_data
+ self.data_dir = data_dir
def print(self):
html1 = "\n"
@@ -71,6 +77,7 @@ def print_message(self, message: SlackMessage) -> str:
html += f' {self.to_time(message.date)} \n'
html += "
\n"
html += f' {self.format_message(message.text)}
\n'
+ html += self.print_files(message.files)
html += self.print_reactions(message.reactions)
html += self.print_replies(message)
html += " \n"
@@ -90,7 +97,7 @@ def calc_color_num(self, user: str) -> int:
letter_sum += ord(letter)
return letter_sum % 15
- def format_message(self, text: str) -> str:
+ def format_message(self, text: str) -> str:
text = text.replace("", 'here ')
text = text.replace("", 'channel ')
text = re.sub(r"<(http.*?)\|(.*?)>", self.create_html_url_with_alias, text)
@@ -182,6 +189,27 @@ def print_replies(self, message: SlackMessage) -> str:
html += " \n"
return html
+ def print_files(self, files: list[SlackFile]) -> str:
+ html = ""
+ for file in files:
+ input_filename = f"{self.data_dir}/{file.id}-{file.name}"
+ output_filename = f"{self.slack_data.channel_name}/{file.id}-{file.name}"
+ output_file = Path("out/" + output_filename)
+ output_file.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copyfile(input_filename, f"out/{output_filename}")
+ relative_url = urllib.parse.quote(output_filename)
+
+ if file.mimetype.startswith("image/"):
+ html += f'
\n'
+ elif file.mimetype.startswith("video/"):
+ html += f'
\n'
+ elif file.mimetype.startswith("audio/"):
+ html += f'
\n'
+ else:
+ text = escape(file.title)
+ html += f' Download {text}
\n'
+ return html
+
def print_reply(self, reply: SlackThreadMessage) -> str:
html = ' \n'
html += self.print_user_image(reply.user)
@@ -190,6 +218,7 @@ def print_reply(self, reply: SlackThreadMessage) -> str:
html += f'
{self.to_datetime(reply.date)} \n'
html += " \n"
html += f'
{self.format_message(reply.text)}
\n'
+ html += self.print_files(reply.files)
html += self.print_reactions(reply.reactions)
html += "
\n"
return html
diff --git a/src/SlackDataCleaner.py b/src/SlackDataCleaner.py
index 279129f..2ccaa2c 100644
--- a/src/SlackDataCleaner.py
+++ b/src/SlackDataCleaner.py
@@ -189,6 +189,8 @@ def _read_channel_file(self):
for line in lines[1:]:
line = line.replace("🔒 ", "🔒").strip()
parts = line.split(" ")
+ if parts[-1] == '(archived)':
+ continue
self.channel_map[parts[0]] = (ChannelType(parts[-1][0]), parts[-1][1:])
def replace_names(self, slack_data: SlackData):
diff --git a/src/SlackDumpReader.py b/src/SlackDumpReader.py
index 9f14c1e..e7dea5f 100644
--- a/src/SlackDumpReader.py
+++ b/src/SlackDumpReader.py
@@ -6,7 +6,7 @@
from typing import Tuple
from src.SlackDataCleaner import SlackDataCleaner
-from src.data_structures import SlackData, SlackMessage, SlackThreadMessage, ChannelType
+from src.data_structures import SlackData, SlackFile, SlackMessage, SlackThreadMessage, ChannelType
EMOJI_PATH = "data/emojis/emojis/"
@@ -17,7 +17,7 @@ class SlackDumpReader:
def __init__(self, data_cleaner: SlackDataCleaner):
self.data_cleaner = data_cleaner
- def read(self, file_path: str) -> SlackData:
+ def read(self, file_path: str, data_dir: str | None) -> SlackData:
dump_file = open(file_path, encoding="utf-8")
file_name = Path(file_path).stem
dump_data = json.load(dump_file)
@@ -25,14 +25,15 @@ def read(self, file_path: str) -> SlackData:
messages: list[SlackMessage] = list()
for message in dump_data["messages"]:
- if message["type"] == "message" and "subtype" not in message and "text" in message:
- replies = self.read_replies(message)
+ if message["type"] == "message" and "subtype" not in message:
+ replies = self.read_replies(message, data_dir)
messages.append(
SlackMessage(
user=self.get_user(message),
text=self.get_message_content(message),
date=self.to_datetime(message["ts"]),
reactions=self.read_reactions(message),
+ files=self.read_files(message, data_dir),
replies=replies,
)
)
@@ -65,7 +66,7 @@ def get_message_content(self, message: dict) -> str:
if self.is_gif(message):
return f' '
else:
- return message["text"]
+ return message.get("text", "")
def is_gif(self, message: dict) -> bool:
return (
@@ -74,7 +75,7 @@ def is_gif(self, message: dict) -> bool:
and str(message["blocks"][0]["image_url"]).__contains__("giphy.com")
)
- def read_replies(self, message: dict) -> list[SlackThreadMessage]:
+ def read_replies(self, message: dict, data_dir: str | None) -> list[SlackThreadMessage]:
replies: list[SlackThreadMessage] = list()
if "slackdump_thread_replies" in message:
for reply in message["slackdump_thread_replies"]:
@@ -85,10 +86,31 @@ def read_replies(self, message: dict) -> list[SlackThreadMessage]:
text=self.get_message_content(reply),
date=self.to_datetime(reply["ts"]),
reactions=self.read_reactions(reply),
+ files=self.read_files(reply, data_dir)
)
)
return replies
+ def read_files(self, message: dict, data_dir: str | None) -> list[SlackFile]:
+ files: list[SlackFile] = list()
+ if "files" in message and data_dir:
+ for file in message["files"]:
+ if not file["id"] or not file["name"]:
+ continue
+ data_file = Path(data_dir) / ("%s-%s" % (file["id"], file["name"]))
+ if not data_file.exists():
+ print(f"missing file {data_file}")
+ continue
+ files.append(
+ SlackFile(
+ id=file["id"],
+ name=file["name"],
+ title=file["title"],
+ mimetype=file["mimetype"],
+ )
+ )
+ return files
+
def read_reactions(self, message: dict) -> dict[str, int]:
reactions: dict[str, int] = dict()
if "reactions" in message:
diff --git a/src/data_structures.py b/src/data_structures.py
index b972194..8c7f092 100644
--- a/src/data_structures.py
+++ b/src/data_structures.py
@@ -3,12 +3,20 @@
from enum import StrEnum
+@dataclass
+class SlackFile:
+ id: str
+ name: str
+ title: str
+ mimetype: str
+
@dataclass
class SlackThreadMessage:
user: str
text: str
date: datetime
reactions: dict[str, int]
+ files: list[SlackFile]
@dataclass
@@ -18,6 +26,7 @@ class SlackMessage:
date: datetime
reactions: dict[str, int]
replies: list[SlackThreadMessage]
+ files: list[SlackFile]
class ChannelType(StrEnum):
diff --git a/src/style/style.css b/src/style/style.css
index 40d7825..1e654a3 100644
--- a/src/style/style.css
+++ b/src/style/style.css
@@ -226,6 +226,20 @@ h1 {
max-width: 1.1em;
}
+.embedded {
+ margin-left: 3.5em;
+}
+
+.embedded-img > img {
+ max-height: 35vh;
+ max-width: 100vh;
+}
+
+.embedded-video > video {
+ max-height: 35vh;
+ max-width: 100vh;
+}
+
code {
background-color: #efefef;
border: 1px solid #d4d4d4;