Module: Termtter::Client

Includes:
Hookable
Defined in:
lib/plugins/hi.rb,
lib/plugins/ar.rb,
lib/plugins/db.rb,
lib/plugins/l2.rb,
lib/plugins/me.rb,
lib/plugins/sl.rb,
lib/plugins/erb.rb,
lib/plugins/foo.rb,
lib/plugins/log.rb,
lib/plugins/say.rb,
lib/plugins/web.rb,
lib/plugins/bomb.rb,
lib/plugins/cool.rb,
lib/plugins/open.rb,
lib/plugins/async.rb,
lib/plugins/clear.rb,
lib/plugins/clock.rb,
lib/plugins/curry.rb,
lib/plugins/draft.rb,
lib/plugins/eject.rb,
lib/plugins/en2ja.rb,
lib/plugins/grass.rb,
lib/plugins/group.rb,
lib/plugins/mongo.rb,
lib/plugins/mongo.rb,
lib/plugins/quote.rb,
lib/plugins/shell.rb,
lib/plugins/timer.rb,
lib/plugins/toppo.rb,
lib/plugins/train.rb,
lib/plugins/wassr.rb,
lib/plugins/whois.rb,
lib/plugins/yhara.rb,
lib/plugins/yonda.rb,
lib/plugins/fibyou.rb,
lib/plugins/filter.rb,
lib/plugins/hatebu.rb,
lib/plugins/ignore.rb,
lib/plugins/irc_gw.rb,
lib/plugins/otsune.rb,
lib/plugins/otsune.rb,
lib/plugins/primes.rb,
lib/plugins/random.rb,
lib/plugins/reblog.rb,
lib/plugins/ruby-v.rb,
lib/plugins/scrape.rb,
lib/plugins/source.rb,
lib/plugins/stream.rb,
lib/plugins/trends.rb,
lib/plugins/w3mimg.rb,
lib/plugins/capture.rb,
lib/plugins/fluentd.rb,
lib/plugins/friends.rb,
lib/plugins/history.rb,
lib/plugins/jakigan.rb,
lib/plugins/md5pass.rb,
lib/plugins/nyancat.rb,
lib/plugins/outputz.rb,
lib/plugins/replace.rb,
lib/plugins/storage.rb,
lib/plugins/twitpic.rb,
lib/termtter/client.rb,
lib/plugins/addspace.rb,
lib/plugins/countter.rb,
lib/plugins/defaults.rb,
lib/plugins/encoding.rb,
lib/plugins/favotter.rb,
lib/plugins/open_url.rb,
lib/plugins/paranoid.rb,
lib/plugins/saykanji.rb,
lib/plugins/uri-open.rb,
lib/plugins/ar-single.rb,
lib/plugins/easy_post.rb,
lib/plugins/mashimaro.rb,
lib/plugins/mashimaro.rb,
lib/plugins/mashimaro.rb,
lib/plugins/mashimaro.rb,
lib/plugins/quicklook.rb,
lib/plugins/fib_filter.rb,
lib/plugins/multi_post.rb,
lib/plugins/quick_exit.rb,
lib/plugins/search_url.rb,
lib/plugins/searchline.rb,
lib/plugins/typable_id.rb,
lib/plugins/appendtitle.rb,
lib/plugins/http_server.rb,
lib/plugins/list_switch.rb,
lib/plugins/multi_reply.rb,
lib/plugins/reduce_text.rb,
lib/plugins/user_stream.rb,
lib/plugins/command_plus.rb,
lib/plugins/command_plus.rb,
lib/plugins/multi_output.rb,
lib/plugins/multi_output.rb,
lib/plugins/custom_prompt.rb,
lib/plugins/defaults/eval.rb,
lib/plugins/defaults/exec.rb,
lib/plugins/defaults/list.rb,
lib/plugins/defaults/user.rb,
lib/plugins/github-issues.rb,
lib/plugins/reply_retweet.rb,
lib/plugins/screen-notify.rb,
lib/plugins/system_status.rb,
lib/plugins/update_editor.rb,
lib/plugins/url_shortener.rb,
lib/plugins/another_prompt.rb,
lib/plugins/capital_update.rb,
lib/plugins/defaults/alias.rb,
lib/plugins/defaults/cache.rb,
lib/plugins/list_with_opts.rb,
lib/plugins/defaults/plugin.rb,
lib/plugins/defaults/stdout.rb,
lib/plugins/defaults/switch.rb,
lib/plugins/defaults/system.rb,
lib/plugins/defaults/hashtag.rb,
lib/plugins/defaults/keyword.rb,
lib/plugins/defaults/retweet.rb,
lib/plugins/event_invoked_at.rb,
lib/plugins/hatebu_and_update.rb,
lib/plugins/defaults/expand_tco_url.rb,
lib/plugins/defaults/standard_commands.rb,
lib/plugins/defaults/standard_completion.rb

Overview

config.plugins.alias.aliases = { :e => ‘exit’, :q => ‘exit’}

Defined Under Namespace

Modules: Jakigan Classes: DirectMessage, SearchEvent, UserSearchEvent

Constant Summary collapse

SEARCH_URI =
'search.twitter.com'
W3MIMG =
'/usr/lib/w3m/w3mimgdisplay'
LINEH =
PH / CH
PROTOCOLS =
%w(http https)

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Hookable

included

Class Attribute Details

.commandsObject (readonly)

Returns the value of attribute commands.



21
22
23
# File 'lib/termtter/client.rb', line 21

def commands
  @commands
end

.loggerObject (readonly)

Returns the value of attribute logger.



21
22
23
# File 'lib/termtter/client.rb', line 21

def logger
  @logger
end

.task_managerObject (readonly)

Returns the value of attribute task_manager.



21
22
23
# File 'lib/termtter/client.rb', line 21

def task_manager
  @task_manager
end

Class Method Details

.add_command(name) ⇒ Object



82
83
84
85
86
87
88
89
90
# File 'lib/termtter/client.rb', line 82

def add_command(name)
  if block_given?
    command = Command.new(:name => name)
    yield command
    @commands[command.name] = command
  else
    raise ArgumentError, 'must be given block to set parameters'
  end
end

.add_filter(&b) ⇒ Object



52
53
54
55
# File 'lib/termtter/client.rb', line 52

def add_filter(&b)
  warn "add_filter method will be removed. Use Termtter::Client.register_hook(:name => ..., :point => :filter_for_output, :exec => ... ) instead."
  @filters << b
end

.add_task(*arg, &block) ⇒ Object



216
217
218
# File 'lib/termtter/client.rb', line 216

def add_task(*arg, &block)
  @task_manager.add_task(*arg, &block)
end

.alias_command(arg) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/plugins/command_plus.rb', line 12

def alias_command(arg)
  original, new = arg.split(/\s+/)
  if @commands[original.to_sym]
    @commands[new.to_sym] = @commands[original.to_sym].clone
    @commands[new.to_sym].name    = new.to_sym
    @commands[new.to_sym].aliases = []
    @commands[new.to_sym].help    = ''
    puts "alias '#{original}' to '#{new}'."
  else
    raise "#{original} command is not found."
  end
end

.alive_thread?(name) ⇒ Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/plugins/stream.rb', line 50

def alive_thread?(name)
  config.plugins.stream.__send__(name).alive? rescue false
end

.apply_filters_for_hook(hook_name, statuses, event) ⇒ Object



149
150
151
152
153
# File 'lib/termtter/client.rb', line 149

def apply_filters_for_hook(hook_name, statuses, event)
  get_hooks(hook_name).inject(statuses) {|s, hook|
    hook.call(s, event)
  }
end

.clear_commandObject



92
93
94
# File 'lib/termtter/client.rb', line 92

def clear_command
  @commands.clear
end

.clear_filterObject



57
58
59
# File 'lib/termtter/client.rb', line 57

def clear_filter
  @filters.clear
end

.clear_lineObject



363
364
365
# File 'lib/termtter/client.rb', line 363

def clear_line
  print "\e[0G" + "\e[K" unless win?
end

.collect_hashtags(text) ⇒ Object



56
57
58
59
# File 'lib/plugins/defaults/standard_completion.rb', line 56

def self.collect_hashtags(text)
  text.force_encoding("UTF-8") if text.respond_to?(:force_encoding)
  public_storage[:hashtags_for_completion] += text.scan(/#([0-9A-Za-z_]+)/u).flatten
end

.command_exists?(text) ⇒ Boolean

Returns:

  • (Boolean)


196
197
198
# File 'lib/termtter/client.rb', line 196

def command_exists?(text)
  @commands.values.any? {|command| command.match?(text) }
end

.confirm(message, default_yes = true, &block) ⇒ Object



367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/termtter/client.rb', line 367

def confirm(message, default_yes = true, &block)
  pause # TODO: TaskManager から呼ばれるならこれいらないなぁ

  print "\"#{message.strip}\" "
  readline = Readline.readline(default_yes ? "[Y/n] " : "[N/y] ", false)
  result =
    if !!(/^$/ =~ readline)
      default_yes
    else
      !!(/^y/i =~ readline)
    end

  if result && block
    block.call
  end

  result
ensure
  resume # TODO: TaskManager から呼ばれるならこれいらないなぁ
end

.data_to_typable_id(data) ⇒ Object



75
76
77
78
# File 'lib/plugins/defaults/stdout.rb', line 75

def self.data_to_typable_id(data)
  id = config.plugins.stdout.typable_id_prefix +
    @typable_id_generator.get_id(data)
end

.default_loggerObject



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/termtter/client.rb', line 274

def default_logger
  logger = Logger.new(STDOUT)
  logger.formatter = lambda {|severity, time, progname, message|
    color =
      case severity
      when /^DEBUG/
        'blue'
      when /^INFO/
        'cyan'
      when /^WARN/
        'magenta'
      when /^ERROR/
        'red'
      when /^FATAL/
        'on_red'
      else
        'white'
      end
    TermColor.parse("<#{color}>" + TermColor.escape("[#{severity}] #{message}\n") + "</#{color}>")
  }
  logger
end

.delete_and_replace(recent, pattern_reg, replace, global) ⇒ Object



4
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
# File 'lib/plugins/replace.rb', line 4

def self.delete_and_replace(recent, pattern_reg, replace, global)
  new_text =
    if global
      recent.text.gsub(pattern_reg, replace)
    else
      recent.text.sub(pattern_reg, replace)
    end

  param =
    if recent.in_reply_to_status_id
      {:in_reply_to_status_id => recent.in_reply_to_status_id}
    else
      {}
    end

  if new_text == recent.text
    puts "It was not replaced."
    raise Termtter::CommandCanceled
  end

  if /^y?$/i !~ Readline.readline("\"replace #{new_text}\" [Y/n] ", false)
    puts 'canceled.'
    raise Termtter::CommandCanceled
  else
    result = Termtter::API.twitter.remove_status(recent.id)
    puts "deleted => #{result.text}"
    result = Termtter::API.twitter.update(new_text, param)
    puts "updated => #{result.text}"
  end
end

.delete_command(arg) ⇒ Object



4
5
6
7
8
9
10
# File 'lib/plugins/command_plus.rb', line 4

def delete_command(arg)
  if @commands.delete(arg.to_sym)
    puts "#{arg} command is deleted."
  else
    raise "#{arg} command is not found."
  end
end

.delete_output(name) ⇒ Object



10
11
12
# File 'lib/plugins/multi_output.rb', line 10

def delete_output(name)
  @outputs.delete(name)
end

.delete_task(key) ⇒ Object



220
221
222
# File 'lib/termtter/client.rb', line 220

def delete_task(name)
  @task_manager.delete_task(name)
end

.encode(text, encoding) ⇒ Object



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
# File 'lib/plugins/encoding.rb', line 5

def self.encode text, encoding
  return text unless encoding

  if RUBY_VERSION >= '1.9'
    begin
      text = text.encode encoding
    rescue
      # no encodings exception
    end
  else
    begin 
      require 'nkf'
    rescue
      return text
    end

    text = case encoding
           when 'utf-8'
             NKF.nkf('-w', text)
           when 'euc-jp'
             NKF.nkf('-e', text)
           when 'sjis'
             NKF.nkf('-s', text)
           else
             text
           end
  end
end

.english?(text) ⇒ Boolean

Returns:

  • (Boolean)


14
15
16
# File 'lib/plugins/en2ja.rb', line 14

def self.english?(text)
  /[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+/ !~ text
end

.eval_init_blockObject



306
307
308
# File 'lib/termtter/client.rb', line 306

def eval_init_block
  @init_block.call(self) if @init_block
end

.execute(text) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/termtter/client.rb', line 155

def execute(text)
  text = text.strip

  @task_manager.invoke_and_wait do
    # FIXME: This block can become Maybe Monad
    get_hooks("pre_command").each {|hook|
      break if text == nil # interrupt if hook returns nil
      text = hook.call(text)
    }
    return if text.empty?

    command = find_command(text)
    raise CommandNotFound, text unless command

    command_str, modified_arg = command.split_command_line(text)
    command_str.strip!
    modified_arg ||= ''

    # FIXME: This block can become Maybe Monad
    get_hooks("modify_arg_for_#{command.name.to_s}").each {|hook|
      break if modified_arg == false # interrupt if hook return false
      modified_arg.strip!
      modified_arg = hook.call(command_str, modified_arg) || ''
    }
    modified_arg.strip!

    begin
      call_hooks("pre_exec_#{command.name.to_s}", command, modified_arg)
      result = command.call(command_str, modified_arg, text) # exec command
      call_hooks("post_exec_#{command.name.to_s}", command_str, modified_arg, result)
      call_hooks("post_command", text)
    rescue CommandCanceled
      return false
    end
    return true
  end
rescue Timeout::Error
  call_hooks("timeout", text)
  raise
end

.exitObject



224
225
226
227
228
# File 'lib/termtter/client.rb', line 224

def exit
  puts 'finalizing...'
  call_hooks(:exit)
  @task_manager.kill
end

.expand_tco_urls!(text, urls) ⇒ Object



13
14
15
16
17
18
# File 'lib/plugins/defaults/expand_tco_url.rb', line 13

def self.expand_tco_urls!(text, urls)
  urls.each do |u|
    next unless u[:expanded_url]
    text.sub!(/#{Regexp.escape(u[:url])}/, u[:expanded_url])
  end
end

.fetch_title_data(uri) ⇒ Object

returns :uri | :uri | nil



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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/plugins/appendtitle.rb', line 12

def self.fetch_title_data(uri) # returns {:title, :uri} | {:uri} | nil
  return unless uri
  key = %w{ plugins appendtitle title-data}.push(Digest::SHA1.hexdigest(uri)).join('-')
  if v = memory_cache.get(key)
    logger.debug "appendtitle: cache hit for #{uri}"
    return v
  end

  memory_cache.set(key, {}, config.plugins.appendtitle.cache_expire) # to avoid duplicate fetch
  logger.debug "appendtitle: fetching title for #{uri}"
  data = {}
  uri_fetch = uri
  begin
    io = URI.parse(uri_fetch).read
    base_uri = io.base_uri.to_s
    base_uri = uri_fetch if base_uri.length > 1000
    data[:uri] = base_uri
    charset = io.scan(/charset="?([^\s"]*)/i).flatten.inject(Hash.new{0}){|a, b| a[b]+=1; a}.to_a.sort_by{|a|a[1]}.reverse.first[0] # XXX: scan charset from source
    begin # title
      source = Nokogiri(io, base_uri, charset)
      title = source.at('title').text rescue nil
      title ||= source.at('h1').text rescue nil
      title ||= source.at('h2').text rescue nil
      title = title.gsub(/\n/, '').gsub(/\s+/, ' ') if title
      data[:title] = title if title
    rescue
    end
    memory_cache.set(key, data, config.plugins.appendtitle.cache_expire)
    data
  rescue RuntimeError => error
    # example: redirection forbidden: http://bit.ly/gSarwN -> https://github.com/jugyo/termtter/commit/6e5fa4455a5117fb6c10bdf82bae52cfcf57a91f
    if error.message =~ /^redirection forbidden/
      logger.debug "appendtitle: #{error.message}"
      uri_fetch = error.message.split(/\s+/).last
      retry
    end
  rescue Timeout::Error, StandardError => error
    logger.debug "appendtitle: error #{uri}, #{error.class.to_s}: #{error.message}"
    nil
  end
end

.find_command(text) ⇒ Object



200
201
202
203
204
205
206
# File 'lib/termtter/client.rb', line 200

def find_command(text)
  @commands.
    values.
    select {|command| command.match?(text) }.
    sort_by {|command| command.name.to_s.split(' ').size }.
    last
end

.find_filter_candidates(a, b, filters) ⇒ Object



49
50
51
52
53
54
55
56
# File 'lib/plugins/filter.rb', line 49

def self.find_filter_candidates(a, b, filters)
  if a.empty?
    filters.to_a
  else
    filters.grep(/^#{Regexp.quote a}/i)
  end.
  map {|u| b % u }
end

.find_group_candidates(a, b) ⇒ Object



9
10
11
12
13
# File 'lib/plugins/group.rb', line 9

def self.find_group_candidates(a, b)
  config.plugins.group.groups.keys.map {|k| k.to_s}.
    grep(/^#{Regexp.quote a}/).
    map {|u| b % u }
end

.find_status_ids(text) ⇒ Object



585
586
587
# File 'lib/plugins/defaults/standard_commands.rb', line 585

def find_status_ids(text)
  public_storage[:status_ids].select {|id| /#{Regexp.quote(text)}/ =~ id.to_s }
end

.find_users(text) ⇒ Object



589
590
591
# File 'lib/plugins/defaults/standard_commands.rb', line 589

def find_users(text)
  public_storage[:users].select {|user| /^#{Regexp.quote(text)}/ =~ user }
end

.following_friendsObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/plugins/irc_gw.rb', line 16

def following_friends
  user_name = config.user_name
  frinends  = []
  last = nil
  begin
    puts "collecting friends (#{frinends.length})"
    last = Termtter::API::twitter.friends(:screen_name => user_name, :cursor => last ? last.next_cursor : -1)
    frinends += last.users
  rescue Timeout::Error, StandardError # XXX
    break
  end until last.next_cursor == 0
  puts "You have #{frinends.length} friends."
  Set.new(frinends.map(&:screen_name))
end

.formatted_help(helps) ⇒ Object



462
463
464
465
466
467
468
469
# File 'lib/plugins/defaults/standard_commands.rb', line 462

def self.formatted_help(helps)
  helps = helps.sort_by {|help| help[0] }
  width = helps.map {|n, _| n.size }.max
  space = 3
  helps.map {|name, desc|
    name.to_s.ljust(width + space) + desc.to_s
  }.join("\n")
end

.friends_or_followers_command(type, arg) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/plugins/defaults/standard_commands.rb', line 174

def self.friends_or_followers_command(type, arg)
  raise "type should be :friends or :followers" unless [:friends, :followers].include? type
  limit = 20
  if /\-([\d]+)/ =~ arg
    limit = $1.to_i
    arg = arg.gsub(/\-([\d]+)/, '')
  end
  arg.strip!
  user_name = arg.empty? ? config.user_name : arg
  users = get_friends_or_followers(type, user_name, limit)
  longest = users.map{ |u| u.screen_name.length}.max
  users.reverse.each{|user|
    padding = ' ' * (longest - user.screen_name.length)
    user_id = Termtter::Client.data_to_typable_id(user.id) rescue ''
    color = user.following ? config.plugins.stdout.colors.first : config.plugins.stdout.colors.last
    mark  = user.following ? '' : ''
    erbed_text = ERB.new(config.plugins.standard.one_line_profile_format).result(binding)
    puts TermColor.unescape(TermColor.parse(erbed_text))
  }
end

.gen_pass(master_pass) ⇒ Object



11
12
13
14
15
16
17
18
19
# File 'lib/plugins/md5pass.rb', line 11

def self.gen_pass(master_pass)
    salt = config.plugins.md5pass.salt
    len = config.plugins.md5pass.len
    times = config.plugins.md5pass.times
    url = "http://#{config.host}/"
    user = config.user_name
    str = (url + salt + user + master_pass) * (2 ** times);
    Base64.encode64(Digest::MD5.digest(str))[0, len]
end

.get_command(name) ⇒ Object

MEMO: attr_reader :commands してるからこれいらない気もする



97
98
99
# File 'lib/termtter/client.rb', line 97

def get_command(name)
  @commands[name]
end

.get_followers(user_name, max) ⇒ Object



139
140
141
# File 'lib/plugins/defaults/standard_commands.rb', line 139

def self.get_followers(user_name, max)
  self.get_friends_or_followers(:followers, user_name, max)
end

.get_friends(user_name, max) ⇒ Object



135
136
137
# File 'lib/plugins/defaults/standard_commands.rb', line 135

def self.get_friends(user_name, max)
  self.get_friends_or_followers(:friends, user_name, max)
end

.get_friends_or_followers(type, user_name, max) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/plugins/defaults/standard_commands.rb', line 143

def self.get_friends_or_followers(type, user_name, max)
  raise "type should be :friends or :followers" unless [:friends, :followers].include? type
  users = []
  cursor = -1
  begin
    tmp = Termtter::API::twitter.__send__(type, user_name, :cursor => cursor)
    cursor = tmp[:next_cursor]
    users += tmp[:users]
    puts "#{users.length}/#{max}" if max > 100
  rescue
    break
  end until (cursor.zero? or users.length > max)
  users.take(max)
end

.get_group_of(screen_name) ⇒ Object



15
16
17
# File 'lib/plugins/group.rb', line 15

def self.get_group_of(screen_name)
  config.plugins.group.groups.select{ |k, v| v.include? screen_name}.map{|a| a.first}
end

.handle_error(e) ⇒ Object



349
350
351
352
353
# File 'lib/termtter/client.rb', line 349

def handle_error(e)
  logger.error("#{e.class.to_s}: #{e.message}")
  logger.error(e.backtrace.join("\n")) if (e.backtrace and config.devel)
  get_hooks(:on_error).each {|hook| hook.call(e) }
end

.init(&block) ⇒ Object



297
298
299
# File 'lib/termtter/client.rb', line 297

def init(&block)
  @init_block = block
end

.input_editorObject



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/plugins/github-issues.rb', line 180

def self.input_editor(body = nil)
  file = Tempfile.new('termtter')
  editor = config.plugins.github_issues.editor
  file.write body if body
  file.close
  system("#{editor} #{file.path}")
  result = file.open.read
  file.flush
  file.close(false)
  result
end

.is_member?(status, group = nil) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
59
60
61
62
# File 'lib/plugins/group.rb', line 56

def self.is_member?(status, group = nil)
  if group
    config.plugins.group.groups[group].include? status.user.screen_name
  else
    config.plugins.group.groups.values.flatten.include? status.user.screen_name
  end
end

.kill_thread(name) ⇒ Object



46
47
48
49
# File 'lib/plugins/stream.rb', line 46

def kill_thread(name)
  config.plugins.stream.__send__(name).kill rescue nil
  config.plugins.stream.__assign__(name, nil)
end

.legacy_config_supportObject



243
244
245
246
247
248
249
250
# File 'lib/termtter/client.rb', line 243

def legacy_config_support
  case File.ftype(File.expand_path('~/.termtter'))
  when 'directory'
    # nop
  when 'file'
    move_legacy_config_file
  end
end

.list_name_to_slug(list_name) ⇒ Object



183
184
185
# File 'lib/plugins/defaults/list.rb', line 183

def self.list_name_to_slug(list_name)
  list_name[/([^\/]*)$/]
end

.load_configObject



230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/termtter/client.rb', line 230

def load_config
  legacy_config_support() if File.exist? Termtter::CONF_DIR
  unless File.exist?(Termtter::CONF_FILE)
    require 'termtter/config_setup'
    ConfigSetup.run
  end
  load Termtter::CONF_FILE
  unless config.dmsg_permission
    require 'termtter/config_setup'
    ConfigSetup.reauth
  end
end

.load_historyObject



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/plugins/history.rb', line 17

def self.load_history
  filename = File.expand_path(config.plugins.history.filename)
  keys = config.plugins.history.keys

  if File.exist?(filename)
    begin
      history = Marshal.load Zlib::Inflate.inflate(File.read(filename))
    rescue Zlib::BufError => e
      ui = create_highline
      delete = ui.ask("Unable to read #{filename}. Do you wish to remove it?")
      if delete =~ /^y/i
        if File.delete(filename) > 1
          puts "Removed #{filename}"
        end
      end
      history = nil
    end
    if history
      keys.each do |key|
        public_storage[key] = history[key] if history[key]
      end
      Readline::HISTORY.push *history[:history] if history[:history]
      puts "history loaded(#{File.size(filename)/1000}kb)"
    end
  end
end

.load_pluginsObject



301
302
303
304
# File 'lib/termtter/client.rb', line 301

def load_plugins
  plug 'defaults'
  plug config.system.load_plugins
end

.memory_cacheObject



48
49
50
# File 'lib/termtter/client.rb', line 48

def memory_cache
  @memory_cache ||= Termtter::MemoryCache.new
end

.mongo_dbObject



6
7
8
# File 'lib/plugins/mongo.rb', line 6

def mongo_db
  @mongo_db ||= Mongo::Connection.new('localhost', 27017, :pool_size => 5, :timeout => 5).db(config.plugins.mongo.db_name)
end

.move_legacy_config_fileObject

MEMO: This method will be removed in Termtter 2.0.0



253
254
255
256
257
258
259
260
261
# File 'lib/termtter/client.rb', line 253

def move_legacy_config_file
  FileUtils.mv(
    Termtter::CONF_DIR,
    File.expand_path('~/.termtter___'))
  Dir.mkdir(Termtter::CONF_DIR)
  FileUtils.mv(
    File.expand_path('~/.termtter___'),
    Termtter::CONF_FILE)
end

.normalize_as_user_name(text) ⇒ Object



581
582
583
# File 'lib/plugins/defaults/standard_commands.rb', line 581

def normalize_as_user_name(text)
  text.strip.sub(/^@/, '')
end

.notify(*args) ⇒ Object



145
146
147
# File 'lib/termtter/client.rb', line 145

def notify(*args)
  ::Notify.notify(*args)
end

.open_editor(path) ⇒ Object



40
41
42
43
# File 'lib/plugins/defaults/plugin.rb', line 40

def self.open_editor(path)
  # TODO: change to common method or use launchy
  system ENV['EDITOR'] || 'vim', path
end

.open_uri(uri) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/plugins/open_url.rb', line 6

def self.open_uri(uri)
  unless config.plugins.open_url.browser.empty?
    system config.plugins.open_url.browser, uri
  else
    case RUBY_PLATFORM
    when /linux/
      system 'xdg-open', uri
    when /mswin(?!ce)|mingw|bccwin/
      system 'explorer', uri
    else
      system 'open', uri
    end
  end
end

.output(statuses, event = :default) ⇒ Object

statuses => [status, status, …] status => {

  :id => status id,
  :created_at => created time,
  :user_id => user id,
  :name => user name,
  :screen_name => user screen_name,
  :source => source,
  :reply_to => reply_to status id,
  :text => status,
  :original_data => original data,
}


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/termtter/client.rb', line 121

def output(statuses, event = :default)
  return if statuses.nil? || statuses.empty?
  event = Termtter::Event.new(event) unless event.kind_of? Termtter::Event

  statuses = statuses.sort_by(&:id)
  call_hooks(:pre_filter, statuses, event)

  filtered = apply_filters_for_hook(:filter_for_output, statuses.map(&:clone), event)

  @filters.each do |f|  # TODO: code for compatibility. delete someday.
    # but... when is the "someday"?
    filtered = f.call(filtered, event)
  end

  call_hooks(:post_filter, filtered, event)
  get_hooks(:output).each do |hook|
    Termtter::Client.logger.debug { "output: call hook :output #{hook.inspect}" }
    hook.call(
      apply_filters_for_hook(:"filter_for_#{hook.name}", filtered, event),
      event)
  end
  Termtter::Client.logger.debug "output: call hook :output, done"
end

.output_favorites(target, threshold) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/plugins/favotter.rb', line 11

def output_favorites(target, threshold)
  favorites = parse("http://favotter.net/user/#{target}?mode=new&threshold=#{threshold}")

  public_storage[:favorited_ids].clear
  alphabet = '$a'
  max_amount_width = favorites.map {|f| now = f[2].to_s.size }.max
  favorites.reverse.each do |id, text, amount, users|
    public_storage[:favorited_ids][alphabet] = id
    color = fav_color(amount)
    fav   = "fav#{amount == 1 ? '' : 's'}"
    favorites_info = "(#{amount} #{fav})".rjust(max_amount_width + 7)
    format = "<GREEN>#{favorites_info} #{alphabet}</GREEN> <YELLOW>%s</YELLOW>: <#{color}>%s</#{color}>"
    values = [users.join(', '), CGI.escape(text)]
    puts CGI.unescape(TermColor.parse(format % values ))
    alphabet.succ!
  end
end

.parse_optionsObject



314
315
316
# File 'lib/termtter/client.rb', line 314

def parse_options
  Termtter::OptParser.parse!(ARGV)
end

.pauseObject



208
209
210
# File 'lib/termtter/client.rb', line 208

def pause
  @task_manager.pause
end

.plug(name, options = {}) ⇒ Object

call-seq:

plug :: Name -> (Hash) -> IO () where NAME = String | Symbol | [NAME]


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/termtter/client.rb', line 25

def plug(name, options = {})
  if Array === name # Obviously `name.respond_to?(:each)` is better, but for 1.8.6 compatibility we cannot.
    name.each {|i| plug(i, options) }
    return
  end

  name = name.to_s

  return if config.system.disable_plugins.include?(name.gsub('defaults/', ''))

  name_sym = name.gsub(/-/, '_').to_sym
  options.each do |key, value|
    config.plugins.__refer__(name_sym).__assign__(key.to_sym, value)
  end
  load "plugins/#{name}.rb"
rescue Exception => e
  Termtter::Client.handle_error(e)
end

.plugin_files(include_system_plugins = false) ⇒ Object



50
51
52
53
54
# File 'lib/plugins/defaults/plugin.rb', line 50

def self.plugin_files(include_system_plugins = false)
  files = Dir["#{Termtter::CONF_DIR}/plugins/*.rb"]
  files += Dir["#{File.expand_path(File.dirname(__FILE__))}/*.rb"] if include_system_plugins
  files
end

.plugin_listObject

plugin_list

IO ()



493
494
495
496
497
# File 'lib/plugins/defaults/standard_commands.rb', line 493

def self.plugin_list
  (Dir["#{File.dirname(__FILE__)}/../**/*.rb"] + Dir["#{Termtter::CONF_DIR}/plugins/**/*.rb"]).
    map {|f| File.expand_path(f).scan(/.*plugins\/(.*)\.rb/).flatten[0] }.
    sort
end

.post_quote(s, comment = nil) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/plugins/quote.rb', line 7

def self.post_quote(s, comment = nil)
  if s.user.protected && config.plugins.quote.confirm_protected &&
      !confirm("#{s.user.screen_name} is protected! Are you sure?", false)
    return
  end

  comment += ' ' unless comment.nil?
  text = ERB.new(config.plugins.quote.format).result(binding)
  Termtter::API.twitter.update(text)
  puts "=> #{text}"

  return text
end

.post_reply_retweet(s, comment = nil) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/plugins/reply_retweet.rb', line 7

def self.post_reply_retweet(s, comment = nil)
  if s.user.protected && config.plugins.reply_retweet.confirm_protected &&
      !confirm("#{s.user.screen_name} is protected! Are you sure?", false)
    return
  end

  text = s.text.gsub(/RT.+\z/, '')
  comment += ' ' unless comment.nil?
  text = ERB.new(config.plugins.reply_retweet.format).result(binding)
  Termtter::API.twitter.update(text)
  puts "=> #{text}"

  return text
end

.post_retweet(s, comment = nil) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/plugins/defaults/retweet.rb', line 15

def self.post_retweet(s, comment = nil)
  s[:user][:protected] and
    config.plugins.retweet.confirm_protected and
    !confirm("#{s.user.screen_name} is protected! Are you sure?", false) and
    return

  # NOTE: If it's possible, this plugin tries to
  #   use the default RT feature twitter provides.
  if comment.nil? && config.plugins.retweet.official_retweet
    begin
      Termtter::API.twitter.retweet(s.id)
      # TODO: Vimshell support
      puts TermColor.parse("<blue>=&gt; RT @#{s.user.screen_name}: #{s.text}</blue>")
      return
    rescue Rubytter::APIError  # XXX: just for transition period
      if $!.to_s == 'Not found'
        Termtter::Client.logger.warn "Failed official retweet. Set twitter langage to English in https://twitter.com/account/settings or set config.plugins.retweet.official_retweet to false."
      else
        raise
      end
    end
  end
  comment += ' ' unless comment.nil?
  rt_or_qt = (config.plugins.retweet.quotetweet and comment) ? 'QT' : 'RT'
  text = ERB.new(config.plugins.retweet.format).result(binding)
  params = config.plugins.retweet.as_reply ? {:in_reply_to_status_id => s.id} : {}
  Termtter::API.twitter.update(text, params)
  puts "=> #{text}"
end

.public_storageObject



44
45
46
# File 'lib/termtter/client.rb', line 44

def public_storage
  @public_storage ||= {}
end

.puts(message) ⇒ Object



14
15
16
17
18
# File 'lib/plugins/multi_output.rb', line 14

def puts message
  @outputs.each_value do |block|
    block.call(message)
  end
end

.register_alias(alias_name, command) ⇒ Object Also known as: alias



24
25
26
# File 'lib/plugins/defaults/alias.rb', line 24

def register_alias(alias_name, command)
  @aliases[alias_name.to_sym] = command.to_s
end

.register_command(arg, opts = {}, &block) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/termtter/client.rb', line 61

def register_command(arg, opts = {}, &block)
  command = case arg
    when Command
      arg
    when Hash
      Command.new(arg)
    when String, Symbol
      options = { :name => arg }
      options.merge!(opts)
      options[:exec_proc] = block
      Command.new(options)
    else
      raise ArgumentError, 'must be given Termtter::Command, Hash or String(Symbol) with block'
    end
  @commands[command.name] = command
end

.register_macro(name, macro, options = {}) ⇒ Object



101
102
103
104
105
106
107
# File 'lib/termtter/client.rb', line 101

def register_macro(name, macro, options = {})
  command = {
    :name => name.to_sym,
    :exec_proc => lambda {|arg| execute(macro % arg)}
  }.merge(options)
  register_command(command)
end

.register_output(as, &block) ⇒ Object



6
7
8
# File 'lib/plugins/multi_output.rb', line 6

def register_output(as, &block)
  @outputs[as] = block
end

.remove_alias(alias_name) ⇒ Object



29
30
31
# File 'lib/plugins/defaults/alias.rb', line 29

def remove_alias(alias_name)
  @aliases.delete alias_name.to_sym
end

.remove_command(name) ⇒ Object



78
79
80
# File 'lib/termtter/client.rb', line 78

def remove_command(name)
  commands.delete(name.to_sym)
end

.rescue_errorObject



355
356
357
358
359
360
361
# File 'lib/termtter/client.rb', line 355

def rescue_error
  begin
    yield
  rescue Exception => e
    handle_error(e)
  end
end

.resumeObject



212
213
214
# File 'lib/termtter/client.rb', line 212

def resume
  @task_manager.resume
end

.runObject



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/termtter/client.rb', line 322

def run
  parse_options
  show_splash unless config.system.cmd_mode
  setup_task_manager
  load_config
  load_plugins
  eval_init_block
  begin
    Termtter::API.setup
  rescue Rubytter::APIError => e
    handle_error(e)
    exit!
  end

  config.system.eval_scripts.each {|script| rescue_error { eval script }}
  config.system.run_commands.each {|cmd| rescue_error { execute(cmd) }}

  unless config.system.cmd_mode
    @task_manager.run()
    call_hooks(:initialize)
    add_task(:name => :call_hooks_after_launched, :after => 1) do
      call_hooks(:launched)
    end
    call_hooks(:init_command_line)
  end
end

.save_historyObject



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/plugins/history.rb', line 44

def self.save_history
  filename = File.expand_path(config.plugins.history.filename)
  keys = config.plugins.history.keys
  history = { }
  keys.each do |key|
    history[key] = public_storage[key]
  end
  max_of_history = config.plugins.history.max_of_history
  history[:history] = Readline::HISTORY.to_a.reverse.uniq.reverse
  if history[:history].size > max_of_history
    history[:history] = history[:history][-max_of_history..-1]
  end

  File.open(filename, 'w') do |f|
    f.write Zlib::Deflate.deflate(Marshal.dump(history))
  end
  puts "history saved(#{File.size(filename)/1000}kb)"
end

.scrape_group(group) ⇒ Object



14
15
16
17
# File 'lib/plugins/scrape.rb', line 14

def self.scrape_group(group)
  members = config.plugins.group.groups[group] || []
  scrape_members(members)
end

.scrape_members(members) ⇒ Object



5
6
7
8
9
10
11
12
# File 'lib/plugins/scrape.rb', line 5

def self.scrape_members(members)
  statuses = []
  members.each_with_index do |member, index|
    puts "member #{index+1}/#{members.size} #{member}"
    statuses += Termtter::API.twitter.user_timeline(:screen_name => member, :include_entities => 1)
  end
  statuses
end

.search_plugin_file(name, include_system_plugins = false) ⇒ Object



45
46
47
48
# File 'lib/plugins/defaults/plugin.rb', line 45

def self.search_plugin_file(name, include_system_plugins = false)
  regex = /#{Regexp.quote(name + '.rb')}$/
  plugin_files(include_system_plugins).detect {|f| regex =~ f}
end

.setup_loggerObject



267
268
269
270
271
272
# File 'lib/termtter/client.rb', line 267

def setup_logger
  @logger = config.logger || default_logger
  @logger.level = config.devel ? Logger::DEBUG : Logger::INFO
  call_hooks(:post_setup_logger)
  @logger
end

.setup_task_managerObject



310
311
312
# File 'lib/termtter/client.rb', line 310

def setup_task_manager
  @task_manager = Termtter::TaskManager.new(1)
end

.shorten_url(url, host, format) ⇒ Object

returns nil if not shorten



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/plugins/url_shortener.rb', line 50

def self.shorten_url(url, host, format)
  return url if config.plugins.url_shortener.ignore_regexp =~ url # already shorten
  url_enc = URI.escape(url, /[^a-zA-Z0-9.:]/)
  res = Termtter::HTTPpool.start(host) do |h|
    h.get(format % url_enc)
  end
  if res.code == '200'
    result = res.body
    if /"(http.*?)"/ =~ result
      result = $1
    elsif /"statusCode": "ERROR"/ =~ result
      return nil
    end
    result
  else
    nil
  end
end

.show_settings(conf, level = 0) ⇒ Object



383
384
385
386
387
388
389
390
391
392
393
# File 'lib/plugins/defaults/standard_commands.rb', line 383

def self.show_settings(conf, level = 0)
  conf.__values__.each do |k, v|
    if v.instance_of? Termtter::Config
      puts "#{k}:"
      show_settings v, level + 1
    else
      print '  ' * level
      puts "#{k} = #{v.nil? ? 'nil' : v.inspect}"
    end
  end
end

.show_splashObject



318
319
320
# File 'lib/termtter/client.rb', line 318

def show_splash
  puts TermColor.parse(config.splash)
end

.swap_timeline_format(format) ⇒ Object



36
37
38
39
40
41
42
43
44
# File 'lib/plugins/stream.rb', line 36

def swap_timeline_format(format)
  original = config.plugins.stdout.timeline_format
  if /\$orig/ =~ format
    format.gsub!(/\$orig/, original)
  end
  config.plugins.stdout.timeline_format = format
  yield
  config.plugins.stdout.timeline_format = original
end

.time_format_for(statuses) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/plugins/defaults/stdout.rb', line 84

def self.time_format_for(statuses)
  t0 = Time.now
  t1 = Time.parse(statuses.first[:created_at])
  t2 = Time.parse(statuses.last[:created_at])
  if [t0.year, t0.month, t0.day] == [t1.year, t1.month, t1.day] \
    and [t1.year, t1.month, t1.day] == [t2.year, t2.month, t2.day]
    config.plugins.stdout.time_format_today
  else
    config.plugins.stdout.time_format_not_today
  end
end

.train(length) ⇒ Object



4
5
6
7
8
# File 'lib/plugins/train.rb', line 4

def self.train(length)
  text = "ε="
  length.times{ text << "⋤⋥" }
  text
end

.typable_id?(id) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
61
62
63
64
# File 'lib/plugins/typable_id.rb', line 58

def self.typable_id?(id)
  if public_storage[:typable_id].assoc(id.to_s)
    return true
  else
    return false
  end
end

.typable_id_convert(id) ⇒ Object



39
40
41
42
43
44
45
46
47
# File 'lib/plugins/typable_id.rb', line 39

def self.typable_id_convert(id)
  if current_id = public_storage[:typable_id].assoc(id.to_s)
    return current_id[1]
  elsif current_id = public_storage[:typable_id].rassoc(id.to_s)
    return current_id[0]
  else
    return nil
  end
end

.typable_id_status(id) ⇒ Object



49
50
51
52
53
54
55
56
# File 'lib/plugins/typable_id.rb', line 49

def self.typable_id_status(id)
  if current_id = (public_storage[:typable_id].assoc(id.to_s)||\
                   public_storage[:typable_id].rassoc(id.to_s))
    return current_id[2]
  else
    return nil
  end
end

.typable_id_to_data(id) ⇒ Object



80
81
82
# File 'lib/plugins/defaults/stdout.rb', line 80

def self.typable_id_to_data(id)
  @typable_id_generator.get(id)
end

.update_with_user_and_id(text, username, id) ⇒ Object



575
576
577
578
579
# File 'lib/plugins/defaults/standard_commands.rb', line 575

def update_with_user_and_id(text, username, id)
  text = "@#{username} #{text}"
  result = Termtter::API.twitter.update(text, {'in_reply_to_status_id' => id })
  puts "replied => #{result.text}"
end

.wassr_update(text) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/plugins/multi_post.rb', line 5

def wassr_update(text)
  if text.match(/^(\d+)\s+(.+)$/) and
      (s = Termtter::API.twitter.show($1) rescue nil)
    tmp_text = "@#{s.user.screen_name} #{$2}"
  else
    tmp_text = text
  end

  Net::HTTP.version_1_2
  req = Net::HTTP::Post.new("/statuses/update.json?")
  req.basic_auth config.plugins.wassr.username, config.plugins.wassr.password
  Net::HTTP.start('api.wassr.jp', 80) do |http|
    res = http.request(req, "status=#{URI.escape(tmp_text)}&source=Termtter")
  end
end

Instance Method Details

#friends(max) ⇒ Object



17
18
19
20
21
22
# File 'lib/plugins/stream.rb', line 17

def friends(max)
  Status.group(:user_id).
    select(:user_id, :screen_name).
    join(:users, :id => :user_id).
    order(:COUNT.sql_function.desc).take(max)
end