From ccdce54b7336d8eb9c575502afa6c228a05e67fe Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Sat, 8 Jun 2024 19:23:22 +0300 Subject: [PATCH] add global express script --- data/02_global-express.json | 46 ++++---- merged-data.json | 58 +++++----- popup.js | 2 +- scripts/update.js | 48 ++------ scripts/update/globalExpress.js | 190 ++++++++++++++++++++++++++++++++ scripts/update/index.js | 4 + scripts/update/ozon-wb-dpr.js | 4 +- scripts/update/sevenDostavka.js | 6 +- scripts/update/woyag.js | 43 ++++++++ 9 files changed, 304 insertions(+), 97 deletions(-) create mode 100644 scripts/update/globalExpress.js create mode 100644 scripts/update/woyag.js diff --git a/data/02_global-express.json b/data/02_global-express.json index 124d754..5ec1953 100644 --- a/data/02_global-express.json +++ b/data/02_global-express.json @@ -1,66 +1,66 @@ { "name": "Global Express", - "source": "https://t.me/Mariupol_global_express/1989", + "source": "https://t.me/Mariupol_global_express/1977", "points": [ - { - "coordinates": [ - 37.775896, - 47.959707 - ], - "name": "Донецк 7 (микр-н Звездный)", - "link": "https://ozon.ru/point/534953", - "address": "Донецк, пр-кт Ленинский 98А", - "operationTime": "пн-вс с 9:00 до 17:00" - }, { "coordinates": [ 37.811995, 48.011767 ], - "name": "Донецк 8 (Крытый рынок)", "link": "https://ozon.ru/point/533323", - "address": "Донецк, ул. 50-летия СССР 157А", - "operationTime": "пн-пт с 9:00 до 17:00" + "name": "г. Донецк, ул. 50-летия СССР, 157А", + "address": "г. Донецк, ул. 50-летия СССР, 157А", + "operationTime": "ГРАФИК РАБОТЫ" }, { "coordinates": [ 37.799192, 47.991666 ], - "name": "Донецк 1 (Южный)", "link": "https://ozon.ru/point/533347", - "address": "Донецк, площадь Коммунаров 1к1, ТЦ «Южный»", - "operationTime": "пн-пт с 9:00 до 17:00, сб-вс с 10:00 ло 17:00" + "name": "г. Донецк, площадь Коммунаров 1к1, ТЦ «Южный»", + "address": "г. Донецк, площадь Коммунаров 1к1, ТЦ «Южный»", + "operationTime": "ГРАФИК РАБОТЫ" + }, + { + "coordinates": [ + 37.775896, + 47.959707 + ], + "link": "https://ozon.ru/point/534953", + "name": "г. Донецк, пр - кт Ленинский, 98Аостановка «Звездный»", + "address": "г. Донецк, пр - кт Ленинский, 98Аостановка «Звездный»", + "operationTime": "ГРАФИК РАБОТЫ" }, { "coordinates": [ 38.635921, 48.047327 ], - "name": "Торез 3", "link": "https://ozon.ru/point/533350", + "name": "г. Торез, 4й микрорайон, (торговые павильоны в районе дома 23)", "address": "г. Торез, 4й микрорайон, (торговые павильоны в районе дома 23)", - "operationTime": "пн-вс с 9:00 до 17:00" + "operationTime": "ГРАФИК РАБОТЫ" }, { "coordinates": [ 38.761523, 48.021133 ], - "name": "Снежное", "link": "https://ozon.ru/point/534920", + "name": "Снежное, ул. Дзержинского, 2", "address": "Снежное, ул. Дзержинского, 2", - "operationTime": "пн-вс с 9:00 до 17:00" + "operationTime": "ГРАФИК РАБОТЫ" }, { "coordinates": [ 38.208714, 48.221055 ], - "name": "Енакиево", "link": "https://ozon.ru/point/534903", + "name": "Енакиево, проспект Ленина, 98в", "address": "Енакиево, проспект Ленина, 98в", - "operationTime": "пн-вс с 9:00 до 17:00" + "operationTime": "ГРАФИК РАБОТЫ" } ] } \ No newline at end of file diff --git a/merged-data.json b/merged-data.json index c23a263..0c4a340 100644 --- a/merged-data.json +++ b/merged-data.json @@ -323,28 +323,16 @@ "source": "https://login.woyag.ru/map", "provider": "WOЯЖ" }, - { - "coordinates": [ - 37.775896, - 47.959707 - ], - "name": "Донецк 7 (микр-н Звездный)", - "link": "https://ozon.ru/point/534953", - "address": "Донецк, пр-кт Ленинский 98А", - "operationTime": "пн-вс с 9:00 до 17:00", - "source": "https://t.me/Mariupol_global_express/1989", - "provider": "Global Express" - }, { "coordinates": [ 37.811995, 48.011767 ], - "name": "Донецк 8 (Крытый рынок)", "link": "https://ozon.ru/point/533323", - "address": "Донецк, ул. 50-летия СССР 157А", - "operationTime": "пн-пт с 9:00 до 17:00", - "source": "https://t.me/Mariupol_global_express/1989", + "name": "г. Донецк, ул. 50-летия СССР, 157А", + "address": "г. Донецк, ул. 50-летия СССР, 157А", + "operationTime": "ГРАФИК РАБОТЫ", + "source": "https://t.me/Mariupol_global_express/1977", "provider": "Global Express" }, { @@ -352,11 +340,23 @@ 37.799192, 47.991666 ], - "name": "Донецк 1 (Южный)", "link": "https://ozon.ru/point/533347", - "address": "Донецк, площадь Коммунаров 1к1, ТЦ «Южный»", - "operationTime": "пн-пт с 9:00 до 17:00, сб-вс с 10:00 ло 17:00", - "source": "https://t.me/Mariupol_global_express/1989", + "name": "г. Донецк, площадь Коммунаров 1к1, ТЦ «Южный»", + "address": "г. Донецк, площадь Коммунаров 1к1, ТЦ «Южный»", + "operationTime": "ГРАФИК РАБОТЫ", + "source": "https://t.me/Mariupol_global_express/1977", + "provider": "Global Express" + }, + { + "coordinates": [ + 37.775896, + 47.959707 + ], + "link": "https://ozon.ru/point/534953", + "name": "г. Донецк, пр - кт Ленинский, 98Аостановка «Звездный»", + "address": "г. Донецк, пр - кт Ленинский, 98Аостановка «Звездный»", + "operationTime": "ГРАФИК РАБОТЫ", + "source": "https://t.me/Mariupol_global_express/1977", "provider": "Global Express" }, { @@ -364,11 +364,11 @@ 38.635921, 48.047327 ], - "name": "Торез 3", "link": "https://ozon.ru/point/533350", + "name": "г. Торез, 4й микрорайон, (торговые павильоны в районе дома 23)", "address": "г. Торез, 4й микрорайон, (торговые павильоны в районе дома 23)", - "operationTime": "пн-вс с 9:00 до 17:00", - "source": "https://t.me/Mariupol_global_express/1989", + "operationTime": "ГРАФИК РАБОТЫ", + "source": "https://t.me/Mariupol_global_express/1977", "provider": "Global Express" }, { @@ -376,11 +376,11 @@ 38.761523, 48.021133 ], - "name": "Снежное", "link": "https://ozon.ru/point/534920", + "name": "Снежное, ул. Дзержинского, 2", "address": "Снежное, ул. Дзержинского, 2", - "operationTime": "пн-вс с 9:00 до 17:00", - "source": "https://t.me/Mariupol_global_express/1989", + "operationTime": "ГРАФИК РАБОТЫ", + "source": "https://t.me/Mariupol_global_express/1977", "provider": "Global Express" }, { @@ -388,11 +388,11 @@ 38.208714, 48.221055 ], - "name": "Енакиево", "link": "https://ozon.ru/point/534903", + "name": "Енакиево, проспект Ленина, 98в", "address": "Енакиево, проспект Ленина, 98в", - "operationTime": "пн-вс с 9:00 до 17:00", - "source": "https://t.me/Mariupol_global_express/1989", + "operationTime": "ГРАФИК РАБОТЫ", + "source": "https://t.me/Mariupol_global_express/1977", "provider": "Global Express" }, { diff --git a/popup.js b/popup.js index 5067a1b..c81150b 100644 --- a/popup.js +++ b/popup.js @@ -46,7 +46,7 @@ function onClick(event) { popupAddress.href = `https://yandex.ru/maps/?whatshere[point]=${lon},${lat}&whatshere[zoom]=18&l=map` popupLink.href = feature.get("link"); popupSource.href = feature.get("source"); - popupOperationTime.textContent = feature.get("operationTime") ?? "неизвестно"; + popupOperationTime.innerHTML = feature.get("operationTime") ?? "неизвестно"; QRCode.toCanvas(popupCanvas, feature.get("link"), function (error) { if (error) console.error(error); diff --git a/scripts/update.js b/scripts/update.js index 8b3d519..dd13f2d 100644 --- a/scripts/update.js +++ b/scripts/update.js @@ -1,49 +1,15 @@ -import fs from "node:fs/promises"; -import { asyncMap } from "modern-async"; -import { getFinalURL } from "./utils.js"; -import ozonWbDpr from "./update/ozon-wb-dpr.js"; -import sevenDostavka from "./update/sevenDostavka.js"; - -async function woyag() { - const apiResponse = await fetch("https://login.woyag.ru/ajax/pvz-list"); - const json = await apiResponse.json(); - - let points = json.filter((point) => !!point.link); - - points = await asyncMap(points, async (point) => { - const link = await getFinalURL(point.link).then(u => { - const final = new URL(u); - final.search = ''; - return final.toString(); - }) - - return { - coordinates: [parseFloat(point["geo_lng"]), parseFloat(point["geo_lat"])], - name: point.name, - address: point.address, - link, - operationTime: "пн-вс с 9:00 до 17:45", - }; - }); - - await fs.writeFile( - "data/01_woyag.json", - JSON.stringify( - { - name: "WOЯЖ", - source: "https://login.woyag.ru/map", - points, - }, - undefined, - 4 - ) - ); -} +import { + woyag, + sevenDostavka, + ozonWbDpr, + globalExpress +} from './update/index.js'; async function main() { await woyag(); await sevenDostavka(); await ozonWbDpr(); + await globalExpress(); } main(); diff --git a/scripts/update/globalExpress.js b/scripts/update/globalExpress.js new file mode 100644 index 0000000..ef04766 --- /dev/null +++ b/scripts/update/globalExpress.js @@ -0,0 +1,190 @@ +import fs from "node:fs/promises"; + +import { JSDOM } from "jsdom"; +import { asyncMap } from "modern-async"; +import { getTelegramMessage } from "../utils.js"; + +const OUTPUT_FILE = "data/02_global-express.json"; +const MAIN_URL = "https://t.me/Mariupol_global_express/1977"; + +/* +function generateReadableSchedule(schedule) { + const dayMappings = { + mon: "Пн", + tue: "Вт", + wed: "Ср", + thu: "Чт", + fri: "Пт", + sat: "Сб", + sun: "Вс", + }; + + let readableSchedule = ""; + + Object.keys(dayMappings).forEach((key) => { + const day = dayMappings[key]; + const daySchedule = schedule[key]; + + if ( + daySchedule && + daySchedule.open_time !== null && + daySchedule.close_time !== null + ) { + const openTime = formatTime(daySchedule.open_time); + const closeTime = formatTime(daySchedule.close_time); + const breakStart = daySchedule.break_open_time + ? daySchedule.break_open_time + : daySchedule.close_time; + const breakEnd = daySchedule.break_close_time + ? daySchedule.break_close_time + : daySchedule.close_time; + + if (breakStart === daySchedule.close_time) { + readableSchedule += `${day} ${openTime} - ${closeTime}\n`; + } else { + const breakStartFormatted = formatTime(breakStart); + const breakEndFormatted = formatTime(breakEnd); + readableSchedule += `${day} ${openTime} - ${breakStartFormatted}, ${breakEndFormatted} - ${closeTime}\n`; + } + } else { + readableSchedule += `${day} Выходной\n`; + } + }); + + return readableSchedule.trim(); +} + +function formatTime(minutes) { + if (minutes < 0) return "00:00"; // Время меньше 0, возвращаем 00:00 + const hours = Math.floor(minutes / 60); + const mins = minutes % 60; + return `${padZero(hours)}:${padZero(mins)}`; +} + +function padZero(num) { + return num.toString().padStart(2, "0"); +} +*/ + +async function getPVZFromPost(post) { + const message = await getTelegramMessage(post); + + const dom = new JSDOM(message.text); + const document = dom.window.document; + + // Ищем координаты, которые всегда в скобках, возможно с пробелами + const coordinatesNode = Array.from(document.querySelectorAll("br")) + .map((br) => br.nextSibling) + .find( + (node) => + node && + node.nodeType === 3 && + /\(\s*\d+(\.\d+)?\s*,\s*\d+(\.\d+)?\s*\)/.test(node.nodeValue.trim()) + ); + + let coordinates = null; + if (coordinatesNode) { + const matches = coordinatesNode.nodeValue.trim().match(/\(\s*([^)]*)\s*\)/); + if (matches) { + const [lat, lng] = matches[1] + .split(",") + .map((coord) => parseFloat(coord.trim())); + coordinates = [lng, lat]; + } + } + + // Ищем ссылку, которая начинается с https://ozon.ru/point + const linkNode = document.querySelector('a[href^="https://ozon.ru/point"]'); + const link = linkNode ? linkNode.href : null; + + return { + coordinates, + link, + }; +} + +async function getFromTelegram() { + const message = await getTelegramMessage(MAIN_URL); + + const dom = new JSDOM(message.text); + const document = dom.window.document; + + const addressesSection = Array.from(document.querySelectorAll("b")).find( + (b) => b.textContent.includes("Наши адреса ПВЗ с OZON") + ); + const privilegesSection = Array.from(document.querySelectorAll("u")).find( + (u) => u.textContent.includes("Какие привилегии") + ); + + const links = []; + let currentElement = addressesSection.nextElementSibling; + + while (currentElement && currentElement !== privilegesSection) { + if (currentElement.tagName === "A") { + links.push(currentElement); + } + currentElement = currentElement.nextElementSibling; + } + + const data = await asyncMap(links, async (link) => { + const textContent = link.textContent; + + const pvzData = await getPVZFromPost(link.href); + + return { + ...pvzData, + name: textContent, + address: textContent, + }; + }); + + return data; +} + +/* +async function getFromVK() { + const group_id = 124759560; + const access_token = ""; + const v = "5.199"; + + const res = await fetch( + `https://api.vk.com/method/groups.getAddresses?group_id=${group_id}&access_token=${access_token}&count=999&v=${v}`, + { + headers: { + "Accept-Language": "ru,en;q=0.9", + }, + } + ); + + const items = (await res.json()).response.items; + + return items.map((item) => ({ + address: `${item.city.title}, ${item.address}`, + timetable: item.timetable, + })); +} +*/ + +async function globalExpress() { + const points = await getFromTelegram(); + // const points = await getFromVK(); + + fs.writeFile( + OUTPUT_FILE, + JSON.stringify( + { + name: "Global Express", + source: MAIN_URL, + points: points.map((p) => ({ + ...p, + operationTime: + `ГРАФИК РАБОТЫ`, + })), + }, + undefined, + 4 + ) + ); +} + +export default globalExpress; diff --git a/scripts/update/index.js b/scripts/update/index.js index e69de29..9cb03a0 100644 --- a/scripts/update/index.js +++ b/scripts/update/index.js @@ -0,0 +1,4 @@ +export { default as woyag } from "./woyag.js"; +export { default as ozonWbDpr } from "./ozon-wb-dpr.js"; +export { default as sevenDostavka } from "./sevenDostavka.js"; +export { default as globalExpress } from "./globalExpress.js"; diff --git a/scripts/update/ozon-wb-dpr.js b/scripts/update/ozon-wb-dpr.js index 7f3a047..2381d40 100644 --- a/scripts/update/ozon-wb-dpr.js +++ b/scripts/update/ozon-wb-dpr.js @@ -4,6 +4,8 @@ import { JSDOM } from "jsdom"; import { asyncMap } from "modern-async"; import fs from "node:fs/promises"; +const OUTPUT_FILE = "data/03_ozon-wb-dpr.json"; + const MAIN_URL = "https://t.me/ozon_wb_dpr/627"; const QR_FOR_PVZ_STRING = "QR для ПВЗ"; @@ -108,7 +110,7 @@ async function ozonWbDpr() { }); await fs.writeFile( - "data/03_ozon-wb-dpr.json", + OUTPUT_FILE, JSON.stringify( { name: "ПВЗ ДНР", diff --git a/scripts/update/sevenDostavka.js b/scripts/update/sevenDostavka.js index 87afeb4..fb1cfab 100644 --- a/scripts/update/sevenDostavka.js +++ b/scripts/update/sevenDostavka.js @@ -1,6 +1,8 @@ import fs from "node:fs/promises"; import { JSDOM } from "jsdom"; +const OUTPUT_FILE = "data/99_sevenDostavka.json"; + const linkRegexp = new RegExp(/https:\/\/ozon\.ru\/point\/\d+/); async function sevenDostavka() { @@ -19,7 +21,7 @@ async function sevenDostavka() { const points = new Function(`return [{${x}}]`)(); fs.writeFile( - "data/99_sevenDostavka.json", + OUTPUT_FILE, JSON.stringify( { name: "7dostavka", @@ -44,4 +46,4 @@ async function sevenDostavka() { } } -export default sevenDostavka; \ No newline at end of file +export default sevenDostavka; diff --git a/scripts/update/woyag.js b/scripts/update/woyag.js new file mode 100644 index 0000000..1a966d5 --- /dev/null +++ b/scripts/update/woyag.js @@ -0,0 +1,43 @@ +import fs from "node:fs/promises"; +import { asyncMap } from "modern-async"; +import { getFinalURL } from "../utils.js"; + +const OUTPUT_FILE = "data/01_woyag.json"; + +async function woyag() { + const apiResponse = await fetch("https://login.woyag.ru/ajax/pvz-list"); + const json = await apiResponse.json(); + + let points = json.filter((point) => !!point.link); + + points = await asyncMap(points, async (point) => { + const link = await getFinalURL(point.link).then(u => { + const final = new URL(u); + final.search = ''; + return final.toString(); + }) + + return { + coordinates: [parseFloat(point["geo_lng"]), parseFloat(point["geo_lat"])], + name: point.name, + address: point.address, + link, + operationTime: "пн-вс с 9:00 до 17:45", + }; + }); + + await fs.writeFile( + OUTPUT_FILE, + JSON.stringify( + { + name: "WOЯЖ", + source: "https://login.woyag.ru/map", + points, + }, + undefined, + 4 + ) + ); +} + +export default woyag \ No newline at end of file