# `2ch.hk` API

## Определения

### Board

Информация о доске.

```js
{
  // Сколько постов можно написать в тред данной доски
  // до тех пор, пока тред не перестанет от них "бампаться" (подниматься).
  "bump_limit": 500,

  // Раздел, в который входит данная доска.
  "category": "Разное",

  // Имя автора сообщения по умолчанию (если не указано).
  "default_name": "Аноним",

  // "Разрешения" (feature-флаги) для данной доски.
  "enable_dices": false, // Возможно, разрешены ли некие "ролл", хз.
  "enable_flags": false, // Показываются ли "флаги" (страны) у постов на данной доске.
  "enable_icons": false, // Разрешены ли "значки" (например, флажки политической ориентации в `/po/`) у постов на данной доске. При отправке поста в форме можно выбрать "значок" из списка.
  "enable_likes": false, // Можно ли ставить лайки/дизлайки на этой доске.
  "enable_names": false, // Есть ли поле "Имя" в форме отправки сообщения на данной доске.
  "enable_oekaki": false, // Видимо, можно ли рисовать "оэкаки" на этой доске.
  "enable_posting": true, // Видимо, можно ли постить на этой доске.
  "enable_sage": false, // Видимо, можно ли "сагать" на этой доске (ответ с "сажей" ("sage", см. ниже) не "бампает" тред).
  "enable_shield": false, // Возможно, включён ли какой-нибудь спам-лист (или что-то типа того) на этой доске.
  "enable_subject": true, // Показывать ли поле "Тема" в форме отправки сообщения или создания треда на данной доске.
  "enable_thread_tags": true, // Можно ли помечать треды тегами на данной доске.
  "enable_trips": false, // Разрешены ли "трип-коды" в поле "Имя" при отправке сообщений на данной доске.

  // Поддерживаемые типы файлов у вложений на данной доске.
  "file_types": [
    "jpg",
    "png",
    "gif",
    "webm",
    "sticker",
    "mp4",
    "youtube",
    "webp"
  ],

  // Текст, который показывается наверху страницы доски.
  // Может быть длинным.
  // Содержит разметку HTML.
  "info": "/hi/ - Это раздел для обсуждения великих (и не очень) событий прошлого и всего что с ними связано в рамках Исторической науки.\r\nПриветствуется: конструктивный, аргументированный диалог, желательно со ссылками на авторитетные опубликованные источники.\r\nНе рекомендуется: переходить на личности; создавать треды о событиях менее чем тридцатилетней давности.\r\nКарается: нацисрач в любом виде; политика, актуальные новости (в том числе провокационные треды); фольк-хистори, теории заговора; обсуждение блоггеров и одиозных личностей. Для вышеперечисленных дискуссий существуют отдельные разделы. Официальная конфа Гитлерача в тг: <a href=\"https://t.me/ru2chhistory\">https://t.me/ru2chhistory</a>",

  // Какое-то ещё описание доски.
  // Можно назвать его "кратким": часто состоит из одного слова.
  // Часто начинается на малую букву.
  "info_outer": "железо, видеокарты, ноутбуки, intel, amd, nvidia, ati",

  // ID доски.
  "id": "b",

  // Название доски.
  "name": "Бред",

  // Максимальная длина комментария.
  "max_comment": 15000,

  // Максимальный размер прикрепляемого файла.
  "max_files_size": 20480,

  // Максимальное количество страниц тредов на данной доске.
  "max_pages": 10,

  // Количество тредов на странице.
  "threads_per_page": 20,

  // (Не обязательное поле)
  // (Только если присутствует флаг `enable_icons: true`)
  // Иконки, используемые для авторов комментариев на данной доске.
  // (можно выбрать иконку при создании треда или написании комментария)
  "icons": [
    {
      "name": "[4X] Age Of Wonders 3",
      "num": 1,
      "url": "/static/icons/gsg/[4X] Age Of Wonders 3.png"
    },
    ...
  ],

  // (Не обязательное поле)
  // (Только если присутствует флаг `enable_thread_tags: true`)
  // Теги, которые могут быть использованы при создании тредов на данной доске.
  "tags": [
    "all",
    "arthas",
    ...
  ]
}
```

### BoardBanners

Информация о баннерах, показываемых на доске.

```js
{
  // Рекламный баннер сверху (устройства с большим экраном).
  "advert_top_image": "/banners/bDpQCWt8xPTuC43S.jpg",
  // Ссылка рекламного баннер сверху (устройства с большим экраном).
  "advert_top_link": "/banners/bDpQCWt8xPTuC43S/",

  // Рекламный баннер снизу (устройства с большим экраном).
  "advert_bottom_image": "/banners/AZMtfxdMbkGwpPXB.jpg",
  // Ссылка рекламного баннера снизу (устройства с большим экраном).
  "advert_bottom_link": "/banners/AZMtfxdMbkGwpPXB/",

  // Рекламный баннер (мобильные устройства).
  "advert_mobile_image": "/banners/S4BcqS4adse3B2Cb.jpg",
  // Рекламный баннер (мобильные устройства).
  "advert_mobile_link": "/banners/S4BcqS4adse3B2Cb/",

  // Баннер случайной доски (каждый раз — разный; своего рода «реклама» другой доски).
  "board_banner_image": "/ololo/kpop_7.gif",
  // Ссылка баннера случайной доски.
  "board_banner_link": "kpop"
}
```

### Post

Пост (комментарий).

```js
{
  // ID поста.
  "num": 29102706,

  // Дата написания поста ("unix time").
  "timestamp": 1549035324,

  // "Человекочитаемая" дата написания поста.
  "date": "01/02/19 Птн 18:35:24",

  // Если "1", то данный пост написан человеком,
  // создавшим тред с галкой "ОП треда",
  // и запостившим это сообщение с галкой "ОП треда".
  // Подробности — в подразделе "ОП треда".
  // Также, "op" будет "1" у всех постов, оставленных
  // человеком, создавшим тред, если в треде показываются
  // id пользователей (наипример, треды в `/po/`).
  "op": 0,

  // Имя автора поста.
  "name": "Аноним",

  // "email" автора поста (с префиксом "mailto:").
  // Пример: "mailto:admin@example.com".
  "email": "",

  // "Трип-код" автора поста.
  // Для администраторов и модераторов тут ставится соответствующая метка.
  "trip": "",
  "subject": "Четырнадцатый двачкап", // "Тема" поста.
  "comment": "Скинул на почту", // HTML-код комментария.

  // Список вложений.
  // Если вложений нет, то `null`.
  "files": Attachment[],

  // Забанен ли автор поста за данный пост.
  "banned": 0,

  // Закрыт ли этот тред.
  "closed": 0,

  // Является ли тред "бесконечным".
  // "Бесконечный" тред — это тред, не имеющий "бамплимита"
  // ("бампается" при любом ответе в нём), но при этом максимальное
  // количество постов в треде ограничено бамплимитом доски (например, 500 шт.),
  // и при добавлении в тред новых постов наиболее старые из существующих автоматически удаляются.
  "endless": 0,

  // Видимо, `timestamp` комментария, который является
  // (на текущее время) "последним", "бампающим" данный тред.
  // Например, первый комментарий в треде, или 500-ый комментарий
  // в треде с "бамп-лимитом" в 500 и количеством постов больше 500.
  // (что означает, что `lasthit` будет меньше `timestamp`а комментариев,
  //  выходящих за "бамп-лимит").
  //
  // Одно и то же значение у всех постов треда.
  //
  // Также, возможно, является датой "последнего изменения" треда:
  // например, добавление постов, удаление постов, изменения статуса
  // "прикреплён"/"не прикреплён", и т.п., потому что так это работает
  // на `4chan.org` (там это называется `last_modified`).
  //
  "lasthit": 1549117714,

  // Закреплён ли этот тред наверху в списке тредов доски.
  // Если не `0`, то может быть как `1`, так и любое другое целое положительное число.
  // Например, тред со значением `0` не закреплён.
  // Тред со значением `1` закреплён.
  // Тред со значением `2` закреплён выше треда со значением `1`.
  // Таким образом, числовое значение — это приоритет закрепления наверху.
  "sticky": 0,

  // (только у первого поста треда)
  // Теги треда. Пустая строка, если не указано тегов.
  "tags": "lolcup",

  // id треда данного поста (в виде строки).
  // `0` для первого поста треда, для остальных постов —
  // одно и то же значение: id треда, он же id первого поста треда.
  "parent": 0,

  // (optional)
  // Только для тредов с "лайками":
  "likes": 1, // Количество "лайков" у поста.
  "dislikes": 1, // Количество "дизлайков" у поста.
}
```

### Attachment

Вложение. Картинка или видео, прикреплённые к посту.

```js
{
  // Тип файла (1 — jpeg, 2 — png, 4 — gif, 6 - webm, 9 — webp, 10 — mp4, 100 — png стикер).
  "type": 2,
  // Помечена ли эта картинка как "NSFW" ("18+").
  "nsfw": 0,
  // Размер картинки или видео в килобайтах.
  // `0` для "стикеров".
  "size": 1611,

  // Ширина картинки или видео.
  "width": 1363,
  // Высота картинки или видео.
  "height": 768,

  // Имя файла (ограниченное по длине).
  "displayname": "photo2018-10-27[...]..png",
  // Имя файла (отсутствует для "стикеров"").
  "fullname": "photo2018-10-2705-29-50.png",
  // Имя файла на сервере.
  "name": "15490353246680.png",
  // URL картинки или видео.
  "path": "/vg/src/29102706/15490353246680.png",

  // MD5 хеш файла.
  "md5": "bc441048422b76dd41d626e1420fa0f7",

  // URL уменьшенной картинки.
  "thumbnail": "/vg/thumb/29102706/15490353246680s.jpg",
  // Ширина уменьшенной картинки.
  "tn_width": 250,
  // Высота уменьшенной картинки.
  "tn_height": 140,

  // (только для видео)
  // "Человекочитаемая" длительность видео.
  "duration": "00:00:53",
  // (только для видео)
  // Длительность видео в секундах.
  "duration_secs": 53

  // (только для "стикеров")
  //
  // Ссылка на установку стикера.
  "install": "/makaba/stickers/show/HycdNR0H"
  // Имя файла стикера.
  "name": "ygfyCF0H.png"
  // ID набора стикеров.
  "pack": "HycdNR0H"
  // ID стикера.
  "sticker": "ygfyCF0H"
}
```

Размеры миниатюр не определены. По умолчанию они ограничены `250` пикселями. На доске `/b/` они ограничены `220` пикселями. Иногда на одной и той же доске встречаются миниатюры, ограниченные `200` пикселями, рядом с миниатюрами, ограниченными `250` пикселями: возможно, это просто эффект старого треда, созданного «до изменений», и нового треда, созданного «после изменений».

<!-- В любом случае, качество миниатюр очень низкое (особенно это видно на экранах "ретина"), поэтому `anychan` по умолчанию использует размер в `200` пикселей. -->

### Error

Многие методы API возвращают «код результата» — число `result`:

* `0` — Произошла ошибка.
* `1` — Ошибки не произошло.
* `2` — Возратило со status 200 при попытке запроса капчи будучи залогиненным с "пасскодом": `{"result":2, "type":"passcode", "maxFiles":4, "maxFilesSize":40960, "maxImageResolution":40960}`. Судя по всему, такой ответ означал: "решение капчи не требуется для залогиненных пользователей".

В случае `result: 0`, в ответе также будет присутствовать объект с информацией об ошибке:

```js
{
  "result": 0,
  "error": {
    "code": -123,
    "message": 'Error message text'
  }
}
```

<details>
<summary>Список кодов ошибок</summary>

######

* `0`: `"Постинг запрещён. Бан: 908831. Причина: [W] United States (AMAZON-2011L)."`. См. код ошибки `-6`. Хз, что означает код ошибки `0`: мб это не обязательно "бан".
* `-1`: `"База данных недоступна."`. Случается редко: когда у макабы падает бэкэнд, либо когда вы отправляете неправильно составленный или сегментированный `POST`.
* `-2`: `"Доска не существует."`. Возникает, если отправлен неполный или неверно составленный "boundary" в HTTP-заголовке, когда пост содержит вложения.
* `-3`: `"Тред не существует."`.
* `-4`: `"Доступ запрещен."` — Возникает при попытке постинга на доске, на которой можешь постить лишь ограниченный круг пользователей. Предположительно возникает при попытке постинга в тред, в который может постить лишь ограниченный круг пользователей. Возможно также возникает при попытке постинга в закрытый тред. Также может возникать при постинге из забаненных подсетей из разных стран. С них запрещен постинг везде, кроме `/int/`. Такие страны перечислены на [картинке в треде `2ch-wiper`](https://lolifox.org/rus/res/1405484.html#q1429468): почти вся Латинская Америка, почти вся Африка, Ближний Восток (Монархии Персидского залива, Иран, Ирак, Турция, Сирия, и т.п.), Балканы, Дальний Восток (Китай, Корея, Индия, Филиппины, и т.п.).
* `-5`: `"Капча невалидна."`. Неправильная капча.
* `-6`: `"Постинг запрещён. Бан: <id-бана>. Причина: <причина>."`. Пример ошибки при отправке сообщения из [Tor](https://ru.wikipedia.org/wiki/Tor)-а: id бана — `554455`, причина — `[P] TOR exit node from United States (CMU-DOTCOM)`. Пример причины ошибки при отправке сообщения из-под [прокси](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D0%BA%D1%81%D0%B8-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80) или [VPN](https://ru.wikipedia.org/wiki/VPN)-а: `[W] proxy vpn`. Примеры ошибок в случае бана пользователя за сообщение: `"Постинг запрещён. Бан: 703362. Причина: Фагочатоc //!b. Истекает: Чтв Июн 01 00:00:00 2015."`, `"Постинг запрещён. Бан: 878620. Причина: Общее 10 - Реклама, вредоносные ссылки, попрошайка. Истекает: Птн Фев 01 00:00:00 2018."`, `"Постинг запрещён. Бан: 831265. Причина: [М] Общее 4 - Шитпостинг (Качество контента) //!mg. Истекает: Втр Янв 01 00:00:00 2017."`. ID бана требуется прикладывать при отправке запроса на разбан. Баны могут быть как "локальными" — на одной доске (это можно определить по соответствующей приписке в причине бана, например: `//!b`) — так и "глобальными" (на всех досках).
* `-7`: `"Тред закрыт."`.
* `-8`: `"Вы постите слишком быстро."`. Стандартный "кулдаун" на отправку постов на всех досках — 20 секунд, на создание тредов в `/b/` — 20 секунд, в тематиках — 300 секунд. Тут стоит уточнить, что "кулдаун" на создание тредов работает не так, как у на отправку постов, а несколько иначе: он распространяется не на конкретную подсеть IP-адресов, а вообще на всех. То есть, если кто-то создал тред, например, в `/b/`, то в последующие 20 секунд у всех постеров будет выбивать `-8` при попытке создать ещё один тред на этой же доске, вне зависимости от их IP-адреса.
* `-9`: Отправлено слишком большое значение какого-то из параметров. Например, отправлен слишком длинный текст комментария.
* `-10`: `"Вы загружаете одинаковые файлы."`. Возникает, если в отправляемом сообщении присутствуют одинаковые файлы.
* `-11`: `"Тип файла не поддерживается."`. Возникает, если в отправляемом сообщении присутствуют вложения, тип файлов которых не поддерживается.
* `-12`: Размер какого-то из вложений превышает максимально допустимый.
* `-13`: `"Вы запостили слишком много файлов."`. Возникает при попытке отправить более `4`х вложений в посте (с некоторыми видами пасскодов разрешено до 8 вложений). Возможно бывает также при превышении предела суммарного размера файлов для доски (например, `20` Мб для `/b/`) (но это не точно).
* `-14`: Используемый "трипкод" был забанен.
* `-15`: `"Слишком длинное сообщение."`. Возникает, если длина комментария превышает максимальную для доски (например, для `/b/` это `15 000` символов).
* `-16`: `"В сообщении присутствует слово из спам листа."`.
* `-19`: При создании треда требуется прикрепить файл (вложение).
* `-20`: `"Вы ничего не запостили."`. Возникает при попытке отправить пустое сообщение.
* `-21`: Пасскод не найден.
* `-22`: Достигнуто максимально разрешённое количество запросов. Попробуйте позже.
* `-23`: Возможно, уже не используется. «Слишком короткая строка для поиска». (использовался в API поиска сообщений).
* `-24`: Некорректный id прикрепляемого стикера.
* `-25`: Прикрепляемый стикер с таким id не найден.
* `-31`: Пост не существует.
* `-41`: Доска закрыта.
* `-42`: Доступ к доске возможен только с пасскодом.
* `-50`: Слишком много постов для жалобы.
* `-51`: Вы ничего не написали в жалобе.
* `-52`: Вы уже отправляли жалобу.
* `-300`: Хз, что это. "Приложение не существует или было отключено".
* `-301`: Хз, что это. "Некорректный идентификатор приложения".
* `-302`: Хз, что это. "Идентификатор приложения истёк".
* `-303`: Хз, что это. "Неверная подпись поста с помощью идентификатора".
* `-304`: Хз, что это. "Указанный идентификатор уже был использован".
* `403`: Доступ запрещён.
* `666`: «Внутренняя ошибка». «Непредвиденная» ошибка. "Внутренняя ошибка сервера, повторите запрос позже.".
* `667`: Если API по данному URL не доступен для используемого HTTP метода (`GET`, `POST`).
</details>

## Синтаксис сообщений

HTML синтаксис сообщений:

* `<strong>...</strong>` — жирный текст.
* `<em>...</em>` — курсивный текст.
* `<b>...</b>` — жирный текст (иногда бывает и такой, если кто-то вручную пишет HTML код: например, модераторы в "закреплённых" постах).
* `<i>...</i>` — жирный текст (иногда бывает и такой, если кто-то вручную пишет HTML код: например, модераторы в "закреплённых" постах).
* `<sub>...</sub>` — нижний индекс.
* `<sup>...</sup>` — верхний индекс.
* `<span class="s">...</span>` — зачёркнутый текст.
* `<span class="u">...</span>` — подчёркнутый текст.
* `<span class="o">...</span>` — надчёркнутый текст.
* `<span class="spoiler">...</span>` — спойлер.
* `<span class="unkfunc">...</span>` — цитируемый текст (начинается с `>`).
* `<div class="quote">...</div>` — возможно, ещё один вариант цитируемого текста (хз, где я его видел).
* `<a href="..." data-thread="12345" data-num="12367">...</a>` — ссылка на сообщение (`data-thread` — ID треда, `data-num` — ID поста) (начинается с `>>`).
* Также модераторы имеют полномочия писать на чистом HTML коде (например, в "прикреплённых" сообщениях), и возможны любые теги, например: `<div/>`, `<p/>`, `<h1/>`, `<style/>`, `<script/>`, `<table/>`, `<tr/>`, `<td/>`, и тому подобные, так что любые нестандартные/невалидные теги следует игнорировать, просто показывая их содержимое (которое, в свою очередь, также может содержать нестандартные/невалидные теги).

## API

### Список постов треда

[`https://2ch.hk/<доска>/res/<номер-треда>.json`](https://2ch.hk/abu/res/42375.json)

```js
{
  // Объект типа `Board`.
  "board": Board,

  // Содержит все свойства объекта `BoardBanners`.
  ...BoardBanners,

  // id запрошенного треда.
  "current_thread": 12345,

  // id «последнего» поста в треде.
  "max_num": 12347,

  // Количество постов в треде.
  "posts_count": 123,

  // Количество файлов, прикреплённых к постам треда.
  //
  // В данном случае, включая файлы заглавного поста,
  // в отличие от `/catalog.json`, где они не включены.
  //
  // `files_count` считается неправильно,
  // как в `/catalog.json`, так и при запросе "получить посты треда".
  // И в обоих этих случаях — ещё и по-разному.
  // https://gitlab.com/catamphetamine/imageboard/blob/master/docs/engines/makaba-issues.md
  //
  "files_count": 3,

  // Количество "уникальных" (по хешу подсети) пользователей,
  // написавших комментарий в треде.
  // Игнорирует заглавный пост треда по неведомой причине:
  // автор треда посчитается только если он оставит комментарий в треде.
  "unique_posters": 7,

  // Закрыт ли тред.
  // `1`, если тред закрыт.
  "is_closed": 0,

  // Ничего не значит.
  // Просто legacy флаг, который всегда `false`,
  // потому что это API для получения "информации о треде", а не "информации о доске".
  "is_board": false,

  // Ничего не значит.
  // Просто legacy флаг, который всегда `false`.
  "is_index": false,

  // (deprecated)
  // "file_prefix", использовался для очень старых "архивных" тредов
  // (с появления архива `2016-03-06` по `2016-11-12` включительно),
  // и для тех старых тредов его требовалось подставлять к адресам
  // прикреплённых к постам файлов.
  // Например, для треда `https://2ch.hk/b/arch/2016-03-06/res/119034529.json`
  // `file_prefix` — "../", поэтому адреса всех картинок в нём преобразуются
  // из `thumb/119034529/14572604256670s.jpg` в
  // `https://2ch.hk/b/arch/2016-03-06/thumb/119034529/14572604256670s.jpg`.
  "file_prefix": "../",

  // Название треда.
  // Видимо, то же самое, что `posts[0].subject`.
  "title": "Blah blah blah",

  // «Главная» картинка этого треда.
  // Например, если в первом посте треда прикреплено несколько картинок,
  // то это будет URL одной из этих картинок.
  // Может использоваться в качестве «иконки» (thumbnail) треда.
  "thread_first_image": "/a/src/7491407/16600704660283.jpg",

  "threads": [
    {
      // Список объектов типа `Post`.
      "posts": [
        {
          // Содержит все свойства объекта `post`.
          ...Post,

          // Порядковый номер поста в треде, начиная с `1`.
          "number": 1
        },
        ...
      ]
    }
  ]
}
```

### Список постов треда, начиная с id поста

Возвращает список постов треда, имеющих id >= `<пост>`.

[`https://2ch.hk/api/mobile/v2/after/<доска>/<тред>/<пост>`](https://2ch.hk/api/mobile/v2/after/abu/42375/42375)

Ответ:

```js
{
  posts: Post[],

  // Код результата. "1" — не было ошибки (видимо).
  result: 1,

  // См. свойство `unique_posters` у объекта `Thread`.
  unique_posters: 123
}
```

### Краткая информация о треде

[`https://2ch.hk/api/mobile/v2/info/<доска>/<тред>`](https://2ch.hk/api/mobile/v2/info/abu/42375)

Ответ

```js
{
  // Код результата.
  // 1 — не было ошибки.
  "result": 1,

  // Информация о треде.
  "thread": {
    // ID треда.
    "num": 42375,

    // Количество постов в треде.
    "posts": 16,

    // Дата создания треда (unix timestamp).
    "timestamp": 1443375145
  }
}
```

Я полагаю, этот метод ни для чего не используется.

### Информация о посте

[`https://2ch.hk/api/mobile/v2/post/<доска>/<тред>`](https://2ch.hk/api/mobile/v2/post/abu/42375)

Ответ

```js
{
  // Код результата.
  // 1 — не было ошибки.
  "result": 1,

  // Объект типа `Post`.
  "post": Post
}
```

Я полагаю, этот метод ни для чего не используется.

### Список тредов доски (с сортировкой по дате «последнего» комментария: от новых к старым)

[`https://2ch.hk/<доска>/catalog.json`](https://2ch.hk/b/catalog.json)

Выводит список тредов, упорядоченных по дате "последнего" (на текущий момент) поста в них (самые новые — сверху).

При этом [пропускает](https://2ch.pm/mobi/res/2094363.html#2124716) треды без картинки, что, безусловно, является багом API Двача. Например, в разделе `/d/` данный API выдаёт пустой список тредов, потому что в разделе `/d/` прикреплять картинки запрещено. В целом, можно игнорировать данный факт, так как на Дваче на всех досках, кроме `/d/`, прикрепление картинки является обязательным при создании треда.

`threads` — список "тредов", где "тред" — первый пост треда, то есть объект типа [`Post`](#post)

```js
{
  // Объект типа `Board`.
  "board": Board,

  // Содержит все свойства объекта `BoardBanners`.
  ...BoardBanners,

  // Сортировка:
  // * "standart" — по дате «последнего» комментария (от новых к старым).
  // * "num" — по дате создания треда (от новых к старым).
  "filter": "standart",

  // Список тредов.
  "threads": [
    {
      // Количество файлов, прикреплённых к комментариям треда.
      // Не включает файлы заглавного поста по неведомой причине.
      //
      // `files_count` считается неправильно,
      // как в `/catalog.json`, так и при запросе "получить посты треда".
      // И в обоих этих случаях — ещё и по-разному.
      // https://gitlab.com/catamphetamine/imageboard/blob/master/docs/engines/makaba-issues.md
      //
      "files_count": 0,

      // Количество комментариев в треде.
      // Не включает заглавный пост по неведомой причине.
      "posts_count": 27,

      // Также содержит все свойства объекта типа `Post`.
      ...Post
    },
    ...
  ]
}
```

### Список тредов доски (с сортировкой по дате создания треда: от новых к старым)

[`https://2ch.hk/<доска>/catalog_num.json`](https://2ch.hk/b/catalog_num.json)

Ответ — такой же, как у `catalog.json` выше, с единственным отличием в том, что значение свойства `filter` — `"num"`.

Точно так же пропускает треды без картинки, как и `/catalog.json`.

### Список тредов доски ("облегченный" вариант, с количеством просмотров и "рейтингом" для "топа тредов")

[`https://2ch.hk/<доска>/threads.json`](https://2ch.hk/b/threads.json)

```js
{
  // ID доски.
  board: "b",

  // Список тредов.
  threads: [
    {
      // ID треда.
      "num": 3475861,

      // "Рейтинг" треда.
      "score": 17.8347107438,

      // Количество просмотров треда.
      "views": 2159,

      // Количество постов в треде.
      "posts_count": 374,

      // Дата создания треда (unix timestamp).
      "timestamp": 1540597950,

      // Тема треда.
      "subject": "Во что играем?",

      // Содержание первого поста треда.
      "comment": "ИТТ описываем свои похождения, ...",

      // Дата «последнего» комментария в треде (unix timestamp).
      // Или, может быть, дата «последнего» «обновления» треда, что бы это ни значило.
      // См. описание свойства `lasthit` у объекта типа `Post`.
      "lasthit": 1541028071
    },
    ...
  ]
}
```

Список не включает "закреплённые" треды: очевидно, что для них не имеет смысла считать "рейтинг", но это также означает, что для получения полного списка тредов всё равно придётся получать полный список тредов из `/catalog.json`, и не понятно тогда, зачем в этом списке у каждого треда имеется `comment`.

Я полагаю, этот метод ни для чего не используется.

### Список тредов доски (постраничный, с «последними» постами у каждого треда)

Возвращает страницу из постраничного списка тредов доски.

Особенность данного метода в том, что он возвращает также список из нескольких «последних» постов в каждом треде, что может быть использовано, например, для «обзорного» отображения первой страницы какой-либо доски «сразу» с «последними» комментариями в этих тредах.

[`https://2ch.hk/<доска>/<номер-страницы>.json`](https://2ch.hk/b/1.json)

Параметры:

* `<номер-страницы>` — `index` для первой страницы, далее `1`, `2`, ...

```js
{

  // Объект типа `Board`.
  "board": Board,

  // Содержит все свойства объекта `BoardBanners`.
  ...BoardBanners,

  // Количество постов, создаваемых на этой доске, в час.
  "board_speed": 1234,

  // Ничего не значит.
  // Просто legacy флаг, который всегда `true`,
  // потому что это API для получения "информации о доске".
  "is_board": true,

  // Ничего не значит.
  // Просто legacy флаг, который всегда `false`.
  "is_index": false,

  // Номер текущей страницы.
  "current_page": 0,

  // Ничего не означает.
  // Всегда `0`, потому что это API для получения "информации о доске",
  // а не "информации о треде".
  "current_thread": 0,

  // Список доступных страниц.
  "pages": [0, 1, 2, 3, ...],

  // Список тредов текущей страницы.
  "threads": [{
    // Количество файлов, прикреплённых к постам треда.
    // (вероятно, не включая файлы заглавного поста).
    // Можно игнорировать это значение здесь, потому что оно присутствует
    // также в первом элементе массива `posts`: `posts[0].files_count`.
    "files_count": 0,

    // `files_count` вообще считается неправильно: как в `/catalog.json`, так и при запросе "получить посты треда". И в обоих этих случаях — ещё и по-разному.
    // https://gitlab.com/catamphetamine/imageboard/blob/master/docs/engines/makaba-issues.md

    // Количество комментариев в треде (не включая заглавный пост).
    // Можно игнорировать это значение здесь, потому что оно присутствует
    // также в первом элементе массива `posts`: `posts[0].posts_count`.
    // Значение `posts_count` не верное: как здесь, так и в `posts[0].posts_count`.
    // Для получения верного значения следует прибавить `1`.
    "posts_count": 27,

    // ID треда.
    // Можно игнорировать это значение здесь, потому что оно присутствует
    // также в первом элементе массива `posts`: `posts[0].num`.
    "thread_num": 3475861,

    // «Последние» комментарии в этом треде.
    // Список объектов типа `Post`.
    "posts": Post[]
  }]
}
```

### Список досок

[`https://2ch.hk/api/mobile/v2/boards`](https://2ch.hk/api/mobile/v2/boards)

Ответ — список объектов типа [`Board`](#board).

### CAPTCHA

Ранее `2ch.hk` использовал Google ReCaptcha, однако затем перешёл на собственную `2chcaptcha`.

<!--
`2ch.hk` позволяет [выбрать капчу на свой вкус из нескольких возможных](https://www.google.com/search?q=%D0%B0%D0%BA%D1%81%D0%B8%D0%BE%D0%BC%D0%B0+%D1%8D%D1%81%D0%BA%D0%BE%D0%B1%D0%B0%D1%80%D0%B0&oq=%D0%B0%D0%BA%D1%81%D0%B8%D0%BE%D0%BC%D0%B0+%D1%8D%D1%81). Для получения списка возможных капч можно отправлять `GET` запрос на адрес `https://2ch.hk/api/captcha/settings/<id-доски>`. Пример ответа:

```js
{
  // `1` означает, что капча требуется для постинга на этой доске.
  "enabled": 1,

  // ???
  "result": 1,

  // Возможные виды капч на этой доске.
  "types": [
    {
      // Капча истекает, если её не разгадать за указанное количество секунд.
      "expires": 360,
      // Вид капчи — reCAPTCHA.
      "id": "recaptcha"
    },
    {
      // Капча истекает, если её не разгадать за указанное количество секунд.
      "expires": 360,
      // Вид капчи — Invisible reCAPTCHA.
      "id": "invisible_recaptcha"
    }
  ]
}
```

После того, как выбран вид капчи, требуется получить ID капчи для разгадывания с сервера `2ch.hk`. Для этого отправляется `GET` запрос по адресу: `https://2ch.hk/api/captcha/<выбранный-вид-капчи>/id`
-->

Сначала требуется получить ID капчи для разгадывания с сервера `2ch.hk`:

`GET` [`https://2ch.hk/api/captcha/<вид-капчи>/id`](https://2ch.hk/api/captcha/2chcaptcha/id)

<!--
Пример ответа Google Invisible ReCAPTCHA:

```js
{
  // Код ответа. "0" — "ошибка".
  result: 1,

  // Разновидность капчи.
  type: "invisible_recaptcha",

  // ID капчи.
  id: "6LdwXD4UAAAAAHxyTiwSMuge1-pf1ZiEL4qva_xu"
}
```
-->

Пример ответа для вида капчи `2chcaptcha`:

```js
{
  // Код ответа. "0" — "ошибка".
  result: 1,

  // Разновидность капчи.
  type: "2chcaptcha",

  // Разновидность символов.
  // * "numeric" — Только цифры (разгадка уравнения). Вероятно, не используется? Или используется, но иногда?
  // * "russian" — Что-то на русском (при этом на момент 22.10.2023 там стоит "russian" даже если капча — это математическое уравнение вида "2 + 2 = ?").
  input: "russian",

  // ID капчи.
  id: "08a5b59b51999b7bbdb895f7b54aecc4d98fb2f97498fb5d34e9eb16cd3be345771c7e14b5ff8cddacf7ea093a608622055ca622e63a69d6d0fa01b09c417dcd25021f",

  // ? Вероятно, не используется ?
  challenge: {
    "hash": "070fda309a47cf7e36ba4bf3a9bf92584db6e80e9f48a593df923ce6f0f1ee1799bed2b7c6fd5cd83bcd920a371f7e9d96c3085481d00cd4be2ce85d19cf01b5",
    "limit": 20000,
    "template": "0iGt3fn9geuWH5VZ%d7XUPNEUmysNkEroB"
  }
}
```

Пример ответа, когда применён ["пасскод"](#пасскод):

```js
{
  result: 2,
  type: "passcode",
  maxFiles: 4,
  maxFilesSize: 40960,
  maxImageResolution: 40960
}
```

Полученный `id` капчи используется для показа капчи пользователю для её разгадывания.

<!--
### Google ReCAPTCHA

Начиная с 1-ого июля 2021-ого, все капчи от Google были отключены, и доступна лишь `2chcaptcha`.

[Документация по Invisible reCAPTCHA](https://developers.google.com/recaptcha/docs/invisible)

### 2chcaptcha
-->

`2chcaptcha` истекает через 300 секунд, после чего требуется её обновить.

Разгадывать капчу `2chcaptcha` можно только один раз, после чего требуется её обновить.

Для показа картинки капчи используйте URL:

`https://2ch.hk/api/captcha/2chcaptcha/show?id=<id-капчи>`

Возвращает HTTP Status 404 в случае несуществующего ID капчи.

## Постинг (API)

Постинг на `2ch.hk` требует либо наличия "пасскода", либо разгадывания капчи.

### Отправка сообщения в тред

HTTP `POST` запрос вида `multipart/form-data` по адресу `https://2ch.hk/user/posting?nc=1`

Параметры:

* `task`: `"post"`.
* `board`: ID доски. Пример: `"b"`.
* `thread`: ID треда. Пример: `194336016`.
* `op_mark`: Галка "ОП треда" в форме создания треда или отправке комментария. Подробности — в подразделе ["ОП треда"](#оп-треда). Пример в случае проставленной галки "ОП треда": `1`. Можно оставить пустым.
* `usercode`: Если пользователь входит под "пасскодом", ему выдаётся случайно сгенерированный хеш. Этот хеш (не сам "пасскод") следует записывать в данный параметр при отправке сообщения или треда. Подробнее о "пасскодах" можно прочесть в разделе "Пасскод". Можно оставить пустым.
* `code`: Судя по всему, не используется. Оставить пустым.
* `captcha_type`: Выбранный вид капчи. Возможные значения:
  * `recaptcha` — Google ReCAPTCHA.
  * `invisible_recaptcha` — Google Invisible ReCAPTCHA.
  * `recaptcha3` — Google ReCAPTCHA 3.
  * `2chcaptcha` – "Самописная" капча Двача.
  * `appid` — Постинг без капчи с "ключом приложения". Подробности в разделе "Новое API" в подразделе ["Постинг без капчи"](#постинг-без-капчи).
  * `passcode` — Постинг без капчи с ["пасскодом"](#пасскод).
  * `nocaptcha` — Если капча не требуется. Например, капча может не требоваться вообще на какой-либо доске, или пользователю может быть разрешено некоторое время постить без капчи, потому что он уже прошёл проверку на "робота".
* `email`: Электронная почта пользователя. Примеры: `"admin@example.com"`, `"sage"`. На некоторых досках (например, `/b/`) в форме отправки сообщения нет поля "Имя", и имя можно вписать в поле "Email", например: `"Анон"`. Можно оставить пустым.
* `name`: Имя пользователя. Пример: `"Анон"`. На некоторых досках (например, `/b/`) в форме отправки сообщения нет поля "Имя". Можно оставить пустым.
* `subject`: Тема сообщения. На некоторых досках (например, `/b/`) в форме отправки сообщения нет поля "Тема". Можно оставить пустым.
* `icon`: На некоторых досках можно прикреплять "значки" к отправляемым сообщениям. Например, в `/po/` разрешены значки политической ориентации: "Коммунизм", "Родноверие", и т.п. Возможные значения: число (ID значка), или `-1`, если значок не выбран.
* `comment`: Текст отправляемого комментария. Можно оставить пустым.
* `file[]`: Список вложений, прикреплённых к отправляемому комментарию. Двоичные данные (стандартная отправка файлов в составе HTTP `POST` запроса — массив объектов типа [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File)). Можно оставить пустым.
<!-- * `formimages[]` — Замена `file[]`? Не используется? -->
* `oekaki_image`: Видимо, используется для отправки рисунков ["оэкаки"](http://lurkmore.to/%D0%9E%D1%8D%D0%BA%D0%B0%D0%BA%D0%B8). Можно оставить пустым.
* `oekaki_metadata`: Видимо, используется для отправки рисунков ["оэкаки"](http://lurkmore.to/%D0%9E%D1%8D%D0%BA%D0%B0%D0%BA%D0%B8). Можно оставить пустым.
* `2chaptcha_id`: (видимо, только в случае использования какой-то старой "самописной" капчи Двача; видимо, не относится к "новой" самописной капче Двача, которая `2chcaptcha`) ID капчи, который был получен ранее с сервера `2ch.hk`.
* `g-recaptcha-response`: (только в случае использования капчи reCAPTCHA) Ответ разгаданной капчи reCAPTCHA.
* `2chcaptcha_id`: (только в случае использования капчи `2chcaptcha`) ID капчи `2chcaptcha`, который был получен ранее с сервера `2ch.hk`.
* `2chcaptcha_value`: (только в случае использования капчи `2chcaptcha`) Ответ разгаданной капчи `2chcaptcha`.
* `makaka_id`: Судя по всему, не используется. Оставить пустым.
* `makaka_answer`: Судя по всему, не используется. Оставить пустым.
* `file[]`: Судя по всему, не используется. Оставить пустым.

Параметры передаются в формате «формы» (он же `multipart/form-data`, он же `FormData`).

Если пользователь вошёл с "пасскодом", то к HTTP запросу требуется приложить куки `passcode_auth`. Если в параметрах запроса указать `usercode`, но при этом не приложить куки `passcode_auth`, то сервер вернёт ошибку с кодом `666` и текстом `Внутренняя ошибка сервера, повторите запрос позже.`.

Пример ответа без ошибки:

```js
{
  "result": 1,
  "num": 1234567890
}
```

Где:

* `num` — ID отправленного комментария

Пример ответа с ошибкой:

```js
{
  "result": 0,
  "error": Error
}
```

Где:

* `error` — Объект типа [`Error`](#error)

При возникновении ошибки "статус" HTTP ответа останется таким же — `200` — что не корректно, и не соответствует спецификации HTML REST API, но не является препятствием. Исключение: ошибка "тред не найден", при которой "статус" HTTP ответа будет `404`.

### Создание треда

Делается всё то же самое, что и для отправки комментария в тред, с тем отличием, что поле формы `thread` в данном случае следует установить в значение `0`, а также можно добавить поле формы `tags` для добавления "тегов" к создаваемому треду.

Пример ответа без ошибки:

```js
{
  "result": 1,
  "thread": 194366740
}
```

Где:

* `thread` — ID созданного треда.

## Жалобы

Доступен API для отправки жалобы на тред или комментарий.

`POST` `/user/report` с параметрами:

Параметры:

* `board` — ID доски.
* `thread` — ID треда.
* `post` — ID комментария. В случае жалобы на тред совпадает с ID треда.
* `comment` — Текст жалобы.

Параметры передаются в формате «формы» (он же `multipart/form-data`, он же `FormData`).

Пример ответа:

```js
{
  "result": 1
}
```

## Голосование

На некоторых досках (например, `/po/`) можно голосовать за посты. В этом случае в описании доски будет свойство `enable_likes: true`. У объекта Post в этом случае добавятся поля `likes: число` и `dislikes: число`.

`GET` (не опечатка) `https://2ch.hk/api/<действие>`

где `<действие>` — это либо `like`, либо `dislike`.

Параметры:

* `board` — ID доски
* `num` — ID поста

Пример ответа без ошибки:

```js
{
  "result": 1
}
```

Пример ответа с ошибкой (пользователь уже голосовал ранее за этот пост):

```js
{
  "result": 0,
  "error": {
    "code": -4,
    "message": "Постинг запрещён."
  }
}
```

## Пасскод

"Пасскоды" имеют вид строки (случайным образом сгенерированный "хеш" код), и представляют собой форму добровольного пожертвования сайту `2ch.hk` в обмен на бо́льшие удобства (возможность постинга без капчи, возможность прикреплять большие файлы, возможность постить с зарубежных прокси/VPN).

Для [входа](https://2ch.hk/2ch/) с "пасскодом" отправляется `POST` запрос вида `multipart/form-data` по адресу `https://2ch.hk/user/passlogin` с параметрами:

* `passcode` — пасскод

Результат:
* При ошибке — Возвращает ответ HTTP status 500.
* При успешном применении пасскода — Перенаправляет на доску `/b/`.

При успешном применении пасскода сервер `2ch.hk` проставляет (не http-only, не secure) cookie `passcode_auth` со случайно сгенерированным "хешем". Значение этой cookie следует каким-то образом прочесть и сохранить, и в дальнейшем передавать его в поле `usercode` при отправке сообщения или создании треда: так сервер `2ch.hk` определит, что сообщение отправлено с "пасскодом".

Код ответа при успешном применении пасскода, на данный момент, `303`, с перенаправлением на доску `/b/`.

Для "выхода" из-под "пасскода" отправляется `POST` запрос вида `multipart/form-data` по адресу `https://2ch.hk/user/passlogout`.

Результат: сервер `2ch.hk` стирает cookie `passcode_auth` и перенаправляет на доску `/b/`.

<!--
Для [входа](https://2ch.hk/2ch/) с "пасскодом" отправляется `GET` запрос по адресу `https://2ch.hk/makaba/makaba.fcgi` с параметрами:

* `task` — `"auth"`
* `usercode` — пасскод
* `json` — `1`

Пример ответа без ошибки:

```js
{
  // Максимальное количество прикрепляемых файлов.
  // Для "премиальных" пасскодов это количество увеличено.
  files: 4

  // Максимальный размер прикрепляемого файла.
  // Для "премиальных" пасскодов этот размер увеличен.
  files_size: 40960

  // Какой-то "хеш".
  hash: "5c5e915ae9b..."

  // Максимальная ширина/высота прикрепляемой картинки.
  max_image_resolution: 40960

  // Код ответа. Если не возникло ошибки — `1`.
  result: 1

  // Срок годности "пасскода".
  type: "year"
}
```

После успешного применения пасскода сервером `2ch.hk` проставляется (не http-only, не secure) cookie `passcode_auth` со случайно сгенерированным "хешем" (не совпадающим с `hash`, полученным в ответе). Значение этой cookie следует каким-то образом прочесть и сохранить, и в дальнейшем передавать его в поле `usercode` при отправке сообщения или создании треда: так сервер `2ch.hk` определит, что сообщение отправлено с "пасскодом".

Пример ответа с ошибкой:

```js
{
  // Текст ошибки.
  description: "Ваш код не существует!",

  // Код ошибки.
  // `4` — Нет такого пасскода.
  error: 4,

  // Код ответа. Если возникла ошибка — `0`.
  result: 0
}
```

Для "выхода" из-под "пасскода" отправляется `POST` запрос по тому же адресу `https://2ch.hk/makaba/makaba.fcgi` с параметрами:

* `task` — `"logout"`
* `json` — `1`

Пример ответа:

```js
{
  Error: null,
  Status: "OK"
}
```
-->

### Поиск по всем постам доски

Поиск производится по подстроке в теме или тексте поста.

`POST` `https://2ch.hk/user/search?json=1`

Параметры:

* `board: "id доски"`
* `text: "искомый текст"`

Параметры передаются в формате «формы» (он же `multipart/form-data`, он же `FormData`).

Поиск прекращается на 100 результатах поиска.

Также имеется ["Поиск по архиву"](#поиск-по-архиву), но он возвращает результат в формате HTML.

<details>
<summary>Пример ответа. <code>board: "a"</code> <code>text: "гештальт"</code></summary>

######

```js
{
  // Объект типа `Board`.
  "board": Board,

  // Запрос поиска.
  "search": "гештальт",

  // Список объектов типа `Post`.
  "posts": [
    Post,
    ...
  ]
}
```
</details>

<details>
<summary>Пример ответа "Не найдено". <code>board: "a"</code> <code>text: "перцепция"</code></summary>

######

```js
{
  // Объект типа `Board`.
  "board": Board,

  // Список объектов типа `Post`.
  "posts": null
}
```
</details>

## Прочее

### Архив

На каждой доске (судя по всему) есть "архив", куда помещаются все "потонувшие" треды. Судя по всему, "архив" появился в 2016-ом году, и с тех пор хранит каждый "потонувший" тред, в отличие от 4chan-а, который "архивирует" не все доски, а только некоторые из них, и на тех, которые "архивирует", хранит максимум 3000 тредов, и не более трёх дней.

Не на всех досках доступен архив: на некоторых досках (даже если они "юзердоски") он доступен (`law`, `who`, `kz`), а на некоторых досках он не доступен (например, `fi`, `ld`).

Также, в отличие от 4chan-а, на котором все URL-ы остаются неизменными при попадании треда в "архив", на Дваче при перемещении треда в архив меняется его URL. Например, был — `/b/res/119034838.html`, стал — `/b/arch/2016-03-06/res/119034838.html`. Для файлов `*.html` работает перенаправление `301 Moved Permanently` из `/b/res/12345.html` в `/b/arch/res/12345.html`, а оттуда — перенаправление `302 Found` в `/b/arch/2016-03-06/res/12345.html`, где `2016-03-06` — это дата помещения треда в архив. Для файлов `*.json` не работает первое из этих перенаправлений: `/b/res/12345.json` просто выдаст `404 Not Found`, поэтому при получении ответа `404 Not Found` для `*.json`-а треда, всегда следует перепосылать запрос с префиксом `/arch/`, и в этом случае, если тред был перемещён в архив, произойдёт перенаправление `302 Found` в `/b/arch/2016-03-06/res/12345.json`.

При этом в самих данных треда (`*.json`) не добавляется никакого поля в стиле `archived: 1` или `archived_on: 1234567890`. Но, при этом, в архивных тредах с появления архива `2016-03-06` по `2016-11-12` включительно проставлялось свойство `file_prefix: "../"`, а адреса картинок при этом были не абсолютными (`/b/thumb/12345/67890s.jpg`), а относительными (`thumb/12345/67890s.jpg`), поэтому при просмотре этих совсем старых архивных тредов требуется вручную преобразовывать адреса картинок, предварительно каким-то образом заполучив дату архивирования треда (например, из "конечного" адреса `*.json`-а треда после автоматического перенаправления).

Не все треды архивируются, а только те, в которых есть как минимум 50 комментариев. Это позволяет отбрасывать "мусорные" и "никому не нужные" треды из истории. Также есть задержка (например, около часа) между тем как тред "потонул" и его появлением в архиве.

#### Поиск по архиву

Поиск производится по заголовку треда.

`POST` `https://2ch.hk/api/archive/search`

```js
{
  board: "<доска>",
  query: "<подстрока для поиска>" // без пробелов, без цифр.
}
```

Ответ — документ `HTML` (даже если ошибка).

Выдаётся максимум 21 результат, каждый раз в случайном порядке.

<details>
<summary>Пример ответа. <code>board: "b"</code> <code>query: "технологии"</code></summary>

######

```html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
  <head>
    <title>
      Архив
    </title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link href="/newtest/resources/images/makaba.ico" rel="shortcut icon">
    <link href="/makaba/templates/css/normalize.css" type="text/css" rel="stylesheet">
    <link href="/makaba/templates/css/melchizedek.css?2" type="text/css" rel="stylesheet">
    <link href="/makaba/templates/css/archive.css" type="text/css" rel="stylesheet">
  </head>

  <body>
    <header class="header">
      <a class="logo" href="/">Два.ч</a>
    </header>

    <div class="box">
      <div class="box-header">
        Архив /b /
        <form action="/api/archive/search" method="POST" class="arch-searchform" enctype="multipart/form-data">
          <input type="hidden" name="board" value="b">
          <input type="text" name="query" value="" placeholder="текст">
          <input type="submit" value="Найти">
        </form>
      </div>

      <div class="box-data">

        <a href="/b/arch/2021-01-11/res/237499388.html">Анон, каким ты видишь мир, через 50-100-200-500-1000 лет?
          Какие технологии, какие города, чего достигло человечество. Политику не обсуждаем</a>
        <span class="arch-threadnum">( 237499388 )</span>
        <hr class="dotted">

        <a href="/b/arch/2020-05-03/res/219328543.html">Лучшие технологии - Япония. Лучшие фильмы - Япония.</a>
        <span class="arch-threadnum">( 219328543 )</span>
        <hr class="dotted">

        <a href="/b/arch/2021-01-30/res/238962974.html"> ИЗУЧАЛ ПРОГРАММИРОВАНИЕ, МАТЕМАТИКУ, ТЕХНОЛОГИИ,</a>
        <span class="arch-threadnum">( 238962974 )</span>
        <hr class="dotted">

        <a href="/b/arch/2017-05-03/res/152228565.html">Подумываю вкатиться в IT. Какие нынче языки и технологии
          востребованы, что учить и, главное, с чего начать?</a> <span class="arch-threadnum">( 152228565 )</span>
        <hr class="dotted">

        <a href="/b/arch/2017-09-20/res/161433184.html">ТЕХНОЛОГИИ И МЕДИА, 19 СЕН, 22:02 ФСБ и Роскомнадзор</a>
        <span class="arch-threadnum">( 161433184 )</span>
        <hr class="dotted">

        <a href="/b/arch/2019-03-10/res/192702576.html">Лучшие технологии - Япония. Лучшие фильмы - США.</a>
        <span class="arch-threadnum">( 192702576 )</span>
        <hr class="dotted">

        <a href="/b/arch/2018-01-04/res/168087077.html">Лучшие технологии - США. Лучшие фильмы - США.</a>
        <span class="arch-threadnum">( 168087077 )</span>
        <hr class="dotted">

        <a href="/b/arch/2016-07-17/res/132012985.html">А что если взять наши современные технологии,</a>
        <span class="arch-threadnum">( 132012985 )</span>
        <hr class="dotted">

        <a href="/b/arch/2018-12-01/res/187441268.html">Лучшие технологии - Россия. Лучшие фильмы - Россия.</a>
        <span class="arch-threadnum">( 187441268 )</span>
        <hr class="dotted">

        <a href="/b/arch/2019-05-12/res/196196046.html">Почему технологии перестали развиваться, и вместо ощутимого
          прогресса мы имеем каплевидный вырез в смартфоне? </a> <span class="arch-threadnum">( 196196046 )</span>
        <hr class="dotted">

        <a href="/b/arch/2019-10-11/res/205269957.html">Заканчиваю шарагу МГУ, биотехнологии, есть нихуёвые</a>
        <span class="arch-threadnum">( 205269957 )</span>
        <hr class="dotted">

        <a href="/b/arch/2018-11-13/res/186483997.html">Почему кроме смартфонов за 20 лет не появилось ни одной
          новой потребительской технологии? </a> <span class="arch-threadnum">( 186483997 )</span>
        <hr class="dotted">

        <a href="/b/arch/2016-12-20/res/142531251.html">Программист в треде. Могу пояснить за работу программистом и
          технологии, в рамках того, что сам знаю. Задавайте свои ответы.</a>
        <span class="arch-threadnum">( 142531251 )</span>
        <hr class="dotted">

        <a href="/b/arch/2017-05-23/res/153658212.html">Продвинутые технологии смогут создавать города за 1 день
          http:&amp;#47;&amp;#47;oskarstalberg.com&amp;#47;game&amp;#47;CityGenerator&amp;#47; </a>
        <span class="arch-threadnum">( 153658212 )</span>
        <hr class="dotted">

        <a href="/b/arch/2017-10-07/res/162454890.html">Смотрите, пацаны, все современные технологии</a>
        <span class="arch-threadnum">( 162454890 )</span>
        <hr class="dotted">

        <a href="/b/arch/2016-10-25/res/138543605.html">Все технологии принадлежат паразитам, технологии</a>
        <span class="arch-threadnum">( 138543605 )</span>
        <hr class="dotted">

        <a href="/b/arch/2020-11-25/res/234005159.html">Я вот одного не понимаю. 21 век век, нанотехнологии,</a>
        <span class="arch-threadnum">( 234005159 )</span>
        <hr class="dotted">

        <a href="/b/arch/2018-10-23/res/185226008.html">Лучшие технологии - США. Лучшие фильмы - США.</a>
        <span class="arch-threadnum">( 185226008 )</span>
        <hr class="dotted">

        <a href="/b/arch/2017-04-28/res/151943051.html">1. Какие технологии используются на 2ch.hk ? 2. Что</a>
        <span class="arch-threadnum">( 151943051 )</span>
        <hr class="dotted">

        <a href="/b/arch/2017-05-19/res/153428789.html">Лучшие технологии - Россия. Лучшие фильмы - Россия.</a>
        <span class="arch-threadnum">( 153428789 )</span>
        <hr class="dotted">

        <a href="/b/arch/2017-10-16/res/163075308.html">Взлетающих технологий тхред Обсуждаем, как технологии</a>
        <span class="arch-threadnum">( 163075308 )</span>
        <hr class="dotted">

        <br>
        <div class="pager">
          [<a href="/b/arch/">0</a>]
        </div>
      </div>
    </div>
  </body>
</html>
```
</details>

<details>
<summary>Пример ошибки "Запрос не валиден". <code>board: "b"</code> <code>query: "нано технологии"</code></summary>

######

```html
<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <title>Ошибка</title>
    <link href="/makaba/templates/css/normalize.css" type="text/css" rel="stylesheet">
    <link href="/makaba/templates/css/melchizedek.css" type="text/css" rel="stylesheet">
  </head>
  <body>
    <header class="header">
      <a class="logo" href="/makaba/stickers/editor">Два.ч</a>
    </header>
    <div class="error-body">
      <div class="error-body-error">
        <div class="error-body-error-text">
          <h1 class="error-title">Ошибка</h1>
          <p>Ваш запрос не валиден.</p>
        </div>
        <div class="error-body-error-links">
          <a class="button" href="/makaba/stickers/">Главная</a>
          <a class="button" href="#" onclick="window.history.back();">Назад</a>
        </div>
      </div>
    </div>
  </body>
</html>
```
</details>

<details>
<summary>Пример ошибки "Доска не найдена". <code>board: "xxxxx"</code> <code>query: "технологии"</code></summary>

######

```html
<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <title>Ошибка</title>
    <link href="/makaba/templates/css/normalize.css" type="text/css" rel="stylesheet">
    <link href="/makaba/templates/css/melchizedek.css" type="text/css" rel="stylesheet">
  </head>

  <body>
    <header class="header">
      <a class="logo" href="/makaba/stickers/editor">Два.ч</a>
    </header>
    <div class="error-body">
      <div class="error-body-error">
        <div class="error-body-error-text">
          <h1 class="error-title">Ошибка</h1>
          <p>Доска не существует.</p>
        </div>
        <div class="error-body-error-links">
          <a class="button" href="/makaba/stickers/">Главная</a>
          <a class="button" href="#" onclick="window.history.back();">Назад</a>
        </div>
      </div>
    </div>
  </body>
</html>
```
</details>

<details>
<summary>Пример ответа "Не найдено". <code>board: "b"</code> <code>query: "визуально-слуховой"</code></summary>

######

```html
<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <title>Ошибка</title>
    <link href="/makaba/templates/css/normalize.css" type="text/css" rel="stylesheet">
    <link href="/makaba/templates/css/melchizedek.css" type="text/css" rel="stylesheet">
  </head>

  <body>
    <header class="header">
      <a class="logo" href="/makaba/stickers/editor">Два.ч</a>
    </header>
    <div class="error-body">
      <div class="error-body-error">
        <div class="error-body-error-text">
          <h1 class="error-title">Ошибка</h1>
          <p>Ничего не найдено.</p>
        </div>
        <div class="error-body-error-links">
          <a class="button" href="/makaba/stickers/">Главная</a>
          <a class="button" href="#" onclick="window.history.back();">Назад</a>
        </div>
      </div>
    </div>
  </body>
</html>
```
</details>

### Разделы 18+

Доступ в разделы "18+" разрешён только для запросов, имеющих cookie `usercode_auth`. Эта cookie устанавливается сама при ответе в любой тред на любой доске. Пример значения: `1234567890abcdef7b4d603f0d8f0035`.

### ОП треда

У форм отправки сообщения в тред и создания треда есть галка "ОП треда". Эта галка может быть использована автором треда для публичного подтверждения своего авторства при последующих ответах в такой тред.

При создании треда с галкой "ОП треда" сервер `2ch.hk` отправляет в ответ cookie под названием `op_<id-доски>_<id-треда>`, значением которой является случайно-сгенирированный хеш. Пример: название — `op_mg_412244`, значение — `5a2ac8be2534b02e818cc4020d69677f`.

При отправке комментария в такой тред можно будет опять проставить галку "ОП треда", и в этом случае сервер будет искать в запросе cookie под названием `op_<id-доски>_<id-треда>`, и если такая cookie есть, то сравнивать её значение с тем хешем, который был случайно-сгенерирован при создании треда. Если значение cookie совпадает с хешем, то это позволяет говорить о подлинности автора треда, и в таком случае сервер пометит данный комментарий флажком "ОП треда".

### Флаги

Если у доски `"enable_flags": 1` (например, на доске `/int/`), то у всех постов будет поле `icon`:

```js
{
  "icon": "<img hspace=\"3\" src=\"/flags/KR.png\" border=\"0\" />"
}
```

Есть пара странных флагов: `"A1.png"` и `"A2.png"`. Возможно, они для обозначения [Tor](https://ru.wikipedia.org/wiki/Tor)'а.

### Значки

На некоторых досках (например, `/po/`) при отправке сообщения можно выбрать значок, который будет прикреплён к отправляемому сообщению (ID значка — число). ID значка можно взять из поля `icons` JSON объекта с данными доски. Пример:

```js
{
  "icon": "<img hspace=\"3\" src=\"/icons/logos/gay.png\" title=\"ЛГБТ\" border=\"0\" />"
}
```

Один раз я видел исключение, когда это была не `.png` картинка. Этим исключением был значок Крыма: `krym.gif`.

### ID пользователей

На некоторых досках (например, `/po/`) показываются ID пользователей (являющиеся хешами их IP адресов). Хеш IP адреса будет представлен как в виде цвета автора сообщения, так и в виде самого случайно сгенерированного имени автора сообщения (у автора основного сообщения треда не будет цвета и имени). Примеры:

```js
{
  "name": "Аноним&nbsp;ID:&nbsp;Heaven"
}
```

```js
{
  "name": "Аноним&nbsp;ID:&nbsp;<span id=\"id_tag_7ab0a33a\" style=\"color:rgb(116,48,218);\">Насмешливый&nbsp;Обеликс</span>"
}
```

### Автогенерирование заголовка треда

На некоторых досках (например: `/b/`, `/rf/`) зачем-то нет поля "Темы" (заголовка треда) при создании треда или отправке сообщения. В этих случаях заголовок треда зачем-то генерируется сама. Судя по всему, алгоритм какой-то такой: все `<br>`'ы заменяются на пробелы, а все остальные теги — убираются.

```js
comment.replace(/<br>/g, ' ').replace(/<.+?>/g, '')
```

### Идентификация ОП

При создании темы можно ставить галку "ОП". В этом случае система запишет, что тред создан пользователем с таким-то хешем IP адреса. Если при ответе в такой тред снова поставить галку "ОП", и в случае, если хеш IP адреса отвечающего совпадёт с хешем IP адреса создавшего тред, то у поста будет поле `"op": 1`. Таким образом "ОП" (original poster) (автор треда) может идентифицировать себя в треде.

### Email

Автор сообщения может вписать в поле "Имя" свою почту, и тогда у поста будет поле `"email": "mailto:..."` с этим электронным адресом. Если в поле "Имя" было вписано ["sage"](https://knowyourmeme.com/memes/sage) (также называемая "сажей"), то у поста будет поле `"email": "mailto:sage"`, и такой пост не будет "бампать" (поднимать) тред.

### Трипкоды

Поле `trip` у поста.

"Трипкоды" привелегированных пользователей:

* `"!!%adm%!!"` — Администратор.
* `"!!%mod%!!"` — Модератор.

Обычные пользователи (т.н. "трипфаги") также могут использовать "трипкоды" для самоидентификации. Пример: `"!!5pvF7WEJc."`.

### Стикеры.

Список стикеров - https://2ch.hk/makaba/stickers/

Добавить свой стикер пак - https://2ch.hk/makaba/stickers/editor

После создания стикер пака и загрузки изображений, необходимо нажать "Активировать", чтобы стикер пак был доступен всем для поиска.

Стикер паки владелец может обновлять, добавлять и удалять из них стикеры, а также удалить вовсе.
Измененный пак обновляется автоматически раз в сутки или можете обновить вручную, если видите что в нем появились новые изображения, нажав в окошке со стикерами U.

Стикер вызывается через кнопку S в окошке поста.

Один пост = один стикер. Можно добавлять .gif, которые автоматически проигрываются в превью. .png автоматически отображаются прозрачными.

Стикеры доступны как для анонов (можно использовать до 5 паков), так и для пасскодобояр (без лимита).

Создавать паки могут абсолютно все.

Для загрузки изображение PNG/GIF должно вписываться в квадрат 512x512 (одна сторона — от 200 до 512 пикселей, другая — 512 или меньше, но не менее 50)

## "Новый" API

["Новое" мобильное API](https://2ch.hk/mobi/res/2094363.html)

### Список постов в треде начиная с поста

`https://2ch.hk/api/mobile/v2/info/{доска}/{id-треда}/{id-поста}`

```js
{
  "result": 1, // Код ответа. "0" — "ошибка".
  "unique_posters": 258, // Количество уникальных IP-адресов комментаторов.
  "posts": Post[] // Список постов.
}
```

<details>
<summary>Примеры ошибок</summary>

#####

```js
{
  "error": {
    "code": -31,
    "message": "Пост не существует."
  },
  "result": 0
}
```

```js
{
  "error": {
    "code": -3,
    "message": "Тред не существует."
  },
  "result": 0
}
```

```js
{
  "error": {
    "code": -2,
    "message": "Доска не существует."
  },
  "result": 0
}
```
</details>

### Информация о треде

`https://2ch.hk/api/mobile/v2/info/{доска}/{id-треда}`

```js
{
  "result": 1, // Код ответа. "0" — "ошибка".
  "thread": {
    "num": 7372049, // ID треда.
    "posts": 1247, // Количество постов в треде.
    "timestamp": 1633068105 // Дата создания треда.
  }
}
```
<details>
<summary>Примеры ошибок</summary>

#####

```js
{
  "error": {
    "code": -3,
    "message": "Тред не существует."
  },
  "result": 0
}
```

```js
{
  "error": {
    "code": -2,
    "message": "Доска не существует."
  },
  "result": 0
}
```
</details>

### Получение поста

ID поста уникален в пределах доски, поэтому пост можно запрашивать и без указания ID треда.

`https://2ch.hk/api/mobile/v2/post/{доска}/{id-поста}`

```js
{
  "result": 1, // Код ответа. "0" — "ошибка".
  "post": Post // Пост.
}
```
<details>
<summary>Примеры ошибок</summary>

#####

```js
{
  "error": {
    "code": -31,
    "message": "Пост не существует."
  },
  "result": 0
}
```

```js
{
  "error": {
    "code": -2,
    "message": "Доска не существует."
  },
  "result": 0
}
```
</details>

### Постинг без капчи

Для разработчиков мобильных приложений существует возможность получения пары из открытого и закрытого ключей, с помощью которых можно постить без капчи (только оставлять комментарии в существующих тредах, не создавать новые треды). Для получения ключей отправьте письмо по адресу [admin@2ch.hk](mailto:admin@2ch.hk) с темой "Получение ключа для приложения" и ссылкой на ваше приложение.

Данные ключи представляется возможным использовать только при компиляции приложения в некий недекомпилируемый исполняемый файл, иначе такие ключи быстро "вытащат" и используют для ["вайпа"](https://lurkmore.to/%D0%92%D0%B0%D0%B9%D0%BF).

Для постинга с ключами сначала получите параметр `app_response_id` для отправки поста:

`https://2ch.hk/api/captcha/app/id/<открытый-ключ>?board=<доска>&thread=<id-треда>`

Параметры `board` и `thread` — необязательные, но [рекомендуется](https://2ch.hk/mobi/res/2094363.html#2097934) их указывать, так как на их основе, а также на основе различных намеренно недокументированных cookie, "отслеживающих" активность пользователя, может быть принято решение о включении "адаптивной" капчи для пользователя — временном включении режима `nocaptcha` если пользователь уже проходил проверку на "робота". Хз, что они имели ввиду под этим, потому что капча и так не требуется при постинге из приложения с ключами.

Пример ответа:

```js
{
  result: 1, // Код ответа. "0" — "ошибка".
  type: "appid", // Тип капчи.
  id: "...", // Полученный `app_response_id`.
  expires: 180 // Через сколько секунд истечёт `app_response_id`.
}
```

По умолчанию, срок жизни `app_response_id` — 3 минуты (180 секунд).

Далее вычислите `app_response` — `sha256(app_response_id + '|' + закрытый-ключ)` — который добавьте в качестве параметра в запрос отправки поста (см. ниже) вместе с параметром `app_response_id`. Никакие капчи при этом, соответственно, добавлять в запросе отправки поста не требуется, а параметр `captcha_type` отправьте со значением `"appid"`.

### Жалоба

`POST` `https://2ch.hk/user/report`

Параметры:

* `board` — Доска.
* `thread` — ID треда.
* `post` — Список ID комментариев.
* `comment` — Текст жалобы.

Пример ответа:

```js
{
  result: 1 // Код ответа. "0" — "ошибка".
}
```

Пример ответа с ошибкой:

```js
{
  result: 0, // Код ответа. "0" — "ошибка".
  error: {
    code: -2, // Код ошибки.
    message: "Доска не существует." // Или что-нибудь типа того.
  }
}
```

### Вход с пасскодом

Хз, как этот метод используется и зачем он.

`POST` `https://2ch.hk/user/passlogin`

Параметры:

* `passcode` — Пасскод.

Параметры передаются в формате «формы» (он же `multipart/form-data`, он же `FormData`).

Пример ответа:

```js
{
  result: 1, // Код ответа. "0" — "ошибка".
  passcode: {
    type: "...", // Какой-то тип чего-то.
    expires: ... // Через сколько секунд истечёт `app_response_id`.
  }
}
```

### Голосование

"Действие" — `like` или `dislike`.

`GET` `https://2ch.hk/api/<действие>`

Параметры:

* `board` — ID доски.
* `num` — ID поста.

Пример ответа:

```js
{
  // Код ответа. "0" — "ошибка".
  result: 1
}
```

## Ссылки

* https://2ch.hk/api.yml — Официальная спецификация API в формате OpenAPI.
* https://2ch.hk/abu/res/42375.html — Краткий список (не всех) доступных методов API.