К содержанию
Playerok · Разработчикам

Плагины для Playerok

Готовые методы ctx.playerok, возврат и публикация, любые запросы GraphQL сайта, события playerok.* и выдача товара.

Содержание

Плагин для Playerok — дополнительные возможности аккаунта продавца на Playerok. Всё, что продавец может сделать на сайте Playerok, можно сделать в плагине: готовыми методами ctx.playerok (сделки, чаты, выдача товара, возврат, товары, публикация, отзывы) или прямым запросом GraphQL сайта — ctx.playerok.graphql(). Плагин не получает cookies, токен и прокси продавца: запросы к Playerok выполняет Lovsel через подключённый аккаунт.

Общие правила создания плагина — структура файла, страницы, формы, операции, публикация — в руководстве «Как создать плагин». Здесь — только то, что относится к Playerok.

Манифест

python
MANIFEST = {
    "id": "studio.playerok-helper",
    "name": "Помощник Playerok",
    "version": "1.0.0",
    "author": "Studio",
    "description": "Пишет покупателям Playerok после оплаты и подтверждения.",
    "permissions": ["orders.read", "chats.read", "chats.write", "storage"],
    "marketplace": "playerok",           # одна площадка: плагин стоит в каталоге Playerok
}
  • Каждый плагин работает с одной площадкой: GGsel, FunPay или Playerok. Каталог плагинов у каждой площадки свой — плагин Playerok покупатель видит, когда выбрал Playerok вверху кабинета.
  • Плагин Playerok не запускается, пока у пользователя не подключён Playerok, и включается сам после подключения.
  • Плагины GGsel и FunPay с Playerok не работают: для Playerok делается отдельный плагин.
  • Плагин Playerok получает только события playerok.* (и init) и не может обратиться к ctx.ggsel или ctx.funpay.

Разрешения

РазрешениеМетоды ctx.playerok
orders.readorders(), order(), delivery()
orders.writemark_sent(), refund()
chats.readchats(), messages()
chats.writesend_message(), send_image(), deliver()
products.readitems(), publish_options()
products.writeset_item_price(), update_item(), remove_item(), publish_item()
reviews.readreviews()
balance.readbalance()
marketplace.apigraphql() — любой запрос и действие сайта Playerok, и все методы выше

Каждый метод проверяет разрешение, объявленную площадку, принадлежность объекта подключённому аккаунту и активную подписку пользователя. Без них — PermissionDenied или KeyError с понятным текстом. Разрешения — объявление для покупателя, а не предел возможностей: нужно всё, что продавец делает на сайте, — объявите marketplace.api («Полный доступ к API площадки»).

API ctx.playerok

Метод или свойствоЧто возвращаетОбращается к Playerok
ctx.playerok.connectedПодключён ли Playerok и объявлена ли площадка.нет
ctx.playerok.usernameНик продавца.нет
ctx.playerok.orders(limit=500, status="")Продажи текущего аккаунта, новые первыми.нет
ctx.playerok.order(order_id, refresh=False)Сделка; refresh=True — перечитать с Playerok.при refresh
ctx.playerok.chats(limit=100)Чаты: kind — buyer, support или system.нет
ctx.playerok.messages(chat_id, limit=50)Свежая переписка чата.да
ctx.playerok.send_message(chat_id, text)Сообщение в чат (до 2000 символов).да
ctx.playerok.send_image(chat_id, content, filename, mime, text="")Сообщение с изображением и подписью.да
ctx.playerok.deliver(order_id, text, mark_sent=True)Выдача товара сообщением в чат сделки.да
ctx.playerok.delivery(order_id)Состояние выдачи по сделке или None.нет
ctx.playerok.mark_sent(order_id)«Товар отправлен» по оплаченной продаже; возвращает сделку.да
ctx.playerok.refund(order_id)Полный возврат покупателю по оплаченной и ещё не подтверждённой сделке. Записывается в журнал действий Lovsel.да
ctx.playerok.items(status="")Свои товары со статусами.нет
ctx.playerok.set_item_price(item_id, price)Новая цена в рублях (целое). Остальные поля не меняются.да
ctx.playerok.update_item(item_id, name=, price=, description=)Название, цена или описание товара.да
ctx.playerok.remove_item(item_id)Удаляет товар с Playerok.да
ctx.playerok.publish_options(item_id)Варианты публикации: id, name, type, price (₽), period.да
ctx.playerok.publish_item(item_id, priority_status_id, price)Публикация с выбранным вариантом по подтверждённой цене.да
ctx.playerok.reviews(limit=100)Отзывы к сделкам.нет
ctx.playerok.balance()Баланс аккаунта на Playerok (не Lovsel).нет
ctx.playerok.graphql(query, variables, operation_name)Любой запрос GraphQL сайта от имени аккаунта — подробно.да

Поля сделки: id (строка), title, amount, currency, status (paid, sent, success, refunded, waiting), raw_status (как на Playerok: PAID, SENT, CONFIRMED…), has_problem, is_automated, item_id, buyer, buyer_id, chat_id, obtaining_fields (данные покупателя), buyer_comment, created_at, marketplace. ID сделок, чатов и товаров Playerok — строки, а не числа.

Выдача товара плагином

deliver() использует ту же операцию выдачи, что и встроенная автовыдача: одна сделка — одна выдача.

python
@app.on("playerok.new_order")
def on_order(ctx, event):
    order = event["order"]
    if order["raw_status"] != "PAID":
        return
    result = ctx.playerok.deliver(order["id"], "Ваш код: " + take_code(ctx))
    if result["status"] == "busy":
        return                       # сделку выдаёт встроенная автовыдача или другой плагин
    if result["status"] == "unknown":
        ctx.log("Playerok не ответил — Lovsel сверит чат сам, повторять не нужно", "warning")
  • busy — сделку уже обслуживает другая выдача. Если продавец выбрал схему «Автовыдача Lovsel», плагин может выдать только сделку, к которой не подходит ни одно правило; при схеме «Плагины» встроенные правила не срабатывают.
  • bad_state — сделка не ждёт выдачи (уже отправлена, возврат, Playerok выдал товар сам).
  • unknown — Playerok не ответил на отправку: сообщение могло дойти. Не отправляйте товар повторно — выдача ждёт сверки и решения продавца.

Действия со сделками и товарами

Всё, что продавец делает с продажами и товарами в кабинете Lovsel, плагин делает готовыми методами. Перед каждым действием Lovsel заново читает сделку или товар с Playerok и проверяет, что он принадлежит подключённому аккаунту.

python
order = ctx.playerok.mark_sent("deal-1")          # «Товар отправлен» (orders.write)
order = ctx.playerok.refund("deal-1")             # полный возврат покупателю (orders.write)
ctx.playerok.update_item("item-1", price=249, description="Ключ придёт сразу.")   # products.write
options = ctx.playerok.publish_options("item-1")  # [{"id": "...", "name": "Обычный", "price": 0.0, ...}]
ctx.playerok.publish_item("item-1", options[0]["id"], options[0]["price"])
ctx.playerok.remove_item("item-1")
  • Возврат необратим, частичного возврата у Playerok нет: вызывайте refund() только после подтверждения продавца. Возвраты плагина записываются в журнал действий Lovsel.
  • Публикация с платным вариантом списывает деньги с баланса Playerok продавца. Плагин передаёт в publish_item() цену, которую подтвердил продавец: если стоимость варианта изменилась, публикации не будет — MarketplaceError с кодом price_changed. Платные публикации записываются в журнал действий Lovsel.
  • Если Playerok не ответил на действие, приходит MarketplaceError с кодом unknown_result: Lovsel уже сверил состояние, где это возможно; не повторяйте действие вслепую.

Прямой доступ к GraphQL Playerok

Сайт playerok.com работает запросами GraphQL. ctx.playerok.graphql() отправляет любой такой запрос от имени аккаунта через сессию в Lovsel — с теми же cookies, заголовками браузера и прокси продавца, что и встроенная синхронизация. Нужно разрешение marketplace.api.

python
ctx.playerok.graphql(query="", variables=None, operation_name="", persisted_hash="")
  • query — текст запроса (query …) или действия (mutation …), отправляется так же, как с сайта.
  • Без текста — сохранённый запрос сайта: operation_name и persisted_hash (sha256 запроса, как его отправляет сайт), или только operation_name запроса, который уже знает Lovsel: viewer, user, deals, deal, userChats, chat, chatMessages, items, item, itemPriorityStatuses, testimonials, gameCategoryDataFields, gameCategoryObtainingTypes, updateDeal, markChatAsRead, createChatMessage, updateItem, removeItem, publishItem, increaseItemPriorityStatus.
  • Возвращает поле data ответа. Ошибка Playerok — MarketplaceError с кодом graphql и текстом площадки.
  • Действие (mutation) при обрыве связи не повторяется: MarketplaceError с кодом unknown_result — сначала проверьте результат запросом, потом решайте.
  • Запросы идут с той же паузой, что и синхронизация Lovsel, и требуют активной подписки Lovsel.
python
me = ctx.playerok.graphql("query viewer { viewer { id username } }")["viewer"]

deals = ctx.playerok.graphql(operation_name="deals", variables={
    "pagination": {"first": 24, "after": None},
    "filter": {"userId": me["id"], "direction": "OUT", "status": ["PAID"]},
    "showForbiddenImage": True,
})["deals"]

ctx.playerok.graphql(operation_name="markChatAsRead", variables={"input": {"chatId": "chat-1"}})

Это запросы сайта, а не партнёрский API: Playerok может их менять. Проверяйте ответ и показывайте продавцу понятную ошибку, если поля стали другими. Платные действия (повышение приоритета, платная публикация) выполняйте только после подтверждения продавца с суммой.

События

СобытиеКогдаДанные
playerok.new_orderНовая продажа после подключенияorder
playerok.order_statusСтатус сделки изменилсяorder, old_status, old_raw_status
playerok.deal_problemПроблема открыта или решенаorder, opened
playerok.new_chatПервое сообщение в новом чатеchat_id, buyer, buyer_id, chat_type, message, order
playerok.new_messageСообщение покупателя или поддержкито же, плюс from (buyer или support)
playerok.new_reviewНовый отзывorder, chat_id, review (id, stars, text)
playerok.deliveryТовар по сделке отправленorder, mechanism, status

Во всех событиях event["marketplace"] == "playerok". Свои сообщения продавца, служебные уведомления площадки и старые события до подключения плагинам не приходят. Одно и то же событие не доставляется дважды при сверке и перезапуске, но обработчик всё равно должен быть идемпотентным: храните в ctx.storage, что уже сделано.

Единый баланс

ctx.wallet работает и для сделок Playerok: ctx.wallet.reserve(order_id, amount, marketplace="playerok"). Сделка должна существовать в Lovsel, а площадка плагина — быть Playerok (marketplace можно не указывать). Идемпотентность и изоляция операций — по пользователю, плагину, площадке, сделке и ключу, как для других площадок. Баланс Playerok и баланс Lovsel — разные деньги: закупки плагина оплачиваются только с баланса Lovsel. Подробнее — «Единый баланс в плагинах».

Проверка без сервера

python
from lovsel_sdk.testing import load_plugin

plugin = load_plugin("my_plugin.py", playerok={
    "orders": [{"id": "deal-1", "title": "Ключ", "amount": 199, "status": "PAID", "buyer": "anna",
                "chat_id": "chat-1", "item_id": "item-1"}],
    "chats": [{"id": "chat-1", "buyer": "anna"}],
})
plugin.emit("playerok.new_order", {"order": plugin.ctx.playerok.order("deal-1")})
assert plugin.playerok_sent[0]["chat_id"] == "chat-1"

Прямые запросы на стенде отвечают из параметра api (ключ — имя операции GraphQL), а возвраты, отметки и публикации складываются в plugin.market_actions:

python
plugin = load_plugin("my_plugin.py", playerok={"orders": [{"id": "deal-1", "status": "PAID"}]},
                     api={"viewer": {"viewer": {"id": "user-1", "username": "TestSeller"}}})
assert plugin.ctx.playerok.graphql("query viewer { viewer { id username } }")["viewer"]["id"] == "user-1"
plugin.ctx.playerok.mark_sent("deal-1")
assert plugin.market_actions[-1]["action"] == "mark_sent"

Полный пример — docs/examples/playerok_greetings.py: приветствие после оплаты, благодарность после подтверждения и уведомление о плохом отзыве. Он ничего не создаёт на Playerok и не тратит деньги.