PageRenderTime 28ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/plugins/defaults/standard_commands.rb

https://github.com/technohippy/termtter
Ruby | 500 lines | 489 code | 10 blank | 1 comment | 5 complexity | 214e1787be561c4215b7b9e56b11c97c MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. require 'erb'
  3. require 'set'
  4. config.plugins.standard.set_default(
  5. :limit_format,
  6. '<<%=remaining_color%>><%=limit.remaining_hits%></<%=remaining_color%>>/<%=limit.hourly_limit%> until <%=Time.parse(limit.reset_time).getlocal%> (<%=remaining_time%> remaining)')
  7. config.set_default(:easy_reply, true)
  8. module Termtter::Client
  9. register_command(
  10. :name => :reload,
  11. :exec => lambda {|arg|
  12. args = @since_id ? [{:since_id => @since_id}] : []
  13. statuses = Termtter::API.twitter.friends_timeline(*args)
  14. unless statuses.empty?
  15. print "\e[0G" + "\e[K" unless win?
  16. @since_id = statuses[0].id
  17. output(statuses, :update_friends_timeline)
  18. Readline.refresh_line if arg =~ /\-r/
  19. end
  20. }
  21. )
  22. register_command(
  23. :name => :update, :alias => :u,
  24. :exec => lambda {|arg|
  25. unless arg.empty?
  26. params =
  27. if config.easy_reply && /^\s*(@\w+)/ =~ arg
  28. user_name = normalize_as_user_name($1)
  29. in_reply_to_status_id = Termtter::API.twitter.user(user_name).status.id rescue nil
  30. in_reply_to_status_id ? {:in_reply_to_status_id => in_reply_to_status_id} : {}
  31. else
  32. {}
  33. end
  34. result = Termtter::API.twitter.update(arg, params)
  35. puts "updated => #{result.text}"
  36. end
  37. },
  38. :help => ["update,u TEXT", "Post a new message"]
  39. )
  40. register_command(
  41. :name => :delete, :aliases =>[:del],
  42. :exec_proc => lambda {|arg|
  43. id =
  44. case arg
  45. when ''
  46. Termtter::API.twitter.user_timeline(config.user_name)[0].id
  47. when /^\d+$/
  48. arg.to_i
  49. end
  50. if id
  51. result = Termtter::API.twitter.remove_status(id)
  52. puts "deleted => #{result.text}"
  53. end
  54. },
  55. :help => ['delete,del [STATUS ID]', 'Delete a status']
  56. )
  57. direct_message_struct = Struct.new(:id, :text, :user, :created_at)
  58. direct_message_struct.class_eval do
  59. def method_missing(*args, &block)
  60. nil
  61. end
  62. end
  63. register_command(
  64. :name => :direct, :aliases => [:d],
  65. :exec_proc => lambda {|arg|
  66. case arg
  67. when /^([^\s]+)\s+?(.*)\s*$/
  68. user, text = normalize_as_user_name($1), $2
  69. Termtter::API.twitter.direct_message(user, text)
  70. puts "=> to:#{user} message:#{text}"
  71. when 'list'
  72. output(
  73. Termtter::API.twitter.direct_messages.map { |d|
  74. direct_message_struct.new(d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
  75. },
  76. :direct_messages
  77. )
  78. when 'sent_list'
  79. output(
  80. Termtter::API.twitter.sent_direct_messages.map { |d|
  81. direct_message_struct.new(d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
  82. },
  83. :direct_messages
  84. )
  85. end
  86. },
  87. :help => [
  88. ["direct,d USERNAME TEXT", "Send direct message"],
  89. ["direct,d list", 'List direct messages'],
  90. ["direct,d sent_list", 'List sent direct messages']
  91. ]
  92. )
  93. register_command(
  94. :name => :profile, :aliases => [:p],
  95. :exec_proc => lambda {|arg|
  96. user = Termtter::API.twitter.user(normalize_as_user_name(arg))
  97. attrs = %w[ name screen_name url description profile_image_url location protected following
  98. friends_count followers_count statuses_count favourites_count
  99. id time_zone created_at utc_offset notifications
  100. ]
  101. label_width = attrs.map(&:size).max
  102. attrs.each do |attr|
  103. value = user.__send__(attr.to_sym)
  104. puts "#{attr.gsub('_', ' ').rjust(label_width)}: #{value}"
  105. end
  106. },
  107. :help => ["profile,p USERNAME", "Show user's profile"]
  108. )
  109. register_command(
  110. :name => :followers,
  111. :exec_proc => lambda {|arg|
  112. user_name = normalize_as_user_name(arg)
  113. user_name = config.user_name if user_name.empty?
  114. followers = []
  115. page = 0
  116. begin
  117. followers += tmp = Termtter::API.twitter.followers(user_name, :page => page+=1)
  118. end until tmp.empty?
  119. Termtter::Client.public_storage[:followers] = followers
  120. public_storage[:users] += followers.map(&:screen_name)
  121. puts followers.map(&:screen_name).join(' ')
  122. },
  123. :help => ["followers", "Show followers"]
  124. )
  125. register_command(
  126. :name => :list, :aliases => [:l],
  127. :exec_proc => lambda {|arg|
  128. if arg.empty?
  129. event = :list_friends_timeline
  130. statuses = Termtter::API.twitter.friends_timeline
  131. else
  132. event = :list_user_timeline
  133. statuses = []
  134. Array(arg.split).each do |user|
  135. user_name = normalize_as_user_name(user)
  136. statuses += Termtter::API.twitter.user_timeline(user_name)
  137. end
  138. end
  139. output(statuses, event)
  140. },
  141. :help => ["list,l [USERNAME]", "List the posts"]
  142. )
  143. class SearchEvent; attr_reader :query; def initialize(query); @query = query end; end
  144. public_storage[:search_keywords] = Set.new
  145. register_command(
  146. :name => :search, :aliases => [:s],
  147. :exec_proc => lambda {|arg|
  148. statuses = Termtter::API.twitter.search(arg)
  149. public_storage[:search_keywords] << arg
  150. output(statuses, SearchEvent.new(arg))
  151. },
  152. :completion_proc => lambda {|cmd, arg|
  153. public_storage[:search_keywords].grep(/^#{Regexp.quote(arg)}/).map { |i| "#{cmd} #{i}" }
  154. },
  155. :help => ["search,s TEXT", "Search for Twitter"]
  156. )
  157. register_hook(:highlight_for_search_query, :point => :pre_coloring) do |text, event|
  158. case event
  159. when SearchEvent
  160. query = event.query.split(/\s/).map {|q|Regexp.quote(q)}.join("|")
  161. text.gsub(/(#{query})/i, '<on_magenta><white>\1</white></on_magenta>')
  162. else
  163. text
  164. end
  165. end
  166. register_command(
  167. :name => :replies, :aliases => [:r],
  168. :exec_proc => lambda {|arg|
  169. res = Termtter::API.twitter.replies
  170. unless arg.empty?
  171. res = res.map {|e| e.user.screen_name == arg ? e : nil }.compact
  172. end
  173. output(res, :replies)
  174. },
  175. :help => ["replies,r", "List the replies"]
  176. )
  177. register_command(
  178. :name => :show,
  179. :exec_proc => lambda {|arg|
  180. id = arg.gsub(/.*:\s*/, '')
  181. output([Termtter::API.twitter.show(id)], :show)
  182. },
  183. :completion_proc => lambda {|cmd, arg|
  184. case arg
  185. when /(\w+):\s*(\d+)\s*$/
  186. find_status_ids($2).map {|id| "#{cmd} #{$1}: #{id}"}
  187. else
  188. users = find_users(arg)
  189. unless users.empty?
  190. users.map {|user| "#{cmd} #{user}:"}
  191. else
  192. find_status_ids(arg).map {|id| "#{cmd} #{id}"}
  193. end
  194. end
  195. },
  196. :help => ["show ID", "Show a single status"]
  197. )
  198. register_command(
  199. :name => :shows,
  200. :exec_proc => lambda {|arg|
  201. id = arg.gsub(/.*:\s*/, '')
  202. # TODO: Implement
  203. #output([Termtter::API.twitter.show(id)], :show)
  204. puts "Not implemented yet."
  205. },
  206. :completion_proc => get_command(:show).completion_proc
  207. )
  208. register_command(
  209. :name => :follow, :aliases => [],
  210. :exec_proc => lambda {|args|
  211. args.split(' ').each do |arg|
  212. user_name = normalize_as_user_name(arg)
  213. res = Termtter::API::twitter.follow(user_name)
  214. end
  215. },
  216. :help => ['follow USER', 'Follow user']
  217. )
  218. register_command(
  219. :name => :leave, :aliases => [],
  220. :exec_proc => lambda {|args|
  221. args.split(' ').each do |arg|
  222. user_name = normalize_as_user_name(arg)
  223. res = Termtter::API::twitter.leave(user_name)
  224. end
  225. },
  226. :help => ['leave USER', 'Leave user']
  227. )
  228. register_command(
  229. :name => :favorite, :aliases => [:fav],
  230. :exec_proc => lambda {|arg|
  231. id = 0
  232. case arg
  233. when /^\d+/
  234. id = arg.to_i
  235. when /^@([A-Za-z0-9_]+)/
  236. user_name = normalize_as_user_name($1)
  237. statuses = Termtter::API.twitter.user_timeline(user_name)
  238. return if statuses.empty?
  239. id = statuses[0].id
  240. when /^\/(.*)$/
  241. word = $1
  242. raise "Not implemented yet."
  243. else
  244. if public_storage[:typable_id] && typable_id?(arg)
  245. id = typable_id_convert(arg)
  246. else
  247. return
  248. end
  249. end
  250. r = Termtter::API.twitter.favorite id
  251. puts "Favorited status ##{r.id} on user @#{r.user.screen_name} #{r.text}"
  252. },
  253. :completion_proc => lambda {|cmd, arg|
  254. case arg
  255. when /(\d+)/
  256. find_status_ids(arg).map {|id| "#{cmd} #{id}"}
  257. else
  258. if data = Termtter::Client.typable_id_to_data(arg)
  259. "#{cmd} #{data}"
  260. else
  261. %w(favorite).grep(/^#{Regexp.quote arg}/)
  262. end
  263. end
  264. },
  265. :help => ['favorite,fav (ID|@USER|TYPABLE|/WORD)', 'Mark a status as a favorite']
  266. )
  267. def self.show_settings(conf, level = 0)
  268. conf.__values__.each do |k, v|
  269. if v.instance_of? Termtter::Config
  270. puts "#{k}:"
  271. show_settings v, level + 1
  272. else
  273. print ' ' * level
  274. puts "#{k} = #{v.nil? ? 'nil' : v.inspect}"
  275. end
  276. end
  277. end
  278. register_command(
  279. :name => :settings, :aliases => [:set],
  280. :exec_proc => lambda {|_|
  281. show_settings config
  282. },
  283. :help => ['settings,set', 'Show your settings']
  284. )
  285. # TODO: Change colors when remaining_hits is low.
  286. # TODO: Simmulate remaining_hits.
  287. register_command(
  288. :name => :limit, :aliases => [:lm],
  289. :exec_proc => lambda {|arg|
  290. limit = Termtter::API.twitter.limit_status
  291. remaining_time = "%dmin %dsec" % (Time.parse(limit.reset_time) - Time.now).divmod(60)
  292. remaining_color =
  293. case limit.remaining_hits / limit.hourly_limit.to_f
  294. when 0.2..0.4 then :yellow
  295. when 0..0.2 then :red
  296. else :green
  297. end
  298. erbed_text = ERB.new(config.plugins.standard.limit_format).result(binding)
  299. puts TermColor.parse(erbed_text)
  300. },
  301. :help => ["limit,lm", "Show the API limit status"]
  302. )
  303. register_command(
  304. :name => :pause,
  305. :exec_proc => lambda {|arg| pause},
  306. :help => ["pause", "Pause updating"]
  307. )
  308. register_command(
  309. :name => :resume,
  310. :exec_proc => lambda {|arg| resume},
  311. :help => ["resume", "Resume updating"]
  312. )
  313. register_command(
  314. :name => :exit, :aliases => [:quit],
  315. :exec_proc => lambda {|arg| exit},
  316. :help => ['exit,quit', 'Exit']
  317. )
  318. register_command(
  319. :name => :help, :aliases => [:h],
  320. :exec_proc => lambda {|arg|
  321. helps = []
  322. @commands.map do |name, command|
  323. next unless command.help
  324. case command.help[0]
  325. when String
  326. helps << command.help
  327. when Array
  328. helps += command.help
  329. end
  330. end
  331. helps.compact!
  332. unless arg.empty?
  333. helps = helps.select {|n, _| /#{arg}/ =~ n }
  334. end
  335. puts formatted_help(helps)
  336. },
  337. :help => ["help,h", "Print this help message"]
  338. )
  339. def self.formatted_help(helps)
  340. helps = helps.sort_by {|help| help[0] }
  341. width = helps.map {|n, _| n.size }.max
  342. space = 3
  343. helps.map {|name, desc|
  344. name.to_s.ljust(width + space) + desc.to_s
  345. }.join("\n")
  346. end
  347. register_command(
  348. :name => :plug,
  349. :alias => :plugin,
  350. :exec_proc => lambda {|arg|
  351. if arg.empty?
  352. plugin_list
  353. return
  354. end
  355. begin
  356. result = plug arg
  357. rescue LoadError
  358. ensure
  359. puts "=> #{result.inspect}"
  360. end
  361. },
  362. :completion_proc => lambda {|cmd, args|
  363. plugin_list.grep(/^#{Regexp.quote(args)}/).map {|i| "#{cmd} #{i}"}
  364. },
  365. :help => ['plug FILE', 'Load a plugin']
  366. )
  367. ## plugin_list :: IO ()
  368. def self.plugin_list
  369. plugin_list = (Dir["#{File.dirname(__FILE__)}/../*.rb"] + Dir["#{Termtter::CONF_DIR}/plugins/*.rb"]).
  370. map {|f| File.basename(f).sub(/\.rb$/, '')}.
  371. sort
  372. list = plugin_list
  373. width = list.map {|i|i.size}.max + 2
  374. a = []
  375. list.sort.each_slice(4) {|i|
  376. a << i.map {|j| j + (" " * (width - j.size))}.join
  377. }
  378. puts TermColor.parse('<green>' + TermColor.escape(a.join("\n")) + '</green>')
  379. end
  380. register_command(
  381. :name => :reply,
  382. :aliases => [:re],
  383. :exec_proc => lambda {|arg|
  384. case arg
  385. when /^\s*(?:list|ls)\s*(?:\s+(\w+))?\s*$/
  386. public_storage[:log4re] =
  387. if $1
  388. public_storage[:log].
  389. select {|l| l.user.screen_name == $1}.
  390. sort {|a,b| a.id <=> b.id}
  391. else
  392. public_storage[:log].sort {|a,b| a.id <=> b.id}
  393. end
  394. public_storage[:log4re].each_with_index do |s, i|
  395. puts "#{i}: #{s.user.screen_name}: #{s.text}"
  396. end
  397. when /^\s*(?:up(?:date)?)\s+(\d+)\s+(.+)$/
  398. id = public_storage[:log4re][$1.to_i].id
  399. text = $2
  400. user = public_storage[:log4re][$1.to_i].user
  401. update_with_user_and_id(text, user.screen_name, id) if user
  402. public_storage.delete :log4re
  403. when /^\s*(\d+)\s+(.+)$/
  404. s = Termtter::API.twitter.show($1) rescue nil
  405. if s
  406. update_with_user_and_id($2, s.user.screen_name, s.id)
  407. end
  408. when /^\s*(@\w+)/
  409. user_name = normalize_as_user_name($1)
  410. s = Termtter::API.twitter.user(user_name).status
  411. if s
  412. params = s ? {:in_reply_to_status_id => s.id} : {}
  413. Termtter::API.twitter.update(arg, params)
  414. puts "=> #{arg}"
  415. end
  416. end
  417. },
  418. :completion_proc => lambda {|cmd, arg|
  419. case arg
  420. when /(\d+)/
  421. find_status_ids(arg).map {|id| "#{cmd} #{id}"}
  422. end
  423. },
  424. :help => ["reply,re @USERNAME or STATUS_ID", "Send a reply"]
  425. )
  426. register_command(
  427. :name => :redo,
  428. :aliases => [:"."],
  429. :exec_proc => lambda {|arg|
  430. break if Readline::HISTORY.length < 2
  431. i = Readline::HISTORY.length - 2
  432. input = ""
  433. begin
  434. input = Readline::HISTORY[i]
  435. i -= 1
  436. return if i <= 0
  437. end while input == "redo" or input == "."
  438. begin
  439. Termtter::Client.call_commands(input)
  440. rescue CommandNotFound => e
  441. warn "Unknown command \"#{e}\""
  442. warn 'Enter "help" for instructions'
  443. rescue => e
  444. handle_error e
  445. end
  446. },
  447. :help => ["redo,.", "Execute previous command"]
  448. )
  449. def self.update_with_user_and_id(text, username, id)
  450. text = "@#{username} #{text}"
  451. result = Termtter::API.twitter.update(text, {'in_reply_to_status_id' => id})
  452. puts "replied => #{result.text}"
  453. end
  454. def self.normalize_as_user_name(text)
  455. text.strip.sub(/^@/, '')
  456. end
  457. def self.find_status_ids(text)
  458. public_storage[:status_ids].select {|id| /#{Regexp.quote(text)}/ =~ id.to_s}
  459. end
  460. def self.find_users(text)
  461. public_storage[:users].select {|user| /^#{Regexp.quote(text)}/ =~ user}
  462. end
  463. end