Tiarra やめました。

長らく利用していた IRC プロキシソフト Tiarra の利用をやめました。理由は次のとおりです。

  • tscreen(GNU Screenみたいなの)上の riece で IRC には常駐しているので、端末ログインすればどこからでも繋がる。
    • 携帯電話なんかの専用クライアントから接続できる手軽さはあるけれど、果たしてサーバソフトを常駐させるまで使うかっていうと、そうでもなかった。
    • 携帯電話なんかの専用クライアントから接続する場合は、大人しく別USERとしてログインすればいい気がしてた。
  • 頼んでもない大量の WHO リストが1,2分おきに tiarra サーバから出力されるようになって、チャットどころじゃなくなる。
    • サーバメッセージをメインログじゃなくてチャンネルログに流して、かつアクティブを持っていっちゃうようなクライアント側にも問題があるはずなんだけど、なんだか気持ち悪い。XChatやLimechatで接続すると、WHOリストは「プロトコルログ」のような生ログ表示以外には表示されないので、特に問題ない。
    • tiarra を経由しなかったら、riece でも WHOリストの大量取得現象は見られなかったから、IRCサーバ側にリクエストを投げているのは tiarra のはず。ここ1年くらいで、現象が起きる日と起きないがある。設定はいじってないし、なんだろうな。
    • 次の理由により、詳しく調べる前にtiarraの利用をやめることにしました。
  • かなり高度かつ柔軟な設定が出来るわりに、得られる効果がほとんどない。
    • tiarra のメリットと言えば、同一ホストからのセッション数を減らすことができる点。
    • それ以外だと、メイン宛のPRIVMSGを外部から読める、チャンネルの入退室を最小限にできる、常駐ロガーとして活躍…くらいか。
      • 常駐ロガーという意味では、自分のように既にクライアントソフトが常駐しているユーザにとってまるで意味がないもの。
    • デメリット
      • 設定が高度な分、煩雑でもあり、接続ができなくなったり、DCCのルーティング設定で問題があった場合など、問題を複雑化してしまいそう。
      • 複数サーバーに対し、それぞれ違う nick でログインしている/したいのに、そこらへんの設定が考慮されずに設計されていそう。
      • 設定自体、ちょっとWindows寄り。起動スクリプトを書いてcronなどに登録したり、ユーザごとの設定ファイルに対応させたり、いろいろ改造して使ってました。

お世話になっていたのは確かなのですが、IRC を便利に手間なく使うための Tiarra を本気で改良するのも本末転倒になってしまいそうで、結果的に利用をやめてしまいました。

当初、自分が Tiarra を導入した頃はガラケーでしたので、CGI/HTMLベースのIRCクライアントと一緒に使う際に威力を発揮していたんですが、今じゃスマホからSSHで端末を開いて、screen セッション上で動いている riece on emacs を表示して、普通にチャットができますもんね。

なんだかマイナス意見ばかりになってしまいましたが(利用をやめた理由だから当たり前ですが…)、オープンソースでこれだけのものを作って公開してくださった方には感謝しています。

 

Python で IRC クライアント的な

基本

無駄がなくて綺麗なコード。やっぱり Python って素敵。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket, string

# ソケット作成、接続、ログイン
irc = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
irc.connect( ( 'irc.example.com', 6667 ) )
irc.send( '''
    USER arianne * * arianne
   NICK arianne
   JOIN #hogefuga
''' )

while True:
   # recv() で、受信するまでブロッキング
   buf = unicode( irc.recv( 1024 ).strip(), 'utf-8' )
   # コマンドプロンプトの場合は、'cp932' を指定
   print buf.encode( 'utf-8' )
   # IRC サーバから PING が来たら PONG を自動応答する
   if string.split( buf )[0] == "PING":
      irc.send( "PONG " + string.split( buf )[1] + "\n" )
   # 文字列に反応してみたり
   if buf.find( u"こんにちは" ) > -1:
      irc.send( u"PRIVMSG #hogefuga :こんにちわぁ\n".encode( 'utf-8' ) )
モジュール化

車輪の再発明だと思いつつ、自分の使い易い形のクラスにしてみた。サーバー応答対応やエラーと例外処理が未実装だけれど「スレッド化した受信監視→外部から指定されたイベントハンドラ実行」は、これで・・・いけるはず・・・。もうちょっとスマートに書けるようになったらいいな。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket, string, threading
import logging

class ircc ( object ):
   """The IRC Client Class"""
   def __init__( self, hostname, port, server_encoding='utf-8',         terminal_encoding='utf-8' ):
      # ソケット作成
      self.server = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
      self.server.connect( ( hostname, port ) )
      self.server_encoding = server_encoding
      self.terminal_encoding = terminal_encoding
      script_encoding   = 'utf-8'
      logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s' )
   def __str__( self ):
      return 'ircc'
   def send( self, message ):
      # 渡された文字列をサーバに送信するだけ
      message = unicode( message, self.script_encoding )
      logging.debug( ">>> " + message.encode( self.terminal_encoding ) )
      self.server.send( message.encode( self.server_encoding ) + "\n")
   def recv( self, receive_size = 1024 ):
      # サーバから文字列を受信するまで待機
      buf = unicode( self.server.recv( receive_size ).strip(), self.server_encoding )
      for line in string.split( buf, '\n' ):
         logging.debug( "<<< " + line.encode( self.terminal_encoding ) )
      return buf
   # こういう細々とした IRC コマンドは、はたして個別に実装する必要があるのかニャー
   def quit( self, message = '' ):
      self.send( 'QUIT :' + message )
   def nick( self, nickname ):
      self.send( 'NICK ' + nickname )
   def join( self, channel ):
      self.send( 'JOIN ' + channel )
   def privmsg( self, to, message ):
      self.send( 'PRIVMSG ' + to + ' ' + ' :' + message )
   # ログイン・フェイズはエラー処理が重要になってくるので、メソッド化しておく。
   def login( self, loginname, nickname, description, wait_for_motd = True ):
      self.send( 'USER %s * * :%s' % ( loginname, description ) )
      self.nick( nickname )
      #if self.recv().find( '433' ):
      #    self.quit()
      #    return 1
      while wait_for_motd:
         if self.recv().find( ' 376 ' ):
            break
   # イベントハンドラ、send()、recv() それぞれのアドレスを渡してスレッド化
   def set_event( self, event_handler ):
      _irc_recv_thread( event_handler, self.send, self.recv )

class _irc_recv_thread( threading.Thread ):
   """ メッセージイベントを検知し、外部から定義された
        イベントハンドラを呼ぶスレッドオブジェクト """
   def __init__( self, event_handler, send_handler, receive_handler ):
      self.__event   = event_handler
      self.__send    = send_handler
      self.__receive = receive_handler
      self.__msg     = ''
      threading.Thread.__init__( self )
      self.setDaemon( True )
      self.start()
   def run( self ):
      while True:
         # メインループ
         self.__msg = self.__receive()
         if len( self.__msg ) > 0:
             for line in string.split( self.__msg, '\n' ):
                 if string.split( line )[0] == 'PING':
                     self.__send( 'PONG ' + string.split( line )[1] )
                     self.__event( line )
                 else:
                     break

使うのは、以下のようなイメージで。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import ircc

# イベントハンドラ
def check_message( msg ):
    if msg.find( u"こん" ) > -1:
        irc.privmsg( '#hogefuga', 'こん' )
    irc = ircc.ircc( 'irc.example.com', 6667, 'utf-8' )
    irc.login( 'arianne', 'arianne', 'Arianne the BOT', False )
    irc.join( '#hogefuga' )
    # イベントハンドラを登録
    irc.set_event( check_message )
    while True:
        pass # 何か他の事でもやる

んー、クラス化するメリットが少ないかも。日々使ってないとすぐ忘れるタチなので、wxPython/PyGame なんかと一緒に IRC プロトコルを使った通信将棋やチェスでも書きたいなぁ。

※ 2020/07/02 度重なるブログ移転・ブログシステムのアップデートにより崩れた記事を校正。Python なのにインデントがすべて吹き飛んでいるので間違いがあるかも…。

IRCのDCCファイル送信は簡単に”横取り”する事が可能

IRC(Internet Reray Chat)には、DCCというファイル送受信や個人チャット用途に設けられた機能がある。Direct Crient-to-Crient という名前のとおり、IRCサーバを介しない通信に用いられる。これには、以下の2つの利点と目的がある。

続きを読む IRCのDCCファイル送信は簡単に”横取り”する事が可能