Разработка Chrome Extension / JS против посреднико-риэлторов

Всем привет,

Решил от нечего делать попилить себе очередной «мега-стартап», на этот раз в виде плагина к хрому. Чтоб не так скучно было, скажу что это такая себе база-данных, которая будет хранить комменты к товарам и продавцам на olx.ua — по сути коллективная жалобная книга.

Опущу попытки обойти ограничения по доступу к body events/window через инъекцию js, сразу скажу что сейчас получилось:

popup.js
просит данные у content.js когда открывается окошко плагина:

window.addEventListener('DOMContentLoaded', function () {
    chrome.tabs.query({
        active: true,
        currentWindow: true
    }, function (tabs) {
        chrome.tabs.sendMessage(tabs[0].id, {}, getData);
    });
});

content.js
отвечает на запрос из popup.js

// Listen for messages from the popup
chrome.runtime.onMessage.addListener(function (msg, sender, response) {
    response(sendData());
});

Это то что получилось. А теперь список того что пока не получилось:

1. Словить ивент adPageShowContact который асинхронно приходит в body после клика по элементу (клик сделать удается, словить ивент — нет) и отдать данные в popup.js и там запустить метод проверки данных

2. Автоматически открыть попал в случае получения данных и поменять иконку

3. Автоматически добавить на страницу сайта кастомный элемент в случае получения данных

4. Скрыть иконку расширения для всех сайтов кроме нужных (в данном случае это пока что только olx.ua)

Если кто-то сталкивался и тоже набивал себе шишки — буду рад помощи. В гугле меня пока что не забанили, но я уже перерыл developer.chrome.com/extensions + stackoverflow.com и он близок к тому, чтобы меня забанить х_х

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось1
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Возможно будет интересно: видео с конференции по теме «How to develop a Chrome Extension in 2018» youtu.be/N-E4p0PDN7w, шаблон для Хром Расширения на Реакте (примері) — github.com/...​ev/react-chrome-extension

UPD: короче отказались мы от попапа пока что и сошлись на инжекте своей панельки в тело страницы, сейчас выглядит вот так: puu.sh/u53lI/affbfb6c82.png (с жалобами), puu.sh/u53rk/f418e23ef7.png (без жалоб).

Так тогда не понятно, где сервис, где сайт, как то нехорошо влезать прям в UI. Еще и гемор куда инжектится своим узлом на разных страницах, или со сменой верстки да еще и как минимум поплыть может, это мазохизм ). Уж лучше DOM независимая верхняя/боковая полупрозрачная узкая панель, что выезжает или делает popup при клике, независимо от UI сайта, или такой нету в bootstrap отсюда вся беда?)) Достаточно глянуть на примеры аналогичной бабуйни для алиекспресса, там тоже интерфейс плагина выполнен в виде оврелея, никто страницу не насилует.

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

Кстати место там идеальное оставили, они аж хотели чтобы я туда чет засунул :)

Много объявление на один аккаунт создавать там стоит денег. По этому у всех по 100 аккаунтов с одним объявлением. Раньше вроде можно было просто переименовывать объявление, и таким образом крутить рекламу бесконечно. Но сейчас они поступают хитрее, они не паляться до встречи с клиентом. Так как olx пытается бороться с фейкам и иногда прозванивает. Только при личной встрече вы узнаете, что объявление фейк. Идея хорошая, но для того что бы работала вам нужна наверное независимая база, которую будут наполнять ваши пользователи сами. Ну и тут главное, что бы ее не загадили эти жи риелторы )

независимая база, которую будут наполнять ваши пользователи сами
Так собственно это и пилим, любой юзер приложения сможет добавить жалобу в БД :)
что бы ее не загадили эти жи риелторы
Методом жалоб на других риэлторов?

Угу, если у вас будут у всех одни только жалобы. То толку от такой базы? ))

Дойдем — разберемся :)

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

Тогда проще сразу свой OLX делать (или потом).

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

1. Словить ивент adPageShowContact который асинхронно приходит в body после клика по элементу (клик сделать удается, словить ивент — нет) и отдать данные в popup.js и там запустить метод проверки данных
Что мешает вызывать колбек по таймауту?

Ненадежное и топорное решение. Сетевой лаг — и не успели к таймауту получить данные. А ставить заведомо больший таймаут — ухудшать отзывчивость расширения.

Я, откровенно говоря, сомневаюсь что можно как-то обойтись без такого костыля. У расширений своя песочница, за границы которой их не пустят из соображений безопасности, и, особенно, в клиент-серверное AJAX-общение (CORS, вот это вот всё).

А другие костыли вроде DOMSubtreeModified — это еще более ненадежно и топорно (как минимум эти события задепрекейчены developer.mozilla.org/...de/Events/Mutation_events и могут ощутимо просадить перформанс groups.google.com/...dev.platform/L0Lx11u5Bvs

Им есть незадепрекейченая альтернатива developer.mozilla.org/.../Web/API/MutationObserver
А перфоманс может просесть если наблюдать за изменениями всего документа, в то время как в данном случае нужно мониторить всего лишь небольшой блок.

в клиент-серверное AJAX-общение
Контент-скрипт может выполнить ajax-запрос к бэкенду. CORS скорей всего не обойдешь, не пробовал.

Кстати это тоже идея :)

Вообще есть фокус и инъекцией JS-файла, но тогда нативные функции chrome.* внутри не будут работать. Плюс данные буду ассайниться в настоящий window и не будут доступны для content.js

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

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

А если хочется, тогда webRequest должен помочь.

А где на olx что то подгружается асинхронно и почему там надо делать клик? Что то в упор не вижу xhr запросов на их домен.
Почему Mutation_events / MutationObserver/ их полифилл через тот же dirty checking не подходят?

Клик по «показать» в блоке телефона:
puu.sh/u4RLT/c833dfe705.png

Mutation_events / MutationObserver/
Еще не пробовал, пытался обойтись обычными средствами типа bind()
Клик по «показать» в блоке телефона:
А, вот оно что... а то смотрел на скрытый телефон, тот что прямо в тексте, так там инфа через дата атрибут тупо подвязана :)
Ну так там нижний блок телефона вообще с одного узла состоит, то можно и тупо костылем проверять innerHTML каждые 250мс, пока не изменилось, делов то... С учетом простоты дерева и с событиями и незачем связываться.

Та фтопку 250мс :)

$.ajax('https://www.olx.ua/ajax/misc/contact/phone/pu1B0/white/')
Вуаля — и приходит телефон :)
pu1B0 — можно взять из адреса страницы:
www.olx.ua/…​to-moto-velo-IDpu1B0.html

ну это логично, если можно делать запросы c inject.js, а то cross-origin там нету :) Хотя это все муть, ибо парсить страницу нужно на сервере, с клиента требовать только id страницы которую смотрит, иначе моно интересные вещи натворить со стороны клиента, если отслеживать связи между телефоном, объявлением и аккаунтом.
Когда то тоже самое под taobao.com мутил от нефиг делать, только дело было давно и в виде прикладного оверлей виджета на делфях с бекендом на php, а не плагинов под браузеры.

Cross-origin сейчас нам не препятствует, видимо потому что включил доме в permissions или из-за crossOrigin: true внутри ajax(), короче работает оно нормально пока что без инжекции :)

Можем тебя в тиму взять если хочешь :)

вообще не понятен глубокий смысл сокрытия этого телефона, ну то есть от тупого бота ничего не знающего о olx да, но сайт немаленький, кому надо бота научат тырить номер за 10 строк скрипта ради хоть и сомнительной базы имя-телефон :)

Можем тебя в тиму взять если хочешь :)
хех) а тима уже состоит с тимлида сеньйора Олега и мидла Суворова? Оно скорее всего заглохнет, ты сам его забросишь через неделю- у меня заглохло почти перед бетой тогда, точнее бета уже была но в очень узком кругу, так что не считалось. Как только реализовал интересующие тогда меня плюшки типа перевода с китайского и начитка сводки товара синтезатором русской речи sapi запал пропал гг

Пока что вдвоем пилим, я фронт, друг бекенд делает.
Хз когда забросим, ничего не хочу обещать %)

Что мешает вызывать колбек по таймауту?
Мне бы помешала совесть.

Опыт у меня небольшой, 2 дня и 1 расширение. Кратко о расширении: есть одна игра, Perfect World, для неё есть сторонний чат-бот для клан-чата. У этого бота есть веб-морда, но её интерфейс не очень удобный, поэтому я написал для себя расширение, которое добавляло свои UI-элементы к существующим и вместо меня делало нужные клики/запросы на странице.

Словить ивент adPageShowContact который асинхронно приходит в body после клика по элементу (клик сделать удается, словить ивент — нет) и отдать данные в popup.js и там запустить метод проверки данных
Скрипт, который инжектится в страницу (content.js), работает в своем контексте. Он имеет доступ к DOM, но не имеет доступа к памяти, переменным и т.д.

Однако, если цель — получить контакты продавца когда пользователь их откроет, то можно попробовать повесить на контейнер с контактами обработчик события DOMSubtreeModified (developer.mozilla.org/...Events/DOMSubtreeModified). Мне он помог в расширении отслеживать аяксовые изменения на странице. Только нужно быть осторожным — если в этом обработчике изменить DOM внутри отслеживаемого контейнера — получится рекурсия.

2. Автоматически открыть попал в случае получения данных и поменять иконку
Не знаю, можно ли открыть попап скриптом — нужно копаться в документации. Для обмена сообщениями между фоновым скриптом и content.js используется sendMessage (developer.chrome.com/extensions/messaging). Таким образом, при открытии номера из content.js отправляем сообщение с этим номером, а в background.js можем его обработать. Вот на SO писали про изменение иконки: stackoverflow.com/...the-chrome-extension-icon Насчет попапа — гораздо проще может оказаться не париться с ним, а добавить свои UI-элементы прямо на страницу. Попап расширения имеет серьезную проблему — закрывается когда теряет фокус, и это нерешаемо. В него можно просто краткую информацию о плагине вставить, или вообще убрать попап и повесить на иконку обработчик клика чтобы активировать/деактивировать плагин (показывать/убирать свой UI). Контент-скриптов кстати может быть несколько, и все они имеют один контекст (но отличный от контекста страницы). Это позволяет подключить любую нужную js-библиотеку, например jquery (и совершенно неважно, используется ли он на самой странице — ничего не сломается). И CSS свои можно добавлять.
3. Автоматически добавить на страницу сайта кастомный элемент в случае получения данных
Смотри выше — обрабатываешь DOMSubtreeModified и дорисовываешь все что хочешь средствами js прямо в контент-скрипте.
4. Скрыть иконку расширения для всех сайтов кроме нужных (в данном случае это пока что только olx.ua)
По-моему это невозможно, но при правильно написанном background.js иконка будет активна только на нужном (нужных) сайте. Вот код:
/* global chrome */
var rule = {
    conditions: [
        new chrome.declarativeContent.PageStateMatcher({
            pageUrl: {hostEquals: 'site.com', schemes: ['https', 'http']}
        })
    ],
    actions: [new chrome.declarativeContent.ShowPageAction()]
};
chrome.runtime.onInstalled.addListener(function (details) {
    chrome.declarativeContent.onPageChanged.removeRules(undefined, function () {
        chrome.declarativeContent.onPageChanged.addRules([rule]);
    });
});

А вот документация: developer.chrome.com/...nsions/declarativeContent

UPD: Свой UI, если html-кода очень много, можно добавлять следующим образом:

1) Добавляем в расширение файл, например my-popup.html

2) В манифест пишем:

"web_accessible_resources": [
    "my-popup.html"
  ]

2) Пишем в контент-скрипте такое:

$.get(chrome.extension.getURL("my-popup.html"), function (response) {
            $('body').append($(response));
           /*Тут навешиваем обработчики на нужные элементы, применяем плагин "модальное окошко", если нужно и т.д.*/
});

Спасибо,

Скрипт сам кликает по ссылке «посмотреть телефон», осталось понять как словить колбек и передать данные. Сейчас юзер просто может сам открыть попап и с вероятностью 99,9% колбек успеет прилететь и там отрендерятся данные, но подход так себе.

Не совсем понятно как можно нормально внедрить свое UI на страницу. В первой версии я конечно вставлял через append() пару несчастных дивов, но уже это не прокатывает. У меня есть здоровый кусок кода который написан через шаблонизатор jsrender (есть план перейти на jsviews), к нему имплементятся бутстрап стили плюс свои кастомные. Если стили можно подключать так же просто как js, то куда пихать свой .html — хз.

2. С самой сменой иконки я вроде как разобрался, осталось понять как ее менять только для текущей вкладке. Она у меня сейчас отображает смысловую нагрузку аля error / warning / checked и т.п. которая по идее должна показать статус текущего его юзера (его объявления/вкладки), но сейчас везде показывается одинаковая.

4. Спасибо, то что надо должно быть, сейчас попробую.
Скрывать полностью не обязательно, пусть хотя бы неактивной станет.
А то я уже начал запиливать велосипед через chrome.tabs.onActivated.addListener

то куда пихать свой .html — хз.
В отдельный файл, который добавляется в манифесте в web_accessible_resources, и потом получается get-запросом и добавляется к странице через append (или скармливается шаблонизатору). Я там внизу отредактировал ответ чуть позже.

UPD: иконка все равно светиться везде puu.sh/u4jZ3/6901daa017.png , что я делаю не так? х_х

background.js прописан в манифесте? Например

"permissions": [
    "declarativeContent"
  ],
  "incognito": "split",
  "background": {
    "scripts": [
      "js/background.js"
    ],
    "persistent": false
  },
  "page_action": {
    "default_icon": {
      "19": "images/icon19.png",
      "38": "images/icon38.png"
    },
    "default_title": "pwAcolyte helper"
  },
Может еще content.js инжекстится на все страницы, там нужно указать куда инжектить:
"content_scripts": [
    {
      "matches": [
        "http://pwacolyte.ru/*"
      ],
      "css": [
        "vendor/jquery-ui-1.12.0/jquery-ui.min.css",
        "css/jquery-ui-images.css",
        "css/dialog.css"
      ],
      "js": [
        "vendor/jquery-3.1.0.min.js",
        "vendor/jquery-ui-1.12.0/jquery-ui.min.js",
        "vendor/moment.min.js",
        "vendor/jquery.tablesorter/jquery.tablesorter.min.js",
        "js/jquery-scroll-bottom.js",
        "js/content.js"
      ]
    }
  ],

Я кажись понял в чем беда, у меня browser_action, а у тебя page_action.

Короче действительно остается всю логику переносить в content.js и оттуда плясать, а то эти попапы только гемора добавляют.

1. Тут не видя кода сложно что-то сказать.
2. В хроме открытие всяких попапов без юзер экшена обычно проблематично, но теоретичеси возможно (либо через window.open либо через chrome.tabs), но, если я правильно помню сие позволено делать из background script-а, которого судя по описанию у вас нет (если это так, рекомендую завести), хотя по специфике экстеншена это из категории мастхев.
3. Странно, тут вроде ничего сложного быть не должно, главное в манифесте екстеншена прописать нужные пермишены.
4. Если я правильно помню, опять же согласно пермишенам иконка автоматом будет либо «активной» (цветной) на сайтах, прописанных в манифесте, либо «неактивной» серой на остальных. Скрыть вроде как нельзя: «all extensions now must have an icon».

Спасибо за ответ, щас попробуем разобраться вместе :)

1. Получается что расширения есть 3 типа подключаемых скриптов:
— background / работает на всех табах и страницах — сейчас его нет
— popup.html -> popup.js / работает только внутри отдельной страницы-попапа которая показывается по клику
— content / внедряется в страницу в соответствии с matches настройкой, имеет доступ к DOM, но не имеет доступа к ивентам и window (как я это понимаю)

2. puu.sh/u3gmL/e5fb57e8c8.jpg — попап имеется в виду хромовский родной, не window.open . Сейчас активируется только по клику

3. Тут вопрос что данные запрашиваются и обрабатывается в недрах popup.js, их каким-то образом надо допинать в content.js

4. Она у меня светится на каждом сайте без разбора, плюс появляется попап. Понятное дело что нигде вне olx.ua пока ничего хорошего там не рендерится

Бекграцнд точно понадобится. Плюс через него можно организовать пересылку данных с попапа в контент и обратно. Ну или через локалсторадж тож можно.
Касательно 4 надо смотреть манифест файл. Мб там че-то лишнее. Как пример можно глчнуть екстеншн steamdb. Там такое точно работает.

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

steamdb
посмотрю, спасибо

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

Бекграунд отвечает за взаимодействие с «внешним миром» и скалдывает все в локалсторадж. Все остальные попапы, контенты взаимодействуют с бекграундом (через chrome.runtime.onMessage и т.п.) и отображают то, что есть в локалсторадже. Пример можно глянуть в поделье, которое я как-то раньше писал. Там есть небольшая магия с самописным загрузчиком, так что дебажить его будет крайне неудобно (если что это фича), но взаимоействие между бекграундом и остальным можно посмотреть.

Не совсем понятно как их можно открыть, выкачивается всегда crx файл.

Изначально сфера для скрипта была — объявления про недвижимость (сдача/продажа).
Там сильно много грязи / разводов и просто мудаков, на которых напарывается практически каждый. Этот скрипт будет сигналить если текущий продавец помечен как мудак и будет выводить текст жалоб на него, что собственно сэкономит кучу времени, денег и нервов другим людям.

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

Большинство там ленивые сволочи которые даже описания не могут запостить нормально.
Вероятность того что они будут быстро перекидывать симки + апдейтить на всех сайтах свои телефоны — очень низкая.

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

Т.к. у каждого там в среднем штук 10-20 объявлений, то после смены телефона придется каждое вручную пересоздать чтобы поменять в них урлы.

Абсолютно плевать сколько и как зарабатывает конкретный постер.

Я в процессе поиска жилья напарывался на схемы:
— ой, там фотографии левые
— вышлите 200грн на карту чтобы забронивать
— давайте подпишем договор перед просмотром

Таких объявлений на вскидку 60%+, на них натыкается каждый кто ищет.
Если бы у меня был плагин который бы показывал чужие репорты — я бы не стал звонить в десятки из этих телефонов.

Даже если постерпоменяет себе и все телефоны и переапдейтит каждое объявление — это будет ровно до первого юзера который его пометит.

ФИО кстати плагин будет игнорить, там все равно все подписаны как «виталики» и «дмитрии» — толку их грабить? Год создания акка тоже сейчас не учитывается. Потом возможно введем что-то типа слепков объявления, которые смогут даже при смене урлы находить в базе похожие по контрольным сумам картинок и текстовым данным.

Относительно общего числа единицы, ориентируюсь что его реально будет юзать до 1000 уникальных человек по Киеву. В любом случае даже 10 активно рыскающих по объявлениям человек уже друг другу помогут не звонить лишний раз мудакам :)

В группе «риэлтор — зло» уже 75к людей, 1% из них я думаю точно захотят поюзать плагин

Реквестирую прохладных историй про разводняки и как их избежать

Т.к. у каждого там в среднем штук 10-20 объявлений, то после смены телефона придется каждое вручную пересоздать чтобы поменять в них урлы.
Там не просто url старого объявления. Не прозевай момент — в каждом объявлении есть ID аккаунта www.olx.ua/list/user/здесь_ID Т.е. все последующие сразу палятся + заодно можно сохранить все текущие объявления этого му***а и потом фильтровать по тексту, но с других аккаунтов.

Спасибо, наверно его ID отдельно в базу положим, будем искать еще и по нему

Это вы еще не знаете что у них есть сеть, и они сливают в бан пользователей которые оставляют им плохие комментарии.

Для нас это роли не играет :)

Кстати юзер который пожаловался будет не виден, может мы у себя и соберем какую-то инфу, но никому ее не покажем

Підписатись на коментарі