我们之前在博客中写过一些关于 IRC 的内容,并谈到了 Freenode IRC 网络上的 #LinuxAcademy 频道。我们的学生多次明确表示他们希望我们能提供更多 Python 内容。正如我们之前宣布的那样,我们的新导师 Shiraz 将提供更多内容。不过,在他准备发布新的 Python 内容之前,我想通过一个我喜欢的有趣 Python 项目来帮助您渡过难关:制作 IRC 机器人。在这篇博文中,我将介绍如何使用 Python 3 创建基本的 IRC 机器人。我们假设您具备 Python 和函数的基本知识,如果您已经学习了我们的Linux 上的 Python 简介课程,那么您应该具备此基本 IRC 机器人教程所需的所有知识。如果您有任何问题或需要任何帮助,请随时在下面发表评论或访问 Freenode 上的 #LinuxAcademy 频道。我在 IRC(和大多数其他地方)上的用户名是 OrderChaos,所以请留意这个名字!
IRC 机器人
在我们开始之前,您可能想知道“什么是 IRC 机器人?” 非常有用且精彩的 Wikipedia 将 IRC 机器人定义为“一组脚本或一个独立程序,它作为客户端连接到 Internet 中继聊天,因此对其他 IRC 用户来说,它就像另一个用户。IRC 机器人与常规客户端的不同之处在于,它不是为人类用户提供对 IRC 的交互式访问,而是执行自动化功能。”(来源)因此,基本上,IRC 机器人对其他所有人来说都是另一个用户,但它会根据其脚本执行设置的操作以响应预定事件(通常是聊天中的特定消息)。它们可以用于各种目的:从保存聊天记录到管理工具,再到许多其他愚蠢、有用或有时奇怪的功能。有些机器人可以读取 Twitter 提要、执行网络搜索,甚至可以执行sed
查找和替换之类的操作(我已经为此编写了一个,并将其分享到 GitHub 上!有关详细信息,请参阅下面的总结部分)。IRC 机器人可以用多种语言编写。 PHP 和 Python 是两种常见的 IRC 机器人,但也有许多其他语言的 IRC 机器人,包括 C、Java 甚至 Haskell。维基百科有一个页面列出了许多 IRC 机器人的比较。其中许多机器人现在已经不复存在,不再开发或使用,但它们可以作为灵感来源,读起来也很有趣。好的,背景知识足够了。让我们进入最酷的部分:创建您自己的 IRC 机器人。
准备机器人
在我们开始为我们的机器人编写所有酷炫有趣的东西之前,首先我们必须准备好机器人本身。我们需要确保我们已经设置了 Python,分配了一些变量,并确保我们可以连接到 IRC 网络。在我们开始之前的最后一点说明,我将在这篇文章的附件中提供我们经过的完整代码 — 如果您在编码时迷路了,只需对照示例查看您应该在哪里。所有代码的解释都包含在附加机器人的评论中,因此您可以在该文件中获得所需的所有信息。
Python 设置
首先,请确保您已安装 Python 3。我们假设您在阅读本文之前已安装并配置了 Python 3。打开您最喜欢的Python IDE(如果没有,可以使用纯文本编辑器,甚至终端中的 vim)。首先,我们应该确保指定我们正在使用 Python 3。对于 Windows,这意味着将文件命名为以.py结尾的文件。对于 Linux/Mac,请在文件顶部包含此行:#!/usr/bin/python3
。指定 Python3 后,我们需要导入“socket”库。套接字库用于通过网络端口进行连接和通信。我们使用它来连接和与 IRC 服务器通信。在 Python 中导入库非常容易,而且由于套接字库是内置的,您无需安装任何东西。只需在行import socket
下添加#!/usr/bin/python3
。所以现在您的文件应该只包含以下两行:
#!/usr/bin/python3导入套接字
全局变量
现在我们已经指定了我们在 Python3 中工作并导入了套接字库,我们需要定义一些变量。这些变量在整个机器人中都会使用,因此我们希望在编写任何函数之前定义它们。第一个变量是我们用于连接和与 IRC 服务器通信的套接字。套接字很复杂,可以以多种方式用于许多任务。如果您想了解有关套接字的更多信息,请参阅此处: https://docs.python.org/3/howto/sockets.html。现在,只需知道这用于在机器人运行时与 IRC 服务器建立持续连接,以发送和接收信息。接下来是我们要连接的服务器和频道的名称。我们可以硬编码这些,但有一个变量可以使几个步骤更容易。例如,如果我们想要连接到频道列表(而不是像本例中那样只有一个)或更改为不同的服务器或频道,我们不必找到每个实例,而只需编辑此变量即可。我在本例中使用chat.freenode.net。对于其他 IRC 网络,只需在相同位置输入名称即可。之后,我们定义最终要加入的频道。我们不想在测试时使用官方/已建立的频道。除了粗鲁之外,许多频道对机器人有特定的规则或根本不允许使用它们。在将机器人添加到频道之前,请务必与频道的版主核实。对于我们的测试,我们使用自定义的未注册房间(在 Freenode 上,在频道名称前用“##”表示)。这样,在我们进行测试时,我们将是频道中唯一拥有机器人的人。botnick是我们为机器人命名的。这是其他用户在频道中看到机器人的方式。确保这是一个未使用且未注册的昵称,因为如果您的机器人已在使用中,它将无法连接,而如果它是已注册且受保护的昵称,它将被分配一个随机名称。有关昵称注册的更多信息,请参见此处最后两个变量将在我们的主函数中使用。它在 IRC 机器人中不是必需的,但我们要使用的函数需要它。我们所做的就是定义一个可以向机器人发送管理命令的昵称和一个用于结束机器人脚本的退出代码。我们将在主函数的末尾了解如何执行此操作。
ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server = "chat.freenode.net" # Serverchannel = "##bot-testing" # Channelbotnick = "IamaPythonBot" # Your bots nickadminname = "OrderChaos" #Your IRC nickname. 在 IRC (和大多数其他地方) 我使用 OrderChaos 所以我在这个例子中使用它。exitcode = "bye " + botnick
连接到 IRC
现在我们已经准备好了机器人并建立了一些要使用的变量,让我们开始实际的连接部分。要连接到 IRC,我们需要使用套接字变量 (ircsock) 并连接到服务器。IRC 通常在端口 6667 或 6697 上(6697 通常用于带有 SSL 的 IRC,这更安全)。我们在示例中使用 6667。我们需要将服务器名称(在我们的全局变量中建立)和端口号放在括号内,以便将其作为单个项目传递给连接。建立连接后,我们需要向服务器发送一些信息,让服务器知道我们是谁。我们通过发送用户信息,然后设置我们想要使用的昵称来实现这一点。通常这些是相同的,但注册用户有时会将多个昵称绑定到他们的帐户,并且可以选择其中任何一个。
ircsock.connect((server, 6667)) # 在这里我们使用端口 6667 连接到服务器ircsock.send(bytes("USER "+ botnick +" "+ botnick +" "+ botnick + " " + botnick + "n", "UTF-8")) # 我们基本上用这行填写表格,并将所有字段设置为机器人昵称。ircsock.send(bytes("NICK "+ botnick +"n", "UTF-8")) # 将昵称分配给机器人
定义函数
这里我们定义了机器人使用的各种函数。这些是可能需要多次调用的代码段。
频道加入
连接到 IRC 很好,但除非我们与其他用户处于同一频道,否则它不会有太大用处!我们将其放在一个函数中,而不是像上面的 ircsock.connect 部分那样对其进行硬编码,因为您可以通过一个连接进入多个频道。我们只在本例中使用一个,因此可以对其进行硬编码,但这样您就可以看到它是如何完成的,并且如果您愿意,可以轻松地针对多个频道对其进行修改。在这里,我们将频道名称作为函数的一部分,然后将加入命令发送到 IRC 网络。'bytes' 部分让我们指定发送以 UTF-8 编码的消息。这是明确将正确的编码发送到 IRC 服务器。在 Python 2 中,这不是必需的,但 Python 3 中对字符串编码的更改使其成为一项要求。每当我们向 IRC 服务器发送数据时,您都会看到这一点。另外需要注意的是,消息末尾的“n”是换行符。这相当于在聊天窗口中按 Enter 键。它让服务器知道我们已经完成该命令,而不是将所有命令链接到同一行。发送加入命令后,我们希望启动一个循环,以不断检查并接收来自服务器的新消息,直到我们收到带有“/NAMES 列表结束”的消息。这表明我们已成功加入频道。每个函数如何工作的详细信息在下面的主要函数部分中描述。
def joinchan(chan): # 加入频道。 ircsock.send(bytes("JOIN "+ chan +"n", "UTF-8")) ircmsg = "" while ircmsg.find("/NAMES 列表结束。") == -1: ircmsg = ircsock.recv(2048).decode("UTF-8") ircmsg = ircmsg.strip('nr') print(ircmsg)
乒乓球
不,我不是指桌面游戏。IRC 服务器通常会定期向已连接的用户发送“PING”信号,以确保他们仍处于连接状态。我们必须响应这些信号,让服务器知道我们仍处于连接状态。如果我们不响应这些信号,我们可能会与服务器断开连接,因为它会假定我们已断开连接。此函数不需要接受任何参数,因为响应始终相同。只需使用 PONG 响应任何 PING。不同的服务器对 PING 的响应有不同的要求,因此您可能需要根据您的服务器调整/更新它。我已将此特定示例与 Freenode 一起使用,从未遇到任何问题。
def ping(): # 响应服务器 Ping。ircsock.send(bytes("PONG :pingisn", "UTF-8"))
发送消息
我们已经完成了所有主要准备工作,现在让我们编写一些函数,以便我们的机器人可以真正地做一些事情。这个函数将允许我们向频道或用户发送消息。这个函数所需要的只是接受一个变量,其中包含我们将要发送的消息以及我们将发送给谁的消息。在参数部分使用 target=channel 将“target”变量的默认值设置为通道全局变量。如果只有一个参数 msg 传递给函数,它将使用“target”的默认值。target 和 msg 之间的“:”允许服务器将目标和消息分开。
def sendmsg(msg, target=channel): # 向目标发送消息。ircsock.send(bytes("PRIVMSG "+ target +" :"+ msg +"n", "UTF-8"))
主要功能
好的,现在我们已经有了主动函数,并准备好了所有连接信息,是时候编写机器人的持续部分了。这是机器人的主要功能。它将根据需要调用其他函数并处理从 IRC 收到的信息并确定如何处理它。
启动
我们首先加入在“全局变量”部分中定义的通道。之后,我们开始一个无限循环,以不断检查并接收来自服务器的新信息。这确保我们的连接保持打开状态。我们不想再次调用 main(),因为除了尝试不断重新加入通道之外,在连续多次递归调用函数时会遇到问题。在这种情况下,无限 while 循环效果更好。
def main(): joinchan(channel) while 1:
接收信息
在这里,我们接收 IRC 服务器发送给我们的信息。IRC 发送以 UTF-8 字符编码的信息,因此我们告诉套接字连接接收最多 2048 个字节并将其解码为 UTF-8 字符。然后我们将其分配给 ircmsg 变量进行处理。之后,从字符串中删除所有换行符。如果有人在频道中输入“n”,它仍会将其包含在消息中。这只会删除可能导致处理问题的特殊字符。我们还将收到的信息打印到终端。如果您不想看到它,可以跳过此步骤,但它有助于调试并确保机器人正常运行。
ircmsg = ircsock.recv(2048).decode("UTF-8") ircmsg = ircmsg.strip('nr') 打印(ircmsg)
拆分收到的消息
接下来,检查我们收到的信息是否在文本中包含 PRIVMSG。PRIVMSG 是频道中的标准消息(以及直接发送给机器人的消息)的传入方式。大多数消息处理都在此部分中进行。如果是 PRIVMSG,我们希望获取发送消息的人的昵称并将其从消息中分离出来。来自 IRC 的消息格式为“:[昵称]!~[主机名]@[IP 地址] PRIVMSG [频道] :[消息]”,因此我们将其拆分为不同的部分并将它们分配给单独的变量。
如果 ircmsg.find("PRIVMSG") != -1: 则 name = ircmsg.split('!',1)[0][1:] 消息 = ircmsg.split('PRIVMSG',1)[1].split(':',1)[1]
选择操作
现在,我们在其自己的变量中拥有名称信息,我们检查名称是否少于 17 个字符。用户名(至少对于 Freenode)限制为 16 个字符。因此,通过此检查,我们可以确保我们不会响应无效用户或其他恰好包含“PRIVMSG”的消息。之后,我们使用检测块来查看它是否包含机器人应该采取行动的某些文本。对于第一节,我们正在寻找是否有人在消息中的任何地方向机器人打招呼,然后回复。由于我们没有定义目标,它将被发送到频道。第二个是如何在消息开头查找“命令”并解析它以执行复杂任务的示例。在这种情况下,我们正在寻找以“.tell”开头的消息,并使用它作为代码来查找要发送到的消息和特定目标。整个消息应该看起来像“.tell [target] [message]”才能正常工作。附加的机器人文件中有注释,详细解释了其工作原理。
如果 len(name) < 17: 如果 message.find('Hi ' + botnick) != -1: sendmsg("Hello " + name + "!") 如果 message[:5].find('.tell') != -1: target = message.split(' ', 1)[1] 如果 target.find(' ') != -1: message = target.split(' ', 1)[1] target = target.split(' ')[0] else: target = name message = "无法解析。消息应采用 '.tell [target] [message]' 格式才能正常工作。" sendmsg(message, target)
停止机器人
由于我们在此函数中创建了一个无限循环,因此没有自然结束。相反,我们将检查一些文本并使用它来结束函数(这会自动结束循环)。我们所做的是查看发送消息的人的姓名是否与我们之前定义的管理员姓名相匹配。我们将两者都设为小写,以防管理员在加入时输入的姓名略有不同。在 IRC 上,“OrderChaos”和“orderchaos”是相同的昵称,但 Python 会将它们解释为不同的字符串,除非我们先将其全部转换为小写。我们还确保消息与上面的退出代码匹配。退出代码和消息必须完全相同。这样,管理员仍然可以输入带有额外文本的退出代码来解释它或与其他用户讨论它,并且不会导致机器人退出。我们所做的唯一调整是删除消息末尾的任何空格。因此,如果消息匹配,但末尾有一个额外的空格,它仍然有效。如果管理员发送了退出代码,则该函数将命中“return”行,该行会自动跳出任何循环和 if 语句,并转到调用该函数的行。通常它会继续执行其他代码行,但我们将在调用 main() 时结束脚本,这样就不会再有代码可供它运行,并且机器人将关闭。
如果 name.lower() == adminname.lower() 且 message.rstrip() == exitcode: sendmsg("哦...好的。:'(") ircsock.send(bytes("QUIT n", "UTF-8")) 返回否则:
响应 Ping
如果消息不是 PRIVMSG,则可能仍需要一些响应。如果我们收到的信息是 PING 请求,我们将调用我们之前定义的 ping() 函数以 PONG 进行响应。这让服务器知道我们仍然在线且处于连接状态。
如果 ircmsg.find("PING:") != -1: ping()
启动主函数
最后,既然主函数已经定义好了,我们需要一些代码来启动它。它不接受任何参数,所以就像这一行一样简单。
主要的()
包起来
就这样,你就拥有了一个基本的、可以工作的 IRC 连接机器人。它不是最高效或最防错的机器人,但它是一个很好的开始,可以为你提供一些基础。如果你对更复杂和更高级的 IRC 机器人感兴趣,请查看顶部提到的许多流行 IRC 机器人的 Wikipedia 比较。特别是, Sopel IRC 机器人是一个非常复杂和深入的 IRC 机器人,具有多种功能和插件。它正在积极开发中,有许多贡献者。我有时也会开发自己的 Python IRC 机器人,它还没有很多功能(目前!)。你可以在这里找到原始版本(用 Python 版本 2 编写!):https://github.com/Orderchaos/ircbot。它记录最近的聊天,并可以使用类似 sed 的命令进行查找/替换。开发分支有一个 Python 3 版本,我正在进行一次重大重写,它将展示一些带有 sqlite3 数据库的复杂日志记录功能。附件是 我们刚刚介绍过的机器人。您可以对照它检查您的工作,也可以将其保存到您自己的脚本中并按原样开始使用它!编辑:附件似乎无法正常工作。我们会将机器人上传到我们的 GitHub 并尽快提供。今天晚些时候再回来查看带有评论的完整代码!与此同时,请随意从这篇文章中复制代码并编译您自己的代码!编辑 2:您现在可以在这里找到所有代码:https://github.com/Orderchaos/LinuxAcademy-IRC-Bot 今天晚些时候将添加其他评论,这是原始代码,与本博客文章中显示的完全一样。如果您想聊天或有任何问题,请随时跳转到 Freenode 并加入我们的 #LinuxAcademy 或给我留言(再次强调,我的 Freenode 昵称是 OrderChaos)。
请先 登录后发表评论 ~