Skip to content

Commit

Permalink
update telegram provider
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanovvladi committed Aug 11, 2024
1 parent 1bf012f commit 8dfbdf7
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 34 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ db/
npm-debug.log
.DS_Store
.idea
.vscode/
16 changes: 16 additions & 0 deletions lib/api/routes/notificationAdapterRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ notificationAdapterRouter.post('/try', async (req, res) => {
size: '666 2m',
link: 'https://www.orange-coding.net',
},
{
price: '1500 €',
title:
'This is a test listing with long and doubled title. This is a test listing with long and doubled title.',
address: 'some address',
size: '777 2m',
link: 'https://www.orange-coding.net',
},
{
price: '2500 €',
title: 'This is a test listing with an image',
address: 'some address',
size: '555 2m',
link: 'https://www.orange-coding.net',
image: 'https://images.pexels.com/photos/106399/pexels-photo-106399.jpeg',
},
],
notificationConfig,
jobKey: 'TestJob',
Expand Down
2 changes: 1 addition & 1 deletion lib/notification/adapter/apprise.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const send = ({ serviceName, newListings, notificationConfig, jobKey }) =
const jobName = job == null ? jobKey : job.name;
const promises = newListings.map((newListing) => {
const title = `${jobName} at ${serviceName}: ${newListing.title}`;
const message = `Address: ${newListing.address}\nSize: ${newListing.size}\nPrice: ${newListing.price}\Link: ${newListing.link}`;
const message = `Address: ${newListing.address}\nSize: ${newListing.size}\nPrice: ${newListing.price}\nLink: ${newListing.link}`;
return fetch(server, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
Expand Down
130 changes: 99 additions & 31 deletions lib/notification/adapter/telegram.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,111 @@ const arrayChunks = (inputArray, perChunk) =>
all[ch] = [].concat(all[ch] || [], one);
return all;
}, []);
function shorten(str, len = 30) {

function shorten(str, len = 45) {
return str.length > len ? str.substring(0, len) + '...' : str;
}
export const send = ({ serviceName, newListings, notificationConfig, jobKey }) => {

function isValidURL(url) {
try {
new URL(url);
return true;
} catch (_) {
return false;
}
}

const sendTelegramMessage = (token, chatId, message, isPhoto = false, photoUrl = '') => {
const url = isPhoto
? `https://api.telegram.org/bot${token}/sendPhoto`
: `https://api.telegram.org/bot${token}/sendMessage`;
const body = isPhoto
? JSON.stringify({
chat_id: chatId,
photo: photoUrl,
caption: message,
parse_mode: 'HTML',
})
: JSON.stringify({
chat_id: chatId,
text: message,
parse_mode: 'HTML',
disable_web_page_preview: !isPhoto,
});

/**
* This is to not break the rate limit. It is to only send 1 message per second
*/
return new Promise((resolve, reject) => {
setTimeout(() => {
fetch(url, {
method: 'post',
body: body,
headers: { 'Content-Type': 'application/json' },
})
.then(() => {
resolve();
})
.catch(() => {
reject();
});
}, RATE_LIMIT_INTERVAL);
});
};

const sendRichMessage = ({ serviceName, newListings, notificationConfig, jobKey }) => {
const { token, chatId } = notificationConfig.find((adapter) => adapter.id === config.id).fields;
const job = getJob(jobKey);
const jobName = job == null ? jobKey : job.name;

const promises = newListings.map((o, index) => {
let message = '';
/**
* Only send the job information once
*/
if (index === 0) {
message += `<i>${jobName}</i> (${serviceName}) found <b>${newListings.length}</b> new listings:\n\n`;
}
message +=
`<a href='${o.link}'><b>${shorten(o.title.replace(/\*/g, '')).trim()}</b></a>\n` +
`🏠 Address: ${o.address}\n` +
`💰 Price: ${o.price}\n` +
`📐 Size: ${o.size}\n`;
const imageURL = o.lazyImage || o.image;
return sendTelegramMessage(token, chatId, message, imageURL && isValidURL(imageURL), imageURL);
});
return Promise.all(promises);
};

const sendPlainMessage = ({ serviceName, newListings, notificationConfig, jobKey }) => {
const { token, chatId } = notificationConfig.find((adapter) => adapter.id === config.id).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` +
[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
*/
return new Promise((resolve, reject) => {
setTimeout(() => {
fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
method: 'post',
body: JSON.stringify({
chat_id: chatId,
text: message,
parse_mode: 'HTML',
disable_web_page_preview: true,
}),
headers: { 'Content-Type': 'application/json' },
})
.then(() => {
resolve();
})
.catch(() => {
reject();
});
}, RATE_LIMIT_INTERVAL);
});
message += chunk
.map(
(o) =>
`<a href='${o.link}'><b>${shorten(o.title.replace(/\*/g, '')).trim()}</b></a>\n` +
[o.address, o.price, o.size].join(' | '),
)
.join('\n\n');
return sendTelegramMessage(token, chatId, message);
});
return Promise.all(promises);
};

export const send = ({ serviceName, newListings, notificationConfig, jobKey }) => {
const { richMessage } = notificationConfig.find((adapter) => adapter.id === config.id).fields;
if (richMessage) {
return sendRichMessage({ serviceName, newListings, notificationConfig, jobKey });
} else {
return sendPlainMessage({ serviceName, newListings, notificationConfig, jobKey });
}
};

export const config = {
id: 'telegram',
name: 'Telegram',
Expand All @@ -74,5 +136,11 @@ export const config = {
label: 'Chat Id',
description: 'The chat id to send messages to you.',
},
richMessage: {
type: 'boolean',
label: 'Send rich message',
description: 'When selected sends a rich message with image.',
value: false,
},
},
};
2 changes: 2 additions & 0 deletions lib/provider/immoscout.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const config = {
title: '.result-list-entry .result-list-entry__brand-title-container h2 | removeNewline | trim',
link: '.result-list-entry .result-list-entry__brand-title-container@href',
address: '.result-list-entry .result-list-entry__map-link',
image: '.result-list-entry .gallery-container .slick-list .gallery__image@src',
lazyImage: '.result-list-entry .gallery-container .gallery__image@data-lazy-src',
},
paginate: '#pager .align-right a@href',
normalize: normalize,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,9 @@ export default function NotificationAdapterMutator({
return (
<Form key={key}>
{uiElement.type === 'boolean' ? (
<Switch
checked={uiElement.value || false}
<Form.Switch
checked={uiElement.checked || false}
label={uiElement.label}
onChange={(checked) => {
setValue(selectedAdapter, uiElement, key, checked);
}}
Expand Down

0 comments on commit 8dfbdf7

Please sign in to comment.