Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
slack2html
<?
/////////////////////
// slack2html
// by @levelsio
/////////////////////
//
/////////////////////
// WHAT DOES THIS DO?
/////////////////////
//
// Slack lets you export the chat logs (back to the first messages!), even if
// you are a free user (and have a 10,000 user limit)
//
// This is pretty useful for big chat groups (like mine, #nomads), where you
// do wanna see the logs, but can't see them within Slack
//
// Problem is that Slack exports it as JSON files, which is a bit unusable,
// so this script makes it into actual HTML files that look like Slack chats
//
///////////////////
// INSTRUCTIONS
///////////////////
//
// Run this script inside the directory of an extracted (!) Slack export zip
// e.g. "/tmp/#nomads Slack export Aug 25 2015" like this:
// MacBook-Pro:#nomads Slack export Aug 25 2015 mbp$ php slack2html.php
//
// It will then make two dirs:
// /slack2html/json
// /slack2html/html
//
// In the JSON dir it will put each channels chat log combined from all the
// daily logs that Slack outputs (e.g. /channel/2014-11-26.json)
//
// In the HTML dir it will generate HTML files with Slack's typical styling.
// It will also create an index.html that shows all channels
//
///////////////////
// FEEDBACK
///////////////////
//
// Let me know any bugs by tweeting me @levelsio
//
// Hope this helps!
//
// Pieter @levelsio
//
/////////////////////
ini_set('memory_limit', '1024M');
date_default_timezone_set('UTC');
mb_internal_encoding("UTF-8");
error_reporting(E_ERROR);
// <config>
$stylesheet="
* {
font-family:sans-serif;
}
body {
text-align:center;
padding:1em;
}
.messages {
width:100%;
max-width:700px;
text-align:left;
display:inline-block;
}
.messages img {
background-color:rgb(248,244,240);
width:36px;
height:36px;
border-radius:0.2em;
display:inline-block;
vertical-align:top;
margin-right:0.65em;
}
.messages .time {
display:inline-block;
color:rgb(200,200,200);
margin-left:0.5em;
}
.messages .username {
display:inline-block;
font-weight:600;
line-height:1;
}
.messages .message {
display:inline-block;
vertical-align:top;
line-height:1;
width:calc(100% - 3em);
}
.messages .message .msg {
line-height:1.5;
}
";
// </config>
// <compile daily logs into single channel logs>
$files=scandir(__DIR__);
$baseDir=__DIR__.'/../slack2html';
$jsonDir=$baseDir.'/'.'json';
if(!is_dir($baseDir)) mkdir($baseDir);
if(!is_dir($jsonDir)) mkdir($jsonDir);
foreach($files as $channel) {
if($channel=='.' || $channel=='..') continue;
if(is_dir($channel)) {
$channelJsonFile=$jsonDir.'/'.$channel.'.json';
if(file_exists($channelJsonFile)) {
echo "JSON already exists ".$channelJsonFile."\n";
continue;
}
unset($chats);
$chats=array();
echo '====='."\n";
echo 'Combining JSON files for #'.$channel."\n";
echo '====='."\n";
$dates=scandir(__DIR__.'/'.$channel);
foreach($dates as $date) {
if(!is_dir($date)) {
echo '.';
$messages=json_decode(file_get_contents(__DIR__.'/'.$channel.'/'.$date),true);
if(empty($messages)) continue;
foreach($messages as $message) {
array_push($chats,$message);
}
}
}
echo "\n";
file_put_contents($channelJsonFile,json_encode($chats));
echo number_format(count($chats)).' messages exported to '.$channelJsonFile."\n";
}
}
// </compile daily logs into single channel logs>
// <load users file>
$users=json_decode(file_get_contents(__DIR__.'/'.'users.json'),true);
$usersById=array();
foreach($users as $user) {
$usersById[$user['id']]=$user;
}
// </load users file>
// <load channels file>
$channels=json_decode(file_get_contents(__DIR__.'/'.'channels.json'),true);
$channelsById=array();
foreach($channels as $channel) {
$channelsById[$channel['id']]=$channel;
}
// </load channels file>
// <generate html from channel logs>
$htmlDir=$baseDir.'/'.'html';
if(!is_dir($htmlDir)) mkdir($htmlDir);
$channels=scandir($jsonDir);
$channelNames=array();
$mostRecentChannelTimestamps=array();
foreach($channels as $channel) {
if($channel=='.' || $channel=='..') continue;
if(is_dir($channel)) continue;
$mostRecentChannelTimestamp=0;
if($message['ts']>$mostRecentChannelTimestamp) {
$mostRecentChannelTimestamp=$message['ts'];
}
$array=explode('.json',$channel);
$channelName=$array[0];
$channelHtmlFile=$htmlDir.'/'.$channelName.'.html';
if(file_exists($channelHtmlFile)) {
echo "HTML already exists ".$channelJsonFile."\n";
continue;
}
array_push($channelNames,$channelName);
echo '====='."\n";
echo 'Generating HTML for #'.$channelName."\n";
echo '====='."\n";
$messages=json_decode(file_get_contents($jsonDir.'/'.$channel),true);
if(empty($messages)) continue;
$htmlMessages='<html><body><style>'.$stylesheet.'</style><div class="messages">';
foreach($messages as $message) {
if(empty($message)) continue;
if(empty($message['text'])) continue;
echo '.';
// change <@U38A3DE9> into levelsio
if(stripos($message['text'],'<@')!==false) {
$usersInMessage=explode('<@',$message['text']);
foreach($usersInMessage as $userInMessage) {
$array=explode('>',$userInMessage);
$userHandleInBrackets=$array[0];
$array=explode('|',$array[0]);
$userInMessage=$array[0];
$username=$array[1];
if(empty($username)) {
$username=$usersById[$userInMessage]['name'];
}
$message['text']=str_replace('<@'.$userHandleInBrackets.'>','@'.$username,$message['text']);
}
}
// change <#U38A3DE9> into #_chiang-mai
if(stripos($message['text'],'<#')!==false) {
$channelsInMessage=explode('<#',$message['text']);
foreach($channelsInMessage as $channelInMessage) {
$array=explode('>',$channelInMessage);
$channelHandleInBrackets=$array[0];
$array=explode('|',$array[0]);
$channelInMessage=$array[0];
$channelNameInMessage=$array[1];
if(empty($username)) {
$channelNameInMessage=$channelsById[$channelInMessage]['name'];
}
if(!empty($username)) {
$message['text']=str_replace('<#'.$channelHandleInBrackets.'>','#'.$channelNameInMessage,$message['text']);
}
}
}
// change <http://url> into link
if(stripos($message['text'],'<http')!==false) {
$linksInMessage=explode('<http',$message['text']);
foreach($linksInMessage as $linkInMessage) {
$array=explode('>',$linkInMessage);
$linkTotalInBrackets=$array[0];
$array=explode('|',$array[0]);
$linkInMessage=$array[0];
$message['text']=str_replace('<http'.$linkTotalInBrackets.'>','<a href="http'.$linkInMessage.'">http'.$linkInMessage.'</a>',$message['text']);
}
}
// change @levelsio has joined the channel into
// @levelsio\n has joined #channel
if(stripos($message['text'],'has joined the channel')!==false) {
$message['text']=str_replace('the channel','#'.$channelName,$message['text']);
$message['text']=str_replace('@'.$usersById[$message['user']]['name'].' ','',$message['text']);
}
$array=explode('.',$message['ts']);
$time=$array[0];
$message['text']=utf8_decode($message['text']);
$htmlMessage='';
$htmlMessage.='<div><img src="'.$usersById[$message['user']]['profile']['image_72'].'" /><div class="message"><div class="username">'.$usersById[$message['user']]['name'].'</div><div class="time">'.date('Y-m-d H:i',$message['ts']).'</div><div class="msg">'.$message['text']."</div></div></div><br/>\n";
$htmlMessages.=$htmlMessage;
}
$htmlMessages.='</div></body></html>';
file_put_contents($channelHtmlFile,$htmlMessages);
$mostRecentChannelTimestamps[$channelName]=$mostRecentChannelTimestamp;
echo "\n";
}
asort($mostRecentChannelTimestamps);
$mostRecentChannelTimestamps=array_reverse($mostRecentChannelTimestamps);
// </generate html from channel logs>
// <make index html>
$html='<html><body><style>'.$stylesheet.'</style><div class="messages">';
foreach($mostRecentChannelTimestamps as $channel => $timestamp) {
$html.='<a href="./'.$channel.'.html">#'.$channel.'</a> '.date('Y-m-d H:i',$timestamp).'<br/>'."\n";
}
$html.='</div></body></html>';
file_put_contents($htmlDir.'/index.html',$html);
// </make index html>
?>

Awesome.

I am not sure how to run this script. Can anyone give me a quick nudge? I am not a programmer and I don't know how to get the result working.

I did the following,

  1. installed PHP to my C:\PHP folder
  2. created a new slack2html.php file, copied everything from the code above into this .php file
  3. created a batch file named "start.bat" that has the following commands,
    @echo off
    C:\PHP\php.exe "D:\Slack export Aug 28 2015\slack2html.php"
    PAUSE

After I run the batch file, all I get is the command line screen filled with code on display, but nothing else happens :-/

I'm no programmer but I really need to backup my slack history. I'd really appreciate if someone told me what should I do here.

good work
Also try https://github.com/trototype/Slack-Api-Helpers for private groups

kics82 commented Dec 19, 2015

I give up :( i tired. I installed php, tried to run it with php. googled and googled and ended up even installing Apache. Need a dummies guide to this.

@levelsio

Whenever I try using this I get an error that VCRUNTIME140.dll is not installed on my computer. Online, it says that I need to install the correct .NET framework from Microsoft (specifically here: http://stackoverflow.com/questions/30811668/php7-missing-vcruntime140-dll). What did you use to make this?

targi commented Jan 10, 2016

@ everyone who has problems with Unicode (? instead of any non-ASCII character), please check out my fork. Disclaimer: I'm no PHP expert, so my fix can probably be improved, but it worked for me.

@levelsio: Maybe you can review my fix and incorporate it somehow into your gist, as I feel the Unicode issue is an important one.

donis88 commented Jan 19, 2016

@targi Thank you!!

My PHP skills are weak - any way to get this to grab attachment text (like for pinned items) and spit that out too instead of just "user has pinned an item"? I would be happy with just a point in the right direction to try and tinker with it.

Ninja Edit - got something working: $attachments = $message['attachments'][0]['text'];

alxpck commented Feb 5, 2016

This is great! Thank you @levelsio

+1 for changing <? to <?php on line one of the file, and +1 to @F1rstStep for reminding me I needed to rename gistfile1.txt to slack2html.php

felagsw commented Feb 12, 2016

Thanks @levelsio for this code !

I forked and modified it to add an optional parameter to have result in chronological order (my default preferred view) or in reverse chronological order (which is your default).

hfaran commented Feb 27, 2016

Hey folks, I decided to take this a little further and wrote https://github.com/hfaran/slack-export-viewer with an expanded feature set including:

  • An installable application
  • Automated archive extraction and retention
  • A Slack-like sidebar that lets you switch channels easily
  • Much more "sophisticated" rendering of messages
  • A Flask server which lets you serve the archive contents as opposed to a PHP script which does static file generation

Check it out if you'd like :)

Thanks @levelsio for creating this. I just wanted to let everyone I've forked and modified this script to allow it to also work for Slack history exported using https://github.com/hisabimbola/slack-history-export - a command line module to allow you to download your Slack history.

When you export all your history from Slack, it does not include history from private groups/channels and DMs. The slack-history-export tool provides a way to export history from these private groups/channels and DMs, but the original version of this script was not compatible with the exported JSON files from the slack-history-export tool. This fork updates the script to make it compatible so that you can combine both your exported public channel history from Slack, and your private group/channel and DM history from the slack-history-export tool and convert all of this to HTML.

See the "Changes in this fork" section for instructions on how to use the fork.

I would suggest replacing <? by <?php

lukcha commented Jun 29, 2016

Does anyone else have issues with punctuation in the exported HTML appearing as question marks instead of the correct punctuation? It's tedious to manually search and replace.

Is anyone else having issues with usernames not displaying properly for messages in a given channel?

maanku commented Jul 13, 2016

@hfaran well done! Simple and sweet.

Nice job

Thanks for the great idea, however it would be nice, if a little bit more introduction is provided to use this script for people without php knowledge. There is one other problem that this tool assumes that the input file is standard slack zip export. Normal users do not have the possibility to download slack archives and hence this script is only restricted to admins.

Actually, I found several other scripts but each with it's own limitation:

  1. https://gist.github.com/Chandler/fb7a070f52883849de35 - This is the one I used and gives a dump of channel including private channels and messages. However unable to convert to suitable html format
  2. https://gist.github.com/levelsio/122907e95956602e5c09- Requires php knowledge also assumes a standard slack zip export
  3. https://github.com/hfaran/slack-export-viewer - Probably provides the desired functionality, but also assumes standard slack zip export
  4. https://github.com/humor4fun/slack-backup/blob/master/slack-backup.sh - L
    functionality, but also assumes standard slack zip export
  5. https://github.com/humor4fun/slack-backup/blob/master/slack-backup.sh - Linux based
  6. https://github.com/joefitzgerald/slack-dump - Unsure about the programming language used and how the tool would work and how to install
  7. https://www.npmjs.com/package/slack-history-export - Assumes that one have a working knowledge of npm. It took a while to actually install npm first and then install this tool. But after installation I'm not sure how to actually run this tool?
  8. https://gist.github.com/jordanmkoncz/0ce0ce11a3359209f48949eefee945ce - This tool requires import from slack-history-export tool, which I find unable to run (See #6)

Any help guys.. some newbie instructions would be great

snikch commented Aug 25, 2016

@lukcha The reason for the incorrect characters is due to the line $message['text']=utf8_decode($message['text']);. This can be removed if you want your output files encoded as UTF-8 (which most people will want to do). If you open the files directly after this, you may notice other funny characters showing, however this is because the browser isn't displaying in UTF-8. You can force it to UTF-8 encoding in chrome from the View > Encoding menu.

image

If you're taking the html and pasting it into a website then if that website itself uses utf-8 encoding, for example uxmastery.com, then you won't have a problem.

Alternatively anyone using this script and wanting the files to work with UTF-8, you can change the line $htmlMessages='<html><body><style>'.$stylesheet.'</style><div class="messages">'; to $htmlMessages='<html><head><meta charset="UTF-8"></head><body><style>'.$stylesheet.'</style><div class="messages">';. The page will now correctly render the UTF-8 content.

Working on the Fedora24, the instruction is as follow:

  1. install php: sudo dnf install php
  2. install php-mbstring: sudo dnf install php-mbstring
  3. install php-xml: sudo dnf install php-xml
  4. modify this file first line <? to <?php
  5. extract the your-team-name.zip to folder /your-team-name
  6. copy the the modified file to this folder
  7. run php slack2html.php
  8. you will find a new folder slack2html in the parent directory of the /your-team-name
  9. open the html in the /html folder

Frost212 commented Sep 3, 2016

Is it possible to use this to instead covert the json to Plain text files? (I.e, merge them into the single json file as in part 1 of the script, then parse all of the user/channel ID's to their actual names, and into a plain text file so it can be logged and searched in a database?) Instead of making them into Html images?

@levelsio this is superb! And it worked like a charm. Thanks a lot!

I have a question! How can I sync all slack history in real time using slack's API method in stead of downloading its backups manually every time?

Is there a way to export the chats in full "Slack view" (i.e. pasted link unfurls, Giphys, pasted images, etc.)?

bjt1t2t3 commented Nov 8, 2016

Found this through a Google search and would like to be able to use this for our Slack team. I've downloaded the Slack archive, but am not a programmer and really do not understand how to proceed. I have no idea how to run a script inside a directory, as the instructions state.

Are there any step-by-step instructions available on how to use this for someone without php or programming skills?

Zseselja commented Nov 17, 2016 edited

Thanks mate! Works great!

elina-codes commented Dec 18, 2016 edited

@natsandman-rav I'm having the same issue. Did you find a solution?

humor4fun commented Jan 25, 2017 edited

@github3332 My slack-backup.sh script uses a derivative work of this script. Currently there exists another issue though, where line 53 of this php tool continues to fail.

root@kali:~/Desktop# php slack-json-2-html.php 
PHP Fatal error:  Uncaught Error: Call to undefined function mb_internal_encoding() in /root/Desktop/slack-json-2-html.php:53
Stack trace:
#0 {main}
  thrown in /root/Desktop/slack-json-2-html.php on line 53

Can be fixed by installing the php-mbstring package for your os.

Usernames aren't pulled in to the html.

0600Zulu commented Mar 4, 2017

Is there a way to add a feature that includes threaded messages? This tool works fine even since the threaded message update, but it sorts threaded messages by time in the whole channel vs. with the original message.

maks-ua commented Mar 14, 2017

Does that work for Windows?

Great post and works like a charm. Well done Sir! 👍

I had a memory problem when running is on my mac. The error was:
Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 32 bytes) in /Users/c2349192/workspace/slack-archives/Team Awesome Slack export Mar 20 2017/slack2html.php on line 186

I increased the memory limit on line 50 and it completed with no problems.

ini_set('memory_limit;, '2048M'); 

dorseyca commented Sep 1, 2017

For windows:,

  1. Install Wamp Server (http://www.wampserver.com/en/)
  2. In the install directory, find the location of php.exe. You'll need reference this path later. (i.e "C:\Wamp64\bin\php\php7.0.0\php.exe" )
  3. Create a folder on your desktop called 'Slack'. Download the Slack message history zip. Extract to 'Slack' folder. You'll need to reference this path later. (i.e "C:\Users\YourName\Desktop\Slack\Slack_Project_Name Slack export Sep 1 2017"
  4. Download the file from this repository (gistfile1.txt) and store it in the same folder as Step 3.
  5. Rename gistFile1.txt to GenerateSlackReport.php
  6. Open the command prompt, (Start->Run->Cmd) (You may need to find this program and right click to Run as Administrator)
  7. Change directory to the location of Step 3. (ie. "cd C:/Users/YourName/Desktop/Slack/Slack_Project_Name Slack export Sep 1 2017"
  8. In the command prompt, execute this command, replacing your php.exe location with your own: "C:\Wamp64\bin\php\php7.0.0\php.exe" GenerateReport.php

Reports are generated now. Hope this helps.

mario6097 commented Sep 9, 2017 edited

Hi

As far as I see, this script does not create code to see pictures on the local page, it renders only a link to the slack url of the picture, doesn't it?
Indeed, it would be nice to have it be able to download pictures in a local folder.
Any chance to see a improved code (which is indeed very very useful)
Thanks Cheers
mario

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment