# # A simple (nasty?) geektalk interface for weechat, so I don't have to have so # many console windows up everywhere. # # # Put into your weechat plugins dir: # # ~/.weechat/ruby/geektalk.rb # # and load inside of weechat: /ruby load geektalk.rb # # # TODO: channel switching support for the nick list? Ehh? # # Mahlon E. Smith # require 'socket' require 'etc' include Socket::Constants SIGNATURE = [ 'geektalk', 'Mahlon E. Smith', '0.4', 'BSD', 'Simple geektalk client for Weechat.', 'weechat_unload', 'UTF-8' ] HOST = 'skylab.org' PORT = 41000 ### Manage the current geektalk connection state. ### def geektalk_command( data, buffer, arg ) case arg when '' create_buffer() print_info "No connection made. Type '/geektalk connect'!" when 'connect' if $sock && ! $sock.closed? print_info( "You seem to already be connected. '/geektalk disconnect' first." ) return Weechat::WEECHAT_RC_OK end create_buffer() unless $buffer $sock.close if $sock && ! $sock.closed? begin create_connection() print_info "Connected to %s!" % [ HOST ] $populate_nicks = true Weechat.nicklist_remove_all( $buffer ) print_info 'Getting user list...' $sock.puts '/names' # The 'hook_fd' is the *right* way to do this, but it appears to # crash weechat for unknown reasons. This '10 times a second poll' # sucks, but it's the only workaround I could use -- Weechat seems # to somehow take over sockets spawned from within it, and doing # normal read/available operations on them cause weechat client hangs. # # Weechat.hook_fd( $sock.fileno, 1, 0, 0, 'socket_process_input', '' ) $timer = Weechat.hook_timer( 100, 0, 0, 'parse_input', '' ) rescue => err Weechat.print '', "%sGeektalk error during connection: %s%s" % [ Weechat.prefix( 'error' ), Weechat.color( 'red' ), err.message ] Weechat.buffer_close( $buffer ) if $buffer end when 'disconnect' if ! $sock || $sock.closed? print_info( "Not connected? '/geektalk connect' first." ) return Weechat::WEECHAT_RC_OK end Weechat.buffer_close( $buffer ) if $buffer Weechat.print '', 'Geektalk disconnected.' end return Weechat::WEECHAT_RC_OK end ### Generate the geektalk buffer. ### def create_buffer $buffer = Weechat::buffer_new( 'geektalk', 'buffer_process_input', '', 'buffer_teardown', '' ) Weechat::buffer_set( $buffer, 'title', "Geektalk @ %s" % [ HOST ] ) Weechat::buffer_set( $buffer, 'type', 'formatted' ) Weechat::buffer_set( $buffer, 'display', '1' ) Weechat::buffer_set( $buffer, 'time_for_each_line', '1' ) Weechat::buffer_set( $buffer, 'nicklist', '1' ) Weechat::buffer_set( $buffer, 'nicklist_case_sensitive', '1' ) Weechat::buffer_set( $buffer, 'unread', '-' ) Weechat::buffer_set( $buffer, 'highlight_words', Etc.getpwuid( Process.uid ).name ) end ### Called on buffer close. ### def buffer_teardown( data, buffer ) $sock.close if $sock && ! $sock.closed? Weechat.unhook( $timer ) if $timer Weechat.unhook( $hook ) if $hook $buffer = nil return Weechat::WEECHAT_RC_OK end ### Quick wrapper for sending info messages to the geektalk buffer. ### def print_info( msg ) Weechat.print $buffer, "%s-->\t%s" % [ Weechat.color('yellow'), msg ] end ### Perform the raw connection to geektalk, and sign the user on. ### def create_connection $sock = TCPSocket.new( HOST, PORT ) $sock.sync = true $sock.puts "u=%s\nh=%s\n/signon" % [ Etc.getpwuid( Process.uid ).name, Socket.gethostname ] end ### Called when you input data from Weechat. ### def buffer_process_input( data, buffer, input_data ) input_data.sub!( /^\*\//, '/' ) # let geektalk commands get through via */ $sock.puts( input_data ) return Weechat::WEECHAT_RC_OK rescue IOError => err print_info "Error on geektalk socket: %s, %s" % [ err.class.name, err.message ] $sock.close if $sock && ! $sock.closed? $sock = nil Weechat.unhook( $hook ) if $hook return Weechat::WEECHAT_RC_OK end ### Timer workaround. Poll for socket readability, munge up to ### look nice, output to geektalk buffer. ### def parse_input( data, calls ) return Weechat::WEECHAT_RC_OK unless $sock && ! $sock.closed? ready = Kernel.select( [ $sock ], nil, nil, 0 ) or return Weechat::WEECHAT_RC_OK read = ready.first or return Weechat::WEECHAT_RC_OK sock = read.first or return Weechat::WEECHAT_RC_OK line = sock.gets # regular chat line if line.match( /^<\d{2}:\d{2}\w{2} (\S+)> (.+)/ ) nick, content = $1, $2 line = "%s\t%s" % [ nick, content ] Weechat.print( $buffer, line ) # 'me' action line elsif line.match( /^<\d{2}:\d{2}\w{2}> (\S+) (.+)/ ) nick, content = $1, $2 line = "%s %s" % [ nick, content ] print_info( line ) # nick renames elsif line.match( /^\|\d{2}:\d{2}\w{2}\|\s+(\S+) is now known as (\w+)/ ) old_nick, new_nick = $1, $2 Weechat.nicklist_remove_nick( $buffer, Weechat.nicklist_search_nick($buffer, '', old_nick) ) Weechat.nicklist_add_nick( $buffer, '', new_nick, "bar_fg", '', '', 1 ) print_info( line ) # signin/outs elsif line.match( /^\|\d{2}:\d{2}\w{2} sign(off|on)\|.+\((\w+)\)/ ) nick = $2 case $1 when 'on' Weechat.nicklist_add_nick( $buffer, '', nick, "bar_fg", '', '', 1 ) when 'off' Weechat.nicklist_remove_nick( $buffer, Weechat.nicklist_search_nick($buffer, '', nick) ) end print_info( line ) else # initial nicklist population if $populate_nicks if line.match( /^\*\s+>?(\d+)\s+(\S+)\s+/ ) Weechat.nicklist_add_nick( $buffer, '', $2, "bar_fg", '', '', 1 ) print_info( line ) end $populate_nicks = false if line =~ /^\*\s+$/ else # unknown line, just send it through as is. Weechat.print( $buffer, line ) end end return Weechat::WEECHAT_RC_OK end # def socket_process_input( data, fd ) # sock = TCPSocket.for_fd( fd ) # sock.each_line do |line| # Weechat.print( $buffer, line ) # end # return Weechat::WEECHAT_RC_OK # end ### Weechat entry point. ### def weechat_init Weechat::register *SIGNATURE Weechat.hook_command( 'geektalk', 'Connect/Disconnect from Geektalk', '[connect|disconnect]', '', 'connect || disconnect', 'geektalk_command', '' ) return Weechat::WEECHAT_RC_OK end ### Hook for manually unloading this script, ensure cleanup. ### def weechat_unload $sock.close if $sock && ! $sock.closed? return Weechat::WEECHAT_RC_OK end