Hello I am trying to create a chat bot for my twitch channel in Visual Basic. I did some research and I found this code:
Public Class My_IRC
Private _sServer As String = String.Empty '-- IRC server name
Private _sChannel As String = String.Empty '-- the channel you want to join (prefex with #)
Private _sNickName As String = String.Empty '-- the nick name you want show up in the side bar
Private _lPort As Int32 = 6667 '-- the port to connect to. Default is 6667
Private _bInvisible As Boolean = False '-- shows up as an invisible user. Still working on this.
Private _sRealName As String = "nodibot" '-- More naming
Private _sUserName As String = "nodi_the_bot" '-- Unique name so of the IRC network has a unique handle to you regardless of the nickname.
Private _tcpclientConnection As TcpClient = Nothing '-- main connection to the IRC network.
Private _networkStream As NetworkStream = Nothing '-- break that connection down to a network stream.
Private _streamWriter As StreamWriter = Nothing '-- provide a convenient access to writing commands.
Private _streamReader As StreamReader = Nothing '-- provide a convenient access to reading commands.
Public Sub New(ByVal server As String, ByVal channel As String, ByVal nickname As String, ByVal port As Int32, ByVal invisible As Boolean)
_sServer = server
_sChannel = channel
_sNickName = nickname
_lPort = port
_bInvisible = invisible
End Sub
Public Sub Connect()
'-- IDENT explained:
'-- -- When connecting to the IRC server they will send a response to your 113 port.
'-- -- It wants your user name and a response code back. If you don't some servers
'-- -- won't let you in or will boot you. Once verified it drastically speeds up
'-- -- the connecting time.
'-- -- -- http://en.wikipedia.org/wiki/Ident
'-- Heads up - when sending a command you need to flush the writer each time. That's key.
Dim sIsInvisible As String = String.Empty
Dim sCommand As String = String.Empty '-- commands to process from the room.
'-- objects used for the IDENT response.
Dim identListener As TcpListener = Nothing
Dim identClient As TcpClient = Nothing
Dim identNetworkStream As NetworkStream = Nothing
Dim identStreamReader As StreamReader = Nothing
Dim identStreamWriter As StreamWriter = Nothing
Dim identResponseString As String = String.Empty
Try
'-- Start the main connection to the IRC server.
Console.WriteLine("**Creating Connection**")
_tcpclientConnection = New TcpClient(_sServer, _lPort)
_networkStream = _tcpclientConnection.GetStream
_streamReader = New StreamReader(_networkStream)
_streamWriter = New StreamWriter(_networkStream)
'-- Yeah, questionable if this works all the time.
If _bInvisible Then
sIsInvisible = 8
Else
sIsInvisible = 0
End If
'-- Send in your information
Console.WriteLine("**Setting up name**")
_streamWriter.WriteLine(String.Format("USER {0} {1} * :{2}", _sUserName, sIsInvisible, _sRealName))
_streamWriter.Flush()
'-- Create your nickname.
Console.WriteLine("**Setting Nickname**")
_streamWriter.WriteLine(String.Format(String.Format("NICK {0}", _sNickName)))
_streamWriter.Flush()
'-- Tell the server you want to connect to a specific room.
Console.WriteLine("**Joining Room**")
_streamWriter.WriteLine(String.Format("JOIN {0}", _sChannel))
_streamWriter.Flush()
'-- By now the IDENT should be sent to your port 113. Listen to it, grab the text,
'-- and send a response.
'-- Idents are usually #### , ####
'-- That is four digits, a space, a comma, and four more digits. You need to send
'-- this back with your user name you connected with and your system.
identListener = New TcpListener(IPAddress.Any, 113)
identListener.Start()
identClient = identListener.AcceptTcpClient
identListener.Stop()
Console.WriteLine("ident connection?")
identNetworkStream = identClient.GetStream
identStreamReader = New StreamReader(identNetworkStream)
identResponseString = identStreamReader.ReadLine
Console.WriteLine("ident got: " + identResponseString)
identStreamWriter = New StreamWriter(identNetworkStream)
'-- The general format for the IDENT response. You can use UNIX, WINDOWS VISTA, WINDOWS XP, or what ever your system is.
identStreamWriter.WriteLine(String.Format("{0} : USERID : WINDOWS 7 : {1}", identResponseString, _sUserName))
identStreamWriter.Flush()
'-- By now you should be connected to your room and visible to anyone else.
'-- If you are receiving errors they are pretty explicit and you can maneuver
'-- to debuggin them.
'--
'-- What happens here is the command processing. In an infinite loop the bot
'-- read in commands and act on them.
While True
sCommand = _streamReader.ReadLine
Console.WriteLine(sCommand)
'-- Not the best method but for the time being it works.
'--
'-- Example of a command it picks up
' :nodi123!nodi12312#ipxxx-xx.net PRIVMSG #nodi123_test :? hola!
'-- You can extend the program to better read the lines!
Dim sCommandParts(sCommand.Split(" ").Length) As String
sCommandParts = sCommand.Split(" ")
'-- Occasionally the IRC server will ping the app. If it doesn't respond in an
'-- appropriate amount of time the connection is closed.
'-- How does one respond to a ping, but with a pong! (and the hash it sends)
If sCommandParts(0) = "PING" Then
Dim sPing As String = String.Empty
For i As Int32 = 1 To sCommandParts.Length - 1
sPing += sCommandParts(i) + " "
Next
_streamWriter.WriteLine("PONG " + sPing)
_streamWriter.Flush()
Console.WriteLine("PONG " + sPing)
End If
'-- With my jank split command we want to look for specific commands sent and react to them!
'-- In theory this should be dumped to a method, but for this small tutorial you can see them here.
'-- Also any user can input this. If you want to respond to commands from you only you would
'-- have to extend the program to look for your non-bot-id in the sCommandParts(0)
If sCommandParts.Length >= 4 Then
'-- If a statement is proceeded by a question mark (the semi colon's there automatically)
'-- then repeat the rest of the string!
If sCommandParts(3).StartsWith(":?") Then
Dim sVal As String = String.Empty
Dim sOut As String = String.Empty
'-- the text might have other spaces in them so concatenate the rest of the parts
'-- because it's all text.
For i As Int32 = 3 To sCommandParts.Length - 1
sVal += sCommandParts(i)
sVal += " "
Next
'-- remove the :? part.
sVal = sVal.Substring(2, sVal.Length - 2)
'-- Trim for good measure.
sVal = sVal.Trim
'-- Send the text back out. The format is they command to send the text and the room you are in.
sOut = String.Format("PRIVMSG {0} : You said '{1}'", _sChannel, sVal)
_streamWriter.WriteLine(sOut)
_streamWriter.Flush()
End If
'-- If you don't quit the bot correctly the connection will be active until a ping/pong is failed.
'-- Even if your programming isn't running!
'-- To stop that here's a command to have the bot quit!
If sCommandParts(3).Contains(":!Q") Then
' Stop
_streamWriter.WriteLine("QUIT")
_streamWriter.Flush()
Exit Sub
End If
End If
End While
Catch ex As Exception
'-- Any exception quits the bot gracefully.
Console.WriteLine("Error in Connecting. " + ex.Message)
_streamWriter.WriteLine("QUIT")
_streamWriter.Flush()
Finally
'-- close your connections
_streamReader.Dispose()
_streamWriter.Dispose()
_networkStream.Dispose()
End Try
End Sub
End Class
and
Imports System
Imports System.Net
Imports System.Net.Sockets
Imports System.IO
Public Sub Main()
Dim foo As New My_IRC("irc.freenode.net", "#nodi123_test", "nodime", 6667, False)
foo.Connect()
End Sub
But I can not succeed to connect using a password because it is simply to hard for me as a beginner. (You can either set a password or use the command /server irc.twitch.tv 6667 oauth:AUTHKEY) so my question is, is there a way to set the password in the code I posted? Or is there another stable way to use IRC in Visual Basic which allows me to set a server password? Let me know, thanks.
Your first stop should be the IRC RFC documents, RFC 1459 and 2812. From the snippet of code you provided, your IRC client does not implement the PASS message -- see section 4.1.1 of RFC 1459.
4.1.1 Password message
Command: PASS Parameters: <password>
The PASS command is used to set a 'connection password'. The
password can and must be set before any attempt to register the
connection is made. Currently this requires that clients send a PASS
command before sending the NICK/USER combination and servers must
send a PASS command before any SERVER command. The password supplied
must match the one contained in the C/N lines (for servers) or I
lines (for clients). It is possible to send multiple PASS commands
before registering but only the last one sent is used for
verification and it may not be changed once registered. Numeric
Replies:
ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
Example:
PASS secretpasswordhere
It should be relatively easy to implement:
**Console.WriteLine("**Setting Password**")
_streamWriter.WriteLine(String.Format(String.Format("PASS {0}", whateverVariableYouChooseToHoldThePassword)))
_streamWriter.Flush()**
'-- Send in your information
Console.WriteLine("**Setting up name**")
_streamWriter.WriteLine(String.Format("USER {0} {1} * :{2}", _sUserName, sIsInvisible, _sRealName))
_streamWriter.Flush()
I'll take this opportunity to mention that I wrote an IRC library a while back that may help you, at least as a reference point for how IRC behaves. I implemented a decent portion of RFC 1459 before I lost interest. Interestingly enough, my IRC library doesn't support sending the PASS message either (although it would be similarly easy to add support).
My library could theoretically be of use to you as-is, but I don't think it's production ready, so to speak.
Related
I have a simple TCP/IP HTML server. To access this HTML server the user needs to type the IP address of the machine the HTML server is running on also, if the user types a faulty URL as long as it starts with the correct IP-address followed by a forward slash (example: 123.456.789.0/FaultyText) it also works.
My understanding is that if I type in a web browsers address bar it broadcasts that text-string till some server says "hey I recognize this, let me react upon it" meaning (according to my understanding) the opposite is also true if a text-string is broadcast which the server does not recognize it says "This I cannot identify, I'll pass reacting upon it". This would mean the text-string must always be read to know it's at the right destination or not.
So how come if I type something random in the URL address bar, the following code snippet won't reference to it in its HTTP headers Host:, or Refrence: sections (or anywhere else for that matter of fact)?
#Region "Start Server"
Dim MyIp As String
Dim MyPort As String
Try
'If Not MyNetworkPortBase64Plain = Nothing Then
' MyIp = MyNetworkIpBase64Plain
'Else
' Dim MyComputerName As String = Dns.GetHostName()
' MyIp = Dns.GetHostByName(MyComputerName).AddressList(0).ToString()
'End If
If Not MyNetworkPortBase64Plain = Nothing Then
MyPort = MyNetworkPortBase64Plain
Else
MyPort = "8080"
End If
MyIp = (IPAddress.Any).ToString
MyHtmlServer = New TcpListener(IPAddress.Parse(MyIp), MyPort)
MyHtmlServer.Start()
Threading.ThreadPool.QueueUserWorkItem(AddressOf MyNewClientSub)
#Region "Server Start/ Stop Button"
#Region "Get/ Collect Incoming Bytes"
Dim MyNetworkStream As NetworkStream = MyClient.GetStream()
Dim MyReceivingBytes(100000) As Byte
MyNetworkStream.Read(MyReceivingBytes, 0, MyReceivingBytes.Length)
Dim MyIncommingRequests As String = Encoding.ASCII.GetString(MyReceivingBytes)
Dim webClient As New System.Net.WebClient
MsgBox(MyIncommingRequests)
#End Region
How can I catch all the URL requests made?
I'm trying to make a simple client-side application to recieve small text data, compare it and then does something on client machine depending on what server sent.
Server Logic: The server side is made in java, so can't change anything there. Server sends string "abc001" on connecting to client.
Client Logic: Client recieves the string "abc001" from server & checks if it's recieved string is the same as "abc001", then does something accordingly.
Problem: When the client recieves data, I display it in msgbox. But instead of just "abc001", there pops up an extra blank msgbox(image included).
Client Code - On Start:
Try
' declare vals
Dim ip As String = "127.0.0.1"
Dim port As Integer = 5000
' set client
_client = New TcpClient(ip, port)
' disable cross thread calls checking
CheckForIllegalCrossThreadCalls = False
' recieve msg
Threading.ThreadPool.QueueUserWorkItem(AddressOf RecieveMessages)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Client Code - Recieve Data
Private Sub RecieveMessages(state As Object)
Try
While True
Dim ns As NetworkStream = _client.GetStream()
Dim toRecieve(_client.ReceiveBufferSize) As Byte
ns.Read(toRecieve, 0, CInt(_client.ReceiveBufferSize))
Dim txt As String = Encoding.ASCII.GetString(toRecieve)
MsgBox(txt)
End While
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
MsgBox 1
MsgBox 2
How to not get the blank msgbox. Even when compared, the data recived does not match parameters. Tried to use delay, tried fixing the buffer size to 6 bytes but no use.. Any help is appreciated. Thanks.
EDIT 1: Tried my best to figure it out but can't.. Tried cleaning the returned string data and even tried storing each return data in array. Saw the stack and it says the msgbox has "nothing" in it. It's null.. I don't even know what to do.. Here's the code for strings clean:
Private Sub RecieveMessages(state As Object)
Dim message(0) As String
Dim command_raw, command_clean, command As String
Dim counter As Integer = 0
Try
While True
Dim ns As NetworkStream = _client.GetStream()
Dim toRecieve(_client.ReceiveBufferSize) As Byte
ns.Read(toRecieve, 0, CInt(_client.ReceiveBufferSize))
Dim txt As String = Encoding.ASCII.GetString(toRecieve)
message(0) = txt
command_raw = message(0)
command_clean = command_raw.Replace(vbCrLf, Nothing)
command = command_clean.Substring(0, 6)
MsgBox(command)
End While
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
I'm starting to work with a pinpad.
I need that my program find the port where the pinpad is connected without user interaction.
I tried with:
Dim searcher As New ManagementObjectSearcher("root\cimv2","SELECT * FROM Win32_SerialPort")
For Each queryObj As ManagementObject In searcher.Get()
MsgBox(queryObj("Name"))
Next
but this only give me "COM1" and "COM2" as an answer (My device is connected to COM4)
and with
Dim searcher As New ManagementObjectSearcher("root\cimv2", "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0")
For Each queryObj As ManagementObject In searcher.Get()
MsgBox(queryObj("Name"))
Next
With this one I can see my device friendly name but I donĀ“t know how to get the port (I receive the names like 'HP printer')
Any idea of how can I get the port that I need?
Thanks in advance
Based on the comments it sounds like your device is a USB device that has a driver that causes it to appear to be (emulates) a serial port attached device. In that case I would use:
My.Computer.Ports.SerialPortNames
to enumerate and loop over all serial ports. Then, one at a time try to open each one and send a command to the device that you know it responds to. Most devices have some kind of heartbeat or keep alive message that they will respond to. Whichever port you get a response on is the port you need to use.
I want to point 2 matters:
1: here is a solution i used for this problem (efficiancy corrections will be appreciated)
I used this soution i used to figure out on which port vx805 verifone pin pad was connected (has a unique device id):
Friend Class pinPadComLocater
Private Shared com As String
Private Const PNPDeviceID = "VID_11CA&PID_0220"
Private Const scope = "root\cimv2"
Public ReadOnly pinPadCom As String = Nothing
Sub New()
If isVX805PinPadConnected() Then
pinPadCom = com
Output.mainLog(Output.pinpadLocationMsg + com)
Else
Output.mainLog(Output.pinpadNotFoundMsg)
End If
End Sub
Private Shared Function queryCom(port As String) As Boolean
Dim query = "SELECT * FROM Win32_PnPEntity WHERE ClassGuid=""{4d36e978-e325-11ce-bfc1-08002be10318}"" AND DeviceID LIKE ""%" + PNPDeviceID + "%"" AND Caption LIKE ""%" + port + "%"""
Dim resp = New ManagementObjectSearcher(scope, query).Get
If resp.Count = 1 Then Return True
For Each queryObj As ManagementObject In resp
For Each prop In queryObj.Properties 'print all data for development purposes
Try
Console.writeline(prop.Name + " : " + queryObj(prop.Name).ToString)
catch ex As Exception
End Try
Next
Next
Return False
End Function
Private Shared Function isVX805PinPadConnected() As Boolean
For Each port In My.Computer.Ports.SerialPortNames
Try
If queryCom(port) Then
com = port
Return True
End If
Catch err As ManagementException
Throw New ConstraintException("An error occurred while querying for WMI data: " & err.Message)
End Try
Next
Throw New ConstraintException("Pin Pad Com Port could not be located")
Return False
End Function
End Class
2: would love more clarifiaction on that:
Then, one at a time try to open each one and send a command to the device that you know it responds to. Most devices have some kind of heartbeat or keep alive message that they will respond to
I would love to see a code example of how you send such a heartbeat check to a pinpad
I'm not even sure I understand this situation enough to come up with a proper title. I come from a modest understanding of VB6 and having to climb a steep learning curve for VB 2010.
I am trying to create a multi-client server program that will communicate with my Enterprise iPhone app. I found a relatively simple example to build upon here: http://www.strokenine.com/blog/?p=218. I have been able to modify the code enough to make it work with my app, but with one glitch: I can't get access to the controls on the form to add items, even though the method is invoked within the form's class. (I tried this on the original code too, and it does the same thing. I don't know how the author managed to get it to work.)
Here's the code segment in question:
Public Class Server 'The form with the controls is on/in this class.
Dim clients As New Hashtable 'new database (hashtable) to hold the clients
Sub recieved(ByVal msg As String, ByVal client As ConnectedClient)
Dim message() As String = msg.Split("|") 'make an array with elements of the message recieved
Select Case message(0) 'process by the first element in the array
Case "CHAT" 'if it's CHAT
TextBox3.Text &= client.name & " says: " & " " & message(1) & vbNewLine 'add the message to the chatbox
sendallbutone(message(1), client.name) 'this will update all clients with the new message
' and it will not send the message to the client it recieved it from :)
Case "LOGIN" 'A client has connected
clients.Add(client, client.name) 'add the client to our database (a hashtable)
ListBox1.Items.Add(client.name) 'add the client to the listbox to display the new user
End Select
End Sub
Under Case "LOGIN" the code tries to add the login ID to the listbox. It throws an exception: "A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll" The listbox (all controls, for that matter) is in the same class, Server.vb and Server.vb [Design].
The data comes in from another class that is created whenever a client logs on, which raises the event that switches back to the Server class:
Public Class ConnectedClient
Public Event gotmessage(ByVal message As String, ByVal client As ConnectedClient) 'this is raised when we get a message from the client
Public Event disconnected(ByVal client As ConnectedClient) 'this is raised when we get the client disconnects
Sub read(ByVal ar As IAsyncResult) 'this will process all messages being recieved
Try
Dim sr As New StreamReader(cli.GetStream) 'initialize a new streamreader which will read from the client's stream
Dim msg As String = sr.ReadLine() 'create a new variable which will be used to hold the message being read
RaiseEvent gotmessage(msg, Me) 'tell the server a message has been recieved. Me is passed as an argument which represents
' the current client which it has recieved the message from to perform any client specific
' tasks if needed
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'continue reading from the stream
Catch ex As Exception
Try 'if an error occurs in the reading purpose, we will try to read again to see if we still can read
Dim sr As New StreamReader(cli.GetStream) 'initialize a new streamreader which will read from the client's stream
Dim msg As String = sr.ReadLine() 'create a new variable which will be used to hold the message being read
RaiseEvent gotmessage(msg, Me) 'tell the server a message has been recieved. Me is passed as an argument which represents
' the current client which it has recieved the message from to perform any client specific
' tasks if needed
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'continue reading from the stream
Catch ' IF WE STILL CANNOT READ
RaiseEvent disconnected(Me) 'WE CAN ASSUME THE CLIENT HAS DISCONNECTED
End Try
End Try
End Sub
I hope I am making sense with all this. It all seems to bounce back and forth, it seems so convoluted.
I've tried using Me.listbox1 and Server.listbox1 and several other similar structures, but to no avail.
I'm reading a lot about Invoke and Delegates, but would that be necessary if the method and the control are in the same class? Or do I have a fundamental misperception of what a class is?
Many thanks for any help I can get.
Private Delegate Sub UpdateListDelegate(byval itemName as string)
Private Sub UpdateList(byval itemName as string)
If Me.InvokeRequired Then
Me.Invoke(New UpdateListDelegate(AddressOf UpdateList), itemName)
Else
' UpdateList
' add list add code
ListBox1.Items.Add(itemName)
End If
End Sub
Add above, then replace:
ListBox1.Items.Add(client.name)
to
UpdateList(client.name)
Does it work? check the syntax, may have typo as I type it.
I am new to programming in vb.net. I have come a long ways in my development and understanding of vb, but there is one hurtle I can not seem to fix. I am hosting an ftp server on my pc and I am making an app for it to connect to my server and download files. The problem with all the sample code is that everyone ASSUMES the server WILL be ONLINE. My pc may not be running 24/7 and I also may not have the ftp service running.In the first case it shouldnt even register that it is connected. In the second case, it WILL say that is connected b/c the pc is on, but it will return that the machine ou are trying to connect to is actively refusing the connection. Is there a way to TRULY check if the program is indeed connected to the server WITHOUT generating a bunch of Exceptions in the debugger? All I want is a call like:
Dim ftponline As Boolean = False 'Set default to false
ftponline = checkftp()
If ftponline Then
'continue program
Else
'try a different server
End If
So it would be a function called checkftp that returns a boolean value of true or false.
Here is my info:
Using Visual Studio 2010 Pro
Using .Net framework 4
Can anyone help?
Thanks!
I have tried the rebex ftp pack as well as the Ultimate FTP Pack.
Here is the updated code:
Public Function CheckConnection(address As String) As Boolean
Dim logonServer As New System.Net.Sockets.TcpClient()
Try
logonServer.Connect(address, 21)
Catch generatedExceptionName As Exception
MessageBox.Show("Failed to connect to: " & address)
End Try
If logonServer.Connected Then
MessageBox.Show("Connected to: " & address)
Return True
logonServer.Close()
Else
Return False
End If
End Function
Public Sub ConnectFtp()
types.Clear()
models.Clear()
ListBox1.Items.Clear()
ListBox2.Items.Clear()
TextBox2.Clear()
Dim request As New Rebex.Net.Ftp
If CheckConnection(*) Then
Dim tempString As String()
request.Connect(*)
request.Login(*, *)
request.ChangeDirectory("/atc3/HD_Models")
Dim list As Array
list = request.GetNameList()
Dim item As String = ""
For Each item In list
tempString = item.Split(New Char() {" "c})
If types.Contains(tempString(0)) = False Then
types.Add(tempString(0))
End If
If models.Contains(item) = False Then
models.Add(item)
End If
Next
request.Disconnect()
request.Dispose()
ElseIf CheckConnection(*) Then
request.Connect(*)
request.Login(*, *)
request.ChangeDirectory(*)
Dim list2 As Array
list2 = request.GetNameList()
Dim item2 As String = ""
Dim tempString2 As String()
For Each item2 In list2
MessageBox.Show(item2)
tempString2 = item2.Split(New Char() {" "c})
If types.Contains(tempString2(0)) = False Then
types.Add(tempString2(0))
End If
If models.Contains(item2) = False Then
models.Add(item2)
End If
Next
request.Disconnect()
request.Dispose()
End If
End Sub
No matter what I do, the second server will not connect. I even put a messagebox to show what items were being returned in the second server, but there are no messageboxes apearing when I run the program with my server offline. Is there anyone who can help?
If your code is designed with proper exception catching, it shouldn't be generating a "bunch" of exceptions. The first exception you catch should be your indication that the connection failed and your code should cease attempting to communicate at that point. If for some reason you really need to check the connectivity before attempting the FTP connection, you should be able to simply attempt to synchronously open a TCP socket to the FTP server's port. If that works, it's up and running.
You could simply open a socket to the server's IP address on Port 21 (assuming default FTP port).
I'm not much of a VB.Net programmer, but here's a link to sample code:
http://vb.net-informations.com/communications/vb.net_Client_Socket.htm
If you can establish the socket connection, you know that something is listening on that port (though you have not yet proven it's an FTP server, or that it will accept your login credentials...).
If you wish to simply avoid exceptions in the debugger, you could place the connection code in a method and apply the DebuggerHidden attribute to that method.