You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何同时运行discord.py机器人与Flask应用并共享Users列表?

解决discord.py机器人与Flask应用同时运行及数据共享问题

核心问题分析

  1. 无法同时运行:app.run()和bot.run()都是阻塞式调用,会独占主线程,导致另一个服务无法启动。
  2. 数据共享不可靠:使用globals()存储用户数据,在多线程环境下存在线程安全问题,且代码可维护性差。

解决方案

一、让Flask与discord.py同时运行

将其中一个服务放到子线程中执行,避免阻塞主线程。推荐把Flask放到线程,discord.py在主线程运行(discord.py的事件循环更适合主线程)。

修改步骤:

  1. 导入threading模块。
  2. 封装Flask启动逻辑到函数中,关闭自动重载(避免重复启动)。
  3. 启动Flask线程后,再启动discord.py。
import threading

# ... 其他原有代码 ...

def run_flask():
    # 关闭use_reloader,防止Flask自动重载时重复创建线程
    app.run(port=80, host="127.0.0.1", use_reloader=False)

# 启动Flask子线程
flask_thread = threading.Thread(target=run_flask)
flask_thread.start()

# 启动discord.py(主线程阻塞)
bot.run("你的Bot Token")

二、安全共享用户数据

放弃globals(),改用线程安全的内存存储或数据库实现数据共享。

方案1:线程安全的内存字典(适合小项目/测试)

用threading.Lock保护全局字典,确保多线程下读写安全。

  1. 定义全局存储和锁:
import threading

# 键:用户Discord ID,值:用户对象
user_store = {}
# 线程锁,保证读写操作互斥
user_lock = threading.Lock()
  1. 修改on_ready事件初始化用户数据:
@bot.event
async def on_ready():
    server = bot.guilds[0]
    with user_lock:
        for member in server.members:
            if member.id not in user_store:
                user_store[member.id] = p(member.name, member.id)
    print("Bot is Online !")
  1. 修改机器人命令:
@bot.command()
async def get_data(ctx):
    user_id = ctx.author.id
    with user_lock:
        usr = user_store.get(user_id)
    if usr:
        await ctx.send(f"age = {usr.age}")
    else:
        await ctx.send("未找到你的数据")

@bot.command()
async def edit_data(ctx, arg1):
    try:
        new_age = int(arg1)
    except ValueError:
        await ctx.send("请输入有效数字")
        return
    user_id = ctx.author.id
    with user_lock:
        usr = user_store.get(user_id)
        if usr:
            usr.age = new_age
            await ctx.send(f"age 更新为 {usr.age}")
        else:
            await ctx.send("未找到你的数据")
  1. 修改Flask路由:
@app.route("/me/")
@requires_authorization
def me():
    user = disc.fetch_user()
    with user_lock:
        usr = user_store.get(user.id)
    age = usr.age if usr else "未找到数据"
    return f"""
    <html>
        <head>
            <title>{user.name}</title>
        </head>
        <body>
            <img src='{user.avatar_url}' />
            <p>用户年龄:{age}</p>
        </body>
    </html>"""

方案2:使用SQLite数据库(适合长期/持久化需求)

用数据库存储数据,天然支持多线程共享,且数据不会随程序重启丢失。

  1. 初始化数据库:
import sqlite3

def init_database():
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()
    # 创建用户表,默认年龄17
    cursor.execute('''CREATE TABLE IF NOT EXISTS users
                      (id INTEGER PRIMARY KEY, name TEXT, age INTEGER DEFAULT 17)''')
    conn.commit()
    conn.close()

# 启动前初始化数据库
init_database()
  1. 修改on_ready事件导入用户:
@bot.event
async def on_ready():
    server = bot.guilds[0]
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()
    for member in server.members:
        # 不存在则插入,避免重复
        cursor.execute("INSERT OR IGNORE INTO users (id, name) VALUES (?, ?)", 
                      (member.id, member.name))
    conn.commit()
    conn.close()
    print("Bot is Online !")
  1. 修改机器人命令:
@bot.command()
async def get_data(ctx):
    user_id = ctx.author.id
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()
    cursor.execute("SELECT age FROM users WHERE id = ?", (user_id,))
    result = cursor.fetchone()
    conn.close()
    if result:
        await ctx.send(f"age = {result[0]}")
    else:
        await ctx.send("未找到你的数据")

@bot.command()
async def edit_data(ctx, arg1):
    try:
        new_age = int(arg1)
    except ValueError:
        await ctx.send("请输入有效数字")
        return
    user_id = ctx.author.id
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()
    cursor.execute("UPDATE users SET age = ? WHERE id = ?", (new_age, user_id))
    conn.commit()
    conn.close()
    await ctx.send(f"age 更新为 {new_age}")
  1. 修改Flask路由:
@app.route("/me/")
@requires_authorization
def me():
    user = disc.fetch_user()
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()
    cursor.execute("SELECT age FROM users WHERE id = ?", (user.id,))
    result = cursor.fetchone()
    conn.close()
    age = result[0] if result else "未找到数据"
    return f"""
    <html>
        <head>
            <title>{user.name}</title>
        </head>
        <body>
            <img src='{user.avatar_url}' />
            <p>用户年龄:{age}</p>
        </body>
    </html>"""

注意事项

  • 确保Discord开发者后台的重定向URI与Flask配置的DISCORD_REDIRECT_URI完全一致(包括http/https)。
  • 测试时os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "true"仅用于本地开发,生产环境需使用HTTPS。
  • 如果使用线程方案,避免在Flask中直接调用discord.py的异步方法,如需调用需使用bot.loop.run_until_complete()。

内容的提问来源于stack exchange,提问作者Joseph Yosoevsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 16:36:06