discord.py - 特定角色排行榜前 10 名

discord.py - top 10 specific roles leaderboard

我想做的是建立一个排行榜,其中包含最多角色的人,例如:

member1 - 10
member2 - 7
member3 - 4
...

有效的主要代码是这样的:

data = []
for member in ctx.guild.members:
    name, score = member.name, len(member.roles)
    data.append((name,score))
data.sort(key=lambda t: t[1], reverse=True)
await ctx.send("Leaderboard")
for i, (name, score) in enumerate(data[:10]):
    await ctx.send(f"{i+1}. {name} - {score}")

但是我不想算Member,Admin这个角色。所以我试着做了以下:

@client.command()
async def top(ctx):
  data = []
  mroles = []
  final = []
  banned = ['Admin','Member']
  for member in ctx.guild.members:
    for roles in member.roles:
      name, score = member.name, roles.name
      mroles.append(score)
  
      for roles in mroles:
        if roles in banned:
          continue
        else:
          final.append(roles)
      
      data.append((name,len(final)))
  data.sort(key=lambda t: t[1], reverse=True)
  await ctx.send("Leaderboard")
  for i, (name, score) in enumerate(data[:10]):
      await ctx.send(f"{i+1}. {name} - {score}")

但输出是一些疯狂的值和成员的名字 x2:

 1. testuser - 16671
 2. testuser - 16532
 3. testuser2 - 16393
 4. testuser2 - 16255

有线索吗?我烧了好几个小时,我无法让它发挥作用。

您永远不会重置 mrolesfinal。结合嵌套的 for 循环,这会导致它塞满项目,并随着您的继续而快速增加。

这些名字也出现了多次,因为你的 data.append((name,len(final)))for roles in member.roles 里面 运行 - 这意味着他们将在排行榜中为他们拥有的每个角色获得另一个条目。

这是一个快速的方法:

@client.command()
async def top(ctx):
    data = []
    banned_roles = {'Admin', 'Member'}  # this should be a set, because speed

    for member in ctx.guild.members:
        total_role_count = len(member.roles)
        # Uncomment this line if you don't want the "@everyone" role to be included. Yes, this is an actual role.
        #total_role_count -= 1

        # fast O(n) way to compute # of banned roles
        # it finds the roles that are banned by intersecting the member's roles and the banned roles
        # then take the length of that and subtract it from the total number
        # you could probably also do this using `set.difference`
        role_names = {role.name for role in member.roles}
        banned_count = len(role_names.intersection(banned_roles))

        data.append((member.name, total_role_count - banned_count))

    data.sort(key=lambda t: t[1], reverse=True)
    # instead of sending all the messages, send a big message with newlines in it
    # this avoids the ratelimit of 5 messages every 5 seconds
    text = '\n'.join([f"{i+1}. {name} - {score}" for i, (name, score) in enumerate(data[:10])])
    await ctx.send(f'Leaderboard\n\n{text}')