From eeb7d8e66cd3ece37d2c14488e32228fadf56c32 Mon Sep 17 00:00:00 2001 From: Volker Date: Sun, 12 Jan 2025 20:10:59 -0600 Subject: [PATCH] Add option to add/skip DMs (#207) * Add option to add/skip DMs * Fix: handle exports with DMs --- README.md | 2 + slackviewer/cli.py | 42 +++++++++++++++++-- slackviewer/main.py | 35 +++++++++++----- slackviewer/reader.py | 20 ++++++--- .../example_template_single_export.html | 19 +++++++++ slackviewer/templates/export_single.html | 19 +++++++++ slackviewer/templates/viewer.html | 6 +++ 7 files changed, 124 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 7a4832a..d835900 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ Usage: cli.py export [OPTIONS] ARCHIVE_DIR Options: --debug + --show-dms Show direct messages --since [%Y-%m-%d] Only show messages since this date. --template FILENAME Custom single file export template --help Show this message and exit. @@ -153,6 +154,7 @@ Export: $ slack-export-viewer-cli export \ --since $(date -d "2 days ago" '+%Y-%m-%d') \ --template /tmp/example_template_single_export.html \ + --show-dms \ /tmp/slack-export Archive already extracted. Viewing from /tmp/slack-export... Exported to slack-export.html diff --git a/slackviewer/cli.py b/slackviewer/cli.py index 95853b6..072beb8 100644 --- a/slackviewer/cli.py +++ b/slackviewer/cli.py @@ -34,30 +34,66 @@ def clean(wet): @cli.command(help="Generates a single-file printable export for an archive file or directory") @click.option('--debug', is_flag=True, default=flag_ennvar("FLASK_DEBUG")) +@click.option('--show-dms', is_flag=True, default=False, help="Show direct messages") @click.option("--since", default=None, type=click.DateTime(formats=["%Y-%m-%d"]), help="Only show messages since this date.") @click.option("--template", default=None, type=click.File('r'), help="Custom single file export template") @click.argument('archive_dir') -def export(archive_dir, debug, since, template): +def export(archive_dir, debug, since, template, show_dms): css = pkgutil.get_data('slackviewer', 'static/viewer.css').decode('utf-8') tmpl = Environment(loader=PackageLoader('slackviewer')).get_template("export_single.html") if template: tmpl = Environment(loader=PackageLoader('slackviewer')).from_string(template.read()) export_file_info = get_export_info(archive_dir) - r = Reader(export_file_info["readable_path"], debug, since) + config = { + "debug": debug, + "since": since, + } + r = Reader(export_file_info["readable_path"], config) channel_list = sorted( [{"channel_name": k, "messages": v} for (k, v) in r.compile_channels().items()], key=lambda d: d["channel_name"] ) + dm_list = [] + mpims = [] + if show_dms: + # + # Direct DMs + dm_list = r.compile_dm_messages() + dm_users = r.compile_dm_users() + + # make list better lookupable. Also hide own user in 1:1 DMs + dm_users = {dm['id']: dm['users'][0].display_name for dm in dm_users} + + # replace id with slack username + dm_list = [{'name': dm_users[k], 'messages': v} for k, v in dm_list.items()] + + # + # Group DMs + mpims = r.compile_mpim_messages() + mpim_users = r.compile_mpim_users() + + # make list better lookupable + mpim_users = {g['name']: g['users'] for g in mpim_users} + # Get the username instead of object + mpim_users = {k: [u.display_name for u in v] for k, v in mpim_users.items()} + # make the name a string + mpim_users = {k: ', '.join(v) for k, v in mpim_users.items()} + + # replace id with group member list + mpims = [{'name': mpim_users[k], 'messages': v} for k, v in mpims.items()] + html = tmpl.render( css=css, generated_on=datetime.now(), workspace_name=export_file_info["workspace_name"], source_file=export_file_info["basename"], - channels=channel_list + channels=channel_list, + dms=dm_list, + mpims=mpims, ) with open(export_file_info['stripped_name'] + '.html', 'wb') as outfile: outfile.write(html.encode('utf-8')) diff --git a/slackviewer/main.py b/slackviewer/main.py index a73f320..ccb26b2 100644 --- a/slackviewer/main.py +++ b/slackviewer/main.py @@ -11,25 +11,31 @@ from slackviewer.utils.click import envvar, flag_ennvar -def configure_app(app, archive, channels, no_sidebar, no_external_references, debug, since): - app.debug = debug - app.no_sidebar = no_sidebar - app.no_external_references = no_external_references +def configure_app(app, archive, channels, config): + app.debug = config.get("debug", False) + app.no_sidebar = config.get("no_sidebar", False) + app.no_external_references = config.get("no_external_references", False) if app.debug: print("WARNING: DEBUG MODE IS ENABLED!") app.config["PROPAGATE_EXCEPTIONS"] = True path = extract_archive(archive) - reader = Reader(path, debug, since) + reader = Reader(path, config) top = flask._app_ctx_stack top.path = path top.channels = reader.compile_channels(channels) top.groups = reader.compile_groups() - top.dms = reader.compile_dm_messages() - top.dm_users = reader.compile_dm_users() - top.mpims = reader.compile_mpim_messages() - top.mpim_users = reader.compile_mpim_users() + top.dms = {} + top.dm_users = [] + top.mpims = {} + top.mpim_users = [] + if not config.get("skip_dms", False): + top.dms = reader.compile_dm_messages() + top.dm_users = reader.compile_dm_users() + top.mpims = reader.compile_mpim_messages() + top.mpim_users = reader.compile_mpim_users() + # remove any empty channels & groups. DM's are needed for now # since the application loads the first @@ -68,6 +74,7 @@ def configure_app(app, archive, channels, no_sidebar, no_external_references, de help="If you want static HTML only, set this.") @click.option("--since", default=None, type=click.DateTime(formats=["%Y-%m-%d"]), help="Only show messages since this date.") +@click.option('--skip-dms', is_flag=True, default=False, help="Hide direct messages") def main( port, @@ -82,11 +89,19 @@ def main( output_dir, html_only, since, + skip_dms, ): if not archive: raise ValueError("Empty path provided for archive") - configure_app(app, archive, channels, no_sidebar, no_external_references, debug, since) + config = { + "debug": debug, + "since": since, + "skip_dms": skip_dms, + "no_sidebar": no_sidebar, + "no_external_references": no_external_references, + } + configure_app(app, archive, channels, config) if html_only: # We need relative URLs, otherwise channel refs do not work diff --git a/slackviewer/reader.py b/slackviewer/reader.py index c26ec58..ee86d40 100644 --- a/slackviewer/reader.py +++ b/slackviewer/reader.py @@ -17,10 +17,10 @@ class Reader(object): Reader object will read all of the archives' data from the json files """ - def __init__(self, PATH, debug, since): + def __init__(self, PATH, config): self._PATH = PATH - self._debug = debug - self._since = since + self._debug = config.get("debug", False) + self._since = config.get("since", None) # slack name that is in the url https://.slack.com self._slack_name = self._get_slack_name() # TODO: Make sure this works @@ -54,6 +54,7 @@ def compile_channels(self, channels=None): return self._create_messages(channel_names, channel_data) def compile_groups(self): + """Get private channels""" group_data = self._read_from_json("groups.json") group_names = [c["name"] for c in group_data.values()] @@ -104,6 +105,7 @@ def compile_dm_users(self): return all_dms_users def compile_mpim_messages(self): + """Return multiple person DM groups""" mpim_data = self._read_from_json("mpims.json") mpim_names = [c["name"] for c in mpim_data.values()] @@ -168,11 +170,17 @@ def _create_messages(self, names, data, isDms=False): formatter = SlackFormatter(self.__USER_DATA, data) # Channel name to channel id mapping. Needed to create a messages - # permalink when using slackdump - channel_name_to_id = {c["name"]: c["id"] for c in data.values()} + # permalink with at least slackdump exports + channel_name_to_id = {} + for c in data.values(): + if "name" in c: + channel_name_to_id[c["name"]] = c["id"] + else: + # direct messages have no channel name and are also + # stored with the the id's folder. + channel_name_to_id[c["id"]] = c["id"] for name in names: - # gets path to dm directory that holds the json archive dir_path = os.path.join(self._PATH, name) messages = [] diff --git a/slackviewer/templates/example_template_single_export.html b/slackviewer/templates/example_template_single_export.html index 7577232..2f8ea9c 100644 --- a/slackviewer/templates/example_template_single_export.html +++ b/slackviewer/templates/example_template_single_export.html @@ -357,6 +357,25 @@

TEMPLATE Export of Slack Workspace "{{workspace_name}}"

Generated on: {{generated_on.strftime("%F %H:%M:%S")}} + {% for dm in dms %} +
+

DM with {{dm.name}}

+
+ {% for message in dm.messages %} + {{render_message(message)}} + {% endfor %} +
+
+ {% endfor %} + {% for mpim in mpims %} +
+

Group DM with {{mpim.name}}

+
+ {% for message in mpim.messages %} + {{render_message(message)}} + {% endfor %} +
+ {% endfor %} {% for channel in channels %}

Messages in #{{channel.channel_name}}

diff --git a/slackviewer/templates/export_single.html b/slackviewer/templates/export_single.html index a249079..e8d26f5 100644 --- a/slackviewer/templates/export_single.html +++ b/slackviewer/templates/export_single.html @@ -21,6 +21,25 @@

Export of Slack Workspace "{{workspace_name}}"

Generated on: {{generated_on.strftime("%F %H:%M:%S")}}
+ {% for dm in dms %} +
+

DM with {{dm.name}}

+
+ {% for message in dm.messages %} + {{render_message(message)}} + {% endfor %} +
+
+ {% endfor %} + {% for mpim in mpims %} +
+

Group DM with {{mpim.name}}

+
+ {% for message in mpim.messages %} + {{render_message(message)}} + {% endfor %} +
+ {% endfor %} {% for channel in channels %}

Messages in #{{channel.channel_name}}

diff --git a/slackviewer/templates/viewer.html b/slackviewer/templates/viewer.html index 8ae5a49..e84db12 100644 --- a/slackviewer/templates/viewer.html +++ b/slackviewer/templates/viewer.html @@ -23,6 +23,7 @@

Public Channels

{% endfor %} + {% if groups %}

Private Channels

    {% for group in groups %} @@ -33,6 +34,8 @@

    Private Channels

    {% endfor %}
+ {% endif %} + {% if dms %}

Direct Messages

    {% for dm in dm_users %} @@ -44,6 +47,8 @@

    Direct Messages

    {% endfor %}
+ {% endif %} + {% if mpims %}

Group Direct Messages

    {% for mpim in mpim_users %} @@ -57,6 +62,7 @@

    Group Direct Messages

    {% endfor %}
+ {% endif %}
{%- endif -%}