From cbff05689a47c6cd0e1fd565a887795d0a64148a Mon Sep 17 00:00:00 2001 From: sk22 Date: Tue, 25 Nov 2025 18:53:31 +0100 Subject: [PATCH] feat(route): Add WBV-GPA Austrian communal housing search (#20544) * feat(category): add housing category * feat(route): Add WBV-GPA housing search * fix(route/wbv-gpa): fix import * fix(route/wbv-gpa): fix feed title * Revert "feat(category): add housing category" This reverts commit 953f59421717592dc5d8f2e0d8c42b5d54c77c72. * feat(route/wbv-gpa): change category --------- Co-authored-by: Samuel Kaiser --- lib/routes/wbv-gpa/index.ts | 71 +++++++++++++++++++++++++++++++++ lib/routes/wbv-gpa/namespace.ts | 8 ++++ 2 files changed, 79 insertions(+) create mode 100644 lib/routes/wbv-gpa/index.ts create mode 100644 lib/routes/wbv-gpa/namespace.ts diff --git a/lib/routes/wbv-gpa/index.ts b/lib/routes/wbv-gpa/index.ts new file mode 100644 index 000000000..49cf9d509 --- /dev/null +++ b/lib/routes/wbv-gpa/index.ts @@ -0,0 +1,71 @@ +import { load } from 'cheerio'; +import ofetch from '@/utils/ofetch'; +import type { Data, DataItem, Route } from '@/types'; + +const FEED_LANGUAGE = 'de' as const; +const FEED_LOGO = 'https://www.wbv-gpa.at/app/uploads/2024/01/cropped-WBV-Favicon-192x192.png'; +const SITE_URL = 'https://www.wbv-gpa.at' as const; +const BASE_URL = `${SITE_URL}/angebote/` as const; + +export const route: Route = { + name: 'Angebote', + example: '/wbv-gpa/wohnungen/wien', + path: '/:category?/:state?', + maintainers: ['sk22'], + categories: ['other'], + description: ` +Search housing by WBV-GPA, see "Angebote" menu item in https://www.wbv-gpa.at. +Filtering by state is done client-side. +`, + parameters: { + category: 'Anything behind `/angebote/` in the URL. Default: `wohnungen`', + state: 'Optionally filter by Austrian state (`wien`, `steiermark`, ...)', + }, + radar: [ + { + // wbv-gpa.at/wohnungen is equivalent to wbv-gpa.at/angebote/wohnungen + source: [`${SITE_URL}/wohnungen/`, `${BASE_URL}/:category`], + target: '/:category', + }, + ], + + async handler(ctx) { + const category = ctx.req.param('category') || 'wohnungen'; + const state = ctx.req.param('state'); + + const link = BASE_URL + category; + const response = await ofetch(link); + const $ = load(response); + const title = $('title').text(); + + const items = $('.objects__list__rows__item.mix') + .toArray() + .map((el) => { + const $el = $(el); + const link = $el.find('a').attr('href'); + const title = $el + .find('.objects__list__rows__item__info__cell:not(.desktop_only):not(.objects__list__rows__item__info__cell--link)') + .toArray() + .map((el) => $(el).text().trim()) + .join(', '); + const description = $el + .find('.objects__list__rows__item__info__cell.desktop_only:not(.objects__list__rows__item__info__cell--link)') + .toArray() + .map((el) => $(el).text().trim()) + .join(', '); + + // no pubDate and no image :( + return state && !$el.hasClass(state) ? false : ({ title, description, link } satisfies DataItem); + }) + .filter((item) => item !== false); + + return { + title, + language: FEED_LANGUAGE, + logo: FEED_LOGO, + allowEmpty: true, + item: items, + link, + } satisfies Data; + }, +}; diff --git a/lib/routes/wbv-gpa/namespace.ts b/lib/routes/wbv-gpa/namespace.ts new file mode 100644 index 000000000..98c859b0e --- /dev/null +++ b/lib/routes/wbv-gpa/namespace.ts @@ -0,0 +1,8 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'WBV-GPA', + description: 'Wohnbauvereinigung für Privatangestellte - Gemeinnützige Gesellschaft mit beschränkter Haftung', + url: 'wbv-gpa.at', + lang: 'de', +};