PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/plugins/irc_gw.rb

https://github.com/Epictetus/termtter
Ruby | 245 lines | 217 code | 27 blank | 1 comment | 24 complexity | 3492b0d9ecda7402395d1d85f22769e4 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. require 'net/irc'
  3. require 'set'
  4. require 'cgi'
  5. config.plugins.irc_gw.set_default(:port, 16669)
  6. config.plugins.irc_gw.set_default(:last_statuses_count, 100)
  7. config.plugins.irc_gw.set_default(:logger_level, Logger::ERROR)
  8. config.plugins.irc_gw.set_default(:command_regexps, [/^(.+): *(.*)/])
  9. Termtter::Client.plug 'multi_output'
  10. module Termtter::Client
  11. class << self
  12. def following_friends
  13. user_name = config.user_name
  14. frinends = []
  15. last = nil
  16. begin
  17. puts "collecting friends (#{frinends.length})"
  18. last = Termtter::API::twitter.friends(user_name, :cursor => last ? last.next_cursor : -1)
  19. frinends += last.users
  20. rescue Timeout::Error, StandardError # XXX
  21. break
  22. end until last.next_cursor == 0
  23. puts "You have #{frinends.length} friends."
  24. Set.new(frinends.map(&:screen_name))
  25. end
  26. end
  27. end
  28. class TermtterIrcGateway < Net::IRC::Server::Session
  29. @@listners = []
  30. @@last_statuses = []
  31. Termtter::Client.register_hook(
  32. :name => :irc_gw_output,
  33. :point => :output,
  34. :exec => lambda { |statuses, event|
  35. if event == :update_friends_timeline
  36. @@last_statuses =
  37. (@@last_statuses + statuses.dup).reverse![0..config.plugins.irc_gw.last_statuses_count].reverse!
  38. end
  39. @@listners.each do |listner|
  40. listner.call(statuses.dup, event)
  41. end
  42. }
  43. )
  44. Termtter::Client.register_hook(
  45. :name => :irc_gw_handle_error,
  46. :point => :on_error,
  47. :exec => lambda { |error|
  48. @@listners.each{ |listener|
  49. listener.log "[ERROR] #{error.class.to_s}: #{error.message}"
  50. }
  51. }
  52. )
  53. if Termtter::Client.respond_to? :register_output
  54. Termtter::Client.register_output(:irc) do |message|
  55. @@listners.each do |listener|
  56. listener.log(message.to_s.gsub(/\e\[\d+m/, '')) # remove escape sequence
  57. end
  58. end
  59. end
  60. def server_name; 'termtter' end
  61. def server_version; '0.0.0' end
  62. def main_channel; '#termtter' end
  63. def initialize(*args)
  64. super
  65. @@listners << self
  66. @members = Set.new
  67. @commands = []
  68. Termtter::Client.register_hook(:collect_user_names_for_irc_gw, :point => :pre_filter) do |statuses, event|
  69. new_users = []
  70. statuses.each do |s|
  71. screen_name = s.user.screen_name
  72. next if screen_name == config.user_name
  73. next unless friends_ids.include? s.user.id
  74. next if @members.include? screen_name
  75. @members << screen_name
  76. new_users << screen_name
  77. end
  78. join_members(new_users)
  79. end
  80. Termtter::Client.register_command(
  81. :name => :collect_friends,
  82. :help => 'Collect friends for IRC.',
  83. :exec => lambda {|arg|
  84. sync_friends
  85. })
  86. Termtter::Client.register_hook(:collect_commands_for_irc_gw, :point => :post_command) do |text|
  87. sync_commands if text =~ /plug/
  88. end
  89. end
  90. def call(statuses, event, indent = 0)
  91. if event == :update_friends_timeline
  92. msg_type = PRIVMSG
  93. else
  94. time_format = Termtter::Client.time_format_for statuses
  95. msg_type = NOTICE
  96. end
  97. statuses.each do |s|
  98. typable_id = Termtter::Client.data_to_typable_id(s.id)
  99. time = Time.parse(s.created_at).strftime(time_format) if time_format
  100. reply_to_status_id_str =
  101. if s.in_reply_to_status_id
  102. "(reply to #{Termtter::Client.data_to_typable_id(s.in_reply_to_status_id)})"
  103. else
  104. nil
  105. end
  106. padding = indent > 0 ? '→' : nil
  107. post s.user.screen_name, msg_type, main_channel, [time, padding, CGI.unescapeHTML(s.text), typable_id, reply_to_status_id_str].compact.join(' ')
  108. if config.plugins.stdout.show_reply_chain && s.in_reply_to_status_id && indent < config.plugins.stdout.max_indent_level
  109. begin
  110. if reply = Termtter::API.twitter.cached_status(s.in_reply_to_status_id)
  111. call([reply], event, indent+1)
  112. end
  113. rescue Rubytter::APIError
  114. end
  115. end
  116. end
  117. end
  118. def on_message(m)
  119. termtter_command = m.command.downcase + ' ' + m.params.join(' ')
  120. return unless Termtter::Client.find_command(termtter_command)
  121. execute_command(termtter_command)
  122. rescue Exception => e
  123. Termtter::Client.handle_error(e)
  124. end
  125. def on_user(m)
  126. super
  127. @user = m.params.first
  128. post @prefix, JOIN, main_channel
  129. post server_name, MODE, main_channel, "+o", @prefix.nick
  130. sync_commands
  131. self.call(@@last_statuses || [], :update_friends_timeline)
  132. end
  133. def on_privmsg(m)
  134. target, message = *m.params
  135. if message =~ / +\//
  136. termtter_command = message.gsub(/ +\//, '')
  137. return unless Termtter::Client.find_command(termtter_command)
  138. execute_command(termtter_command)
  139. return
  140. end
  141. config.plugins.irc_gw.command_regexps and
  142. config.plugins.irc_gw.command_regexps.each do |rule|
  143. if message =~ rule
  144. command = message.scan(rule).first.join(' ')
  145. next unless Termtter::Client.find_command(command)
  146. execute_command(command)
  147. return
  148. end
  149. end
  150. execute_command('update ' + message)
  151. post @prefix, TOPIC, main_channel, message
  152. rescue Exception => e
  153. Termtter::Client.handle_error(e)
  154. end
  155. def execute_command(command)
  156. command.encode!('utf-8', 'utf-8') if command.respond_to? :encode!
  157. original_confirm = config.confirm
  158. config.confirm = false
  159. post '#termtter', NOTICE, main_channel, '> ' + command
  160. Termtter::Client.execute(command)
  161. ensure
  162. config.confirm = original_confirm
  163. end
  164. def log(str)
  165. str.each_line do |line|
  166. post server_name, NOTICE, main_channel, line
  167. end
  168. end
  169. def sync_friends
  170. previous_friends = @members
  171. new_friends = Termtter::Client.following_friends
  172. diff = new_friends - previous_friends
  173. join_members(diff)
  174. @members += diff
  175. end
  176. def sync_commands
  177. previous_commands = @commands
  178. new_commands = (
  179. Termtter::Client.commands.keys + Termtter::Client.commands.values.map(&:aliases)
  180. ).flatten.uniq.compact
  181. join_members(new_commands - previous_commands)
  182. @commands = new_commands
  183. end
  184. def join_members(members)
  185. params = []
  186. max_params_count = 3
  187. members.each do |member|
  188. prefix = Prefix.new("#{member}!#{member}@localhost")
  189. next if prefix.extract.empty?
  190. post prefix, JOIN, main_channel
  191. params << prefix.nick
  192. next if params.size < max_params_count
  193. post server_name, MODE, main_channel, "+#{"v" * params.size}", *params
  194. params = []
  195. end
  196. post server_name, MODE, main_channel, "+#{"v" * params.size}", *params unless params.empty?
  197. end
  198. def friends_ids
  199. if !@friends_ids || !@friends_ids_expire ||@friends_ids_expire < Time.now
  200. @friends_ids = Termtter::API.twitter.friends_ids(config.user_name)
  201. @friends_ids_expire = Time.now + 3600
  202. end
  203. @friends_ids
  204. end
  205. end
  206. unless defined? IRC_SERVER
  207. logger = Logger.new($stdout)
  208. logger.level = config.plugins.irc_gw.logger_level
  209. IRC_SERVER = Net::IRC::Server.new(
  210. '127.0.0.1',
  211. config.plugins.irc_gw.port,
  212. TermtterIrcGateway,
  213. :logger => logger
  214. )
  215. Thread.start do
  216. IRC_SERVER.start
  217. end
  218. end