twitterpunch 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/twitterpunch +32 -3
- data/lib/twitterpunch.rb +1 -1
- data/lib/twitterpunch/poster.rb +20 -14
- data/lib/twitterpunch/queue.rb +53 -0
- data/lib/twitterpunch/remote.rb +54 -0
- data/public/touch-icon-ipad-retina.png +0 -0
- data/public/touch-icon-iphone-retina.png +0 -0
- data/views/index.erb +147 -0
- metadata +18 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f713d6cb1c1e707769c09897f11913046bbffbd4
|
4
|
+
data.tar.gz: c37e34a59d06f4e0f6edfcfee3d23e0d87af4b77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 840fce97e3c6a0b6b55d34aea8080a2bc82873bcb528b6e108036544e6427b48b542eac7509b1cbce852fb0dd108c0dba010df85bb261da23efa3eed4642e7cd
|
7
|
+
data.tar.gz: 789cc9841fb3bf2b22f0184ff425fca27b8bf8b33aeaf76312efba604d07bbdecb041063ff1cc4cdf2f5508037c5676e4d1644cde99056ef6850a4c80942cd6e
|
data/bin/twitterpunch
CHANGED
@@ -4,10 +4,14 @@ require 'rubygems'
|
|
4
4
|
require 'twitter'
|
5
5
|
require 'optparse'
|
6
6
|
require "net/http"
|
7
|
+
require 'fileutils'
|
7
8
|
require 'yaml'
|
8
9
|
|
9
10
|
require 'twitterpunch'
|
10
11
|
|
12
|
+
FileUtils.mkdir_p File.expand_path('~/.twitterpunch')
|
13
|
+
FileUtils.touch(File.expand_path('~/.twitterpunch/queue.yaml'))
|
14
|
+
|
11
15
|
configfile = File.expand_path('~/.twitterpunch.yaml')
|
12
16
|
config = YAML.load_file(configfile) rescue {}
|
13
17
|
|
@@ -44,10 +48,18 @@ optparse = OptionParser.new { |opts|
|
|
44
48
|
config[:action] = :configure
|
45
49
|
end
|
46
50
|
|
47
|
-
opts.on("-i", "--
|
51
|
+
opts.on("-i", "--remote", "Start the Twitterpunch remote web service") do
|
52
|
+
config[:action] = :remote
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on("-r", "--install", "Install the Folder Action workflow (OS X only).") do
|
48
56
|
config[:action] = :install
|
49
57
|
end
|
50
58
|
|
59
|
+
opts.on("-t", "--test", "Load up Pry debugging console.") do
|
60
|
+
config[:action] = :test
|
61
|
+
end
|
62
|
+
|
51
63
|
opts.on("-v", "--version", "Display version information.") do
|
52
64
|
puts "Twitterpunch v#{Twitterpunch::VERSION}"
|
53
65
|
exit 0
|
@@ -62,8 +74,11 @@ optparse = OptionParser.new { |opts|
|
|
62
74
|
}
|
63
75
|
optparse.parse!
|
64
76
|
|
65
|
-
config[:action]
|
66
|
-
config[:resources]
|
77
|
+
config[:action] = :post if ARGV.size > 0 and config[:action].nil?
|
78
|
+
config[:resources] = File.expand_path("#{File.dirname(__FILE__)}/../resources")
|
79
|
+
config[:queue] = {}
|
80
|
+
config[:queue][:file] = File.expand_path('~/.twitterpunch/queue.yaml')
|
81
|
+
config[:queue][:ttl] = 30
|
67
82
|
|
68
83
|
case config[:action]
|
69
84
|
when :stream
|
@@ -103,6 +118,20 @@ when :configure
|
|
103
118
|
when :install
|
104
119
|
system('open', "#{config[:resources]}/Twitterpunch.workflow")
|
105
120
|
|
121
|
+
when :remote
|
122
|
+
require 'twitterpunch/remote'
|
123
|
+
options = {
|
124
|
+
:port => 8080,
|
125
|
+
:bind => '0.0.0.0',
|
126
|
+
:queue => Twitterpunch::Queue.new(config),
|
127
|
+
}
|
128
|
+
|
129
|
+
Twitterpunch::Remote.run! options
|
130
|
+
|
131
|
+
when :test
|
132
|
+
require 'pry'
|
133
|
+
binding.pry
|
134
|
+
|
106
135
|
else
|
107
136
|
puts "Run twitterpunch --help for usage."
|
108
137
|
exit 1
|
data/lib/twitterpunch.rb
CHANGED
data/lib/twitterpunch/poster.rb
CHANGED
@@ -1,34 +1,40 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'twitter'
|
3
|
+
require 'twitterpunch/queue'
|
3
4
|
|
4
5
|
module Twitterpunch
|
5
6
|
class Poster
|
6
7
|
def initialize(config)
|
7
8
|
@config = config
|
8
9
|
@client = Twitter::REST::Client.new(config[:twitter])
|
10
|
+
@sound = @config[:sendsound] || "#{@config[:resources]}/tweet_sent.wav"
|
11
|
+
@length = 113 - @config[:hashtag].length
|
12
|
+
@queue = Twitterpunch::Queue.new(config)
|
9
13
|
end
|
10
14
|
|
11
15
|
def post(files)
|
12
16
|
files.each do |img|
|
17
|
+
message = @queue.pop || @config[:messages].sample
|
18
|
+
message = "#{message[0..@length]} ##{@config[:hashtag]}"
|
13
19
|
|
14
|
-
message = "#{@config[:messages].sample} ##{@config[:hashtag]}"
|
15
20
|
@client.update_with_media(message, File.new(File.expand_path(img)))
|
21
|
+
chirp()
|
22
|
+
end
|
23
|
+
end
|
16
24
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
puts 'gem install win32-sound to enable sounds.'
|
26
|
-
end
|
27
|
-
when /darwin/
|
28
|
-
system("afplay #{soundfile}")
|
25
|
+
def chirp
|
26
|
+
case RUBY_PLATFORM
|
27
|
+
when /mingw|cygwin/
|
28
|
+
begin
|
29
|
+
require 'win32/sound'
|
30
|
+
Win32::Sound.play(@sound)
|
31
|
+
rescue LoadError
|
32
|
+
puts 'gem install win32-sound to enable sounds.'
|
29
33
|
end
|
30
|
-
|
34
|
+
when /darwin/
|
35
|
+
system("afplay #{@sound}")
|
31
36
|
end
|
32
37
|
end
|
38
|
+
|
33
39
|
end
|
34
40
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Twitterpunch::Queue
|
2
|
+
def initialize(config)
|
3
|
+
@config = config
|
4
|
+
@file = config[:queue][:file]
|
5
|
+
@ttl = config[:queue][:ttl]
|
6
|
+
end
|
7
|
+
|
8
|
+
def pop
|
9
|
+
message = nil
|
10
|
+
File.open(@file, 'r+') do |file|
|
11
|
+
file.flock(File::LOCK_EX)
|
12
|
+
queue = load(file)
|
13
|
+
|
14
|
+
unless queue.empty?
|
15
|
+
if RUBY_VERSION < "1.9.3"
|
16
|
+
message = queue[queue.keys.sort.first]
|
17
|
+
else
|
18
|
+
message = queue.shift[1]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
save(file, queue)
|
23
|
+
end
|
24
|
+
message
|
25
|
+
end
|
26
|
+
|
27
|
+
def push(message)
|
28
|
+
return unless message.is_a? String
|
29
|
+
return if message.empty?
|
30
|
+
File.open(@file, 'r+') do |file|
|
31
|
+
file.flock(File::LOCK_EX)
|
32
|
+
|
33
|
+
queue = load(file)
|
34
|
+
queue[Time.now] = message
|
35
|
+
save(file, queue)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def load(file)
|
40
|
+
queue = YAML.load(file.read) rescue {}
|
41
|
+
queue ||= {}
|
42
|
+
|
43
|
+
# forcibly expire anything that's too old.
|
44
|
+
queue.select { |k,v| (Time.now - k) < @ttl }
|
45
|
+
end
|
46
|
+
|
47
|
+
def save(file, queue)
|
48
|
+
puts "Saving #{queue.inspect}"
|
49
|
+
file.rewind
|
50
|
+
file.truncate 0
|
51
|
+
file.write(queue.to_yaml)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'webrick'
|
3
|
+
require 'tilt/erb'
|
4
|
+
require 'open3'
|
5
|
+
require 'twitterpunch/queue'
|
6
|
+
|
7
|
+
class Twitterpunch::Remote < Sinatra::Base
|
8
|
+
|
9
|
+
set :views, File.dirname(__FILE__) + '/../../views'
|
10
|
+
set :public_folder, File.dirname(__FILE__) + '/../../public'
|
11
|
+
set :erb, :trim => '-'
|
12
|
+
set :configfile, File.expand_path('~/.twitterpunch/queue.yaml')
|
13
|
+
|
14
|
+
configure :production, :development do
|
15
|
+
enable :logging
|
16
|
+
enable :sessions
|
17
|
+
system('osascript', '-e' 'tell application "Photo Booth" to activate')
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/' do
|
21
|
+
erb :index
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/photo' do
|
25
|
+
settings.queue.push(params[:message]) unless params.empty?
|
26
|
+
photo()
|
27
|
+
end
|
28
|
+
|
29
|
+
not_found do
|
30
|
+
status 404
|
31
|
+
end
|
32
|
+
|
33
|
+
helpers do
|
34
|
+
def photo
|
35
|
+
begin
|
36
|
+
stdout, status = Open3.capture2e('osascript', '-e', 'tell application "Photo Booth" to activate')
|
37
|
+
puts stdout
|
38
|
+
raise "Could not activate Photo Booth" unless status.success?
|
39
|
+
|
40
|
+
# This is kind of iffy, because it depends on having full control over the UI.
|
41
|
+
# This will only work when Photo Booth actually has the foreground.
|
42
|
+
stdout, status = Open3.capture2e('osascript', '-e', 'tell application "System Events" to keystroke return')
|
43
|
+
puts stdout
|
44
|
+
raise "Snapshot failed" unless status.success?
|
45
|
+
|
46
|
+
'ok'
|
47
|
+
rescue => e
|
48
|
+
status 500
|
49
|
+
e.message
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
Binary file
|
Binary file
|
data/views/index.erb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
5
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
6
|
+
<meta name="apple-mobile-web-app-title" content="Twitterpunch" />
|
7
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
8
|
+
<link rel="apple-touch-icon" sizes="120x120" href="touch-icon-iphone-retina.png">
|
9
|
+
<link rel="apple-touch-icon" sizes="152x152" href="touch-icon-ipad-retina.png">
|
10
|
+
<title>Twitterpunch Remote</title>
|
11
|
+
<style>
|
12
|
+
body {
|
13
|
+
display: flex;
|
14
|
+
flex-direction: column;
|
15
|
+
margin: 0;
|
16
|
+
padding: 0;
|
17
|
+
height: 100%;
|
18
|
+
width: 100%;
|
19
|
+
background-color: black;
|
20
|
+
}
|
21
|
+
#message {
|
22
|
+
flex: 1;
|
23
|
+
font-size: 16px;
|
24
|
+
}
|
25
|
+
input[type=button] {
|
26
|
+
flex: 4;
|
27
|
+
margin: 5px;
|
28
|
+
padding: 0;
|
29
|
+
font-size: 104pt;
|
30
|
+
font-weight: bolder;
|
31
|
+
background-color: #5e5959;
|
32
|
+
border: 6px solid #ccc;
|
33
|
+
border-radius: 32px;
|
34
|
+
white-space:normal;
|
35
|
+
-webkit-appearance: none;
|
36
|
+
}
|
37
|
+
input.active {
|
38
|
+
color: white;
|
39
|
+
font-size: 64pt;
|
40
|
+
background-color: #5e2524;
|
41
|
+
}
|
42
|
+
body.focused #message {
|
43
|
+
flex: 2;
|
44
|
+
}
|
45
|
+
body.focused input[type=button] {
|
46
|
+
flex: 3;
|
47
|
+
margin-bottom: 325px;
|
48
|
+
font-size: 64pt;
|
49
|
+
}
|
50
|
+
|
51
|
+
@media screen and (max-width: 370px) and (orientation:portrait) {
|
52
|
+
input[type=button] {
|
53
|
+
font-size: 64pt;
|
54
|
+
}
|
55
|
+
input.active {
|
56
|
+
font-size: 52pt;
|
57
|
+
}
|
58
|
+
body.focused #message {
|
59
|
+
flex: 6;
|
60
|
+
}
|
61
|
+
body.focused input[type=button] {
|
62
|
+
flex: 4;
|
63
|
+
margin-bottom: 250px;
|
64
|
+
font-size: 20pt;
|
65
|
+
border-radius: 6px;
|
66
|
+
padding: 0;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
@media screen and (max-width: 650px) and (orientation:landscape) {
|
70
|
+
input[type=button] {
|
71
|
+
font-size: 84pt;
|
72
|
+
}
|
73
|
+
input.active {
|
74
|
+
font-size: 56pt;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
</style>
|
78
|
+
<script type="text/javascript">
|
79
|
+
function get(url, callback) {
|
80
|
+
var xmlHttp = new XMLHttpRequest();
|
81
|
+
xmlHttp.onreadystatechange = function() {
|
82
|
+
if (xmlHttp.readyState == 4) {
|
83
|
+
if(xmlHttp.status == 200) {
|
84
|
+
if(typeof(callback) == 'function') {
|
85
|
+
callback(xmlHttp.responseText);
|
86
|
+
}
|
87
|
+
}
|
88
|
+
else {
|
89
|
+
console.log(xmlHttp.responseText);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
xmlHttp.open("GET", url, true);
|
94
|
+
xmlHttp.send(null);
|
95
|
+
}
|
96
|
+
function activate() {
|
97
|
+
var msg = document.getElementById("message");
|
98
|
+
var btn = document.getElementById("btn");
|
99
|
+
var txt = msg.value;
|
100
|
+
btn.className = "active";
|
101
|
+
btn.value = "Say Cheese!";
|
102
|
+
|
103
|
+
setTimeout(function(){
|
104
|
+
btn.className = "";
|
105
|
+
btn.value = "Push Me";
|
106
|
+
msg.value = "";
|
107
|
+
},3000);
|
108
|
+
|
109
|
+
if (txt == "") {
|
110
|
+
get('/photo');
|
111
|
+
}
|
112
|
+
else {
|
113
|
+
get('/photo?message='+txt);
|
114
|
+
}
|
115
|
+
}
|
116
|
+
function shrink() {
|
117
|
+
var body = document.getElementsByTagName("body")[0];
|
118
|
+
body.className = "focused";
|
119
|
+
}
|
120
|
+
function expand() {
|
121
|
+
var body = document.getElementsByTagName("body")[0];
|
122
|
+
body.className = "";
|
123
|
+
}
|
124
|
+
</script>
|
125
|
+
</head>
|
126
|
+
<body>
|
127
|
+
<textarea id="message" class="item" placeholder="Enter a message if you like..." onclick="shrink()" onblur="expand()"></textarea>
|
128
|
+
<input type="button" id="btn" class="item" value="Push Me" onClick="activate()" />
|
129
|
+
</body>
|
130
|
+
<script>
|
131
|
+
// really truly disable the shit out of pinch-to-zoom
|
132
|
+
var lastTouchEnd = 0;
|
133
|
+
document.documentElement.addEventListener('touchend', function (event) {
|
134
|
+
var now = (new Date()).getTime();
|
135
|
+
if (now - lastTouchEnd <= 300) {
|
136
|
+
event.preventDefault();
|
137
|
+
}
|
138
|
+
lastTouchEnd = now;
|
139
|
+
}, false);
|
140
|
+
|
141
|
+
document.documentElement.addEventListener('touchstart', function (event) {
|
142
|
+
if (event.touches.length > 1) {
|
143
|
+
event.preventDefault();
|
144
|
+
}
|
145
|
+
}, false);
|
146
|
+
</script>
|
147
|
+
</html>
|
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twitterpunch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Ford
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: twitter
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: oauth
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: colorize
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubygame
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
description: "Twitterpunch\n===============\n\nTwitterpunch is designed to work with
|
@@ -134,6 +134,8 @@ files:
|
|
134
134
|
- lib/twitterpunch/configuration.rb
|
135
135
|
- lib/twitterpunch/logger.rb
|
136
136
|
- lib/twitterpunch/poster.rb
|
137
|
+
- lib/twitterpunch/queue.rb
|
138
|
+
- lib/twitterpunch/remote.rb
|
137
139
|
- lib/twitterpunch/sprite.rb
|
138
140
|
- lib/twitterpunch/streamer.rb
|
139
141
|
- lib/twitterpunch/viewer.rb
|
@@ -144,6 +146,9 @@ files:
|
|
144
146
|
- resources/Twitterpunch.workflow/Contents/document.wflow
|
145
147
|
- resources/say.vbs
|
146
148
|
- resources/tweet_sent.wav
|
149
|
+
- views/index.erb
|
150
|
+
- public/touch-icon-ipad-retina.png
|
151
|
+
- public/touch-icon-iphone-retina.png
|
147
152
|
homepage: http://binford2k.com
|
148
153
|
licenses: []
|
149
154
|
metadata: {}
|
@@ -162,17 +167,17 @@ require_paths:
|
|
162
167
|
- lib
|
163
168
|
required_ruby_version: !ruby/object:Gem::Requirement
|
164
169
|
requirements:
|
165
|
-
- -
|
170
|
+
- - ">="
|
166
171
|
- !ruby/object:Gem::Version
|
167
172
|
version: '0'
|
168
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
174
|
requirements:
|
170
|
-
- -
|
175
|
+
- - ">="
|
171
176
|
- !ruby/object:Gem::Version
|
172
177
|
version: '0'
|
173
178
|
requirements: []
|
174
179
|
rubyforge_project:
|
175
|
-
rubygems_version: 2.0.14
|
180
|
+
rubygems_version: 2.0.14.1
|
176
181
|
signing_key:
|
177
182
|
specification_version: 4
|
178
183
|
summary: A simple tool to automate the posting and streaming of PhotoBooth shots over
|