IRCサーバに指定ユーザのログイン状況が知りたい

netcat(The GNU Netcat -- Official homepage)越しにirc語は話したことがあったけど,なにもないとこから書くのも面倒なのと,どうせクライアントのスケルトンがあると思ったらあったのでPython でシンプルな IRC クライアントを作成するを参考にircクライアントをpython3のunicode文字とbytearrayの使い方を理解するために書いてみた.あとircクライアントのpythonライブラリは探してもいない.

# -*- coding: utf-8 -*-
"""
 refer: http://www.codereading.com/codereading/python/python-irc-client.html
 @date: 2010/02/23
 @author: karronoli
 Python3.1.1 irc client test for WIDE server
"""
import socket
import difflib
import sys

SERVER = 'irc.tokyo.wide.ad.jp'
PORT = 6667
NICKNAME = 'your_nickname'
CHARCODE = 'iso2022_jp'
#CHANNEL = '#test_py'

#open a socket to handle the connection
IRC = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
IRC.settimeout(3.0)
#open a connection with the server
def irc_conn():
    IRC.connect((SERVER, PORT))

#simple function to send data through the socket
def send_data(command):
    status = IRC.send((command + "\r\n").encode(CHARCODE)) # unicode -> iso2022_jp
    if status == -1 : raise Exception('send_data error', status)


#join the channel
def join(channel):
    send_data('JOIN ' + channel)

#send login data (customizable)
def login(nickname, username='yourname', password = None, realname='yourname', hostname='hoge', servername='hoge'):
    send_data('USER %s %s %s %s' % (username, hostname, servername, realname))
    send_data("NICK " + nickname)

def get_motd():
    buffer = bytearray()
    """
    # 内包表記で書きたかったけど失敗した残骸
    # motd size(WIDE):about 4650bytes, append method is bad for only 1bytes append
    [buffer.append(tmp) for tmp in IRC.recv(1024) if buffer.find(b"End of MOTD command") < 0]
    time.sleep(3)
    [buffer.append(tmp) for tmp in IRC.recv(4096) if buffer.find(b"End of MOTD command") < 0]
    """
    #[buffer.append(tmp) for tmp in IRC.recv(8192) if buffer.find(b"End of MOTD command") < 0]

    #appendは1byte単位しか追加できないけど原因はrecvが途中で止まるせいだと思う
    #[buffer.append(tmp) for tmp in IRC.recv(1) if buffer.find(b"End of MOTD command") < 0]


    while buffer.find(b"End of MOTD command") < 0 and len(buffer) < 5120:
        buffer += IRC.recv(1024)
    
    return buffer.decode(CHARCODE) # iso2022_jp -> unicode
    #return IRC.recv(8192).decode(CHARCODE) #一回で読み込むことはできない
    
    
def is_present(user):
    """
    #下のと同じだけどなんとなく残してる
    send_data('ISON ' + user)
    search_user = IRC.recv(1024).decode(CHARCODE) # iso2022_jp -> unicode
    if len(search_user.split(':')[1]) == 0:
        return False
    else:
        return True
    """

    print('Search user ' + user)
    send_data("ISON " + user)
    #search_user = IRC.recv(1024).decode(CHARCODE).split(':') # iso2022_jp -> unicode
    #search_user = []
    #[search_user.append(tmp.strip()) for tmp in IRC.recv(1024).decode('iso2022_jp').split(':') if tmp.strip() != '']
    search_user = [tmp.strip() for tmp in IRC.recv(1024).decode('iso2022_jp').split(':') if tmp.strip() != '']
    print(search_user)
    if (len(search_user) > 1):
        return True
    else:
        return False

    """
    # この場合現実的じゃないけどdiffを使ってみた.
    # 自分がログインしたときのISONの返り値と探したいユーザのを:区切りの単語単位でdiffる
    send_data('ISON ' + NICKNAME)
    my = IRC.recv(1024).decode(CHARCODE).split(":")
    #diff = difflib.ndiff(search_user, my)
    for buf in difflib.ndiff(search_user, my):
        #print(buf)
        diff_status = buf[0:2]
        if (diff_status == "- " or diff_status == "+ "):
            _user = buf.split(":")[1]
            if len(_user) == 0:
                print("%s is absent" % user)
                return False
            else:
                print("%s is present" % _user)
                return True
                #print(buf.split(":")[1], )
    """

if __name__ == '__main__':
    irc_conn()
    login(NICKNAME)
    #join(CHANNEL)
    
    motd = get_motd()
    print(motd)
    # windowsで動かしてたのでcp932とか使った
    open('motd.txt', 'wb').write(motd.encode('cp932'))
    print('motd ended.')
    
    print(is_present('ison_test_no_user_expected'))
    print(is_present(NICKNAME))
    send_data('WHOIS karronoli') # 自分が普段使うユーザ名
    print(IRC.recv(1024).decode(CHARCODE))
    send_data('WHOIS ' + NICKNAME)
    print(IRC.recv(1024).decode(CHARCODE))
    send_data('EXIT')
    IRC.close()

ちょっと気に食わないけど土台としてはほぼお決まりな感じなので備忘録がてら貼り付けた.やっぱpythonで新規に何か書くときはpython3だな.文字コードをちゃんと意識してますよ感が結構好き.あと内包表記のスコープとか.encode/decodeはどっちがどっちか良く分からなくなるので必ずコメントを足すようにしてる.

ところでpidginのユーザ情報の取得が異様に遅い気がするんだけど生ソケット越しのWHOISの応答はすぐに返ってくる.どうしてなんだろ?