Making Fredy an ESM project (#70)

Making Fredy an ESM project
This commit is contained in:
Christian Kellner
2023-03-13 13:42:43 +01:00
committed by GitHub
parent 7d0ec72a0c
commit 2c5eceb0c1
69 changed files with 862 additions and 1567 deletions

View File

@@ -1,19 +1,12 @@
const { markdown2Html } = require('../../services/markdown');
import { markdown2Html } from '../../services/markdown.js';
/**
* simply prints out the found data to the console
* @param serviceName e.g immowelt
* @param newListings an array with newly found listings
* @param jobKey name of the current job that is being executed
*/
exports.send = ({ serviceName, newListings, jobKey }) => {
export const send = ({ serviceName, newListings, jobKey }) => {
/* eslint-disable no-console */
return [Promise.resolve(console.info(`Found entry from service ${serviceName}, Job: ${jobKey}:`, newListings))];
/* eslint-enable no-console */
};
exports.config = {
id: __filename.slice(__dirname.length + 1, -3),
export const config = {
id: 'console',
name: 'Console',
description: 'This adapter sends new listings to the console. It is mostly useful for debugging.',
config: {},

View File

@@ -1,33 +1,22 @@
const mailjet = require('node-mailjet');
const path = require('path');
const fs = require('fs');
const template = fs.readFileSync(path.resolve(__dirname, '../', 'emailTemplate/template.hbs'), 'utf8');
const Handlebars = require('handlebars');
import mailjet from 'node-mailjet';
import path from 'path';
import fs from 'fs';
import Handlebars from 'handlebars';
import { markdown2Html } from '../../services/markdown.js';
import { getDirName } from '../../utils.js';
const __dirname = getDirName();
const template = fs.readFileSync(path.resolve(__dirname + '/notification/emailTemplate/template.hbs'), 'utf8');
const emailTemplate = Handlebars.compile(template);
const { markdown2Html } = require('../../services/markdown');
/**
* sends a new listing using MailJet
* @param serviceName e.g immowelt
* @param newListings an array with newly found listings
* @param notificationConfig config of this notification adapter
* * @param jobKey name of the current job that is being executed
* @returns {Promise<Chat.PostMessage.Response> | void}
*/
exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
export const send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
const { apiPublicKey, apiPrivateKey, receiver, from } = notificationConfig.find(
(adapter) => adapter.id === 'mailJet'
).fields;
const to = receiver
.trim()
.split(',')
.map((r) => ({
Email: r.trim(),
}));
return mailjet
.connect(apiPublicKey, apiPrivateKey)
.post('send', { version: 'v3.1' })
@@ -49,9 +38,8 @@ exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
],
});
};
exports.config = {
id: __filename.slice(__dirname.length + 1, -3),
export const config = {
id: 'mailjet',
name: 'MailJet',
description: 'MailJet is being used to send new listings via mail.',
readme: markdown2Html('lib/notification/adapter/mailJet.md'),

View File

@@ -1,26 +1,15 @@
const { markdown2Html } = require('../../services/markdown');
const { getJob } = require('../../services/storage/jobStorage');
const fetch = require('node-fetch');
/**
* sends new listings to mattermost
* @param serviceName e.g immowelt
* @param newListings an array with newly found listings
* @param notificationConfig config of this notification adapter
* @param jobKey name of the current job that is being executed
* @returns {Promise<Void> | void}
*/
exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
import { markdown2Html } from '../../services/markdown.js';
import { getJob } from '../../services/storage/jobStorage.js';
import fetch from 'node-fetch';
export const send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
const { webhook, channel } = notificationConfig.find((adapter) => adapter.id === 'mattermost').fields;
const job = getJob(jobKey);
const jobName = job == null ? jobKey : job.name;
let message = `### *${jobName}* (${serviceName}) found **${newListings.length}** new listings:\n\n`;
message += `| Title | Address | Size | Price |\n|:----|:----|:----|:----|\n`;
message += newListings.map(
(o) => `| [${o.title}](${o.link}) | ` + [o.address, o.size.replace(/2m/g, '$m^2$'), o.price].join(' | ') + ' |\n'
);
return fetch(webhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -30,14 +19,8 @@ exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
},
});
};
/**
* exported config is being used in the frontend to generate the fields
* incoming values will be the keys (and values) of the fields
*
*/
exports.config = {
id: __filename.slice(__dirname.length + 1, -3),
export const config = {
id: 'mattermost',
name: 'Mattermost',
readme: markdown2Html('lib/notification/adapter/mattermost.md'),
description: 'Fredy will send new listings to your mattermost team chat.',

View File

@@ -1,15 +1,6 @@
const sgMail = require('@sendgrid/mail');
const { markdown2Html } = require('../../services/markdown');
/**
* sends a new listing using SendGrid
* @param serviceName e.g immowelt
* @param newListings an array with newly found listings
* @param notificationConfig config of this notification adapter
* @param jobKey name of the current job that is being executed
* @returns {Promise<Chat.PostMessage.Response> | void}
*/
exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
import sgMail from '@sendgrid/mail';
import { markdown2Html } from '../../services/markdown.js';
export const send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
const { apiKey, receiver, from, templateId } = notificationConfig.find((adapter) => adapter.id === 'sendGrid').fields;
sgMail.setApiKey(apiKey);
const msg = {
@@ -28,9 +19,8 @@ exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
};
return sgMail.send(msg);
};
exports.config = {
id: __filename.slice(__dirname.length + 1, -3),
export const config = {
id: 'sendgrid',
name: 'SendGrid',
description: 'SendGrid is being used to send new listings via mail.',
readme: markdown2Html('lib/notification/adapter/sendGrid.md'),

View File

@@ -1,16 +1,7 @@
const Slack = require('slack');
import Slack from 'slack';
import { markdown2Html } from '../../services/markdown.js';
const msg = Slack.chat.postMessage;
const { markdown2Html } = require('../../services/markdown');
/**
* sends a new listing to slack
* @param serviceName e.g immowelt
* @param newListings an array with newly found listings
* @param notificationConfig config of this notification adapter
* * @param jobKey name of the current job that is being executed
* @returns {Promise<Chat.PostMessage.Response> | void}
*/
exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
export const send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
const { token, channel } = notificationConfig.find((adapter) => adapter.id === 'slack').fields;
return newListings.map((payload) =>
msg({
@@ -47,9 +38,8 @@ exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
})
);
};
exports.config = {
id: __filename.slice(__dirname.length + 1, -3),
export const config = {
id: 'slack',
name: 'Slack',
readme: markdown2Html('lib/notification/adapter/slack.md'),
description: 'Fredy will send new listings to the slack channel of your choice..',

View File

@@ -1,13 +1,6 @@
const { markdown2Html } = require('../../services/markdown');
const Database = require('better-sqlite3');
/**
* Stores data in a sqlite db in order to use the search results for later analytics
* @param serviceName e.g immowelt
* @param newListings an array with newly found listings
* @param jobKey name of the current job that is being executed
*/
exports.send = ({ serviceName, newListings, jobKey }) => {
import { markdown2Html } from '../../services/markdown.js';
import Database from 'better-sqlite3';
export const send = ({ serviceName, newListings, jobKey }) => {
const db = new Database('db/listings.db');
const fields = ['serviceName', 'jobKey', 'id', 'size', 'rooms', 'price', 'address', 'title', 'link', 'description'];
db.prepare(`CREATE TABLE IF NOT EXISTS listing (${fields.join(' TEXT, ')} TEXT);`).run();
@@ -23,9 +16,8 @@ exports.send = ({ serviceName, newListings, jobKey }) => {
});
return Promise.resolve();
};
exports.config = {
id: __filename.slice(__dirname.length + 1, -3),
export const config = {
id: 'sqlite',
name: 'Sqlite',
description: 'This adapter stores listings in a local sqlite3 database.',
config: {},

View File

@@ -1,7 +1,6 @@
const { markdown2Html } = require('../../services/markdown');
const { getJob } = require('../../services/storage/jobStorage');
const fetch = require('node-fetch');
import { markdown2Html } from '../../services/markdown.js';
import { getJob } from '../../services/storage/jobStorage.js';
import fetch from 'node-fetch';
const MAX_ENTITIES_PER_CHUNK = 8;
const RATE_LIMIT_INTERVAL = 1010;
/**
@@ -16,32 +15,23 @@ const arrayChunks = (inputArray, perChunk) =>
all[ch] = [].concat(all[ch] || [], one);
return all;
}, []);
/**
* sends new listings to telegram
* @param serviceName e.g immowelt
* @param newListings an array with newly found listings
* @param notificationConfig config of this notification adapter
* @param jobKey name of the current job that is being executed
* @returns {Promise<Void> | void}
*/
exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
function shorten(str, len = 30) {
return str.length > len ? str.substring(0, len) + '...' : str;
}
export const send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
const { token, chatId } = notificationConfig.find((adapter) => adapter.id === 'telegram').fields;
const job = getJob(jobKey);
const jobName = job == null ? jobKey : job.name;
//we have to split messages into chunk, because otherwise messages are going to become too big and will fail
const chunks = arrayChunks(newListings, MAX_ENTITIES_PER_CHUNK);
const promises = chunks.map((chunk) => {
let message = `<i>${jobName}</i> (${serviceName}) found <b>${newListings.length}</b> new listings:\n\n`;
message += chunk.map(
(o) =>
`<a href="${o.link}"><b>${shorten(o.title.replace(/\*/g, ''), 45).trim()}</b></a>\n` +
`<a href='${o.link}'><b>${shorten(o.title.replace(/\*/g, ''), 45).trim()}</b></a>\n` +
[o.address, o.price, o.size].join(' | ') +
'\n\n'
);
/**
* This is to not break the rate limit. It is to only send 1 message per second
*/
@@ -66,21 +56,10 @@ exports.send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
}, RATE_LIMIT_INTERVAL);
});
});
return Promise.all(promises);
};
function shorten(str, len = 30) {
return str.length > len ? str.substring(0, len) + '...' : str;
}
/**
* exported config is being used in the frontend to generate the fields
* incoming values will be the keys (and values) of the fields
*
*/
exports.config = {
id: __filename.slice(__dirname.length + 1, -3),
export const config = {
id: 'telegram',
name: 'Telegram',
readme: markdown2Html('lib/notification/adapter/telegram.md'),
description: 'Fredy will send new listings to your mobile, using Telegram.',

View File

@@ -1,24 +1,24 @@
const fs = require('fs');
import fs from 'fs';
const path = './adapter';
/** Read every integration existing in ./adapter **/
const adapter = fs
.readdirSync('./lib/notification/adapter')
.filter((file) => file.endsWith('.js'))
.map((integPath) => require(`${path}/${integPath}`));
const adapter = await Promise.all(
fs
.readdirSync('./lib/notification/adapter')
.filter((file) => file.endsWith('.js'))
.map(async (integPath) => await import(`${path}/${integPath}`))
);
if (adapter.length === 0) {
throw new Error('Please specify at least one notification provider');
}
exports.send = (serviceName, newListings, notificationConfig, jobKey) => {
const findAdapter = (notificationAdapter) => {
return adapter.find((a) => a.config.id === notificationAdapter.id);
};
export const send = (serviceName, newListings, notificationConfig, jobKey) => {
//this is not being used in tests, therefore adapter are always set
return notificationConfig
.filter((notificationAdapter) => findAdapter(notificationAdapter) != null)
.map((notificationAdapter) => findAdapter(notificationAdapter))
.map((a) => a.send({ serviceName, newListings, notificationConfig, jobKey }));
};
const findAdapter = (notificationAdapter) => {
return adapter.find((a) => a.config.id === notificationAdapter.id);
};