Массовая выдача наград через CSV
Когда наградить нужно тысячи игроков разом — компенсация за баг, выплата по инциденту — награды загружаются файлом из ACP: Почта → Массовая загрузка. Панель разбирает файл на глазах у оператора, показывает построчный разбор до отправки и отчёт после.
Формат файла
Первая строка — заголовок с названиями колонок. Порядок любой, колонки, которых импорт не знает, просто игнорируются: выгрузка из аналитики вместе со всей рабочей арифметикой загружается как есть.
api_uid,api_type,award:soft_currency,award:hard_currency,text:en,text:ru
t2_klwd697t,39,24000,5,Sorry about that,Извините
t2_mm1a2b3c,39,22000,0,Sorry about that,Извините| Колонка | Обязательна | Что значит |
|---|---|---|
api_uid | да | UID получателя на платформе |
api_type | нет | платформа строки; иначе берётся выбранная в форме |
award:<ключ> | для режима «у каждого своя» | сколько этой награды выдать строке |
text:<язык> | нет | текст сообщения на этом языке |
<ключ> — ключ награды проекта (soft_currency, hard_currency), <язык> — язык проекта (en, ru). Колонок award: и text: может быть сколько угодно — по одной на награду и на язык, поэтому строка не ограничена одной наградой и одним переводом. Ноль или пустая ячейка в award: означают «эту награду этой строке не выдаём».
Колонка award: или text:, называющая награду или язык, которых у проекта нет, пропускается, и панель говорит об этом один раз по заголовку, а не тысячей одинаковых отказов по строкам.
Пустая ячейка api_type, ноль и отсутствие колонки означают одно и то же — «платформа не указана», и берётся выбранная в форме. Ноль читается именно так, а не как платформа №0, на которой нет ни одного аккаунта. Поле «Платформа по умолчанию» в форме обязательно ровно тогда, когда хоть одна строка платформу не называет; если её называет каждая — поле не используется.
Текст задаётся колонками text:<язык>. Общий текст в форме предлагается только в режиме «одна награда на всех» — там же, где и общая награда; в построчном режиме его нет, чтобы не было двух источников одного и того же. Строка со своим текстом перекрывает общий, строка без текста в построчном режиме уходит с одной наградой.
Колонки определяются только по заголовку — сопоставлять их в панели вручную не нужно и нельзя: в award: и text: заголовок и есть ответ, а для получателя понимаются, кроме api_uid и api_type, и обиходные имена (uid, user_id, player_id, platform).
Разделитель определяется сам (,, ;, табуляция, |) по строке заголовка. Кавычки, кавычки внутри кавычек и переводы строк внутри кавычек читаются по правилам CSV, BOM от Excel отбрасывается. Суммы принимаются в том виде, в каком их пишет таблица: 24000, 24 000, 24 000 с неразрывным пробелом, 1,5.
Всё это есть и в самом диалоге — блок «Как подготовить файл» над зоной загрузки, с примером на наградах и языках конкретного проекта.
Тип сообщения у массовой выдачи всегда «прямая награда игроку» — выбора нет: массово рассылают именно её.
Два режима
- Одна награда на всех — награда и текст задаются в форме, колонки
award:не нужны, от файла нужен только список получателей. Колонкиtext:при этом работают: одинаковая награда не значит одинаковую формулировку, и строка со своим текстом перекрывает общий. - У каждого своя — награды берутся из колонок
award:построчно. Строка, которая по итогу не выдаёт ничего (пусто или везде ноль), не отправляется: это не компенсация.
Что не отправится
Строка не выбрасывается молча — она остаётся в таблице с причиной: нет UID, платформа не число, нет награды, награда не читается, дубликат. Дубликатом считается повтор пары платформа+UID; побеждает первая строка.
Получатели, за которыми нет аккаунта, не создаются — они попадают в отчёт как no_account. Файл с ошибочной платформой иначе завёл бы тысячи пустых аккаунтов, которые потом кому-то разгребать.
Ограничения
За один импорт — не больше 10 000 наград. Панель режет файл на запросы по 500 строк и шлёт их последовательно, показывая прогресс.
Повторная отправка ничего не оплачивает дважды
Каждый импорт получает batch_key — имя одного решения оператора. Все запросы одного файла несут его, а база отказывается записать второе сообщение тому же получателю под тем же ключом.
Отсюда следует главное: повторить импорт всегда безопасно. Оборвалось соединение на середине, оператор нажал дважды, перезагрузил страницу и положил тот же файл снова — отправится только то, что не дошло. Разные импорты — разные ключи, поэтому наградить тех же игроков ещё раз в следующем месяце можно обычным способом.
Отчёт
После импорта показываются счётчики — отправлено, уже было отправлено, аккаунт не найден, пропущено в файле — и отчёт выгружается в CSV: каждая строка исходного файла с тем, что с ней стало, адресованная так же, как в исходнике, чтобы строку можно было найти в своей таблице.
API
POST /acp/messages/bulk-create — один чанк импорта. Требует права AccessMessage.
{
"batch_key": "mail-20260923T120000-a1b2c3",
"type": 2,
"api_type": 39,
"award": { "soft_currency": 5000 },
"description": [{ "lang": "en", "text": "Sorry about that" }],
"attributes": {},
"expired_at": "2026-10-23T08:00:00.000Z",
"entries": [
{
"api_uid": "t2_klwd697t",
"award": { "soft_currency": 24000, "hard_currency": 5 },
"description": [{ "lang": "ru", "text": "Извините" }]
},
{ "api_uid": "t2_mm1a2b3c", "award": { "soft_currency": 22000 } }
]
}award и description на верхнем уровне применяются ко всем; те же поля внутри entries перекрывают их для конкретного получателя. api_type внутри записи перекрывает верхний. batch_key обязателен, в одном запросе не больше 1000 записей.
Автор сообщения проставляется сервером по авторизованному оператору — в теле запроса его задать нельзя.
Ответ — отчёт по этому чанку:
{
"report": {
"requested": 2,
"sent": 2,
"already_sent": 0,
"no_account": 0,
"rows": [
{ "api_type": 39, "api_uid": "t2_klwd697t", "outcome": "sent" },
{ "api_type": 39, "api_uid": "t2_mm1a2b3c", "outcome": "sent" }
]
}
}outcome принимает значения sent, already_sent (получатель уже получил сообщение под этим batch_key) и no_account.
