【问题标题】:Python 3 (Bot) script stops workingPython 3 (Bot) 脚本停止工作
【发布时间】:2014-12-16 21:42:06
【问题描述】:

我正在尝试使用 QueryServer 连接到 TeamSpeak 服务器以创建机器人。我已经听取了this thread 的建议,但我仍然需要帮助。

这是我正在使用的The TeamSpeak API

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

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

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

我正在使用 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 - 更新代码和问题的实际状态。
我设法使双重连接工作或多或少“正常”,但如果房间内 60 秒内没有任何反应,它会断开连接。尝试使用 Threading.timer 但我无法使其工作。整个问题代码已经更新。

我想要一个答案,它可以帮助我从频道阅读和回答它,而无需为此连接第二个机器人(就像它实际上正在做的那样......)如果答案我会加分还可以帮助我理解一种简单的方法,即每 50 秒向服务器查询一次,这样它就不会断开连接。

【问题讨论】:

  • @Parker:这是更新的代码,有一个新问题,而不是重复。但是,对于 OP:值得尝试找出使这一点更清晰的方法,因为很多人都会立即做出反应。我不确定该怎么做,尤其是当英语不是您的第一语言时……也许常见问题解答有一些指导?
  • 为了将来参考 OP,在本地 git 存储库中编写这样的代码确实是值得的,或者至少在对源代码进行重大修改之前备份源文件。我在项目中犯过非常类似的错误,例如这个错误,它们都随着备份/源管理而消失。

标签: python multithreading python-3.x


【解决方案1】:

通过查看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 对象将作为第一个参数传递,在调用者传递的任何参数之前。 (classmethods 和 staticmethods 有例外,但这些不适用于此处。)此外,在该方法中,您几乎可以肯定访问 self,而不是全局变量 Bot恰好与self 是同一个对象。
  • 如果manejadorDeEventos 实际上是recv_in_thread 的回调,那么这里就有一个竞争条件:如果第一条消息在你的主线程完成on_event 分配之前进入,recv_on_thread 将不会能够调用您的事件处理程序。 (这正是那种经常出现百万分之一的错误,当您在部署或发布代码数月后发现它时,调试起来会非常痛苦。)因此,将这两行颠倒过来。

最后一件事:看一眼这个库的代码有点令人担忧。它看起来不像是真正知道自己在做什么的人写的。我上面复制的方法只有3行代码,但它包括一个无用的return None和一个永远不能被joined泄露的Thread,更不用说让你调用这个方法的整个设计(和在收到每个事件之后产生一个新线程)是很奇怪的,而且考虑到它没有得到真正的解释,更是如此。如果这是您必须使用的服务的标准客户端库,那么您确实没有太多选择,但如果不是,我会考虑寻找不同的库。

【讨论】:

  • 我不能再次调用recv_in_thread,因为停止的是python代码,而不是发送到我电脑的数据。我有点强迫它再次调用它,错误是“已经收到消息”,正如预期的那样。我不明白为什么整个 python 代码都在 SendTextToChannel 部分停止,甚至 Print 函数也停止工作,就像它不是脚本的一部分一样。
  • @Saelyth:好的,这看起来是一个不同的问题。所有send* 方法都会阻塞,直到它们得到响应。如果您的代码在处理前一条消息之前开始处理一条消息没有问题,那么有一个简单的解决方法:只需在调用sendtextmessage 之前调用recv_in_thread(或者,更好的是,在您发送到sendtextmessage 之前)。这将启动一个新线程以等待下一个响应。 (或者,您可以运行自己的线程,并让它在循环中重复调用recv,因此您无需在每次接收后继续启动它。)
  • 那不行,如果我尝试会发生这种情况:prntscr.com/4ypm0x 我也尝试自己运行一个主线程,它有点工作,因为我能够在聊天中询问不同的命令Bot 和 python 代码并没有自行关闭。但是,我注意到它实际上记录了我所说的所有内容,但没有发送文本进行聊天。所以这与我的实际问题相反,它正在执行打印,但不是它上面的行。令人沮丧...
  • 编辑了原帖,最后,我觉得这样更容易理解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-23
  • 2021-09-09
  • 2015-12-03
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 2017-01-10
相关资源
最近更新 更多