我正在尝试使用QueryServer连接到TeamSpeak服务器来制作机器人。我已经从this thread获得建议,但是我仍然需要帮助。

这是我正在使用的The TeamSpeak API

在进行编辑之前,这是我的脚本(1个连接)中实际发生的情况的摘要:


它连接。
它检查频道ID(以及它自己的客户端ID)
它加入频道并开始阅读所有内容
如果有人说了一条特定的命令,它将执行该命令,然后断开连接。


我该如何做才能使其不断开连接?如何使脚本保持“等待”状态,以便在执行命令后继续读取?

我正在使用Python 3.4.1。
我尝试学习线程技术,但要么我很笨,要么以我认为的方式无法正常工作。还有一个“ bug”,一旦等待事件,如果我不使用命令触发任何东西,它将在60秒后断开连接。

#Librerias
import ts3
import threading
import datetime
from random import choice, sample

# Data needed #
USER = "thisisafakename"
PASS = "something"
HOST = "111.111.111.111"
PORT = 10011
SID = 1


class BotPrincipal:
    def __init__(self, manejador=False):
        self.ts3conn = ts3.query.TS3Connection(HOST, PORT)
        self.ts3conn.login(client_login_name=USER, client_login_password=PASS)
        self.ts3conn.use(sid=SID)
        channelToJoin = Bot.GettingChannelID("TestingBot")
        try: #Login with a client that is ok
            self.ts3conn.clientupdate(client_nickname="The Reader Bot")
            self.MyData = self.GettingMyData()
            self.MoveUserToChannel(ChannelToJoin, Bot.MyData["client_id"])
            self.suscribirEvento("textchannel", ChannelToJoin)
            self.ts3conn.on_event = self.manejadorDeEventos
            self.ts3conn.recv_in_thread()
        except ts3.query.TS3QueryError: #Name already exists, 2nd client connect with this info
            self.ts3conn.clientupdate(client_nickname="The Writer Bot")
            self.MyData = self.GettingMyData()
            self.MoveUserToChannel(ChannelToJoin, Bot.MyData["client_id"])

    def __del__(self):
        self.ts3conn.close()

    def GettingMyData(self):
        respuesta = self.ts3conn.whoami()
        return respuesta.parsed[0]

    def GettingChannelID(self, nombre):
        respuesta = self.ts3conn.channelfind(pattern=ts3.escape.TS3Escape.unescape(nombre))
        return respuesta.parsed[0]["cid"]

    def MoveUserToChannel(self, idCanal, idUsuario, passCanal=None):
        self.ts3conn.clientmove(cid=idCanal, clid=idUsuario, cpw=passCanal)

    def suscribirEvento(self, tipoEvento, idCanal):
        self.ts3conn.servernotifyregister(event=tipoEvento, id_=idCanal)

    def SendTextToChannel(self, idCanal, mensajito="Error"):
        self.ts3conn.sendtextmessage(targetmode=2, target=idCanal, msg=mensajito) #This works
        print("test") #PROBLEM HERE This doesn't work. Why? the line above did work

    def manejadorDeEventos(sender, event):
        message = event.parsed[0]['msg']
        if "test" in message: #This works
            Bot.SendTextToChannel(ChannelToJoin, "This is a test") #This works


if __name__ == "__main__":
    Bot = BotPrincipal()
    threadprincipal = threading.Thread(target=Bot.__init__)
    threadprincipal.start()


在使用2个机器人之前,我进行了测试,以在连接时启动SendTextToChannel,并且它可以完美运行,在将文本发送到通道之后,我可以做任何我想做的事情。只有在manejadorDeEventos触发的情况下,才会停止使整个python代码停止的错误

编辑1-试用线程。
我用线程弄乱了时间,得到了2个客户端同时连接的结果。我以某种方式认为其中之一正在阅读事件,而另一个正在回答。该脚本不再自行关闭,这是一个胜利,但是拥有克隆连接看起来并不好。

编辑2-更新代码和问题的实际状态。
我设法使双连接或多或少地处于“精细”状态,但是如果60秒钟内房间什么也没发生,它将断开连接。使用Threading.timer尝试过,但是我无法使其正常工作。整个问题代码已更新。

我想要一个答案,它可以帮助我从频道中读取内容并对其进行回答,而无需为其连接第二个机器人(就像它实际上在做...)。如果答案也对我有帮助,我会给出额外的分数了解每50秒对服务器进行一次查询的简便方法,这样它就不会断开连接。

最佳答案

通过查看the sourcerecv_in_thread不会创建一个在接收消息之前一直循环直到退出时间的线程,而是创建一个接收单个消息然后退出的线程:

def recv_in_thread(self):
    """
    Calls :meth:`recv` in a thread. This is useful,
    if you used ``servernotifyregister`` and you expect to receive events.
    """
    thread = threading.Thread(target=self.recv, args=(True,))
    thread.start()
    return None


这意味着您必须反复调用recv_in_thread,而不仅仅是调用一次。

我不确定从阅读文档中该怎么做,但是大概是在由接收到的事件触发的任何回调的末尾。我认为这是您的manejadorDeEventos方法? (或者也许与servernotifyregister方法有关?我不确定servernotifyregister是什么,on_event是什么……)



manejadorDeEventos提出了两个要点:


您已声明manejadorDeEventos错误。每个方法都必须将self作为其第一个参数。当您传递绑定方法(如self.manejadorDeEventos)时,该绑定的self对象将作为第一个参数传递,而调用方将传递任何参数。 (对于classmethodstaticmethod,有一些例外,但不适用于此处。)而且,在该方法中,几乎可以肯定的是,您应该访问的是self,而不是全局变量Bot恰好与self是同一对象。
如果manejadorDeEventos实际上是recv_in_thread的回调,则这里有一个竞争条件:如果在主线程完成on_event分配之前出现第一条消息,则recv_on_thread将无法调用您的事件处理程序。 (这恰恰是这种错误,经常会在一百万次中出现一次,当您在部署或发布代码几个月后发现它时,就很难进行调试。)因此,请反转这两行。




最后一件事:简要浏览该库的代码有点令人担忧。它看起来好像不是由真正知道自己在做什么的人写的。我上面复制的方法只有3行代码,但是它包含一个无用的return None和一个永远无法被Thread泄漏的join,更不用说使您调用此方法的整个设计了(并且在收到的每个事件都怪异之后生成一个新线程),并且在没有真正解释的情况下更是如此。如果这是您必须使用的服务的标准客户端库,那么您实际上没有太多选择,但是如果没有,我会考虑寻找其他库。

07-26 09:40