mirror of
https://github.com/opelly27/streetmerchant.git
synced 2026-05-20 08:47:43 +00:00
refactor: use gts instead of xo
feat: add browser opening to test:notification feat: add c8 and mocha for testing feat: update Docker and ci style: update editorconfig
This commit is contained in:
+15
-15
@@ -7,20 +7,20 @@ import notifier from 'node-notifier';
|
||||
const {desktop} = config.notifications;
|
||||
|
||||
export function sendDesktopNotification(link: Link, store: Store) {
|
||||
if (desktop) {
|
||||
logger.debug('↗ sending desktop notification');
|
||||
(async () => {
|
||||
notifier.notify({
|
||||
icon: join(
|
||||
__dirname,
|
||||
'../../docs/assets/images/streetmerchant-logo.png'
|
||||
),
|
||||
message: link.cartUrl ? link.cartUrl : link.url,
|
||||
open: link.cartUrl ? link.cartUrl : link.url,
|
||||
title: Print.inStock(link, store)
|
||||
});
|
||||
if (desktop) {
|
||||
logger.debug('↗ sending desktop notification');
|
||||
(async () => {
|
||||
notifier.notify({
|
||||
icon: join(
|
||||
__dirname,
|
||||
'../../../docs/assets/images/streetmerchant-logo.png'
|
||||
),
|
||||
message: link.cartUrl ? link.cartUrl : link.url,
|
||||
open: link.cartUrl ? link.cartUrl : link.url,
|
||||
title: Print.inStock(link, store),
|
||||
});
|
||||
|
||||
logger.info('✔ desktop notification sent');
|
||||
})();
|
||||
}
|
||||
logger.info('✔ desktop notification sent');
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
+57
-65
@@ -6,84 +6,76 @@ import {logger} from '../logger';
|
||||
const {notifyGroup, webhooks, notifyGroupSeries} = config.notifications.discord;
|
||||
|
||||
function getIdAndToken(webhook: string) {
|
||||
const match = /.*\/webhooks\/(\d+)\/(.+)/.exec(webhook);
|
||||
const match = /.*\/webhooks\/(\d+)\/(.+)/.exec(webhook);
|
||||
|
||||
if (!match) {
|
||||
throw new Error('could not get discord webhook');
|
||||
}
|
||||
if (!match) {
|
||||
throw new Error('could not get discord webhook');
|
||||
}
|
||||
|
||||
return {
|
||||
id: match[1],
|
||||
token: match[2]
|
||||
};
|
||||
return {
|
||||
id: match[1],
|
||||
token: match[2],
|
||||
};
|
||||
}
|
||||
|
||||
export function sendDiscordMessage(link: Link, store: Store) {
|
||||
if (webhooks.length > 0) {
|
||||
logger.debug('↗ sending discord message');
|
||||
if (webhooks.length > 0) {
|
||||
logger.debug('↗ sending discord message');
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle('_**Stock alert!**_')
|
||||
.setDescription(
|
||||
'> provided by [streetmerchant](https://github.com/jef/streetmerchant) with :heart:'
|
||||
)
|
||||
.setThumbnail(
|
||||
'https://raw.githubusercontent.com/jef/streetmerchant/main/docs/assets/images/streetmerchant-logo.png'
|
||||
)
|
||||
.setColor('#52b788')
|
||||
.setTimestamp();
|
||||
(async () => {
|
||||
try {
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle('_**Stock alert!**_')
|
||||
.setDescription(
|
||||
'> provided by [streetmerchant](https://github.com/jef/streetmerchant) with :heart:'
|
||||
)
|
||||
.setThumbnail(
|
||||
'https://raw.githubusercontent.com/jef/streetmerchant/main/docs/assets/images/streetmerchant-logo.png'
|
||||
)
|
||||
.setColor('#52b788')
|
||||
.setTimestamp();
|
||||
|
||||
embed.addField('Store', store.name, true);
|
||||
if (link.price)
|
||||
embed.addField(
|
||||
'Price',
|
||||
`${store.currency}${link.price}`,
|
||||
true
|
||||
);
|
||||
embed.addField('Product Page', link.url);
|
||||
if (link.cartUrl) embed.addField('Add to Cart', link.cartUrl);
|
||||
embed.addField('Brand', link.brand, true);
|
||||
embed.addField('Model', link.model, true);
|
||||
embed.addField('Series', link.series, true);
|
||||
embed.addField('Store', store.name, true);
|
||||
if (link.price)
|
||||
embed.addField('Price', `${store.currency}${link.price}`, true);
|
||||
embed.addField('Product Page', link.url);
|
||||
if (link.cartUrl) embed.addField('Add to Cart', link.cartUrl);
|
||||
embed.addField('Brand', link.brand, true);
|
||||
embed.addField('Model', link.model, true);
|
||||
embed.addField('Series', link.series, true);
|
||||
|
||||
embed.setTimestamp();
|
||||
embed.setTimestamp();
|
||||
|
||||
let notifyText: string[] = [];
|
||||
let notifyText: string[] = [];
|
||||
|
||||
if (notifyGroup) {
|
||||
notifyText = notifyText.concat(notifyGroup);
|
||||
}
|
||||
if (notifyGroup) {
|
||||
notifyText = notifyText.concat(notifyGroup);
|
||||
}
|
||||
|
||||
if (Object.keys(notifyGroupSeries).indexOf(link.series) !== 0) {
|
||||
notifyText = notifyText.concat(
|
||||
notifyGroupSeries[link.series]
|
||||
);
|
||||
}
|
||||
if (Object.keys(notifyGroupSeries).indexOf(link.series) !== 0) {
|
||||
notifyText = notifyText.concat(notifyGroupSeries[link.series]);
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
for (const webhook of webhooks) {
|
||||
const {id, token} = getIdAndToken(webhook);
|
||||
const client = new Discord.WebhookClient(id, token);
|
||||
const promises = [];
|
||||
for (const webhook of webhooks) {
|
||||
const {id, token} = getIdAndToken(webhook);
|
||||
const client = new Discord.WebhookClient(id, token);
|
||||
|
||||
promises.push({
|
||||
client,
|
||||
message: client.send(notifyText.join(' '), {
|
||||
embeds: [embed],
|
||||
username: 'streetmerchant'
|
||||
})
|
||||
});
|
||||
}
|
||||
promises.push({
|
||||
client,
|
||||
message: client.send(notifyText.join(' '), {
|
||||
embeds: [embed],
|
||||
username: 'streetmerchant',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
(await Promise.all(promises)).forEach(({client}) =>
|
||||
client.destroy()
|
||||
);
|
||||
(await Promise.all(promises)).forEach(({client}) => client.destroy());
|
||||
|
||||
logger.info('✔ discord message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send discord message", error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
logger.info('✔ discord message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send discord message", error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
+31
-31
@@ -9,47 +9,47 @@ const {email} = config.notifications;
|
||||
const transportOptions: any = {};
|
||||
|
||||
if (email.username && (email.password || email.smtpAddress)) {
|
||||
transportOptions.auth = {};
|
||||
transportOptions.auth.user = email.username;
|
||||
transportOptions.auth.pass = email.password;
|
||||
transportOptions.auth = {};
|
||||
transportOptions.auth.user = email.username;
|
||||
transportOptions.auth.pass = email.password;
|
||||
}
|
||||
|
||||
if (email.smtpAddress) {
|
||||
transportOptions.host = email.smtpAddress;
|
||||
transportOptions.port = email.smtpPort;
|
||||
transportOptions.host = email.smtpAddress;
|
||||
transportOptions.port = email.smtpPort;
|
||||
} else {
|
||||
transportOptions.service = 'gmail';
|
||||
transportOptions.service = 'gmail';
|
||||
}
|
||||
|
||||
export const transporter = nodemailer.createTransport({
|
||||
...transportOptions
|
||||
...transportOptions,
|
||||
});
|
||||
|
||||
export function sendEmail(link: Link, store: Store) {
|
||||
if (email.username && (email.password || email.smtpAddress)) {
|
||||
logger.debug('↗ sending email');
|
||||
if (email.username && (email.password || email.smtpAddress)) {
|
||||
logger.debug('↗ sending email');
|
||||
|
||||
const mailOptions: Mail.Options = {
|
||||
attachments: link.screenshot
|
||||
? [
|
||||
{
|
||||
filename: link.screenshot,
|
||||
path: `./${link.screenshot}`
|
||||
}
|
||||
]
|
||||
: undefined,
|
||||
from: email.username,
|
||||
subject: Print.inStock(link, store),
|
||||
text: Print.productInStock(link),
|
||||
to: email.to
|
||||
};
|
||||
const mailOptions: Mail.Options = {
|
||||
attachments: link.screenshot
|
||||
? [
|
||||
{
|
||||
filename: link.screenshot,
|
||||
path: `./${link.screenshot}`,
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
from: email.username,
|
||||
subject: Print.inStock(link, store),
|
||||
text: Print.productInStock(link),
|
||||
to: email.to,
|
||||
};
|
||||
|
||||
transporter.sendMail(mailOptions, (error) => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't send email", error);
|
||||
} else {
|
||||
logger.info('✔ email sent');
|
||||
}
|
||||
});
|
||||
}
|
||||
transporter.sendMail(mailOptions, error => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't send email", error);
|
||||
} else {
|
||||
logger.info('✔ email sent');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+67
-67
@@ -7,60 +7,60 @@ const {mqtt} = config.notifications;
|
||||
let client: MqttClient.Client;
|
||||
|
||||
if (mqtt.broker) {
|
||||
if (checkInsecureUsage(mqtt.password, mqtt.broker)) {
|
||||
logger.warn(
|
||||
'✖ Insecure transport of password - Only use credentials with MQTT brokers on private networks.'
|
||||
);
|
||||
} else {
|
||||
const clientOptions: IClientOptions = {
|
||||
clean: mqtt.clientId === '',
|
||||
clientId: mqtt.clientId === '' ? undefined : mqtt.clientId,
|
||||
password: mqtt.password === '' ? undefined : mqtt.password,
|
||||
username: mqtt.username === '' ? undefined : mqtt.username
|
||||
};
|
||||
client = MqttClient.connect(
|
||||
`mqtt://${mqtt.broker}:${mqtt.port}`,
|
||||
clientOptions
|
||||
);
|
||||
}
|
||||
if (checkInsecureUsage(mqtt.password, mqtt.broker)) {
|
||||
logger.warn(
|
||||
'✖ Insecure transport of password - Only use credentials with MQTT brokers on private networks.'
|
||||
);
|
||||
} else {
|
||||
const clientOptions: IClientOptions = {
|
||||
clean: mqtt.clientId === '',
|
||||
clientId: mqtt.clientId === '' ? undefined : mqtt.clientId,
|
||||
password: mqtt.password === '' ? undefined : mqtt.password,
|
||||
username: mqtt.username === '' ? undefined : mqtt.username,
|
||||
};
|
||||
client = MqttClient.connect(
|
||||
`mqtt://${mqtt.broker}:${mqtt.port}`,
|
||||
clientOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function sendMqttMessage(link: Link, store: Store) {
|
||||
if (client) {
|
||||
logger.debug('↗ sending mqtt message');
|
||||
if (client) {
|
||||
logger.debug('↗ sending mqtt message');
|
||||
|
||||
(async () => {
|
||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||
const message = `{"msg":"${Print.inStock(
|
||||
link,
|
||||
store
|
||||
)}", "url":"${givenUrl}"}`;
|
||||
const topic = generateTopic(link, store, mqtt.topic);
|
||||
const pubOptions: IClientPublishOptions = {
|
||||
qos: mqtt.qos as 0 | 1 | 2,
|
||||
retain: false
|
||||
};
|
||||
(async () => {
|
||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||
const message = `{"msg":"${Print.inStock(
|
||||
link,
|
||||
store
|
||||
)}", "url":"${givenUrl}"}`;
|
||||
const topic = generateTopic(link, store, mqtt.topic);
|
||||
const pubOptions: IClientPublishOptions = {
|
||||
qos: mqtt.qos as 0 | 1 | 2,
|
||||
retain: false,
|
||||
};
|
||||
|
||||
try {
|
||||
client.publish(topic, message, pubOptions);
|
||||
logger.info('✔ mqtt message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send mqtt message", error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
try {
|
||||
client.publish(topic, message, pubOptions);
|
||||
logger.info('✔ mqtt message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send mqtt message", error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
function generateTopic(link: Link, store: Store, topic: string): string {
|
||||
topic.trim();
|
||||
topic = topic.replace(/^\//, '');
|
||||
topic = topic
|
||||
.replace(/%series%/g, link.series)
|
||||
.replace(/%brand%/g, link.brand)
|
||||
.replace(/%model%/g, link.model)
|
||||
.replace(/%store%/g, store.name);
|
||||
topic.trim();
|
||||
topic = topic.replace(/^\//, '');
|
||||
topic = topic
|
||||
.replace(/%series%/g, link.series)
|
||||
.replace(/%brand%/g, link.brand)
|
||||
.replace(/%model%/g, link.model)
|
||||
.replace(/%store%/g, store.name);
|
||||
|
||||
return topic;
|
||||
return topic;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,43 +72,43 @@ function generateTopic(link: Link, store: Store, topic: string): string {
|
||||
*
|
||||
*/
|
||||
function checkInsecureUsage(pass: string, address: string): boolean {
|
||||
if (pass !== '') {
|
||||
if (
|
||||
isClassANet(address) ||
|
||||
isClassBNet(address) ||
|
||||
isClassCNet(address) ||
|
||||
isLinkLocal(address)
|
||||
) {
|
||||
logger.debug(`MQTT using private network broker: ${address}`);
|
||||
} else {
|
||||
logger.debug(`MQTT using public network broker: ${address}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (pass !== '') {
|
||||
if (
|
||||
isClassANet(address) ||
|
||||
isClassBNet(address) ||
|
||||
isClassCNet(address) ||
|
||||
isLinkLocal(address)
|
||||
) {
|
||||
logger.debug(`MQTT using private network broker: ${address}`);
|
||||
} else {
|
||||
logger.debug(`MQTT using public network broker: ${address}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
function isClassANet(address: string): boolean {
|
||||
const classRegex = /^(10\.(\d|[1-9]\d|[12][0-5]{2})\.(\d|[1-9]\d|[12][0-5]{2})\.(\d|[1-9]\d|[12][0-5]{2}))$/;
|
||||
const classRegex = /^(10\.(\d|[1-9]\d|[12][0-5]{2})\.(\d|[1-9]\d|[12][0-5]{2})\.(\d|[1-9]\d|[12][0-5]{2}))$/;
|
||||
|
||||
return Boolean(classRegex.exec(address));
|
||||
return Boolean(classRegex.exec(address));
|
||||
}
|
||||
|
||||
function isClassBNet(address: string): boolean {
|
||||
const classRegex = /^(172\.(1[6-9]|2\d|3[01])\.(\d|[1-9]\d|[12][0-5]{2})\.(\d|[1-9]\d|[12][0-5]{2}))$/;
|
||||
const classRegex = /^(172\.(1[6-9]|2\d|3[01])\.(\d|[1-9]\d|[12][0-5]{2})\.(\d|[1-9]\d|[12][0-5]{2}))$/;
|
||||
|
||||
return Boolean(classRegex.exec(address));
|
||||
return Boolean(classRegex.exec(address));
|
||||
}
|
||||
|
||||
function isClassCNet(address: string): boolean {
|
||||
const classRegex = /^(192\.168\.(\d|[1-9]\d|[12][0-5]{2})\.(\d|[1-9]\d|[12][0-5]{2}))$/;
|
||||
const classRegex = /^(192\.168\.(\d|[1-9]\d|[12][0-5]{2})\.(\d|[1-9]\d|[12][0-5]{2}))$/;
|
||||
|
||||
return Boolean(classRegex.exec(address));
|
||||
return Boolean(classRegex.exec(address));
|
||||
}
|
||||
|
||||
function isLinkLocal(address: string): boolean {
|
||||
const linkLocal = /.+\.local$/;
|
||||
const linkLocal = /.+\.local$/;
|
||||
|
||||
return Boolean(linkLocal.exec(address));
|
||||
return Boolean(linkLocal.exec(address));
|
||||
}
|
||||
|
||||
@@ -17,22 +17,22 @@ import {sendTwitchMessage} from './twitch';
|
||||
import {updateRedis} from './redis';
|
||||
|
||||
export function sendNotification(link: Link, store: Store) {
|
||||
// Priority
|
||||
playSound();
|
||||
sendDiscordMessage(link, store);
|
||||
sendDesktopNotification(link, store);
|
||||
sendEmail(link, store);
|
||||
sendSms(link, store);
|
||||
// Non-priority
|
||||
adjustPhilipsHueLights();
|
||||
sendMqttMessage(link, store);
|
||||
sendPagerDutyNotification(link, store);
|
||||
sendPushbulletNotification(link, store);
|
||||
sendPushoverNotification(link, store);
|
||||
sendSlackMessage(link, store);
|
||||
sendTelegramMessage(link, store);
|
||||
sendTweet(link, store);
|
||||
sendTwilioMessage(link, store);
|
||||
sendTwitchMessage(link, store);
|
||||
updateRedis(link, store);
|
||||
// Priority
|
||||
playSound();
|
||||
sendDiscordMessage(link, store);
|
||||
sendDesktopNotification(link, store);
|
||||
sendEmail(link, store);
|
||||
sendSms(link, store);
|
||||
// Non-priority
|
||||
adjustPhilipsHueLights();
|
||||
sendMqttMessage(link, store);
|
||||
sendPagerDutyNotification(link, store);
|
||||
sendPushbulletNotification(link, store);
|
||||
sendPushoverNotification(link, store);
|
||||
sendSlackMessage(link, store);
|
||||
sendTelegramMessage(link, store);
|
||||
sendTweet(link, store);
|
||||
sendTwilioMessage(link, store);
|
||||
sendTwitchMessage(link, store);
|
||||
updateRedis(link, store);
|
||||
}
|
||||
|
||||
@@ -6,26 +6,26 @@ import {config} from '../config';
|
||||
const pd = new PDClient('');
|
||||
|
||||
export function sendPagerDutyNotification(link: Link, store: Store) {
|
||||
if (config.notifications.pagerduty.integrationKey) {
|
||||
logger.debug('↗ sending pagerduty message');
|
||||
const links = [{href: link.url, text: 'Visit Store'}];
|
||||
if (link.cartUrl) {
|
||||
links.push({
|
||||
href: link.cartUrl,
|
||||
text: 'Add to Cart'
|
||||
});
|
||||
}
|
||||
if (config.notifications.pagerduty.integrationKey) {
|
||||
logger.debug('↗ sending pagerduty message');
|
||||
const links = [{href: link.url, text: 'Visit Store'}];
|
||||
if (link.cartUrl) {
|
||||
links.push({
|
||||
href: link.cartUrl,
|
||||
text: 'Add to Cart',
|
||||
});
|
||||
}
|
||||
|
||||
pd.events.sendEvent({
|
||||
dedup_key: link.url,
|
||||
event_action: 'trigger',
|
||||
payload: {
|
||||
links,
|
||||
severity: config.notifications.pagerduty.severity,
|
||||
source: store.name,
|
||||
summary: Print.inStock(link, store)
|
||||
},
|
||||
routing_key: config.notifications.pagerduty.integrationKey
|
||||
});
|
||||
}
|
||||
pd.events.sendEvent({
|
||||
dedup_key: link.url,
|
||||
event_action: 'trigger',
|
||||
payload: {
|
||||
links,
|
||||
severity: config.notifications.pagerduty.severity,
|
||||
source: store.name,
|
||||
summary: Print.inStock(link, store),
|
||||
},
|
||||
routing_key: config.notifications.pagerduty.integrationKey,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+108
-120
@@ -5,135 +5,123 @@ import {logger} from '../logger';
|
||||
|
||||
const {LightState} = hueAPI.lightStates;
|
||||
const {
|
||||
apiKey,
|
||||
bridgeIp,
|
||||
lightIds,
|
||||
lightColor,
|
||||
lightPattern,
|
||||
clientId,
|
||||
clientSecret,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
remoteApiUsername
|
||||
apiKey,
|
||||
bridgeIp,
|
||||
lightIds,
|
||||
lightColor,
|
||||
lightPattern,
|
||||
clientId,
|
||||
clientSecret,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
remoteApiUsername,
|
||||
} = config.notifications.philips_hue;
|
||||
|
||||
// Default Light State
|
||||
const lightState = new LightState()
|
||||
.on(true)
|
||||
.brightness(100)
|
||||
.rgb(46.27, 72.55, 0);
|
||||
.on(true)
|
||||
.brightness(100)
|
||||
.rgb(46.27, 72.55, 0);
|
||||
|
||||
const adjustLightsWithAPI = (hueBridge: Api) => {
|
||||
logger.debug('Connected to Philips Hue bridge.');
|
||||
// Set the custom light state (COLOR and METHOD here)
|
||||
if (lightColor) {
|
||||
const rgbArray = lightColor.split(',');
|
||||
// If there's not three values, must not be RGB
|
||||
if (rgbArray.length === 3) {
|
||||
lightState.rgb(rgbArray[0], rgbArray[1], rgbArray[2]);
|
||||
} else {
|
||||
logger.debug('✖ Error assigning RGB Values');
|
||||
}
|
||||
}
|
||||
logger.debug('Connected to Philips Hue bridge.');
|
||||
// Set the custom light state (COLOR and METHOD here)
|
||||
if (lightColor) {
|
||||
const rgbArray = lightColor.split(',');
|
||||
// If there's not three values, must not be RGB
|
||||
if (rgbArray.length === 3) {
|
||||
lightState.rgb(rgbArray[0], rgbArray[1], rgbArray[2]);
|
||||
} else {
|
||||
logger.debug('✖ Error assigning RGB Values');
|
||||
}
|
||||
}
|
||||
|
||||
// If blink is specified, then blink the lights
|
||||
if (lightPattern === 'blink') {
|
||||
lightState.alertLong();
|
||||
}
|
||||
// If blink is specified, then blink the lights
|
||||
if (lightPattern === 'blink') {
|
||||
lightState.alertLong();
|
||||
}
|
||||
|
||||
// If we've been given light IDs, then only adjust those IDs
|
||||
if (lightIds) {
|
||||
const arrayOfIDs = lightIds.split(',');
|
||||
arrayOfIDs.forEach((light) => {
|
||||
logger.debug('adjusting specified lights');
|
||||
(hueBridge.lights.setLightState(
|
||||
light,
|
||||
lightState
|
||||
) as Promise<any>).catch((error: Error) => {
|
||||
logger.error('Failed to adjust specified lights.');
|
||||
logger.error(error);
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Adjust all light IDs
|
||||
hueBridge.lights
|
||||
.getAll()
|
||||
.then((allLights: any[]) => {
|
||||
allLights.forEach((light: any) => {
|
||||
logger.debug('adjusting all hue lights');
|
||||
(hueBridge.lights.setLightState(
|
||||
light,
|
||||
lightState
|
||||
) as Promise<any>).catch((error: Error) => {
|
||||
logger.error('Failed to adjust all lights.');
|
||||
logger.error(error);
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
logger.error('Failed to get all lights.');
|
||||
logger.error(error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
// If we've been given light IDs, then only adjust those IDs
|
||||
if (lightIds) {
|
||||
const arrayOfIDs = lightIds.split(',');
|
||||
arrayOfIDs.forEach(light => {
|
||||
logger.debug('adjusting specified lights');
|
||||
(hueBridge.lights.setLightState(light, lightState) as Promise<any>).catch(
|
||||
(error: Error) => {
|
||||
logger.error('Failed to adjust specified lights.');
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Adjust all light IDs
|
||||
hueBridge.lights
|
||||
.getAll()
|
||||
.then((allLights: any[]) => {
|
||||
allLights.forEach((light: any) => {
|
||||
logger.debug('adjusting all hue lights');
|
||||
(hueBridge.lights.setLightState(
|
||||
light,
|
||||
lightState
|
||||
) as Promise<any>).catch((error: Error) => {
|
||||
logger.error('Failed to adjust all lights.');
|
||||
logger.error(error);
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
logger.error('Failed to get all lights.');
|
||||
logger.error(error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function adjustPhilipsHueLights() {
|
||||
// Check if the required variables have been set
|
||||
if (apiKey && bridgeIp) {
|
||||
logger.info('↗ adjusting Philips Hue lights over LAN');
|
||||
(async () => {
|
||||
logger.debug(
|
||||
'Attempting to connect to Philips Hue bridge at ' + bridgeIp
|
||||
);
|
||||
hueAPI.api
|
||||
.createLocal(bridgeIp)
|
||||
.connect(apiKey)
|
||||
.then(
|
||||
(hueBridge) => {
|
||||
adjustLightsWithAPI(hueBridge);
|
||||
logger.info('✔ adjusted Philips Hue lights over LAN');
|
||||
},
|
||||
(error: Error) => {
|
||||
logger.error("✖ couldn't adjust hue lights.", error);
|
||||
}
|
||||
);
|
||||
})();
|
||||
} else if (apiKey && clientId && clientSecret) {
|
||||
logger.info('↗ adjusting Philips Hue lights over cloud');
|
||||
(async () => {
|
||||
logger.debug(
|
||||
'Attempting to connect to Philips Hue bridge over cloud'
|
||||
);
|
||||
const remoteBootstrap = hueAPI.api.createRemote(
|
||||
clientId,
|
||||
clientSecret
|
||||
);
|
||||
if (accessToken && refreshToken) {
|
||||
remoteBootstrap
|
||||
.connectWithTokens(
|
||||
accessToken,
|
||||
refreshToken,
|
||||
remoteApiUsername
|
||||
)
|
||||
.then(
|
||||
(hueBridge) => {
|
||||
adjustLightsWithAPI(hueBridge);
|
||||
logger.info(
|
||||
'✔ adjusted Philips Hue lights over cloud'
|
||||
);
|
||||
},
|
||||
(error: Error) => {
|
||||
logger.error(
|
||||
'Failed to get a remote Philips Hue connection using supplied tokens.'
|
||||
);
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
}
|
||||
})();
|
||||
}
|
||||
// Check if the required variables have been set
|
||||
if (apiKey && bridgeIp) {
|
||||
logger.info('↗ adjusting Philips Hue lights over LAN');
|
||||
(async () => {
|
||||
logger.debug(
|
||||
'Attempting to connect to Philips Hue bridge at ' + bridgeIp
|
||||
);
|
||||
hueAPI.api
|
||||
.createLocal(bridgeIp)
|
||||
.connect(apiKey)
|
||||
.then(
|
||||
hueBridge => {
|
||||
adjustLightsWithAPI(hueBridge);
|
||||
logger.info('✔ adjusted Philips Hue lights over LAN');
|
||||
},
|
||||
(error: Error) => {
|
||||
logger.error("✖ couldn't adjust hue lights.", error);
|
||||
}
|
||||
);
|
||||
})();
|
||||
} else if (apiKey && clientId && clientSecret) {
|
||||
logger.info('↗ adjusting Philips Hue lights over cloud');
|
||||
(async () => {
|
||||
logger.debug('Attempting to connect to Philips Hue bridge over cloud');
|
||||
const remoteBootstrap = hueAPI.api.createRemote(clientId, clientSecret);
|
||||
if (accessToken && refreshToken) {
|
||||
remoteBootstrap
|
||||
.connectWithTokens(accessToken, refreshToken, remoteApiUsername)
|
||||
.then(
|
||||
hueBridge => {
|
||||
adjustLightsWithAPI(hueBridge);
|
||||
logger.info('✔ adjusted Philips Hue lights over cloud');
|
||||
},
|
||||
(error: Error) => {
|
||||
logger.error(
|
||||
'Failed to get a remote Philips Hue connection using supplied tokens.'
|
||||
);
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,22 +6,22 @@ import {config} from '../config';
|
||||
const {pushbullet} = config.notifications;
|
||||
|
||||
export function sendPushbulletNotification(link: Link, store: Store) {
|
||||
if (pushbullet) {
|
||||
logger.debug('↗ sending pushbullet message');
|
||||
if (pushbullet) {
|
||||
logger.debug('↗ sending pushbullet message');
|
||||
|
||||
const pusher = new PushBullet(pushbullet);
|
||||
const pusher = new PushBullet(pushbullet);
|
||||
|
||||
pusher.note(
|
||||
{},
|
||||
Print.inStock(link, store),
|
||||
link.cartUrl ? link.cartUrl : link.url,
|
||||
(error: Error) => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't send pushbullet message", error);
|
||||
} else {
|
||||
logger.info('✔ pushbullet message sent');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
pusher.note(
|
||||
{},
|
||||
Print.inStock(link, store),
|
||||
link.cartUrl ? link.cartUrl : link.url,
|
||||
(error: Error) => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't send pushbullet message", error);
|
||||
} else {
|
||||
logger.info('✔ pushbullet message sent');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,37 +6,37 @@ import {config} from '../config';
|
||||
const {pushover} = config.notifications;
|
||||
|
||||
export function sendPushoverNotification(link: Link, store: Store) {
|
||||
if (pushover.token && pushover.username) {
|
||||
logger.debug('↗ sending pushover message');
|
||||
if (pushover.token && pushover.username) {
|
||||
logger.debug('↗ sending pushover message');
|
||||
|
||||
const push = new Push({
|
||||
token: pushover.token,
|
||||
user: pushover.username
|
||||
});
|
||||
const push = new Push({
|
||||
token: pushover.token,
|
||||
user: pushover.username,
|
||||
});
|
||||
|
||||
const message: PushoverMessage =
|
||||
pushover.priority < 2
|
||||
? {
|
||||
message: link.cartUrl ? link.cartUrl : link.url,
|
||||
priority: pushover.priority,
|
||||
title: Print.inStock(link, store),
|
||||
...(link.screenshot && {file: `./${link.screenshot}`})
|
||||
}
|
||||
: {
|
||||
expire: pushover.expire,
|
||||
message: link.cartUrl ? link.cartUrl : link.url,
|
||||
priority: pushover.priority,
|
||||
retry: pushover.retry,
|
||||
title: Print.inStock(link, store),
|
||||
...(link.screenshot && {file: `./${link.screenshot}`})
|
||||
};
|
||||
const message: PushoverMessage =
|
||||
pushover.priority < 2
|
||||
? {
|
||||
message: link.cartUrl ? link.cartUrl : link.url,
|
||||
priority: pushover.priority,
|
||||
title: Print.inStock(link, store),
|
||||
...(link.screenshot && {file: `./${link.screenshot}`}),
|
||||
}
|
||||
: {
|
||||
expire: pushover.expire,
|
||||
message: link.cartUrl ? link.cartUrl : link.url,
|
||||
priority: pushover.priority,
|
||||
retry: pushover.retry,
|
||||
title: Print.inStock(link, store),
|
||||
...(link.screenshot && {file: `./${link.screenshot}`}),
|
||||
};
|
||||
|
||||
push.send(message, (error: Error) => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't send pushover message", error);
|
||||
} else {
|
||||
logger.info('✔ pushover message sent');
|
||||
}
|
||||
});
|
||||
}
|
||||
push.send(message, (error: Error) => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't send pushover message", error);
|
||||
} else {
|
||||
logger.info('✔ pushover message sent');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+26
-26
@@ -7,39 +7,39 @@ const {url} = config.notifications.redis;
|
||||
let client: RedisClient;
|
||||
|
||||
function initRedis(): RedisClient | null {
|
||||
if (url) {
|
||||
client = redis.createClient({url});
|
||||
}
|
||||
if (url) {
|
||||
client = redis.createClient({url});
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function updateRedis(link: Link, store: Store) {
|
||||
try {
|
||||
if (client) {
|
||||
const key = `${store.name}:${link.brand}:${link.model}`
|
||||
.split(' ')
|
||||
.join('-');
|
||||
try {
|
||||
if (client) {
|
||||
const key = `${store.name}:${link.brand}:${link.model}`
|
||||
.split(' ')
|
||||
.join('-');
|
||||
|
||||
const value = {
|
||||
...link,
|
||||
labels: store.labels,
|
||||
links: store.links,
|
||||
name: store.name,
|
||||
updatedAt: new Date().toUTCString()
|
||||
};
|
||||
const value = {
|
||||
...link,
|
||||
labels: store.labels,
|
||||
links: store.links,
|
||||
name: store.name,
|
||||
updatedAt: new Date().toUTCString(),
|
||||
};
|
||||
|
||||
const redisUpdated = client.set(key, JSON.stringify(value));
|
||||
const redisUpdated = client.set(key, JSON.stringify(value));
|
||||
|
||||
if (redisUpdated) {
|
||||
logger.info('✔ redis updated');
|
||||
} else {
|
||||
logger.error(`✖ couldn't update redis for key (${key})`);
|
||||
}
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't update redis", error);
|
||||
}
|
||||
if (redisUpdated) {
|
||||
logger.info('✔ redis updated');
|
||||
} else {
|
||||
logger.error(`✖ couldn't update redis for key (${key})`);
|
||||
}
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't update redis", error);
|
||||
}
|
||||
}
|
||||
|
||||
initRedis();
|
||||
|
||||
+19
-19
@@ -7,27 +7,27 @@ const {channel, token} = config.notifications.slack;
|
||||
const web = new WebClient(token);
|
||||
|
||||
export function sendSlackMessage(link: Link, store: Store) {
|
||||
if (channel && token) {
|
||||
logger.debug('↗ sending slack message');
|
||||
if (channel && token) {
|
||||
logger.debug('↗ sending slack message');
|
||||
|
||||
(async () => {
|
||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||
(async () => {
|
||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||
|
||||
try {
|
||||
const result = await web.chat.postMessage({
|
||||
channel: channel.replace('#', ''),
|
||||
text: `${Print.inStock(link, store)}\n${givenUrl}`
|
||||
});
|
||||
try {
|
||||
const result = await web.chat.postMessage({
|
||||
channel: channel.replace('#', ''),
|
||||
text: `${Print.inStock(link, store)}\n${givenUrl}`,
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
logger.error("✖ couldn't send slack message", result);
|
||||
return;
|
||||
}
|
||||
if (!result.ok) {
|
||||
logger.error("✖ couldn't send slack message", result);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('✔ slack message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send slack message", error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
logger.info('✔ slack message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send slack message", error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
+57
-55
@@ -7,72 +7,74 @@ import {transporter} from './email';
|
||||
const {email, phone} = config.notifications;
|
||||
|
||||
if (phone.number.length > 0 && (!email.username || !email.password)) {
|
||||
logger.warn(
|
||||
'✖ in order to receive sms alerts, email notifications must also be configured'
|
||||
);
|
||||
logger.warn(
|
||||
'✖ in order to receive sms alerts, email notifications must also be configured'
|
||||
);
|
||||
}
|
||||
|
||||
if (phone.carrier.length !== phone.number.length) {
|
||||
logger.warn(
|
||||
'✖ the number of carriers must match the number of phone numbers',
|
||||
{carrier: phone.carrier, number: phone.number}
|
||||
);
|
||||
logger.warn(
|
||||
'✖ the number of carriers must match the number of phone numbers',
|
||||
{carrier: phone.carrier, number: phone.number}
|
||||
);
|
||||
}
|
||||
|
||||
export function sendSms(link: Link, store: Store) {
|
||||
for (
|
||||
let i = 0;
|
||||
i < Math.max(phone.number.length, phone.carrier.length);
|
||||
i++
|
||||
) {
|
||||
const currentNumber = phone.number[i];
|
||||
const currentCarrier = phone.carrier[i];
|
||||
for (
|
||||
let i = 0;
|
||||
i < Math.max(phone.number.length, phone.carrier.length);
|
||||
i++
|
||||
) {
|
||||
const currentNumber = phone.number[i];
|
||||
const currentCarrier = phone.carrier[i];
|
||||
|
||||
if (!currentNumber) {
|
||||
logger.error(`✖ ${currentCarrier} is not associated with a number`);
|
||||
continue;
|
||||
} else if (!currentCarrier) {
|
||||
logger.error(`✖ ${currentNumber} is not associated with a carrier`);
|
||||
continue;
|
||||
}
|
||||
if (!currentNumber) {
|
||||
logger.error(`✖ ${currentCarrier} is not associated with a number`);
|
||||
continue;
|
||||
} else if (!currentCarrier) {
|
||||
logger.error(`✖ ${currentNumber} is not associated with a carrier`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!phone.availableCarriers.has(currentCarrier)) {
|
||||
logger.error(`✖ unknown carrier ${currentCarrier}`);
|
||||
continue;
|
||||
}
|
||||
if (!phone.availableCarriers.has(currentCarrier)) {
|
||||
logger.error(`✖ unknown carrier ${currentCarrier}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.debug('↗ sending sms');
|
||||
logger.debug('↗ sending sms');
|
||||
|
||||
const mailOptions: Mail.Options = {
|
||||
attachments: link.screenshot
|
||||
? [
|
||||
{
|
||||
filename: link.screenshot,
|
||||
path: `./${link.screenshot}`
|
||||
}
|
||||
]
|
||||
: undefined,
|
||||
from: email.username,
|
||||
subject: Print.inStock(link, store, false, true),
|
||||
text: link.cartUrl ? link.cartUrl : link.url,
|
||||
to: generateAddress(currentNumber, currentCarrier)
|
||||
};
|
||||
const mailOptions: Mail.Options = {
|
||||
attachments: link.screenshot
|
||||
? [
|
||||
{
|
||||
filename: link.screenshot,
|
||||
path: `./${link.screenshot}`,
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
from: email.username,
|
||||
subject: Print.inStock(link, store, false, true),
|
||||
text: link.cartUrl ? link.cartUrl : link.url,
|
||||
to: generateAddress(currentNumber, currentCarrier),
|
||||
};
|
||||
|
||||
transporter.sendMail(mailOptions, (error) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
`✖ couldn't send sms to ${currentNumber} for carrier ${currentCarrier}`,
|
||||
error
|
||||
);
|
||||
} else {
|
||||
logger.info('✔ sms sent');
|
||||
}
|
||||
});
|
||||
}
|
||||
transporter.sendMail(mailOptions, error => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
`✖ couldn't send sms to ${currentNumber} for carrier ${currentCarrier}`,
|
||||
error
|
||||
);
|
||||
} else {
|
||||
logger.info('✔ sms sent');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function generateAddress(number: string, carrier: string) {
|
||||
if (carrier && phone.availableCarriers.has(carrier)) {
|
||||
return [number, phone.availableCarriers.get(carrier)].join('@');
|
||||
}
|
||||
function generateAddress(number: string, carrier: string): string {
|
||||
if (carrier && phone.availableCarriers.has(carrier)) {
|
||||
return [number, phone.availableCarriers.get(carrier)].join('@');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
+24
-30
@@ -6,41 +6,35 @@ import {logger} from '../logger';
|
||||
let player: PlaySound;
|
||||
|
||||
if (config.notifications.playSound) {
|
||||
player = config.notifications.soundPlayer
|
||||
? playerLib({players: [config.notifications.soundPlayer]})
|
||||
: playerLib();
|
||||
player = config.notifications.soundPlayer
|
||||
? playerLib({players: [config.notifications.soundPlayer]})
|
||||
: playerLib();
|
||||
|
||||
if (player.player === null) {
|
||||
logger.warn("✖ couldn't find sound player");
|
||||
} else {
|
||||
const playerName = player.player;
|
||||
logger.info(`✔ sound player found: ${playerName}`);
|
||||
}
|
||||
if (player.player === null) {
|
||||
logger.warn("✖ couldn't find sound player");
|
||||
} else {
|
||||
const playerName = player.player;
|
||||
logger.info(`✔ sound player found: ${playerName}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function playSound() {
|
||||
if (config.notifications.playSound && player.player !== null) {
|
||||
logger.debug('↗ playing sound');
|
||||
if (config.notifications.playSound && player.player !== null) {
|
||||
logger.debug('↗ playing sound');
|
||||
|
||||
fs.access(
|
||||
config.notifications.playSound,
|
||||
fs.constants.F_OK,
|
||||
(error) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
`✖ error opening sound file: ${error.message}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
fs.access(config.notifications.playSound, fs.constants.F_OK, error => {
|
||||
if (error) {
|
||||
logger.error(`✖ error opening sound file: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
player.play(config.notifications.playSound, (error: Error) => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't play sound", error);
|
||||
}
|
||||
player.play(config.notifications.playSound, (error: Error) => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't play sound", error);
|
||||
}
|
||||
|
||||
logger.info('✔ played sound');
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
logger.info('✔ played sound');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,32 +6,32 @@ import {config} from '../config';
|
||||
const {telegram} = config.notifications;
|
||||
|
||||
const client = new TelegramClient({
|
||||
accessToken: telegram.accessToken
|
||||
accessToken: telegram.accessToken,
|
||||
});
|
||||
|
||||
export function sendTelegramMessage(link: Link, store: Store) {
|
||||
if (telegram.accessToken && telegram.chatId) {
|
||||
logger.debug('↗ sending telegram message');
|
||||
if (telegram.accessToken && telegram.chatId) {
|
||||
logger.debug('↗ sending telegram message');
|
||||
|
||||
(async () => {
|
||||
const message = Print.productInStock(link);
|
||||
const results = [];
|
||||
(async () => {
|
||||
const message = Print.productInStock(link);
|
||||
const results = [];
|
||||
|
||||
for (const chatId of telegram.chatId) {
|
||||
try {
|
||||
results.push(
|
||||
client.sendMessage(
|
||||
chatId,
|
||||
`${Print.inStock(link, store)}\n${message}`
|
||||
)
|
||||
);
|
||||
logger.info('✔ telegram message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send telegram message", error);
|
||||
}
|
||||
}
|
||||
for (const chatId of telegram.chatId) {
|
||||
try {
|
||||
results.push(
|
||||
client.sendMessage(
|
||||
chatId,
|
||||
`${Print.inStock(link, store)}\n${message}`
|
||||
)
|
||||
);
|
||||
logger.info('✔ telegram message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send telegram message", error);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(results);
|
||||
})();
|
||||
}
|
||||
await Promise.all(results);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
+25
-25
@@ -7,34 +7,34 @@ const {twilio} = config.notifications;
|
||||
let client: Twilio;
|
||||
|
||||
if (twilio.accountSid && twilio.authToken) {
|
||||
client = new Twilio(twilio.accountSid, twilio.authToken);
|
||||
client = new Twilio(twilio.accountSid, twilio.authToken);
|
||||
}
|
||||
|
||||
export function sendTwilioMessage(link: Link, store: Store) {
|
||||
if (client) {
|
||||
logger.debug('↗ sending twilio message');
|
||||
if (client) {
|
||||
logger.debug('↗ sending twilio message');
|
||||
|
||||
(async () => {
|
||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||
const message = `${Print.inStock(link, store)}\n${givenUrl}`;
|
||||
const numbers = twilio.to.split(',');
|
||||
const results = [];
|
||||
for (const number of numbers) {
|
||||
try {
|
||||
results.push(
|
||||
client.messages.create({
|
||||
body: message,
|
||||
from: twilio.from,
|
||||
to: number
|
||||
})
|
||||
);
|
||||
logger.info('✔ twilio message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send twilio message", error);
|
||||
}
|
||||
}
|
||||
(async () => {
|
||||
const givenUrl = link.cartUrl ? link.cartUrl : link.url;
|
||||
const message = `${Print.inStock(link, store)}\n${givenUrl}`;
|
||||
const numbers = twilio.to.split(',');
|
||||
const results = [];
|
||||
for (const number of numbers) {
|
||||
try {
|
||||
results.push(
|
||||
client.messages.create({
|
||||
body: message,
|
||||
from: twilio.from,
|
||||
to: number,
|
||||
})
|
||||
);
|
||||
logger.info('✔ twilio message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send twilio message", error);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(results);
|
||||
})();
|
||||
}
|
||||
await Promise.all(results);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
+68
-72
@@ -11,95 +11,91 @@ const messages: string[] = [];
|
||||
let alreadySaying = false;
|
||||
|
||||
let tokenData = {
|
||||
accessToken: twitch.accessToken,
|
||||
expiryTimestamp: 0,
|
||||
refreshToken: twitch.refreshToken
|
||||
accessToken: twitch.accessToken,
|
||||
expiryTimestamp: 0,
|
||||
refreshToken: twitch.refreshToken,
|
||||
};
|
||||
|
||||
if (existsSync('./twitch.json')) {
|
||||
tokenData = {
|
||||
...JSON.parse(readFileSync('./twitch.json', 'utf-8')),
|
||||
...tokenData
|
||||
};
|
||||
tokenData = {
|
||||
...JSON.parse(readFileSync('./twitch.json', 'utf-8')),
|
||||
...tokenData,
|
||||
};
|
||||
}
|
||||
|
||||
const chatClient: ChatClient = new ChatClient(
|
||||
new RefreshableAuthProvider(
|
||||
new StaticAuthProvider(twitch.clientId, tokenData.accessToken),
|
||||
{
|
||||
clientSecret: twitch.clientSecret,
|
||||
expiry:
|
||||
tokenData.expiryTimestamp === null
|
||||
? null
|
||||
: new Date(tokenData.expiryTimestamp),
|
||||
onRefresh: async ({accessToken, refreshToken, expiryDate}) => {
|
||||
return promises.writeFile(
|
||||
'./twitch.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
accessToken,
|
||||
expiryTimestamp:
|
||||
expiryDate === null
|
||||
? null
|
||||
: expiryDate.getTime(),
|
||||
refreshToken
|
||||
},
|
||||
null,
|
||||
4
|
||||
),
|
||||
'utf-8'
|
||||
);
|
||||
},
|
||||
refreshToken: tokenData.refreshToken
|
||||
}
|
||||
),
|
||||
{
|
||||
channels: [twitch.channel]
|
||||
}
|
||||
new RefreshableAuthProvider(
|
||||
new StaticAuthProvider(twitch.clientId, tokenData.accessToken),
|
||||
{
|
||||
clientSecret: twitch.clientSecret,
|
||||
expiry:
|
||||
tokenData.expiryTimestamp === null
|
||||
? null
|
||||
: new Date(tokenData.expiryTimestamp),
|
||||
onRefresh: async ({accessToken, refreshToken, expiryDate}) => {
|
||||
return promises.writeFile(
|
||||
'./twitch.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
accessToken,
|
||||
expiryTimestamp:
|
||||
expiryDate === null ? null : expiryDate.getTime(),
|
||||
refreshToken,
|
||||
},
|
||||
null,
|
||||
4
|
||||
),
|
||||
'utf-8'
|
||||
);
|
||||
},
|
||||
refreshToken: tokenData.refreshToken,
|
||||
}
|
||||
),
|
||||
{
|
||||
channels: [twitch.channel],
|
||||
}
|
||||
);
|
||||
|
||||
chatClient.onJoin((channel: string, user: string) => {
|
||||
if (channel === `#${twitch.channel}` && user === chatClient.currentNick) {
|
||||
while (messages.length) {
|
||||
const message: string | undefined = messages.shift();
|
||||
if (channel === `#${twitch.channel}` && user === chatClient.currentNick) {
|
||||
while (messages.length) {
|
||||
const message: string | undefined = messages.shift();
|
||||
|
||||
if (message !== undefined) {
|
||||
try {
|
||||
void chatClient.say(channel, message);
|
||||
logger.info('✔ twitch message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send twitch message", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message !== undefined) {
|
||||
try {
|
||||
void chatClient.say(channel, message);
|
||||
logger.info('✔ twitch message sent');
|
||||
} catch (error: unknown) {
|
||||
logger.error("✖ couldn't send twitch message", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void chatClient.quit();
|
||||
void chatClient.quit();
|
||||
});
|
||||
|
||||
chatClient.onDisconnect(() => {
|
||||
alreadySaying = false;
|
||||
alreadySaying = false;
|
||||
});
|
||||
|
||||
export function sendTwitchMessage(link: Link, store: Store) {
|
||||
if (
|
||||
tokenData.accessToken &&
|
||||
twitch.channel &&
|
||||
twitch.clientId &&
|
||||
twitch.clientSecret &&
|
||||
tokenData.refreshToken
|
||||
) {
|
||||
logger.debug('↗ sending twitch message');
|
||||
if (
|
||||
tokenData.accessToken &&
|
||||
twitch.channel &&
|
||||
twitch.clientId &&
|
||||
twitch.clientSecret &&
|
||||
tokenData.refreshToken
|
||||
) {
|
||||
logger.debug('↗ sending twitch message');
|
||||
|
||||
messages.push(
|
||||
`${Print.inStock(link, store)}\n${
|
||||
link.cartUrl ? link.cartUrl : link.url
|
||||
}`
|
||||
);
|
||||
messages.push(
|
||||
`${Print.inStock(link, store)}\n${link.cartUrl ? link.cartUrl : link.url}`
|
||||
);
|
||||
|
||||
if (!alreadySaying) {
|
||||
alreadySaying = true;
|
||||
void chatClient.connect();
|
||||
}
|
||||
}
|
||||
if (!alreadySaying) {
|
||||
alreadySaying = true;
|
||||
void chatClient.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-25
@@ -6,35 +6,35 @@ import {config} from '../config';
|
||||
const {twitter} = config.notifications;
|
||||
|
||||
const client = new Twitter({
|
||||
access_token_key: twitter.accessTokenKey,
|
||||
access_token_secret: twitter.accessTokenSecret,
|
||||
consumer_key: twitter.consumerKey,
|
||||
consumer_secret: twitter.consumerSecret
|
||||
access_token_key: twitter.accessTokenKey,
|
||||
access_token_secret: twitter.accessTokenSecret,
|
||||
consumer_key: twitter.consumerKey,
|
||||
consumer_secret: twitter.consumerSecret,
|
||||
});
|
||||
|
||||
export function sendTweet(link: Link, store: Store) {
|
||||
if (
|
||||
twitter.accessTokenKey &&
|
||||
twitter.accessTokenSecret &&
|
||||
twitter.consumerKey &&
|
||||
twitter.consumerSecret
|
||||
) {
|
||||
logger.debug('↗ sending twitter message');
|
||||
if (
|
||||
twitter.accessTokenKey &&
|
||||
twitter.accessTokenSecret &&
|
||||
twitter.consumerKey &&
|
||||
twitter.consumerSecret
|
||||
) {
|
||||
logger.debug('↗ sending twitter message');
|
||||
|
||||
let status = `${Print.inStock(link, store)}\n${
|
||||
link.cartUrl ? link.cartUrl : link.url
|
||||
}`;
|
||||
let status = `${Print.inStock(link, store)}\n${
|
||||
link.cartUrl ? link.cartUrl : link.url
|
||||
}`;
|
||||
|
||||
if (twitter.tweetTags) {
|
||||
status += `\n\n${twitter.tweetTags}`;
|
||||
}
|
||||
if (twitter.tweetTags) {
|
||||
status += `\n\n${twitter.tweetTags}`;
|
||||
}
|
||||
|
||||
client.post('statuses/update', {status}, (error) => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't send twitter notification", error);
|
||||
} else {
|
||||
logger.info('✔ twitter notification sent');
|
||||
}
|
||||
});
|
||||
}
|
||||
client.post('statuses/update', {status}, error => {
|
||||
if (error) {
|
||||
logger.error("✖ couldn't send twitter notification", error);
|
||||
} else {
|
||||
logger.info('✔ twitter notification sent');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user