View Single Post
Posts: 986 | Thanked: 1,526 times | Joined on Jul 2010
#31
here is my util for splitting and joining the smsbackuprestore dumps.

it looks at all your exports, automatically removes dupes, splits the texts by phone number into separate files, and sorts by date sent/received.

for restoring, it joins them together taking only texts from the last 30 days from the backup.


note: for non-united-states folks, you might wanna remove this line below before using.
@messages = removeUSCountryCode @messages;


i run it on the phone, like this:

#exports to ~/MyDocs/sms-backup/#######.sms
perl n9-sms-tool.pl backup

#reads all .sms files in ~/MyDocs/sms-backup,
#removes duplicates, and splits them out by phone number,
#into separate files in ~/MyDocs/sms-backup-repo
perl n9-sms-tool.pl split

#unnecessary step: commits a git repo {local only, no pushing}
perl n9-sms-tool.pl commit

#makes a file for importing, using texts only from the last 30 days
perl n9-sms-tool.pl join


Code:
#!/usr/bin/perl
#n9-sms-tool v0.1
#Copyright 2012 Elliot Wolk
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#See the GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program.
#If not, see <http://www.gnu.org/licenses/>.
use strict;
use warnings;

my $cutoffDate = "30 days ago";

my $smsDir = "/home/user/MyDocs/sms-backup";
my $repoDir = "/home/user/MyDocs/sms-backup-repo";

sub filterMessages(\@);
sub getMessagesFromDir($);
sub getMessages($);
sub messageToString($);
sub writeMessageFile(\@$);
sub writeContactsFiles(\@$);
sub removeUSCountryCode(\@);
sub removeDupes(\@);

sub main(@){
  my $arg = shift;
  $arg = '' if not defined $arg;
  my @okArgs = qw(split join commit backup);
  my $ok = join "|", @okArgs;
  die "Usage: $0 $ok\n" if $arg !~ /^($ok)$/ or @_ > 0;

  if($arg eq 'split'){
    system "mkdir $repoDir -p";
    my @messages = (getMessagesFromDir($smsDir), getMessagesFromDir($repoDir));
    @messages = removeDupes @messages;
    system "mkdir -p $repoDir";
    system "rm $repoDir/*.sms";
    writeContactsFiles @messages, $repoDir;
  }elsif($arg eq 'backup'){
    system "mkdir $smsDir -p";
    chdir $smsDir;
    system "smsbackuprestore", "export", time . ".sms";
  }elsif($arg eq 'join'){
    my @messages = getMessagesFromDir $repoDir;
    @messages = removeDupes @messages;
    @messages = filterMessages @messages;
    writeMessageFile @messages, "$smsDir/filtered.sms";
  }elsif($arg eq 'commit'){
    chdir $repoDir;
    if(not -d '.git'){
      system "git init";
    }
    system "git add *.sms";
    system "git --no-pager diff --cached";
    system "git commit -m 'automatic commit'";
  }
}

sub filterMessages(\@){
  my $targetDate = `date --date="$cutoffDate" '+%Y-%m-%d %H:%M:%S'`;
  chomp $targetDate;

  my @messages = @{shift()};
  my @newMessages;
  for my $msg(@messages){
    my $date = $$msg[2];
    if($date gt $targetDate){
      push @newMessages, $msg;
    }
  }
  return @newMessages;
}

sub getMessagesFromDir($){
  my $dir = shift;
  my $content = '';
  for my $file(`ls $dir/*.sms`){
    $content .= `cat $file`;
  }
  return getMessages($content);
}

sub getMessages($){
  my $content = shift;
  my @messages;
  while($content =~ /^([^,]+),([^,]+),([^,]+),("(?:[^"]|"")*")\n/gm){
    my ($phone, $dir, $datetime, $msg) = ($1,$2,$3,$4);
    push @messages, [$1,$2,$3,$4];
  }
  @messages = removeUSCountryCode @messages;
  return @messages;
}

sub messageToString($){
  my ($phone, $dir, $datetime, $msg) = @{$_[0]};
  return "$phone,$dir,$datetime,$msg\n";
}

sub writeMessageFile(\@$){
  my @messages = @{shift()};
  my $file = shift;
  open FH, "> $file" or die "Could not open $file\n";
  @messages = sort {$$a[2] cmp $$b[2]} @messages;
  for my $msg(@messages){
    print FH messageToString $msg;
  }
  close FH;
}

sub writeContactsFiles(\@$){
  my @messages = @{shift()};
  my $dir = shift;

  my %byContact;
  for my $msg(@messages){
    my $phone = $$msg[0];
    $byContact{$phone} = [] if not defined $byContact{$phone};
    push @{$byContact{$phone}}, $msg;
  }

  for my $phone(keys %byContact){
    my $file = "$dir/$phone.sms";
    $file =~ s@:/org/freedesktop/Telepathy/Account/ring/tel/ring@@;
    my @messages = @{$byContact{$phone}};
    writeMessageFile @messages, $file;
  }
}

sub removeUSCountryCode(\@){
  my @messages = @{shift()};
  for my $msg(@messages){
    my $phone = $$msg[0];
    $phone =~ s/^\+?1(\d\d\d\d\d\d\d\d\d\d)$/$1/;
    $$msg[0] = $phone;
  }
  return @messages;
}

sub removeDupes(\@){
  my @messages = @{shift()};
  my %strings;
  my %onelineStrings;
  for my $msg(@messages){
    my $str = messageToString $msg;
    
    my $oneline = $str;
    $oneline =~ s/[\n\r]+//g;
    if(defined $strings{$str}){
      next;
    }elsif(defined $onelineStrings{$oneline}){
      my $prevMsg = $onelineStrings{$oneline};
      my $prevStr = messageToString $prevMsg;

      my $strLines = @{[$str =~ /([\n\r])/g]};
      my $prevStrLines = @{[$prevStr =~ /([\n\r])/g]};
      if($strLines > $prevStrLines){
        #previous one is no good, missing newlines
        delete $strings{$prevStr};
      }elsif($prevStrLines > $strLines){
        #this one is no good, missing newlines
        next;
      }else{
        print "WEIRD: newline count is the same, not skipping\n";
      }
    }
    
    $strings{$str} = $msg;
    $onelineStrings{$oneline} = $msg;
  }
  return values %strings;
}

&main(@ARGV);
__________________
~ teleshoes ~
 

The Following User Says Thank You to wolke For This Useful Post: