Перейти к основному содержимому

DMarket API - практика и замечания

· 7 мин. чтения

UPD 2023.12.15

Привет из 2023. В свое время пост принес мне много вопросов от желающих использовать API, а еще наверняка немного внутреннего хейта от команды DMarket.

Мой путь с DMarket лежал с самого начала его существования, с ICO, а закончился год назад баном. Поддержка не смогла внятно ответить на мои многичесленные вопросы о причинах бана, несмотря на то, что у нас перед баном были многочесленные диалоги обо всем и все были очень приветливы друг к другу (несмотря на этот пост злобы), а я всегда был благодарен за помощь.

Год назад, после начала войны, я очередной раз авторизировался на сайте, после чего получил табличку бана. Поддержка сказала пройти верификацию, скинуть им фотки и все дела. После чего объявила об окончательном, бесповоротном парманентном бане, который запрещено обжаловать и запрещено узнать официальную причину.

Сейчас не имеет смысла врать, если бы на это были действительно настоящие причины, например, какие-то махинации с моей стороны или злоупотребление их услугами, но причин для бана действительно не было и я уверен, что это даже не из-за этого поста.

Но раз я начал редактировать этот пост, то я решил все же донести мысль, что ваши вещи, по сути, ваши настоящие деньги, могут быть запросто заблокированы по прихоти каких-то людей без объяснения причин и возврата денег. Навсегда. Деньги самого обычного, рядового пользователя.


Заметки для тех, кто решил поработать с API DMarket.com. Сразу предупреждаю, что некоторые из них могут быть неуместны, поскольку все знать невозможно и я просто не понял разработчиков, что сделали ту или иную "странность"


Итак, начнем с мелочей

API ключ

Он помещается в Headers и в сам запрос.

dmarket_token

При больших GET запросах возможен провал из-за огромной длины ключа, но по факту с таким еще не сталкивался. Но в некоторых методах достаточно только в Headers (вроде)

Формат чисел

Числа в виде строк, при чем без запятых. Полагаю, что сделано для корректной работы на всех процессорах и более компактного хранения в БД. Насколько я помню, в Qiwi так же, но там числа есть числа, а не строки.

Угадаете, чему будет равен b в Lua b = "120" < "15"? (true, блэд)

Еще не ясно почему JSON в pretty виде, но это мелочи жизни

Число или строка? Эээ

И иногда числа бывают числами. Вот и ковыряйтесь

Не стандартизированы регистры переменных

Тут по интуиции работать не получится. Если в одном месте вы писали .price, то в другом нужно уже .Price. Но есть маленький лайфхак: есть результат находится внутри таблицы Items, значит юзается UpperCamel, если же внутри results, то lowerCamel, только это вроде не всегда. Вопрос, почему некоторые результаты внутри results, а другие Items тоже не совсем понятно

+ особенности метода /buy/offers

Обратите внимание на функцию покупки итема. Вы должны указывать цену и даже валюту предмета, который собираетесь купить, хотя у него есть конкретный ID с УЖЕ установленной ценой.

Есть теория, что это сделано для того, чтобы люди, вроде меня, получившие уведомление об интересном оффере, спустя время нажав у себя кнопку "купить" не офигели от того, что с них списало не ту сумму, на которую они рассчитывали из-за того, что владелец тем временем изменил цену. Но я считаю, что об этом должен беспокоиться сам человек, перепроверяя, не изменилась ли цена оффера.

Но и это пол беды. Сама функция покупки выглядит "не очень" из-за этого. Приходится заполнять кучу полей вместо того, чтобы указать просто ID итема для покупки. А покупку нескольких итемов можно было бы сделать, указав ID'ы через запятую, как у VKontakte и функция выглядела бы изящно

offer.updated не соответствует действительности

У офферов есть поле updated, а есть created. У итемов тоже есть такое поле, но имеет другое название: lastUpdated (ррр). Так вот эти поля обновляются по какому-то странному алгоритму. Возможно, чтобы продвигать определенные предметы в списке на сайте (UPD. Если кто-то изменяет цену предмета на сайте, итем (листинг) считается обновленным).

Всегда проверяйте, действительно ли оффер "свежий" и есть ли реально новые офферы в итеме или же вы проверяете один и тот же итем 50й раз, потому что что-то там "обновилось".

Это одна из главных проблем, с которой пришлось столкнуться и из-за нее я получал уведомления об одном и том же предмете несколько часов подряд

Какой-то повсеместный рассинхрон данных

В разных местах указана разная цена

У итемов есть поле .cheapestOfferPrice. А еще есть .offerPrices.cheapest.amount и они отличаются _ :| _/. Обратите внимание не на тот и ваш скрипт начнет присылать вам 💩 (корректно второе)

Кстати .offerPrices.cheapest.amount обновляется ПОСЛЕ, а не до или даже одновременно с .lastUpdate, что приводит к тому, что я обрабатываю "новый" итем с устаревшей инфой.

При чем задержка просто огромна, а в комбинации с непонятным обновлением .lastUpdate в принципе, возникают аномальные баги в коде, на отладку которого приходится тратить времени больше, чем на написание

Уверен, это не единственное несоответствие, но оно стало причиной моей головной боли

Проблема списка депозитов

Речь про /trading/v1/report/accounting/balance. Выглядит вот так: dmarket_deposits

Про регистры переменных я уже писал, поэтому добавлю немного про рассинхрон, скорость выполнения и еще одну косметическую проблему

После пополнения баланса DMarket вы видите изменение сразу, но вот проблема: транзакция пополнения отобразится только примерно спустя час. При этом начинаются лаги с получением баланса и истории пополнений. У меня небольшой бот, который подсчитывает мои деньги на платформе и, конечно же, он отобразит некорректные данные из-за задержки.

Это мелочная задержка, есть еще одна: скорость выполнения самой функции. Она, наверное, проклята, но у меня бОльшая половина запросов падает по таймауту. При чем если отправить кучку запросов, то функция словно разгоняется и начинает работать корректно. Но по факту лучше всего кешировать результат, если это возможно.

Что касается косметической проблемы, то помимо того, что это почему-то POST запрос, в Body нужно указывать DateStart и DateEnd. Даже если вы хотите получить данные за все время, указывайте 1 и os.time(). Кстати, именно 1, потому что если укажете 0, то поймаете ошибку _ :| _/

Не стандартизированные ошибки

dmarket_errors_standart

Мне пришлось провозиться пол часа, чтобы понять, почему я получаю METHOD NOT ALLOWED. Дело было в лишнем слеше, который в GET нормально обрабатывается. Конечно, это мой провтык, но что с форматом и детализацией ошибок?

После решения проблемы со слешем натыкаешься на уже json (в предыдущем случае была plain text) ошибку INVALID_REQUEST_BODY. Провтык был в том, что цену нужно было указать в виде строки, а не decimal или int

Но проблема не только в сериализации выхлопа ошибки, но и в разных ее форматах. Например, бывает и такое: dmarket_error_format

HasErrors, который опять же CamelCase, можно было заменить на ok = true/false, как сделано в телеграм (очень удобно: if res.ok then ... else ...). Кстати, внутри даже Status = "failed", что ни в какие ворота не лезет

Уже смирились? Ловите: trading/v1/report/accounting/balance Это в trading/v1/report/accounting/balance и чтобы понять в чем дело потребовалось еще не менее 20 минут. В lua все числа это decimal по умолчанию и библиотека конвертации в json добавляла к числам .0. Пришлось костылями фиксить, чтобы этот нолик снимался


Послесловие

Некоторые из заметок могут быть не актуальны, еще больше я попросту забыл, пока дошли руки до публикации, а некоторые, вроде ужасной документации я просто умолчал - такое не пропустишь.

Работу с API я начал 8 ноября 2018 и вожусь до сих пор, пытаясь подобрать оптимальные параметры отбора предметов. Если вы имеете какие-то мысли или знаете ботов для других площадок - пишите, обменяемся опытом ;)

Над чем я работал

Никогда ранее я не торговал CS:GO скинами, но при этом у меня было несколько, которые я хотел продать. И уж раз я вложился в DMT, то и продал их здесь, где впервые обратил внимание на то, что люди готовы платить больше за предметы, которые можно вывести сразу

Для справки, Steam блокирует предмет на 7 дней после трейдинга

Тоесть, я попробовал покупать "замороженные" предметы, давал им неделю отлежаться в инвентаре и выставлял на продажу по большей цене.

Искать такие предметы вручную можно, но это медленно и требует много времени. Но забегая наперед, скажу, что время, потраченное на борьбу с DMT API вряд ли окупилось :) Но это скорее из-за недочетов моей торговой стратегии