Коротко — нужно сохранить пользователя (или его id) в самом модальном окне или во View с кнопками, чтобы потом в callback'ах кнопок к нему обратиться. Ниже — минимальный рабочий пример на disnake, показывающий полный поток:
- пользователь открывает модал;
- в callback модала бот отправляет сообщение для модераторов с кнопками «Одобрить / Отклонить» и в View сохраняет submitter;
- при клике модератора бот отправляет личное сообщение (DM) тому, кто отправил модал.
Пример:
```py
import disnake
from disnake.ext import commands
bot = commands.Bot(command_prefix="!", intents=disnake.Intents.all())
class ApproveRejectView(disnake.ui.View):
def __init__(self, submitter: disnake.User, *, timeout: float = None):
super().__init__(timeout=timeout)
self.submitter = submitter
@disnake.ui.button(label="Одобрить", style=disnake.ButtonStyle.green)
async def approve(self, button: disnake.ui.Button, inter: disnake.MessageInteraction):
# избавляемся от кнопок и редактируем сообщение
await inter.response.edit_message(content=f"✅ Одобрено модератором {inter.author.display_name}", view=None)
# отправляем DM submitter'у (ловим исключение, если DM отключены)
try:
await self.submitter.send("Ваша заявка одобрена ✅")
except Exception:
# по желанию логировать или уведомить модератора, что DM не доставлены
pass
@disnake.ui.button(label="Отклонить", style=disnake.ButtonStyle.red)
async def reject(self, button: disnake.ui.Button, inter: disnake.MessageInteraction):
await inter.response.edit_message(content=f"❌ Отклонено модератором {inter.author.display_name}", view=None)
try:
await self.submitter.send("Ваша заявка отклонена ❌")
except Exception:
pass
class MyModal(disnake.ui.Modal):
def __init__(self, invoker: disnake.User):
# добавляем поля модала (пример одного текстового поля)
components = [
disnake.ui.TextInput(
label="Описание",
custom_id="desc",
style=disnake.TextInputStyle.paragraph,
required=True,
max_length=1000
)
]
super().__init__(title="Заявка", components=components)
# сохраняем пользователя, который открыл модал
self.invoker = invoker
async def callback(self, inter: disnake.ModalInteraction):
# читаем введённый текст
desc = inter.text_values.get("desc") # или inter.text_values["desc"]
# создаём View с кнопками и передаём туда submitter
view = ApproveRejectView(self.invoker)
# отправляем сообщение (например, в тот же канал), указываем ник отправившего
await inter.response.send_message(
f"Новая заявка от {self.invoker.display_name}:\n{desc}",
view=view
)
# команда/слэш-команда, которая открывает модал
@bot.slash_command(description="Открыть форму")
async def send_form(inter: disnake.ApplicationCommandInteraction):
# сохраняем пользователя, который открыл модал (inter.author или inter.user)
invoker = getattr(inter, "author", None) or inter.user
modal = MyModal(invoker)
await inter.response.send_modal(modal)
bot.run("TOKEN")
```
Несколько важных замечаний:
- Важно сохранять именно объект пользователя (disnake.User/Member) или его id в modal/view, чтобы в кнопках иметь к кому писать.
- Если вы хотите, чтобы данные кнопок пережили рестарт бота (persistent views), то храните id пользователя в custom_id кнопки (например, "approve:1234567890") и парсите его в обработчике. Persistent views нельзя хранить Python-объекты в памяти между рестартами.
- Пользователь может запретить сообщения в ЛС — отправка DM может вызвать ошибку. Обязательно ловите исключения.
- В callback'ах interaction.author — это тот, кто нажал кнопку (модератор); interaction.user — тоже можно использовать. В модале используйте inter.user/inter.author для определения, кто его отправил (мы сохраняем этот user при создании модала).
Если надо, могу показать вариант с передачей user.id через custom_id кнопки (для persist-views) или адаптировать пример под вашу текущую структуру команд.