termtter 1.7.2 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/README.rdoc +1 -0
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/bin/termtter +1 -0
- data/bin/termtter_frame +134 -0
- data/lib/plugins/aa.rb +44 -0
- data/lib/plugins/another_prompt.rb +42 -41
- data/lib/plugins/appendtitle.rb +82 -0
- data/lib/plugins/ar.rb +11 -8
- data/lib/plugins/async.rb +3 -2
- data/lib/plugins/capital_update.rb +12 -0
- data/lib/plugins/channel.rb +149 -0
- data/lib/plugins/clock.rb +10 -0
- data/lib/plugins/defaults/command_line.rb +8 -0
- data/lib/plugins/defaults/confirm.rb +1 -1
- data/lib/plugins/defaults/hashtag.rb +1 -1
- data/lib/plugins/defaults/keyword.rb +11 -0
- data/lib/plugins/defaults/list.rb +32 -6
- data/lib/plugins/defaults/standard_commands.rb +135 -52
- data/lib/plugins/defaults/standard_completion.rb +14 -0
- data/lib/plugins/defaults/stdout.rb +59 -27
- data/lib/plugins/defaults/user.rb +32 -0
- data/lib/plugins/draft.rb +9 -12
- data/lib/plugins/easy_post.rb +5 -0
- data/lib/plugins/event_invoked_at.rb +23 -0
- data/lib/plugins/expand-tinyurl.rb +13 -20
- data/lib/plugins/favotter.rb +9 -9
- data/lib/plugins/footer.rb +9 -12
- data/lib/plugins/friends.rb +0 -26
- data/lib/plugins/grass.rb +2 -4
- data/lib/plugins/growl.rb +47 -0
- data/lib/plugins/gyazo.rb +16 -18
- data/lib/plugins/hi.rb +31 -10
- data/lib/plugins/http_server.rb +3 -2
- data/lib/plugins/irc_gw.rb +71 -17
- data/lib/plugins/line.rb +10 -0
- data/lib/plugins/linefeed.rb +6 -1
- data/lib/plugins/list_switch.rb +76 -0
- data/lib/plugins/nop.rb +9 -0
- data/lib/plugins/notify-send.rb +1 -1
- data/lib/plugins/notify-send2.rb +25 -16
- data/lib/plugins/notify-send3.rb +16 -13
- data/lib/plugins/nuance.rb +29 -0
- data/lib/plugins/open_url.rb +1 -5
- data/lib/plugins/random.rb +2 -6
- data/lib/plugins/reply_sound.rb +33 -0
- data/lib/plugins/say.rb +2 -2
- data/lib/plugins/storage/sqlite3.rb +1 -1
- data/lib/plugins/story.rb +44 -0
- data/lib/plugins/tinyurl.rb +50 -29
- data/lib/plugins/translate_tweet.rb +38 -0
- data/lib/plugins/web.rb +27 -0
- data/lib/termtter.rb +8 -4
- data/lib/termtter/client.rb +17 -21
- data/lib/termtter/command.rb +35 -13
- data/lib/termtter/config.rb +13 -0
- data/lib/termtter/config_template.erb +3 -2
- data/lib/termtter/default_config.rb +2 -2
- data/lib/termtter/event.rb +69 -0
- data/lib/termtter/hook.rb +6 -1
- data/lib/termtter/hookable.rb +9 -1
- data/lib/termtter/httppool.rb +17 -9
- data/lib/termtter/optparse.rb +11 -1
- data/lib/termtter/rubytter_proxy.rb +21 -5
- data/spec/plugins/capital_update_spec.rb +9 -0
- data/spec/plugins/fib_spec.rb +2 -4
- data/spec/plugins/hi_spec.rb +9 -0
- data/spec/plugins/tinyurl_spec.rb +78 -0
- data/spec/termtter/client_spec.rb +5 -12
- data/spec/termtter/command_spec.rb +22 -10
- data/spec/termtter/config_spec.rb +23 -0
- data/spec/termtter/event_spec.rb +129 -0
- data/spec/termtter/optparse_spec.rb +2 -2
- data/spec/termtter/rubytter_proxy_spec.rb +1 -1
- metadata +39 -8
- data/bin/kill_termtter +0 -22
- data/lib/plugins/defaults/users.rb +0 -63
- data/lib/plugins/pause.rb +0 -3
- data/test/friends_timeline.json +0 -5
- data/test/search.json +0 -8
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
@@ -8,10 +8,11 @@ begin
|
|
8
8
|
gem.summary = "Terminal based Twitter client."
|
9
9
|
gem.description = "Termtter is a terminal based Twitter client."
|
10
10
|
gem.executables = ["termtter"]
|
11
|
-
gem.add_dependency("
|
11
|
+
gem.add_dependency("json", ">= 1.1.3")
|
12
12
|
gem.add_dependency("highline", ">= 1.5.0")
|
13
13
|
gem.add_dependency("termcolor", ">= 1.0.0")
|
14
14
|
gem.add_dependency("rubytter", ">= 0.11.0")
|
15
|
+
gem.add_dependency("notify", ">= 0.2.1")
|
15
16
|
gem.authors = %w(jugyo ujihisa)
|
16
17
|
gem.email = 'jugyo.org@gmail.com'
|
17
18
|
gem.homepage = 'http://termtter.org/'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.8.0
|
data/bin/termtter
CHANGED
data/bin/termtter_frame
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# vim: filetype=ruby
|
4
|
+
|
5
|
+
self_file =
|
6
|
+
if File.symlink?(__FILE__)
|
7
|
+
require 'pathname'
|
8
|
+
Pathname.new(__FILE__).realpath
|
9
|
+
else
|
10
|
+
__FILE__
|
11
|
+
end
|
12
|
+
$:.unshift(File.dirname(self_file) + "/../lib")
|
13
|
+
ARGV.unshift '-m' unless ARGV.include?('-m') #monochrome
|
14
|
+
|
15
|
+
require 'singleton'
|
16
|
+
require 'termtter'
|
17
|
+
|
18
|
+
module Termtter
|
19
|
+
class CommandLine
|
20
|
+
include Singleton
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def start
|
24
|
+
instance.start
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
instance.stop
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def start
|
33
|
+
start_input_thread
|
34
|
+
end
|
35
|
+
|
36
|
+
def stop
|
37
|
+
@input_thread.kill if @input_thread
|
38
|
+
end
|
39
|
+
|
40
|
+
def call(command_text)
|
41
|
+
# Example:
|
42
|
+
# t.register_hook(:post_all, :point => :prepare_command) do |s|
|
43
|
+
# "update #{s}"
|
44
|
+
# end
|
45
|
+
Client.get_hooks('prepare_command').each {|hook|
|
46
|
+
command_text = hook.call(command_text)
|
47
|
+
}
|
48
|
+
Client.execute(command_text)
|
49
|
+
rescue CommandNotFound => e
|
50
|
+
hooks = Client.get_hooks('command_not_found')
|
51
|
+
raise e if hooks.empty?
|
52
|
+
hooks.each {|hook|
|
53
|
+
hook.call(command_text)
|
54
|
+
}
|
55
|
+
rescue TimeoutError
|
56
|
+
puts TermColor.parse("<red>Time out :(</red>")
|
57
|
+
end
|
58
|
+
|
59
|
+
def prompt
|
60
|
+
prompt_text = config.prompt
|
61
|
+
Client.get_hooks('prepare_prompt').each {|hook|
|
62
|
+
prompt_text = hook.call(prompt_text)
|
63
|
+
}
|
64
|
+
prompt_text
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def start_input_thread
|
70
|
+
@input_thread = Thread.new do
|
71
|
+
buf = ""
|
72
|
+
while buf = gets.chomp
|
73
|
+
begin
|
74
|
+
call(buf)
|
75
|
+
rescue Exception => e
|
76
|
+
Client.handle_error(e)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@input_thread.join
|
81
|
+
end
|
82
|
+
|
83
|
+
def do_completion(input)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Client.register_hook(:initialize_command_line, :point => :init_command_line) do
|
88
|
+
CommandLine.start
|
89
|
+
end
|
90
|
+
|
91
|
+
Client.register_hook(:finalize_command_line, :point => :exit) do
|
92
|
+
CommandLine.stop
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module Termtter::Client
|
97
|
+
class << self
|
98
|
+
attr_reader :task_manager
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
config.system.disable_plugins ||= []
|
104
|
+
config.system.disable_plugins << :command_line
|
105
|
+
|
106
|
+
config.__freeze__(:user_name) unless config.user_name.empty?
|
107
|
+
Termtter::Client.parse_options
|
108
|
+
Termtter::Client.load_config
|
109
|
+
Termtter::Client.setup_task_manager
|
110
|
+
|
111
|
+
Termtter::Client.load_plugins
|
112
|
+
Termtter::Client.eval_init_block
|
113
|
+
config.__unfreeze__(:user_name)
|
114
|
+
Termtter::API.setup
|
115
|
+
|
116
|
+
config.system.eval_scripts.each do |script|
|
117
|
+
begin
|
118
|
+
eval script
|
119
|
+
rescue Exception => e
|
120
|
+
Termtter::Client.handle_error(e)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
config.system.run_commands.each {|cmd| execute(cmd) }
|
125
|
+
|
126
|
+
unless config.system.cmd_mode
|
127
|
+
Termtter::Client.task_manager.run()
|
128
|
+
Termtter::Client.call_hooks(:initialize)
|
129
|
+
Termtter::Client.add_task(:name => :call_hooks_after_launched, :after => 1) do
|
130
|
+
Termtter::Client.call_hooks(:launched)
|
131
|
+
end
|
132
|
+
Termtter::Client.call_hooks(:init_command_line)
|
133
|
+
end
|
134
|
+
|
data/lib/plugins/aa.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module AAMaker
|
4
|
+
# view-source:http://amachang.sakura.ne.jp/misc/aamaker/
|
5
|
+
def self.random(n)
|
6
|
+
rand(n).to_i
|
7
|
+
end
|
8
|
+
def self.randSelect(array)
|
9
|
+
return array[random(array.length)]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.make
|
13
|
+
rinkaku = randSelect([['(', ')'], ['(', ')'], ['|', '|'], ['|', '|']]);
|
14
|
+
otete = randSelect([['', '', '', '', ''], ['', '', 'm', '', ''], ['', '', 'ლ', '', ''], ['ლ', '', '', 'ლ', ''], ['', '「', '', '', '「'], ['', ' つ', '', '', 'つ'], ['', ' ', '', '', 'o彡゚'], ['', 'n', '', '', 'η'], ['', '∩', '', '∩', ''], ['∩', '', '', '', '∩'], ['ヽ', '', '', '', 'ノ'], ['┐', '', '', '', '┌'], ['╮', '', '', '', '╭'], ['<', '', '', '', '/'], ['╰', '', '', ' ', ''], ['o', '', '', '', 'o'], ['o', '', '', '', 'ツ'], ['', '', '', '', 'ノシ']]);
|
15
|
+
omeme = randSelect([['◕', '◕'], ['╹', '╹'], ['>', '<'], ['^', '^'], ['・', '・'], ['´・', '・`'], ['`・', '・´'], ['´', '`'], ['≧', '≦'], ['゚', '゚'], ['\'', '\''], ['・ิ', '・ิ'], ['❛', '❛'], ['⊙', '⊙'], [' ̄', ' ̄'], ['◕ˇ', 'ˇ◕']]);
|
16
|
+
okuti = randSelect(['ω', '∀', '▽', '△', 'Д' , '□', '~', 'ー', 'ェ', 'ρ', 'o']);
|
17
|
+
hoppe = randSelect([['', ''], ['*', ''], ['', '*'], ['', '#'], ['#', ''], ['✿', ''], ['', '✿'], ['', ';'], [';', ''], ['。', '。'], ['。', ''], ['', '。'], ['▰', '▰'], ['', '▰'], ['▰', ''], ['๑', '๑'], ['', '๑'], ['๑', '']]);
|
18
|
+
|
19
|
+
text = [
|
20
|
+
otete[0],
|
21
|
+
rinkaku[0],
|
22
|
+
otete[1] || (otete[3] ? '' : hoppe[0]),
|
23
|
+
omeme[0],
|
24
|
+
otete[2] || okuti,
|
25
|
+
omeme[1],
|
26
|
+
otete[3] || (otete[1] ? '' : hoppe[1]),
|
27
|
+
rinkaku[1],
|
28
|
+
otete[4]
|
29
|
+
].join('')
|
30
|
+
|
31
|
+
text
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Termtter::Client.register_command(
|
36
|
+
:name => :aa,
|
37
|
+
:exec_proc => lambda {|arg|
|
38
|
+
name = Termtter::Client.normalize_as_user_name(arg)
|
39
|
+
command = name.length > 0 ? "u @#{name} #{AAMaker.make}" : "u #{AAMaker.make}"
|
40
|
+
Termtter::Client.execute command
|
41
|
+
},
|
42
|
+
:help => ["aa [(Optinal) USER]", "Post a AA"]
|
43
|
+
)
|
44
|
+
|
@@ -1,53 +1,55 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
|
3
2
|
config.plugins.another_prompt.
|
4
|
-
set_default(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
3
|
+
set_default(
|
4
|
+
:shortcut_setting,
|
5
|
+
{
|
6
|
+
':' => '',
|
7
|
+
'd' => 'direct',
|
8
|
+
'D' => 'delete',
|
9
|
+
'f' => 'fib',
|
10
|
+
'F' => 'favorite',
|
11
|
+
'l' => 'list',
|
12
|
+
'o' => 'open',
|
13
|
+
'p' => 'profile',
|
14
|
+
'R' => 'reply',
|
15
|
+
's' => 'search',
|
16
|
+
't' => 'retweet',
|
17
|
+
'u' => 'update',
|
18
|
+
'c' => lambda do
|
19
|
+
system('clear')
|
20
|
+
end,
|
21
|
+
'L' => lambda do
|
22
|
+
puts '-' *
|
23
|
+
`stty size`.chomp.
|
24
|
+
sub(/^\d+\s(\d+)$/, '\\1').to_i
|
25
|
+
end,
|
26
|
+
'q' => lambda do
|
27
|
+
Termtter::Client.execute('quit')
|
28
|
+
end,
|
29
|
+
'r' => lambda do
|
30
|
+
Termtter::Client.execute('replies')
|
31
|
+
end,
|
32
|
+
'?' => lambda do
|
33
|
+
Termtter::Client.execute('help')
|
34
|
+
end,
|
35
|
+
"\e" => lambda do
|
36
|
+
system('screen', '-X', 'eval', 'copy')
|
37
|
+
end
|
38
|
+
})
|
38
39
|
|
39
40
|
Termtter::Client.plug 'curry'
|
40
41
|
|
41
42
|
module Termtter::Client
|
42
|
-
add_task(
|
43
|
-
|
44
|
-
|
43
|
+
add_task(
|
44
|
+
:name => :auto_reload,
|
45
|
+
:interval => config.update_interval,
|
46
|
+
:after => config.update_interval) do
|
45
47
|
begin
|
46
48
|
execute('reload')
|
47
49
|
rescue Exception => e
|
48
50
|
handle_error(e)
|
49
51
|
end
|
50
|
-
|
52
|
+
end
|
51
53
|
|
52
54
|
register_hook(
|
53
55
|
:name => :auto_reload_init,
|
@@ -58,8 +60,7 @@ module Termtter::Client
|
|
58
60
|
rescue Exception => e
|
59
61
|
handle_error(e)
|
60
62
|
end
|
61
|
-
}
|
62
|
-
)
|
63
|
+
})
|
63
64
|
end
|
64
65
|
|
65
66
|
module Termtter
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'uri'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'timeout'
|
6
|
+
require 'memcache'
|
7
|
+
require 'digest/sha1'
|
8
|
+
|
9
|
+
module Termtter::Client
|
10
|
+
config.plugins.appendtitle.set_default(:timeout, 30)
|
11
|
+
config.plugins.appendtitle.set_default(:cache_expire, 3600 * 24 * 7)
|
12
|
+
config.plugins.appendtitle.set_default(:memcached_server, 'localhost:11211')
|
13
|
+
|
14
|
+
def self.memcache_client
|
15
|
+
@memcache_client ||= MemCache.new(config.plugins.appendtitle.memcached_server)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.fetch_title(uri)
|
19
|
+
return unless uri
|
20
|
+
key = %w{ termtter plugins appendtitle title}.push(Digest::SHA1.hexdigest(uri)).join('-')
|
21
|
+
if v = memcache_client.get(key)
|
22
|
+
logger.debug "appendtitle: cache hit for #{uri}"
|
23
|
+
return v
|
24
|
+
end
|
25
|
+
|
26
|
+
memcache_client.set(key, '', config.plugins.appendtitle.cache_expire) # to avoid duplicate fetch
|
27
|
+
begin
|
28
|
+
logger.debug "appendtitle: fetching title for #{uri}"
|
29
|
+
source = Nokogiri(open(uri).read)
|
30
|
+
if source and source.at('title')
|
31
|
+
title = source.at('title').text
|
32
|
+
memcache_client.set(key, title, config.plugins.appendtitle.cache_expire)
|
33
|
+
return title
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
rescue Timeout::Error
|
37
|
+
nil
|
38
|
+
rescue
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
register_hook(
|
44
|
+
:name => :appendtitle,
|
45
|
+
:point => :filter_for_output,
|
46
|
+
:exec_proc => lambda do |statuses, event|
|
47
|
+
threads = statuses.map do |status|
|
48
|
+
Thread.new{
|
49
|
+
begin
|
50
|
+
status.text.gsub!(URI.regexp(['http', 'https'])) {|uri|
|
51
|
+
title = fetch_title(uri)
|
52
|
+
title = title.gsub(/\n/, '').gsub(/\s+/, ' ') if title
|
53
|
+
body_for_compare = status.text.gsub(/\n/, '').gsub(/\s+/, ' ')
|
54
|
+
if title and not (
|
55
|
+
body_for_compare.include? title or
|
56
|
+
body_for_compare.include? title[0..(title.length/2)] or
|
57
|
+
body_for_compare.include? title[(title.length/2)..-1]) # XXX: heuristic!!!
|
58
|
+
"#{uri} (#{title})"
|
59
|
+
else
|
60
|
+
uri
|
61
|
+
end
|
62
|
+
}
|
63
|
+
rescue
|
64
|
+
end
|
65
|
+
}
|
66
|
+
end
|
67
|
+
begin
|
68
|
+
# wait for join or timeout
|
69
|
+
timeout(config.plugins.appendtitle.timeout) {
|
70
|
+
threads.each{ |t| t.join }
|
71
|
+
}
|
72
|
+
rescue Timeout::Error
|
73
|
+
logger.error 'appendtitle: timeout'
|
74
|
+
end
|
75
|
+
|
76
|
+
statuses
|
77
|
+
end
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
# appendtitle.rb:
|
82
|
+
# append title for uri.
|
data/lib/plugins/ar.rb
CHANGED
@@ -70,10 +70,12 @@ module Termtter
|
|
70
70
|
end
|
71
71
|
|
72
72
|
register_command(:db_search, :alias => :ds) do |arg|
|
73
|
-
statuses = Status.find(
|
74
|
-
|
75
|
-
|
76
|
-
|
73
|
+
statuses = Status.find(
|
74
|
+
:all,
|
75
|
+
:conditions => [
|
76
|
+
'text LIKE :l',
|
77
|
+
{:l => "%" + arg + "%"}],
|
78
|
+
:limit => 20)
|
77
79
|
output(statuses, :db_search)
|
78
80
|
end
|
79
81
|
|
@@ -86,10 +88,11 @@ module Termtter
|
|
86
88
|
|
87
89
|
register_command(:db_list) do |arg|
|
88
90
|
user_name = normalize_as_user_name(arg)
|
89
|
-
statuses = Status.find(
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
statuses = Status.find(
|
92
|
+
:all,
|
93
|
+
:joins => "LEFT OUTER JOIN users ON users.uid = statuses.user_id",
|
94
|
+
:conditions => ['users.screen_name = :u',{:u => user_name}],
|
95
|
+
:limit => 20)
|
93
96
|
output(statuses, :db_search)
|
94
97
|
end
|
95
98
|
|
data/lib/plugins/async.rb
CHANGED
@@ -4,10 +4,11 @@ module Termtter::Client
|
|
4
4
|
:alias => :a,
|
5
5
|
:help => ['async COMMAND', 'asynchronously execute the command'],
|
6
6
|
:completion => lambda {|cmd, arg|
|
7
|
-
commands.
|
7
|
+
commands.
|
8
|
+
map {|name, command| command.complement(arg) }.
|
8
9
|
flatten.
|
9
10
|
compact.
|
10
|
-
map{|i| "#{cmd} #{i}"}
|
11
|
+
map {|i| "#{cmd} #{i}" }
|
11
12
|
},
|
12
13
|
:exec => lambda {|arg|
|
13
14
|
@task_manager.invoke_later do
|