Viber: получить history в текстовом виде и скрипт бекапа

Автор: | 10/07/2017
 

Как и SkypeLinux-клиент Viber хранит данные в локальной SQLite базе:

$ ls -l ~/.ViberPC/38096***26/ | grep db
-rw-r--r-- 1 setevoy setevoy   38912 Jun 30 15:30 data.db
-rw-r--r-- 1 setevoy setevoy   32768 Jul 10 10:38 data.db-shm
-rw-r--r-- 1 setevoy setevoy 1048032 Jul 10 10:38 data.db-wal
-rw-r--r-- 1 setevoy setevoy 5072896 Jul 10 09:59 viber.db
-rw-r--r-- 1 setevoy setevoy   32768 Jul 10 10:15 viber.db-shm
-rw-r--r-- 1 setevoy setevoy 2329736 Jul 10 10:15 viber.db-wal

История чатов лежит в базе viber.db.

Задача – бекапить по крону чат с одним контактом, и записывать его в текстовый файл.

Содержание

SQLite

Сначала – проверяем вручную, потом добавим скрипт.

Подключаемся к базе:

[simterm]

$ cd ~/.ViberPC/38096***26
$ sqlite3 viber.db
SQLite version 3.19.3 2017-06-08 14:26:16
Enter ".help" for usage hints.
sqlite>

[/simterm]

Таблицы тут:

[simterm]

sqlite> .tables
Calls           ChatsMetaData   Events          RecycleBin    
ChatInfo        Contact         EventsMetaData  Settings      
ChatRecovery    DownloadFile    LikeRelation    UploadFile    
ChatRelation    EventInfo       Messages        Versions

[/simterm]

Нас интересует eventinfo, получаем её описание:

[simterm]

sqlite> pragma table_info(eventinfo);
0|EventID|integer|0||0
...
33|EncryptedNumber|TEXT|0||0
34|Number|TEXT|0||0

[/simterm]

И Contact:

[simterm]

sqlite> pragma table_info(contact);
0|ContactID|integer|0||1
1|Name|TEXT|0||0
2|ABContact|smallint (0,1)|1|0|0
3|SortName|TEXT|0||0
4|Number|TEXT|0||0
5|ViberContact|smallint (0,1)|1|0|0
6|ClientName|TEXT|0||0
7|DownloadID|TEXT|0||0
8|EncryptedNumber|TEXT|0||0
9|MID|TEXT|0||0
10|VID|TEXT|0||0
11|ContactFlags|long|0|0|0

[/simterm]

Находим пользователя и ID:

[simterm]

sqlite> select name, mid from contact where name='Имя';
Имя|O6ZfYO7jST4=

[/simterm]

Теперь, используя этот ID – находим все сообщения пользователя:

[simterm]

sqlite> select datetime(timestamp, 'unixepoch'), chattoken, body from eventinfo where chattoken like "O6ZfYO7jST4=%" order by timestamp;

[/simterm]

Либо выводим только последние 2 записи:

[simterm]

sqlite> select datetime(timestamp, 'unixepoch'), chattoken, body from eventinfo where chattoken like "O6ZfYO7jST4=%" order by timestamp desc limit 2;
2017-07-10 07:15:46|O6ZfYO7jST4=1494955353356|Согласна
2017-07-10 07:15:28|O6ZfYO7jST4=1494955353356|Господи, ненавижу разговорчивых таксистов... 
Не умолкает, сцуко!

[/simterm]

Либо сообщения только за сегодня:

[simterm]

sqlite> select datetime(timestamp, 'unixepoch'), chattoken, body from eventinfo where chattoken like "O6ZfYO7jST4=%" and datetime(timestamp, 'unixepoch') like "2017-07-10%" order by timestamp;
2017-07-10 06:07:37|O6ZfYO7jST4=1494955353356|
2017-07-10 06:16:06|O6ZfYO7jST4=1494955353356|
2017-07-10 06:16:10|O6ZfYO7jST4=1494955353356|утречка
2017-07-10 07:15:28|O6ZfYO7jST4=1494955353356|Господи, ненавижу разговорчивых таксистов... 
Не умолкает, сцуко!
2017-07-10 07:15:46|O6ZfYO7jST4=1494955353356|Согласна

[/simterm]

Python – скрипт бекапа

И последним шагом – небольшой python-скрипт для бекапа чатов с этим контактом:

 

#!/usr/bin/env python

import os
import sys
import sqlite3
import datetime

# phone number to find path
# i.e. /home/setevoy/.ViberPC/380968889900
if len(sys.argv) < 2:
    print('ERROR: specify your cell num as first argument.')
    exit(1)

# some globals
db_name = 'viber.db'
dbpath = '/home/setevoy/.ViberPC/'
contact_name = "Имя"
back_path = '/home/setevoy/Backups/ViberChats'


def get_mid():

    """Get contact's MID"""

    cur = conn.cursor()
    mid = cur.execute("""select name, mid from contact where name='{}';""".format(contact_name))

    for i in mid:
        return(i[1])


def get_todays_history(mid):

    """Select all messages for today from Contact's MID"""

    cur = conn.cursor()
    history_today = cur.execute("""select datetime(timestamp, 'unixepoch'), chattoken, body \
                                   from eventinfo where chattoken like '{mid}%' and datetime(timestamp, 'unixepoch') like "{day}%" \
                                   order by timestamp""".format(mid=mid, day=today));

    return history_today


def save_history():

    """Save all todays messages to /home/setevoy/Backups/ViberChats/Name_datetime"""

    mid = get_mid()
    back_file = contact_name + "_" +  today

    if not os.path.isdir(back_path):
        print('WARNING: o {} directory found, creating.').format(back_path)
        os.mkdir(back_path)
    else:
        print("OK: {} found.".format(back_path))

    os.chdir(back_path)
    with open(back_file, 'w') as bf:
        for mes in get_todays_history(mid):
            data = "{}\n".format(mes)
            bf.write(data)


if __name__ == "__main__":

    my_num = sys.argv[1]

    # Create database connection, 
    conn = sqlite3.connect(os.path.join(dbpath, my_num, db_name))

    # 2017_07_10
    today = datetime.datetime.now().strftime('%Y_%m_%d')

    save_history()

Скрипт можно взять тут>>> (с небольшим изменением – Имя контакта передаётся вторым аргументом).

Его выполнение:

[simterm]

$ ./viber_backup.py 38096***6
OK: /home/setevoy/Backups/ViberChats found.

[/simterm]

Проверяем:

[simterm]

$ ls -l ../Backups/ViberChats/
total 4
-rw-r--r-- 1 setevoy setevoy 836 Jul 10 13:34 Имя_2017_07_1

[/simterm]

Содержимое:

[simterm]

$ cat ../Backups/ViberChats/Имя_2017_07_10 
('2017-07-10 06:07:37', 'O6ZfYO7jST4=1494955353356', None)
('2017-07-10 06:16:06', 'O6ZfYO7jST4=1494955353356', None)
('2017-07-10 06:16:10', 'O6ZfYO7jST4=1494955353356', 'утречка')
('2017-07-10 07:15:28', 'O6ZfYO7jST4=1494955353356', 'Господи, ненавижу разговорчивых таксистов... \nНе умолкает, сцуко!')
('2017-07-10 07:15:46', 'O6ZfYO7jST4=1494955353356', 'Согласна')

[/simterm]

Последним шагом – добавляем задачу в крон:

[simterm]

$ crontab -e

[/simterm]

0 0 * * * /home/setevoy/Work/RTFM/rtfm/14657/viber_backup.py 38096*****26 Имя

Готово.

Каталог ~/Backups у меня бекапится в AWS S3 с помощью другой утилиты – myBackup.

Из недостатков – смайлики в текстовый файл сохранить проблематично, поэтому они будут None.