Distributed programming/AutoHotkey

From Rosetta Code
Revision as of 16:36, 9 September 2009 by rosettacode>Glennj (split huge AutoHotkey entry from Distributed program)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Distributed programming/AutoHotkey is part of Distributed Program. You may find other members of Distributed Program at Category:Distributed Program.

WinSock2 library created by derRaphael. Basic code structure created by Trikster. Untested. If someone reading this has two computers and AutoHotkey installed on both, please test. My other computers are all in states of semi-decay.

<lang autohotkey>/*

WinSock2.ahk // a rewrite by derRaphael (w) Sep, 9 2008

based on the WinLIRC Script from Chris

   http://www.autohotkey.com/docs/scripts/WinLIRC.htm 

and on the WinLIRC Rewrite by ZedGecko

   http://www.autohotkey.com/forum/viewtopic.php?t=13829 
   

__WSA_GetHostByName - Parts based upon scripts from DarviK and Tasman. Not much left of the origin source, but it was their achievement by doing the neccessary research.

  • /
WS2 Connect - This establishes a connection to a named resource
The parameter is to be passed in an URI
Port manner.
Returns the socket upon successfull connection, otherwise it
returns -1. In the latter case more Information is in the global
variable __WSA_ErrMsg
Usage-Example
Pop3_Socket
= WS2_Connect("mail.isp.com:110")
See the Doc for more Information.

WS2_Connect(lpszUrl) {

   Global 
   ; split our targetURI 
   __WinINet_InternetCrackURL("info://" lpszUrl,"__WSA") 
   ; name our port 
   WS2_Port := __WSA_nPort 
   
   ; Init the Winsock connection 
   if (     !( __WSA_ScriptInit()           )         ; Init the Scriptvariables 
         || !( __WSA_Startup()              ) ) {     ; Fire up the WSA 
       WS2_CleanUp()         ; Do a premature cleanup 
       return -1             ; and return an error indication 
   } 
   ; check the URI if it's valid 
   if (RegExMatch(__WSA_lpszHostName,"[^\d.]+")) ; Must be different than IP 
   { 
       WS2_IPAddress := __WSA_GetHostByName(__WSA_lpszHostName) 
   } else {     ; Let's check if the IP is valid 
       StringSplit,__WSA_tmpIPFragment, __WSA_lpszHostName,. 
       Loop,4 
           If (    ( __WSA_tmpIPFragment%A_Index%<0   ) 
                || ( __WSA_tmpIPFragment%A_Index%>255 ) 
                || ( __WSA_tmpIPFragment0!=4          ) ) { 
               __WSA_IPerror = 1 
               Break 
           } 
       If (__WSA_IPerror=1) 
           __WSA_ErrMsg .= "No valid IP Supplied" 
       else 
           WS2_IPAddress := __WSA_lpszHostName 
   } 
   ; CONVERSIONS 
   ; The htons function returns the value in TCP/IP network byte order. 
   ; http://msdn.microsoft.com/en-us/library/ms738557(VS.85).aspx 
   __WSA_Port                := DllCall("Ws2_32\htons", "UShort", WS2_Port) 
   ; The inet_addr function converts a string containing an IPv4 dotted-decimal 
   ; address into a proper address for the IN_ADDR structure. 
   ; inet_addr: http://msdn.microsoft.com/en-us/library/ms738563(VS.85).aspx 
   ; IN_ADDR:   http://msdn.microsoft.com/en-us/library/ms738571(VS.85).aspx 
   __WSA_InetAddr           := DllCall("Ws2_32\inet_addr", "Str", WS2_IPAddress) 
   If (     ( __WSA_Socket:=__WSA_Socket() ) 
         && ( __WSA_Connect()              )   ) 
       return __WSA_Socket   ; All went OK, return the SocketID 
   Else { 
       WS2_CleanUp()         ; Do a premature cleanup 
       return -1             ; and return an error indication 
   } 

}

WS2 OnMessage - This function defines, whatever should happen when
a Message is received on the socket.
Expected Parameter
Ws2_Socket => Socket returned from WS2_Connect() Call
UDF => An UserDefinedFunction to which the received
Data will be passed to
Optional Parameter
WindowMessage => A number indicating upon which WM_Message to react
Returns -1 on error, 0 on success

WS2_AsyncSelect(Ws2_Socket,UDF,WindowMessage="") {

   Global __WSA_ErrMsg 
   If (    ( StrLen(Ws2_Socket)=0 ) 
        || ( StrLen(UDF)=0        ) ) { 
       res := -1 
   } else { 
       If (    (StrLen(WindowMessage)=0) 
            || (WindowMessage+0=0)      ) 
           WindowMessage := 0x5000 
       res := __WSA_AsyncSelect(Ws2_Socket, UDF, WindowMessage) 
   } 
   return res 

}

WS2_SendData(WS2_Socket,DataToSend) {

   Global __WSA_ErrMsg 
   If (__WSA_send(WS2_Socket, DataToSend)=-1) { 
       MsgBox, 16, %A_ScriptName%: Send-Error, % __WSA_ErrMsg 
   } 

}

WS2 Cleanup - This needs to be called whenever Your Script exits
Usually this is invoked by some OnExit, Label subroutines.

WS2_CleanUp() {

   DllCall("Ws2_32\WSACleanup") 

}

WS2_Disconnect(WS2_Socket) {

   Global __WSA_ErrMsg 
   if (res := __WSA_CloseSocket(WS2_Socket)) 
       MsgBox, 16, %A_ScriptName%: CloseSocket-Error, % __WSA_ErrMsg 

}

WS2 ScriptInit - for internal use only
Initializes neccessary variables for this Script.

__WSA_ScriptInit() {

   ; CONTANTS 
   ; We're working with version 2 of Winsock 
   Local VersionRequested    := 2 
   ; from http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx 
   Local AF_INET             := 2 
   Local SOCK_STREAM         := 1 
   Local IPPROTO_TCP         := 6 
   Local FD_READ             := 0x1 
   Local FD_CLOSE            := 0x20 
   __AI_PASSIVE              := 1 
   __WSA_WSVersion           := VersionRequested 
   __WSA_SocketType          := SOCK_STREAM 
   __WSA_SocketProtocol      := IPPROTO_TCP 
   __WSA_SocketAF            := AF_INET 
   __WSA_lEvent              := FD_READ|FD_CLOSE 
   __WSA_WOULDBLOCK          := 10035  ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET 
   __WSA_CONNRESET           := 10054  ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET 
   return 1 

}

WS2 Startup - for internal use only
Initializes the Winsock 2 Adapter

__WSA_Startup() {

   Global WSAData, __WSA_ErrMsg, __WSA_WSVersion 
   ; It's a good idea, to have a __WSA_ErrMsg Container, so any Error Msgs 
   ; may be catched by the script. 
   __WSA_ErrMsg := "" 
   ; Generate Structure for the lpWSAData 
   ; as stated on http://msdn.microsoft.com/en-us/library/ms742213.aspx 
   ; More on WSADATA (structure) to be found here: 
   ; http://msdn.microsoft.com/en-us/library/ms741563(VS.85).aspx 
   VarSetCapacity(WSAData, 32) 
   result := DllCall("Ws2_32\WSAStartup", "UShort", __WSA_WSVersion, "UInt", &WSAData) 
   if (ErrorLevel) 
       __WSA_ErrMsg .= "Ws2_32\WSAStartup could not be called due to error " ErrorLevel "`n" 
                     . "Winsock 2.0 or higher is required.`n" 
   if (result!=0) 
       __WSA_ErrMsg .= "Ws2_32\WSAStartup " __WSA_GetLastError() 
   If (StrLen(__WSA_ErrMsg)>0) 
       Return -1 
   Else 
       Return 1 

}

WS2 Socket Descriptor - for internal use only
Sets type and neccessary structures for a successfull connection

__WSA_Socket() {

   Global __WSA_ErrMsg, __WSA_SocketProtocol, __WSA_SocketType, __WSA_SocketAF 
   ; Supposed to return a descriptor referencing the new socket 
   ; http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx 
   __WSA_Socket := DllCall("Ws2_32\socket" 
                           , "Int", __WSA_SocketAF 
                           , "Int", __WSA_SocketType 
                           , "Int", __WSA_SocketProtocol) 
   if (socket = -1) 
       __WSA_ErrMsg .= "Ws2_32\socket " __WSA_GetLastError() 
   If (StrLen(__WSA_ErrMsg)>0) 
       Return -1 
   Else 
       Return __WSA_Socket 

}

WS2 Connection call - for internal use only
Establishes a connection to a foreign IP at the specified port

__WSA_Connect() {

   Global __WSA_ErrMsg, __WSA_Port, __WSA_Socket, __WSA_InetAddr, __WSA_SocketAF 
   ; Generate socketaddr structure for the connect() 
   ; http://msdn.microsoft.com/en-us/library/ms740496(VS.85).aspx 
   __WSA_SockAddrNameLen  := 16 
   VarSetCapacity(__WSA_SockAddr, __WSA_SockAddrNameLen) 
   NumPut(__WSA_SocketAF, __WSA_SockAddr, 0, "UShort") 
   NumPut(__WSA_Port,     __WSA_SockAddr, 2, "UShort") 
   NumPut(__WSA_InetAddr, __WSA_SockAddr, 4) 
   ; The connect function establishes a connection to a specified socket. 
   ; http://msdn.microsoft.com/en-us/library/ms737625(VS.85).aspx 
   result := DllCall("Ws2_32\connect" 
                       , "UInt", __WSA_Socket 
                       , "UInt", &__WSA_SockAddr 
                       , "Int" , __WSA_SockAddrNameLen) 
   if (result) 
       __WSA_ErrMsg .= "Ws2_32\connect " __WSA_GetLastError() 
   If (StrLen(__WSA_ErrMsg)>0) 
       Return -1 
   Else 
       Return 1 

}


/*

This code based originally upon an example by DarviK 
   http://www.autohotkey.com/forum/topic8871.html 
and on the modifcations by Tasman 
   http://www.autohotkey.com/forum/viewtopic.php?t=9937 
  • /
Resolves canonical domainname to IP

__WSA_GetHostByName(url) {

   Global __WSA_ErrMsg 
   ; gethostbyname returns information about a domainname into a Hostent Structure 
   ; http://msdn.microsoft.com/en-us/library/ms738524(VS.85).aspx 
   IP := "" 
   if ((PtrHostent:=DllCall("Ws2_32\gethostbyname","str",url)) != 0) { 
       Loop, 1 ; 3 is max No of retrieved addresses 
           If (PtrTmpIP := NumGet(NumGet(PtrHostent+12)+(offset:=(A_Index-1)*4),offset)) { 
               IP := (IP) ? IP "|" : "" 
               Loop, 4 ; Read our IP address 
                   IP .= NumGet(PtrTmpIP+offset,(A_Index-1 ),"UChar") "." 
               IP := SubStr(IP,1,-1) 
           } else ; No more IPs left 
               Break 
       result := IP 
   } else { 
       __WSA_ErrMsg .= "Ws2_32\gethostbyname failed`n " 
       result := -1 
   } 
   return result 

}

Return the last Error with a lil bit o' text if neccessary
Note
the txt variable is set to 0 when checking for received content

__WSA_GetLastError(txt=1) {

   Err       := DllCall("Ws2_32\WSAGetLastError") 
   ExtraInfo := __WSA_ErrLookUp(RegExReplace(Err,"[^\d]")) 
   If ((InStr(ExtraInfo,"Sorry, no")) || (txt!=1)) 
       ExtraInfo := "" 
   Return ( txt ? "indicated Winsock error " : "") 
          . Err 
          . ( txt ? "`n" ExtraInfo : "") 

}

WS2 AsyncSelect - for internal use only
Sets up an Notification Handler for Receiving Messages
Expected Parameters
Socket from Initialisation
Optional
NotificationMsg - default 0x5000
WSA_DataReiceiver - an different Name to standard
wm_* processor function.
default __WSA_ReceiveData
Returns -1 on Error, 0 on success

__WSA_AsyncSelect(__WSA_Socket, UDF, __WSA_NotificationMsg=0x5000

                             ,__WSA_DataReceiver="__WSA_recv") 

{

   Global 
   __WSA_UDF := UDF 
   OnMessage(__WSA_NotificationMsg, __WSA_DataReceiver) 
   ; The WSAAsyncSelect function requests Windows message-based notification 
   ; of network events for a socket. 
   ; http://msdn.microsoft.com/en-us/library/ms741540(VS.85).aspx 
   Result := DllCall("Ws2_32\WSAAsyncSelect" 
               , "UInt", __WSA_Socket 
               , "UInt", __WSA_GetThisScriptHandle() 
               , "UInt", __WSA_NotificationMsg 
               , "Int",  __WSA_lEvent) 
   if (Result) { 
       __WSA_ErrMsg .= "Ws2_32\WSAAsyncSelect() " . __WSA_GetLastError() 
       Result := -1 
   } 
   Return Result 

}

WS2 Receive - for internal use only
Triggers upon Notification Handler when Receiving Messages

__WSA_recv(wParam, lParam) {

   Global __WSA_UDF, __WSA_ErrMsg 
   ; __WSA_UDF containes the name of the UserDefinedFunction to call when the event 
   ; has been triggered and text may be processed (allthough the reveived text might 
   ; be inclomplete, especially when receiving large chunks of data, like in eMail- 
   ; attachments or sometimes in IRC). The UDF needs to accept two parameter: socket 
   ; and the received buffer 
   
   __WSA_Socket := wParam 
   __WSA_BufferSize = 4096 
   Loop 
   { 
       VarSetCapacity(__WSA_Buffer, __WSA_BufferSize, 0) 
       __WSA_BufferLength := DllCall("Ws2_32\recv" 
                                       , "UInt", __WSA_Socket 
                                       , "Str",  __WSA_Buffer 
                                       , "Int",  __WSA_BufferSize 
                                       , "Int",  0 ) 
       if (__WSA_BufferLength = 0) 
           break 
       if (__WSA_BufferLength = -1) 
       { 
           __WSA_Err := __WSA_GetLastError(0) 
           ; __WSA_WOULDBLOCK (from http://www.sockets.com/) 
           ; The socket is marked as non-blocking (non-blocking operation mode), and 
           ; the requested operation is not complete at this time. The operation is 
           ; underway, but as yet incomplete. 
           if (__WSA_Err = __WSA_WOULDBLOCK ) 
               return 1 
           ; __WSA_CONNRESET: (from http://www.sockets.com/) 
           ; A connection was forcibly closed by a peer. This normally results from 
           ; a loss of the connection on the remote socket due to a timeout or a reboot. 
           if (__WSA_Err != __WSA_CONNRESET) 
               __WSA_ErrMsg .= "Ws2_32\recv indicated Winsock error " __WSA_Err "`n" 
           break 
       } 
       if (StrLen(__WSA_UDF)!=0) ; If set, call UserDefinedFunction and pass Buffer to it 
           %__WSA_UDF%(__WSA_Socket,__WSA_Buffer) 
   } 
   return 1 

}


WSA Send - for internal use only
Users are encouraged to use the WS2_SendData() Function

__WSA_send(__WSA_Socket, __WSA_Data) {

   Global __WSA_ErrMsg 
   Result := DllCall("Ws2_32\send" 
                      , "UInt", __WSA_Socket 
                      , "Str",  __WSA_Data 
                      , "Int", StrLen(__WSA_Data) 
                      , "Int", 0) 
  If (Result = -1) 
     __WSA_ErrMsg .=  "Ws2_32\send " __WSA_GetLastError() 
  Return Result 

}

Closes Open Socket - for internal use only
Returns 0 on success

__WSA_CloseSocket(__WSA_Socket) {

   Global __WSA_ErrMsg 
   Result := DllCall("Ws2_32\closesocket" 
                      , "UInt", __WSA_Socket) 
   If (Result != 0) 
     __WSA_ErrMsg .=  "Ws2_32\closesocket " __WSA_GetLastError() 
   Return result 

}

GetThisScriptHandle - for internal use only
Returns the handle of the executing script

__WSA_GetThisScriptHandle() {

   HiddenWindowsSave := A_DetectHiddenWindows 
   DetectHiddenWindows On 
   ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " DllCall("GetCurrentProcessId")) 
   DetectHiddenWindows %HiddenWindowsSave% 
   Return ScriptMainWindowId 

}

Lookup Winsock ErrCode - for internal use only
This list is form http://www.sockets.com

__WSA_ErrLookUp(sNumber) { WSA_ErrorList = (LTrim Join`n

   10004 - Interrupted system call 
   10009 - Bad file number 
   10013 - Permission denied 
   10014 - Bad address 
   10022 - Invalid argument 
   10024 - Too many open files 
   10035 - Operation would block 
   10036 - Operation now in progress 
   10037 - Operation already in progress 
   10038 - Socket operation on non-socket 
   10039 - D   estination address required 
   10040 - Message too long 
   10041 - Protocol wrong type for socket 
   10042 - Bad protocol option 
   10043 - Protocol not supported 
   10044 - Socket type not supported 
   10045 - Operation not supported on socket 
   10046 - Protocol family not supported 
   10047 - Address family not supported by protocol family 
   10048 - Address already in use 
   10049 - Can't assign requested address 
   10050 - Network is down 
   10051 - Network is unreachable 
   10052 - Net dropped connection or reset 
   10053 - Software caused connection abort 
   10054 - Connection reset by peer 
   10055 - No buffer space available 
   10056 - Socket is already connected 
   10057 - Socket is not connected 
   10058 - Can't send after socket shutdown 
   10059 - Too many references, can't splice 
   10060 - Connection timed out 
   10061 - Connection refused 
   10062 - Too many levels of symbolic links 
   10063 - File name too long 
   10064 - Host is down 
   10065 - No Route to Host 
   10066 - Directory not empty 
   10067 - Too many processes 
   10068 - Too many users 
   10069 - Disc Quota Exceeded 
   10070 - Stale NFS file handle 
   10091 - Network SubSystem is unavailable 
   10092 - WINSOCK DLL Version out of range 
   10093 - Successful WSASTARTUP not yet performed 
   10071 - Too many levels of remote in path 
   11001 - Host not found 
   11002 - Non-Authoritative Host not found 
   11003 - Non-Recoverable errors: FORMERR, REFUSED, NOTIMP 
   11004 - Valid name, no data record of requested type 
   11004 - No address, look for MX record 

) ExNr := 0, ExErr := "Sorry, but no definition available." Loop,Parse,WSA_ErrorList,`n {

   RegExMatch(A_LoopField,"(?P<Nr>\d+) - (?P<Err>.*)",Ex) 
   if (sNumber = ExNr) 
       break 

} Return ExNr " means " ExErr "`n" }

WinINet InternetCrackURL - for internal use only
v 0.1 / (w) 25.07.2008 by derRaphael / zLib-Style release
This routine was originally posted here
http://www.autohotkey.com/forum/viewtopic.php?p=209957#209957

__WinINet_InternetCrackURL(lpszUrl,arrayName="URL") {

   local hModule, offset_name_length 
   hModule := DllCall("LoadLibrary", "Str", "WinINet.Dll") 
   ; SetUpStructures for URL_COMPONENTS / needed for InternetCrackURL 
   ; http://msdn.microsoft.com/en-us/library/aa385420(VS.85).aspx 
   offset_name_length:= "4-lpszScheme-255|16-lpszHostName-1024|28-lpszUserName-1024|" 
                      . "36-lpszPassword-1024|44-lpszUrlPath-1024|52-lpszExtrainfo-1024" 
   VarSetCapacity(URL_COMPONENTS,60,0) 
   ; Struc Size               ; Scheme Size                  ; Max Port Number 
   NumPut(60,URL_COMPONENTS,0), NumPut(255,URL_COMPONENTS,12), NumPut(0xffff,URL_COMPONENTS,24) 
   Loop,Parse,offset_name_length,| 
   { 
       RegExMatch(A_LoopField,"(?P<Offset>\d+)-(?P<Name>[a-zA-Z]+)-(?P<Size>\d+)",iCU_) 
       VarSetCapacity(%iCU_Name%,iCU_Size,0) 
       NumPut(&%iCU_Name%,URL_COMPONENTS,iCU_Offset) 
       NumPut(iCU_Size,URL_COMPONENTS,iCU_Offset+4) 
   } 
   ; Split the given URL; extract scheme, user, pass, authotity (host), port, path, and query (extrainfo) 
   ; http://msdn.microsoft.com/en-us/library/aa384376(VS.85).aspx 
   DllCall("WinINet\InternetCrackUrlA","Str",lpszUrl,"uInt",StrLen(lpszUrl),"uInt",0,"uInt",&URL_COMPONENTS) 
   ; Update variables to retrieve results 
   Loop,Parse,offset_name_length,| 
   { 
       RegExMatch(A_LoopField,"-(?P<Name>[a-zA-Z]+)-",iCU_) 
       VarSetCapacity(%iCU_Name%,-1) 
       %arrayName%_%iCU_Name% := % %iCU_Name% 
   } 
   %arrayName%_nPort:=NumGet(URL_COMPONENTS,24,"uInt") 
   DllCall("FreeLibrary", "UInt", hModule)                    ; unload the library 

}

  1. Persistent
  2. SingleInstance, force
  3. Include WinSock2.ahk ; derRaphaels WinSock Rewrite

SetBatchLines, -1 SetWorkingDir, %A_ScriptDir% Gui, Add, Button, gB1, Button number One! Gui, Add, Edit, vE1, Type in me! Gui, Add, Button, gB2, Press me after you're done typing! Gui, Add, DropDownList, vDDL1 gDDL1, Select something!||One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten Gui, Add, Button, gB3, Click me!

Server  := "irc.freenode.net" Port  := "6667"

Channel := "#UIHjbfilgbafLIEfbAIJCICBGncaiJBHiwehFIUHEIbnjKCINJEhiUAHEJcKLACui" ; Choose something long and random - a channel sure to be deserted.

Nick := "somenick"  ; Register this! You can drop it later if you want, but register it! Pass := "somepass"  ; It can be any nick and any pass.

Name := "distributed program"

BotCmd := "!" OtherBotCmd := "~"

OnExit, CleanUp WS2_CleanUp() If (!Socket := WS2_Connect(Connection := Server . ":" . Port)) {

  MsgBox, 16, Error!, An error occured wilist connecting to %Connection%

}

  ; Set OnMessage function
  WS2_AsyncSelect(Socket, "DataProcess")
  
  ; Send AUTH info to the server
  ; USER A-trivial-nickname a-domain-like-google.com some-trivial-stuff :your-real-name
  WS2_SendData(Socket, "USER " . Nick . " google.com AHKBOT :" . Name . "`n") ; All data send to the server must be
                                                                              ; followed by a newline.
  
  ; PASS A-trivial-pass
  WS2_SendData(Socket, "PASS " . Pass . "`n")
  
  ; NICK A-trivial-nick
  WS2_SendData(Socket, "NICK " . Nick . "`n")
  
  ; Join channel
  ; JOIN A-trivial-channel
  WS2_SendData(Socket, "JOIN " . Channel . "`n")
  
  Gui, Show

Return

DataProcess(Socket, Data) ; OnMessage function {

  global Server,Port,Channel,Nick,Pass,Name,BotCMD
  StringSplit, Param, Data, %A_Space%
  Name := SubStr(Data, 2, InStr(Data, "!")-2)
  StringReplace, Command, Param5, % Chr(10),, All
  StringReplace, Command, Command, % Chr(13),, All
  
  If (Param1 == "PING")
     WS2_SendData(Socket, "PONG " . Param2 . "`n")
  Else If (RegExMatch(Data, ":\" . BotCMD . " "))
  {
     If (Command == "B1"
        MsgBox, The other person pressed Button Number One
     Else If (Command == "B2E1")
     {
        MsgBox, The other person typed in Edit One then pressed Button Two
        Loop
        {
           If (A_Index <= 6)
              continue
           If Param%A_Index%
              out := out . " " . Param%A_Index%
           Else
              break
        }
        MsgBox, The other person typed:%out%
     }
     Else If (Command == "DDL1")
        MsgBox, The other person selected %Param7% in the drop-down-list.
     Else If (Command == "B3")
        MsgBox, The other person clicked Button Three.
  }
  return

} B1: WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B1" return B2: Gui, Submit, NoHide WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B2E1 " . E1 . " | " . B2 return DDL1: Gui, Submit, NoHide WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " DDL1 " . DDL1 return B3: WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B3" return CleanUp: GuiClose: WS2_CleanUp()

ExitApp</lang>

Put the above on one computer, and run it. Then put the below on a different computer (no need for a network, just add Internet!) and run it.

<lang autohotkey>/*

WinSock2.ahk // a rewrite by derRaphael (w) Sep, 9 2008

based on the WinLIRC Script from Chris

   http://www.autohotkey.com/docs/scripts/WinLIRC.htm 

and on the WinLIRC Rewrite by ZedGecko

   http://www.autohotkey.com/forum/viewtopic.php?t=13829 
   

__WSA_GetHostByName - Parts based upon scripts from DarviK and Tasman. Not much left of the origin source, but it was their achievement by doing the neccessary research.

  • /
WS2 Connect - This establishes a connection to a named resource
The parameter is to be passed in an URI
Port manner.
Returns the socket upon successfull connection, otherwise it
returns -1. In the latter case more Information is in the global
variable __WSA_ErrMsg
Usage-Example
Pop3_Socket
= WS2_Connect("mail.isp.com:110")
See the Doc for more Information.

WS2_Connect(lpszUrl) {

   Global 
   ; split our targetURI 
   __WinINet_InternetCrackURL("info://" lpszUrl,"__WSA") 
   ; name our port 
   WS2_Port := __WSA_nPort 
   
   ; Init the Winsock connection 
   if (     !( __WSA_ScriptInit()           )         ; Init the Scriptvariables 
         || !( __WSA_Startup()              ) ) {     ; Fire up the WSA 
       WS2_CleanUp()         ; Do a premature cleanup 
       return -1             ; and return an error indication 
   } 
   ; check the URI if it's valid 
   if (RegExMatch(__WSA_lpszHostName,"[^\d.]+")) ; Must be different than IP 
   { 
       WS2_IPAddress := __WSA_GetHostByName(__WSA_lpszHostName) 
   } else {     ; Let's check if the IP is valid 
       StringSplit,__WSA_tmpIPFragment, __WSA_lpszHostName,. 
       Loop,4 
           If (    ( __WSA_tmpIPFragment%A_Index%<0   ) 
                || ( __WSA_tmpIPFragment%A_Index%>255 ) 
                || ( __WSA_tmpIPFragment0!=4          ) ) { 
               __WSA_IPerror = 1 
               Break 
           } 
       If (__WSA_IPerror=1) 
           __WSA_ErrMsg .= "No valid IP Supplied" 
       else 
           WS2_IPAddress := __WSA_lpszHostName 
   } 
   ; CONVERSIONS 
   ; The htons function returns the value in TCP/IP network byte order. 
   ; http://msdn.microsoft.com/en-us/library/ms738557(VS.85).aspx 
   __WSA_Port                := DllCall("Ws2_32\htons", "UShort", WS2_Port) 
   ; The inet_addr function converts a string containing an IPv4 dotted-decimal 
   ; address into a proper address for the IN_ADDR structure. 
   ; inet_addr: http://msdn.microsoft.com/en-us/library/ms738563(VS.85).aspx 
   ; IN_ADDR:   http://msdn.microsoft.com/en-us/library/ms738571(VS.85).aspx 
   __WSA_InetAddr           := DllCall("Ws2_32\inet_addr", "Str", WS2_IPAddress) 
   If (     ( __WSA_Socket:=__WSA_Socket() ) 
         && ( __WSA_Connect()              )   ) 
       return __WSA_Socket   ; All went OK, return the SocketID 
   Else { 
       WS2_CleanUp()         ; Do a premature cleanup 
       return -1             ; and return an error indication 
   } 

}

WS2 OnMessage - This function defines, whatever should happen when
a Message is received on the socket.
Expected Parameter
Ws2_Socket => Socket returned from WS2_Connect() Call
UDF => An UserDefinedFunction to which the received
Data will be passed to
Optional Parameter
WindowMessage => A number indicating upon which WM_Message to react
Returns -1 on error, 0 on success

WS2_AsyncSelect(Ws2_Socket,UDF,WindowMessage="") {

   Global __WSA_ErrMsg 
   If (    ( StrLen(Ws2_Socket)=0 ) 
        || ( StrLen(UDF)=0        ) ) { 
       res := -1 
   } else { 
       If (    (StrLen(WindowMessage)=0) 
            || (WindowMessage+0=0)      ) 
           WindowMessage := 0x5000 
       res := __WSA_AsyncSelect(Ws2_Socket, UDF, WindowMessage) 
   } 
   return res 

}

WS2_SendData(WS2_Socket,DataToSend) {

   Global __WSA_ErrMsg 
   If (__WSA_send(WS2_Socket, DataToSend)=-1) { 
       MsgBox, 16, %A_ScriptName%: Send-Error, % __WSA_ErrMsg 
   } 

}

WS2 Cleanup - This needs to be called whenever Your Script exits
Usually this is invoked by some OnExit, Label subroutines.

WS2_CleanUp() {

   DllCall("Ws2_32\WSACleanup") 

}

WS2_Disconnect(WS2_Socket) {

   Global __WSA_ErrMsg 
   if (res := __WSA_CloseSocket(WS2_Socket)) 
       MsgBox, 16, %A_ScriptName%: CloseSocket-Error, % __WSA_ErrMsg 

}

WS2 ScriptInit - for internal use only
Initializes neccessary variables for this Script.

__WSA_ScriptInit() {

   ; CONTANTS 
   ; We're working with version 2 of Winsock 
   Local VersionRequested    := 2 
   ; from http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx 
   Local AF_INET             := 2 
   Local SOCK_STREAM         := 1 
   Local IPPROTO_TCP         := 6 
   Local FD_READ             := 0x1 
   Local FD_CLOSE            := 0x20 
   __AI_PASSIVE              := 1 
   __WSA_WSVersion           := VersionRequested 
   __WSA_SocketType          := SOCK_STREAM 
   __WSA_SocketProtocol      := IPPROTO_TCP 
   __WSA_SocketAF            := AF_INET 
   __WSA_lEvent              := FD_READ|FD_CLOSE 
   __WSA_WOULDBLOCK          := 10035  ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET 
   __WSA_CONNRESET           := 10054  ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET 
   return 1 

}

WS2 Startup - for internal use only
Initializes the Winsock 2 Adapter

__WSA_Startup() {

   Global WSAData, __WSA_ErrMsg, __WSA_WSVersion 
   ; It's a good idea, to have a __WSA_ErrMsg Container, so any Error Msgs 
   ; may be catched by the script. 
   __WSA_ErrMsg := "" 
   ; Generate Structure for the lpWSAData 
   ; as stated on http://msdn.microsoft.com/en-us/library/ms742213.aspx 
   ; More on WSADATA (structure) to be found here: 
   ; http://msdn.microsoft.com/en-us/library/ms741563(VS.85).aspx 
   VarSetCapacity(WSAData, 32) 
   result := DllCall("Ws2_32\WSAStartup", "UShort", __WSA_WSVersion, "UInt", &WSAData) 
   if (ErrorLevel) 
       __WSA_ErrMsg .= "Ws2_32\WSAStartup could not be called due to error " ErrorLevel "`n" 
                     . "Winsock 2.0 or higher is required.`n" 
   if (result!=0) 
       __WSA_ErrMsg .= "Ws2_32\WSAStartup " __WSA_GetLastError() 
   If (StrLen(__WSA_ErrMsg)>0) 
       Return -1 
   Else 
       Return 1 

}

WS2 Socket Descriptor - for internal use only
Sets type and neccessary structures for a successfull connection

__WSA_Socket() {

   Global __WSA_ErrMsg, __WSA_SocketProtocol, __WSA_SocketType, __WSA_SocketAF 
   ; Supposed to return a descriptor referencing the new socket 
   ; http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx 
   __WSA_Socket := DllCall("Ws2_32\socket" 
                           , "Int", __WSA_SocketAF 
                           , "Int", __WSA_SocketType 
                           , "Int", __WSA_SocketProtocol) 
   if (socket = -1) 
       __WSA_ErrMsg .= "Ws2_32\socket " __WSA_GetLastError() 
   If (StrLen(__WSA_ErrMsg)>0) 
       Return -1 
   Else 
       Return __WSA_Socket 

}

WS2 Connection call - for internal use only
Establishes a connection to a foreign IP at the specified port

__WSA_Connect() {

   Global __WSA_ErrMsg, __WSA_Port, __WSA_Socket, __WSA_InetAddr, __WSA_SocketAF 
   ; Generate socketaddr structure for the connect() 
   ; http://msdn.microsoft.com/en-us/library/ms740496(VS.85).aspx 
   __WSA_SockAddrNameLen  := 16 
   VarSetCapacity(__WSA_SockAddr, __WSA_SockAddrNameLen) 
   NumPut(__WSA_SocketAF, __WSA_SockAddr, 0, "UShort") 
   NumPut(__WSA_Port,     __WSA_SockAddr, 2, "UShort") 
   NumPut(__WSA_InetAddr, __WSA_SockAddr, 4) 
   ; The connect function establishes a connection to a specified socket. 
   ; http://msdn.microsoft.com/en-us/library/ms737625(VS.85).aspx 
   result := DllCall("Ws2_32\connect" 
                       , "UInt", __WSA_Socket 
                       , "UInt", &__WSA_SockAddr 
                       , "Int" , __WSA_SockAddrNameLen) 
   if (result) 
       __WSA_ErrMsg .= "Ws2_32\connect " __WSA_GetLastError() 
   If (StrLen(__WSA_ErrMsg)>0) 
       Return -1 
   Else 
       Return 1 

}


/*

This code based originally upon an example by DarviK 
   http://www.autohotkey.com/forum/topic8871.html 
and on the modifcations by Tasman 
   http://www.autohotkey.com/forum/viewtopic.php?t=9937 
  • /
Resolves canonical domainname to IP

__WSA_GetHostByName(url) {

   Global __WSA_ErrMsg 
   ; gethostbyname returns information about a domainname into a Hostent Structure 
   ; http://msdn.microsoft.com/en-us/library/ms738524(VS.85).aspx 
   IP := "" 
   if ((PtrHostent:=DllCall("Ws2_32\gethostbyname","str",url)) != 0) { 
       Loop, 1 ; 3 is max No of retrieved addresses 
           If (PtrTmpIP := NumGet(NumGet(PtrHostent+12)+(offset:=(A_Index-1)*4),offset)) { 
               IP := (IP) ? IP "|" : "" 
               Loop, 4 ; Read our IP address 
                   IP .= NumGet(PtrTmpIP+offset,(A_Index-1 ),"UChar") "." 
               IP := SubStr(IP,1,-1) 
           } else ; No more IPs left 
               Break 
       result := IP 
   } else { 
       __WSA_ErrMsg .= "Ws2_32\gethostbyname failed`n " 
       result := -1 
   } 
   return result 

}

Return the last Error with a lil bit o' text if neccessary
Note
the txt variable is set to 0 when checking for received content

__WSA_GetLastError(txt=1) {

   Err       := DllCall("Ws2_32\WSAGetLastError") 
   ExtraInfo := __WSA_ErrLookUp(RegExReplace(Err,"[^\d]")) 
   If ((InStr(ExtraInfo,"Sorry, no")) || (txt!=1)) 
       ExtraInfo := "" 
   Return ( txt ? "indicated Winsock error " : "") 
          . Err 
          . ( txt ? "`n" ExtraInfo : "") 

}

WS2 AsyncSelect - for internal use only
Sets up an Notification Handler for Receiving Messages
Expected Parameters
Socket from Initialisation
Optional
NotificationMsg - default 0x5000
WSA_DataReiceiver - an different Name to standard
wm_* processor function.
default __WSA_ReceiveData
Returns -1 on Error, 0 on success

__WSA_AsyncSelect(__WSA_Socket, UDF, __WSA_NotificationMsg=0x5000

                             ,__WSA_DataReceiver="__WSA_recv") 

{

   Global 
   __WSA_UDF := UDF 
   OnMessage(__WSA_NotificationMsg, __WSA_DataReceiver) 
   ; The WSAAsyncSelect function requests Windows message-based notification 
   ; of network events for a socket. 
   ; http://msdn.microsoft.com/en-us/library/ms741540(VS.85).aspx 
   Result := DllCall("Ws2_32\WSAAsyncSelect" 
               , "UInt", __WSA_Socket 
               , "UInt", __WSA_GetThisScriptHandle() 
               , "UInt", __WSA_NotificationMsg 
               , "Int",  __WSA_lEvent) 
   if (Result) { 
       __WSA_ErrMsg .= "Ws2_32\WSAAsyncSelect() " . __WSA_GetLastError() 
       Result := -1 
   } 
   Return Result 

}

WS2 Receive - for internal use only
Triggers upon Notification Handler when Receiving Messages

__WSA_recv(wParam, lParam) {

   Global __WSA_UDF, __WSA_ErrMsg 
   ; __WSA_UDF containes the name of the UserDefinedFunction to call when the event 
   ; has been triggered and text may be processed (allthough the reveived text might 
   ; be inclomplete, especially when receiving large chunks of data, like in eMail- 
   ; attachments or sometimes in IRC). The UDF needs to accept two parameter: socket 
   ; and the received buffer 
   
   __WSA_Socket := wParam 
   __WSA_BufferSize = 4096 
   Loop 
   { 
       VarSetCapacity(__WSA_Buffer, __WSA_BufferSize, 0) 
       __WSA_BufferLength := DllCall("Ws2_32\recv" 
                                       , "UInt", __WSA_Socket 
                                       , "Str",  __WSA_Buffer 
                                       , "Int",  __WSA_BufferSize 
                                       , "Int",  0 ) 
       if (__WSA_BufferLength = 0) 
           break 
       if (__WSA_BufferLength = -1) 
       { 
           __WSA_Err := __WSA_GetLastError(0) 
           ; __WSA_WOULDBLOCK (from http://www.sockets.com/) 
           ; The socket is marked as non-blocking (non-blocking operation mode), and 
           ; the requested operation is not complete at this time. The operation is 
           ; underway, but as yet incomplete. 
           if (__WSA_Err = __WSA_WOULDBLOCK ) 
               return 1 
           ; __WSA_CONNRESET: (from http://www.sockets.com/) 
           ; A connection was forcibly closed by a peer. This normally results from 
           ; a loss of the connection on the remote socket due to a timeout or a reboot. 
           if (__WSA_Err != __WSA_CONNRESET) 
               __WSA_ErrMsg .= "Ws2_32\recv indicated Winsock error " __WSA_Err "`n" 
           break 
       } 
       if (StrLen(__WSA_UDF)!=0) ; If set, call UserDefinedFunction and pass Buffer to it 
           %__WSA_UDF%(__WSA_Socket,__WSA_Buffer) 
   } 
   return 1 

}


WSA Send - for internal use only
Users are encouraged to use the WS2_SendData() Function

__WSA_send(__WSA_Socket, __WSA_Data) {

   Global __WSA_ErrMsg 
   Result := DllCall("Ws2_32\send" 
                      , "UInt", __WSA_Socket 
                      , "Str",  __WSA_Data 
                      , "Int", StrLen(__WSA_Data) 
                      , "Int", 0) 
  If (Result = -1) 
     __WSA_ErrMsg .=  "Ws2_32\send " __WSA_GetLastError() 
  Return Result 

}

Closes Open Socket - for internal use only
Returns 0 on success

__WSA_CloseSocket(__WSA_Socket) {

   Global __WSA_ErrMsg 
   Result := DllCall("Ws2_32\closesocket" 
                      , "UInt", __WSA_Socket) 
   If (Result != 0) 
     __WSA_ErrMsg .=  "Ws2_32\closesocket " __WSA_GetLastError() 
   Return result 

}

GetThisScriptHandle - for internal use only
Returns the handle of the executing script

__WSA_GetThisScriptHandle() {

   HiddenWindowsSave := A_DetectHiddenWindows 
   DetectHiddenWindows On 
   ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " DllCall("GetCurrentProcessId")) 
   DetectHiddenWindows %HiddenWindowsSave% 
   Return ScriptMainWindowId 

}

Lookup Winsock ErrCode - for internal use only
This list is form http://www.sockets.com

__WSA_ErrLookUp(sNumber) { WSA_ErrorList = (LTrim Join`n

   10004 - Interrupted system call 
   10009 - Bad file number 
   10013 - Permission denied 
   10014 - Bad address 
   10022 - Invalid argument 
   10024 - Too many open files 
   10035 - Operation would block 
   10036 - Operation now in progress 
   10037 - Operation already in progress 
   10038 - Socket operation on non-socket 
   10039 - D   estination address required 
   10040 - Message too long 
   10041 - Protocol wrong type for socket 
   10042 - Bad protocol option 
   10043 - Protocol not supported 
   10044 - Socket type not supported 
   10045 - Operation not supported on socket 
   10046 - Protocol family not supported 
   10047 - Address family not supported by protocol family 
   10048 - Address already in use 
   10049 - Can't assign requested address 
   10050 - Network is down 
   10051 - Network is unreachable 
   10052 - Net dropped connection or reset 
   10053 - Software caused connection abort 
   10054 - Connection reset by peer 
   10055 - No buffer space available 
   10056 - Socket is already connected 
   10057 - Socket is not connected 
   10058 - Can't send after socket shutdown 
   10059 - Too many references, can't splice 
   10060 - Connection timed out 
   10061 - Connection refused 
   10062 - Too many levels of symbolic links 
   10063 - File name too long 
   10064 - Host is down 
   10065 - No Route to Host 
   10066 - Directory not empty 
   10067 - Too many processes 
   10068 - Too many users 
   10069 - Disc Quota Exceeded 
   10070 - Stale NFS file handle 
   10091 - Network SubSystem is unavailable 
   10092 - WINSOCK DLL Version out of range 
   10093 - Successful WSASTARTUP not yet performed 
   10071 - Too many levels of remote in path 
   11001 - Host not found 
   11002 - Non-Authoritative Host not found 
   11003 - Non-Recoverable errors: FORMERR, REFUSED, NOTIMP 
   11004 - Valid name, no data record of requested type 
   11004 - No address, look for MX record 

) ExNr := 0, ExErr := "Sorry, but no definition available." Loop,Parse,WSA_ErrorList,`n {

   RegExMatch(A_LoopField,"(?P<Nr>\d+) - (?P<Err>.*)",Ex) 
   if (sNumber = ExNr) 
       break 

} Return ExNr " means " ExErr "`n" }

WinINet InternetCrackURL - for internal use only
v 0.1 / (w) 25.07.2008 by derRaphael / zLib-Style release
This routine was originally posted here
http://www.autohotkey.com/forum/viewtopic.php?p=209957#209957

__WinINet_InternetCrackURL(lpszUrl,arrayName="URL") {

   local hModule, offset_name_length 
   hModule := DllCall("LoadLibrary", "Str", "WinINet.Dll") 
   ; SetUpStructures for URL_COMPONENTS / needed for InternetCrackURL 
   ; http://msdn.microsoft.com/en-us/library/aa385420(VS.85).aspx 
   offset_name_length:= "4-lpszScheme-255|16-lpszHostName-1024|28-lpszUserName-1024|" 
                      . "36-lpszPassword-1024|44-lpszUrlPath-1024|52-lpszExtrainfo-1024" 
   VarSetCapacity(URL_COMPONENTS,60,0) 
   ; Struc Size               ; Scheme Size                  ; Max Port Number 
   NumPut(60,URL_COMPONENTS,0), NumPut(255,URL_COMPONENTS,12), NumPut(0xffff,URL_COMPONENTS,24) 
   Loop,Parse,offset_name_length,| 
   { 
       RegExMatch(A_LoopField,"(?P<Offset>\d+)-(?P<Name>[a-zA-Z]+)-(?P<Size>\d+)",iCU_) 
       VarSetCapacity(%iCU_Name%,iCU_Size,0) 
       NumPut(&%iCU_Name%,URL_COMPONENTS,iCU_Offset) 
       NumPut(iCU_Size,URL_COMPONENTS,iCU_Offset+4) 
   } 
   ; Split the given URL; extract scheme, user, pass, authotity (host), port, path, and query (extrainfo) 
   ; http://msdn.microsoft.com/en-us/library/aa384376(VS.85).aspx 
   DllCall("WinINet\InternetCrackUrlA","Str",lpszUrl,"uInt",StrLen(lpszUrl),"uInt",0,"uInt",&URL_COMPONENTS) 
   ; Update variables to retrieve results 
   Loop,Parse,offset_name_length,| 
   { 
       RegExMatch(A_LoopField,"-(?P<Name>[a-zA-Z]+)-",iCU_) 
       VarSetCapacity(%iCU_Name%,-1) 
       %arrayName%_%iCU_Name% := % %iCU_Name% 
   } 
   %arrayName%_nPort:=NumGet(URL_COMPONENTS,24,"uInt") 
   DllCall("FreeLibrary", "UInt", hModule)                    ; unload the library 

}

  1. Persistent
  2. SingleInstance, force
  3. Include WinSock2.ahk ; derRaphaels WinSock Rewrite

SetBatchLines, -1 SetWorkingDir, %A_ScriptDir% Gui, Add, Button, gB1, Button number One! Gui, Add, Edit, vE1, Type in me! Gui, Add, Button, gB2, Press me after you're done typing! Gui, Add, DropDownList, vDDL1 gDDL1, Select something!||One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten Gui, Add, Button, gB3, Click me!

Server  := "irc.freenode.net" Port  := "6667"

Channel := "#UIHjbfilgbafLIEfbAIJCICBGncaiJBHiwehFIUHEIbnjKCINJEhiUAHEJcKLACui" ; Choose something long and random - a channel sure to be deserted.

Nick := "someothernick"  ; Register this! You can drop it later if you want, but register it! Pass := "someotherpass"  ; It can be any nick and any pass.

Name := "distributed program"

BotCmd := "~" OtherBotCmd := "!"

OnExit, CleanUp WS2_CleanUp() If (!Socket := WS2_Connect(Connection := Server . ":" . Port)) {

  MsgBox, 16, Error!, An error occured wilist connecting to %Connection%

}

  ; Set OnMessage function
  WS2_AsyncSelect(Socket, "DataProcess")
  
  ; Send AUTH info to the server
  ; USER A-trivial-nickname a-domain-like-google.com some-trivial-stuff :your-real-name
  WS2_SendData(Socket, "USER " . Nick . " google.com AHKBOT :" . Name . "`n") ; All data send to the server must be
                                                                              ; followed by a newline.
  
  ; PASS A-trivial-pass
  WS2_SendData(Socket, "PASS " . Pass . "`n")
  
  ; NICK A-trivial-nick
  WS2_SendData(Socket, "NICK " . Nick . "`n")
  
  ; Join channel
  ; JOIN A-trivial-channel
  WS2_SendData(Socket, "JOIN " . Channel . "`n")
  
  Gui, Show

Return

DataProcess(Socket, Data) ; OnMessage function {

  global Server,Port,Channel,Nick,Pass,Name,BotCMD
  StringSplit, Param, Data, %A_Space%
  Name := SubStr(Data, 2, InStr(Data, "!")-2)
  StringReplace, Command, Param5, % Chr(10),, All
  StringReplace, Command, Command, % Chr(13),, All
  
  If (Param1 == "PING")
     WS2_SendData(Socket, "PONG " . Param2 . "`n")
  Else If (RegExMatch(Data, ":\" . BotCMD . " "))
  {
     If (Command == "B1"
        MsgBox, The other person pressed Button Number One
     Else If (Command == "B2E1")
     {
        MsgBox, The other person typed in Edit One then pressed Button Two
        Loop
        {
           If (A_Index <= 6)
              continue
           If Param%A_Index%
              out := out . " " . Param%A_Index%
           Else
              break
        }
        MsgBox, The other person typed:%out%
     }
     Else If (Command == "DDL1")
        MsgBox, The other person selected %Param7% in the drop-down-list.
     Else If (Command == "B3")
        MsgBox, The other person clicked Button Three.
  }
  return

} B1: WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B1" return B2: Gui, Submit, NoHide WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B2E1 " . E1 . " | " . B2 return DDL1: Gui, Submit, NoHide WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " DDL1 " . DDL1 return B3: WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B3" return CleanUp: GuiClose: WS2_CleanUp()

ExitApp</lang>