jugyo-termtter 0.7.0 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Manifest.txt +19 -9
- data/README.rdoc +20 -0
- data/Rakefile +3 -2
- data/bin/termtter +11 -5
- data/lib/filter/expand-tinyurl.rb +8 -1
- data/lib/filter/fib.rb +13 -0
- data/lib/filter/yhara.rb +1 -1
- data/lib/plugin/bomb.rb +26 -0
- data/lib/plugin/confirm.rb +24 -0
- data/lib/plugin/favorite.rb +25 -4
- data/lib/plugin/fib.rb +2 -1
- data/lib/plugin/filter.rb +1 -3
- data/lib/plugin/follow.rb +40 -0
- data/lib/plugin/group.rb +40 -0
- data/lib/plugin/history.rb +53 -0
- data/lib/plugin/keyword.rb +1 -1
- data/lib/plugin/log.rb +8 -2
- data/lib/plugin/plugin.rb +8 -8
- data/lib/plugin/quicklook.rb +36 -0
- data/lib/plugin/reload.rb +3 -0
- data/lib/plugin/shell.rb +2 -3
- data/lib/plugin/sl.rb +3 -0
- data/lib/plugin/spam.rb +7 -0
- data/lib/plugin/standard_plugins.rb +10 -1
- data/lib/plugin/stdout.rb +25 -10
- data/lib/plugin/uri-open.rb +10 -4
- data/lib/plugin/yhara.rb +142 -0
- data/lib/termtter.rb +173 -37
- data/run_termtter.rb +0 -2
- data/test/test_termtter.rb +0 -22
- metadata +38 -11
- data/lib/filter.rb +0 -28
data/lib/plugin/keyword.rb
CHANGED
@@ -12,5 +12,5 @@ end
|
|
12
12
|
# keyword.rb
|
13
13
|
# provides a keyword watching method
|
14
14
|
# example config
|
15
|
-
# configatron.timeline_format = '<%= color(time, 90) %> <%= color(status, s.has_keyword ? 4 : status_color) %> <%= color(id, 90) %>'
|
15
|
+
# configatron.plugins.stdout.timeline_format = '<%= color(time, 90) %> <%= color(status, s.has_keyword ? 4 : status_color) %> <%= color(id, 90) %>'
|
16
16
|
# configatron.plugins.keyword.keywords = [ /motemen/ ]
|
data/lib/plugin/log.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
module Termtter::Client
|
2
2
|
public_storage[:log] = []
|
3
|
+
configatron.plugins.log.set_default('max_size', 1/0.0)
|
3
4
|
|
4
5
|
add_help '/WORD', 'Search log for WORD'
|
5
6
|
|
6
|
-
add_hook do |statuses,event|
|
7
|
+
add_hook do |statuses, event|
|
7
8
|
case event
|
8
9
|
when :update_friends_timeline
|
9
10
|
public_storage[:log] += statuses
|
11
|
+
max_size = configatron.plugins.log.max_size
|
12
|
+
if public_storage[:log].size > max_size
|
13
|
+
public_storage[:log] = public_storage[:log][-max_size..-1]
|
14
|
+
end
|
15
|
+
public_storage[:log] = public_storage[:log].uniq.sort_by{|a| a.created_at} if statuses.first
|
10
16
|
end
|
11
17
|
end
|
12
18
|
|
13
|
-
add_command %r'^/(.+)' do |m,t|
|
19
|
+
add_command %r'^/(.+)' do |m, t|
|
14
20
|
pat = Regexp.new(m[1])
|
15
21
|
statuses = public_storage[:log].select { |s| s.text =~ pat }
|
16
22
|
call_hooks(statuses, :list_friends_timeline, t)
|
data/lib/plugin/plugin.rb
CHANGED
@@ -16,21 +16,21 @@ module Termtter::Client
|
|
16
16
|
|
17
17
|
add_help 'plugins', 'Show list of plugins'
|
18
18
|
add_command /^plugins$/ do |m, t|
|
19
|
-
puts public_storage[:plugins].join("\n")
|
19
|
+
puts public_storage[:plugins].sort.join("\n")
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.find_plugin_candidates(a, b)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
public_storage[:plugins].grep(/^#{Regexp.quote a}/i)
|
27
|
-
end.
|
28
|
-
map {|u| b % u }
|
23
|
+
public_storage[:plugins].
|
24
|
+
grep(/^#{Regexp.quote a}/i).
|
25
|
+
map {|u| b % u }
|
29
26
|
end
|
30
27
|
|
31
28
|
add_completion do |input|
|
32
|
-
|
29
|
+
case input
|
30
|
+
when /^(plugin)\s+(.+)/
|
33
31
|
find_plugin_candidates $2, "#{$1} %s"
|
32
|
+
when /^(plugin)\s+$/
|
33
|
+
public_storage[:plugins].sort
|
34
34
|
else
|
35
35
|
%w[ plugin plugins ].grep(/^#{Regexp.quote input}/)
|
36
36
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'pathname'
|
4
|
+
require 'tmpdir'
|
5
|
+
|
6
|
+
configatron.plugins.quicklook.set_default(:quicklook_tmpdir, "#{Dir.tmpdir}/termtter-quicklook-tmpdir")
|
7
|
+
tmpdir = Pathname.new(configatron.plugins.quicklook.quicklook_tmpdir)
|
8
|
+
tmpdir.mkdir unless tmpdir.exist?
|
9
|
+
|
10
|
+
def quicklook(url)
|
11
|
+
tmpdir = Pathname.new(configatron.plugins.quicklook.quicklook_tmpdir)
|
12
|
+
path = tmpdir + Pathname.new(url).basename
|
13
|
+
|
14
|
+
Thread.new do
|
15
|
+
open(path, 'w') do |f|
|
16
|
+
f.write(open(url).read)
|
17
|
+
end
|
18
|
+
system("qlmanage -p #{path} > /dev/null 2>&1")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Termtter::Client
|
23
|
+
add_command %r'^(?:quicklook|ql)\s+(\w+)$' do |m,t|
|
24
|
+
id = m[1]
|
25
|
+
status = t.show(id).first
|
26
|
+
|
27
|
+
if (status)
|
28
|
+
uris = URI.regexp.match(status.text).to_a
|
29
|
+
quicklook(uris.first) unless uris.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# quicklook.rb
|
35
|
+
# TODO:
|
36
|
+
# Close quicklook window automatically.
|
data/lib/plugin/shell.rb
CHANGED
data/lib/plugin/sl.rb
ADDED
data/lib/plugin/spam.rb
ADDED
@@ -32,6 +32,14 @@ module Termtter::Client
|
|
32
32
|
call_hooks(t.show(m[1]), :show, t)
|
33
33
|
end
|
34
34
|
|
35
|
+
# TODO: Change colors when remaining_hits is low.
|
36
|
+
# TODO: Simmulate remaining_hits.
|
37
|
+
add_command /^(limit|lm)\s*$/ do |m, t|
|
38
|
+
limit = t.get_rate_limit_status
|
39
|
+
puts "=> #{limit.remaining_hits}/#{limit.hourly_limit}"
|
40
|
+
end
|
41
|
+
|
42
|
+
|
35
43
|
add_command /^pause\s*$/ do |m, t|
|
36
44
|
pause
|
37
45
|
end
|
@@ -50,6 +58,7 @@ exit,e Exit
|
|
50
58
|
help Print this help message
|
51
59
|
list,l List the posts in your friends timeline
|
52
60
|
list,l USERNAME List the posts in the the given user's timeline
|
61
|
+
limit,lm Show the API limit status
|
53
62
|
pause Pause updating
|
54
63
|
update,u TEXT Post a new message
|
55
64
|
resume Resume updating
|
@@ -124,7 +133,7 @@ show ID Show a single status
|
|
124
133
|
end
|
125
134
|
|
126
135
|
add_completion do |input|
|
127
|
-
standard_commands = %w[exit help list pause update resume replies search show]
|
136
|
+
standard_commands = %w[exit help list pause update resume replies search show limit]
|
128
137
|
case input
|
129
138
|
when /^(list|l)?\s+(.*)/
|
130
139
|
find_user_candidates $2, "#{$1} %s"
|
data/lib/plugin/stdout.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
|
+
require 'highline'
|
1
2
|
require 'erb'
|
2
3
|
|
3
|
-
configatron.set_default(
|
4
|
+
configatron.plugins.stdout.set_default(
|
5
|
+
:colors,
|
6
|
+
[:white, :red, :green, :yellow, :blue, :magenta, :cyan])
|
7
|
+
configatron.plugins.stdout.set_default(
|
4
8
|
:timeline_format,
|
5
9
|
'<%= color(time, 90) %> <%= color(status, status_color) %> <%= color(id, 90) %>')
|
6
10
|
|
7
|
-
|
11
|
+
$highline = HighLine.new
|
12
|
+
|
13
|
+
if win?
|
8
14
|
require 'kconv'
|
9
15
|
def color(str, num)
|
10
16
|
str.to_s.tosjis
|
@@ -13,21 +19,24 @@ if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
|
|
13
19
|
STDOUT.puts(str.tosjis)
|
14
20
|
end
|
15
21
|
else
|
16
|
-
def color(str,
|
17
|
-
|
22
|
+
def color(str, value)
|
23
|
+
case value
|
24
|
+
when String, Symbol
|
25
|
+
$highline.color(str, value)
|
26
|
+
else
|
27
|
+
"\e[#{value}m#{str}\e[0m"
|
28
|
+
end
|
18
29
|
end
|
19
30
|
end
|
20
31
|
|
21
32
|
Termtter::Client.add_hook do |statuses, event|
|
22
|
-
colors = %w(0 31 32 33 34 35 36 91 92 93 94 95 96)
|
23
|
-
|
24
33
|
case event
|
25
34
|
when :update_friends_timeline, :list_friends_timeline, :list_user_timeline, :show, :replies
|
26
35
|
unless statuses.empty?
|
27
36
|
statuses.reverse! if event == :update_friends_timeline
|
28
37
|
statuses.each do |s|
|
29
38
|
text = s.text
|
30
|
-
status_color = colors[s.user_screen_name.hash % colors.size]
|
39
|
+
status_color = configatron.plugins.stdout.colors[s.user_screen_name.hash % configatron.plugins.stdout.colors.size]
|
31
40
|
status = "#{s.user_screen_name}: #{text}"
|
32
41
|
if s.in_reply_to_status_id
|
33
42
|
status += " (reply to #{s.in_reply_to_status_id})"
|
@@ -43,18 +52,24 @@ Termtter::Client.add_hook do |statuses, event|
|
|
43
52
|
|
44
53
|
id = s.id
|
45
54
|
|
46
|
-
puts ERB.new(configatron.timeline_format).result(binding)
|
55
|
+
puts ERB.new(configatron.plugins.stdout.timeline_format).result(binding)
|
47
56
|
end
|
48
57
|
end
|
49
58
|
when :search
|
50
59
|
statuses.each do |s|
|
51
60
|
text = s.text
|
52
|
-
status_color = colors[s.user_screen_name.hash % colors.size]
|
61
|
+
status_color = configatron.plugins.stdout.colors[s.user_screen_name.hash % configatron.plugins.stdout.colors.size]
|
53
62
|
|
54
63
|
status = "#{s.user_screen_name}: #{text}"
|
55
64
|
time = "(#{s.created_at.strftime('%m-%d %H:%M')})"
|
56
65
|
id = s.id
|
57
|
-
puts ERB.new(configatron.timeline_format).result(binding)
|
66
|
+
puts ERB.new(configatron.plugins.stdout.timeline_format).result(binding)
|
58
67
|
end
|
59
68
|
end
|
60
69
|
end
|
70
|
+
|
71
|
+
# stdout.rb
|
72
|
+
# output statuses to stdout
|
73
|
+
# example config
|
74
|
+
# configatron.plugins.stdout.colors = [:white, :red, :green, :yellow, :blue, :magenta, :cyan]
|
75
|
+
# configatron.plugins.stdout.timeline_format = '<%= color(time, 90) %> <%= color(status, status_color) %> <%= color(id, 90) %>'
|
data/lib/plugin/uri-open.rb
CHANGED
@@ -10,11 +10,17 @@ module Termtter::Client
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.open_uri(uri)
|
13
|
-
|
14
|
-
|
15
|
-
system 'firefox', uri
|
13
|
+
unless configatron.plugins.uri_open.browser.nil?
|
14
|
+
system configatron.plugins.uri_open.browser, uri
|
16
15
|
else
|
17
|
-
|
16
|
+
case RUBY_PLATFORM
|
17
|
+
when /linux/
|
18
|
+
system 'firefox', uri
|
19
|
+
when /mswin(?!ce)|mingw|bccwin/
|
20
|
+
system 'explorer', uri
|
21
|
+
else
|
22
|
+
system 'open', uri
|
23
|
+
end
|
18
24
|
end
|
19
25
|
end
|
20
26
|
|
data/lib/plugin/yhara.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
if RUBY_VERSION < "1.8.7"
|
2
|
+
class Array
|
3
|
+
def choice
|
4
|
+
at(rand(size))
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# based on new-harizon.rb
|
10
|
+
module Yharian
|
11
|
+
|
12
|
+
VOICES =
|
13
|
+
%w(Agnes Albert Bad\ News Bahh Bells Boing Bruce Bubbles Cellos Deranged Fred Hysterical Junior Kathy Pipe\ Organ Princess Ralph Trinoids Vicki Victoria Whisper Zarvox)
|
14
|
+
|
15
|
+
|
16
|
+
class Speaker
|
17
|
+
attr_reader :name
|
18
|
+
def initialize(name)
|
19
|
+
@name = name
|
20
|
+
end
|
21
|
+
|
22
|
+
def talk(context)
|
23
|
+
n = 7
|
24
|
+
words = (0..rand(n)).map { %w[y hara].choice }.
|
25
|
+
inject {|r, e| r + (rand < 0.97 ? ' ' : ', ') + e }
|
26
|
+
eos = %w(? ? . . . . . . . . !).choice
|
27
|
+
[Remark.new(self,words, eos)]
|
28
|
+
end
|
29
|
+
|
30
|
+
def voice(context = nil)
|
31
|
+
@name
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
class Alex < Speaker
|
38
|
+
def initialize
|
39
|
+
super 'Alex'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Vicki < Speaker
|
44
|
+
def initialize
|
45
|
+
super 'Vicki'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Yhara < Speaker
|
50
|
+
def initialize
|
51
|
+
super 'yhara'
|
52
|
+
end
|
53
|
+
|
54
|
+
def voice(context = nil)
|
55
|
+
VOICES.choise
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Jenifer < Speaker
|
60
|
+
ARABIAN = %w[ايران نيست]
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
super 'jenifer'
|
64
|
+
end
|
65
|
+
|
66
|
+
def talk(context)
|
67
|
+
words = (0..rand(3)). map { ARABIAN.choice }.join(' ')
|
68
|
+
[Remark.new(self,words, '')]
|
69
|
+
end
|
70
|
+
|
71
|
+
def voice(context = nil)
|
72
|
+
'Princess'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Remark
|
77
|
+
attr_reader :speaker, :words, :eos, :pronounciation
|
78
|
+
|
79
|
+
def initialize(speaker, words, eos, options = {})
|
80
|
+
@speaker = speaker
|
81
|
+
@words = words
|
82
|
+
@eos = eos # end of text : "?" or "." or "!"
|
83
|
+
@pronounciation = options[:pronounciation] || text
|
84
|
+
end
|
85
|
+
|
86
|
+
def text
|
87
|
+
@words + @eos
|
88
|
+
end
|
89
|
+
|
90
|
+
def interrogative?
|
91
|
+
@eos == '?'
|
92
|
+
end
|
93
|
+
|
94
|
+
def display
|
95
|
+
puts "#{@speaker.name}: #{text}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def say(context = nil)
|
99
|
+
Kernel.say pronounciation, :voice => @speaker.voice(context)
|
100
|
+
end
|
101
|
+
|
102
|
+
def correct?(s)
|
103
|
+
s.gsub(/[^yhar]/,'') == @words.gsub(/[^yhar]/,'')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
@@context = []
|
108
|
+
@@speakers = [Alex.new, Vicki.new]
|
109
|
+
|
110
|
+
def self.text
|
111
|
+
if ( @@context.last && Yhara === @@context.last.speaker && rand < 0.25 ) || rand < 0.01
|
112
|
+
speaker = Jenifer.new
|
113
|
+
elsif @@context.last && @@context.last.words =~ /y hara/ and @@context.last.interrogative? and rand < 0.25
|
114
|
+
speaker = Yhara.new
|
115
|
+
else
|
116
|
+
speaker = @@speakers[rand(2)]
|
117
|
+
end
|
118
|
+
|
119
|
+
remark = speaker.talk(@@context).first
|
120
|
+
@@context.push remark
|
121
|
+
remark.text
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
module Termtter::Client
|
126
|
+
|
127
|
+
add_help 'yhara', 'Post a new Yharian sentence'
|
128
|
+
|
129
|
+
add_command /^(yhara|y)\s*/ do |m, t|
|
130
|
+
text = Yharian::text
|
131
|
+
unless text.empty?
|
132
|
+
t.update_status(text)
|
133
|
+
puts "=> #{text}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# yhara.rb
|
139
|
+
# post a new yharian sentence
|
140
|
+
# example:
|
141
|
+
# > yhara
|
142
|
+
# => hara y y hara.
|
data/lib/termtter.rb
CHANGED
@@ -3,21 +3,25 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|
3
3
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'json'
|
6
|
+
require 'net/https'
|
6
7
|
require 'open-uri'
|
7
8
|
require 'cgi'
|
8
9
|
require 'readline'
|
9
10
|
require 'enumerator'
|
10
11
|
require 'parsedate'
|
11
12
|
require 'configatron'
|
12
|
-
require 'filter'
|
13
13
|
|
14
14
|
if RUBY_VERSION < '1.8.7'
|
15
15
|
class Array
|
16
|
-
def take(n)
|
16
|
+
def take(n) self[0...n] end
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
def win?
|
21
|
+
RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
|
22
|
+
end
|
23
|
+
|
24
|
+
if win?
|
21
25
|
require 'kconv'
|
22
26
|
module Readline
|
23
27
|
alias :old_readline :readline
|
@@ -30,6 +34,17 @@ end
|
|
30
34
|
|
31
35
|
configatron.set_default(:update_interval, 300)
|
32
36
|
configatron.set_default(:prompt, '> ')
|
37
|
+
configatron.set_default(:enable_ssl, false)
|
38
|
+
configatron.proxy.set_default(:port, '8080')
|
39
|
+
|
40
|
+
# FIXME: we need public_storage all around the script
|
41
|
+
module Termtter
|
42
|
+
module Client
|
43
|
+
def self.public_storage
|
44
|
+
@@public_storage ||= {}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
33
48
|
|
34
49
|
def plugin(s)
|
35
50
|
require "plugin/#{s}"
|
@@ -37,32 +52,76 @@ end
|
|
37
52
|
|
38
53
|
def filter(s)
|
39
54
|
load "filter/#{s}.rb"
|
55
|
+
rescue LoadError
|
56
|
+
raise
|
57
|
+
else
|
58
|
+
Termtter::Client.public_storage[:filters] = []
|
59
|
+
Termtter::Client.public_storage[:filters] << s
|
60
|
+
true
|
40
61
|
end
|
41
62
|
|
42
63
|
# FIXME: delete this method after the major version up
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
64
|
+
unless defined? original_require
|
65
|
+
alias original_require require
|
66
|
+
def require(s)
|
67
|
+
if %r|^termtter/(.*)| =~ s
|
68
|
+
puts "[WARNING] use plugin '#{$1}' instead of require"
|
69
|
+
puts " Such a legacy .termtter file will not be supported until version 1.0.0"
|
70
|
+
s = "plugin/#{$1}"
|
71
|
+
end
|
72
|
+
original_require s
|
49
73
|
end
|
50
|
-
original_require s
|
51
74
|
end
|
52
75
|
|
53
76
|
module Termtter
|
54
|
-
VERSION = '0.7.
|
77
|
+
VERSION = '0.7.5'
|
55
78
|
APP_NAME = 'termtter'
|
56
79
|
|
80
|
+
class Connection
|
81
|
+
attr_reader :protocol, :port, :proxy_uri
|
82
|
+
|
83
|
+
def initialize
|
84
|
+
@proxy_host = configatron.proxy.host
|
85
|
+
@proxy_port = configatron.proxy.port
|
86
|
+
@proxy_user = configatron.proxy.user_name
|
87
|
+
@proxy_password = configatron.proxy.password
|
88
|
+
@proxy_uri = nil
|
89
|
+
@enable_ssl = configatron.enable_ssl
|
90
|
+
@protocol = "http"
|
91
|
+
@port = 80
|
92
|
+
|
93
|
+
unless @proxy_host.empty?
|
94
|
+
@http_class = Net::HTTP::Proxy(@proxy_host, @proxy_port,
|
95
|
+
@proxy_user, @proxy_password)
|
96
|
+
@proxy_uri = "http://" + @proxy_host + ":" + @proxy_port + "/"
|
97
|
+
else
|
98
|
+
@http_class = Net::HTTP
|
99
|
+
end
|
100
|
+
|
101
|
+
if @enable_ssl
|
102
|
+
@protocol = "https"
|
103
|
+
@port = 443
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def start(host, port, &block)
|
108
|
+
http = @http_class.new(host, port)
|
109
|
+
http.use_ssl = @enable_ssl
|
110
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
|
111
|
+
http.start(&block)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
57
115
|
class Twitter
|
58
116
|
|
59
117
|
def initialize(user_name, password)
|
60
118
|
@user_name = user_name
|
61
119
|
@password = password
|
120
|
+
@connection = Connection.new
|
62
121
|
end
|
63
122
|
|
64
123
|
def update_status(status)
|
65
|
-
|
124
|
+
@connection.start("twitter.com", @connection.port) do |http|
|
66
125
|
uri = '/statuses/update.xml'
|
67
126
|
http.request(post_request(uri), "status=#{CGI.escape(status)}&source=#{APP_NAME}")
|
68
127
|
end
|
@@ -70,13 +129,13 @@ module Termtter
|
|
70
129
|
end
|
71
130
|
|
72
131
|
def get_friends_timeline(since_id = nil)
|
73
|
-
uri =
|
132
|
+
uri = "#{@connection.protocol}://twitter.com/statuses/friends_timeline.json"
|
74
133
|
uri << "?since_id=#{since_id}" if since_id
|
75
134
|
return get_timeline(uri)
|
76
135
|
end
|
77
136
|
|
78
137
|
def get_user_timeline(screen_name)
|
79
|
-
return get_timeline("
|
138
|
+
return get_timeline("#{@connection.protocol}://twitter.com/statuses/user_timeline/#{screen_name}.json")
|
80
139
|
rescue OpenURI::HTTPError => e
|
81
140
|
puts "No such user: #{screen_name}"
|
82
141
|
nears = near_users(screen_name)
|
@@ -85,7 +144,7 @@ module Termtter
|
|
85
144
|
end
|
86
145
|
|
87
146
|
def search(query)
|
88
|
-
results = JSON.parse(open(
|
147
|
+
results = JSON.parse(open("#{@connection.protocol}://search.twitter.com/search.json?q=" + CGI.escape(query)).read, :proxy => @connection.proxy_uri)['results']
|
89
148
|
return results.map do |s|
|
90
149
|
status = Status.new
|
91
150
|
status.id = s['id']
|
@@ -97,15 +156,15 @@ module Termtter
|
|
97
156
|
end
|
98
157
|
|
99
158
|
def show(id)
|
100
|
-
return get_timeline("
|
159
|
+
return get_timeline("#{@connection.protocol}://twitter.com/statuses/show/#{id}.json")
|
101
160
|
end
|
102
161
|
|
103
162
|
def replies
|
104
|
-
return get_timeline("
|
163
|
+
return get_timeline("#{@connection.protocol}://twitter.com/statuses/replies.json")
|
105
164
|
end
|
106
165
|
|
107
166
|
def get_timeline(uri)
|
108
|
-
data = JSON.parse(open(uri, :http_basic_authentication => [
|
167
|
+
data = JSON.parse(open(uri, :http_basic_authentication => [user_name, password], :proxy => @connection.proxy_uri).read)
|
109
168
|
data = [data] unless data.instance_of? Array
|
110
169
|
return data.map do |s|
|
111
170
|
status = Status.new
|
@@ -121,16 +180,48 @@ module Termtter
|
|
121
180
|
end
|
122
181
|
end
|
123
182
|
|
183
|
+
# note: APILimit.reset_time_in_seconds == APILimit.reset_time.to_i
|
184
|
+
APILIMIT = Struct.new("APILimit", :reset_time, :reset_time_in_seconds, :remaining_hits, :hourly_limit)
|
185
|
+
def get_rate_limit_status
|
186
|
+
uri = 'http://twitter.com/account/rate_limit_status.json'
|
187
|
+
data = JSON.parse(open(uri, :http_basic_authentication => [user_name, password], :proxy => @connection.proxy_uri).read)
|
188
|
+
|
189
|
+
reset_time = Time.parse(data['reset_time'])
|
190
|
+
reset_time_in_seconds = data['reset_time_in_seconds'].to_i
|
191
|
+
|
192
|
+
APILIMIT.new(reset_time, reset_time_in_seconds, data['remaining_hits'], data['hourly_limit'])
|
193
|
+
end
|
194
|
+
|
195
|
+
alias :api_limit :get_rate_limit_status
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def user_name
|
200
|
+
unless @user_name.instance_of? String
|
201
|
+
@user_name = Readline.readline('user name: ', false)
|
202
|
+
end
|
203
|
+
@user_name
|
204
|
+
end
|
205
|
+
|
206
|
+
def password
|
207
|
+
unless @password.instance_of? String
|
208
|
+
system 'stty -echo'
|
209
|
+
@password = Readline.readline('password: ', false)
|
210
|
+
system 'stty echo'
|
211
|
+
puts
|
212
|
+
end
|
213
|
+
@password
|
214
|
+
end
|
215
|
+
|
124
216
|
def near_users(screen_name)
|
125
217
|
Client::public_storage[:users].select {|user|
|
126
218
|
/#{user}/i =~ screen_name || /#{screen_name}/i =~ user
|
127
219
|
}.join(', ')
|
128
220
|
end
|
129
|
-
private :near_users
|
130
221
|
|
131
222
|
def post_request(uri)
|
132
223
|
req = Net::HTTP::Post.new(uri)
|
133
|
-
req.basic_auth(
|
224
|
+
req.basic_auth(user_name, password)
|
134
225
|
req.add_field('User-Agent', 'Termtter http://github.com/jugyo/termtter')
|
135
226
|
req.add_field('X-Twitter-Client', 'Termtter')
|
136
227
|
req.add_field('X-Twitter-Client-URL', 'http://github.com/jugyo/termtter')
|
@@ -144,6 +235,7 @@ module Termtter
|
|
144
235
|
@@hooks = []
|
145
236
|
@@commands = {}
|
146
237
|
@@completions = []
|
238
|
+
@@filters = []
|
147
239
|
@@helps = []
|
148
240
|
|
149
241
|
class << self
|
@@ -159,6 +251,12 @@ module Termtter
|
|
159
251
|
@@commands[regex] = block
|
160
252
|
end
|
161
253
|
|
254
|
+
def add_macro(r, s)
|
255
|
+
add_command(r) do |m, t|
|
256
|
+
call_commands(s % m, t)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
162
260
|
def clear_commands
|
163
261
|
@@commands.clear
|
164
262
|
end
|
@@ -179,6 +277,27 @@ module Termtter
|
|
179
277
|
@@helps.clear
|
180
278
|
end
|
181
279
|
|
280
|
+
def add_filter(&filter)
|
281
|
+
@@filters << filter
|
282
|
+
end
|
283
|
+
|
284
|
+
def clear_filters
|
285
|
+
@@filters.clear
|
286
|
+
end
|
287
|
+
|
288
|
+
# memo: each filter must return Array of Status
|
289
|
+
def apply_filters(statuses)
|
290
|
+
filtered = statuses
|
291
|
+
@@filters.each do |f|
|
292
|
+
filtered = f.call(filtered)
|
293
|
+
end
|
294
|
+
filtered
|
295
|
+
rescue => e
|
296
|
+
puts "Error: #{e}"
|
297
|
+
puts e.backtrace.join("\n")
|
298
|
+
statuses
|
299
|
+
end
|
300
|
+
|
182
301
|
Readline.basic_word_break_characters= "\t\n\"\\'`><=;|&{("
|
183
302
|
Readline.completion_proc = proc {|input|
|
184
303
|
@@completions.map {|completion|
|
@@ -186,10 +305,6 @@ module Termtter
|
|
186
305
|
}.flatten.compact
|
187
306
|
}
|
188
307
|
|
189
|
-
def public_storage
|
190
|
-
@@public_storage ||= {}
|
191
|
-
end
|
192
|
-
|
193
308
|
def call_hooks(statuses, event, tw)
|
194
309
|
statuses = apply_filters(statuses)
|
195
310
|
@@hooks.each do |h|
|
@@ -231,6 +346,8 @@ module Termtter
|
|
231
346
|
end
|
232
347
|
|
233
348
|
def exit
|
349
|
+
call_hooks([], :exit, nil)
|
350
|
+
@@main_thread.kill
|
234
351
|
@@update_thread.kill
|
235
352
|
@@input_thread.kill
|
236
353
|
end
|
@@ -240,7 +357,9 @@ module Termtter
|
|
240
357
|
initialized = false
|
241
358
|
@@pause = false
|
242
359
|
tw = Termtter::Twitter.new(configatron.user_name, configatron.password)
|
360
|
+
call_hooks([], :initialize, tw)
|
243
361
|
|
362
|
+
@@input_thread = nil
|
244
363
|
@@update_thread = Thread.new do
|
245
364
|
since_id = nil
|
246
365
|
loop do
|
@@ -251,9 +370,16 @@ module Termtter
|
|
251
370
|
unless statuses.empty?
|
252
371
|
since_id = statuses[0].id
|
253
372
|
end
|
373
|
+
print "\e[1K\e[0G" if !statuses.empty? && !win?
|
254
374
|
call_hooks(statuses, :update_friends_timeline, tw)
|
255
375
|
initialized = true
|
256
|
-
|
376
|
+
@@input_thread.kill if @@input_thread && !statuses.empty?
|
377
|
+
rescue OpenURI::HTTPError => e
|
378
|
+
if e.message == '401 Unauthorized'
|
379
|
+
puts 'Could not login'
|
380
|
+
puts 'plese check your account settings'
|
381
|
+
exit!
|
382
|
+
end
|
257
383
|
rescue => e
|
258
384
|
puts "Error: #{e}"
|
259
385
|
puts e.backtrace.join("\n")
|
@@ -270,8 +396,25 @@ module Termtter
|
|
270
396
|
Readline.__send__("#{vi_or_emacs}_editing_mode")
|
271
397
|
end
|
272
398
|
|
273
|
-
|
274
|
-
|
399
|
+
begin
|
400
|
+
stty_save = `stty -g`.chomp
|
401
|
+
trap("INT") { system "stty", stty_save; exit }
|
402
|
+
rescue Errno::ENOENT
|
403
|
+
end
|
404
|
+
|
405
|
+
@@main_thread = Thread.new do
|
406
|
+
loop do
|
407
|
+
@@input_thread = create_input_thread(tw)
|
408
|
+
@@input_thread.join
|
409
|
+
end
|
410
|
+
end
|
411
|
+
@@main_thread.join
|
412
|
+
end
|
413
|
+
|
414
|
+
def create_input_thread(tw)
|
415
|
+
Thread.new do
|
416
|
+
erb = ERB.new(configatron.prompt)
|
417
|
+
while buf = Readline.readline(erb.result(tw.__send__(:binding)), true)
|
275
418
|
begin
|
276
419
|
call_commands(buf, tw)
|
277
420
|
rescue CommandNotFound => e
|
@@ -283,18 +426,8 @@ module Termtter
|
|
283
426
|
end
|
284
427
|
end
|
285
428
|
end
|
286
|
-
|
287
|
-
begin
|
288
|
-
stty_save = `stty -g`.chomp
|
289
|
-
trap("INT") { system "stty", stty_save; exit }
|
290
|
-
rescue Errno::ENOENT
|
291
|
-
end
|
292
|
-
|
293
|
-
@@input_thread.join
|
294
429
|
end
|
295
|
-
|
296
430
|
end
|
297
|
-
|
298
431
|
end
|
299
432
|
|
300
433
|
class CommandNotFound < StandardError; end
|
@@ -307,6 +440,9 @@ module Termtter
|
|
307
440
|
attr_accessor attr.to_sym
|
308
441
|
end
|
309
442
|
|
443
|
+
def eql?(other); self.id == other.id end
|
444
|
+
def hash; self.id end
|
445
|
+
|
310
446
|
def english?
|
311
447
|
self.class.english?(self.text)
|
312
448
|
end
|