Python IRC ChatBot hangs on socket.recv after seemingly random time even though socket.settimeout is 8
Question: Hey so I decided to create an IRC ChatBot whose sole purpose it is to read
incoming messages from Twitch Chat and if a giveaway is recognized by a
keyword it's supposed to enter the giveaway by sending !enter in Chat.

I build the Bot upon this source: <https://github.com/BadNidalee/ChatBot>. I
only changed things in the Run.py so thats the only Code I'm going to post.
The unaltered ChatBot does work but it has no reconnect ability and regularly
stops receiving data because the socket closes or other reasons.

All I wanted to change was make it so that the ChatBot is stable and can just
stay in the IRC Chat constantly without disconnecting. I tried to achieve this
by setting a timeout of 8 seconds for my socket and catching timeout
exceptions that would occur and reconnect after they occur.

And all in all it does seem to work, my Bot does what it's supposed to even
when alot of messages are coming in, it recognizes when a Giveaway starts and
answers acordingly. IRC Server PING Messages are also handled and answered
correctly. If there is no message in Chat for over 8 seconds the Exception
gets thrown correctly and the Bot also reconnects correctly to IRC.

BUT heres my Problem: After seemingly random times the socket will literally
just Stop working. What I find strange is it will sometimes work for 20
minutes and sometimes for an hour. It doesn't occur when special events, like
lots of messages or something else happens in Chat, it really seems random. It
will not timeout there's just nothing happening anymore. If I cancel the
program with CTRL-C at this point the console sais the last call was
"readbuffer = s.recv(1024)" But why is it not throwing a timeout exception at
that point? If s.recv was called the socket should timeout if nothing is
received after 8 seconds but the program just stops and there is no more
output until you manually abort it.

Maybe I went about it the wrong way completely. I just want a stable 24/7-able
ChatBot that scans for one simple keyword and answers with one simple !enter.
This is also my first Time programming in Python so If I broke any conventions
or made any grave mistakes let me know.

The getUser Method returns the username of the line of chat that is scanned
currently.

The getMessage Method returns the message of the line of chat that is scanned.

The openSocket Method opens the Socket and sends JOIN NICK PASS etc to the IRC

    
    
    #!/usr/bin/python
    import string
    import socket
    import datetime
    import time
    from Read import getUser, getMessage
    from Socket import openSocket, sendMessage
    from Initialize import joinRoom
    
    connected = False
    readbuffer = ""
    
    
    def connect():
        print "Establishing Connection..."
        irc = openSocket()
        joinRoom(irc)
        global connected
        connected = True
        irc.settimeout(8.0)
        print "Connection Established!"
        return irc
    
    while True:
        s = connect()
        s.settimeout(8.0)
        while connected:
                try:
                    readbuffer = s.recv(1024)
                    temp = string.split(readbuffer, "\n")
                    readbuffer = temp.pop()
    
    
                    for line in temp:
                        if "PING" in line:
                            s.send(line.replace("PING", "PONG"))
                            timern = str(datetime.datetime.now().time())
                            timern = timern[0:8]
                            print timern + " PING received"
                            break
    
                        user = getUser(line)
                        message = getMessage(line)
                        timern = str(datetime.datetime.now().time())
                        timern = timern[0:8]
                        print timern +" " + user + ": " + message
                        if "*** NEW" in message:
                            sendMessage(s, "!enter")
                            break
    
    
                except socket.timeout:
                    connected = False
                    print "Socket Timed Out, Connection closed!"
                    break
                except socket.error:
                    connected = False
                    print "Socket Error, Connection closed!"
                    break
    


Answer: I think you've missunderstood how timeout work on the socket.

    
    
    s.settimeout(8.0)
    

Will only set `s.connect(...)` to timeout if it can't reach the destination
host.  
Further more, usually what you want to use instead if `s.setblocking(0)`
however this alone won't help you either (probably).

Instead what you want to use is:

    
    
    import select
    ready = select.select([s], [], [], timeout_in_seconds)
    if ready[0]:
        data = s.recv(1024)
    

What select does is check the buffer to see if any incoming data is available,
if there is you call `recv()` which in itself is a blocking operation. If
there's nothing in the buffer `select` will return empty and you should avoid
calling `recv()`.

If you're running everything on *Nix you're also better off using
[epoll](https://docs.python.org/3/library/select.html#select.epoll).

    
    
    from select import epoll, EPOLLIN
    poll = epoll()
    poll.register(s.fileno(), EPOLLIN)
    
    events = poll.poll(1) # 1 sec timeout
    for fileno, event in events:
        if event is EPOLLIN and fileno == s.fileno():
            data = s.recv(1024)
    

This is a crude example of how epoll could be used.  
But it's quite fun to play around with and you should [read more about
it](https://stackoverflow.com/questions/17355593/why-is-epoll-faster-than-
select)

