【问题标题】:discord.py Voice leaderboarddiscord.py 语音排行榜
【发布时间】:2021-08-18 20:42:40
【问题描述】:

我已经编写了一段时间的不和谐机器人,我想做一个语音活动跟踪。

问题是,网上几乎没有这方面的文档。我使用this 作为基础,但我更改了很多代码以使其成为“每个服务器”。我至少有 2 个问题。

第一个是因为日期时间,例如,如果用户在 11:45 加入语音频道并在 00:45 离开,结果将是

"guild_id": {
    "user_id": "-1 day, ..."
}

因为运算是 11 - (~25)。

我的第二个问题也是因为日期时间,

如果用户在语音中的时间超过 24 小时,我会收到这种错误:

click to see the error

这是我的代码:

    @commands.Cog.listener()
    async def on_voice_state_update(self, member, before, after):
        with open('data/voice_leaderboard.json', 'r') as file:
            voice_data = json.load(file)
            new_user = str(member.id)
            guild_id = str(member.guild.id)
            
        # Update existing user
        if new_user in voice_data[guild_id]:
            voice_leave_time = datetime.datetime.now().time().strftime('%H:%M:%S')
            voice_join_time = voice_data[guild_id][new_user]

            calculate_time = (datetime.datetime.strptime(voice_leave_time, '%H:%M:%S') - datetime.datetime.strptime(voice_join_time, '%H:%M:%S'))

            voice_data[guild_id][new_user] = str(calculate_time)

            with open('data/voice_leaderboard.json', 'w') as update_user_data:
                json.dump(voice_data, update_user_data, indent=4)

        # Add new user
        else:
            if member.bot:
                return
            else:
                new_voice_join_time = datetime.datetime.utcnow().strftime('%H:%M:%S')
                voice_data[guild_id][new_user] = new_voice_join_time

            with open('data/voice_leaderboard.json', 'w') as new_user_data:
                json.dump(voice_data, new_user_data, indent=4)

这是 json 文件的一部分:

{
    "749948728248631297": {
        "437265873401544705": "0:06:49"
    },

我不知道如何通过使用 discord.js 来解决这两个问题,但这不是我想要的,所以如果有人知道我该怎么做,请帮助我

【问题讨论】:

  • 请将您的问题限制在每个 How to Ask 的单一、可回答的查询中。提出两个或更多不同问题的问题被认为过于宽泛,不适合 Stack Overflow Q&A 格式。

标签: python discord discord.py


【解决方案1】:

这两个问题都发生了,因为您将时间数据存储为仅时间,不包括日期。

包含日期可以解决第一个问题,因为它现在知道两个时间戳都在不同的日期。此外,第二个问题将得到修复,因为时间戳不会超过 24 小时,而是增加了一天。

%d/%m/%Y 添加到您的所有strptimes 和strftimes 应该可以解决问题。

这样做,您的代码将如下所示:

dateFormat = "%d/%m/%Y %H:%M:%S"
    @commands.Cog.listener()
    async def on_voice_state_update(self, member, before, after):
        with open('data/voice_leaderboard.json', 'r') as file:
            voice_data = json.load(file)
            new_user = str(member.id)
            guild_id = str(member.guild.id)
            
        # Update existing user
        if new_user in voice_data[guild_id]:
            voice_leave_time = datetime.datetime.time().strftime(dateFormat)
            voice_join_time = voice_data[guild_id][new_user]

            calculate_time = (datetime.datetime.strptime(voice_leave_time, dateFormat) - datetime.datetime.strptime(voice_join_time, dateFormat))

            voice_data[guild_id][new_user] = str(calculate_time)

            with open('data/voice_leaderboard.json', 'w') as update_user_data:
                json.dump(voice_data, update_user_data, indent=4)

        # Add new user
        else:
            if member.bot:
                return
            else:
                new_voice_join_time = datetime.datetime.utcnow().strftime(dateFormat)
                voice_data[guild_id][new_user] = new_voice_join_time

            with open('data/voice_leaderboard.json', 'w') as new_user_data:
                json.dump(voice_data, new_user_data, indent=4)

【讨论】:

  • 您的代码运行良好,但只有 1 次,如果我加入声乐超过 1 次,我会收到错误。 ValueError: time data '30:00:08' does not match format '%d/%m/%Y %H:%M:%S'
  • data/voice_leaderboard.json 中有旧数据吗?这可能会导致问题
  • 不,我删除了所有旧数据
【解决方案2】:

您的两个问题都源于您以小时:分钟:秒格式存储时间,这在您的用例中完全没有必要,因为您只需要相对时间。为了解决您的问题,您可以只使用 time.time() 来给出自纪元以来的时间(也许 round() 让它看起来更好看)。当用户加入 vc 时,您记录加入时间.time(),当他们离开时,您记录离开时间.time()。这两者之间的差异是他们在vc中的时间,您可以将该差异转换为few different ways中的可读格式。

除此之外,您的程序似乎还有一些基本问题,例如:

  • 只有在他们第一次加入 vc 时才会创建一个新的用户密钥(在您的 dict 中),因为您可以保存他们离开后在该密钥下在 vc 中花费的时间(这会导致其他答案)。当您制作排行榜时,我假设您想保留在 vc 中花费的总时间,所以要解决它有两种选择:

    • 使用户ID键保存一个字典,至少有两个键(加入时间和总时间),当用户离开频道时,您检查加入时间voice_data[guild_id][user]['jointime']并将他们在vc中的时间添加到@987654325 @
    • 制作两个字典,一个用于保存他们在 vc 中的总时间,另一个用于保存加入时间,这样做是可能的,但我不建议这样做,因为它会让你做更多的文件比选项 1 的操作。
  • 这增加了问题1,你不检查voice_channel事件的类型,并且根据the docs事件被触发:一个成员加入一个vc,一个成员离开一个vc,一个成员被静音或耳聋他们自己的意愿,一个成员被公会管理员静音或耳聋,等等。当你每次触发代码时都运行代码,这将使你的代码完全混乱,如果不是“用户加入一个 vc”“用户离开那个vc' 发生(例如从该 vc 加入同一服务器中的不同 vc 或聋/静音),您应该应用一些逻辑来检查 beforeafter (事件给你)。它们是VoiceStates,您可以查看该类的通道属性以检查在执行时间函数之前究竟发生了什么。 (您还必须将 channelid 保存在我在问题 1 中建议的字典中。

我知道你并没有要求我编写你的代码,但我只是编写它比给出如何做的提示更容易,抱歉。请注意,我完全是即时编写的,因此您可能会遇到语法错误或我没想到的错误。

    @commands.Cog.listener()
async def on_voice_state_update(self, member, before, after):
    if member.bot: #checking this before anything else will reduce unneeded file operations etc
        return
    with open('data/voice_leaderboard.json', 'r') as file: 
        voice_data = json.load(file)
    new_user = str(member.id)
    guild_id = str(member.guild.id)
    if new_user not in voice_data[guild_id]: #this adds a new user to the guild dict if they're not in it yet
        voice_data[guild_id][new_user] = {
            "total_time" : 0,
            "join_time" : None} 
    userdata = voice_data[guild_id][new_user] #this is to make the next code clearer, adding things to this dict also adds them to the voice_data dict, it just make the code "cleaner"

    #after making sure the user exists you gotta check if they're joining or leaving a vc(and reject all the other options), plus if they change vc within the same guild it should keep counting. There's multiple ways to do this
    if(before.channel == None): #this is when they join a vc (they werent in one before so they gotta have just joined one)
        join_time = round(time.time())
        user_data["join_time"] = join_time
    elif(before.channel.guild == after.channel.guild): #wrote this to only check if they changed vc within the same guild, but then I realised it can also catch all the mute/deafen events yay.
        break
    elif(str(after.channel.guild.id) != guild_id): #this will check if the channel they're in after the event (we wanna record the time passed if its None or a different guild, both of which will get triggered by this)
        if(userdata["join_time"] == None): break #this will catch errors, if they were to happen
        leave_time = time.time()
        passed_time = leave_time - userdata["join_time"]
        userdata[total_time] += passed_time
        userdata["join_time"] = None #preventive measure against errors
    with open('data/voice_leaderboard.json', 'w') as update_user_data:
        json.dump(voice_data, update_user_data, indent=4)
      

最后一点,文件操作是阻塞的,如果你要做很多,你也应该考虑让它们异步(我通过不使用我的机器人进行文件操作来解决这个问题,使 dicts它是全局的,仅在退出时保存)

【讨论】:

  • 您好!我理解你的回答,但我很确定我一个人做不到。我不想让你帮我做,但我想要更多的帮助
  • 嗨,我在答案中添加了一些代码(我刚刚做到了,抱歉,如果您只需要一些提示)
  • 断开连接时出现最后一个错误:elif(before.channel.guild == after.channel.guild): AttributeError: 'NoneType' object has no attribute 'guild'
猜你喜欢
  • 1970-01-01
  • 2021-10-07
  • 2021-09-14
  • 2021-04-20
  • 1970-01-01
  • 2022-06-13
  • 2020-12-25
  • 2020-09-10
  • 2021-05-14
相关资源
最近更新 更多