chatty-import-sms-mms/import_history_mms_sms
Gilles Filippini 2599de93d9 Fix typo
2022-03-22 22:10:32 +01:00

252 lines
9 KiB
Bash
Executable file

#!/bin/bash
set -euo pipefail
database="$1"
history_db="$HOME/.purple/chatty/db/chatty-history.db"
CHATTY_ID_PHONE_VALUE=1
MMS_SMS_USERID=1
PROTOCOL_MMS_SMS=1
THREAD_DIRECT_CHAT=0
THREAD_GROUP_CHAT=1
THREAD_VISIBILITY_VISIBLE=0
CHATTY_DIRECTION_IN=1
CHATTY_DIRECTION_OUT=2
MESSAGE_TYPE_IMAGE=9
MESSAGE_TYPE_TEXT=1
MESSAGE_TYPE_MMS=12
MESSAGE_STATUS_DELIVERED=4
MESSAGE_STATUS_DELIVERY_FAILED=7
MESSAGE_STATUS_READ=5
CHATTY_FILE_DOWNLOADED=1
function join_by { local IFS="$1"; shift; echo "$@"; }
# This must be adapted after your country code
country_code=+33
country_regex='^0([0-9]{9})$'
function normalize_address () {
address="$1"
if [[ "$address" =~ $country_regex ]]; then
address="$country_code${BASH_REMATCH[1]}"
fi
echo "$address"
}
function add_user () {
address="$1"
alias="$2"
value_address="'$address'"
value_alias="$([ -z "$alias" ] && echo null || echo "'$alias'")"
userid="$(sqlite3 "$history_db" "insert or ignore into users(username, type, alias) VALUES (
$value_address,
$CHATTY_ID_PHONE_VALUE,
$value_alias)
ON CONFLICT(username,type)
DO UPDATE SET alias=coalesce($value_alias,alias);
select id from users where username=$value_address and type=$CHATTY_ID_PHONE_VALUE;")"
echo "$userid"
}
declare -A aliases
# Import SMS
while read msgdata; do
echo "$msgdata"
IFS='|' read -a msgdata_array <<<"$msgdata"
msgid="${msgdata_array[0]}"
address="$(normalize_address "${msgdata_array[1]}")"
display_name="${msgdata_array[2]}"
date="${msgdata_array[3]}"
direction="${msgdata_array[4]}"
msg_status="${msgdata_array[5]}"
aliases["${address}"]="$display_name"
if [ "$direction" = "$CHATTY_DIRECTION_OUT" ]; then
if [ "$msg_status" = 0 ]; then
msg_status="$MESSAGE_STATUS_DELIVERED"
else
msg_status="$MESSAGE_STATUS_DELIVERY_FAILED"
fi
else
msg_status="$MESSAGE_STATUS_READ"
fi
sender_id="$(add_user "$address" "${aliases["$address"]}")"
thread_name="'$address'"
thread_alias="'${aliases["$address"]}'"
if [ "$thread_alias" = "''" ]; then
thread_alias=null
fi
local_user_accountid="$(sqlite3 "$history_db" "SELECT id from accounts where user_id=$MMS_SMS_USERID and protocol=$PROTOCOL_MMS_SMS;")"
threadid="$(sqlite3 "$history_db" "INSERT INTO threads(name,alias,account_id,type,visibility) VALUES (
$thread_name,
$thread_alias,
$local_user_accountid,
$THREAD_DIRECT_CHAT,
$THREAD_VISIBILITY_VISIBLE)
ON CONFLICT(name,account_id,type)
DO UPDATE SET alias=$thread_alias, visibility=$THREAD_VISIBILITY_VISIBLE;
select id from threads where name=$thread_name and account_id=$local_user_accountid and type=$THREAD_DIRECT_CHAT;")"
sqlite3 "$history_db" "INSERT OR IGNORE INTO thread_members(thread_id, user_id) VALUES (
$threadid,
$sender_id);"
# Retreive text message
body="$(sqlite3 "$database" "SELECT body FROM sms where _id=$msgid;" | sed "s/'/''/g")"
body="'${body:-}'"
body_type="$MESSAGE_TYPE_TEXT"
# Create message record
uid="'$(sqlite3 "$history_db" "select substr(u,1,8)||'-'||substr(u,9,4)||'-4'||substr(u,13,3)||'-'||v||substr(u,17,3)||'-'||substr(u,21,12)
from (select lower(hex(randomblob(16))) as u, substr('89ab',abs(random()) % 4 + 1, 1) as v)")'"
messageid="$(sqlite3 "$history_db" "INSERT INTO messages(uid,thread_id,sender_id,body,body_type,direction,time,preview_id,status) VALUES(
$uid,
$threadid,
$sender_id,
$body,
$body_type,
$(( -(direction*2-3) )),
$date,
null,
$msg_status)
ON CONFLICT (uid,thread_id,body,time)
DO UPDATE SET status=$msg_status;
select id from messages where uid=$uid;")"
done <<<"$(sqlite3 "$database" "select _id, address, display_name, date/1000, type, status from sms order by cast(_id as integer);")"
# import MMS
while read msgdata; do
echo "$msgdata"
IFS='|' read -a msgdata_array <<<"$msgdata"
msgid="${msgdata_array[0]}"
uid="'${msgdata_array[1]}'"
date="${msgdata_array[2]}"
subject="${msgdata_array[3]:-}"
if [ -n "$subject" ]; then
subject="'$subject'"
else
subject=null
fi
IFS='|' read -a sender <<<$(sqlite3 "$database" "select address, display_name from sender where msg_id='$msgid';")
address="$(normalize_address "${sender[0]}")"
display_name="${sender[1]:-}"
users=()
if [ -z "$address" ]; then
direction=$CHATTY_DIRECTION_OUT
sender_id=null
msg_status="$MESSAGE_STATUS_DELIVERED"
while read line; do
IFS='|' read -a recipient <<<"$line"
users+=("$(normalize_address "${recipient[0]}")")
aliases["${recipient[0]}"]="${recipient[1]:-}"
done <<<$(sqlite3 "$database" "select address, display_name from recipient where msg_id='$msgid';")
else
# We don't collect recipient addresses for inbound messages
# They have little value, especially when you receive MMS sent to a whole address book
direction=$CHATTY_DIRECTION_IN
msg_status="$MESSAGE_STATUS_READ"
users=("$address")
aliases["$address"]="$display_name"
fi
thread_aliases=()
thread_members=()
for user in "${users[@]}"; do
thread_members+=("$(add_user "$user" "${aliases["$user"]:-}")")
thread_aliases+=("${aliases["$user"]:-$user}")
done
sender_id="${sender_id:-${thread_members[0]}}"
thread_name="'$(join_by "," "${users[@]}")'"
thread_alias="'$(join_by "," "${thread_aliases[@]}")'"
if [ "$thread_alias" = "$thread_name" ]; then
thread_alias=null
fi
local_user_accountid="$(sqlite3 "$history_db" "SELECT id from accounts where user_id=$MMS_SMS_USERID and protocol=$PROTOCOL_MMS_SMS;")"
if [ "${#users[@]}" -gt 1 ]; then
thread_type="$THREAD_GROUP_CHAT"
else
thread_type="$THREAD_DIRECT_CHAT"
fi
threadid="$(sqlite3 "$history_db" "INSERT INTO threads(name,alias,account_id,type,visibility) VALUES (
$thread_name,
$thread_alias,
$local_user_accountid,
$thread_type,
$THREAD_VISIBILITY_VISIBLE)
ON CONFLICT(name,account_id,type)
DO UPDATE SET alias=$thread_alias, visibility=$THREAD_VISIBILITY_VISIBLE;
select id from threads where name=$thread_name and account_id=$local_user_accountid and type=$thread_type;")"
for member in "${thread_members[@]}"; do
sqlite3 "$history_db" "INSERT OR IGNORE INTO thread_members(thread_id, user_id) VALUES (
$threadid,
$member);"
done
# Concat plain text parts as the body
body="$(sqlite3 "$database" "SELECT text FROM part where mid=$msgid and ct='text/plain' order by cast(_id as integer);" | sed "s/'/''/g")"
body="'${body:-}'"
body_type="$MESSAGE_TYPE_MMS"
# Create message record
messageid="$(sqlite3 "$history_db" "INSERT INTO messages(uid,thread_id,sender_id,body,body_type,direction,time,preview_id,status,subject) VALUES(
$uid,
$threadid,
$sender_id,
$body,
$body_type,
$(( -(direction*2-3) )),
$date,
null,
$msg_status,
$subject)
ON CONFLICT (uid,thread_id,body,time)
DO UPDATE SET status=$msg_status;
select id from messages where uid=$uid;")"
# Process binary attachements
parent="${XDG_DATA_HOME:-$HOME/.local/share}/chatty"
attachments_info="$(sqlite3 "$database" "SELECT ct, savepath FROM part where mid=$msgid and lower(ct) not in ('application/smil', 'text/plain') order by cast(_id as integer);")"
[ -z "$attachments_info" ] || while read line; do
IFS='|' read -a attachment <<<"$line"
mime_type="${attachment[0]}"
savepath="${attachment[1]}"
name="'$(basename "$savepath")'"
url="'file://$(urlencode -m "$savepath")'"
path="'${savepath#$parent/}'"
size="$(stat --printf="%s" "$savepath")"
mime_type_id="$(sqlite3 "$history_db" "INSERT OR IGNORE INTO mime_type(name) VALUES ('$mime_type');
select id from mime_type where name='$mime_type';")"
# File record
fileid="$(sqlite3 "$history_db" "INSERT INTO files(name,url,path,mime_type_id,size,status) VALUES (
$name,
$url,
$path,
$mime_type_id,
$size,
$CHATTY_FILE_DOWNLOADED)
ON CONFLICT(url)
DO UPDATE SET path=$path, size=$size, status=$CHATTY_FILE_DOWNLOADED;
select id from files where url=$url;")"
# File_metadata
if [[ "$mime_type" =~ ^(image|video|audio)/ ]]; then
raw_metadata="$( (ffmpeg -i "$savepath" 2<&1 || true) | grep -E '(Duration|Stream.* Video):')"
if [[ "$mime_type" =~ ^(image|video)/ ]]; then
if [[ "$raw_metadata" =~ \ ([0-9]+)x([0-9]+)\ ]]; then
width="${BASH_REMATCH[1]}"
height="${BASH_REMATCH[2]}"
fi
fi
if [[ "$mime_type" =~ ^(video|audio)/ ]]; then
if [[ "$raw_metadata" =~ Duration:\ ([0-9]+):([0-9]+):([0-9]+)(\.([0-9]+))?, ]]; then
duration=$((${BASH_REMATCH[1]}*3600+${BASH_REMATCH[2]}*60+${BASH_REMATCH[2]}))
fi
fi
if [ -n "${width:-}${height:-}${duration:-}" ]; then
width="${width:-null}"
height="${height:-null}"
duration="${duration:-null}"
sqlite3 "$history_db" "INSERT OR IGNORE INTO file_metadata(file_id,width,height,duration) VALUES (
$fileid,
$width,
$height,
$duration);"
fi
fi
sqlite3 "$history_db" "INSERT OR IGNORE INTO message_files(message_id,file_id) VALUES (
$messageid,
$fileid);"
done <<<"$attachments_info"
done <<<"$(sqlite3 "$database" "select _id, m_id, date, sub from mms order by cast(mms._id as integer);")"