/kotodoski

Доски почёта и облачные сохранения для игр в VK Play.

Primary LanguagePythonDo What The F*ck You Want To Public LicenseWTFPL

kotodoski

Простые, бесплатные (и ужасные) доски почёта (а теперь и облачные сохранения) для игр в VK Play.

Поддерживают авторизацию как через GAS так и через "Эмуляцию Steam" (далее - VKSteam) по желанию.

Допускается использование и без какой-либо авторизации вовсе, но это очень опасно.

Сейчас используется на проде в VK Play версии игры Wally and the FANTASTIC PREDATORS.

Описание документации

Конфигурация сервера

Коды ошибок авторизации

Доски почёта:

Облачные сохранения:

Служебная информация:

Администрирование:

Конфигурация сервера

Сам сервер настраивается в файле config.py,

от строки # -- КОНФИГУРАЦИЯ -- # до # -- КОНЕЦ КОНФИГУРАЦИИ -- #.

См. файл config_template.py в качестве примера настроек, можете его отредактировать и скопировать как config.py.

Это сделано из-за того, что сам сервер предназначен для хостинга на бесплатном Deta Spaces (мы разработчики игр для VK Play, откуда у нас деньги? с продаж тем более ничего нет), и это было легче всего сделать файлом.

Если вы используете "Эмуляцию Steam" в VK Play, вам НЕ НУЖЕН GAS!

Вы не можете использовать GAS и "Эмуляцию Steam" одновременно, выберите что-то одно!

Если вы вдруг не видите у себя App ID для "Эмуляции Steam", но точно уверены что должны, пожалуйста, свяжитесь с Integration Team во внутреннем чате платформы (зелёная иконка), только они вам помогут.

Имя переменной Тип переменной Описание переменной
CONFIG_USE_GAS bool Использовать ли авторизацию через GAS?
CONFIG_GAS_GMR_ID int GMR ID игры, см. в Системных свойствах!
CONFIG_GAS_SECRET str GAS секрет игры, см. в Системных свойствах!
CONFIG_USE_VKSTEAM bool Использовать ли авторизацию через VKSteam? (для новых игр)
CONFIG_VKSTEAM_APP_ID int ID для эмуляции Steam из Системных свойств!
CONFIG_VKSTEAM_KEY str Секрет для эмуляции Steam API из Системных свойств!
CONFIG_SERVER_USER_AGENT str Как представляться серверам ВК? Советую поменять на что-то своё
CONFIG_ADMIN_SECRET str Секрет для админских API методов доски
CONFIG_LEADERBOARD_INFO dict[str, dict[str, str|int|bool]] Описание всех досок, об этом ниже
CONFIG_GREETING_MESSAGE str Как приветствовать любопытных, что попытались открыть ваш сервер как ссылку в браузере? HTML-строка.

Пример CONFIG_LEADERBOARD_INFO:

# dict, конфигурация досок почёта, key - идентификатор доски.
CONFIG_LEADERBOARD_INFO = {
    # wally daily runs
    'dailyrun': {
        # str, 'day' - сброс каждый день, 'week' - каждую неделю, 'hour' - час, None - не сбрасывать
        'reset_every': 'day',
        # bool, True - сортировать от меньшего к большему
        'reverse_sort': False,
        # bool, разрешать ли публиковать запись если данный игрок УЖЕ публиковал?
        'allow_overwrite': False,
        # int, макс. кол-во записей, None - не ограничено (опасно!)
        'max_entries': 1000
    },
    # wally weekly runs
    'weeklyrun': {
        # str, 'day' - сброс каждый день, 'week' - каждую неделю, 'hour' - час, None - не сбрасывать
        'reset_every': 'week',
        # bool, True - сортировать от меньшего к большему
        'reverse_sort': False,
        # bool, разрешать ли публиковать запись если данный игрок УЖЕ публиковал?
        'allow_overwrite': False,
        # int, макс. кол-во записей, None - не ограничено (опасно!)
        'max_entries': 1000
    }
}

Ключ это желаемый leaderboard_id доски, значение это ещё один dict с описанием доски.

Описание dict-а доски:

Ключ Тип значения Описание
reset_every str|None Интервал сброса доски. None - никогда, 'week' - еженедельно, 'day' - ежедневно, 'hour' - ежечасно, 'minute' - ежеминутно соотв.
reverse_sort bool Если True то сортировать от меньшего значения к большему, если False то от большего к меньшему
allow_overwrite bool Разрешить ли игроку перезаписывать свою запись в доске? Если False то будет выкидываться status ошибка 0 если запись уже есть
max_entries int Максимальное кол-во записей для одной доски, 0 - неограниченно (может забить БД!)

Поле reset_every работает так, что ненужные компоненты даты "убираются" в зависимости от частоты сброса.

Например:

при reset_every: 'day', сброс будет не через 24 часа после старта сервера, а когда наступит следующий день.

при reset_every: 'minute', сброс будет не через 60 секунд после старта сервера, а когда наступит следующая минута.

Документация HTTP API

Коды ошибок авторизации

В возвращаемых JSON-объектах поле status может иметь значение от -10 и ниже, это коды ошибок GAS, или -20 и ниже, это коды ошибок VKSteam.

Коды ошибок GAS:

Код в status Описание
-10 Параметр gas_uid неверный, или не задан
-11 Параметр gas_hash неверный, или не задан
-12 Параметр gas_ip неверный, или не задан (не должно происходить)
-13 GMR ID не был задан в Конфигурации сервера
-14 Секрет GAS не был задан в Конфигурации сервера
-15 Произошла ошибка при подсчёте подписи для запроса в GAS
-16 GAS вернул HTTP-код 400 или больше
-17 GAS вернул status не ok, (см. документацию VK Play)
-18 Произошла ошибка HTTP запроса в GAS (лёг сервак?)

Коды ошибок VKSteam:

Код в status Описание
-20 Параметр user_id неверный, или не задан (попытка подделки?)
-21 Параметр vksteam_ticket неверный, или не задан (попытка подделки?)
-22 VKSteam вернул HTTP-код 400 или больше (истёк тикет?)
-23 В VKSteam запросе response.params.result не OK (попытка подделки ответа?)
-24 Не совпадают данный user_id и steamid в тикете (кто-то химичит трафик?)
-25 Произошла ошибка HTTP запроса в VKSteam (лёг сервак?)

/v1/api/post

Делает запись в доске почёта.

HTTP метод: POST

Параметры:

Имя параметра Тип параметра Описание параметра
leaderboard_id str Идентификатор доски
score int Очки, 1 или больше
user_name str Имя пользователя, всегда обязательно т.к. GAS и VKSteam не дают данную информацию серверу, зато дают её игре
metadata str Опционально, доп. данные для новой записи
user_id str Идентификатор пользователя, используется для поиска существуюших записей игрока. Для GAS опционален, для VKSteam обязателен.
gas_uid str (ТОЛЬКО ДЛЯ GAS) GAS UID из параметров запуска
gas_hash str (ТОЛЬКО ДЛЯ GAS) GAS OTP Hash из параметров запуска
vksteam_ticket str (ТОЛЬКО ДЛЯ VKSTEAM) Тикет авторизации как hex-строка без префикса 0x с чётным кол-вом символов. Параметр user_id становится ОБЯЗАТЕЛЕН!

Возвращает: JSON объект в кодировке UTF-8, см. ниже.

Пример запроса:

https://wallyboards.duckdns.org/v1/api/post

Тело POST:

leaderboard_id=daily&score=228&user_name=SuperNagibator&user_id=71337&vksteam_ticket=abcdef

Делает новую запись в таблицу daily, 228 очков у пользователя SuperNagibator авторизованного через VKSteam.

leaderboard_id=daily&score=228&user_name=SuperNagibator&user_id=71337&vksteam_ticket=abcdef&metadata=mode_hardcode

Повторяет предыдующую запись, но уже с опциональной metadata строкой mode_hardcode.

При ошибке (пример):

{
    "status": -1,
    "error": "сообщение об ошибке на английском"
}

Коды ошибок status:

Код в status Описание
-1 Неверный параметр score, не дан, ноль или отрицательный
-2 Неверный параметр user_id, пустой или не дан (GAS - исключение)
-3 Неверный параметр user_name, пустой или не дан
-4 Неверный параметр leaderboard_id, пустой или не дан
-5 Доски с таким leaderboard_id не существует
-6 Что-то странно поломалось во время пересортировки, очень плохо
0 Доска не поддерживает перезапись, и запись игрока уже там существует, дождитесь сброса
1 Успех, см. ниже

При успехе (пример):

{
    "status": 1,
    "error": "",
    "new_entry_index": 3
}
Имя Тип Описание
status int В случае успеха всегда 1
error str В случае успеха всегда пустая
new_entry_index int Индекс новой записи, всегда начинается с нуля

/v1/api/get

Получает записи из одной доски почёта.

HTTP метод: GET

Параметры:

Имя параметра Тип параметра Описание параметра
leaderboard_id str Идентификатор доски
index_start int С какого индекса получать записи? -1 значит относительно игрока
amount int Максимальное кол-во записей которое хочется получить. 0 значит получить все записи от index_start (МЕДЛЕННО!)
user_id str Идентификатор пользователя, используется когда index_start == -1 и для проверки запроса в VKSteam. Для GAS опционален, для VKSteam обязателен.
gas_uid str (ТОЛЬКО ДЛЯ GAS) GAS UID из параметров запуска
gas_hash str (ТОЛЬКО ДЛЯ GAS) GAS OTP Hash из параметров запуска
vksteam_ticket str (ТОЛЬКО ДЛЯ VKSTEAM) Тикет авторизации как hex-строка без префикса 0x с чётным кол-вом символов. Параметр user_id становится ОБЯЗАТЕЛЕН!

Возвращает: JSON объект в кодировке UTF-8, см. ниже.

Пример запроса:

https://wallyboards.duckdns.org/v1/api/get?leaderboard_id=daily&index_start=0&amount=10&user_id=71337&vksteam_ticket=abcdef

Получает первые 10 записей из доски если это возможно.

https://wallyboards.duckdns.org/v1/api/get?leaderboard_id=daily&index_start=10&amount=10&user_id=71337&vksteam_ticket=abcdef

Получает следующие 10 записей из доски если это возможно.

https://wallyboards.duckdns.org/v1/api/get?leaderboard_id=daily&index_start=-1&amount=1&user_id=71337&vksteam_ticket=abcdef

Пытается получить свою запись в доске если она присутствует, если записи нет то возвращается status код 0.

При ошибке (пример):

{
    "status": -1,
    "error": "сообщение об ошибке на английском"
}

Коды ошибок status:

Код в status Описание
-1 Неверный параметр user_id, пустой или не дан (GAS - исключение)
-2 Неверный параметр leaderboard_id, пустой или не дан
-3 Неверный параметр index_start, меньше -1 или не дан
-4 Доски с данным leaderboard_id не существует
-5 Неверный параметр amount, меньше 0 или не дан
0 Параметр index_start имеен значение -1, но записи с данным игроком не нашлось в доске
1 Успех, см. ниже

При успехе (пример):

{
    "status": 1,
    "error": "",
    "entries": [
        {
            "score": 69,
            "index": 5,
            "timestamp": 1679665740.0,
            "user_id": "7652281488420691337",
            "user_name": "GladitKotovMyNikogdaNeBrosim",
            "metadata": "опционально, может быть пустым"
        },
        {
            "score": 50,
            "index": 6,
            "timestamp": 1679665632.0,
            "user_id": "7654206913372281488",
            "user_name": "SuperNagibator",
            "metadata": ""
        }
    ],
    "amount": 2,
    "total": 13
}
Имя Тип Описание
status int В случае успеха всегда 1
error str В случае успеха всегда пустая
entries array Массив записей, может быть пустым если ничего не было найдено
amount int Сколько записей в entries
total int Сколько всего записей в доске

Описание записей в массиве entries:

Имя Тип Описание
score int Кол-во очков, всегда больше нуля
index int Индекс в глобальном массиве записей, всегда ноль или больше
timestamp float Временная метка в формате UNIX UTC
user_id str Идентификатор пользователя
user_name str Имя пользователя
metadata str Опционально, метаданные записи

/v1/api/admin_action

Запрос для админских штук, пока скудно реализован, идеи приветствуются!

Если в конфигурации сервера не задан админский секрет, то этот метод будет отключен.

Абсолютно все вызовы к этому методу логгируются в Flask.

HTTP метод: GET (исключительно для удобства)

Параметры:

Имя Тип Описание
secret str Секрет для API админа, см. Конфигурация
action str Какое админское действие нужно совершить

Если secret неверный, или не дан вовсе, запрос вернёт строку ne-a, izvinite.

Действия:

Значение параметра action Описание
reset Сбрасывает все данные всех досок почёта
reset_cloud Сбрасывает все облачные сохранения всех игроков
get_cloud_save Получает JSON бэкап облачных сохранений
get_leaderboards Получает JSON бэкап досок почёта

Возвращает: обычную строчку текста (для удобства)

Пример:

https://wallyboards.duckdns.org/v1/api/admin_action?secret=iamadmin&action=reset

Сбросит все данные во всех досках и заставит сделать перезагрузку из конфига.

Если не задан параметр action, но secret верный, запрос вернёт строку admin: secret is correct, but no action was given.

Больше для этого метода особо ничего не реализовано.

/v1/api/cloud_post

Записывает строку данных в облачные сохранения.

Строка не должна содержать спец. символов ASCII/Unicode, и не обязательно в Base64, можно и JSON и Hex-текст.

HTTP метод: POST

Параметры:

Имя параметра Тип параметра Описание параметра
data str|None Строка данных которую нужно записать. Если параметр не дан, сохранение удаляется!
user_id str Идентификатор пользователя. Для GAS опционален, для VKSteam обязателен.
slot_id str Идентификатор слота облачных сохранений
gas_uid str (ТОЛЬКО ДЛЯ GAS) GAS UID из параметров запуска
gas_hash str (ТОЛЬКО ДЛЯ GAS) GAS OTP Hash из параметров запуска
vksteam_ticket str (ТОЛЬКО ДЛЯ VKSTEAM) Тикет авторизации как hex-строка без префикса 0x с чётным кол-вом символов. Параметр user_id становится ОБЯЗАТЕЛЕН!

Возвращает: JSON объект в кодировке UTF-8, см. ниже.

Описание возвращаемого объекта:

Имя Тип Описание
status int 1 при успехе, отрицательное значение при ошибке
error str В случае успеха всегда пустая, иначе сообщение об ошибке
timestamp float Временная метка в формате UNIX UTC, 0 если данные были удалены

Коды ошибок status:

Код в status Описание
-1 Неверный параметр slot_id, пустой или не дан
1 Успех, см. ниже

Пример запроса:

https://wallyboards.duckdns.org/v1/api/cloud_set

Тело POST: user_id=612345&vksteam_ticket=abcdef&slot=wally&data=0YXQvtGH0YMg0L7QsdC90LjQvNCw0YjQtdC6

Запишет base64-строку 0YXQvtGH0YMg0L7QsdC90LjQvNCw0YjQtdC6 в облачные сохранения пользователя в слот wally с идентификатором 612345 и авторизацией через VKSteam.

Тело POST: user_id=612345&vksteam_ticket=abcdef&slot=wally

Удалит облачное сохранение в слоте wally для пользователя с идентификатором 612345.

Пример ответа (при сохранении):

{
    "status": 1,
    "error": "",
    "timestamp": 1679738477.0
}

Пример ответа (при удалении):

{
    "status": 1,
    "error": "",
    "timestamp": 0
}

/v1/api/cloud_get

Получает строку данных из облачных сохранений.

HTTP метод: GET

Параметры:

Имя параметра Тип параметра Описание параметра
user_id str Идентификатор пользователя. Для GAS опционален, для VKSteam обязателен.
slot_id str Идентификатор слота сохранения
gas_uid str (ТОЛЬКО ДЛЯ GAS) GAS UID из параметров запуска
gas_hash str (ТОЛЬКО ДЛЯ GAS) GAS OTP Hash из параметров запуска
vksteam_ticket str (ТОЛЬКО ДЛЯ VKSTEAM) Тикет авторизации как hex-строка без префикса 0x с чётным кол-вом символов. Параметр user_id становится ОБЯЗАТЕЛЕН!

Возвращает: JSON объект в кодировке UTF-8, см. ниже.

Описание возвращаемого объекта:

Имя Тип Описание
status int 1 если данные есть, 0 если нет, отрицательное число при ошибке
error str В случае успеха всегда пустая, иначе сообщение об ошибке
timestamp float Временная метка в формате UNIX UTC, 0 если данных нет
data str Строка данных, пустая если данных нет

Пример запроса:

https://wallyboards.duckdns.org/v1/api/cloud_get?user_id=612345&vksteam_ticket=abcdef&slot_id=wally

Получит строку из слота wally облачных сохранений для пользователя с идентификатором 612345 и авторизацией через VKSteam.

Пример ответа (при отсутствии данных):

{
    "status": 0,
    "error": "no data is present for given user_id or slot_id",
    "timestamp": 0,
    "data": ""
}

Пример ответа (при наличии данных):

{
    "status": 1,
    "error": "",
    "timestamp": 1679738014.0,
    "data": "0J/QsNGB0YXQsNC70L7QuiDQvdC1INCx0YPQtNC10YIu"
}

/v1/api/server_time

Получает текущее время на сервере и возвращает ваш реальный IP-адрес.

Пожалуйста, учтите несколько вещей:

  • Время всегда возвращается по часовому поясу UTC, в формате UNIX Epoch.
  • Время может отставать на несколько секунд в зависимости от пинга, прокси и других внешних факторов.
  • IP-адрес клиента может быть неверным в зависимости от конфигурации прокси вашего сервера, см. функцию get_client_ip чтобы исправить это.

HTTP метод: GET

Параметры:

Нет.

Возвращает: JSON объект в кодировке UTF-8, см. ниже.

Описание возвращаемого объекта:

Имя Тип Описание
status int Всегда 1
error str Всегда пустая
timestamp float Временная метка в формате UNIX Epoch, в часовом поясе UTC
ip str IP-адрес сущности сделавшей запрос

Пример запроса:

https://wallyboards.duckdns.org/v1/api/server_time

Получит текущее серверное время.

Пример ответа:

{
    "status": 1,
    "error": "",
    "timestamp": 1679816858.012961,
    "ip": "5.123.123.200"
}

Если поле ip равно 127.0.0.1 или другой несуразице, вероятно что ваш сервер не может нормально получать IP-адреса клиентов, см. документацию вашего хостинга и функцию get_client_ip.