empyrean 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Empyrean
2
+
3
+ Generate full stats of your account using your Twitter Archive.
4
+
5
+ ## Usage
6
+
7
+ 1. Install Empyrean using `gem install empyrean`
8
+ 1. Download your Twitter archive and unpack it somewhere.
9
+ 2. Generate a new configuration file: `empyrean -g config.yml`
10
+ 2. `empyrean -d path/to/data/js/tweets`
11
+ 3. ???
12
+ 4. Profit!
13
+
14
+ ## Configuration
15
+
16
+ Configuration is done using the `config.yml` file in the current directory.
17
+ You can generate one using `empyrean -g config.yml`. You may also specify a
18
+ different configuration file to use with the `-c` switch.
19
+
20
+ You can also use the `-C` switch to override config values on the fly, such as
21
+ `-C timezone_difference=2` or `-C mentions_enabled=no`. If the configuration
22
+ variable is a list, separate the elements with `,`. For example, to ignore
23
+ the Twitter users `BarackObama` and `baddragon_en`, use
24
+ `-C ignored_users=BarackObama,baddragon_en`.
25
+
data/bin/empyrean ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ # empyrean - generate a statistics page from your Twitter archive
3
+ #
4
+ # This file is part of Empyrean
5
+ # Copyright (C) 2015 nilsding, pixeldesu
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ require 'empyrean/cli'
21
+
22
+ Empyrean::CLI.new(*ARGV)
@@ -0,0 +1,31 @@
1
+ # Empyrean config file
2
+
3
+ timezone_difference: 0 # Time zone difference (e.g. 2 for UTC+2, -6 for UTC-6)
4
+
5
+ mentions:
6
+ enabled: true # Enable "Most mentioned users" list
7
+ top: 10 # How many users to show on the "Most mentioned users" list
8
+ notop: 20 # How many users to show on the "These didn't make it to the top" list
9
+ clients:
10
+ enabled: true # Enable "Most used clients" list
11
+ top: 10 # How many clients to show on the "Most used clients" list
12
+ notop: 20 # How many clients to show on the "These didn't make it to the top" list
13
+ hashtags:
14
+ enabled: true # Enable "Most used hashtags" list
15
+ top: 10 # How many hashtags to show on the "Most used hashtags" list
16
+ notop: 20 # How many hashtags to show on the "These didn't make it to the top" list
17
+ smileys:
18
+ enabled: true # Enable "Most used smileys" list
19
+ top: 10 # How many smileys to show on the "Most used smileys" list
20
+ notop: 0 # How many smileys to show on the "These didn't make it to the top" list
21
+
22
+ ignored_users: # Users to exclude from the statistics.
23
+ - BarackObama
24
+ - tbhjuststop
25
+
26
+ renamed_users: # Users that have renamed themselves
27
+ pixeldesu: "mochizune" # mochizune was previously known as pixeldesu
28
+ technikaflash: "mochizune" # mochizune was also previously known as TechnikaFlash (the downcase on the username is important)
29
+ traxxed22: "mochizune" # and before that, mochizune was known as TraXXed22.
30
+
31
+ # kate: indent-width 2
data/empyrean.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'empyrean/defaults'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'empyrean'
8
+ spec.version = Empyrean::VERSION
9
+ spec.authors = ['nilsding', 'pixeldesu']
10
+ spec.email = ['nilsding@nilsding.org', 'andy@pixelde.su']
11
+ spec.summary = %q{Generates stats using your Twitter archive.}
12
+ spec.description = %q{With Empyrean, you can generate full stats of your Twitter account using your Twitter archive.}
13
+ spec.homepage = 'https://github.com/Leafcat/Empyrean'
14
+ spec.license = 'GPLv3'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+ end
@@ -0,0 +1,97 @@
1
+ # cli.rb
2
+ #
3
+ # This file is part of Empyrean
4
+ # Copyright (C) 2015 nilsding, pixeldesu
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require 'empyrean/defaults'
20
+ require 'empyrean/optparser'
21
+ require 'empyrean/configloader'
22
+ require 'empyrean/tweetloader'
23
+ require 'empyrean/tweetparser'
24
+ require 'empyrean/templatelister'
25
+ require 'empyrean/templaterenderer'
26
+
27
+ module Empyrean
28
+ class CLI
29
+ def initialize(*argv)
30
+ @options = OptParser.parse(argv)
31
+ @config = ConfigLoader.new(@options).load_config
32
+
33
+ parsed = analyze_tweets
34
+ print_stats(parsed)
35
+ generate_html(parsed)
36
+ end
37
+
38
+ def analyze_tweets
39
+ tweet_files = TweetLoader.read_directory(@options.jsondir)
40
+ parsed = []
41
+ parser = TweetParser.new(@options, @config)
42
+ tweet_files.each do |file|
43
+ tweet = TweetLoader.read_file file
44
+ parsed << parser.parse(tweet)
45
+ end
46
+ TweetParser.merge_parsed parsed
47
+ end
48
+
49
+ def print_stats(parsed)
50
+ puts "Analyzed #{parsed[:tweet_count]} tweets"
51
+ puts " - #{(parsed[:retweet_count] * 100 / parsed[:tweet_count].to_f).round(2)}% retweets\n"
52
+ return unless @options.print_stats
53
+
54
+ headers = {
55
+ mentions: ["You send most mentions to:", "times", :name],
56
+ clients: ["Most used clients:", "tweets", :name],
57
+ hashtags: ["Your most used hashtags:", "times", :hashtag],
58
+ smileys: ["Your most used smileys:", "times", :smiley]
59
+ }
60
+
61
+ headers.each do |k, v|
62
+ if @config[k][:enabled]
63
+ puts v[0]
64
+ begin
65
+ @config[k][:top].times do |i|
66
+ puts "#{sprintf "%2d", i + 1}. #{'#' if k == :hashtags}#{parsed[k][i][1][v[2]]} (#{parsed[k][i][1][:count]} #{v[1]})"
67
+ end
68
+ rescue => _
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def generate_html(parsed)
75
+ puts "Generating HTML"
76
+ template = File.read template_file
77
+ renderer = TemplateRenderer.new @config, template, parsed
78
+ File.open(@options.outfile, 'w') do |outfile|
79
+ outfile.write renderer.render
80
+ outfile.flush
81
+ end
82
+ puts " => #{@options.outfile}"
83
+ end
84
+
85
+ def template_file
86
+ if File.exist? File.join(Dir.pwd, @options.template)
87
+ File.join(Dir.pwd, @options.template)
88
+ elsif TemplateLister.list.include? @options.template
89
+ File.join TEMPLATE_DIR, @options.template
90
+ else
91
+ @options.template
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ # kate: indent-width 2
@@ -0,0 +1,105 @@
1
+ # configloader.rb - loads a config file
2
+ #
3
+ # This file is part of Empyrean
4
+ # Copyright (C) 2015 nilsding, pixeldesu
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require 'yaml'
20
+ require 'empyrean/defaults'
21
+
22
+ module Empyrean
23
+ class ConfigLoader
24
+ def initialize(options)
25
+ @options = options
26
+ end
27
+
28
+ ##
29
+ # Loads a YAML file, parses it and returns a hash with symbolized keys.
30
+ def load(file)
31
+ if File.exist? file
32
+ symbolize_keys(YAML.load_file(File.expand_path('.', file)))
33
+ else
34
+ {}
35
+ end
36
+ end
37
+
38
+ # Loads a YAML file, parses it and checks if all values are given. If a
39
+ # value is missing, it will be set with the default value.
40
+ def load_config(file = @options.config)
41
+ config = load(file)
42
+ config[:timezone_difference] = 0 if config[:timezone_difference].nil?
43
+ config[:mentions] = {} if config[:mentions].nil?
44
+ config[:mentions][:enabled] = true if config[:mentions][:enabled].nil?
45
+ config[:mentions][:top] = 10 if config[:mentions][:top].nil?
46
+ config[:mentions][:notop] = 20 if config[:mentions][:notop].nil?
47
+ config[:clients] = {} if config[:clients].nil?
48
+ config[:clients][:enabled] = true if config[:clients][:enabled].nil?
49
+ config[:clients][:top] = 10 if config[:clients][:top].nil?
50
+ config[:clients][:notop] = 20 if config[:clients][:notop].nil?
51
+ config[:hashtags] = {} if config[:hashtags].nil?
52
+ config[:hashtags][:enabled] = true if config[:hashtags][:enabled].nil?
53
+ config[:hashtags][:top] = 10 if config[:hashtags][:top].nil?
54
+ config[:hashtags][:notop] = 20 if config[:hashtags][:notop].nil?
55
+ config[:smileys] = {} if config[:smileys].nil?
56
+ config[:smileys][:enabled] = true if config[:smileys][:enabled].nil?
57
+ config[:smileys][:top] = 10 if config[:smileys][:top].nil?
58
+ config[:smileys][:notop] = 0 if config[:smileys][:notop].nil?
59
+ config[:ignored_users] = [] if config[:ignored_users].nil?
60
+ config[:renamed_users] = [] if config[:renamed_users].nil?
61
+ args_override config
62
+ end
63
+
64
+ def args_override(config)
65
+ config_args = @options.config_values
66
+ config[:timezone_difference] = config_args[:timezone_difference] unless config_args[:timezone_difference].nil?
67
+ config[:mentions][:enabled] = config_args[:mentions_enabled] unless config_args[:mentions_enabled].nil?
68
+ config[:mentions][:top] = config_args[:mentions_top] unless config_args[:mentions_top].nil?
69
+ config[:mentions][:notop] = config_args[:mentions_notop] unless config_args[:mentions_notop].nil?
70
+ config[:clients][:enabled] = config_args[:clients_enabled] unless config_args[:clients_enabled].nil?
71
+ config[:clients][:top] = config_args[:clients_top] unless config_args[:clients_top].nil?
72
+ config[:clients][:notop] = config_args[:clients_notop] unless config_args[:clients_notop].nil?
73
+ config[:hashtags][:enabled] = config_args[:hashtags_enabled] unless config_args[:hashtags_enabled].nil?
74
+ config[:hashtags][:top] = config_args[:hashtags_top] unless config_args[:hashtags_top].nil?
75
+ config[:hashtags][:notop] = config_args[:hashtags_notop] unless config_args[:hashtags_notop].nil?
76
+ config[:smileys][:enabled] = config_args[:smileys_enabled] unless config_args[:smileys_enabled].nil?
77
+ config[:smileys][:top] = config_args[:smileys_top] unless config_args[:smileys_top].nil?
78
+ config[:smileys][:notop] = config_args[:smileys_notop] unless config_args[:smileys_notop].nil?
79
+ config[:ignored_users] = config_args[:ignored_users] unless config_args[:ignored_users].nil?
80
+ config[:ignored_users].each do |user| user.downcase! end
81
+ config[:renamed_users].each do |old, new| new.downcase! end
82
+ config
83
+ end
84
+
85
+ private
86
+
87
+ # Symbolizes the keys of a hash, duh.
88
+ def symbolize_keys(hash)
89
+ hash.inject({}) do |result, (key, value)|
90
+ new_key = case key
91
+ when String then key.to_sym
92
+ else key
93
+ end
94
+ new_value = case value
95
+ when Hash then symbolize_keys(value)
96
+ else value
97
+ end
98
+ result[new_key] = new_value
99
+ result
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ # kate: indent-width 2
@@ -0,0 +1,43 @@
1
+ # defaults.rb - some constants
2
+ #
3
+ # This file is part of Empyrean
4
+ # Copyright (C) 2015 nilsding, pixeldesu
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ module Empyrean
20
+ # Application name
21
+ APP_NAME = "Empyrean"
22
+
23
+ # Version
24
+ VERSION = "0.1.0"
25
+
26
+ # Combined version string
27
+ VERSION_STR = "#{APP_NAME} #{VERSION}"
28
+
29
+ # Regexp for matching user names
30
+ USERNAME_REGEX = /[@]([a-zA-Z0-9_]{1,16})/
31
+
32
+ # Regexp for matching the client source
33
+ SOURCE_REGEX = /^<a href=\"(https?:\/\/\S+|erased_\d+)\" rel=\"nofollow\">(.+)<\/a>$/
34
+
35
+ # Regexp for matching hashtags
36
+ HASHTAG_REGEX = /[#]([^{}\[\]().,\-:!*_?#\s]+)/
37
+
38
+ # Path to the templates
39
+ TEMPLATE_DIR = File.expand_path "../templates", __FILE__
40
+
41
+ # The default template to use
42
+ DEFAULT_TEMPLATE = "default.html.erb"
43
+ end
@@ -0,0 +1,151 @@
1
+ # optparser.rb - parses CLI options using Ruby's OptionParser
2
+ #
3
+ # This file is part of Empyrean
4
+ # Copyright (C) 2015 nilsding, pixeldesu
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require 'optparse'
20
+ require 'ostruct'
21
+ require 'empyrean/defaults'
22
+ require 'empyrean/templatelister'
23
+
24
+ module Empyrean
25
+ class OptParser
26
+
27
+ def self.parse(args)
28
+ options = OpenStruct.new
29
+ options.jsondir = ""
30
+ options.outfile = "output.html"
31
+ options.config = File.expand_path('.', "config.yml")
32
+ options.config_values = {}
33
+ options.print_stats = false
34
+ options.template = DEFAULT_TEMPLATE
35
+ options.verbose = false
36
+
37
+ opts = OptionParser.new do |opts|
38
+ opts.banner = "Usage: stats.rb [options]"
39
+
40
+ opts.separator ""
41
+ opts.separator "Specific options:"
42
+
43
+ opts.on("-c", "--config CONFIG", "The configuration file to use (default: #{options.config})") do |config|
44
+ options.config = config
45
+ end
46
+
47
+ opts.on("-C", "--config-value KEY=VALUE", "Sets a configuration value (e.g. hashtags_enabled=false)") do |val|
48
+ key, value = val.split("=")
49
+ key = key.downcase
50
+ unless value.nil? # ignore empty values
51
+ case key.to_s
52
+ when /_enabled$/
53
+ value = to_bool value
54
+ when /_(no)?top$/, /timezone_difference$/
55
+ value = value.to_i
56
+ when /ignored_users/
57
+ value = value.split ','
58
+ end
59
+ options.config_values[key.to_sym] = value
60
+ end
61
+ end
62
+
63
+ opts.on("-g", "--generate-config CONFIG", "Generate a new configuration file") do |config_file|
64
+ fn = File.expand_path '.', config_file
65
+ if File.exist? fn
66
+ STDERR.puts "Cowardly refusing to overwrite already existing file #{config_file}."
67
+ exit 2
68
+ end
69
+ File.open fn, 'w' do |f|
70
+ f.write File.read File.expand_path("../../../config.yml.example", __FILE__)
71
+ STDERR.puts "Wrote new configuration to #{config_file}."
72
+ exit
73
+ end
74
+ end
75
+
76
+ opts.on("-d", "--jsondir DIRECTORY", "Directory with tweet files (containing 2014_07.js etc.)") do |dir|
77
+ dir = File.expand_path('.', dir)
78
+ unless File.exist?(dir) && File.directory?(dir)
79
+ STDERR.puts "not a directory: #{dir}"
80
+ exit 1
81
+ end
82
+
83
+ entries = Dir.entries(dir)
84
+ entries.each do |e|
85
+ # the file names of the tweet archive are in the format /^\d{4}_\d{2}\.js$/
86
+ unless e =~ /^\d{4}_\d{2}\.js$|\.+?/
87
+ STDERR.puts "not a tweets directory: #{dir}"
88
+ exit 1
89
+ end
90
+ end
91
+
92
+ options.jsondir = dir
93
+ end
94
+
95
+ opts.on("-o", "--outfile OUTFILE", "Output HTML file (default: #{options.outfile})") do |outfile|
96
+ options.outfile = outfile
97
+ end
98
+
99
+ opts.on("-l", "--list-templates", "List available templates") do
100
+ TemplateLister.print_list
101
+ exit
102
+ end
103
+
104
+ opts.on("-t", "--template TEMPLATE", "Template to use (default: #{options.template})") do |template|
105
+ options.template = template
106
+ end
107
+
108
+ opts.on("-p", "--[no-]print-stats", "Print stats to stdout") do |p|
109
+ options.print_stats = p
110
+ end
111
+
112
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
113
+ options.verbose = v
114
+ end
115
+
116
+ # No argument, shows at tail. This will print an options summary.
117
+ opts.on_tail("-h", "--help", "Show this message") do
118
+ puts opts
119
+ exit
120
+ end
121
+
122
+ # Another typical switch to print the version.
123
+ opts.on_tail("--version", "Show version") do
124
+ puts Empyrean::VERSION_STR
125
+ exit
126
+ end
127
+ end.parse!(args)
128
+
129
+ if options.jsondir.empty?
130
+ STDERR.puts "missing argument: --jsondir, see --help for more"
131
+ exit 1
132
+ end
133
+
134
+ options
135
+ end # parse()
136
+
137
+ private
138
+
139
+ ##
140
+ # "converts" a string to a boolean value
141
+ def self.to_bool str
142
+ if str == true || str =~ (/(true|t|yes|y|1)$/i)
143
+ true
144
+ else
145
+ false
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ # kate: indent-width 2
@@ -0,0 +1,35 @@
1
+ # templatelister.rb - lists available templates
2
+ #
3
+ # This file is part of Empyrean
4
+ # Copyright (C) 2015 nilsding, pixeldesu
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require 'empyrean/defaults'
20
+
21
+ module Empyrean
22
+ class TemplateLister
23
+ class << self
24
+ # Returns an array of available templates.
25
+ def list
26
+ Dir[File.join TEMPLATE_DIR, "*.html.erb"].map{ |t| File.basename t }
27
+ end
28
+
29
+ # Prints the available templates to stdout.
30
+ def print_list
31
+ puts list
32
+ end
33
+ end
34
+ end
35
+ end