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


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

def commands
  @commands
end

.loggerObject (readonly)

Returns the value of attribute logger


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

def logger
  @logger
end

.task_managerObject (readonly)

Returns the value of attribute task_manager


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

def task_manager
  @task_manager
end

Class Method Details

.add_command(name) ⇒ Object


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

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


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

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


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

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


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

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

.clear_commandObject


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

def clear_command
  @commands.clear
end

.clear_filterObject


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

def clear_filter
  @filters.clear
end

.clear_lineObject


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

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)

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

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

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


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

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


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

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


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

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


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

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

.execute(text) ⇒ Object


154
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
# File 'lib/termtter/client.rb', line 154

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 TimeoutError
  call_hooks("timeout", text)
  raise
end

.exitObject


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

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


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

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


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

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

.find_users(text) ⇒ Object


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

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


457
458
459
460
461
462
463
464
# File 'lib/plugins/defaults/standard_commands.rb', line 457

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 してるからこれいらない気もする


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

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


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

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


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

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


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

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


181
182
183
# File 'lib/plugins/defaults/list.rb', line 181

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

.load_configObject


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

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


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

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

.memory_cacheObject


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

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


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

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


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

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

.notify(*args) ⇒ Object


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

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,
}

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

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


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

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

.pauseObject


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

def pause
  @task_manager.pause
end

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

call-seq:

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

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

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 ()


488
489
490
491
492
# File 'lib/plugins/defaults/standard_commands.rb', line 488

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


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

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


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

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


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

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


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

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

.rescue_errorObject


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

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

.resumeObject


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

def resume
  @task_manager.resume
end

.runObject


321
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
# File 'lib/termtter/client.rb', line 321

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


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

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


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

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


378
379
380
381
382
383
384
385
386
387
388
# File 'lib/plugins/defaults/standard_commands.rb', line 378

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


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

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


570
571
572
573
574
# File 'lib/plugins/defaults/standard_commands.rb', line 570

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