runoff 0.3.3 → 1.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f33b77e8bf4d95208d8ca83a3f60eac94776c49f
4
- data.tar.gz: 01b49cd3d84adf0bb8390a5b74683e7fdd1c5deb
3
+ metadata.gz: 25b2af0e2aa5ad4b5a8a408e3b5d0f61198970c8
4
+ data.tar.gz: 00cea274e9778ac292e31139cd9b246423e76532
5
5
  SHA512:
6
- metadata.gz: bd54f5eec46b95cc771ac6e42819b45cc2219c0cefa9b7202f8b788a377762b22a3a93cd2270c04a5bf99f24f21e7491088c1bde8f1e9115ba8ee2ab0dfa916a
7
- data.tar.gz: a966fdb49e9f9b109141ec509d123d8e8dcd5e3f3ea549f11360bd448868f0246dda20450a884b12bcc7ec7f86cc1efcb4e251daa902bd3adf9fc19721b820e2
6
+ metadata.gz: 0f4434fecb2f3b0b6c942a7032cf7ad1892793d641ac4530a7f6e5bb81f64765b3cd4434c115ed6f5f6f9d3c6c98643ac5dd0dc0292ecb26f0e109bf4de61f1d
7
+ data.tar.gz: bf8051d987612af62f91f421e2fc98057594dbabdf7345167159aea259619f3dc106d6edb6418fed4483828fe06090c36663be117aa326adbc027d72226d550d
data/.gitignore CHANGED
@@ -1,17 +1,18 @@
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
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
18
+ .DS_Store
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
2
-
3
- # gem's dependencies are specified in runoff.gemspec
4
- gemspec
1
+ source 'https://rubygems.org'
2
+
3
+ # gem's dependencies are specified in runoff.gemspec
4
+ gemspec
data/LICENSE.txt CHANGED
@@ -1,22 +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
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
22
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,40 +1,41 @@
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
- gem install runoff
11
-
12
- ## Usage
13
-
14
- To export all the chat history.
15
-
16
- # save a Zip archive in your home directory
17
- runoff all skype_username
18
-
19
- # save a Zip archive in a specific directory
20
- runoff all skype_username -d ~/backups
21
-
22
- # export database that isn't located in the default path
23
- runoff all -f ~/main.db -d ~/backups
24
-
25
- To export specific chats.
26
-
27
- runoff chat skype_username
28
-
29
- If you don't want to put files into an archive, use `-a` option
30
-
31
- runoff all skype_username -a false
32
-
33
- ## What else?
34
-
35
- Things to do in the future versions:
36
-
37
- - Parse body_xml to filter XML tags and character entities.
38
- - Append only new messages to the previously genetrated files instead of appending everything or create different versions for the files when using `-a false` option.
39
-
40
- If you have something to say about this gem or anything else, you can find me on Twitter as [@aigarsdz](http://twitter.com/aigarsdz "@aigarsdz").
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 command-line tool, that automates the process of exporting your chat history.
7
+
8
+ ## Install
9
+
10
+ gem install runoff
11
+
12
+ ## Usage
13
+
14
+ To export all the chat history.
15
+
16
+ # save a Zip archive in your home directory
17
+ runoff all skype_username
18
+
19
+ # save a Zip archive in a specific directory
20
+ runoff all skype_username -d ~/backups
21
+
22
+ # export database that isn't located in the default path
23
+ runoff all -f ~/main.db -d ~/backups
24
+
25
+ To export specific chats.
26
+
27
+ runoff some skype_username
28
+
29
+ If you don't want to put files into an archive, use `--no-archive` option
30
+
31
+ runoff all skype_username --no-archive
32
+
33
+ runoff some skype_username --no-archive
34
+
35
+ ## What else?
36
+
37
+ Things to do in the future versions:
38
+
39
+ Nothing. For now.
40
+
41
+ If you have something to say about this gem or anything else, you can find me on Twitter as [@aigarsdz](http://twitter.com/aigarsdz "@aigarsdz").
data/Rakefile CHANGED
@@ -1,8 +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
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
8
  end
data/bin/runoff CHANGED
@@ -1,34 +1,58 @@
1
- #!/usr/bin/env ruby
2
-
3
- $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
-
5
- require 'commander/import'
6
- require 'runoff'
7
-
8
- program :name, 'runoff'
9
- program :version, Runoff::VERSION
10
- program :description, 'runoff is a simple application to create Skype backups'
11
-
12
- default_command :help
13
-
14
- global_option '-f', '--from FILE', 'Location of the main.db file'
15
- global_option '-d', '--destination DIR', 'Location for the exoprted files'
16
- global_option '-a', '--archive', 'Save files in a Zip archive (defaults to true)'
17
-
18
- command :all do |command|
19
- command.syntax = 'runoff all [SKYPE_USERNAME] [OPTIONS]'
20
- command.description = 'Export all chat history'
21
-
22
- command.action do |args, options|
23
- Runoff::Commands::All.process args, options.__hash__
24
- end
25
- end
26
-
27
- command :chat do |command|
28
- command.syntax = 'runoff chat [SKYPE_USERNAME] [OPTIONS]'
29
- command.description = 'Export pecified chats history'
30
-
31
- command.action do |args, options|
32
- Runoff::Commands::Chat.process args, options.__hash__
33
- end
34
- end
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'commander/import'
6
+ require 'runoff'
7
+ require 'colorize'
8
+
9
+ program :name, 'runoff'
10
+ program :version, Runoff::VERSION
11
+ program :description, 'runoff is a simple application to create Skype backups'
12
+ program :help, 'Author', 'Aigars Dzerviniks <dzerviniks.aigars@outlook.com>'
13
+
14
+ default_command :help
15
+
16
+ command :all do |c|
17
+ c.syntax = 'runoff all [SKYPE_USERNAME] [OPTIONS]'
18
+ c.description = 'Export all chat history'
19
+ c.option '-f', '--from FILE', 'Location of the main.db file'
20
+ c.option '-d', '--destination DIR', 'Location for the exoprted files'
21
+ c.option '--[no-]archive', 'Indicates whether to create an archive'
22
+
23
+ c.action do |args, options|
24
+ begin
25
+ puts "Exporting...".colorize :green
26
+ Runoff::Commands::All.process args, options
27
+ puts "Export finished.".colorize :green
28
+ rescue ArgumentError => e
29
+ puts e.colorize :red
30
+ rescue Sequel::DatabaseError
31
+ puts "Error: To use runoff you must make sure that the Skype is not running".colorize :red
32
+ rescue StandardError => e
33
+ puts e.colorize :red
34
+ end
35
+ end
36
+ end
37
+
38
+ command :some do |c|
39
+ c.syntax = 'runoff some [SKYPE_USERNAME] [OPTIONS]'
40
+ c.description = "Export only some chats' history"
41
+ c.option '-f', '--from FILE', 'Location of the main.db file'
42
+ c.option '-d', '--destination DIR', 'Location for the exoprted files'
43
+ c.option '--[no-]archive', 'Indicates whether to create an archive'
44
+
45
+ c.action do |args, options|
46
+ begin
47
+ puts "Exporting...".colorize :green
48
+ Runoff::Commands::Some.process args, options
49
+ puts "Export finished.".colorize :green
50
+ rescue ArgumentError => e
51
+ puts e.colorize :red
52
+ rescue Sequel::DatabaseError
53
+ puts "Error: To use runoff you must make sure that the Skype is not running".colorize :red
54
+ rescue StandardError => e
55
+ puts e.colorize :red
56
+ end
57
+ end
58
+ end
@@ -1,36 +1,22 @@
1
- module Runoff
2
- # Public: Commands that can be executed by the application.
3
- module Commands
4
- # Public: Command to export all Skype chat history.
5
- #
6
- # Examples
7
- #
8
- # All.process ['username'], { from: '/path/to/main.db' }
9
- class All < Command
10
- # Public: Export all chats.
11
- #
12
- # args - Array containing skype username
13
- # options - Hash containing user provided options
14
- #
15
- # Examples
16
- #
17
- # All.process ['username'], { from: '~/main.db' }
18
- def self.process(args, options = {})
19
- if args.empty? && !options.has_key?(:from)
20
- raise ArgumentError.new 'You must specify a username or a --from option'
21
- end
22
-
23
- composition = self.get_composition args[0], options[:from]
24
- destination = self.get_destination options[:destination]
25
-
26
- self.print_result composition.export(destination)
27
- self.try_to_archive destination, options[:archive]
28
-
29
- rescue ArgumentError => e
30
- puts e
31
- rescue IOError => e
32
- puts e
33
- end
34
- end
35
- end
36
- end
1
+ require 'sequel'
2
+
3
+ module Runoff
4
+ # Commands that can be executed by the application.
5
+ module Commands
6
+ class All < Command
7
+ # Public: Exports all Skyoe chat history.
8
+ #
9
+ # args - an array containing skype username
10
+ # options - a hash containing user provided options
11
+ #
12
+ # Examples
13
+ #
14
+ # All.process ['username'], { from: '~/main.db' }
15
+ def self.process(args, options = {})
16
+ file_writer, export_path = self.get_file_writer_components args, options
17
+
18
+ file_writer.export_database Runoff::SkypeDataFormat.new, export_path, options.archive
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,85 +1,19 @@
1
- module Runoff
2
- # Public: Commands that can be executed by the application.
3
- module Commands
4
- # Public: Methods that are shared between different commands.
5
- class Command
6
- private
7
- # Internal: Gets a Composition object.
8
- #
9
- # skype_username - A String that contains a username of the Skype account,
10
- # which database we want to access.
11
- # optional_path - A String that contains path to a main.db file (Skype's database).
12
- #
13
- # Examples
14
- #
15
- # get_composition 'skype_username', ''
16
- # # => #<Composition:0x00002324212>
17
- #
18
- # Returns a Composition object with a reference to a specific Skype database.
19
- def self.get_composition(skype_username, optional_path)
20
- Runoff::Composition.new optional_path, skype_username
21
- end
22
-
23
- # Internal: Gets a destination path depending on the entered options.
24
- #
25
- # optional_path - A String that contains path where to save exported files.
26
- #
27
- # Examples
28
- #
29
- # get_destination ''
30
- # # => '~/skype_backup'
31
- #
32
- # Returns a String containing a path to the destination directory.
33
- def self.get_destination(optional_path)
34
- optional_path || "#{ENV['HOME']}/skype-backup"
35
- end
36
-
37
- # Internal: Informs the user that the application has finished running.
38
- #
39
- # count - A number of files that have been exported
40
- #
41
- # Examples
42
- #
43
- # print_result 4
44
- # # => Finished: 4 files were exported
45
- def self.print_result(count)
46
- if count == 1
47
- puts 'Finished: 1 file was exported'
48
- elsif count > 1
49
- puts "Finished: #{count} files were exported"
50
- end
51
- end
52
-
53
- # Internal: Prints available chatnames.
54
- #
55
- # chatnames - An Array containing the chatname strings
56
- #
57
- # Examples
58
- #
59
- # list_chatnames ['something-more', 'something-else']
60
- # # => [0] something-more
61
- # [1] something-else
62
- def self.list_chatnames(chatnames)
63
- chatnames.each_with_index { |n, i| puts "[#{i}] #{n}" }
64
- puts
65
- end
66
-
67
- # Internal: performs archiving if an --archive option is provided
68
- #
69
- # destination - A String containing a path to the export directory.
70
- # is_archive_enebled - A flag indicating whether to create an archive.
71
- #
72
- # Examples
73
- #
74
- # try_to_archive '/home/username/skype-backup', { archive: false }
75
- def self.try_to_archive(destination, is_archive_enebled)
76
- unless is_archive_enebled
77
- Runoff::FileWriter.archive destination
78
- end
79
- rescue StandardError => e
80
- puts e
81
- puts 'Faild to create an archive'
82
- end
83
- end
84
- end
1
+ module Runoff
2
+ # Commands that can be executed by the application.
3
+ module Commands
4
+ # The base class for every runoff command.
5
+ class Command
6
+ def self.get_file_writer_components(args, options)
7
+ if args.empty? && !options.from
8
+ raise ArgumentError.new 'You must specify the Skype username or a --from option'
9
+ end
10
+
11
+ main_db_path = Runoff::Location.get_database_path args[0], options
12
+ export_path = Runoff::Location.get_export_path options
13
+ db_handler = Sequel.sqlite main_db_path
14
+
15
+ return Runoff::FileWriter.new(db_handler), export_path
16
+ end
17
+ end
18
+ end
85
19
  end
@@ -0,0 +1,38 @@
1
+ require 'sequel'
2
+ require 'set'
3
+
4
+ module Runoff
5
+ # Commands that can be executed by the application.
6
+ module Commands
7
+ class Some < Command
8
+ # Public: Exports a specified part of Skyoe chat history.
9
+ #
10
+ # args - an array containing skype username
11
+ # options - a hash containing user provided options
12
+ #
13
+ # Examples
14
+ #
15
+ # Some.process ['username'], { from: '~/main.db' }
16
+ def self.process(args, options = {})
17
+ file_writer, export_path = self.get_file_writer_components args, options
18
+ format = Runoff::SkypeDataFormat.new
19
+
20
+ file_writer.export_database_partially format, export_path, options.archive do |w, d|
21
+ chatname_dataset = d.select(:chatname)
22
+ chatnames = Set.new # Set is necessary to filter out duplicate chatnames.
23
+
24
+ chatname_dataset.each { |row| chatnames.add format.normalize(row[:chatname]) }
25
+
26
+ # In order to use indices we need an array.
27
+ w.selected_entries = chatnames.to_a
28
+
29
+ w.selected_entries.each_with_index { |e, i| puts "[#{i}] #{e}" }
30
+
31
+ puts # Ensure a blank line.
32
+ # Return the user response back to the FileWriter class.
33
+ next ask("Specify which chats to export separating each number with a comma:")
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,53 +1,84 @@
1
- require 'zip'
2
- require 'fileutils'
3
-
4
- module Runoff
5
- # Public: Methods used for writing to files.
6
- class FileWriter
7
- # Public: Initialize a FileWriter object.
8
- #
9
- # format - An object containing necessary methods for data formating.
10
- def initialize(format)
11
- @format = format
12
- end
13
- # Public: Saves a single chat message to a file.
14
- #
15
- # record - a Hash containing data about a single chat message.
16
- # output_directory - A String with the path to the directory, wher the file will be saved.
17
- #
18
- # Examples
19
- #
20
- # save_to_file record, '/home/username/skype_backup'
21
- def save_to_file(record, output_directory)
22
- filename = "#{output_directory}/#{@format.get_filename(record)}"
23
-
24
- Dir.mkdir(output_directory) unless File.exists?(output_directory)
25
- File.open(filename, 'a') do |file|
26
- file.puts @format.build_entry(record)
27
- end
28
-
29
- filename
30
- end
31
-
32
- # Public: Compresses all the exported files into a Zip archive.
33
- #
34
- # output_directory - A String with the path to the directory, wher the file will be saved.
35
- #
36
- # Examples
37
- #
38
- # archive '/home/username/skype-backup'
39
- def self.archive(output_directory)
40
- timestamp = Time.now.strftime "%Y%m%d%H%M%S"
41
-
42
- Zip::File.open "#{output_directory}-#{timestamp}.zip", Zip::File::CREATE do |zipfile|
43
- Dir.entries(output_directory).each do |file|
44
- if File.file?("#{output_directory}/#{file}")
45
- zipfile.add file, "#{output_directory}/#{file}"
46
- end
47
- end
48
- end
49
-
50
- FileUtils.rm_rf output_directory
51
- end
52
- end
53
- end
1
+ require 'zip'
2
+ require 'fileutils'
3
+
4
+ module Runoff
5
+ class FileWriter
6
+ attr_accessor :selected_entries
7
+
8
+ def initialize(db_handler)
9
+ @db_handler = db_handler
10
+ @selected_entries = []
11
+ end
12
+
13
+ # Public: Exports all the chats from the database.
14
+ #
15
+ # data_format - an object that defines how the data should be parsed.
16
+ # export_path - a string that points to the directory where exported files must be saved.
17
+ def export_database(data_format, export_path, create_archive)
18
+ @export_path = export_path
19
+
20
+ schema = data_format.get_schema
21
+ dataset = @db_handler[schema[:table]]
22
+ dataset = dataset.select(*schema[:columns])
23
+
24
+ dataset.each do |row|
25
+ write data_format.build_entry(row)
26
+ end
27
+
28
+ archive unless create_archive == false
29
+ end
30
+
31
+ # Public: Exports specific chats from the database.
32
+ #
33
+ # data_format - an object that defines how the data should be parsed.
34
+ # export_path - a string that points to the directory where exported files must be saved.
35
+ def export_database_partially(data_format, export_path, create_archive, &block)
36
+ @export_path = export_path
37
+
38
+ schema = data_format.get_schema
39
+ dataset = @db_handler[schema[:table]]
40
+ indices = block.call self, dataset
41
+ clean_indices = indices.split(',').map { |e| e.strip }
42
+
43
+ clean_indices.each do |i|
44
+ chatname = @selected_entries[i.to_i]
45
+ approximation = data_format.denormalize chatname
46
+ dataset = @db_handler[schema[:table]]
47
+ dataset = dataset.where(Sequel.like(:chatname, "#{approximation}%"))
48
+
49
+ dataset.each { |row| write data_format.build_entry(row) }
50
+ end
51
+
52
+ archive unless create_archive == false
53
+ end
54
+
55
+ private
56
+
57
+ # Internal: Appends a new entry to a file.
58
+ #
59
+ # entry - a hash containing "filename" and "content" keys.
60
+ #
61
+ # Examples
62
+ #
63
+ # write { filename: "test.txt", content: "test content" }
64
+ def write(entry)
65
+ path = "#@export_path/#{entry[:filename]}"
66
+
67
+ File.open(path, "a+") { |file| file.write entry[:content] }
68
+ end
69
+
70
+ # Internal: Creates a Zip file of the destination directory.
71
+ def archive
72
+ zip_file_name = "#@export_path-#{Time.now.strftime "%Y%m%d%H%S%S"}.zip"
73
+
74
+ Zip::File.open(zip_file_name, Zip::File::CREATE) do |zf|
75
+ Dir[File.join(@export_path, '**', '**')].each do |f|
76
+ zf.add(f.sub("#@export_path/", ''), f)
77
+ end
78
+ end
79
+
80
+ # Delete the destination directory because it is no longer needed.
81
+ FileUtils.rm_r @export_path
82
+ end
83
+ end
84
+ end