基本
無駄がなくて綺麗なコード。やっぱり 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 なのにインデントがすべて吹き飛んでいるので間違いがあるかも…。