Search code examples
pythonpython-2.7pygtkircglade

Making a irc bot with a gui. Difficulties with disconnecting on toggle button toggle


I'm just learning python as I need it, so hold on thight because this code gonna be messy!...

So I worked with glade to create a gui for my client side twitch irc chat bot and created this toggle button in a toolbar:

<object class="GtkToggleToolButton" id="tool_deploy_toggle">
  <property name="use_action_appearance">False</property>
  <property name="visible">True</property>
  <property name="can_focus">False</property>
  <property name="label" translatable="yes">Connect</property>
  <property name="use_underline">True</property>
  <property name="stock_id">gtk-jump-to</property>
  <signal name="toggled" handler="on_tool_deploy_toggle_toggled" swapped="no"/>
</object>

And I want this toggle button to open a socket and deploy the bot to the twitch irc chat when the button is toggled "down" (and also do some defining and loading stuff as you can see):

irc = botOpenSocket()
joinRoom(irc)
readbuffer = ""
irc.send("CAP REQ :twitch.tv/membership\r\n")
irc.send("CAP REQ :twitch.tv/commands\r\n")
irc.send("CAP REQ :twitch.tv/tags\r\n")

try:
    with file("commands.json","r") as commandsDatabase:
        commands = json.load(commandsDatabase)
except IOError:
    commands = {}
    with file("commands.json","w") as commandsDatabase:
        json.dump(commands, commandsDatabase)

globalcommands = {"spank": True}
moderatorcommands = {"addcom": True, "delcom": True}
stringspace = " "
nothing = ""
now = time.time()
cooldown = lambda: time.time() > now + 1

Then I want it to stay looping this code(ignore the comments they are in portuguese)(also yes I know my code isn't the best, I'm just learning):

while True:
    readbuffer = readbuffer + irc.recv(1024)
    temp = string.split(readbuffer, "\n")
    readbuffer = temp.pop()

    for line in temp:
###Essenciais###--------------------------------------------------------------------------------------------------------------------------------------------
#Mostra a linha que e dada pelo servidor de IRC (So pelo sim pelo nao).-----------------------------------------------------------------------
        print (line)
#---------------------------------------------------------------------------------------------------------------------------------------------
#Impede que seja desconectado pelo servidor de IRC.-------------------------------------------------------------------------------------------
        if line.startswith('PING'):
            irc.send('PONG ' + line.split( ) [ 1 ] + '\r\n')
            print "PONGED BACK"
            break
#---------------------------------------------------------------------------------------------------------------------------------------------
#Le a linha que e dada pelo servidor de IRC e devevole o utilizador, a menssagem e o canal. Volta se algum for nulo.--------------------------
        user = getUser(line)
        message = getMessage(line)
        channel = getChannel(line)
        moderator = getModerator(line)
        if channel == None or user == None or message == None:
            break
#---------------------------------------------------------------------------------------------------------------------------------------------
#Formata o texto e mostra mostra na consola.--------------------------------------------------------------------------------------------------
        print channel + ": " + user + " > " + message
#---------------------------------------------------------------------------------------------------------------------------------------------
###Essenciais END###----------------------------------------------------------------------------------------------------------------------------------------

        if message == "!commands\r":
            globalcommandskeys = str(globalcommands.keys()).replace("[", "").replace("]", "")
            moderatorcommandskeys = str(moderatorcommands.keys()).replace("[", "").replace("]", "")
            channelcommandskeys = str(commands.keys()).replace("[", "").replace("]", "")
            sendMessage(irc, "Global commands: " + globalcommandskeys)
            if channelcommandskeys != "":
                sendMessage(irc, "Channel specific commands: " + channelcommandskeys )
            if moderator == "1":
                sendMessage(irc, "Moderator commands: " + moderatorcommandskeys)
            break


        if message.startswith("!addcom ") and (moderator == "1" or user == channel):
            if message.count(" ") >= 2:
                try:
                    commandadd = command_add(message)
                    answer = command_answer(message)
                except IndexError:
                    sendMessage(irc, user + " the command is used this way !addcom !<command_name> <command_answer>")
                    break
                if globalcommands.has_key(commandadd) or moderatorcommands.has_key(commandadd):
                    sendMessage(irc, user + " you can't add the command " + '"!' + commandadd + '" !!!')
                    break
                try:
                    commands[commandadd]
                except KeyError:
                    commands[commandadd] = answer
                    sendMessage(irc, user + " the command !" + commandadd + " has been added!!!")
                    with file("commands.json","w") as commandsDatabase:
                        json.dump(commands, commandsDatabase)
                    break
                sendMessage(irc, user + " the command you tried to add alredy exists!!!")
                break
            sendMessage(irc, user + " the command is used this way !addcom !<command_name> <command_answer>")
            break

        if message.startswith("!delcom ") and (moderator == "1" or user == channel):
            if message.count(" ") == 1:
                try:
                    commanddel = command_del(message)
                except IndexError:
                    sendMessage(irc, user + "the command is used this way !delcom !<command_name>")
                    break
                if globalcommands.has_key(commanddel) or moderatorcommands.has_key(commanddel):
                    sendMessage(irc, user + " you can't delete the command " + '"!' + commanddel + '" !!!')
                    break
                try:
                    commands[commanddel]
                except KeyError:
                    sendMessage(irc, user + " the command you tried to delete doens't exist!!!")
                    break
                del commands[commanddel]
                sendMessage(irc, user + " the command !" + commanddel + " has been deleted!!!")
                with file("commands.json","w") as commandsDatabase:
                    json.dump(commands, commandsDatabase)
                break
            sendMessage(irc, user + " the command is used this way !delcom !<command_name>")
            break

        if message.startswith("!"):
            if cooldown() == True:
                if message.count(" ") == 0:
                    try:
                        command = getCommand(message)
                    except IndexError:
                        break
                    try:
                        sendMessage(irc, commands[command])
                        now = time.time()
                        cooldown = lambda: time.time() > now + 10
                    except KeyError:
                        break
                if message.count(" ") == 1:
                    try:
                        command = getCommandSpaced(message)
                        target = getString(message)
                    except IndexError:
                        break
                    try:
                        replacing = commands[command]
                        sendMessage(irc, replacing.replace("$target", target))
                        now = time.time()
                        cooldown = lambda: time.time() > now + 10
                    except KeyError:
                        break
                break

And then finally when the button is toggled "up" I want to close the socket so the bot leaves the irc server:

irc.close()

I want all the above things to be able to be done without closing and reopening the script.

So the problem is I can't do this.

If I put into the main script(the one that connects the button signals from the GUI) it will break the gtk main loop and the GUI will crash.

I've tried to use threads but I don't seem to understand them.


Solution

  • Status update I done more research on threads and got an example of a thread from another stackoverflow post and got it working!

    I created this thread(Note that after joinRoom(irc, self)the socket is set to nonblocking if the connection is sucessful otherwise it does a loop.clear() wich makes it never enter the bot main loop and just goes straight into irc.close()):

    gobject.threads_init()
    
    class T(threading.Thread):
        loop = threading.Event()
        stop = False
    
        def start(self, *args):
            super(T, self).start()
    
        def run(self):
            while not self.stop:
                #Waits for button to be clicked.#
                self.loop.wait()
    
                #Bot Startup sequence.#
                deploy_button.set_label('Disconnect')
                irc = botOpenSocket()
                joinRoom(irc, self)
                readbuffer = ""
                irc.send("CAP REQ :twitch.tv/membership\r\n")
                irc.send("CAP REQ :twitch.tv/commands\r\n")
                irc.send("CAP REQ :twitch.tv/tags\r\n")
    
                try:
                    with file("commands.json","r") as commandsDatabase:
                        commands = json.load(commandsDatabase)
                except IOError:
                    commands = {}
                    with file("commands.json","w") as commandsDatabase:
                        json.dump(commands, commandsDatabase)
    
                globalcommands = {"spank": True}
                moderatorcommands = {"addcom": True, "delcom": True}
                stringspace = " "
                nothing = ""
                now = time.time()
                cooldown = lambda: time.time() > now + 1
    
                #Keeps reading chat and awsering.#
                while self.loop.is_set():
                    try:
                        readbuffer = readbuffer + irc.recv(1024)
                        temp = string.split(readbuffer, "\n")
                        readbuffer = temp.pop()
                    except:
                        pass
                    else:
                        for line in temp:
                    ###Essenciais###--------------------------------------------------------------------------------------------------------------------------------------------
                    #Mostra a linha que e dada pelo servidor de IRC (So pelo sim pelo nao).-----------------------------------------------------------------------
                            print (line)
                    #---------------------------------------------------------------------------------------------------------------------------------------------
                    #Impede que seja desconectado pelo servidor de IRC.-------------------------------------------------------------------------------------------
                            if line.startswith('PING'):
                                irc.send('PONG ' + line.split( ) [ 1 ] + '\r\n')
                                print "PONGED BACK"
                                break
                    #---------------------------------------------------------------------------------------------------------------------------------------------
                    #Le a linha que e dada pelo servidor de IRC e devevole o utilizador, a menssagem e o canal. Volta se algum for nulo.--------------------------
                            user = getUser(line)
                            message = getMessage(line)
                            channel = getChannel(line)
                            moderator = getModerator(line)
                            if channel == None or user == None or message == None:
                                break
                    #---------------------------------------------------------------------------------------------------------------------------------------------
                    #Formata o texto e mostra mostra na consola.--------------------------------------------------------------------------------------------------
                            print channel + ": " + user + " > " + message
                    #---------------------------------------------------------------------------------------------------------------------------------------------
                    ###Essenciais END###----------------------------------------------------------------------------------------------------------------------------------------
    
                            if message == "!commands\r":
                                globalcommandskeys = str(globalcommands.keys()).replace("[", "").replace("]", "")
                                moderatorcommandskeys = str(moderatorcommands.keys()).replace("[", "").replace("]", "")
                                channelcommandskeys = str(commands.keys()).replace("[", "").replace("]", "")
                                sendMessage(irc, "Global commands: " + globalcommandskeys)
                                if channelcommandskeys != "":
                                    sendMessage(irc, "Channel specific commands: " + channelcommandskeys )
                                if moderator == "1":
                                    sendMessage(irc, "Moderator commands: " + moderatorcommandskeys)
                                break
    
    
                            if message.startswith("!addcom ") and (moderator == "1" or user == channel):
                                if message.count(" ") >= 2:
                                    try:
                                        commandadd = command_add(message)
                                        answer = command_answer(message)
                                    except IndexError:
                                        sendMessage(irc, user + " the command is used this way !addcom !<command_name> <command_answer>")
                                        break
                                    if globalcommands.has_key(commandadd) or moderatorcommands.has_key(commandadd):
                                        sendMessage(irc, user + " you can't add the command " + '"!' + commandadd + '" !!!')
                                        break
                                    try:
                                        commands[commandadd]
                                    except KeyError:
                                        commands[commandadd] = answer
                                        sendMessage(irc, user + " the command !" + commandadd + " has been added!!!")
                                        with file("commands.json","w") as commandsDatabase:
                                            json.dump(commands, commandsDatabase)
                                        break
                                    sendMessage(irc, user + " the command you tried to add alredy exists!!!")
                                    break
                                sendMessage(irc, user + " the command is used this way !addcom !<command_name> <command_answer>")
                                break
    
                            if message.startswith("!delcom ") and (moderator == "1" or user == channel):
                                if message.count(" ") == 1:
                                    try:
                                        commanddel = command_del(message)
                                    except IndexError:
                                        sendMessage(irc, user + "the command is used this way !delcom !<command_name>")
                                        break
                                    if globalcommands.has_key(commanddel) or moderatorcommands.has_key(commanddel):
                                        sendMessage(irc, user + " you can't delete the command " + '"!' + commanddel + '" !!!')
                                        break
                                    try:
                                        commands[commanddel]
                                    except KeyError:
                                        sendMessage(irc, user + " the command you tried to delete doens't exist!!!")
                                        break
                                    del commands[commanddel]
                                    sendMessage(irc, user + " the command !" + commanddel + " has been deleted!!!")
                                    with file("commands.json","w") as commandsDatabase:
                                        json.dump(commands, commandsDatabase)
                                    break
                                sendMessage(irc, user + " the command is used this way !delcom !<command_name>")
                                break
    
                            if message.startswith("!"):
                                if cooldown() == True:
                                    if message.count(" ") == 0:
                                        try:
                                            command = getCommand(message)
                                        except IndexError:
                                            break
                                        try:
                                            sendMessage(irc, commands[command])
                                            now = time.time()
                                            cooldown = lambda: time.time() > now + 10
                                        except KeyError:
                                            break
                                    if message.count(" ") == 1:
                                        try:
                                            command = getCommandSpaced(message)
                                            target = getString(message)
                                        except IndexError:
                                            break
                                        try:
                                            replacing = commands[command]
                                            sendMessage(irc, replacing.replace("$target", target))
                                            now = time.time()
                                            cooldown = lambda: time.time() > now + 10
                                        except KeyError:
                                            break
                                    break
    
                #When button is clicked again do the shutdown sequence.#
                print "coming here"
                irc.close()
                deploy_button.set_label('Connect')
    
                #Waits for 0.1 seconds before going to the top again, just to be sure.#
                time.sleep(0.1)
    

    And created this button(I changed to a normal button instead of a toggle because I tougth it looked better, Im pretty sure it would work with a toggle too):

    <object class="GtkToolButton" id="tool_deploy_button">
     <property name="use_action_appearance">False</property>
     <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="label" translatable="yes">Connect</property>
     <property name="use_underline">True</property>
     <property name="stock_id">gtk-jump-to</property>
     <signal name="clicked" handler="on_tool_deploy_button_clicked" swapped="no"/>
    </object>
    

    And defined it:

    #Defines builder and glade file.#
    builder = gtk.Builder()
    builder.add_from_file("GUI.glade")
    
    #Gets the main widow and shows it.#
    main_Window = builder.get_object("blasterbot_mainwindow")
    main_Window.show_all()
    #Gets some buttons.#
    deploy_button = builder.get_object("tool_deploy_button")
    
    #Starts the thread and the main loop.#
    thread = T()
    def bot_thread(*args):
        if not thread.is_alive():
            thread.start()
            thread.loop.set()
            #deploy_button.set_label('Disconnect') - Old Sutff
            return
    
        if thread.loop.is_set():
            thread.loop.clear()
            #deploy_button.set_label('Connect') - Old Sutff
        else:
            thread.loop.set()
            #deploy_button.set_label('Disconnect') - Old Sutff
    

    And connected the handlers:

    #Handler Connection.#
    handlers = {
    "on_blasterbot_mainwindow_destroy": gtk.main_quit,
    "on_tool_deploy_button_clicked": bot_thread
    }
    builder.connect_signals(handlers)
    
    #Stuff I know I need but don't know what is for.#
    gtk.main()