IRCサーバーをWindowsでリンクさせようと足掻いては見たものの、全く出来ずただ時間を無駄にしてしまった。
そんなわけで、やけになってHSPでモジュール変数を利用したIRCクライアントを作ってみた。
C++でいうオブジェクト指向のような形でプログラムが書けるので、サーバー単位でオブジェクトを生成。
newmod irc,ircc, 0, "127.0.0.1", 6667, "", "NICKNAME", "USERNAME", "HOST NAME", "SERVER NAME", "REAL NAME", 63, 1
この場合、127.0.0.1 が、接続先のIRCサーバーになり、6667はポートになります。
newmodでオブジェクトを生成したら、その下にmod_ircc_connectを定義します。ircというのはモジュール変数です。C++でいえば、クラスのようなもの。
mod_ircc_connect ircif stat==1{
title "connected"oncmd gosub *IRCC_TIMER, WM_TIMER
}
接続に成功すると、*IRCC_TIMERラベルにWM_TIMERメッセージが受け取られるたびにジャンプするようになります。
*IRCC_TIMER
mod_ircc_timer ircif stat==1{
mod_ircc_join irc, "#test,#test2,#test3"}
IRCC_TIMERラベルには、mod_ircc_timer命令を定義し
戻り値(stat)には、正常終了なら0、最初に接続が成功したときだけ1が返ります。
この場合、statの値が1になった場合に、mod_ircc_joinが呼び出されます。
mod_ircc_joinは、チャンネルの入室を行う命令で、指定されたモジュール変数(サーバー)に対して、#test, #test2, #test3 の部屋へ入室するようになります。
IRC制御のほとんどの作業は、このラベルの中で行うようになります。
マクロ機能もあり、mod_ircc_instr_privmsgやmod_ircc_instr_noticeなどの命令を追加することで、ユーザーは、チャンネル参加者の発言に含まれている言葉に反応して、処理を返すことが出来ます。
mod_ircc_instr_privmsg irc, "#test", "時報"if stat==1{
mod_ircc_notice irc, "#test", "今"+gettime(4)+"時です"}
いまのところ入室、退室、PRIVMSG、NOTICE、マクロ、オペレータ権限の配布、PONGの返答などの機能が使えますが、BOTとしては使えてもまだまだクライアントとしてはもうすこし機能がほしいところ。
更新履歴
0.001
- サーバーとの接続とデータの受信用タイマーループを一緒にした
0.002
- mod_ircc_instr系命令のモジュール変数を統一
0.003
- AAなど一括してデータが転送されたときに、mod_ircc_instr系命令で処理が抜けるバグを修正
0.004
0.005
- 発言にスペースが含まれていた場合に、正常に取得できていなかった問題を修正
- 文字コード変換が正常に行われていなかったのを修正
- データが送られてきた場合と、延期された作業がある場合のみデータの処理を行うように修正
- mod_ircc_deconnect命令の追加
- デバッグウィンドウの画面表示を変更
- mod_ircc_timer命令の戻り値を変更
- コマンドの条件分岐方法をelseifからswitchに変更
- 文字列を分割する関数を二重に使うことで、文字列解析を行うようにした
- 使用されていなかったモジュール変数の削除
0.006
- ログをモジュール外に出力する命令を実装
- ログを整形し表示させる命令を実装
mod_ircc.hsp
ご自由にお使いください。また,このモジュールを実行するためには、 pcbnet2プラグインとHSP開発Wiki/文字コード変換モジュール が必要になります。 pcbnet2は、開発元がサイトを閉鎖したために、Shark++氏が再頒布しています。また、文字コード変換モジュールはShark++氏のソースコードです。
ダウンロード:mod_ircc.zip
#include "mod_encode.hsp" #define _irc_debug_ 0#ifndef WM_TIMER#define global WM_TIMER $00000113#endif#module#deffunc ircc_strcut array p1, str p2, str p3, int _limit, local s1, local s2, local s3, local s4, local s5, local s6
s1=p2 s4=p3 s5=strlen(s4)
sdim p1
repeat
s2=instr(s1,s3,p3)if s2=-1{getstr p1(cnt), s1, s6break}else{if cnt>0:if _limit=cnt{
getstr p1(cnt), s1, s6break}
s3+=s2 if s3-s6>0{
p1(cnt)=strmid(s1,s6,s3-s6)
}else{
p1(cnt)=""}
s3+=s5 s6=s3 }
loopreturn#deffunc ircc_strcat var _var, array _array, str _str, local s1
sdim s1,64repeat length(_array)-1s1+=_array(cnt)+_str
loop
s1+=_array(length(_array)-1)
_var=s1return#global#module ircc m_sockid, m_server, m_port, m_pass, m_nickname, m_username, m_hostname, m_realname, m_servername, m_log, m_logsellen, m_timerid, m_debugscreenid, m_new_channel, m_new_command, m_new_p1, m_new_p2, m_new_p3, m_new_p4, m_new_p5, m_connect, m_connected, m_hit, m_channellog, m_channellogname, m_nickname_s, m_nickname_e, m_nickname_accs, m_command_s, m_command_e#include "pcbnet.as"#uselib "user32.dll"#func SetTimer "SetTimer" sptr,sptr,sptr,sptr
#func KillTimer "KillTimer" sptr,sptr
#func PostMessage "PostMessageA" int,int,int,int#define sjis2jis(%1, %2) _FromSJIS@mod_encode %2, CODEPAGE_S_JIS, %1, CODEPAGE_JIS#define ctype jis2sjis(%1) _ToSJIS@mod_encode(%1, CODEPAGE_JIS, CODEPAGE_S_JIS)#modinit int _sockid, str _server, int _port, str _pass, str _nickname, str _username , str _hostname, str _servername, str _realname,int _debugscreenid, int _timerid, local s1
m_sockid = _sockid
m_server = _server
m_port = _port
m_pass = _pass
m_nickname = _nickname
m_username = _username
m_hostname = _hostname
m_servername = _servername
m_realname = _realname
sdim m_channellog
sdim m_channellogname
sdim m_log,64m_logsellen = 30 if _timerid=0{
m_timerid=1}else{
m_timerid=_timerid
}
sdim m_new_channel
sdim m_new_command
sdim m_new_p1
sdim m_new_p2
sdim m_new_p3
sdim m_new_p4
sdim m_new_p5
m_nickname_s="("m_nickname_e=")"m_nickname_accs="*"m_command_s="("m_command_e=")"#ifdef _debugs1=ginfo_selif _debugscreenid=0{
m_debugscreenid=63}else{
m_debugscreenid = _debugscreenid }
dcount++
screen m_debugscreenid, 640,480mesbox m_log,ginfo_winx,ginfo_winy
gsel s1#endifreturn#ifdef _debug#modfunc mod_ircc_logreflesh local _ginfo_sel, local _m_logsize
_ginfo_sel=ginfo_sel
gsel m_debugscreenid
objprm 0,m_log
gsel _ginfo_selreturn#modfunc mod_ircc_debug_title str _title, local _ginfo_sel
_ginfo_sel=ginfo_sel
gsel m_debugscreenid
title _title
gsel _ginfo_selreturn#endif#modfunc mod_ircc_addlog str _str
m_log+=_strnotesel m_log
repeat notemaxif notemax>m_logsellen{
notedel 0continue}
loop
noteunsel#ifdef _debugmod_ircc_logreflesh thismod#endifreturn#modfunc mod_ircc_duplog var _var
dup _var, m_logreturn#modfunc mod_ircc_connect local s1, local s2, local s3, local x1, local x2, local size, local connected
tcpopen m_sockid, m_server, m_port repeat 6000tcpiscon m_sockidif stat==1{
connected=1break}else:if stat==2{ #ifdef _debugmod_ircc_addlog thismod, "IRCC: tcpopen error.\n" #endifbreak}
wait 1loopif connected=1{
SetTimer hwnd, m_timerid, 500, 0}return connected#modfunc mod_ircc_deconnectif varuse(thismod)==0:returnm_connect=0m_connected=0sdim m_new_channel
sdim m_new_command
sdim m_new_p1
sdim m_new_p2
sdim m_new_p3
sdim m_new_p4
sdim m_new_p5
KillTimer hwnd, m_timerid
tcpshut m_sockid
tcpclose m_sockid #ifdef _debugmod_ircc_addlog thismod, "IRCC: サーバーから切断しました" #endifreturn#modfunc mod_ircc_channellogstyle str _nickname_s, str _nickname_e, str _nickname_accs, str _command_s, str _command_e
m_nickname_s=_nickname_s
m_nickname_e=_nickname_e
m_nickname_accs=_nickname_accs
m_command_s=_command_s
m_command_e=_command_ereturn#modfunc mod_ircc_addchannellog str _channelname, str _command, str _p1, str _p2, str _p3, str _p4, str _p5, local fclog, local fccnt, local timelog, local fcnameif _channelname!""{
foreach m_channellognameif m_channellogname(cnt) == _channelname{
fclog=1fccnt=cntbreak}
loop
}else{
fclog=2 }if fclog == 0{if m_channellogname(0)==""{
fccnt=0}else{
fccnt=length(m_channellogname)
}
m_channellogname(fccnt)=_channelname
}
timelog=strf("%02d", gettime(4))+":"+strf("%02d", gettime(5))
foreach m_channellogname
notesel m_channellog(fccnt)switch _commandcase "PRIVMSG"ircc_strcut fcname, _p1, "!"noteadd timelog+" "+m_nickname_s+fcname+m_nickname_e+" "+_p2, -1swbreakcase "NOTICE"ircc_strcut fcname, _p1, "!"noteadd timelog+" "+m_nickname_s+fcname+m_nickname_e+" "+_p2, -1swbreakcase "JOIN"ircc_strcut fcname, _p1, "!"noteadd timelog+" "+m_nickname_accs+fcname+" join "+_channelname+" "+m_command_s+fcname(1)+m_command_e, -1swbreakcase "QUIT"ircc_strcut fcname, _p1, "!"noteadd timelog+" "+m_nickname_accs+fcname+" quit "+m_command_s+_p1+m_command_e, -1swbreakcase "PART"ircc_strcut fcname, _p1, "!"noteadd timelog+" "+m_nickname_accs+fcname+" part "+m_command_s+_p2+m_command_e, -1swbreakcase "TOPIC"ircc_strcut fcname, _p1, "!"noteadd timelog+" "+m_nickname_accs+fcname+" topic : "+_p2, -1swbreak
swend
fccnt++
noteunselif fclog!2:breakloopreturn#modfunc mod_ircc_join str _channelname, local s1if _channelname="":returnmod_ircc_addlog thismod, "JOIN "+_channelname+"\n"ircc_strcut s1,_channelname,","foreach s1
tcpput "JOIN "+s1(cnt)+"\n", m_sockid
loopreturn#modfunc mod_ircc_part str _channelname, str _partmessage, local s1, local s2if _channelname="":returnif _partmessage!""{
s1=" :"+_partmessage
}
mod_ircc_addlog thismod, "PART "+_channelname+s1+"\n"mod_ircc_addchannellog thismod, _channelname, "PART", m_nickname, _partmessage, "", "", ""ircc_strcut s2,_channelname,","foreach s2
tcpput "PART "+s2(cnt)+s1+"\n", m_sockid
loopreturn#modfunc mod_ircc_notice str _channelname, str _message, local s1if _message="":returnif _channelname="":returnmod_ircc_addlog thismod, "NOTICE "+_channelname+" :"+_message+"\n"ircc_strcut s1,_channelname,","foreach s1
mod_ircc_addchannellog thismod, s1(cnt), "NOTICE", m_nickname, _message, "", "", ""tcpput "NOTICE "+s1(cnt)+" :"+_message+"\n", m_sockid
loopreturn#modfunc mod_ircc_privmsg str _channelname, str _message, local s1if _message="":returnif _channelname="":returnmod_ircc_addlog thismod, "privmsg "+_channelname+" :"+_message+"\n"ircc_strcut s1,_channelname,","foreach s1
mod_ircc_addchannellog thismod, s1(cnt), "PRIVMSG", m_nickname, _message, "", "", ""tcpput "privmsg "+s1(cnt)+" :"+_message+"\n", m_sockid
loopreturn#modfunc mod_ircc_vestoper str _channelname, str _nickname, int _flag, local s1if _flag<0:s1="-o":else:s1="+o"mod_ircc_addlog thismod, "MODE "+_channelname+" "+s1+" "+_nickname+"\n"tcpput "MODE "+_channelname+" "+s1+" "+_nickname+"\n", m_sockidreturn#modfunc mod_new_add str _channelname, str _command, str _p1, str _p2, str _p3, str _p4, str _p5
repeatif length(m_new_channel)<=cnt{
m_new_channel(cnt)=_channelname
m_new_command(cnt)=_command
m_new_p1(cnt)=_p1
m_new_p2(cnt)=_p2
m_new_p3(cnt)=_p3
m_new_p4(cnt)=_p4
m_new_p5(cnt)=_p5break}else:if m_new_channel(cnt)=""{
m_new_channel(cnt)=_channelname
m_new_command(cnt)=_command
m_new_p1(cnt)=_p1
m_new_p2(cnt)=_p2
m_new_p3(cnt)=_p3
m_new_p4(cnt)=_p4
m_new_p5(cnt)=_p5break}
loopreturn#modfunc mod_ircc_connect_infosendif m_pass!""{
tcpput "PASS "+m_pass+"\n", m_sockid
mod_ircc_addlog thismod, "PASS "+m_pass+"\n"if stat!0:mod_ircc_addlog thismod, "IRCC: PASSを送れませんでした\n"}
tcpput "NICK "+m_nickname+"\n", m_sockidif stat!0:mod_ircc_addlog thismod, "IRCC: NICKを送れませんでした\n"mod_ircc_addlog thismod, "NICK "+m_nickname+"\n"tcpput "USER "+m_username+" "+m_hostname+" "+m_servername+" :"+m_realname+"\n", m_sockidif stat!0:mod_ircc_addlog thismod, "IRCC: USERを送れませんでした\n"mod_ircc_addlog thismod, "USER "+m_username+" "+m_hostname+" "+m_servername+" :"+m_realname+"\n"return#modfunc mod_ircc_getchannellog var _var, str _channelname, local gcflag
foreach m_channellognameif m_channellogname(cnt) == _channelname{if m_channellog(cnt)!""{
_var = m_channellog(cnt)
m_channellog(cnt)=""gcflag=1}break}
loopif gcflag==0{
_var=""}return gcflag#modfunc mod_ircc_timer local get, local s1, local s2, local s3, local s4, local s5, local s6, local x1, local x2, local x3, local size, local flagif m_connect=0{
mod_ircc_connect_infosend thismod
m_connect=1}
tcpfail m_sockidif stat==2{ #ifdef _debugmod_ircc_addlog thismod, "IRCC :通信が切断されました\nIRCC: このモジュール変数はdelmodによって削除されます\nIRCC :mod_ircc_timerの戻り値に-1が返されました\n開発者はこの戻り値で再接続をするプログラムを書くことが出来ます\n" #endifdelmod thismodreturn -1}
sdim s3,64repeat 120tcpcount s1, m_sockidif s1>0 {
sdim s2, s1+1tcpget s2, s1, m_sockid
s3+=s2
wait 0}else:breakloop#ifdef _debugmod_ircc_debug_title thismod, "【IRCデバッグ】処理スルー回数:"+cc+" 処理回数:"+cc2+" 合計:"+(cc+cc2)#endifif s3==""{if m_hit=0{sdim m_new_channel
sdim m_new_command
sdim m_new_p1
sdim m_new_p2
sdim m_new_p3
sdim m_new_p4
sdim m_new_p5
}else{
m_hit=0flag=2} #ifdef _debugcc++ #endifreturn flag
}else{
s3 = jis2sjis(s3)if m_connected==1 : flag = 2}
notesel s3
repeat notemax
noteget s4,cntif peek(s4)=10: getstr s4 ,s4, 1mod_ircc_addlog thismod, s4+"\n"ircc_strcut s5, s4, ":", 2s6 = length(s5)if s6>=1{
ircc_strcut x1, s5(0), " "if s6>=2{
ircc_strcut x2, s5(1), " "if s6>=3{
x3=s5(2)
}
}
}if x1(0)="PING"{
tcpput "PONG :"+m_nickname+"\n", m_sockid
mod_ircc_addlog thismod, "PONG :"+m_nickname+"\n"flag=0}if length(x2)>=2{switch x2(1)case "JOIN"mod_ircc_addchannellog thismod, x3, x2(1), x2(0), "", "", "", ""mod_new_add thismod, x3, x2(1), x2(0), "", "", "", ""swbreakcase "PRIVMSG"mod_new_add thismod, x2(2), x2(1), x3, "", "", "", ""mod_ircc_addchannellog thismod, x2(2), x2(1), x2(0), x3, "", "", ""swbreakcase "NOTICE"mod_new_add thismod, x2(2), x2(1), x3, "", "", "", ""mod_ircc_addchannellog thismod, x2(2), x2(1), x2(0), x3, "", "", ""swbreakcase "001"mod_ircc_addchannellog thismod, x2(2), x2(1), x3, "", "", "", ""flag=1m_connected=1swbreakcase "433"mod_ircc_addchannellog thismod, x2(2), x2(1), x3, "", "", "", ""m_nickname+="_" mod_ircc_connect_infosend thismod
swbreakcase "QUIT"mod_ircc_addchannellog thismod, "", x2(1), x2(0), x3, "", "", ""mod_new_add thismod, x2(2), x2(1), x3, "", "", "", ""swbreakcase "PART"mod_ircc_addchannellog thismod, x2(2), x2(1), x2(0), x3, "", "", ""mod_new_add thismod, x2(2), x2(1), x3, "", "", "", ""swbreakcase "TOPIC"mod_ircc_addchannellog thismod, x2(2), x2(1), x2(0), x3, "", "", ""mod_new_add thismod, x2(2), x2(1), x3, "", "", "", ""swbreakcase "MODE"mod_ircc_addchannellog thismod, x2(2), x2(1), x2(3), "x2(4)", "", "", ""mod_new_add thismod, x2(2), x2(1), x2(3), "x2(4)", "", "", ""swbreak
swend
}
loop#ifdef _debugcc2++#endifreturn flag
#modfunc mod_new_clear int _cnt
m_new_channel(_cnt)=""m_new_command(_cnt)=""m_new_p1(_cnt)=""m_new_p2(_cnt)=""m_new_p3(_cnt)=""m_new_p4(_cnt)=""m_new_p5(_cnt)=""returnTODOTODOTODO#modfunc mod_ircc_instr_privmsg str _channelname, str _inmessage, int _flag, local _stat
foreach m_new_channelif m_new_channel(cnt)=_channelname{if m_new_command(cnt)="PRIVMSG"{if instr(m_new_p1(cnt), 0, _inmessage)!-1{if _flag=0 : mod_new_clear thismod, cnt
_stat=1m_hit=1break}
}
}
loopreturn _stat#modfunc mod_ircc_instr_notice str _channelname, str _inmessage, int _flag, local _stat
foreach m_new_channelif m_new_channel(cnt)=_channelname{if m_new_command(cnt)="NOTICE"{if instr(m_new_p1(cnt), 0, _inmessage)!-1{if _flag=0 : mod_new_clear thismod, cnt
_stat=1m_hit=1break}
}
}
loopreturn _stat#modfunc mod_ircc_instr_join str _channelname, str _nickname, local _stat, local s1, local s2
ircc_strcut s1, _channelname, ","foreach s1
foreach m_new_channelif m_new_channel(cnt)=s1(s2){if m_new_command(cnt)="JOIN"{if (_nickname=="")|(instr(m_new_p1(cnt), 0, _nickname)!-1){
_stat = 1}
}
}
loop
s2++
loopreturn _stat
#modterm
KillTimer hwnd, m_timerid
tcpshut m_sockid
tcpclose m_sockid
netclearreturn#global#if _irc_debug_gsel 0newmod irc,ircc, 0, "127.0.0.1", 6667, "", "TEST", "TEST", "TEST", "TEST", "TEST", 0, 1mes "接続を開始しています"mod_ircc_connect ircif stat==1{mes "接続が完了しました"title "connected"oncmd gosub *IRCC_TIMER, WM_TIMER
sdim clog,64mesbox clog, ginfo_winx,200}else{mes "接続に失敗しました"}
stop*IRCC_TIMER
mod_ircc_timer irc switch statcase 1mes "サーバーに登録が完了しました"mod_ircc_join irc, "#test,#test2,#test3,#test4,#test5" mod_ircc_part irc, "#test3,#test4,#test5", "Bye" swbreakcase 2mes "データ受信"mod_ircc_instr_join irc, "#test,#test2", "TEST"if stat{
mod_ircc_privmsg irc, "#test,#test2", "やあこんばんは" }
mod_ircc_instr_privmsg irc, "#test", "時報" if stat{
mod_ircc_notice irc,"#test","今"+gettime(4)+"時"+gettime(5)+"分です" }
mod_ircc_instr_privmsg irc, "#test", "再起動" if stat{
mod_ircc_addlog irc, "再起動開始\n" *@
mod_ircc_deconnect irc
mod_ircc_connect irc if stat==1{title "connected"oncmd gosub *IRCC_TIMER, WM_TIMER
}else{mes "接続できませんでした1分後に再接続します"wait 6000goto *@b
}
}
swbreakcase -1*@
mes "サーバーから切断されました1分後に再接続します"wait 6000mod_ircc_deconnect irc
mod_ircc_connect irc if stat==1{title "connected"oncmd gosub *IRCC_TIMER, WM_TIMER
}else{mes "接続できませんでした"goto *@b
}
wait 0swbreak
swendmod_ircc_getchannellog irc, log, "#test"if stat{
clog+=log
objprm 0,clog
}return#endif