Creating Plugins for Dunking Parrot Chat
Dunking Parrot Chat allows you to write "plugins" to enhance the functionality of the product. A plugin is a standalone program that can be launched from the chat client, and that returns data to the client to be displayed in the main chat window. A plugin could be written to go out to the web and look up, for example, stock quotes, the weather, or headline news. This tutorial describes how to create a simple plugin using Microsoft Visual Basic 6.0
How Plugins Are Launched
When a chat user decides to execute a plugin, he types a backslash
("\"), followed by the name of the plugin, then a blank space, then any
parameters that he wishes to pass to the plugin. The chat client will then create a text
file that has the same name as the plugin. This text file contains the port number that
the client receives UDP data on, as well as the parameters that the user wishes to pass to
the plugin. In this tutorial, we will assume that we are writing a plugin named
"stock" that will go out to the web and retrieve stock quotes. Let's
assume that the user wants to execute the stock plugin to look up the stocks for Red Hat
(symbol RHAT) and Microsoft (symbol MSFT). Let us also assume that the user is using
port 3209 to receive UDP data in the chat client. To launch this plugin, the user
would type:
\stock rhat msft
The chat client will create a text file named "stock.txt", and will attempt to launch "stock.exe" from the chat directory. The text file will contain two lines, which look like this:
Port=3209
Data=rhat msft
It is the responsibility of the plugin to parse this text file and properly handle the data read therein.
Getting Started Writing a Plugin
Create a Visual Basic project with a single form (frmMain in our example).
The VISIBILE property of this form should be set to FALSE so that the form does not show
up while the plugin is running. Alternatively, you could set the property to TRUE
and use the form to display progress information while your plugin is running. It's
up to you. You should also place a Microsoft Winsock Control on the form. In
the example below, I have named that control "udpSocket".
In your project, insert a module. I call this module "InternetDownload.bas". It contains the header information and dll declares necessary to do internet calls using the WININET dll file that is included with Microsoft Internet Explorer. This allows you to code plugins without having to use the Internet Transfer Control. The code of the module should look like this:
Option Explicit
' This module handles the internet calls required to perform Internet
' transfers without using the Microsoft Internet Transfer Control.
Public Const INTERNET_OPEN_TYPE_PRECONFIG = 0
Public Const INTERNET_OPEN_TYPE_DIRECT = 1
Public Const INTERNET_OPEN_TYPE_PROXY = 3
Public Const scUserAgent = "VB OpenUrl"
Public Const INTERNET_FLAG_RELOAD = &H80000000
Public Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _
(ByVal sAgent As String, ByVal lAccessType As Long, ByVal sProxyName As String, _
ByVal sProxyBypass As String, ByVal lFlags As Long) As Long
Public Declare Function InternetOpenUrl Lib "wininet.dll" Alias "InternetOpenUrlA" _
(ByVal hOpen As Long, ByVal sUrl As String, ByVal sHeaders As String, _
ByVal lLength As Long, ByVal lFlags As Long, ByVal lContext As Long) As Long
Public Declare Function InternetReadFile Lib "wininet.dll" _
(ByVal hFile As Long, ByVal sBuffer As String, ByVal lNumBytesToRead As Long, _
lNumberOfBytesRead As Long) As Integer
Public Declare Function InternetCloseHandle Lib "wininet.dll" _
(ByVal hInet As Long) As Integer
Heading over to frmMain, you should declare a global variable, messageTab, as String. This will be used to determine which tab to route the plugin out put to on the chat window.
In the form_load() event of frmMain, you should call a routine to read and parse the input file. This is the first thing that occurs in my plugins, and the code to do that looks like this:
Public Sub Read_Input_File()
' Read the "stock.txt" file created by the chat server. This file tells us what port
' to return data to the chat client on as well as which stock symbol(s) to look up.
On Error GoTo error_handler
Dim fileNum As Integer
Dim index As Integer
Dim fileName As String
Dim inString As String
Dim itemValue As String
Dim itemName As String
fileName = App.Path & "\stock.txt"
fileNum = FreeFile()
Open fileName For Input As fileNum
Do While Not EOF(fileNum)
Input #fileNum, inString
If inString = "" Then
' Skip the txt file header and any blank lines
Else
For index = 1 To Len(inString)
If Mid$(inString, index, 1) = "=" Then
itemValue = Mid$(inString, index + 1, (Len(inString) - index))
itemName = Mid$(inString, 1, (index - 1))
End If
Next index
Set_INI_Variable itemName, itemValue
End If
Loop
Close #fileNum
Exit Sub
error_handler:
Send_UDP_Message "Stock plugin encountered error " & Err.Description, False
Send_UDP_Message " ",True
End
End Sub
Private Sub Set_INI_Variable(itemName As String, itemValue As String)
' Here we assign the INI file fields to the variables that they represent
On Error GoTo error_handler
Dim index As Integer
Dim endPos As Integer
Dim startPos As Integer
itemName = UCase(itemName) ' Convert variable name to uppercase
Select Case itemName
Case "DATA"
' Parse your data line here!!
Case "PORT"
returnPort = Val(itemValue)
Case "TAB"
messageTab = itemValue
End Select
Exit Sub
error_handler:
Send_UDP_Message "Stock plugin encountered error " & Err.Description, False
Send_UDP_Message " ",True
End
End Sub
As you can see, we have a couple of places in the code above where we call a routine called "Send_UDP_Message". This is the routine that we use to talk to the chat client. by calling this routine, you can send any message to the client that you want. If you wanted to send a message to the client when your plugin was first launched, you could code something like: Send_UDP_Message "Stock plugin launched!". The code for the Send_UDP_Message routine looks like this:
Private Sub Send_UDP_Message(messageData As String, resetMsg As Boolean)
' Send routine for communicating with the chat client
Dim outMessageType, outMessageName, outMessageLen, outMessageData As String
On Error Resume Next
With udpSocket
.Close
.RemoteHost = "127.0.0.1" ' This IP always refers to the local machine
.RemotePort = returnPort ' Bind to return port that the client supplied us
.Bind 3299 ' 3299 is a randomly chosen port
End With
' Do not leave out the trailing spaces below. The chat client expects to get its
' messages in a specific format. Message Type must be 12 bytes in length. There are
' two possible message types sent to the client. Type "SERVER" causes the client to
' display the incoming data as a server message. Type "RESET" tells the client to reset
' the socket to accept messages from the real chat server once again.
outMessageType = "SERVER "
If resetMsg = True Then
outMessageName = "RESET "
Else
outMessageName = Left("MESSAGE" & messageTab & " ", 16)
End If
' Message name must be 16 bytes in length. You could also use RESPONSE instead
' of MESSAGE. Responses are in a proportional font, and Messages are in the default
' chat font.
outMessageName = "MESSAGE "
outMessageLen = "0000" & Len(messageData)
outMessageLen = Right(outMessageLen, 4)
outMessageData = outMessageType & outMessageName & outMessageLen & messageData
udpSocket.SendData outMessageData
End Sub
These are the main routines that you need to code to parse the input text file and communicate with the client. To retrieve a webpage, you might code something like what is shown below. The example below is a routine called Read_URL. It will read the webpage of your choice and place the entire HTML code into a string variable which you can then parse. You must pass the URL to this routine. You can either hardcode the URL or build it on the fly including variables from the data that the chat client user sent to the plugin. To call the routine, you could do something like this: Read_URL "http://www.thecharlotteworld.com"
Private Sub Read_URL(stockUrl As String, symbol As String)
Dim hOpen As Long
Dim hOpenUrl As Long
Dim bDoLoop As Boolean
Dim bRet As Boolean
Dim sReadBuffer As String * 2048
Dim lNumberOfBytesRead As Long
Dim totalBytes As Long
Dim sBuffer As String
Dim strLaunch As String
totalBytes = 0
hOpen = InternetOpen(scUserAgent, INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0)
hOpenUrl = InternetOpenUrl(hOpen, stockUrl, vbNullString, 0, INTERNET_FLAG_RELOAD, 0)
bDoLoop = True
While bDoLoop
sReadBuffer = vbNullString
bRet = InternetReadFile(hOpenUrl, sReadBuffer, Len(sReadBuffer), lNumberOfBytesRead)
sBuffer = sBuffer & Left$(sReadBuffer, lNumberOfBytesRead)
totalBytes = totalBytes + lNumberOfBytesRead
DoEvents
If Not CBool(lNumberOfBytesRead) Then bDoLoop = False
Wend
If hOpenUrl <> 0 Then InternetCloseHandle (hOpenUrl)
If hOpen <> 0 Then InternetCloseHandle (hOpen)
End Sub
Compiling Your Plugin
Once you have coded your plugin and are ready to compile it, there is one final
thing that you must do. The Chat Client recognizes plugins by scanning every
".exe" file in the path that it was loaded from. The client checks the
version ("App.Minor") of each .exe file, and if a file has a version of
"99" then chat recognizes it as a plugin. When compiling your plugin, make
sure that you select Options from the Compile window and set the minor version of your
application to "99".