如何查找用户上次使用 discord.py 处于活动状态的时间?



我正在使用 discord.py 库,并希望通过将非活动成员从服务器中踢出来清除他们。我知道功能不和谐。Guild.prune_members,但是它没有提供我需要的所有功能,因为我想在用户被删除前几天向用户发送警告。

如何最好地查找用户上次处于活动状态的时间,以便计算用户处于非活动状态的天数?我已经在互联网上搜索了几天,但找不到解决方案。

如 discord.py 服务器中所述,您可以在数据库旁边使用简单的缓存来跟踪这一点。让我们通过一个简单的例子来说明我们如何做到这一点。

from typing import TYPE_CHECKING, Dict, List
import discord
from discord.ext import commands
if TYPE_CHECKING:
import datetime
import asyncpg

# We set the type checking here to avoid errors, your bot instance
# shouldnt run into this issue and this shouldnt be needed.
class Bot(commands.Bot):
pool: asyncpg.Pool[asyncpg.Record]
else:
Bot = commands.Bot

# We're going to layout our data as so:
# CREATE TABLE prune (member_id BIGINT, guild_id BIGINT, last_login TIMESTAMP WITH TIMEZONE)
class Tracker(commands.Cog):
def __init__(self, bot: Bot) -> None:
self.bot: Bot = bot

self.presence_cache: Dict[int, Dict[int, datetime.datetime]] = {} # mapping of guild : {member_id: last_login}

async def cog_load(self) -> None:
# Let's load the info from our database table into our cache.
# This implements a postgres asyncpg pool
data = await self.bot.pool.fetch('SELECT * FROM prune') # type: ignore
for entry in data:
entry: asyncpg.Record

# Enter in the guild id and the member id
if (guild_id := entry['guild_id']) not in self.presence_cache:
self.presence_cache[guild_id] = {}
self.presence_cache[guild_id][entry['member_id']] = entry['last_login']

async def cog_unload(self) -> None:
# We need to dump back into our database here, so we can save our data.
async with self.bot.pool.acquire() as connection:
async with connection.transaction():
query = 'INSERT INTO prune (member_id, guild_id, last_login) VALUES ($1, $2, $3) ON CONFLICT (member_id, guild_id) DO UPDATE SET last_login = $3'

# Let's format our data so we can use executemany
# The formatted data should be a list of tuples, (member_id, guild_id, last_login)
formatted_data = [(member_id, guild_id, last_login) for guild_id, guild_data in self.presence_cache.items() for member_id, last_login in guild_data.items()]
await connection.executemany(query, formatted_data)

@commands.Cog.listener('on_presence_update')
async def on_presence_update(self, before: discord.Member, after: discord.Member) -> None:
if before.status == after.status: # The two statuses are the same, return
return

if not (before.status is discord.Status.offline and after.status is discord.Status.online): # The member did NOT come onine
return

# This means the member's last login was just now, let's add it to our cache to update
if (guild_id := after.guild.id) not in self.presence_cache:
self.presence_cache[guild_id] = {}

self.presence_cache[guild_id][after.id] = discord.utils.utcnow() # utc now (dpy is timezone aware in 2.0)

@commands.command(
name='get_prune',
brief='Get the members who will be pruned',
description='Get the members who will be pruned',
)
@commands.guild_only()
async def get_prune(self, ctx: commands.Context[Bot], days: int) -> None:
assert ctx.guild is not None

# Let's find the members who would be pruned
query = 'SELECT member_id FROM prune WHERE guild_id = $1 AND last_login < $2'
data = await self.bot.pool.fetch(query, ctx.guild.id, (discord.utils.utcnow() - datetime.timedelta(days=days)).replace(tzinfo=None)) 

# Now let's format an embed
embed = discord.Embed(
title=f'{len(data)} members will be pruned',
)

members: List[discord.Member] = []
for entry in data:
member = ctx.guild.get_member(entry['member_id']) or (await ctx.guild.fetch_member(entry['member_id']))
members.append(member)

embed.description = ', '.join(member.mention for member in members)
await ctx.send(embed=embed, allowed_mentions=discord.AllowedMentions.none())

在此示例中,我们使用异步cog_loadcog_unload函数使缓存保持最新。为了附加到我们的缓存中,我们使用了on_presence_update来检查成员何时联机。我还实现了一个简单的命令来获取将在 X 天后修剪的成员。可以这样调用它:

[pr]get_prune 20

如果您有任何疑问,请随时在此线程上联系:)

最新更新