From 7ebd73c9cf209b57f667cd168f8d18c3e2e1001c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Brockm=C3=B6ller?= Date: Sun, 28 Sep 2025 14:16:28 +0200 Subject: [PATCH] Add new provider McMakler (#201) --- .gitignore | 1 + lib/provider/mcMakler.js | 47 +++++++++++++++++++++++++++++++++ test/provider/mcMakler.test.js | 37 ++++++++++++++++++++++++++ test/provider/testProvider.json | 4 +++ 4 files changed, 89 insertions(+) create mode 100755 lib/provider/mcMakler.js create mode 100644 test/provider/mcMakler.test.js diff --git a/.gitignore b/.gitignore index f59f123..f1868a0 100755 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ db/*.db* npm-debug.log .DS_Store .idea +.vscode diff --git a/lib/provider/mcMakler.js b/lib/provider/mcMakler.js new file mode 100755 index 0000000..dca27a6 --- /dev/null +++ b/lib/provider/mcMakler.js @@ -0,0 +1,47 @@ +import { isOneOf, buildHash } from '../utils.js'; +import checkIfListingIsActive from '../services/listings/listingActiveTester.js'; +let appliedBlackList = []; + +function normalize(o) { + const originalId = o.id.split('/').pop(); + const id = buildHash(originalId, o.price); + const size = o.size ?? 'N/A m²'; + const title = o.title || 'No title available'; + const address = o.address?.replace(' / ', ' ') || null; + const link = o.link != null ? `https://www.mcmakler.de${o.link}` : config.url; + return Object.assign(o, { id, size, title, link, address }); +} +function applyBlacklist(o) { + const titleNotBlacklisted = !isOneOf(o.title, appliedBlackList); + const descNotBlacklisted = !isOneOf(o.description, appliedBlackList); + return titleNotBlacklisted && descNotBlacklisted; +} +const config = { + url: null, + crawlContainer: 'article[data-testid="propertyCard"]', + sortByDateParam: 'sortBy=DATE&sortOn=DESC', + waitForSelector: 'ul[data-testid="listsContainer"]', + crawlFields: { + id: 'h2 a@href', + title: 'h2 a | removeNewline | trim', + price: 'footer > p:first-of-type | trim', + size: 'footer > p:nth-of-type(2) | trim', + address: 'div > h2 + p | removeNewline | trim', + image: 'img@src', + link: 'h2 a@href', + }, + normalize: normalize, + filter: applyBlacklist, + activeTester: checkIfListingIsActive, +}; +export const init = (sourceConfig, blacklist) => { + config.enabled = sourceConfig.enabled; + config.url = sourceConfig.url; + appliedBlackList = blacklist || []; +}; +export const metaInformation = { + name: 'McMakler', + baseUrl: 'https://www.mcmakler.de/immobilien/', + id: 'mcMakler', +}; +export { config }; diff --git a/test/provider/mcMakler.test.js b/test/provider/mcMakler.test.js new file mode 100644 index 0000000..2dbfa31 --- /dev/null +++ b/test/provider/mcMakler.test.js @@ -0,0 +1,37 @@ +import * as similarityCache from '../../lib/services/similarity-check/similarityCache.js'; +import { get } from '../mocks/mockNotification.js'; +import { mockFredy, providerConfig } from '../utils.js'; +import { expect } from 'chai'; +import * as provider from '../../lib/provider/mcMakler.js'; + +describe('#mcMakler testsuite()', () => { + after(() => { + similarityCache.stopCacheCleanup(); + }); + + it('should test mcMakler provider', async () => { + const Fredy = await mockFredy(); + provider.init(providerConfig.mcMakler, []); + + const fredy = new Fredy(provider.config, null, provider.metaInformation.id, 'mcMakler', similarityCache); + const listing = await fredy.execute(); + + expect(listing).to.be.a('array'); + const notificationObj = get(); + expect(notificationObj).to.be.a('object'); + expect(notificationObj.serviceName).to.equal('mcMakler'); + notificationObj.payload.forEach((notify) => { + /** check the actual structure **/ + expect(notify.id).to.be.a('string'); + expect(notify.price).to.be.a('string'); + expect(notify.size).to.be.a('string'); + expect(notify.title).to.be.a('string'); + expect(notify.link).to.be.a('string'); + expect(notify.address).to.be.a('string'); + /** check the values if possible **/ + expect(notify.size).that.does.include('m²'); + expect(notify.title).to.be.not.empty; + expect(notify.address).to.be.not.empty; + }); + }); +}); diff --git a/test/provider/testProvider.json b/test/provider/testProvider.json index 927e665..91eeccb 100644 --- a/test/provider/testProvider.json +++ b/test/provider/testProvider.json @@ -28,6 +28,10 @@ "url": "https://www.kleinanzeigen.de/s-immobilien/duesseldorf/anzeige:angebote/wohnung/k0c195l2068r5", "enabled": true }, + "mcMakler": { + "url": "https://www.mcmakler.de/immobilien/results?placeId=62649&search=Leipzig%252C+Sachsen&propertyTypes=APARTMENT&page=0", + "enabled": true + }, "neubauKompass": { "url": "https://www.neubaukompass.de/neubau-immobilien/duesseldorf-region/eigentumswohnung/", "enabled": true