SthephanShinkufag / Dollchan-Extension-Tools

The best way to browse imageboards
https://dollchan.net/extension/
MIT License
368 stars 67 forks source link

Добавить поддержку 0chan.hk #1053

Closed Kagami closed 6 years ago

Kagami commented 7 years ago

https://0chan.hk/ Новый, подозреваю что нестандартный движок.

0x384c0 commented 7 years ago

Там вообще самописный движок. Заебeтесь интегрироваться с ним .

SthephanShinkufag commented 7 years ago

Проблема в том, що там контент генерируется внутренними скриптами. Они грузят json, и с него уже строят страницу. При загрузке страницы же куклоскрипт видит дырку от бублика вместо контента. Ладно, фиг с ним, це я победил с помощью костыля, откладывая исполнение куклы. Мало того, структура постов и тредов очень корявая, некоторые контейнеры вообще бкз классов и id, и структура эта различная на странице и в треде. Черт ногу сломит, в общем. Ну да ладно.

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

Пикрелейтед:

2017-04-03_12-38-06

Дифф для поддержки 0chan.hk:

@@ -13132,10 +13132,34 @@ function getImageBoard(checkDomains, checkEngines) {
            return false;
        }
    }
    ibDomains['0chan.eu'] = _0chanEu;

+   class _0chanHk extends BaseBoard {
+       constructor(prot, dm) {
+           super(prot, dm);
+           this.nul = true;
+
+           this.cReply = 'block post';
+           this.qDForm = '#content > div > .threads-scroll-spy + div, .threads > div:first-of-type';
+           this.qOPost = '.post-op';
+           this.qPostRef = '.post-header';
+           this.qPostMsg = '.post-body-message';
+           this.qRPost = '.block.post:not(.post-op)';
+       }
+       get qThread() {
+           return 'div[style="margin-top: 20px; margin-bottom: 40px;"] > div, .thread > div';
+       }
+       getPNum(post) {
+           return +$q('a[name]', post).name;
+       }
+       getTNum(op) {
+           return +$q('a[name]', op).name;
+       }
+   }
+   ibDomains['0chan.hk'] = _0chanHk;
+
    class _2chan extends BaseBoard {
        constructor(prot, dm) {
            super(prot, dm);

            this.qDForm = 'form:not([enctype])';
@@ -15742,13 +15766,19 @@ function* runMain(checkDomains, cfgPromise) {
    }
    if(!aib) {
        aib = getImageBoard(checkDomains, true);
    }
    let formEl = $q(aib.qDForm + ', form[de-form]');
+   console.log('Trying to run');
    if(!formEl) {
+       if(aib.nul && !$id('app').hasChildNodes()) {
+           console.log('Delform not found');
+           setTimeout(async(runMain.bind(null, checkDomains, cfgPromise)), 2e3)
+       }
        return;
    }
+   console.log('Delform loading success!');
    Logger.log('Imageboard check');
    if(!locStorage) {
        if(!checkStorage()) {
            return;
        }
SthephanShinkufag commented 7 years ago

Подозреваю, что дело в

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">

Тег <html> на нульчане не имеет атрибута xmlns.

Kagami commented 7 years ago

У меня всё нормально в последнем хромиуме на линуксе отсюда https://download-chromium.appspot.com/ det1

Kagami commented 7 years ago

cc @klkvsk

SthephanShinkufag commented 7 years ago

Firefox 52.0.2 SVG не оторбажаются, хотя стили применяются.

Kagami commented 7 years ago

Только форме настроек надо z-index добавить, а то её посты перекрывают и на табы не получается кликать. Ну или какой-нибудь другой фикс.

det2

SthephanShinkufag commented 7 years ago

Там пилить еще много чего. Я запилил только базовую поддержку, половина функционала не работает. Давайте сначала разберемся с SVG в Firefox'e.

Kagami commented 7 years ago

На 8ch.net аналогичный доктайп и без неймспейсов, но работает. Возможно с каким-нибудь стилем сайта конфликтует.

Kagami commented 7 years ago

Нашёл причину проблемы. Это использование тега <base> + SVG URIs. Проблема достаточно известная, много где описана:

Сильно пока не вникал, в качестве временного хака можно добавить document.querySelector("base").remove() перед addSVGIcons().

SthephanShinkufag commented 7 years ago

@Kagami, спасибо! Будем посмотреть.

Y0ba commented 7 years ago

Подобный костыль уже есть для 2chan: https://github.com/SthephanShinkufag/Dollchan-Extension-Tools/blob/17c512992fd042d44bce6990ca58e544e89ce555/src/Dollchan_Extension_Tools.es6.user.js#L13181

Только надо будет проверить, будет ли 0chan работать без этого тега.

Kagami commented 7 years ago

Я слегка потыкал, вроде работает и без base. Хотя можно и получше способ попробовать поискать. Тут ещё дело в том, что новонульч SPA и можно переходить между разделами/тредами без перезагрузки страницы. А куклоскрипт не отслеживает вызовы pushState.

SthephanShinkufag commented 7 years ago

новонульч SPA и можно переходить между разделами/тредами без перезагрузки страницы. А куклоскрипт не отслеживает вызовы pushState.

А вот это, кстати, проблема. Перехожу в тред, и кукле шиш - контент поменялся на лету. Бида.

Y0ba commented 7 years ago

Можно отслеживать при помощи MutationObserver - в нём нет оверхеда если правильно инициализировать (следить за как можно меньшим количеством элементов). Только в некроопере работать не будет. Делформа у нас уже создаётся отдельным объектом и активно используется для загрузки страниц.

SthephanShinkufag commented 7 years ago

Тогда придется как-то убивать все объекты в памяти, касаемые контента, постов и прочего.

Y0ba commented 7 years ago

Они разве уже не убиваются? Я имею в виду, в кукле есть встроенное обновление тредов без перезагрузки и там используется тот же самый механизм, что и при начальной загрузке скрипта.

SthephanShinkufag commented 7 years ago

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

Y0ba commented 7 years ago

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

Kagami commented 7 years ago

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

klkvsk commented 7 years ago

удалять событие onclick на ссылках на другие треды/разделы, чтобы переход по ним вызывал перезагрузку страницы

Спасибо, пригорело.

Давайте пока хотя бы так: window.app.$bus.on('refreshContentDone', () => { ваша инициализация })

SthephanShinkufag commented 7 years ago

@klkvsk спок, братишка, ваш single page application никуда не денется, я уже все это подебил с помощью MutationObserver. Тут другие баги вылязят, однако. Много еще чего допиливать.

SthephanShinkufag commented 7 years ago

Блджад. Номера оп-постов не совпадают с нумерацией тредов. А в кукле на это многое завязано. Спеллы, скрытие, и т.д. и т.п. Теперь точно полскрипта переписывать :(

Kagami commented 7 years ago

Да, весьма странное решение. Может пофиксить каким-нибудь костылём, например считать номер треда равным ОП-посту, а где-нибудь в глубине, там, где XHR-запросы делаются или что ещё, добавить спешл-кейс для нульчана? Ну или выкатить хотя бы базовый функционал, чтобы хоть какая-то поддержка была, а остальное уже потом инкрементально доделывать.

SthephanShinkufag commented 7 years ago

там, где XHR-запросы делаются или что ещё, добавить спешл-кейс для нульчана

Так и сделаю, пожалуй.

выкатить хотя бы базовый функционал

Разберусь с инфинитискроллом и выкачу. Нужно научить куклу перехватывать встроенную подгрузку тредов. Либо порезать ее и запилить инфинитискролл на основе кукольного.

SthephanShinkufag commented 7 years ago

Мда, ожидаемо. При попытке получить тред аяксом, получаем:

<!DOCTYPE html><html>
    <head>
        <meta charset=utf-8><base href=/ ><meta name=description content="">
        <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
        <link rel=icon href=/static/favicon.ico><link href=/static/css/app.9a1165aabdb5f1f6622bbd92281ad0fc.css rel=stylesheet>
    </head>
    <body>
        <div id=app></div>
        <script type=text/javascript src=/static/js/manifest.48fefcd5fe424fc95355.js></script>
        <script type=text/javascript src=/static/js/vendor.d72f88216d1eec66f70b.js></script>
        <script type=text/javascript src=/static/js/app.014c6fc9b807acbe3ebf.js></script>
    </body>
</html>

Контейнер, который должен содержать тред — пуст. Надо пользовать API:

https://0chan.hk/api/thread?thread=2390&after=32496&session=rg8lrxtkz1eswczofxppw8rmnlpg220x

Только вот где брать ключ сессии, хер его знает. Печально.

Kagami commented 7 years ago

sessionId лежит либо в localStorage, либо получается из заголовка X-Session GET-запроса на /api/session. Т.к. код нульчана сам его получает, то идентификатор всегда должен присутствовать в localStorage.

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

Kagami commented 7 years ago

Есть новости? Можно как-нибудь помочь?

SthephanShinkufag commented 7 years ago

Признаю, что поддержка этой борды невозможна, натыкаюсь на подводные камни на каждом шагу. А еще лень и некогда. И допиливать еще ОЧЕНЬ много. Мне стыдно выкладывать то, что сейчас есть.

Kagami commented 7 years ago

Жаль. Может добавить под галку «экспериментальная поддержка 0chan (много багов)» всё, что есть? Глядишь, люди потом пришлют пулл-реквесты с исправлениями. По крайней мере проще улучшить, чем пилить с нуля.

SthephanShinkufag commented 7 years ago

Еще может чутка поковыряю. С формой ответа просто беда. По сути, формы то никакой нет, все делается и отправляется внутренними скриптами движка. А там черт ногу сломит, нужно много времени провести на реверс-инжиниринг.

Y0ba commented 7 years ago

Публичного API у них нет? Вот пусть сделают, а потом уже и будем пилить скрипт.

Kagami commented 7 years ago

Есть API, просто (пока) не документированное. Я могу отреверсить, только скажите, какие действия нужны в первую очередь. Отправка сообщения и создание треда?

Y0ba commented 7 years ago

А смысл? Они его в любой момент поменять могут. Пусть стабилизируется сначала, а потом уже можно и думать что и как.

@SthephanShinkufag а можешь выкладывать патчи частями, не связанные с самим 0chan? Там например поддержку построения ОП-постов на основе JSON и прочее.

SthephanShinkufag commented 7 years ago

Да ладно, хер с апи - я даже банально не могу форму к треду привязать - с нулевой страницы идет попытка создания треда вместо ответа на пост. Форма в треде и на доске ничем не отличается, кроме принудительной ссылки (кстати, затея с принудительной ссылкой в тексте мне жутко не нравится, тупо ефг сделал). Все на внутренних скриптах, чо там и где - хер его знает.

SthephanShinkufag commented 7 years ago

Возможно, в там два разных апи на создаение треда и ответа на пост, да.

Kagami commented 7 years ago

А смысл? Они его в любой момент поменять могут.

Ну так на любой борде может появиться breaking change. Не думаю, что оно будет очень часто меняться. А на редкие изменения просто реагировать после багрепорта.

Ок, я тогда поковыряю API и отпишусь.

Kagami commented 7 years ago

Один товарищ покопался и скинул такое описание (форматирование моё):


отправить ответ

идёт POST-запрос на https://0chan.hk/api/thread/reply?parent=НОМЕР_СООБЩЕНИЯ_НА_КОТОРОЕ_ИДЁТ_ОТВЕТ&session=СЕССИЯ содержимое такое:

{"board": null, "thread": null, "parent": null, "message": "ТЕКСТ_СООБЩЕНИЯ",
 "images": [], "captcha": null}

чтоб сгрузить новое сообщение

дальше идёт запрос https://0chan.hk/api/thread?thread=НОМЕР_ТРЕДА&after=НОМЕР_СООБЩЕНИЯ_ПОСЛЕ_КОТОРОГО_ПОДГРУЗИТЬ_СООБЩЕНИЯ&session=СЕССИЯ


создание треда

‎‎POST-запрос на https://0chan.hk/api/thread/create?board=ИМЯ_БОРДЫ_ОН_ЖЕ_ТЕКСТОВЫЙ_ИДЕНТИФИКАТОР&session=СЕССИЯ содержимое:

{"board": null, "thread": null, "parent": null, "message": "СООБЩЕНИЕ",
 "images": [], "captcha": null}

это если без картинки


картинки

‎сначала делается multipart/form-data POST-запрос на https://0chan.hk/api/attachment/upload?session=СЕССИЯ с содержимым:

------BoundaryТУТКАКАЯТОХУЙНЯСЛУЧАЙНАЯ
Content-Disposition: form-data; name="file"; filename="mi8WwGt.jpg"
Content-Type: image/jpeg

<байты картинки>
------BoundaryТУТКАКАЯТОХУЙНЯСЛУЧАЙНАЯ

приходит ответ такого рода:

{
  "attachment": {
    "embed": null,
    "id": "39747",
    "images": {
      "original": {
        "height": "400",
        "id": "159438",
        "name": "170415ejsqqmwzyffd.jpg",
        "path": "17/04/15/170415ejsqqmwzyffd.jpg",
        "server": "s01",
        "size_kb": 41.99,
        "url": "//s01.0chan.hk/17/04/15/170415ejsqqmwzyffd.jpg?hash=XXX&exp=1492300800",
        "width": "600"
      },
      "thumb_100px": {
        "height": "67",
        "id": "159439",
        "name": "170415ejsqqmwzyffd-100.jpg",
        "path": "17/04/15/170415ejsqqmwzyffd-100.jpg",
        "server": "s01",
        "size_kb": 2.54,
        "url": "//s01.0chan.hk/17/04/15/170415ejsqqmwzyffd-100.jpg?hash=XXX&exp=1492300800",
        "width": "100"
      },
      "thumb_200px": {
        "height": "134",
        "id": "159440",
        "name": "170415ejsqqmwzyffd-200.jpg",
        "path": "17/04/15/170415ejsqqmwzyffd-200.jpg",
        "server": "s01",
        "size_kb": 7.65,
        "url": "//s01.0chan.hk/17/04/15/170415ejsqqmwzyffd-200.jpg?hash=XXX&exp=1492300800",
        "width": "200"
      },
      "thumb_400px": {
        "height": "267",
        "id": "159441",
        "name": "170415ejsqqmwzyffd-400.jpg",
        "path": "17/04/15/170415ejsqqmwzyffd-400.jpg",
        "server": "s01",
        "size_kb": 22.45,
        "url": "//s01.0chan.hk/17/04/15/170415ejsqqmwzyffd-400.jpg?hash=XXX&exp=1492300800",
        "width": "400"
      }
    },
    "isNsfw": false,
    "token": "t6yas3b2pd662jng"
  },
  "ok": true
}

дальше делается запрос создания как без каринки, только в содержимом уже:

{"board": null, "thread": null, "parent": null, "message": "СООБЩЕНИЕ", 
 "images": ["t6yas3b2pd662jng"], "captcha":null}

видно что в images лежит token который пришёл в ответе. когда запрос обработается приходит ответ с тредом ‎кстати когда пост создаёшь тоже приходит пост


как узнать, когда нужна капча ‎и как получить/отсылать

когда делаем POST-запрос на создание, тогда в ответ приходит такой жсон

{"error": 403, "message": "captcha required", "details": {"require": "captcha"}}

дальше надо сделать GET-запрос https://0chan.hk/api/captcha?session=сессия придёт жсон ответ:

{"captcha":"какой-то-текстовый-ид", "image": "data:image/png;base64,xxx"}

как видно base64 картинка с капчей ‎делаем GET-запрос https://0chan.hk/api/captcha?captcha=этоттектовыйид&answer=ответнакапчувurlencodeкажетсякорочспроцентиками&session=сессия ‎приходит жсон ответ если на капчу ответ неправильный:

{"captcha": "новыйиддлякапчи", "ok": false}

а когда правильный:

{"captcha": "новыйидкапчи", "ok": true}

после чего делаем повторный постзапрос https://0chan.hk/api/thread/create?board=имяборды&session=сессия&captcha=новыйидкапчи cо стандартым содержимым жсона для создания треда

Kagami commented 7 years ago

@SthephanShinkufag класс, большое спасибо. Этот баг закрываем или оставить?

SthephanShinkufag commented 7 years ago

Пусть здесь будет general discuss по нульчану. Можете аще пейсать сюда замеченных вами баги (кроме инфинитискролла и формы ответа, конечно). Будем допиливать потихоньку.

Kagami commented 7 years ago

Дублируется плеер ютуба. Пример: https://0chan.hk/test/1311

qxces commented 7 years ago

Не работает нулевая. То есть https://0chan.hk/b - работает, https://0chan.hk/ пишет в консоль

VM2599:3774 Generator throw: TypeError: Cannot read property 'querySelectorAll' of null
    at $Q (eval at E_c (:3:114), <anonymous>:3499:14)
    at Function.gen (eval at E_c (:3:114), <anonymous>:15406:18)
    at Function.init (eval at E_c (:3:114), <anonymous>:15444:11)
    at DelForm.addStuff (eval at E_c (:3:114), <anonymous>:20069:12)
    at runMain$ (eval at E_c (:3:114), <anonymous>:21068:21)
    at tryCatch (eval at E_c (:3:114), <anonymous>:2411:40)
    at Generator.invoke [as _invoke] (eval at E_c (:3:114), <anonymous>:2617:22)
    at Generator.prototype.(anonymous function) [as next] (eval at E_c (:3:114), <anonymous>:2436:21)
    at continuer (eval at E_c (:3:114), <anonymous>:3772:30)
    at onFulfilled (eval at E_c (:3:114), <anonymous>:3786:12)
    at eval (eval at E_c (:3:114), <anonymous>:3791:11)
    at eval (eval at E_c (:3:114), <anonymous>:18193:24)
    at r (https://0chan.hk/static/js/vendor.290177527e891c936288.js:45:47985)
continuer @ VM2599:3774
onFulfilled @ VM2599:3786
(anonymous) @ VM2599:3791
(anonymous) @ VM2599:18193
r @ vendor.290177527e891c936288.js:45
VM2599:3499 Uncaught (in promise) TypeError: Cannot read property 'querySelectorAll' of null
    at $Q (eval at E_c (:3:114), <anonymous>:3499:14)
    at Function.gen (eval at E_c (:3:114), <anonymous>:15406:18)
    at Function.init (eval at E_c (:3:114), <anonymous>:15444:11)
    at DelForm.addStuff (eval at E_c (:3:114), <anonymous>:20069:12)
    at runMain$ (eval at E_c (:3:114), <anonymous>:21068:21)
    at tryCatch (eval at E_c (:3:114), <anonymous>:2411:40)
    at Generator.invoke [as _invoke] (eval at E_c (:3:114), <anonymous>:2617:22)
    at Generator.prototype.(anonymous function) [as next] (eval at E_c (:3:114), <anonymous>:2436:21)
    at continuer (eval at E_c (:3:114), <anonymous>:3772:30)
    at onFulfilled (eval at E_c (:3:114), <anonymous>:3786:12)
    at eval (eval at E_c (:3:114), <anonymous>:3791:11)
    at eval (eval at E_c (:3:114), <anonymous>:18193:24)
    at r (https://0chan.hk/static/js/vendor.290177527e891c936288.js:45:47985)
$Q @ VM2599:3499
gen @ VM2599:15406
init @ VM2599:15444
addStuff @ VM2599:20069
runMain$ @ VM2599:21068
tryCatch @ VM2599:2411
invoke @ VM2599:2617
prototype.(anonymous function) @ VM2599:2436
continuer @ VM2599:3772
onFulfilled @ VM2599:3786
(anonymous) @ VM2599:3791
(anonymous) @ VM2599:18193
r @ vendor.290177527e891c936288.js:45
SthephanShinkufag commented 7 years ago

А у меня работает. Браузер, тип установки скрипта?

SthephanShinkufag commented 7 years ago

Я так понял, это какая-то опера с Violentmonkey? Версия оперы?

qxces commented 7 years ago

@SthephanShinkufag нет, это chrome версия 58.0.3029.81, tampermonkey 4.2.7, кукла v17.2.13.0.9dbef43 теперь уже v17.2.13.0.b1b0328.

Алсо:

  1. При нажатии F5 "Ошибка: сервер отправил повреждённые данные" даже в бэ.
  2. При подзагрузке постов (при автообновлении) теряется разметка. В карте ответов тоже. То есть выходит **ВНЕЗАПНО** вместо ВНЕЗАПНО 2.1. Время выводит в unix формате в виде 1492636080. Короче весь пост выдается без изменений в "сыром" виде.

Оно как то рандомно работает. Сначала раз 10 нормально грузит нулевую, но при подзагрузке не выдает треды, то потом вообще борду не грузит с ошибкой в логе.

SthephanShinkufag commented 7 years ago

Время и разметка исправлены. Чтобы починить нажатие по F5 на доске, надо научить скрипт строить доску с тредами из jsona.

UserNaem commented 7 years ago

Плавающая форма ответа уезжает куда-то за пределы страницы и не имеет кнопок закрытия и прикрепления обратно. Можно "вытащить" руками. image

SthephanShinkufag commented 7 years ago

Пощу, чтобы не потерять: 0chan API

SthephanShinkufag commented 6 years ago

Поддержка 0chan.hk прекращена, весь код убран. 0chan.hk лежит и неизвестно что с ним будет. Слишком много чего надо было допиливать, работал только базовый функционал, да и то через одно место. Можно сказать, толком ничего не работало. Проще выпилить всё вообще.