Python开发Discord机器人:如何实现MySQL数据增查并发送至频道
现有代码核心问题
- 数据库查询逻辑在全局作用域执行,机器人启动时就跑完所有查询,
xp变量最终只会存储查询结果的最后一行,完全脱离了Discord指令的触发时机 - 查询结果没有主动提取,直接通过游标遍历给变量赋值,变量作用域混乱,在指令判断的代码块里拿不到实时查询的完整结果
- 没有按异步逻辑的时序执行查询,在机器人初始化阶段查完数据库,等到用户发指令时才准备发Embed,早已经过了查询的时间点,自然拿不到可以推送的有效数据
- pymysql默认不自动提交事务,哪怕后续写了插入逻辑,不手动执行commit操作,数据根本不会写入数据库
修复后可正常推送查询结果的实现
把数据库查询逻辑封装为独立函数,每次触发Discord指令时实时拉取本地数据库的数据,再把格式化后的内容推送到目标频道:
import discord import pymysql # Discord机器人基础配置,注意必须开启message_content权限才能读取用户发的指令 intents = discord.Intents.default() intents.message_content = True client = discord.Client(intents=intents) # 封装查询数据库的独立函数 def fetch_all_dis_id(): # 每次查询新建独立连接,避免长时间运行后数据库连接失效 conn = pymysql.connect( host='localhost', port=3306, user='root', passwd='', db='xp', charset='utf8mb4' ) cur = conn.cursor() try: cur.execute("SELECT dis_id FROM users") # 主动提取所有查询结果,不要直接遍历游标对象 rows = cur.fetchall() # 把查询结果处理为Discord可正常显示的文本格式 return [f"用户ID条目{idx+1}:{row[0]}" for idx, row in enumerate(rows)] finally: # 无论查询成功失败都关闭游标和连接,避免连接泄漏 cur.close() conn.close() @client.event async def on_ready(): print(f"机器人已登录:{client.user}") @client.event async def on_message(message): # 忽略机器人自身发送的消息,避免循环触发 if message.author == client.user: return # 匹配+xp指令触发逻辑 if message.content.startswith('+xp'): # 触发指令时才实时执行数据库查询 dis_id_content = "\n".join(fetch_all_dis_id()) mbed = discord.Embed( title="Bot Information", description=dis_id_content if dis_id_content else "数据库暂无可查询的dis_id记录", colour=discord.Colour.green() ) channel = client.get_channel(828248385369669642) await channel.send(embed=mbed)
MySQL数据插入实现方法
插入逻辑同样使用参数化查询避免SQL注入,必须手动执行commit提交事务,否则写入的临时数据不会真正落库:
def insert_new_dis_id(target_dis_id): conn = pymysql.connect( host='localhost', port=3306, user='root', passwd='', db='xp', charset='utf8mb4' ) cur = conn.cursor() try: # 使用参数化占位符,不要直接拼接SQL字符串 sql = "INSERT INTO users (dis_id) VALUES (%s)" # 执行语句时把传入的ID作为参数传入,和SQL语句的占位符一一对应 affect_rows = cur.execute(sql, (target_dis_id,)) # 必须提交事务,否则数据不会真正写入数据库 conn.commit() return affect_rows > 0 except Exception as e: # 出现异常时回滚,避免写入无效的脏数据 conn.rollback() print(f"插入操作失败:{e}") return False finally: cur.close() conn.close()
常见踩坑提示
- 不要把数据库连接设置成全局长连接,本地小项目每次操作新建一次连接的稳定性远高于一直挂着的长连接,不会因为数据库超时断开报错
- 直接把整个游标遍历的结果塞到Embed里,会因为游标对象的内存地址不是字符串,导致Discord显示的内容是乱码
- 如果推送时报权限错误,检查机器人在目标频道的发言权限、Embed链接权限,以及开发者后台是否开启了对应的Message Content Intent权限
内容的提问来源于stack exchange,提问作者mujix
相关产品推荐
相关产品推荐

