I'm building an IRC bot in Python for fun. It is supposed to accept commands prefixed with '!' and act on them. The function below is used to parse commands received in an IRC message.
def parse_cmd(self, sender):
#Admin Commands
if sender == self.owner:
if self.command == 'quit':
send_bufr = "QUIT %s\r\n" %(self.channel)
self.sock.send(bytearray(send_bufr, "utf-8"))
self.sock.close()
sys.exit(1)
if self.command == 'hi':
print("Run: Hi")
send_bufr = "PRIVMSG %s :Hello %s" %(self.channel, sender)
print(send_bufr)
self.sock.send(bytearray(send_bufr, "utf-8"))
return
else:
return
else:
return
The exclamation points are parsed earlier and the function uses self.command as the command which is also set earlier. The following code is used to set the USER, NICK, and to join a channel and self.sock.send works fine here:
#Send NICK self.nick to set NICK
send_bufr = ("NICK %s \r\n") %(self.nick)
self.sock.send(bytearray(send_bufr, "utf-8"))
print("Set Nick to %s" %(self.nick))
#Send USER to set USER
send_bufr = ("USER %s 8 * :S0lder \r\n") %(self.nick)
self.sock.send(bytearray(send_bufr, "utf-8"))
print("Set USER to %s 8 :S0lder" %(self.nick))
#JOIN self.channel
send_bufr = ("JOIN %s \r\n") %(self.channel)
self.sock.send(bytearray(send_bufr, "utf-8"))
print("Joined %s" %(self.channel))
time.sleep(5)
However In the function earlier and any instances of self.sock.send() after the initial connection are not sent until the '!quit' command is given, at which point all of the messages that were supposed to be sent earlier are sent. Why is this? Am I misunderstanding the proper way to use sockets?
Edit: I'm connected to the same channel with an IRC client and the messages appear in the channel only after I give the !quit command.
In his comment on my question, Winston Ewert was correct. The socket.send should have read:
send_bufr = "PRIVMSG %s :Hello %s\r\n" %(self.channel, sender)
print(send_bufr)
self.sock.send(bytearray(send_bufr, "utf-8"))
A dumb mistake on my part to leave out the terminator. Thanks to everyone who helped.
Try:
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
which should flush the buffer pretty much immediately.
Related
I'm currently working on a project for a class. It consists in code a simple chat client (protocol given by the teacher) to do (at first) some simple tasks.
My problem is that after I send a mensage on the globlal channel or in other channel that doesn't require the use of a command, and try to send any command, the server replies with an error, saying something like: "msgbeforemsgbeforeCOMMAND" is not a valid command. I just cannot figure it out why this is happening...
(another thing, note that my dictionary is not printing the right why, I dont know why to)
ex:
chat running
import socket, select, string, sys
import threading
import time
def prompt():
sys.stdout.write('<You>: ')
sys.stdout.flush()
tLock = threading.Lock()
shutdown = False
def receber(sock):
while not shutdown:
try:
tLock.acquire()
while True:
data = sock.recv(1024)
if not data:
print ('Disconnected from server\n')
sys.exit()
else:
print ('<server>: %s' % (data.decode()))
sys.stdout.write(data)
except:
pass
finally:
tLock.release()
#Main Function
if __name__ == "__main__":
host = 'mini.alunos.di.uevora.pt'
port = 143
#IP do servidor
try:
busca_ip = socket.gethostbyname( host )
print ('Chat server IP: %s Port: %d \nNow connecting...\n' %(busca_ip, port))
except socket.gaierror:
#Não conseguiu o IP do servidor
print ('Hostname could not be resolved. Exiting.')
sys.exit()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(3)
# connectar ao host
try :
s.connect((busca_ip, port))
s.setblocking(0)
except :
print ('Unable to connect to the server.')
sys.exit()
print ('Connected to chat server. You may start chatting\n')
COM_ARG = {'_Comando_': '_Argumentos_',
'NICK': '<nickname> [\<password>]',
'MSG': '<recipient> \<message>',
'ENTER': '<room>',
'LEAVE': '<room> [\<message>]',
'RLIST':'',
'ULIST':''}
for chave, valor, in COM_ARG.items():
print (("%s %s") % (chave,valor))
print ('\n')
comandos = COM_ARG.keys()
#criar thread para o socket
t = threading.Thread(target = receber, args=(s,))
t.start()
while True:
msg = input('<You>: ')
msg = msg.strip()
msg12 = msg.upper()
msg12 = msg12.split()
try:
if msg12[0] in comandos:
msg = msg + '\n'
except:
pass
s.send(msg.encode())
time.sleep(0.25)
btw, sys.stdout.write(data) is doing something there?
Hope you could help me out.
(another thing, note that my dictionary is not printing the right why, I dont know why to)
Dictionary doesn't respect order.
My problem is that after I send a mensage on the globlal channel or in other channel that doesn't require the use of a command, and try to send any command, the server replies with an error, saying something like: "msgbeforemsgbeforeCOMMAND" is not a valid command. I just cannot figure it out why this is happening...
It's not just a problem with the code, the server recives the msgs, and keeps them until a '\n' appears, just then interprets the command. It's a "problem" with the protocol, but the code must be changed.
btw, sys.stdout.write(data) is doing something there?
Supposedly does the samething that print (data.decode()) does, but doesn't work in my case. I'm not sure.
Usually I would attempt something like this with the twisted library, but that isn't available for python 3 - so I attempted it using the sockets library. The code does establish a connection, but the server quickly responds with "no ident response". I don't have much network programming experience, so I would appreciate it if someone could point out the error I'm making here. I'm also quite aware that there are functions/other code that aren't used, or that Ive used inconsistently. I just thought I would paste the entirety of my code here in case its relevant.
import socket
server = "irc.freenode.net"
channel = "put channel here"
nickname = "put nickname here"
def encode(text):
return text.encode("ascii")
def ping():
irc_socket.send(encode("PONG :pingis\n"))
def send_message(chan, msg):
irc_socket.send(encode("PRIVMSG " + chan + " :" + msg + "\n"))
def join_channel(chan):
irc_socket.send(encode("JOIN " + chan + "\n"))
def login(username='user', realname='Pythonist', hostname='Helena', servername='Server'):
irc_socket.send(encode("USER %s %s %s %s" % (username, hostname, servername, realname)))
irc_socket.send(encode("NICK " + nickname))
irc_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
irc_socket.connect((server, 6667))
login()
join_channel(channel)
while True:
buffer = irc_socket.recv(1024)
msg = str.split(str(buffer))
if msg[0] == "PING":
irc_socket.send("PONG %s" % msg[1])
print(msg)
The code was originally from: http://wiki.shellium.org/w/Writing_an_IRC_bot_in_Python and Ive made minor changes.
Teensy tiny little problem that’s causing all the trouble: you’re missing newlines in login. "USER %s %s %s %s" should be "USER %s %s %s %s\n" and "NICK " + nickname should be "NICK " + nickname + "\n". The server looks up your ident and doesn’t find it, and the next step after that is for you to register, but you never send it a complete line, so it keeps waiting… and waiting…
Ident is UNIX service which nobody has been using for twenty years or so.
It was used to identify the remote user when doing terminal server to remote server connections in terminal applications. Ident is no way safe for modern internet, so nobody is using it anymore.
So you have hit the ghost of the past. Also, as mentioned in the above answer, if you send correct NICK command the IRC server is happy with your client.
I am currently trying to read messages cat channels Twitch. For this, I have read some guides and I learned it had to go through the IRC Twitch. I then found a few lines of simple code.
import socket
import string
HOST="irc.twitch.tv"
PORT=6667
NICK="TwitchUsername"
IDENT="TwitchUsername"
REALNAME="TwitchUsername"
CHANNEL="#ChannelNameHere"
PASSWORD="OAuth Password here" #From http://twitchapps.com/tmi/
readbuffer=""
s=socket.socket( )
s.connect((HOST, PORT))
s.send("PASS %s\r\n" % PASSWORD)
s.send("NICK %s\r\n" % NICK)
s.send("USER %s %s bla :%s\r\n" % (IDENT, HOST, REALNAME))
s.send("JOIN %s\r\n" % CHANNEL)
while 1:
readbuffer=readbuffer+s.recv(1024)
temp=string.split(readbuffer, "\n")
readbuffer=temp.pop( )
for line in temp:
line=string.rstrip(line)
line=string.split(line)
if len(line) > 3:
print line
if(line[0]=="PING"):
s.send("PONG %s\r\n" % line[1])
However, authentication does not proceed as planned, since I get the following message:
[':tmi.twitch.tv', 'NOTICE', '*', ':Login', 'unsuccessful']
I am using a valid OAuth Chat Password, and I see no reason that justifies this failure. Do you also have an error when you try with your username? Or do you have an idea of the problem please?
Your OAuth password needs to be sent as:
PASS oauth:twitch_oauth_token
which means that if you are putting in your token in the PASSWORD variable without the oauth: prefix, you should amend the pass line to:
s.send("PASS oauth:%s\r\n" % PASSWORD)
I was seeing the same :tmi.twitch.tv NOTICE * :Error logging in.
As noted in the readme "Your nickname must be your Twitch username in lowercase".
My issue was not making the NICK exactly my twitch username in lowercase (Not a very informative notice. So hopefully this saves someone else some time...).
Edited
Original question was about trouble with reconnecting (close() and shutdown() confusion). The below code is the working code (fixed)
For Googler's, this script is an IRC bot. Feature list:
Keep reconnecting until connection available
If assigned nick is already taken, puts string behind name (repeats until success)
Listens to PING and responds with PONG
Can listen to commands and respond
If connection is lost, the bot will try to reconnect (if no information is received, no PING, in 5 mins, it treats the connection as if it was disconnected)
That is about it :)
Full Code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import socket
import string
import os
import platform
import time
# Variables
HOST = "irc.server.net"
PORT = 6667
NICK = "Nickname"
IDENT = "Nickname"
REALNAME = os.getenv('USER')
CHAN = "##Channel"
readbuffer = ""
# The Connection itself
keep_connecting = True
while keep_connecting:
irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
irc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
irc.settimeout(300)
try:
irc.connect((HOST, PORT))
pass
except socket.gaierror:
print "No connection, attempting to connect again"
time.sleep(5)
continue
print "Sending info..."
irc.send("NICK %s\r\n" % NICK)
irc.send("USER %s %s bla :%s\r\n" % (IDENT, HOST, REALNAME))
irc.send("JOIN :%s\r\n" % CHAN)
# Initial msg to send when bot connects
irc.send("PRIVMSG %s :%s\r\n" % (CHAN, "TehBot: "+ NICK + " Realname: " + REALNAME + " ."))
while True:
try:
data = irc.recv(4096)
print data
# If disconneted from IRC
if len(data) == 0:
print "Length of data == 0 ?..."
break
# If Nick is in use
if data.find (NICK + " :Nickname is already in use") != -1:
NICK = NICK + str(time.time())[5:-3]
break
# Ping Pong so we don't get disconnected
if data[0:4] == "PING":
irc.send ("PONG " + data.split() [ 1 ] + "\r\n")
except socket.timeout:
print "Socket timeout!"
irc.close()
break
This is most probably because you're switching off wi-fi and the interface is removed from system so you get something like Can't assign requested address. You would get such an error while trying to bind to non-existing local address.
The other thing is you won't be able to reconnect on the same socket after calling close as it releases all resources associated to the socket.
I need help writing a basic IRC bot that just connects to a channel.. is anyone able to explain me this? I have managed to get it to connect to the IRC server but i am unable to join a channel and log on. The code i have thus far is:
import sockethost = 'irc.freenode.org'
port = 6667
join_sock = socket.socket()
join_sock.connect((host, port))
<code here>
Any help would be greatly appreciated.
To connect to an IRC channel, you must send certain IRC protocol specific commands to the IRC server before you can do it.
When you connect to the server you must wait until the server has sent all data (MOTD and whatnot), then you must send the PASS command.
PASS <some_secret_password>
What follows is the NICK command.
NICK <username>
Then you must send the USER command.
USER <username> <hostname> <servername> :<realname>
Both are mandatory.
Then you're likely to see the PING message from server, you must reply to the server with PONG command every time the server sends PING message to you. The server might ask for PONG between NICK and USER commands too.
PING :12345678
Reply with the exact same text after "PING" with PONG command:
PONG :12345678
What's after PING is unique to every server I believe so make sure you reply with the value that the server sent you.
Now you can join a channel with JOIN command:
JOIN <#channel>
Now you can send messages to channels and users with PRIVMSG command:
PRIVMSG <#channel>|<nick> :<message>
Quit with
QUIT :<optional_quit_msg>
Experiment with Telnet! Start with
telnet irc.example.com 6667
See the IRC RFC for more commands and options.
Hope this helps!
I used this as the MAIN IRC code:
import socket
import sys
server = "server" #settings
channel = "#channel"
botnick = "botname"
irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #defines the socket
print "connecting to:"+server
irc.connect((server, 6667)) #connects to the server
irc.send("USER "+ botnick +" "+ botnick +" "+ botnick +" :This is a fun bot!\n") #user authentication
irc.send("NICK "+ botnick +"\n") #sets nick
irc.send("PRIVMSG nickserv :iNOOPE\r\n") #auth
irc.send("JOIN "+ channel +"\n") #join the chan
while 1: #puts it in a loop
text=irc.recv(2040) #receive the text
print text #print text to console
if text.find('PING') != -1: #check if 'PING' is found
irc.send('PONG ' + text.split() [1] + '\r\n') #returnes 'PONG' back to the server (prevents pinging out!)
Then, you can start setting commands like: !hi <nick>
if text.find(':!hi') !=-1: #you can change !hi to whatever you want
t = text.split(':!hi') #you can change t and to :)
to = t[1].strip() #this code is for getting the first word after !hi
irc.send('PRIVMSG '+channel+' :Hello '+str(to)+'! \r\n')
Note that all irc.send texts must start with PRIVMSG or NOTICE + channel/user and the text should start with a : !
It'd probably be easiest to base it on twisted's implementation of the IRC protocol. Take a look at : http://github.com/brosner/bosnobot for inspiration.
This is an extension of MichaelvdNet's Post, which supports a few additional things:
Uses SSL wrapper for socket
Uses server password authentication
Uses nickserv password authentication
Uses nonblocking sockets, to allow other events to trigger
Logs changes to text files to channel
#!/usr/local/bin/python
import socket
import ssl
import time
## Settings
### IRC
server = "chat.freenode.net"
port = 6697
channel = "#meLon"
botnick = "meLon-Test"
password = "YOURPASSWORD"
### Tail
tail_files = [
'/tmp/file-to-tail.txt'
]
irc_C = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #defines the socket
irc = ssl.wrap_socket(irc_C)
print "Establishing connection to [%s]" % (server)
# Connect
irc.connect((server, port))
irc.setblocking(False)
irc.send("PASS %s\n" % (password))
irc.send("USER "+ botnick +" "+ botnick +" "+ botnick +" :meLon-Test\n")
irc.send("NICK "+ botnick +"\n")
irc.send("PRIVMSG nickserv :identify %s %s\r\n" % (botnick, password))
irc.send("JOIN "+ channel +"\n")
tail_line = []
for i, tail in enumerate(tail_files):
tail_line.append('')
while True:
time.sleep(2)
# Tail Files
for i, tail in enumerate(tail_files):
try:
f = open(tail, 'r')
line = f.readlines()[-1]
f.close()
if tail_line[i] != line:
tail_line[i] = line
irc.send("PRIVMSG %s :%s" % (channel, line))
except Exception as e:
print "Error with file %s" % (tail)
print e
try:
text=irc.recv(2040)
print text
# Prevent Timeout
if text.find('PING') != -1:
irc.send('PONG ' + text.split() [1] + '\r\n')
except Exception:
continue
That will open a socket, but you also need to tell the IRCd who you are. I've done something similar in perl ages ago, and I found the IRC RFCs to be very helpful.
Main RFC: http://irchelp.org/irchelp/rfc/rfc.html
Other RFCs: http://irchelp.org/irchelp/rfc/index.html