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

discord.py开发Discord机器人报datetime对象不可调用错误

问题根因
  • 直接触发TypeError: 'datetime.datetime' object is not callable的原因:代码中先执行timenow = datetime.now(timezone.utc),已经把timenow赋值为具体的datetime时间对象(不是可调用的方法/函数),下一行又写timenow()试图把它当函数调用,必然抛出不可调用错误。
  • 之前出现的can’t compare offset-naive and offset-aware datetimes时区错误:是因为混用了带时区标识、不带时区标识的两类datetime对象,同时混用pytz和Python标准库datetime.timezone两套时区处理逻辑导致的。实际上discord.py返回的member.joined_at本身就是UTC时区的带标识时间对象,不需要额外做时区转换。
  • 现有逻辑存在部署级隐患:用asyncio.sleep(604800)硬等7天的写法在Heroku平台完全不可靠,Heroku免费/基础版dyno每天至少重启1次,所有等待中的异步任务会直接丢失,到期不会触发踢人逻辑。
修复方案

1. 修正基础语法与时区逻辑

先清理重复、冲突的导入,修正时间计算的写法:

import discord
import asyncio
from discord.ext import commands
from datetime import datetime, timedelta, timezone

# 删除冗余的import datetime、pytz相关导入,避免命名冲突
TOKEN = "*something*"
intents = discord.Intents.all()
intents.members = True
bot = commands.Bot(command_prefix='.', intents=intents)
bot.remove_command('help')

修正on_member_update事件中的错误逻辑:

@bot.event
async def on_member_update(before, after):
    if len(before.roles) < len(after.roles):
        new_role = next(role for role in after.roles if role not in before.roles)
        guild = bot.get_guild(*something*)
        if '.• 미인증 유저' in new_role.name:
            unverified_role = discord.utils.get(guild.roles, name=".• 미인증 유저")
            await asyncio.sleep(604800) # 等待7天
            # 直接计算阈值时间,不要把时间对象赋值后再当函数调用
            kick_threshold = datetime.now(timezone.utc) - timedelta(weeks=1)
            log_channel = bot.get_channel(*something*)
            # 重新拉取最新成员信息,避免缓存数据不准
            latest_member = await guild.fetch_member(after.id)
            # 直接用自带UTC时区的joined_at比较,不需要额外转时区
            if unverified_role in latest_member.roles and latest_member.joined_at < kick_threshold:
                await latest_member.kick(reason="일주일동안 인증하지 않으셨습니다.")
                
                embed = discord.Embed(
                    title="강퇴",
                    colour=0xB11B1B,
                    timestamp=datetime.now(timezone.utc)
                )
                # discord.py 2.0+版本用avatar.url,1.x版本改回avatar_url即可
                embed.set_thumbnail(url=latest_member.avatar.url)
                fields = [
                    ("이름", f"{latest_member.name}", False),
                    ("사유", f"7일간 미인증으로 머물러 강퇴당함.", False)
                ]
                for name, value, inline in fields:
                    embed.add_field(name=name, value=value, inline=inline)
                await log_channel.send(embed=embed)

2. 补全部署可靠性逻辑

增加机器人启动后的轮询检查任务,解决Heroku重启导致任务丢失的问题:

@bot.event
async def on_ready():
    bot.loop.create_task(unverified_user_check_loop())
    print(f"Bot online: {bot.user}")

async def unverified_user_check_loop():
    await bot.wait_until_ready()
    guild = bot.get_guild(*something*)
    unverified_role = discord.utils.get(guild.roles, name=".• 미인증 유저")
    log_channel = bot.get_channel(*something*)
    while not bot.is_closed():
        kick_threshold = datetime.now(timezone.utc) - timedelta(weeks=1)
        # 遍历所有持有未认证角色的用户,补踢重启期间到期的用户
        for member in unverified_role.members:
            if member.joined_at < kick_threshold:
                await member.kick(reason="일주일동안 인증하지 않으셨습니다.")
                embed = discord.Embed(
                    title="강퇴",
                    colour=0xB11B1B,
                    timestamp=datetime.now(timezone.utc)
                )
                embed.set_thumbnail(url=member.avatar.url)
                fields = [
                    ("이름", f"{member.name}", False),
                    ("사유", f"7일간 미인증으로 머물러 강퇴당함.", False)
                ]
                for name, value, inline in fields:
                    embed.add_field(name=name, value=value, inline=inline)
                await log_channel.send(embed=embed)
        # 每1小时扫描一次,避免请求超限
        await asyncio.sleep(3600)
关键注意点
  • 不要混用两套时区处理逻辑,discord.py返回的所有时间对象默认都是UTC带时区标识的,直接和datetime.now(timezone.utc)比较即可,不需要额外转换。
  • 长周期的asyncio.sleep不要作为唯一的定时触发逻辑,平台重启、网络波动都会导致等待中断,必须搭配轮询任务兜底。
  • 注意discord.py版本适配:1.x版本用avatar_url,2.0+版本用avatar.url,否则会抛出属性不存在的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 23:36:07