Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 479 lines (443 sloc) 14.02 kB
#!/usr/bin/env ruby
# __ _
# / _\| | ___ __ _ __ _ ___ _ __
# \ \ | |/ _ \ / _` |/ _` |/ _ \ '__|
# _\ \| | (_) | (_| | (_| | __/ |
# \__/|_|\___/ \__, |\__, |\___|_|
# |___/ |___/
# Copyright 2012, Brett Terpstra
# http://brettterpstra.com
# --------------------
require 'rubygems'
require 'bundler/setup'
require 'open-uri'
require 'net/http'
require 'net/https'
require 'time'
require 'cgi'
require 'rss'
require 'erb'
require 'logger'
require 'optparse'
require 'fileutils'
require 'rexml/parsers/pullparser'
require 'rubygems'
require 'json'
SLOGGER_HOME = File.dirname(File.expand_path(__FILE__))
ENV['SLOGGER_HOME'] = SLOGGER_HOME
require SLOGGER_HOME + '/lib/sociallogger'
require SLOGGER_HOME + '/lib/configtools'
require SLOGGER_HOME + '/lib/plist.rb'
# require SLOGGER_HOME + '/lib/json'
require SLOGGER_HOME + '/lib/levenshtein-0.2.2/lib/levenshtein.rb'
if RUBY_VERSION.to_f > 1.9
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
end
class String
def markdownify
contents = ''
begin
if RUBY_VERSION.to_f > 1.9
input = self.dup.force_encoding('utf-8')
else
input = self.dup
end
IO.popen('"$SLOGGER_HOME/lib/html2text"', "r+") do |io|
begin
Thread.new { input.each_line { |line|
io << line
}; io.close_write }
rescue Exception => e
$stderr.puts e
end
begin
io.each_line do |line|
contents << line
end
rescue Exception => e
$stderr.puts e
end
end
contents
rescue Exception => e
$stderr.puts e
$stderr.puts "Error in Markdownify"
self
end
end
# convert (multi)Markdown to HTML
def to_html
md = SLOGGER_HOME + '/lib/multimarkdown'
return %x{echo #{self.e_sh}|"#{md}"}
end
# shell escape for passing content to external commands
# e.g. %x{echo content.e_sh|sort}
def e_sh
self.to_s.gsub(/(?=[^a-zA-Z0-9_.\/\-\n])/, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''")
end
def e_link
self.to_s.gsub(/([\[\]\(\)])/, '\\\\\1')
end
# escape text for use in a quoted AppleScript string
#
# string = %q{"This is a quoted string and it's awfully nice!"}
# res = %x{osascript <<'APPLESCRIPT'
# return "hello, #{string.e_as}"
# APPLESCRIPT}
def e_as(str)
str.to_s.gsub(/(?=["\\])/, '\\')
end
def truncate_html(len = 30)
p = REXML::Parsers::PullParser.new(self)
tags = []
new_len = len
results = ''
while p.has_next? && new_len > 0
p_e = p.pull
case p_e.event_type
when :start_element
tags.push p_e[0]
results << "<#{tags.last} #{attrs_to_s(p_e[1])}>"
when :end_element
results << "</#{tags.pop}>"
when :text
results << p_e[0].first(new_len)
new_len -= p_e[0].length
else
results << "<!-- #{p_e.inspect} -->"
end
end
tags.reverse.each do |tag|
results << "</#{tag}>"
end
results
end
private
def attrs_to_s(attrs)
if attrs.empty?
''
else
attrs.to_a.map { |attr| %{#{attr[0]}="#{attr[1]}"} }.join(' ')
end
end
end
class SloggerUtils
def get_stdin(message)
print message + " "
STDIN.gets.chomp
end
def ask(message, valid_options = nil)
if valid_options
answer = get_stdin("#{message} #{valid_options.to_s.gsub(/"/, '').gsub(/, /,'/')} ") while !valid_options.include?(answer)
else
answer = get_stdin(message)
end
answer
end
end
class Slogger
attr_accessor :config, :dayonepath, :plugins
attr_reader :timespan, :log
def initialize
cfg = ConfigTools.new({'config_file' => $options[:config_file]})
@log = Logger.new(STDERR)
original_formatter = Logger::Formatter.new
@log.datetime_format = '%d'
@log.level = 1
@log.progname = self.class.name
@log.formatter = proc { |severity, datetime, progname, msg|
abbr_sev = case severity
when 'WARN' then "> "
when 'ERROR' then "! "
when 'FATAL' then "!!"
else " "
end
spacer_count = 20 - progname.length
spacer = ''
spacer_count.times do
spacer += ' '
end
output = $options[:quiet] ? '' : "#{abbr_sev}#{datetime.strftime('%H:%M:%S')} #{spacer}#{progname}: #{msg}\n"
output
}
@plugins = []
if cfg.config_exists?
@config = cfg.load_config
if @config.nil?
raise "Config should not be nil"
Process.exit(-1)
end
end
if $options[:since_last_run] && @config.key?('last_run_time') && !@config['last_run_time'].nil?
@timespan = Time.parse(@config['last_run_time'])
else
@timespan = Time.now - ((60 * 60 * 24) * $options[:timespan])
end
@config['image_filename_is_title'] ||= false
@dayonepath = self.storage_path
@template = self.template
@date_format = @config['date_format'] || '%F'
@time_format = @config['time_format'] || '%R'
@datetime_format = "#{@date_format} #{@time_format}"
@to = $options[:to]
@from = $options[:from]
end
def undo_slogger(count = 1)
runlog = SLOGGER_HOME+'/runlog.txt'
if File.exists?(runlog)
undo_to = ''
File.open(runlog,'r') do |f|
runs = f.read.split(/\n/)
if runs.length >= count
undo_to = runs[count*-1].match(/^\[(.*?)\]/)[1]
end
end
$stderr.puts undo_to
tnow = Time.now
elapsed = tnow - Time.parse(undo_to) # elapsed time in seconds
files = %x{find "#{self.storage_path}" -newerct '#{elapsed.floor} seconds ago' -type f}.split(/\n/)
files.each do |file|
FileUtils.mv(file,ENV['HOME']+'/.Trash/')
end
@log.info("Moved #{files.length} entries to Trash")
else
@log.fatal("Run log does not exist.")
Process.exit(-1)
end
end
def log_run
File.open(SLOGGER_HOME+'/runlog.txt', 'a') { |f|
f.puts "[#{Time.now.strftime('%c')}] Slogger v#{MAJOR_VERSION} (#{MAJOR_VERSION}.#{MINOR_VERSION}.#{BUILD_NUMBER}) #{$options.inspect}"
}
end
def storage_path
if @config.key?('storage')
if @config['storage'].downcase == 'icloud'
dayonedir = %x{ls ~/Library/Mobile\\ Documents/|grep dayoneapp}.strip
full_path = File.expand_path("~/Library/Mobile\ Documents/#{dayonedir}/Documents/Journal_dayone/")
if File.exists?(full_path)
return full_path
else
raise "Failed to find iCloud storage path"
Process.exit(-1)
end
elsif File.exists?(File.expand_path(@config['storage']))
return File.expand_path(@config['storage'])
else
raise "Path for Day One journal is not specified or doesn't exist. Change your path in slogger_config and run ./slogger again: #{@config['storage']}"
Process.exit(-1)
end
else
raise "Path for Day One journal is not specified or doesn't exist. Change your path in slogger_config and run ./slogger again: #{@config['storage']}"
return
end
end
def run_plugins
@config['last_run_time'] = Time.now.strftime('%c')
new_options = false
plugin_dir = $options[:develop] ? "/plugins_develop/*.rb" : "/plugins/*.rb"
Dir[SLOGGER_HOME + plugin_dir].each do |file|
if $options[:onlyrun]
$options[:onlyrun].each { |plugin_frag|
if File.basename(file) =~ /^#{plugin_frag}/i
require file
end
}
else
require file
end
end
@plugins.each do |plugin|
_namespace = plugin['class'].to_s
@config[_namespace] ||= {}
plugin['config'].each do |k,v|
if @config[_namespace][k].nil?
new_options = true
@config[_namespace][k] ||= v
end
@config[_namespace][_namespace+"_last_run"] = Time.now.strftime('%c')
end
unless $options[:config_only]
# credit to Hilton Lipschitz (@hiltmon)
updated_config = eval(plugin['class']).new.do_log
if updated_config && updated_config.class.to_s == 'Hash'
updated_config.each { |k,v|
@config[_namespace][k] = v
}
end
end
end
ConfigTools.new({'config_file' => $options[:config_file]}).dump_config(@config)
end
def register_plugin(plugin)
@plugins.push plugin
end
def template
markdown = @dayonepath =~ /Journal[\._]dayone\/?$/ ? false : true
unless markdown
ERB.new <<-XMLTEMPLATE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Creation Date</key>
<date><%= datestamp %></date>
<key>Creator</key>
<dict>
<key>Software Agent</key>
<string>Slogger/#{MAJOR_VERSION}.#{MINOR_VERSION}.#{BUILD_NUMBER}</string>
</dict>
<key>Entry Text</key>
<string><%= entry %></string>
<% if location %><key>Location</key>
<dict>
<key>Administrative Area</key>
<string></string>
<key>Country</key>
<string></string>
<key>Latitude</key>
<real><%= lat %></real>
<key>Longitude</key>
<real><%= long %></real>
<key>Place Name</key>
<string><% if place %><%= place %><% end %></string>
</dict><% end %>
<key>Starred</key>
<<%= starred %>/>
<% if tags %><key>Tags</key>
<array>
<% tags.each do |tag| %> <string><%= tag %></string>
<% end %></array><% end %>
<key>UUID</key>
<string><%= uuid %></string>
</dict>
</plist>
XMLTEMPLATE
else
ERB.new <<-MARKDOWNTEMPLATE
Title: Journal entry for <%= datestamp %>
Date: <%= datestamp %>
Starred: <%= starred %>
<% if tags %>Tags: <% tags.join(", ") %> <% end %>
<%= entry %>
MARKDOWNTEMPLATE
end
end
end
require SLOGGER_HOME + '/lib/redirect'
require SLOGGER_HOME + '/lib/dayone'
$options = {}
optparse = OptionParser.new do|opts|
opts.banner = "Usage: slogger [-dq] [-r X] [/path/to/image.jpg]"
$options[:config_file] = File.expand_path(File.dirname(__FILE__)+'/slogger_config')
opts.on('--update-config', 'Create or update a configuration file') do
$options[:config_only] = true
end
opts.on( '-c', '--config FILE', 'Specify configuration file to use') do |file|
file = File.expand_path(file)
$options[:config_file] = file
end
$options[:develop] = false
opts.on( '-d','--develop', 'Develop mode' ) do
$options[:develop] = true
end
$options[:onlyrun] = false
opts.on( '-o','--onlyrun NAME','Only run plugins matching items in comma-delimited string') do |plugin_string|
$options[:onlyrun] = plugin_string.split(/,/).map {|frag| frag.strip }
end
$options[:timespan] = 1
opts.on( '-t', '--timespan DAYS', 'Days of history to collect') do |days|
$options[:timespan] = days.to_i
end
$options[:from] = nil
opts.on( '-F', '--from DATE', 'Time from ') do |date|
$options[:from] = date
end
$options[:to] = nil
opts.on( '-T', '--to DATE', 'Time to') do |date|
$options[:to] = date
end
$options[:quiet] = false
opts.on( '-q','--quiet', 'Run quietly (no notifications/messages)' ) do
$options[:quiet] = true
end
$options[:max_retries] = 3
opts.on( '-r','--retries COUNT', 'Maximum number of retries per plugin (int)' ) do |count|
$options[:max_retries] = count.to_i
end
$options[:since_last_run] = false
opts.on( '-s','--since-last', 'Set the timespan to the last run date' ) do
$options[:since_last_run] = true
end
$options[:undo] = false
opts.on( '-u', '--undo COUNT', 'Undo the last COUNT runs') do |count|
$options[:undo] = count.to_i
end
opts.on( '-v', '--version', 'Display the version number') do
$stdout.puts("Slogger version #{MAJOR_VERSION}.#{MINOR_VERSION}.#{BUILD_NUMBER}")
exit
end
opts.on( '--dedup', 'Remove duplicate entries from Journal') do
puts "This will remove entries from your Journal that have"
puts "duplicate content and matching tags. The oldest copy"
puts "of an entry will be preserved. The entries will be"
puts "moved to a DayOneDuplicates directory on your Desktop."
puts
answer = SloggerUtils.new.ask("Are you sure you want to continue?",["y","n"])
if answer == "y"
DayOne.new.dedup
end
exit
end
## This will be cool when it works.
# opts.on( '--dedup_similar', 'Remove similar entries from Journal') do
# puts "This will remove entries from your Journal that have"
# puts "very similar content on the same date. The oldest copy"
# puts "of an entry will be preserved. The entries will be"
# puts "moved to a DayOneDuplicates directory on your Desktop."
# puts
# puts "This is a slow process and can take >15m on large journals."
# puts
# answer = SloggerUtils.new.ask("Are you sure you want to continue?",["y","n"])
# if answer == "y"
# DayOne.new.dedup(true)
# end
# exit
# end
opts.on( '-h', '--help', 'Display this screen' ) do
puts opts
exit
end
end
optparse.parse!
$slog = Slogger.new
$slog.dayonepath = $slog.storage_path
if ARGV.length > 0
path = File.expand_path(ARGV[0])
if File.exists?(path)
unless path =~ /\.(jpg|png|gif)/i
File.open(path,'r') do | f |
DayOne.new.to_dayone({ 'content' => f.read })
end
else
DayOne.new.store_single_photo(ARGV[0],{},true)
end
else
raise "File \"#{ARGV[0]}\" not found."
end
else
unless $options[:undo]
# Set environment variable SLOGGER_NO_INITIALIZE if you want to load slogger config but not run plugins
if ENV['SLOGGER_NO_INITIALIZE'] == "true"
$stderr.puts "No initialization: Slogger v#{MAJOR_VERSION} (#{MAJOR_VERSION}.#{MINOR_VERSION}.#{BUILD_NUMBER})"
else
$stdout.puts "Initializing Slogger v#{MAJOR_VERSION} (#{MAJOR_VERSION}.#{MINOR_VERSION}.#{BUILD_NUMBER})..."
$slog.log_run
$slog.run_plugins
end
else
$stdout.puts "Undoing the last #{$options[:undo].to_s} runs..."
$slog.undo_slogger($options[:undo])
end
end
Jump to Line
Something went wrong with that request. Please try again.