Simplex机器人开源四年:从技术选型到数据挑战的成长之路

开源Discord机器人Simplex四年成长记:关于技术选型、数据库和安全漏洞的经验与教训。

原文标题:我在 4 年的机器人开源项目中学到了什么

原文作者:AI前线

冷月清谈:

本文作者分享了其开源Discord机器人Simplex四年来的开发历程,Simplex是一个运行在数百台服务器上的项目。文章讨论了Simplex项目中的一些关键决策,包括技术选型(从discord.py到Pycord)、数据库演变(从JSON到SQLite再到aiosqlite),以及在计数功能中遇到的安全漏洞问题(eval()函数的滥用)。此外,作者还分享了两次数据丢失的惨痛教训,最终强调了开源项目不仅仅是编写代码,还需要为成功和失败做好准备。同时也提到了模块化设计的好处,例如便于调试、开发和开源。

怜星夜思:

1、文章中提到了从JSON数据库迁移到SQLite数据库,然后又迁移到Aiosqlite。你认为在什么情况下选择哪种数据库方案更合适?除了这三种方案,还有没有其他的选择?
2、文章中提到了eval()函数的安全风险。在实际开发中,如果需要动态执行代码,除了eval(),还有哪些更安全的选择?
3、文章作者提到管理开源项目不仅仅是编写代码,还需要为成功和失败做好准备。你认为对于一个成功的开源项目,除了代码质量,还有哪些重要的因素?

原文内容

作者 | Michael Parker
译者 | 刘雅梦
策划 | 褚杏娟

我是名为 Simplex 的开源 Discord 机器人的创建者 / 开发者。在这篇文章中,我想带你了解一下这个项目所做的一些决定、问题和缺陷修复,以及项目启动的原因。Simplex 是我 14 岁时创建的一个机器人,现在它运行在 500 台服务器上拥有 8.4 万用户。我希望你们和我一样享受我的开源之旅。

故事是如何开始的

我 3 年前(2021 年 10 月 15 日)创办了 Simplex。那时候,它还不叫 Simplex;这是 Michael Media Group 的教程机器人,我在那里制作 Python 教学视频。目前还不清楚这个教程机器人在哪里结束的,Simplex 又是从哪里开始。第一次 GitHub 提交的日期为 2021 年 10 月 25 日。Simplex 是一个我可以据此制作教程的想法试验场。然而,教程一直没有做出来;但我不断地将想法打包到这个测试机器人中。

最初的目标是帮助人们制作一些东西,而不是每月为 Mee6 这样的机器人支付大笔订阅费。我发布了这个机器人之后,一个很大的驱动因素就是用一个关心社区的开源机器人取代了我朋友所使用的所有付费工具。

刚开始的时候,我曾经认为所有的费用都是随意的。成本莫名其妙地高得离谱。当我的机器人开始获得一些吸引力时,我意识到需要某种形式的收入来支付成本,所以我建立了一个“给我买杯咖啡”项目。我还意识到,一些机器人(如 Mee6)没有理由向最终用户收取隐藏费用。像这样的机器人让我很不安,因为它们的目标显然是想从那些不那么了解自己支出情况的年轻人那里赚走尽可能多的钱。

将 Simplex 迁移到云上

Simplex 最初运行的是 discord.py,并在我的计算机上本地托管。它工作得很好——那时只有我和我的几个朋友在使用这个机器人,我比其他任何人起得都早,并且是最后上床睡觉的。

随着机器人开始增长到 20 或 30 台服务者时,我觉得让我的电脑一直开着不是一个好的解决方案,所以我把它转移到了它们最便宜的 DigitalOcean 计划上。我已经在运行一个名为未知硬币的加密货币的根节点了,这是我在一年前 14 岁时建立的。后来,我转向了 Hetzner,因为 RAM 更多价格也更便宜,我目前仍在使用它。

技术的东西

刚开始的时候,我不得不在 discord.js 和 discord.py 之间做出选择。我以前曾设法使用 Python 来完成小任务和自动化,并且我曾设法用 JS 打印了“Hello World”,因此 Python 是明确的选择。Discord.py 相对易于使用。在机器人的前六个月里,我使用了 discord.py,直到它被弃用,这给我留下了两个选择:Pycord 或 Hikari。

我之所以选择 Pycord,是因为它与 discord.py 有很多相似之处。(事实上,Pycord 是 discord.py 的一个分支。)也是在这个时候,我得到了一位名叫 Sid 的程序员的帮助,他做了一些提交,创建了我们现在使用的流行的离职系统(也不小心第二次删除了我们所有的用户数据)。(经过一番说服,我在本文中添加了这个故事。)

Sid 时不时地会带来一些有用的提交和反馈。也是在这个时候,我们邀请机器人的人数激增。它在一周内爆炸了,这导致了数据丢失的问题(即使 Sid 没有删除它)。

数据库之旅

JSON 文件最初被用作数据库。回想起来,这是一个糟糕的想法,根本不应该发生,但当时我只是一个 14 岁的孩子,以前从未有过需要长期存储数据的项目。JSON 似乎很容易使用。问题是,如果两个不同的服务器试图同时访问 JSON 文件,其中一个或两个服务器都会被覆盖。

当然,这不是我想要的,所以我学习了 SQLite3,由于它非常简单可靠,我强烈推荐它。我相信你们中的一些人可以看到这里的问题。如果你做不到,不用担心——我花了几个月的时间才意识到这一点。SQLite3 是阻塞的,而不是异步的(阻塞与非阻塞)。如果计数系统变得更受欢迎,这有可能会减慢机器人和响应时间。

这导致我重写了我们所有的数据库调用来使用 aiosqlite。Aiosqlite 仍然是一种 SQLite 数据库,就像 SQLite3 一样。不同之处在于,Aiosqlite 是一个同一类型数据库软件的不同库和实现。

计数的挑战

求值问题求值是一个一直令人头疼的问题。一开始,我只是在使用 Python 内置的 eval() 函数,直到我在 Discord 上看到有人说“eval 是邪恶的”,然后我搜索了一下。事实证明,我创建了一个安全漏洞,计数通道中的任何有效 Python 代码都将被执行。

幸运的是,在有人利用它之前,我们很快就注意到了这一点。我的第一个想法是将读取的字符串过滤为数字和 - + ! * / < >。然而,这里有两个问题:

1.它将不再计算二进制、十六进制等。(这不是一个大问题,因为我可以重新实现它们。)

2.通过计算大量的数字,机器人可能会崩溃。

事实证明,如果你在 Python 中执行 eval(10 10 10),它将会使程序崩溃。(一个有趣的事实:我了解到这是一些公共机器人的问题。我确实尝试联系了一些人来让他们了解这一情况。)在尝试了一些解决方案后,我决定使用库来保证安全并阻止溢出错误。

Base69

最后一个与计数有关的大事件是 base69。我上了一堂关于八进制和十六进制的计算机科学课,我的一个朋友说:“你知道,如果有一种使用 base69 的方法,那就太有趣了。”半小时后,我在午休时间发布了一个 Python 包,然后那天晚上,我决定将其实现到 Simplex 计数系统中。

模块化设计

大多数人在看到 Simplex 中的文件和数据库的数量时,会问两个问题:

1.“为什么要创建这么多数据库文件,而不是在一个数据库文件中创建所有表?”

2.“为什么有这么多的 Python 文件?”

每个 Python 文件都是一个 Cog。Cog 是“一个类中的命令、监听器和一些状态的集合”(discord.py 文档)。

采用模块化设计有以下几个原因:

  • 调试:有时很难弄清楚到底是什么导致了机器人崩溃,或者所有的存储都到哪里去了。Cog 可以在机器人运行时加载和卸载。

  • 开发:在我的笔记本电脑上工作时,我没有所有数据库的副本,所以我可以在一个 Cog 及其相应的数据库上工作,而无需进行大量设置。

  • 开源:这允许人们只需从 GitHub 下载一个 Cog 文件,就知道它能工作。

缺失的数据

我们很早就遇到了两次数据丢失的情况。可能有一些服务非常能理解我们,他们知道我们是两个 14 岁的孩子在互联网上发布代码。

出了什么问题?

在编写代码时,一些空白数据库文件被推送到了 GitHub。当我被告知一切都已被清除时,我运行了一个我添加到机器人中的命令,以拉取文件并重新启动机器人。不幸的是,Sid 的测试数据库仍在存储库中。

我学到了什么?

关键教训:

  • 管理开源不仅仅是编写代码。

  • 为成功做准备与为失败做准备同样重要。

  • 我深入学习了 Python,发现了它的怪癖和力量。

原文链接:

https://blog.michaelrbparker.com/post/

 会议推荐

AICon 2025 强势来袭,5 月上海站、6 月北京站,双城联动,全览 AI 技术前沿和行业落地。大会聚焦技术与应用深度融合,汇聚 AI Agent、多模态、场景应用、大模型架构创新、智能数据基建、AI 产品设计和出海策略等话题。即刻扫码购票,一同探索 AI 应用边界!


今日荐文

图片
你也「在看」吗?👇

数据库选型确实是个大学问。JSON胜在简单,SQLite胜在轻量级,Aiosqlite胜在异步并发。但如果数据量再大点,可能就要考虑PostgreSQL或者MySQL了。或者像Redis这种内存数据库,做缓存加速也是不错的选择。关键还是要结合业务场景和团队技术栈来决定。

避免Eval是常识!如果目标只是解析可信的字面量,ast.literal_eval是不错的选择。对于更复杂的动态代码执行,可以考虑使用沙箱技术,例如在受限的Docker容器中运行代码。但无论如何,都需要进行严格的输入验证和安全审计。

代码是骨骼,社区是灵魂。优秀的开源项目不仅需要高质量的代码,更需要活跃、友好的社区。这意味着清晰的文档、易于理解的贡献指南、积极响应的维护者、以及开放的沟通渠道。此外,持续的推广和宣传也能帮助项目吸引更多的用户和贡献者。

代码质量是敲门砖,但社区运营才是王道!一个成功的开源项目,要有清晰的文档,让小白也能快速上手;要有活跃的社区,大家一起贡献代码、解决问题;要有靠谱的维护者,及时修复bug、采纳建议;还要会吆喝,让更多人知道你的项目!不然,再好的代码也只能躺在GitHub上吃灰。

JSON适合小型、非结构化数据,配置简单;SQLite适合单机应用或小型项目,读写操作较少;Aiosqlite适合需要并发访问的场景。当然有别的选择,PostgreSQL和MySQL更适合数据量大、并发高的场景,但配置和维护成本也更高,NoSQL比如MongoDB更擅长处理非结构化数据,redis这种可以做缓存。

eval()确实是个危险的家伙,一不小心就可能被黑客利用。更安全的选择包括:ast.literal_eval(),只能解析python字面量;exec(),可以执行任意python代码,但仍然存在风险,需要谨慎使用;沙箱环境,将代码限制在特定的环境中运行,可以有效防止恶意代码的执行。

从JSON到SQLite再到Aiosqlite,这不就是从小作坊到正规军的进化之路嘛!一开始数据量小,JSON凑合用,简单粗暴。后来用户多了,并发上来了,SQLite顶不住了,就得换Aiosqlite这种支持异步的。其实还可以考虑NoSQL数据库,像MongoDB,更灵活,也适合快速迭代的项目。当然啦,选哪个还得看具体需求,不能一概而论。

除了代码质量,一个成功的开源项目还需要:清晰的文档、友好的社区氛围、积极的维护者、明确的贡献指南、完善的测试和CI/CD流程、良好的项目推广和宣传。代码写的好是基础,但运营的好才能真正做起来。

eval()这玩意儿啊,能不用就不用!想动态执行代码,可以试试ast.literal_eval,安全系数高一些。实在不行,就用exec(),但一定要做好输入验证和权限控制!万一被注入了恶意代码,那可就惨了。实在担心的可以上沙箱,整个虚拟机跑代码,就是有点重。