2014-06-13 39 views
0

我有一個IRC客戶端接收套接字上的消息。發送字符串消息給多個線程

從這個客戶端我創建了幾個機器人,連接到其他人在twitch上的聊天頻道。 (這些是授權的而不是垃圾郵件機器人!)。

每個bot都是在一個單獨的線程中創建的,該線程將通道名稱和其他一些參數一起使用。

我的問題是我的IRC套接字只能綁定到一個端口,並處理所有的IRC消息,每個消息都有一個#channel字符串作爲第三個字符串,將它指向特定的通道。這些消息可以在每個機器人內部處理,因爲每個人都知道其頻道的名稱。

我的問題是;如何將通過套接字接收的字符串發送到多個線程?

import time 
import socket 
import threading 
import string 
import sys 
import os 

class IRCBetBot: 
    #irc ref 
    irc = None 

    def __init__(self,IRCRef,playerName,channelName,currencyName): 

     #assign variables 
     self.irc = IRCRef 
     self.channel = '#' + channelName 

     self.irc.send(('JOIN ' + self.channel + '\r\n') .encode("utf8")) 

     #create readbuffer to hold strings from IRC 
     readbuffer = "" 

     # This is the main loop 
     while 1: 

      ##readbuffer## <- need to send message from IRC to this variable 

      for line in temp: 
       line=str.rstrip(line) 
       line=str.split(line) 

       if (len(line) >= 4) and ("PRIVMSG" == line[1]) and (self.channel == line[2]) and not ("jtv" in line[0]): 
        #call function to handle user message 
       if(line[0]=="PING"): 
        self.irc.send(("PONG %s\r\n" % line[0]).encode("utf8")) 




def runAsThread(ircref,userName, channelName, currencyPrefix): 
    print("Got to runAsThread with : " + str(userName) + " " + str(channelName) + " " + str(currencyPrefix)) 
    IRCBetBot(ircref,userName,channelName,currencyPrefix) 

# Here we create the IRC connection 
#IRC connection variables 
nick = 'mybot'     #alter this value with the username used to connect to IRC eg: "username". 
password = "oauth:mykey"  #alter this value with the password used to connect to IRC from the username above. 
server = 'irc.twitch.tv' 
port = 6667 

#create IRC socket 
irc = socket.socket() 

irc.connect((server, port)) 

#sends variables for connection to twitch chat 
irc.send(('PASS ' + password + '\r\n').encode("utf8")) 
irc.send(('USER ' + nick + '\r\n').encode("utf8")) 
irc.send(('NICK ' + nick + '\r\n').encode("utf8")) 

# Array to hold all the new threads 
threads = [] 
# authorised Channels loaded from file in real program 
authorisedChannels = [["user1","#channel1","coin1"],["user2","#channel2","coin2"],["user3","#channel3","coin3"]] 

for item in authorisedChannels: 
    try: 
     userName = item[0] 
     channelName = item[1] 
     currencyPrefix = item [2] 
     myTuple = (irc,userName,channelName,currencyPrefix) 
     thread = threading.Thread(target=runAsThread,args = myTuple,) 
     thread.start() 
     threads.append(thread) 
     time.sleep(5) # wait to avoid too many connections to IRC at once from same IP 
    except Exception as e: 
     print("An error occurred while creating threads.") 
     print(str(e)) 

#create readbuffer to hold strings from IRC 
readbuffer = "" 

# This is the main loop 
while 1: 
    readbuffer= readbuffer+self.irc.recv(1024).decode("utf-8") 
    temp=str.split(readbuffer, "\n") 
    readbuffer=temp.pop() 
    # 
    #Need to send readbuffer to each IRCBetBot() created in runAsThread that contains a while 1: loop to listen for strings in its __init__() method. 
    # 

print ("Waiting...") 

for thread in threads: 
    thread.join() 

print ("Complete.") 

我需要以某種方式得到主迴路的readbuffer成單獨的線程創建的每個IRCBetBot對象?有任何想法嗎?

+0

你想發送給readbuffer每一個線程,或者只是取其線程的消息實際上是對? – dano

+0

好吧,或者可行,我只需要將消息發送到適當的線程,但是可以在手動或在線程中完成擬合。我認爲在線程中做它可能會更好,因爲它知道它是誰,即它是頻道名稱。 – Zac

回答

1

下面是一個示例,顯示如何使用每個線程的隊列來完成此操作。我們不是隻創建一個線程列表,而是創建一個以通道爲關鍵字的線程的字典,並存儲線程對象和可用於與字典中的線程交談的隊列。

#!/usr/bin/python3 

import threading 
from queue import Queue 


class IRCBetBot(threading.Thread): 
    def __init__(self, q, playerName, channelName, currencyName): 
     super().__init__() 
     self.channel = channelName 
     self.playerName = playerName 
     self.currencyName = currencyName 
     self.queue = q 

    def run(self): 
     readbuffer = "" 
     while 1: 
      readbuffer = self.queue.get() # This will block until a message is sent to the queue. 
      print("{} got msg {}".format(self.channel, readbuffer)) 

if __name__ == "__main__": 

    authorisedChannels = [["user1","#channel1","coin1"], 
          ["user2","#channel2","coin2"], 
          ["user3","#channel3","coin3"]] 

threads = {} 
for item in authorisedChannels: 
    try: 
     userName = item[0] 
     channelName = item[1] 
     currencyPrefix = item [2] 
     myTuple = (userName,channelName,currencyPrefix) 
     q = Queue() 
     thread = IRCBetBot(q, *myTuple) 
     thread.start() 
     threads[channelName] = (q, thread) 
    except Exception as e: 
     print("An error occurred while creating threads.") 
     print(str(e)) 

while 1: 
    a = input("Input your message (channel: msg): ") 
    channel, msg = a.split(":") 
    threads[channel][0].put(msg) # Sends a message using the queue object 

正如你可以看到,當郵件進入插座,我們解析通道出(你的代碼已經這樣做了),然後就傳遞給我們的線程字典合適的隊列中的消息。

樣本輸出(略調整,這樣輸出不炒因併發print話費):

[email protected]:~$ ./test.py 
Input your message (channel: msg): #channel1: hi there 
#channel1 got msg hi there 
Input your message (channel: msg): #channel2: another one 
#channel2 got msg another one 
+0

這絕對是我要努力將它合併到我的代碼中的方法。是否有理由從IRCBetBot構造函數中刪除IRCRef引用?我收到了寫入消息,但偶爾IRCBetBot會發送一條消息返回錯誤的頻道,我將IRCRef重新歸入IRCBetBot,這是原因嗎? – Zac

+0

@Zac,我剛剛刪除了'IRCRef',因爲沒有必要演示如何在線程之間傳遞消息。我不確定你看到的問題的原因是...當'IRCRef'發送一條消息時,它是否明確地說出它應該去哪個頻道? – dano

+0

現在工作正常,我將如何一次向所有通道發送消息?我試圖把線程放在:for循環中:「對於線程中的項目」,但我無法弄清楚如何從那裏索引隊列,而item [0] .put()只是返回錯誤:string沒有put方法。 – Zac

0

那麼一種方法可能是讓readBuffers的數組類似於線程數組。然後每個線程基本上都在等待它的特定readbuffer上的數據。

當您獲取數據時,您可以將它傳遞給您感興趣的線程,或者將數據複製到所有讀緩衝區,並讓線程處理它們(如果他們感興趣)。在這種情況下,觀察者模式最適合。

+0

我將如何確定哪個讀取緩衝區由哪個線程讀取?你是否建議使用threads []和readbuffers []並保持每個索引相同,即:treads [0] <-> readbuffers [0]。如果是的話如何從我的線程中訪問/讀取readbuffer [x]? – Zac

+0

是的,基於1-1索引的映射是可行的。如果readBuffers是一個類似於線程的全局變量,那麼你可以從線程訪問readBuffers沒問題:) –