Skip to content

Instantly share code, notes, and snippets.

@yarrom
Created May 17, 2019 05:51
Show Gist options
  • Save yarrom/e26c8cf524ea38e08285107ec7de37b4 to your computer and use it in GitHub Desktop.
Save yarrom/e26c8cf524ea38e08285107ec7de37b4 to your computer and use it in GitHub Desktop.
Viber chat history to html converter
package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"github.com/json-iterator/go"
"os"
"path/filepath"
"strings"
)
func check(err error) {
if err != nil {
panic(err)
}
}
func skip_utf8_bom(rd *bufio.Reader) error {
aBom, err := rd.Peek(3)
if err == nil && bytes.Equal(aBom, []byte{0xEF, 0xBB, 0xBF}) {
_, err := rd.Discard(3)
//fmt.Printf("%d bytes of BOM discarded.\n", n)
return err
} else {
return err
}
}
type ViberHistory struct {
Date, Time,
Name, Phone,
Text string
}
func scan_csv(rd *bufio.Reader, wr *bufio.Writer) error {
err := skip_utf8_bom(rd)
if err != nil {
return err
}
wr.Write([]byte(fmt.Sprintf(
`<!DOCTYPE html>
<html>
<head>
<meta charset=\"utf-8\">
<title>История переписки</title>
<style>
pre {
white-space: -moz-pre-wrap; /* Mozilla, supported since 1999 */
white-space: -pre-wrap; /* Opera */
white-space: -o-pre-wrap; /* Opera */
white-space: pre-wrap; /* CSS3 - Text module (Candidate Recommendation)
http://www.w3.org/TR/css3-text/#white-space */
word-wrap: break-word; /* IE 5.5+ */
}
</style>
</head>
<body>`)))
const (
ss_Date = iota
ss_Time = iota
ss_Name = iota
ss_Phone = iota
ss_Text = iota
)
var (
aBufRead []byte = make([]byte, 1)
nRead int
ss int = ss_Date
b byte
aBuf [2048]byte
nBuf int = 0
vh ViberHistory
sDatePrev string = ""
)
for {
nRead, _ = rd.Read(aBufRead)
if nRead == 0 {
break
}
for i := 0; i < nRead; i++ {
b = aBufRead[i]
if b == ',' && ss != ss_Text { // Delimiter reached
switch ss {
case ss_Date:
vh.Date = string(aBuf[0:nBuf])
case ss_Time:
vh.Time = string(aBuf[0:nBuf])
case ss_Name:
vh.Name = string(aBuf[0:nBuf])
case ss_Phone:
vh.Phone = string(aBuf[0:nBuf])
}
ss++
nBuf = 0
} else if b == 0x0D {
aB, err := rd.Peek(1)
if err == nil && len(aB) == 1 && aB[0] == 0x0A { // End Of Line reached
if ss != ss_Text {
return errors.New("Unexpected End Of Line reached")
}
vh.Text = string(aBuf[0:nBuf])
preprocess_line(&vh)
if sDatePrev != vh.Date {
wr.Write([]byte(fmt.Sprintf("<center><h3>%s</h3></center>\n", vh.Date)))
sDatePrev = vh.Date
}
wr.Write([]byte(fmt.Sprintf("%s <b>%s</b>: <pre>%s</pre>\n", vh.Time, vh.Name, vh.Text)))
_, _ = rd.Discard(1)
ss++
nBuf = 0
}
} else {
if nBuf >= len(aBuf) {
return errors.New(fmt.Sprintf("Unexpected End Of buffer reached: %d", nBuf))
}
aBuf[nBuf] = b
nBuf++
}
if ss > ss_Text {
ss = ss_Date
}
}
}
wr.Write([]byte(fmt.Sprintf("</body></html>\n")))
return nil
}
func preprocess_line(vh *ViberHistory) {
if key := "add/"; bytes.Equal([]byte(vh.Text)[0:len(key)], []byte(key)) {
vh.Text = "<b><i>теперь в чате</i></b>"
} else if key := "add_by_link/"; bytes.Equal([]byte(vh.Text)[0:len(key)], []byte(key)) {
vh.Text = "<b><i>использовал(а) ссылку из приглашения</i></b>"
} else if key := "message_deleted/"; bytes.Equal([]byte(vh.Text)[0:len(key)], []byte(key)) {
vh.Text = "<b><i>удалил(а) сообщение</i></b>"
} else if key := "group_rename/"; bytes.Equal([]byte(vh.Text)[0:len(key)], []byte(key)) {
s := strings.Split(vh.Text, "//")
vh.Text = fmt.Sprintf("<b><i>переименовал(а) группу в \"%s\"</i></b>", s[len(s)-1])
} else if vh.Text[0] == '[' && vh.Text[len(vh.Text)-1] == ']' {
var val interface{}
err := json.Unmarshal([]byte(vh.Text), &val)
vh.Text = ""
if err == nil {
switch v := val.(type) {
case []interface{}:
for _, itemV := range v {
switch w := itemV.(type) {
case map[string]interface{}:
_type, _type_ok := w["Type"]
if _type_ok && _type == "txt" {
_action, _action_ok := w["Action"]
_text, _text_ok := w["Text"]
_, _underline_ok := w["Underline"]
if _action_ok && _text_ok && _underline_ok {
switch z := _action.(type) {
case map[string]interface{}:
if _name, _name_ok := z["name"]; _name_ok && _name == "openurl" {
if _parameters, _parameters_ok := z["parameters"]; _parameters_ok {
switch x := _parameters.(type) {
case map[string]interface{}:
if _url, _url_ok := x["url"]; _url_ok {
vh.Text += fmt.Sprintf("&nbsp;<a href=\"%v\">%v</a>", _url, _text)
}
default:
}
}
}
default:
}
}
} else if _type_ok && _type == "info" {
if _PushText, _PushText_ok := w["PushText"]; _PushText_ok {
vh.Text += fmt.Sprintf("&nbsp;%v", _PushText)
}
}
default:
}
}
default:
}
}
}
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
func main() {
var (
sNameIn, sNameOut string = "", ""
flIn, flOut *os.File
info os.FileInfo
wr *bufio.Writer
err error
)
if len(os.Args) > 1 {
sNameIn = os.Args[1]
} else {
check(errors.New("Input file name not specified"))
}
if len(os.Args) > 2 {
sNameOut = os.Args[2]
} else {
sExt := filepath.Ext(sNameIn)
sNameOut = sNameIn[0:len(sNameIn)-len(sExt)] + ".html"
}
flIn, err = os.Open(sNameIn)
check(err)
defer flIn.Close()
flOut, err = os.OpenFile(sNameOut, os.O_CREATE|os.O_WRONLY, 0644)
check(err)
defer flOut.Close()
info, err = flOut.Stat()
check(err)
if info.Size() > 0 {
err = flOut.Truncate(0)
check(err)
}
wr = bufio.NewWriter(flOut)
defer wr.Flush()
err = scan_csv(bufio.NewReader(flIn), wr)
check(err)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment