Skip to content

Instantly share code, notes, and snippets.

@lucabased
Created February 28, 2026 19:11
Show Gist options
  • Select an option

  • Save lucabased/0d231c317dc768c9e460c2ad4842862c to your computer and use it in GitHub Desktop.

Select an option

Save lucabased/0d231c317dc768c9e460c2ad4842862c to your computer and use it in GitHub Desktop.
Paste Threema Chat Export logs into a readable format
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Threema Export Viewer</title>
<style>
body{
margin:0;
background:#0f172a;
font-family:system-ui,-apple-system,Segoe UI,Roboto;
color:white;
display:flex;
flex-direction:column;
height:100vh;
}
header{
padding:12px 16px;
background:#111827;
font-weight:600;
}
textarea{
width:100%;
height:180px;
background:#0b1220;
color:#fff;
border:none;
padding:12px;
resize:none;
font-family:monospace;
}
button{
padding:10px 14px;
border:none;
background:#2563eb;
color:white;
cursor:pointer;
}
.chat{
flex:1;
overflow:auto;
padding:20px;
display:flex;
flex-direction:column;
gap:12px;
}
.row{
display:flex;
}
.row.in{ justify-content:flex-start; }
.row.out{ justify-content:flex-end; }
.bubble{
max-width:75%;
padding:10px 14px;
border-radius:16px;
line-height:1.4;
word-break:break-word;
}
.in .bubble{
background:#1f2937;
border-top-left-radius:6px;
}
.out .bubble{
background:#1d4ed8;
border-top-right-radius:6px;
}
.meta{
font-size:11px;
opacity:.6;
margin-top:6px;
text-align:right;
}
.system{
text-align:center;
font-size:12px;
opacity:.6;
margin:10px 0;
}
.file{
margin-top:8px;
padding:8px;
border-radius:10px;
background:rgba(255,255,255,0.1);
font-size:13px;
}
</style>
</head>
<body>
<header>Threema Chat Export Viewer</header>
<textarea id="input" placeholder="Hier Threema-Export einfügen..."></textarea>
<button onclick="parseChat()">Chat anzeigen</button>
<div class="chat" id="chat"></div>
<script>
function parseChat(){
const raw = document.getElementById("input").value;
const chat = document.getElementById("chat");
chat.innerHTML = "";
const lines = raw.split("\n").filter(l=>l.trim() !== "");
const regex = /^(>>>|<<<)\s(.+?)\sum\s(.+?)\sMEZ:\s?(.*)$/;
lines.forEach(line=>{
const match = line.match(regex);
if(!match) return;
const directionSymbol = match[1];
const date = match[2];
const time = match[3];
let content = match[4];
// Systemmeldung erkennen
if(content.includes("Perfect Forward Secrecy")){
const sys = document.createElement("div");
sys.className = "system";
sys.textContent = date + " " + time + " – " + content;
chat.appendChild(sys);
return;
}
const row = document.createElement("div");
row.className = "row " + (directionSymbol === ">>>" ? "out" : "in");
const bubble = document.createElement("div");
bubble.className = "bubble";
// Datei erkennen
if(content.startsWith("Datei")){
const fileMatch = content.match(/Datei \((.*?)\)(?:, Untertitel: (.*))?/);
bubble.innerHTML = "<strong>📎 Datei</strong>";
const fileDiv = document.createElement("div");
fileDiv.className = "file";
fileDiv.textContent = fileMatch ? fileMatch[1] : "";
bubble.appendChild(fileDiv);
if(fileMatch && fileMatch[2]){
const caption = document.createElement("div");
caption.style.marginTop="6px";
caption.textContent = fileMatch[2];
bubble.appendChild(caption);
}
} else {
bubble.textContent = content;
}
const meta = document.createElement("div");
meta.className="meta";
meta.textContent = date + " " + time;
bubble.appendChild(meta);
row.appendChild(bubble);
chat.appendChild(row);
});
chat.scrollTop = chat.scrollHeight;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment