First: a word of warning. This stuff might not be obvious. If you've just learned AHK or are coming here from a search for "IRC bot" then this probably isn't what you want, this is a framework to build around, not a finished product with a fancy GUI for changing settings and stuff. It isn't even something you can change a few variables at the top and have it work. If you want the extremely limited channel management that I have so far coded into it, then it might be for you, replace Freenode with your target server and #botwar with your target channel (make sure you have ops and the other ops agree with your choice to bring a bot in first!), but it is still a work in progress (and indeed I will still be working on the channel management bit, the overall framework is complete - if anyone wishes I'll post the channel management code when I'm done with it).
With that out of the way, for a while now I've been getting by with
WinSock2 and
trik's framework for building small, simple IRC bots. I made several plans for building a modular framework and drafted some sample code for it, only to scrap it a week later because I couldn't read a word of it. I also tried to get rid of JOIN/PART spam by writing the socket to a file, reloading, reading it, and reconnecting as fast as my computer could, which... didn't work. So I sort of gave up on that too.
Then yesterday, I had an idea. I remembered two sample scripts in OnMessage()'s docs, and checked them out. With some time to spend, I coded this. I use it for channel management.
It has three files for now, an INI for settings and two AHKs, one for communicating with the IRC server, and the other for determining what to do and say. Both AHK scripts must be running at the same time (I've been trying to find some way to merge the two icons into the same one, which you rightclick for the two names, which you click or hover over for the normal context menu, but I don't know how to do that in a general manner. Also, it defaults to a user, nick, and name of "bot" and I forgot what to put as the 3rd and 4th parameters of USER. Also, it isn't completely modular as the commands aren't each in a separate file or whatever (one file is used for handling the IRC connection, and the other for determining what to say or do), but that should be rather simple given this framework. I'm too tired to explain how in case it isn't.
I won't say anything about how to add your own commands or functionality or anything to try to stave off skiddies trying to build a warbot: just examine the code, I tried to make it self-explanatory (no comments though).
So, without further ado, other than that you have to have
WinSock2 in your standard library and that all three files must be in the same directory (and that you cannot rename the files):
IRC.ahk:
Code:
#SingleInstance FORCE
#Persistent
SetBatchLines, -1
Run, %A_ScriptDir%\bot.ahk
OnExit, CleanUp
OnMessage(0x4a, "GetMessage")
WS2_AsyncSelect(socket := WS2_Connect("irc.freenode.net:6667"), "ReadIRC")
SendIRC("NICK bot")
SendIRC("USER bot * * :name")
SendIRC("JOIN #botwar")
Return
CleanUp:
SendIRC("QUIT")
SendMessage("eXiT")
WS2_CleanUp()
ExitApp
ReadIRC(socket, data)
{
data := RegExReplace(data, "[\r\n]*$")
If InStr(data, "`r`n"){
Loop, PARSE, data, `n, `r
ReadIRC(socket, A_LoopField)
Return
}
Return SendMessage(data)
}
GetMessage(wParam, lParam)
{
If((StrLen := DllCall("lstrlen", UInt, NumGet(lParam + 8))) > 0){
VarSetCapacity(ToSend, StrLen)
DllCall("lstrcpy", "str", ToSend, "uint", NumGet(lParam + 8))
If(ToSend == "eXiT")
ExitApp
SendIRC(ToSend)
}
Return 1
}
SendMessage(data)
{
VarSetCapacity(CopyDataStruct, 12, 0)
NumPut(StrLen(data) + 1, CopyDataStruct, 4)
NumPut(&data, CopyDataStruct, 8)
Prev_DetectHiddenWindows := A_DetectHiddenWindows
Prev_TitleMatchMode := A_TitleMatchMode
DetectHiddenWindows On
SetTitleMatchMode 2
SendMessage, 0x4a, 0, &CopyDataStruct,, bot.ahk ahk_class AutoHotkey
DetectHiddenWindows %Prev_DetectHiddenWindows%
SetTitleMatchMode %Prev_TitleMatchMode%
Return ErrorLevel
}
SendIRC(data)
{
Global socket
Return WS2_SendData(socket, data . "`r`n")
}
bot.ahk:Code:
#SingleInstance FORCE
#Persistent
SetBatchLines, -1
OnMessage(0x4a, "Get")
Send("PRIVMSG #botwar :Reloaded.")
Return
Get(wParam, lParam)
{
If((StrLen := DllCall("lstrlen", UInt, NumGet(lParam + 8))) > 0){
VarSetCapacity(data, StrLen)
DllCall("lstrcpy", "str", data, "uint", NumGet(lParam + 8))
If(data == "eXiT")
ExitApp
Respond(data)
}
Return 1
}
Send(data)
{
VarSetCapacity(CopyDataStruct, 12, 0)
NumPut(StrLen(data) + 1, CopyDataStruct, 4)
NumPut(&data, CopyDataStruct, 8)
Prev_DetectHiddenWindows := A_DetectHiddenWindows
Prev_TitleMatchMode := A_TitleMatchMode
DetectHiddenWindows ON
SetTitleMatchMode 2
SendMessage, 0x4a, 0, &CopyDataStruct,, IRC.ahk ahk_class AutoHotkey
DetectHiddenWindows %Prev_DetectHiddenWindows%
SetTitleMatchMode %Prev_TitleMatchMode%
Return ErrorLevel
}
GetInfo(key, Section)
{
IniRead, info, %A_ScriptDir%\bot.ini, %Section%, %key%, %A_Space%
Return info
}
SetInfo(key, Section, info = "")
{
If(info != "")
IniWrite, %info%, %A_ScriptDir%\bot.ini, %Section%, %key%
Else
IniDelete, %A_ScriptDir%\bot.ini, %Section%, %key%
}
Respond(data)
{
StringSplit, param, data, %A_Space%
name := SubStr(param1, 2, InStr(param1, "!") - 2)
If(param1 == "PING")
Send("PONG " param2)
Else If(param2 == "433"){
Random, rand, -9, 99
Send("NICK bot" . rand)
Send("JOIN #botwar")
SetTimer, Nick, 60000
;Add More Code Here;
}
Nick:
Send("NICK bot")
Return
EDIT: channel management code removed because it is still under development and I want this to be done enough so that none of the existing code must be modified, ask me if you want it. The functions GetInfo and SetInfo are remnants of that, which you might want to use for between-session storage. GetInfo(thing, category) and SetInfo(thing, category, newvalue) - leave newvalue out to delete the info.
UPDATE: I'll be making this send by PID when I next feel like working on it. IRC script sends its PID as a command line parameter and knows the thinking scripts PID using Runs 4th parameter. That would let you run more than one bot at the same time. But how would the thinking script keep track over reloads? Hm.