runoff 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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