PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/plugins/defaults/stdout.rb

https://github.com/Epictetus/termtter
Ruby | 245 lines | 205 code | 29 blank | 11 comment | 14 complexity | 3e4eb4006ec980956a3f33c725d77c9b MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. require 'termcolor'
  3. require 'erb'
  4. require 'tempfile'
  5. config.plugins.stdout.set_default(:gray, 90)
  6. config.plugins.stdout.set_default(:colors, (31..36).to_a + (91..96).to_a)
  7. config.plugins.stdout.set_default(
  8. :timeline_format,
  9. [
  10. '<<%=config.plugins.stdout.gray||config.plugins.stdout.colors.last%>>',
  11. '<%=time%> [<%=status_id%>] ',
  12. '</<%=config.plugins.stdout.gray||config.plugins.stdout.colors.last%>>',
  13. '<%= indent_text %>',
  14. '<<%=color%>><%=s.user.screen_name%>: <%=text%></<%=color%>> ',
  15. '<<%=config.plugins.stdout.gray||config.plugins.stdout.colors.last%>>',
  16. '<%=reply_to_status_id ? " (reply_to [#{reply_to_status_id}]) " : ""%>',
  17. '<%=retweeted_status_id ? " (retweet_to [#{retweeted_status_id}]) " : ""%>',
  18. '<%=source%><%=s[:user][:protected] ? "[P]" : ""%>',
  19. '</<%=config.plugins.stdout.gray||config.plugins.stdout.colors.last%>>',
  20. ].join('')
  21. )
  22. config.plugins.stdout.set_default(:sweets, %w[jugyo ujm sora_h lingr_termtter termtter nanki sixeight])
  23. config.plugins.stdout.set_default(:sweet_color, 'red')
  24. config.plugins.stdout.set_default(:time_format_today, '%H:%M:%S')
  25. config.plugins.stdout.set_default(:time_format_not_today, '%y/%m/%d %H:%M')
  26. config.plugins.stdout.set_default(:enable_pager, false)
  27. config.plugins.stdout.set_default(:pager, 'less -R -f +G')
  28. config.plugins.stdout.set_default(:window_height, 50)
  29. config.plugins.stdout.set_default(:typable_ids, ('aa'..'zz').to_a)
  30. config.plugins.stdout.set_default(:typable_id_prefix, '$')
  31. config.plugins.stdout.set_default(:show_reply_chain, true)
  32. config.plugins.stdout.set_default(:indent_format, %q("#{' ' * (indent - 1)} → "))
  33. config.plugins.stdout.set_default(:max_indent_level, 1)
  34. config.plugins.stdout.set_default(
  35. :screen_name_to_hash_proc, lambda { |screen_name| screen_name.to_i(36) })
  36. module Termtter
  37. class TypableIdGenerator
  38. def initialize(ids)
  39. if not ids.kind_of?(Array)
  40. raise ArgumentError, 'ids should be an Array'
  41. elsif ids.empty?
  42. raise ArgumentError, 'ids should not be empty'
  43. end
  44. @ids = ids
  45. @table = {}
  46. @rtable = {}
  47. end
  48. def next(data)
  49. id = @ids.shift
  50. @ids.push id
  51. @rtable.delete(@table[id])
  52. @table[id] = data
  53. @rtable[data] = id
  54. id
  55. end
  56. def get(id)
  57. @table[id]
  58. end
  59. def get_id(data)
  60. @rtable[data] || self.next(data)
  61. end
  62. end
  63. module Client
  64. @typable_id_generator = TypableIdGenerator.new(config.plugins.stdout.typable_ids)
  65. def self.data_to_typable_id(data)
  66. id = config.plugins.stdout.typable_id_prefix +
  67. @typable_id_generator.get_id(data)
  68. end
  69. def self.typable_id_to_data(id)
  70. @typable_id_generator.get(id)
  71. end
  72. def self.time_format_for(statuses)
  73. t0 = Time.now
  74. t1 = Time.parse(statuses.first[:created_at])
  75. t2 = Time.parse(statuses.last[:created_at])
  76. if [t0.year, t0.month, t0.day] == [t1.year, t1.month, t1.day] \
  77. and [t1.year, t1.month, t1.day] == [t2.year, t2.month, t2.day]
  78. config.plugins.stdout.time_format_today
  79. else
  80. config.plugins.stdout.time_format_not_today
  81. end
  82. end
  83. end
  84. class StdOut < Hook
  85. def initialize
  86. super(:name => :stdout, :points => [:output])
  87. end
  88. def call(statuses, event)
  89. print_statuses(statuses, event)
  90. end
  91. def inspect
  92. "#<Termtter::StdOut @name=#{@name}, @points=#{@points.inspect}, @exec_proc=#{@exec_proc.inspect}>"
  93. end
  94. def print_statuses(statuses, event, sort = true, time_format = nil)
  95. return unless statuses and statuses.first
  96. time_format ||= Termtter::Client.time_format_for statuses
  97. output_text = ''
  98. statuses.each do |s|
  99. output_text << status_line(s, time_format, event)
  100. end
  101. if config.plugins.stdout.enable_pager &&
  102. ENV['LINES'] &&
  103. statuses.size > ENV['LINES'].to_i
  104. file = Tempfile.new('termtter')
  105. file.print output_text
  106. file.close
  107. system "#{config.plugins.stdout.pager} #{file.path}"
  108. file.close(true)
  109. else
  110. Termtter::Client.clear_line
  111. print output_text
  112. end
  113. end
  114. def status_line(s, time_format, event, indent = 0)
  115. return '' unless s
  116. text = escape(TermColor.escape(s.text))
  117. color = color_of_user(s.user)
  118. status_id = Termtter::Client.data_to_typable_id(s.id)
  119. reply_to_status_id =
  120. if s.in_reply_to_status_id
  121. Termtter::Client.data_to_typable_id(s.in_reply_to_status_id)
  122. else
  123. nil
  124. end
  125. retweeted_status_id =
  126. if s.retweeted_status
  127. Termtter::Client.data_to_typable_id(s.retweeted_status.id)
  128. else
  129. nil
  130. end
  131. time = "(#{Time.parse(s.created_at).strftime(time_format)})"
  132. source =
  133. case s.source
  134. when />(.*?)</ then $1
  135. when 'web' then 'web'
  136. end
  137. text = colorize_users(text)
  138. text = Client.get_hooks(:pre_coloring).inject(text) {|result, hook|
  139. #Termtter::Client.logger.debug "stdout status_line: call hook :pre_coloring #{hook.inspect}"
  140. hook.call(result, event)
  141. }
  142. indent_text = indent > 0 ? eval(config.plugins.stdout.indent_format) : ''
  143. erbed_text = ERB.new(config.plugins.stdout.timeline_format).result(binding)
  144. erbed_text = Client.get_hooks(:pre_output).inject(erbed_text) {|result, hook|
  145. #Termtter::Client.logger.debug "stdout status_line: call hook :pre_output #{hook.inspect}"
  146. hook.call(result, event)
  147. }
  148. text = TermColor.unescape(TermColor.parse(erbed_text) + "\n")
  149. if config.plugins.stdout.show_reply_chain && s.in_reply_to_status_id
  150. indent += 1
  151. unless indent > config.plugins.stdout.max_indent_level
  152. begin
  153. if status = Termtter::API.twitter.cached_status(s.in_reply_to_status_id)
  154. text << status_line(status, time_format, event, indent)
  155. end
  156. rescue Rubytter::APIError
  157. end
  158. end
  159. end
  160. text
  161. end
  162. def colorize_users(text)
  163. text.gsub(/@([0-9A-Za-z_]+)/) do |i|
  164. color = color_of_screen_name($1)
  165. "<#{color}>#{i}</#{color}>"
  166. end
  167. end
  168. def color_of_user(user)
  169. color_of_screen_name(user.screen_name)
  170. end
  171. def color_of_screen_name(screen_name)
  172. return color_of_screen_name_cache[screen_name] if
  173. color_of_screen_name_cache.key?(screen_name)
  174. num = screen_name_to_hash(screen_name)
  175. color = config.plugins.stdout.instance_eval {
  176. sweets.include?(screen_name) ?
  177. sweet_color : colors[num % colors.size]
  178. }
  179. color_of_screen_name_cache[screen_name] = color
  180. color_of_screen_name_cache[screen_name]
  181. end
  182. def screen_name_to_hash(screen_name)
  183. config.plugins.stdout.screen_name_to_hash_proc.
  184. call(screen_name)
  185. end
  186. def color_of_screen_name_cache
  187. @color_of_screen_name_cache ||= {}
  188. end
  189. def escape(data)
  190. data.gsub(/[:cntrl:]/) {|c| c == "\n" ? c : c.dump[1...-1]}.untaint
  191. end
  192. end
  193. Client.register_hook(StdOut.new)
  194. Client.register_hook(
  195. :name => :stdout_typable_id,
  196. :point => /^modify_arg_for_.*/,
  197. :exec => lambda { |cmd, arg|
  198. if arg
  199. prefix = config.plugins.stdout.typable_id_prefix
  200. arg.gsub(/#{Regexp.quote(prefix)}\w+/) do |id|
  201. Termtter::Client.typable_id_to_data(id[1..-1]) || id
  202. end
  203. else
  204. arg
  205. end
  206. }
  207. )
  208. end
  209. # stdout.rb
  210. # output statuses to stdout
  211. # example config
  212. # config.plugins.stdout.colors =
  213. # [:none, :red, :green, :yellow, :blue, :magenta, :cyan]
  214. # config.plugins.stdout.timeline_format =
  215. # '<90><%=time%> [<%=status_id%>]</90> <<%=color%>><%=s.user.screen_name%>: <%=text%></<%=color%>> ' +
  216. # '<90><%=reply_to_status_id ? " (reply_to [#{reply_to_status_id}]) " : ""%><%=source%></90>'