lolcommits 0.9.2 → 0.9.3.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +6 -0
- data/CHANGELOG.md +243 -105
- data/CONTRIBUTING.md +2 -2
- data/README.md +10 -3
- data/bin/lolcommits +9 -16
- data/features/step_definitions/lolcommits_steps.rb +0 -1
- data/features/support/env.rb +0 -1
- data/features/support/path_helpers.rb +0 -1
- data/lib/core_ext/mini_magick/utilities.rb +0 -1
- data/lib/lolcommits.rb +16 -16
- data/lib/lolcommits/backends/git_info.rb +0 -1
- data/lib/lolcommits/backends/installation_git.rb +0 -1
- data/lib/lolcommits/backends/installation_mercurial.rb +0 -1
- data/lib/lolcommits/backends/mercurial_info.rb +0 -1
- data/lib/lolcommits/capturer.rb +0 -1
- data/lib/lolcommits/capturer/capture_cygwin.rb +0 -1
- data/lib/lolcommits/capturer/capture_fake.rb +0 -1
- data/lib/lolcommits/capturer/capture_linux.rb +0 -1
- data/lib/lolcommits/capturer/capture_linux_animated.rb +0 -1
- data/lib/lolcommits/capturer/capture_mac.rb +0 -1
- data/lib/lolcommits/capturer/capture_mac_animated.rb +0 -1
- data/lib/lolcommits/capturer/capture_windows.rb +0 -1
- data/lib/lolcommits/cli/fatals.rb +0 -8
- data/lib/lolcommits/cli/launcher.rb +0 -1
- data/lib/lolcommits/cli/process_runner.rb +0 -2
- data/lib/lolcommits/cli/timelapse_gif.rb +0 -1
- data/lib/lolcommits/configuration.rb +10 -7
- data/lib/lolcommits/gem_plugin.rb +46 -0
- data/lib/lolcommits/installation.rb +0 -1
- data/lib/lolcommits/platform.rb +0 -1
- data/lib/lolcommits/plugin/base.rb +110 -0
- data/lib/lolcommits/plugin/dot_com.rb +50 -0
- data/lib/lolcommits/plugin/lol_flowdock.rb +69 -0
- data/lib/lolcommits/plugin/lol_hipchat.rb +124 -0
- data/lib/lolcommits/plugin/lol_protonet.rb +68 -0
- data/lib/lolcommits/plugin/lol_slack.rb +68 -0
- data/lib/lolcommits/plugin/lol_tumblr.rb +129 -0
- data/lib/lolcommits/plugin/lol_twitter.rb +176 -0
- data/lib/lolcommits/plugin/lol_yammer.rb +84 -0
- data/lib/lolcommits/plugin/lolsrv.rb +58 -0
- data/lib/lolcommits/plugin/loltext.rb +190 -0
- data/lib/lolcommits/plugin/term_output.rb +55 -0
- data/lib/lolcommits/{plugins → plugin}/tranzlate.rb +14 -15
- data/lib/lolcommits/plugin/uploldz.rb +65 -0
- data/lib/lolcommits/plugin_manager.rb +48 -0
- data/lib/lolcommits/runner.rb +4 -5
- data/lib/lolcommits/test_helpers/fake_io.rb +20 -0
- data/lib/lolcommits/test_helpers/git_repo.rb +44 -0
- data/lib/lolcommits/vcs_info.rb +0 -1
- data/lib/lolcommits/version.rb +2 -2
- data/lolcommits.gemspec +2 -2
- data/test/lolcommits_test.rb +1 -2
- data/test/plugins_test.rb +7 -8
- metadata +22 -19
- data/lib/core_ext/class.rb +0 -8
- data/lib/lolcommits/plugin.rb +0 -123
- data/lib/lolcommits/plugins/dot_com.rb +0 -51
- data/lib/lolcommits/plugins/lol_flowdock.rb +0 -70
- data/lib/lolcommits/plugins/lol_hipchat.rb +0 -125
- data/lib/lolcommits/plugins/lol_protonet.rb +0 -69
- data/lib/lolcommits/plugins/lol_slack.rb +0 -69
- data/lib/lolcommits/plugins/lol_tumblr.rb +0 -129
- data/lib/lolcommits/plugins/lol_twitter.rb +0 -176
- data/lib/lolcommits/plugins/lol_yammer.rb +0 -85
- data/lib/lolcommits/plugins/lolsrv.rb +0 -58
- data/lib/lolcommits/plugins/loltext.rb +0 -184
- data/lib/lolcommits/plugins/term_output.rb +0 -54
- data/lib/lolcommits/plugins/uploldz.rb +0 -66
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'oauth'
|
3
|
+
require 'simple_oauth'
|
4
|
+
require 'rest_client'
|
5
|
+
require 'addressable/uri'
|
6
|
+
|
7
|
+
module Lolcommits
|
8
|
+
module Plugin
|
9
|
+
class LolTwitter < Base
|
10
|
+
TWITTER_API_ENDPOINT = 'https://api.twitter.com'.freeze
|
11
|
+
TWITTER_CONSUMER_KEY = 'qc096dJJCxIiqDNUqEsqQ'.freeze
|
12
|
+
TWITTER_CONSUMER_SECRET = 'rvjNdtwSr1H0TvBvjpk6c4bvrNydHmmbvv7gXZQI'.freeze
|
13
|
+
TWITTER_RESERVED_MEDIA_CHARS = 24
|
14
|
+
TWITTER_RETRIES = 2
|
15
|
+
TWITTER_PIN_REGEX = /^\d{4,}$/ # 4 or more digits
|
16
|
+
DEFAULT_SUFFIX = '#lolcommits'.freeze
|
17
|
+
|
18
|
+
def run_postcapture
|
19
|
+
tweet = build_tweet(runner.message)
|
20
|
+
|
21
|
+
attempts = 0
|
22
|
+
begin
|
23
|
+
attempts += 1
|
24
|
+
puts "Tweeting: #{tweet}"
|
25
|
+
debug "--> Tweeting! (attempt: #{attempts}, tweet length: #{tweet.length} chars)"
|
26
|
+
post_tweet(tweet, File.open(runner.main_image, 'r'))
|
27
|
+
rescue StandardError => e
|
28
|
+
debug "Tweet FAILED! #{e.class} - #{e.message}"
|
29
|
+
retry if attempts < TWITTER_RETRIES
|
30
|
+
puts "ERROR: Tweet FAILED! (after #{attempts} attempts) - #{e.message}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def post_url
|
35
|
+
# TODO: this endpoint is deprecated, use the new approach instead
|
36
|
+
# https://dev.twitter.com/rest/reference/post/statuses/update_with_mediath_media
|
37
|
+
@post_url ||= TWITTER_API_ENDPOINT + '/1.1/statuses/update_with_media.json'
|
38
|
+
end
|
39
|
+
|
40
|
+
def post_tweet(status, media)
|
41
|
+
RestClient.post(
|
42
|
+
post_url,
|
43
|
+
{
|
44
|
+
'media[]' => media,
|
45
|
+
'status' => status
|
46
|
+
}, Authorization: oauth_header
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_tweet(commit_message)
|
51
|
+
prefix = config_with_default('prefix', '')
|
52
|
+
suffix = " #{config_with_default('suffix', DEFAULT_SUFFIX)}"
|
53
|
+
prefix = "#{prefix} " unless prefix.empty?
|
54
|
+
|
55
|
+
available_commit_msg_size = max_tweet_size - (prefix.length + suffix.length)
|
56
|
+
if commit_message.length > available_commit_msg_size
|
57
|
+
commit_message = "#{commit_message[0..(available_commit_msg_size - 3)]}..."
|
58
|
+
end
|
59
|
+
|
60
|
+
"#{prefix}#{commit_message}#{suffix}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def configure_options!
|
64
|
+
options = super
|
65
|
+
# ask user to configure tokens if enabling
|
66
|
+
if options['enabled']
|
67
|
+
auth_config = configure_auth!
|
68
|
+
return unless auth_config
|
69
|
+
options = options.merge(auth_config).merge(configure_prefix_suffix)
|
70
|
+
end
|
71
|
+
options
|
72
|
+
end
|
73
|
+
|
74
|
+
def configure_auth!
|
75
|
+
puts '---------------------------'
|
76
|
+
puts 'Need to grab twitter tokens'
|
77
|
+
puts '---------------------------'
|
78
|
+
|
79
|
+
request_token = oauth_consumer.get_request_token
|
80
|
+
rtoken = request_token.token
|
81
|
+
rsecret = request_token.secret
|
82
|
+
|
83
|
+
print "\n1) Please open this url in your browser to get a PIN for lolcommits:\n\n"
|
84
|
+
puts request_token.authorize_url
|
85
|
+
print "\n2) Enter PIN, then press enter: "
|
86
|
+
twitter_pin = gets.strip.downcase.to_s
|
87
|
+
|
88
|
+
unless twitter_pin =~ TWITTER_PIN_REGEX
|
89
|
+
puts "\nERROR: '#{twitter_pin}' is not a valid Twitter Auth PIN"
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
begin
|
94
|
+
debug "Requesting Twitter OAuth Token with PIN: #{twitter_pin}"
|
95
|
+
OAuth::RequestToken.new(oauth_consumer, rtoken, rsecret)
|
96
|
+
access_token = request_token.get_access_token(oauth_verifier: twitter_pin)
|
97
|
+
rescue OAuth::Unauthorized
|
98
|
+
puts "\nERROR: Twitter PIN Auth FAILED!"
|
99
|
+
return
|
100
|
+
end
|
101
|
+
|
102
|
+
return unless access_token.token && access_token.secret
|
103
|
+
puts ''
|
104
|
+
puts '------------------------------'
|
105
|
+
puts 'Thanks! Twitter Auth Succeeded'
|
106
|
+
puts '------------------------------'
|
107
|
+
{
|
108
|
+
'access_token' => access_token.token,
|
109
|
+
'secret' => access_token.secret
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
def configure_prefix_suffix
|
114
|
+
print "\n3) Prefix all tweets with something? e.g. @user (leave blank for no prefix): "
|
115
|
+
prefix = gets.strip
|
116
|
+
print "\n4) End all tweets with something? e.g. #hashtag (leave blank for default suffix #{DEFAULT_SUFFIX}): "
|
117
|
+
suffix = gets.strip
|
118
|
+
|
119
|
+
config = {}
|
120
|
+
config['prefix'] = prefix unless prefix.empty?
|
121
|
+
config['suffix'] = suffix unless suffix.empty?
|
122
|
+
config
|
123
|
+
end
|
124
|
+
|
125
|
+
def configured?
|
126
|
+
!configuration['enabled'].nil? &&
|
127
|
+
configuration['access_token'] &&
|
128
|
+
configuration['secret']
|
129
|
+
end
|
130
|
+
|
131
|
+
def oauth_header
|
132
|
+
uri = Addressable::URI.parse(post_url)
|
133
|
+
SimpleOAuth::Header.new(:post, uri, {}, oauth_credentials)
|
134
|
+
end
|
135
|
+
|
136
|
+
def oauth_credentials
|
137
|
+
{
|
138
|
+
consumer_key: TWITTER_CONSUMER_KEY,
|
139
|
+
consumer_secret: TWITTER_CONSUMER_SECRET,
|
140
|
+
token: configuration['access_token'],
|
141
|
+
token_secret: configuration['secret']
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
def oauth_consumer
|
146
|
+
@oauth_consumer ||= OAuth::Consumer.new(
|
147
|
+
TWITTER_CONSUMER_KEY,
|
148
|
+
TWITTER_CONSUMER_SECRET,
|
149
|
+
site: TWITTER_API_ENDPOINT,
|
150
|
+
request_endpoint: TWITTER_API_ENDPOINT,
|
151
|
+
sign_in: true
|
152
|
+
)
|
153
|
+
end
|
154
|
+
|
155
|
+
def config_with_default(key, default = nil)
|
156
|
+
if configuration[key]
|
157
|
+
configuration[key].strip.empty? ? default : configuration[key]
|
158
|
+
else
|
159
|
+
default
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def max_tweet_size
|
164
|
+
139 - TWITTER_RESERVED_MEDIA_CHARS
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.name
|
168
|
+
'twitter'
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.runner_order
|
172
|
+
:postcapture
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'yammer'
|
2
|
+
require 'rest_client'
|
3
|
+
|
4
|
+
# https://developer.yammer.com/oauth2-quickstart/
|
5
|
+
YAMMER_CLIENT_ID = 'bgORyeKtnjZJSMwp8oln9g'.freeze
|
6
|
+
YAMMER_CLIENT_SECRET = 'oer2WdGzh74a5QBbW3INUxblHK3yg9KvCZmiBa2r0'.freeze
|
7
|
+
YAMMER_ACCESS_TOKEN_URL = 'https://www.yammer.com/oauth2/access_token.json'.freeze
|
8
|
+
YAMMER_RETRY_COUNT = 2
|
9
|
+
|
10
|
+
module Lolcommits
|
11
|
+
module Plugin
|
12
|
+
class LolYammer < Base
|
13
|
+
def self.name
|
14
|
+
'yammer'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.runner_order
|
18
|
+
:postcapture
|
19
|
+
end
|
20
|
+
|
21
|
+
def configured?
|
22
|
+
!configuration['access_token'].nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def configure_access_token
|
26
|
+
print "Open the URL below and copy the `code` param from query after redirected, enter it as `access_token`:\n"
|
27
|
+
print "https://www.yammer.com/dialog/oauth?client_id=#{YAMMER_CLIENT_ID}&response_type=code\n"
|
28
|
+
print 'Enter code param from the redirected URL, then press enter: '
|
29
|
+
code = gets.to_s.strip
|
30
|
+
|
31
|
+
url = YAMMER_ACCESS_TOKEN_URL
|
32
|
+
debug "access_token url: #{url}"
|
33
|
+
params = {
|
34
|
+
'client_id' => YAMMER_CLIENT_ID,
|
35
|
+
'client_secret' => YAMMER_CLIENT_SECRET,
|
36
|
+
'code' => code
|
37
|
+
}
|
38
|
+
debug "params : #{params.inspect}"
|
39
|
+
result = JSON.parse(RestClient.post(url, params))
|
40
|
+
debug "response : #{result.inspect}"
|
41
|
+
# no need for 'return', last line is always the return value
|
42
|
+
{ 'access_token' => result['access_token']['token'] }
|
43
|
+
end
|
44
|
+
|
45
|
+
def configure_options!
|
46
|
+
options = super
|
47
|
+
# ask user to configure tokens if enabling
|
48
|
+
if options['enabled']
|
49
|
+
auth_config = configure_access_token
|
50
|
+
return unless auth_config
|
51
|
+
options.merge!(auth_config)
|
52
|
+
end
|
53
|
+
options
|
54
|
+
end
|
55
|
+
|
56
|
+
def run_postcapture
|
57
|
+
commit_msg = runner.message
|
58
|
+
post = "#{commit_msg} #lolcommits"
|
59
|
+
puts "Yammer post: #{post}" unless runner.capture_stealth
|
60
|
+
|
61
|
+
Yammer.configure do |c|
|
62
|
+
c.client_id = YAMMER_CLIENT_ID
|
63
|
+
c.client_secret = YAMMER_CLIENT_SECRET
|
64
|
+
end
|
65
|
+
|
66
|
+
client = Yammer::Client.new(access_token: configuration['access_token'])
|
67
|
+
|
68
|
+
retries = YAMMER_RETRY_COUNT
|
69
|
+
begin
|
70
|
+
lolimage = File.new(runner.main_image)
|
71
|
+
response = client.create_message(post, attachment1: lolimage)
|
72
|
+
debug response.body.inspect
|
73
|
+
puts "\t--> Status posted!" if response
|
74
|
+
rescue => e
|
75
|
+
retries -= 1
|
76
|
+
retry if retries > 0
|
77
|
+
puts "Status not posted - #{e.message}"
|
78
|
+
puts 'Try running config again:'
|
79
|
+
puts "\tlolcommits --config --plugin yammer"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'pp'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Lolcommits
|
6
|
+
module Plugin
|
7
|
+
class Lolsrv < Base
|
8
|
+
def initialize(runner)
|
9
|
+
super
|
10
|
+
options << 'server'
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_postcapture
|
14
|
+
fork { sync }
|
15
|
+
end
|
16
|
+
|
17
|
+
def configured?
|
18
|
+
!configuration['enabled'].nil? && configuration['server']
|
19
|
+
end
|
20
|
+
|
21
|
+
def sync
|
22
|
+
existing = existing_lols
|
23
|
+
return unless existing.nil?
|
24
|
+
Dir[runner.config.loldir + '/*.{jpg,gif}'].each do |item|
|
25
|
+
sha = File.basename(item, '.*')
|
26
|
+
upload(item, sha) unless existing.include?(sha) || sha == 'tmp_snapshot'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def existing_lols
|
31
|
+
lols = JSON.parse(RestClient.get(configuration['server'] + '/lols'))
|
32
|
+
lols.map { |lol| lol['sha'] }
|
33
|
+
rescue => e
|
34
|
+
log_error(e, "ERROR: existing lols could not be retrieved #{e.class} - #{e.message}")
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def upload(file, sha)
|
39
|
+
RestClient.post(configuration['server'] + '/uplol',
|
40
|
+
lol: File.new(file),
|
41
|
+
url: runner.vcs_info.url + sha,
|
42
|
+
repo: runner.vcs_info.repo,
|
43
|
+
date: File.ctime(file),
|
44
|
+
sha: sha)
|
45
|
+
rescue => e
|
46
|
+
log_error(e, "ERROR: Upload of lol #{sha} FAILED #{e.class} - #{e.message}")
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.name
|
50
|
+
'lolsrv'
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.runner_order
|
54
|
+
:postcapture
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module Lolcommits
|
2
|
+
module Plugin
|
3
|
+
class Loltext < Base
|
4
|
+
DEFAULT_FONT_PATH = File.join(Configuration::LOLCOMMITS_ROOT, 'vendor', 'fonts', 'Impact.ttf')
|
5
|
+
|
6
|
+
def self.name
|
7
|
+
'loltext'
|
8
|
+
end
|
9
|
+
|
10
|
+
# enabled by default (if no configuration exists)
|
11
|
+
def enabled?
|
12
|
+
!configured? || super
|
13
|
+
end
|
14
|
+
|
15
|
+
# valid by default (if no config exists)
|
16
|
+
def valid_configuration?
|
17
|
+
!configured? || super
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_postcapture
|
21
|
+
debug 'Annotating image via MiniMagick'
|
22
|
+
image = MiniMagick::Image.open(runner.main_image)
|
23
|
+
if config_option(:overlay, :enabled)
|
24
|
+
image.combine_options do |c|
|
25
|
+
c.fill config_option(:overlay, :overlay_colors).sample
|
26
|
+
c.colorize config_option(:overlay, :overlay_percent)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
annotate(image, :message, clean_msg(runner.message))
|
31
|
+
annotate(image, :sha, runner.sha)
|
32
|
+
debug "Writing changed file to #{runner.main_image}"
|
33
|
+
image.write runner.main_image
|
34
|
+
end
|
35
|
+
|
36
|
+
def annotate(image, type, string)
|
37
|
+
debug("annotating #{type} to image with #{string}")
|
38
|
+
|
39
|
+
transformed_position = position_transform(config_option(type, :position))
|
40
|
+
annotate_location = '0'
|
41
|
+
if transformed_position == 'South'
|
42
|
+
annotate_location = '+0+20' # Move South gravity off the edge of the image.
|
43
|
+
end
|
44
|
+
|
45
|
+
string.upcase! if config_option(type, :uppercase)
|
46
|
+
|
47
|
+
image.combine_options do |c|
|
48
|
+
c.strokewidth runner.capture_animated? ? '1' : '2'
|
49
|
+
c.interline_spacing(-(config_option(type, :size) / 5))
|
50
|
+
c.stroke config_option(type, :stroke_color)
|
51
|
+
c.fill config_option(type, :color)
|
52
|
+
c.gravity transformed_position
|
53
|
+
c.pointsize runner.capture_animated? ? (config_option(type, :size) / 2) : config_option(type, :size)
|
54
|
+
c.font config_option(type, :font)
|
55
|
+
c.annotate annotate_location, string
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def configure_options!
|
60
|
+
options = super
|
61
|
+
# ask user to configure text options when enabling
|
62
|
+
if options['enabled']
|
63
|
+
puts '---------------------------------------------------------------'
|
64
|
+
puts ' LolText options '
|
65
|
+
puts ''
|
66
|
+
puts ' * any blank options will use the (default)'
|
67
|
+
puts ' * always use the full absolute path to fonts'
|
68
|
+
puts ' * valid text positions are NE, NW, SE, SW, S, C (centered)'
|
69
|
+
puts ' * colors can be hex #FC0 value or a string \'white\''
|
70
|
+
puts ' - use `none` for no stroke color'
|
71
|
+
puts ' * overlay fills your image with a random color'
|
72
|
+
puts ' - set one or more overlay_colors with a comma seperator'
|
73
|
+
puts ' - overlay_percent (0-100) sets the fill colorize strength'
|
74
|
+
puts '---------------------------------------------------------------'
|
75
|
+
|
76
|
+
options[:message] = configure_sub_options(:message)
|
77
|
+
options[:sha] = configure_sub_options(:sha)
|
78
|
+
options[:overlay] = configure_sub_options(:overlay)
|
79
|
+
end
|
80
|
+
options
|
81
|
+
end
|
82
|
+
|
83
|
+
# TODO: consider this type of configuration prompting in the base Plugin
|
84
|
+
# class, working with hash of defaults
|
85
|
+
def configure_sub_options(type)
|
86
|
+
print "#{type}:\n"
|
87
|
+
defaults = config_defaults[type]
|
88
|
+
|
89
|
+
# sort option keys since different `Hash#keys` varys across Ruby versions
|
90
|
+
defaults.keys.sort_by(&:to_s).reduce({}) do |acc, opt|
|
91
|
+
# if we have an enabled key set to false, abort asking for any more options
|
92
|
+
if acc.key?(:enabled) && acc[:enabled] != true
|
93
|
+
acc
|
94
|
+
else
|
95
|
+
print " #{opt.to_s.tr('_', ' ')} (#{defaults[opt]}): "
|
96
|
+
val = parse_user_input(gets.chomp.strip)
|
97
|
+
# handle array options (comma seperated string)
|
98
|
+
if defaults[opt].is_a?(Array) && !val.nil?
|
99
|
+
val = val.split(',').map(&:strip).delete_if(&:empty?)
|
100
|
+
end
|
101
|
+
acc.merge(opt => val)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def config_defaults
|
107
|
+
{
|
108
|
+
message: {
|
109
|
+
color: 'white',
|
110
|
+
font: DEFAULT_FONT_PATH,
|
111
|
+
position: 'SW',
|
112
|
+
size: 48,
|
113
|
+
stroke_color: 'black',
|
114
|
+
uppercase: false
|
115
|
+
},
|
116
|
+
sha: {
|
117
|
+
color: 'white',
|
118
|
+
font: DEFAULT_FONT_PATH,
|
119
|
+
position: 'NE',
|
120
|
+
size: 32,
|
121
|
+
stroke_color: 'black',
|
122
|
+
uppercase: false
|
123
|
+
},
|
124
|
+
overlay: {
|
125
|
+
enabled: false,
|
126
|
+
overlay_colors: [
|
127
|
+
'#2e4970', '#674685', '#ca242f', '#1e7882', '#2884ae', '#4ba000',
|
128
|
+
'#187296', '#7e231f', '#017d9f', '#e52d7b', '#0f5eaa', '#e40087',
|
129
|
+
'#5566ac', '#ed8833', '#f8991c', '#408c93', '#ba9109'
|
130
|
+
],
|
131
|
+
overlay_percent: 50
|
132
|
+
}
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def config_option(type, option)
|
137
|
+
default_option = config_defaults[type][option]
|
138
|
+
if configuration[type]
|
139
|
+
configuration[type][option] || default_option
|
140
|
+
else
|
141
|
+
default_option
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
# explode psuedo-names for text position
|
148
|
+
def position_transform(position)
|
149
|
+
case position
|
150
|
+
when 'NE'
|
151
|
+
'NorthEast'
|
152
|
+
when 'NW'
|
153
|
+
'NorthWest'
|
154
|
+
when 'SE'
|
155
|
+
'SouthEast'
|
156
|
+
when 'SW'
|
157
|
+
'SouthWest'
|
158
|
+
when 'C'
|
159
|
+
'Center'
|
160
|
+
when 'S'
|
161
|
+
'South'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# do whatever is required to commit message to get it clean and ready for imagemagick
|
166
|
+
def clean_msg(text)
|
167
|
+
wrapped_text = word_wrap text
|
168
|
+
escape_quotes wrapped_text
|
169
|
+
escape_ats wrapped_text
|
170
|
+
end
|
171
|
+
|
172
|
+
# conversion for quotation marks to avoid shell interpretation
|
173
|
+
# does not seem to be a safe way to escape cross-platform?
|
174
|
+
def escape_quotes(text)
|
175
|
+
text.gsub(/"/, "''")
|
176
|
+
end
|
177
|
+
|
178
|
+
def escape_ats(text)
|
179
|
+
text.gsub(/@/, '\@')
|
180
|
+
end
|
181
|
+
|
182
|
+
# convenience method for word wrapping
|
183
|
+
# based on https://github.com/cmdrkeene/memegen/blob/master/lib/meme_generator.rb
|
184
|
+
def word_wrap(text, col = 27)
|
185
|
+
wrapped = text.gsub(/(.{1,#{col + 4}})(\s+|\Z)/, "\\1\n")
|
186
|
+
wrapped.chomp!
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|