runoff 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZjY2YzY5MjRhYzFkZTU4NDlhMjQyMzk0MzgxOTE2YzBiZThjNWMxZA==
5
+ data.tar.gz: !binary |-
6
+ MDg2M2Q5NzA4ZTRhNWIzNTA2YmFjNzU0NGVhZmMwNjY5MWZjYmY4OQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ Mzc2Nzk4Mjc2ZTFkM2JkYTdhYWJhYzNhZmY4NTU2NDJlNDMzM2I0ZmQxZWY2
10
+ MzhkNWU4MmMwNWJlZThhMjkwYzljNTFlMGYwNDljNzcyOGViYTVlNzNmMmI2
11
+ YmJiNTcwMzVhZDYzNmYzNWM4NmVkOWU3ODk3NDVjZTNhZTRhNzY=
12
+ data.tar.gz: !binary |-
13
+ MjIwMTEzY2FiM2ZjMWVjNDQzNDM5ZGVlY2E0ZmE1OTc4OThkYmE4N2NmN2Ew
14
+ YjYzZmIwZjUzNjMyZjAxZDhkZTRmNmE4NmJmOTFhNTcwZWIyZTM2NGRlZDg2
15
+ MDc2NWE1YjY2YTRhYWE3NjhlNDkwNDIzMWIxMWRlMmFhNzBiMTY=
checksums.yaml.gz.sig ADDED
@@ -0,0 +1,3 @@
1
+ 8ߓ�Ϳ�ՙM���˄|�:o�ۭ�"x�#l�a������\�����
2
+ �9�5�� ���ކg]T� =�*�m�\��z/������������?,lG��3y�����56 �� ��nE*�\�OLO�e?4X\��|�
3
+ *_]� $F��(�OB�>�m">��"�����_�$2�zV�>�5Qf08����R�ޞT�@2&��B�U@2X+
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # gem's dependencies are specified in runoff.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Aigars Dzerviniks
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # runoff
2
+ ## About
3
+
4
+ A few years ago I had enough of loosing my Skype chat history every time I reinstalled the operating system, so I decided to write a small application that could export it as plain text files. The application is called [SDBR](https://github.com/arvislacis/SDBR) and it is an open source project that I do not maintain anymore. Why? I could say that I lost my interest in it, but the real reason probably is the implementation.
5
+
6
+ SDBR is written in C# using WPF, therefore it runs only on Windows. Moreover, it is a GUI application. Yeah, that's a problem, because you don't need the GUI for this kind of functionality. runoff is a commandline tool, that automates the process of exporting your chat history.
7
+
8
+ ## Install
9
+
10
+ Sorry, this gem is still in development, therefore I haven't published it to RubyGems yet, but you can build it locally and install it that way.
11
+
12
+ ## Usage
13
+
14
+ The current version only works with the default Skype location.
15
+
16
+ <pre><code>runoff all skype_username # this will save all the files in your home directory
17
+ </code></pre>
18
+
19
+ To export files to a specific directory you can use <code>--to</code> or <code>-t</code> option.
20
+
21
+ <pre><code>runoff all skype_username --to ~/skype_backup
22
+ </code></pre>
23
+
24
+ If you're confused, you can get some help.
25
+
26
+ <pre><code>runoff help all
27
+ </code></pre>
28
+
29
+ If you don't want to install the development version, clone down the repository and call the executable file directly!
30
+
31
+ <pre><code>ruby -Ilib ./bin/runoff.rb
32
+ </code></pre>
33
+
34
+ ## What else?
35
+
36
+ Things to do before runoff is ready to be published:
37
+ - Add tests for the executable file.
38
+ - Use <code>--from</code>/<code>-f</code> option to specify the location of main.db file.
39
+ - Add comments to the code.
40
+
41
+ Things to do in the future versions:
42
+ - Add additional methods to export only specific conversations.
43
+ - Append only new messages to the previously genetrated files instead of appending everything.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs.push "lib"
6
+ t.test_files = FileList['test/*_test.rb']
7
+ t.verbose = true
8
+ end
data/bin/runoff ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'runoff'
6
+
7
+ Runoff::Source.start ARGV
@@ -0,0 +1,22 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDnjCCAoagAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMRowGAYDVQQDDBFkemVy
3
+ dmluaWtzLmFpZ2FyczEXMBUGCgmSJomT8ixkARkWB291dGxvb2sxEzARBgoJkiaJ
4
+ k/IsZAEZFgNjb20wHhcNMTMwMzI1MTczODU4WhcNMTQwMzI1MTczODU4WjBKMRow
5
+ GAYDVQQDDBFkemVydmluaWtzLmFpZ2FyczEXMBUGCgmSJomT8ixkARkWB291dGxv
6
+ b2sxEzARBgoJkiaJk/IsZAEZFgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
7
+ ggEKAoIBAQC0WariaIzrjdSzAcUM4k7kUfZGk4S1eaHcby97aDutB3FNzyCuFfxU
8
+ 7PvwrGoZKAFTF52w9j+Jx9cWAOo9ZLCsfbJwN4f7kMS8zdXUbtwz//Ex6sZpoodN
9
+ dCgIC6MroQwK/vc5q1b/teWS4QB4IfpEj6RYb8QaJmZ0IGnvirfXOpVXy7NHIwfT
10
+ SsDytaBE1siP0gx4a9i5SZN5G3U2vbEna9h278/g9WlJqB7VkqoXN/syA8/UHxue
11
+ W/KYFT1WgliGIwixl+AIyQp4llpBl9H3nu7dSvndr9UHRdlkzYxFV3Fc3EurpiPM
12
+ OPSjzI/G8WxIai+UeZciDF1IsYi5tacHAgMBAAGjgY4wgYswCQYDVR0TBAIwADAL
13
+ BgNVHQ8EBAMCBLAwHQYDVR0OBBYEFHCJfKCutfHDLgf1JKjOV6uGLujLMCgGA1Ud
14
+ EQQhMB+BHWR6ZXJ2aW5pa3MuYWlnYXJzQG91dGxvb2suY29tMCgGA1UdEgQhMB+B
15
+ HWR6ZXJ2aW5pa3MuYWlnYXJzQG91dGxvb2suY29tMA0GCSqGSIb3DQEBBQUAA4IB
16
+ AQCntpOQvox8TVSwwXy1qC2uZvvP55zZ6hxyzzbHdRqwAAOnssfAVAZHiXTfLdQO
17
+ 87gyLbO5bVQkGNkyZzdVMbc59a4H6dKSr6AmcCtb/tf+ZWjGU7oAvRFnYRwhirFL
18
+ Y8ML3UdD2TgDDAXAOHKUlsq6tKlU1NEsLv1BRBNkLPo25XyESTzJTV3c8fIn6iKr
19
+ Z/pNzxAd9R7oS8LCfXaLhPxo9vErUVKwt64zdaKbACkjjZnCZoeahlOwgsVsyl0o
20
+ 44ixLmq4u15kH+PTktqOXZurNEZgS2TRXnFDoSZdmOr1236e/R50ssWeGGHQJ1Wl
21
+ 7fYlViA8Ocfwhgrh72sNfTzS
22
+ -----END CERTIFICATE-----
@@ -0,0 +1,161 @@
1
+ require 'sequel'
2
+ require 'set'
3
+
4
+ module Runoff
5
+
6
+ # Public: Provides interaction with a Skype database file.
7
+ #
8
+ # Examples
9
+ #
10
+ # composition = Composition.new 'path/to/the/main.db'
11
+ # exported_files_count = composition.export 'export/folder'
12
+ class Composition
13
+
14
+ # Public: Returns a Set object of all the names of the exported files.
15
+ attr_reader :exported_filenames
16
+
17
+ # Public: Initialize a Composition object.
18
+ #
19
+ # main_db_file_path - A String with the path to the Skype database file.
20
+ #
21
+ # Raises IOError if the file cannot be found
22
+ def initialize(main_db_file_path)
23
+ unless File.exists? main_db_file_path
24
+ raise IOError, "File doesn't exist"
25
+ end
26
+
27
+ skype_database = Sequel.connect "sqlite://#{main_db_file_path}"
28
+ @messages = skype_database.from :Messages
29
+ @exported_filenames = Set.new
30
+ end
31
+
32
+ # Public: Exports Skype chat history to text files.
33
+ #
34
+ # destination_path - A String with folder path, where to put exported files.
35
+ #
36
+ # Examples
37
+ #
38
+ # export '~/skype_backup'
39
+ # # => 8
40
+ #
41
+ # Returns the count of the exported files.
42
+ def export(destination_path)
43
+ chat_records = @messages.select(:chatname, :timestamp, :from_dispname, :body_xml)
44
+
45
+ chat_records.each { |record| save_to_file record, destination_path }
46
+
47
+ @exported_filenames.count
48
+ end
49
+
50
+ # Public: Gets parsed chatnames together with partly parsed chatnames.
51
+ #
52
+ # Examples
53
+ #
54
+ # get_chatnames
55
+ # # => [['something-more', 'somethindg-else'], ['#something/$more;6521032', '#something/$else;8971263']]
56
+ #
57
+ # Returns two Array objects containing parsed chatnames and partly parsed chatnames.
58
+ def get_chatnames
59
+ chatnames = @messages.select(:chatname)
60
+ raw_chatnames = chatnames.map { |record| partly_parse_chatname record[:chatname] }.uniq
61
+ clean_chatnames = raw_chatnames.map { |chatname| parse_chatname chatname }
62
+
63
+ return clean_chatnames, raw_chatnames
64
+ end
65
+
66
+ # Public: Exports Skype chat history to text files for specified chats.
67
+ #
68
+ # chatnames - Array of chatnames, which will be exported
69
+ # destination_path - A String with folder path, where to put exported files.
70
+ #
71
+ # Examples
72
+ #
73
+ # export_chats ['#something/$more;', '#something/$else;'], '~/skype_backup'
74
+ # # => 2
75
+ #
76
+ # Returns the count of the exported files.
77
+ def export_chats(chatnames, destination_path)
78
+ pattern_chatnames = chatnames.map { |name| "#{name}%" }
79
+ chat_records = @messages.where(Sequel.like(:chatname, *pattern_chatnames))
80
+
81
+ chat_records.each { |record| save_to_file record, destination_path }
82
+
83
+ @exported_filenames.count
84
+ end
85
+
86
+ private
87
+
88
+ # Internal: Saves a single chat message to a file.
89
+ #
90
+ # chat_record - a Hash containing data about a single chat message.
91
+ # output_directory - A String with the path to the directory, wher the file will be saved.
92
+ #
93
+ # Examples
94
+ #
95
+ # save_to_file record, '~/skype_backup'
96
+ def save_to_file(chat_record, output_directory)
97
+ datetime = Time.at chat_record[:timestamp]
98
+ output_record = "[#{datetime.to_date}] #{chat_record[:from_dispname]}: #{chat_record[:body_xml]}"
99
+ filename = "#{output_directory}/#{parse_chatname chat_record[:chatname]}.txt"
100
+
101
+ File.open(filename, 'a') do |file|
102
+ file.puts output_record
103
+ end
104
+
105
+ @exported_filenames << filename
106
+ end
107
+
108
+ # Internal: Converts chatname from database to a valid file name.
109
+ #
110
+ # raw_chatname - A String with a chatname read from the database.
111
+ #
112
+ # Examples
113
+ #
114
+ # parse_chatname '#someone/$someone_else;521357125362'
115
+ # # => someone-someone_else.txt
116
+ #
117
+ # Returns a String with a valid file name.
118
+ def parse_chatname(raw_chatname)
119
+ match = raw_chatname.match(/#(.+)\/\$(.+);|#(.+)\/\$/)
120
+ first_part, second_part, third_part = match.captures
121
+ chatname = "#{first_part}-#{second_part}-#{third_part}"
122
+
123
+ trim_dashes chatname
124
+ end
125
+
126
+ # Internal: Removes extra characters from the end of a chatname.
127
+ #
128
+ # raw_chatname - A String with a chatname read from the database
129
+ #
130
+ # Examples
131
+ #
132
+ # partly_parse_chatname '#someone/$someone_else;521357125362'
133
+ # # => #someone/$someone_else;
134
+ #
135
+ # Returns a String with a chatname without extra characters at the end.
136
+ def partly_parse_chatname(raw_chatname)
137
+ match = raw_chatname.match(/(#.+\/\$.+;)|(#.+\/\$)/)
138
+ first_group, second_group = match.captures
139
+
140
+ first_group || second_group
141
+ end
142
+
143
+ # Internal: Removes unnecessary dashes from the begining and the end of the string.
144
+ #
145
+ # string - A String possibly containing dashes at the beggining or the end
146
+ #
147
+ # Examples
148
+ #
149
+ # str = '--example-'
150
+ # trim_dashes str
151
+ # # => example
152
+ #
153
+ # Returns a string without leading and ending dashes.
154
+ def trim_dashes(string)
155
+ string.gsub! /^-+/, ''
156
+ string.gsub! /-+$/, ''
157
+
158
+ string
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,58 @@
1
+ module Runoff
2
+
3
+ # Public: Contains class methods for finding out the appropriate file paths.
4
+ #
5
+ # Examples
6
+ #
7
+ # Location::default_skype_data_location skype_username
8
+ # # => /home/user/.Skype/skype_username/main.db
9
+ class Location
10
+
11
+ # Public: Composes the default Skype database location depending on the operating system.
12
+ #
13
+ # skype_username - A String that contains a username of the Skype account,
14
+ # which database we want to access.
15
+ #
16
+ # Examples
17
+ #
18
+ # On Linux:
19
+ # default_skype_data_location skype_username
20
+ # # => /home/user/.Skype/skype_username/mai.db
21
+ #
22
+ # On Windows:
23
+ # default_skype_data_location skype_username
24
+ # # => C:\Users\user\AppData\Roaming\Skype\skype_username\main.db
25
+ #
26
+ # Returns a String that contains the path to the Skype database file.
27
+ def self.default_skype_data_location(skype_username)
28
+ if RbConfig::CONFIG['host_os'] =~ /mingw/
29
+ "#{ENV['APPDATA']}\\Skype\\#{skype_username}\\main.db"
30
+ elsif RbConfig::CONFIG['host_os'] =~ /linux/
31
+ "#{ENV['HOME']}/.Skype/#{skype_username}/main.db"
32
+ else
33
+ "~/Library/Application Support/Skype/#{skype_username}/main.db"
34
+ end
35
+ end
36
+
37
+ # Public: Clarifies the path to the user's home directory depending on the operating system
38
+ #
39
+ # Examples
40
+ #
41
+ # On Linux:
42
+ # home_path
43
+ # # => /home/user
44
+ #
45
+ # On Windows:
46
+ # home_path
47
+ # # => C:\Users\user
48
+ #
49
+ # Returns a String that contains the path to the user's home directory.
50
+ def self.home_path
51
+ if RbConfig::CONFIG['host_os'] =~ /mingw/
52
+ ENV['USERPROFILE']
53
+ else
54
+ ENV['HOME']
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,149 @@
1
+ require 'thor'
2
+
3
+ # Public: Provides the functionality to back up Skype chat history.
4
+ #
5
+ # Examples
6
+ #
7
+ # runoff all skype_username
8
+ # # => Finished: 8 files were exported
9
+ module Runoff
10
+
11
+ # Public: Entry point for the executable. Processes the CLI input from the user.
12
+ class Source < Thor
13
+ desc 'all [SKYPE_USERNAME] [OPTIONS]', 'Export all chat history'
14
+
15
+ long_desc <<-LONGDESC
16
+ runoff all [SKYPE_USERNAME] [OPTIONS] will export all your Skype chat history as text files.
17
+
18
+ SKYPE_USERNAME - the Skype account username, which data you want to access
19
+ LONGDESC
20
+
21
+ method_option :from, aliases: '-f', desc: 'Specify the location of the main.db file'
22
+ method_option :to, aliases: '-t', desc: 'Specify where to put export files'
23
+
24
+ # Public: Exports all chat history from the Skype database.
25
+ #
26
+ # skype_username - A String that contains a username of the Skype account,
27
+ # which database we want to access.
28
+ #
29
+ # Examples
30
+ #
31
+ # all 'skype_username'
32
+ # # => Finished: 8 files were exported
33
+ def all(skype_username = nil)
34
+ composition = get_composition skype_username
35
+ destination = get_destination
36
+
37
+ print_result composition.export(destination)
38
+
39
+ rescue IOError => e
40
+ puts e
41
+ rescue StandardError => e
42
+ puts e
43
+ end
44
+
45
+ desc 'chat [SKYPE_USERNAME] [OPTIONS]', 'Export pecified chats history'
46
+
47
+ long_desc <<-LONGDESC
48
+ runoff chat [SKYPE_USERNAME] [OPTIONS] will export all your Skype chat history as text files.
49
+
50
+ SKYPE_USERNAME - the Skype account username, which data you want to access
51
+ LONGDESC
52
+
53
+ method_option :from, aliases: '-f', desc: 'Specify the location of the main.db file'
54
+ method_option :to, aliases: '-t', desc: 'Specify where to put export files'
55
+
56
+ # Public: Exports specified chats from the Skype database.
57
+ #
58
+ # skype_username - A String that contains a username of the Skype account,
59
+ # which database we want to access.
60
+ #
61
+ # Examples
62
+ #
63
+ # chat 'skype_username'
64
+ # # => Finished: 3 files were exported
65
+ def chat(skype_username = nil)
66
+ composition = get_composition skype_username
67
+ destination = get_destination
68
+ chatnames, raw_chatnames = composition.get_chatnames
69
+
70
+ list_chatnames chatnames
71
+ indecies = ask "Which chats do you want to export? (Enter indecies) "
72
+ indecies = indecies.split.map { |index| index.to_i }
73
+ selected_chatnames = []
74
+
75
+ indecies.each { |index| selected_chatnames << raw_chatnames[index] }
76
+ print_result composition.export_chats(selected_chatnames, destination)
77
+
78
+ rescue IOError => e
79
+ puts e
80
+ rescue StandardError => e
81
+ puts e
82
+ end
83
+
84
+ private
85
+
86
+ # Internal: Gets a Composition object.
87
+ #
88
+ # skype_username - A String that contains a username of the Skype account,
89
+ # which database we want to access.
90
+ #
91
+ # Examples
92
+ #
93
+ # get_composition 'skype_username'
94
+ # # => #<Composition:0x00002324212>
95
+ #
96
+ # Returns a Composition object with a reference to a specific Skype database.
97
+ # Raises StandardError if no Skype username or --from option is provided.
98
+ def get_composition(skype_username)
99
+ unless skype_username || options[:from]
100
+ raise StandardError, 'You must provide a Skype username or a --from option'
101
+ end
102
+
103
+ main_db_file_location = options[:from] || Location.default_skype_data_location(skype_username)
104
+ Composition.new main_db_file_location
105
+ end
106
+
107
+ # Internal: Gets a destination path depending on the entered options.
108
+ #
109
+ # Examples
110
+ #
111
+ # get_destination
112
+ # # => '~/skype_backup'
113
+ #
114
+ # Returns a String containing a path to the destination directory.
115
+ def get_destination
116
+ options[:to] || Location.home_path
117
+ end
118
+
119
+ # Internal: Informs the user that the application has finished running.
120
+ #
121
+ # count - A number of files that have been exported
122
+ #
123
+ # Examples
124
+ #
125
+ # print_result 4
126
+ # # => Finished: 4 files were exported
127
+ def print_result(count)
128
+ if count == 1
129
+ puts "Finished: 1 file was exported"
130
+ elsif count > 1
131
+ puts "Finished: #{count} files were exported"
132
+ end
133
+ end
134
+
135
+ # Internal: Prints available chatnames.
136
+ #
137
+ # chatnames - An Array containing the chatname strings
138
+ #
139
+ # Examples
140
+ #
141
+ # list_chatnames ['something-more', 'something-else']
142
+ # # => [0] something-more
143
+ # [1] something-else
144
+ def list_chatnames(chatnames)
145
+ chatnames.each_with_index { |n, i| puts "[#{i}] #{n}" }
146
+ puts
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,3 @@
1
+ module Runoff
2
+ VERSION = "0.1.1"
3
+ end
data/lib/runoff.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "runoff/version"
2
+ require "runoff/location"
3
+ require "runoff/composition"
4
+ require "runoff/source"
data/runoff.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'runoff/version'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = "runoff"
9
+ gem.version = Runoff::VERSION
10
+ gem.authors = ["Aigars Dzerviniks"]
11
+ gem.email = ["dzerviniks.aigars@outlook.com"]
12
+ gem.description = %q{runoff provides functionality to export all the Skype chat history or only specified chats from the Skype SQLite database file to text files}
13
+ gem.summary = %q{Tool to export Skype chat history from the SQLite database to text files}
14
+ gem.homepage = 'https://github.com/aidzis/runoff'
15
+ gem.license = 'MIT'
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+
22
+ gem.add_dependency 'thor'
23
+ gem.add_dependency 'sqlite3'
24
+ gem.add_dependency 'sequel'
25
+ end
@@ -0,0 +1,117 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'runoff'
4
+
5
+ describe Runoff::Composition do
6
+ before { @composition = Runoff::Composition.new 'test/test_db.sqlite' }
7
+
8
+ it "must raise an IOError if the file that is passed to the constructor doesn't exist" do
9
+ ->{ composition = Runoff::Composition.new 'not_existing.db' }.must_raise IOError
10
+ end
11
+
12
+ it "must have a getter method for exported filenames" do
13
+ @composition.must_respond_to :exported_filenames
14
+ end
15
+
16
+ it "must remove all starting and ending dashes from a string" do
17
+ string = "---example--"
18
+ valid_name = @composition.send :trim_dashes, string
19
+
20
+ valid_name.must_equal 'example'
21
+ end
22
+
23
+ it "must return a valid and readable name from a raw chatname" do
24
+ raw_chatname = '#something/$else;521357125362'
25
+ chatname = @composition.send :parse_chatname, raw_chatname
26
+
27
+ chatname.must_equal 'something-else'
28
+ end
29
+
30
+ it "must return a valid and readable name from broken chatname records" do
31
+ raw_chatname = '#something/$521357125362'
32
+ chatname = @composition.send :parse_chatname, raw_chatname
33
+
34
+ chatname.must_equal 'something'
35
+ end
36
+
37
+ it "must return a chatname without the extra symbols" do
38
+ raw_chatname = '#something/$else;521357125362'
39
+ chatname = @composition.send :partly_parse_chatname, raw_chatname
40
+
41
+ chatname.must_equal '#something/$else;'
42
+ end
43
+
44
+ it "must return a chatname without the extra symbols for broken chatname records" do
45
+ raw_chatname = '#something/$521357125362'
46
+ chatname = @composition.send :partly_parse_chatname, raw_chatname
47
+
48
+ chatname.must_equal '#something/$'
49
+ end
50
+
51
+ it "must return parsed chatnames together with partly parsed chatnames" do
52
+ chatnames, raw_chatnames = @composition.get_chatnames
53
+
54
+ chatnames.must_equal ['something-more', 'something-else']
55
+ raw_chatnames.must_equal ['#something/$more;', '#something/$else;']
56
+ end
57
+
58
+ describe "#save_to_file" do
59
+ before do
60
+ @composition = Runoff::Composition.new 'test/test_db.sqlite'
61
+ @incorrect_chat_record = {
62
+ chatname: '#something/$;521357125362',
63
+ timestamp: 1362864487,
64
+ from_dispname: 'Aidzis',
65
+ body_xml: ''
66
+ }
67
+ @chat_record = {
68
+ chatname: '#something/$more;521357125362',
69
+ timestamp: 1362864487,
70
+ from_dispname: 'Aidzis',
71
+ body_xml: ''
72
+ }
73
+ end
74
+
75
+ after { FileUtils.rm_rf 'test/tmp/.' }
76
+
77
+ it "must write chat content to file" do
78
+ @incorrect_chat_record[:chatname] = '#something/$else;521357125362'
79
+
80
+ @composition.send :save_to_file, @chat_record, 'test/tmp'
81
+ @composition.send :save_to_file, @incorrect_chat_record, 'test/tmp'
82
+ @composition.exported_filenames.count.must_equal 2
83
+ end
84
+
85
+ it "must append to a file if the filename already exists" do
86
+ @incorrect_chat_record[:chatname] = '#something/$else;521357125362'
87
+ @additional_chat_record = @chat_record
88
+
89
+ @composition.send :save_to_file, @chat_record, 'test/tmp'
90
+ @composition.send :save_to_file, @incorrect_chat_record, 'test/tmp'
91
+ @composition.send :save_to_file, @additional_chat_record, 'test/tmp'
92
+ @composition.exported_filenames.count.must_equal 2
93
+ end
94
+ end
95
+
96
+ describe "#export" do
97
+ after { FileUtils.rm_rf 'test/tmp/.' }
98
+
99
+ it "must return a count of the exported filenames" do
100
+ composition = Runoff::Composition.new 'test/test_db.sqlite'
101
+ file_count = composition.export 'test/tmp'
102
+
103
+ file_count.must_equal 2
104
+ end
105
+ end
106
+
107
+ describe "#export_chats" do
108
+ after { FileUtils.rm_rf 'test/tmp/.' }
109
+
110
+ it "must return a count of the exported filenames" do
111
+ composition = Runoff::Composition.new 'test/test_db.sqlite'
112
+ file_count = composition.export_chats ['#something/$more;', '#something/$else;'], 'test/tmp'
113
+
114
+ file_count.must_equal 2
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,31 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'runoff'
4
+
5
+ describe Runoff::Location do
6
+ describe '.default_skype_data_location' do
7
+ it "must return a default path depending on the operating system" do
8
+ path = Runoff::Location.default_skype_data_location 'aidzis_skype'
9
+
10
+ if RbConfig::CONFIG['host_os'] =~ /mingw/
11
+ path.must_match /[A-Z]:\\Users\\[a-zA-Z0-9]+\\AppData\\Roaming\\Skype\\aidzis_skype\\main\.db/
12
+ elsif RbConfig::CONFIG['host_os'] =~ /linux/
13
+ path.must_match /\/home\/[a-zA-Z0-9]+\/\.Skype\/aidzis_skype\/main\.db/
14
+ else
15
+ path.must_match /~\/Library\/Application Support\/Skype\/aidzis_skype\/main\.db/
16
+ end
17
+ end
18
+ end
19
+
20
+ describe ".home_path" do
21
+ it "must return a path to the user home directory depending on the operating system" do
22
+ path = Runoff::Location.home_path
23
+
24
+ if RbConfig::CONFIG['host_os'] =~ /mingw/
25
+ path.must_match /[A-Z]:\\Users\\[a-zA-Z0-9]+/
26
+ else
27
+ path.must_match /\/home\/[a-zA-Z0-9]+/
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'runoff'
4
+
5
+ describe Runoff::Source do
6
+ after { FileUtils.rm_rf 'test/tmp/.' }
7
+
8
+ it "must export all the chats from a Skype database into text files and print a count of the exported files" do
9
+ command = "all -f test/test_db.sqlite -t test/tmp"
10
+
11
+ ->{ Runoff::Source.start command.split }.must_output "Finished: 2 files were exported\n"
12
+ end
13
+ end
Binary file
data.tar.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ >u�d�2ȷR
2
+ �fs4ӆ�&��)�fXr]����T���>N�d_A���7�7i��n
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: runoff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Aigars Dzerviniks
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - !binary |-
12
+ LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURuakNDQW9hZ0F3SUJB
13
+ Z0lCQVRBTkJna3Foa2lHOXcwQkFRVUZBREJLTVJvd0dBWURWUVFEREJGa2Vt
14
+ VnkKZG1sdWFXdHpMbUZwWjJGeWN6RVhNQlVHQ2dtU0pvbVQ4aXhrQVJrV0Iy
15
+ OTFkR3h2YjJzeEV6QVJCZ29Ka2lhSgprL0lzWkFFWkZnTmpiMjB3SGhjTk1U
16
+ TXdNekkxTVRjek9EVTRXaGNOTVRRd016STFNVGN6T0RVNFdqQktNUm93CkdB
17
+ WURWUVFEREJGa2VtVnlkbWx1YVd0ekxtRnBaMkZ5Y3pFWE1CVUdDZ21TSm9t
18
+ VDhpeGtBUmtXQjI5MWRHeHYKYjJzeEV6QVJCZ29Ka2lhSmsvSXNaQUVaRmdO
19
+ amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBdwpnZ0VLQW9J
20
+ QkFRQzBXYXJpYUl6cmpkU3pBY1VNNGs3a1VmWkdrNFMxZWFIY2J5OTdhRHV0
21
+ QjNGTnp5Q3VGZnhVCjdQdndyR29aS0FGVEY1Mnc5aitKeDljV0FPbzlaTENz
22
+ ZmJKd040ZjdrTVM4emRYVWJ0d3ovL0V4NnNacG9vZE4KZENnSUM2TXJvUXdL
23
+ L3ZjNXExYi90ZVdTNFFCNElmcEVqNlJZYjhRYUptWjBJR252aXJmWE9wVlh5
24
+ N05ISXdmVApTc0R5dGFCRTFzaVAwZ3g0YTlpNVNaTjVHM1UydmJFbmE5aDI3
25
+ OC9nOVdsSnFCN1ZrcW9YTi9zeUE4L1VIeHVlClcvS1lGVDFXZ2xpR0l3aXhs
26
+ K0FJeVFwNGxscEJsOUgzbnU3ZFN2bmRyOVVIUmRsa3pZeEZWM0ZjM0V1cnBp
27
+ UE0KT1BTanpJL0c4V3hJYWkrVWVaY2lERjFJc1lpNXRhY0hBZ01CQUFHamdZ
28
+ NHdnWXN3Q1FZRFZSMFRCQUl3QURBTApCZ05WSFE4RUJBTUNCTEF3SFFZRFZS
29
+ ME9CQllFRkhDSmZLQ3V0ZkhETGdmMUpLak9WNnVHTHVqTE1DZ0dBMVVkCkVR
30
+ UWhNQitCSFdSNlpYSjJhVzVwYTNNdVlXbG5ZWEp6UUc5MWRHeHZiMnN1WTI5
31
+ dE1DZ0dBMVVkRWdRaE1CK0IKSFdSNlpYSjJhVzVwYTNNdVlXbG5ZWEp6UUc5
32
+ MWRHeHZiMnN1WTI5dE1BMEdDU3FHU0liM0RRRUJCUVVBQTRJQgpBUUNudHBP
33
+ UXZveDhUVlN3d1h5MXFDMnVadnZQNTV6WjZoeHl6emJIZFJxd0FBT25zc2ZB
34
+ VkFaSGlYVGZMZFFPCjg3Z3lMYk81YlZRa0dOa3laemRWTWJjNTlhNEg2ZEtT
35
+ cjZBbWNDdGIvdGYrWldqR1U3b0F2UkZuWVJ3aGlyRkwKWThNTDNVZEQyVGdE
36
+ REFYQU9IS1Vsc3E2dEtsVTFORXNMdjFCUkJOa0xQbzI1WHlFU1R6SlRWM2M4
37
+ ZkluNmlLcgpaL3BOenhBZDlSN29TOExDZlhhTGhQeG85dkVyVVZLd3Q2NHpk
38
+ YUtiQUNrampabkNab2VhaGxPd2dzVnN5bDBvCjQ0aXhMbXE0dTE1a0grUFRr
39
+ dHFPWFp1ck5FWmdTMlRSWG5GRG9TWmRtT3IxMjM2ZS9SNTBzc1dlR0dIUUox
40
+ V2wKN2ZZbFZpQThPY2Z3aGdyaDcyc05mVHpTCi0tLS0tRU5EIENFUlRJRklD
41
+ QVRFLS0tLS0K
42
+ date: 2013-03-25 00:00:00.000000000 Z
43
+ dependencies:
44
+ - !ruby/object:Gem::Dependency
45
+ name: thor
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ - !ruby/object:Gem::Dependency
59
+ name: sqlite3
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: sequel
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ description: runoff provides functionality to export all the Skype chat history or
87
+ only specified chats from the Skype SQLite database file to text files
88
+ email:
89
+ - dzerviniks.aigars@outlook.com
90
+ executables:
91
+ - runoff
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - .gitignore
96
+ - Gemfile
97
+ - LICENSE.txt
98
+ - README.md
99
+ - Rakefile
100
+ - bin/runoff
101
+ - gem-public_cert.pem
102
+ - lib/runoff.rb
103
+ - lib/runoff/composition.rb
104
+ - lib/runoff/location.rb
105
+ - lib/runoff/source.rb
106
+ - lib/runoff/version.rb
107
+ - runoff.gemspec
108
+ - test/runoff_composition_test.rb
109
+ - test/runoff_locations_test.rb
110
+ - test/runoff_source_test.rb
111
+ - test/test_db.sqlite
112
+ homepage: https://github.com/aidzis/runoff
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.0.3
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Tool to export Skype chat history from the SQLite database to text files
136
+ test_files:
137
+ - test/runoff_composition_test.rb
138
+ - test/runoff_locations_test.rb
139
+ - test/runoff_source_test.rb
140
+ - test/test_db.sqlite
metadata.gz.sig ADDED
Binary file