UNPKG

n8n-nodes-shabbat-time

Version:

n8n community node to check if a given date/time falls on Shabbat or Jewish holiday with precise time checking

489 lines 23.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ShabatTime = void 0; const n8n_workflow_1 = require("n8n-workflow"); class ShabatTime { constructor() { this.description = { displayName: 'Shabbat Time', name: 'shabatTime', icon: { light: 'file:shabbat.svg', dark: 'file:shabbat.svg' }, group: ['transform'], version: 1, subtitle: '={{$parameter["operation"]}}', description: 'Check if a given date falls on Shabbat or Jewish holiday using Hebcal API', defaults: { name: 'Shabbat Time', }, inputs: ["main"], outputs: ["main"], usableAsTool: true, requestDefaults: { baseURL: 'https://www.hebcal.com', url: '', headers: { Accept: 'application/json', 'User-Agent': 'n8n-nodes-shabbat-time/1.0.0', }, }, properties: [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, options: [ { name: 'Check Date', value: 'checkDate', description: 'Check if a specific date falls on Shabbat or Jewish holiday', action: 'Check if date is shabbat or jewish holiday', }, ], default: 'checkDate', }, { displayName: 'Date', name: 'dateInput', type: 'dateTime', default: '', required: true, description: 'The date to check', displayOptions: { show: { operation: ['checkDate'], }, }, }, { displayName: 'Location', name: 'locationType', type: 'options', options: [ { name: 'Ashdod', value: 'ashdod', description: 'Ashdod, Southern coastal plain', }, { name: 'Beer Sheva', value: 'beersheva', description: 'Beer Sheva, Southern Israel (Negev)', }, { name: 'Custom Location', value: 'custom', description: 'Define your own location using GeoNames ID, coordinates, or ZIP code', }, { name: 'Eilat', value: 'eilat', description: 'Eilat, Southern tip of Israel', }, { name: 'Haifa', value: 'haifa', description: 'Haifa, Northern Israel', }, { name: 'Holon', value: 'holon', description: 'Holon, Tel Aviv metropolitan area', }, { name: 'Jerusalem', value: 'jerusalem', description: 'Jerusalem, Capital of Israel', }, { name: 'Netanya', value: 'netanya', description: 'Netanya, Sharon region', }, { name: 'Petah Tikva', value: 'petahtikva', description: 'Petah Tikva, Central Israel', }, { name: 'Ramat Gan', value: 'ramatgan', description: 'Ramat Gan, Tel Aviv metropolitan area', }, { name: 'Tel Aviv', value: 'telaviv', description: 'Tel Aviv-Yafo, Central Israel', }, ], default: 'telaviv', displayOptions: { show: { operation: ['checkDate'], }, }, }, { displayName: 'Custom Location Type', name: 'customLocationType', type: 'options', options: [ { name: 'GeoNames ID', value: 'geonameid', description: 'Use GeoNames.org numeric ID', }, { name: 'Coordinates', value: 'coordinates', description: 'Use latitude and longitude', }, { name: 'ZIP Code (US)', value: 'zip', description: 'Use United States ZIP code', }, ], default: 'geonameid', displayOptions: { show: { operation: ['checkDate'], locationType: ['custom'], }, }, }, { displayName: 'GeoNames ID', name: 'geonameid', type: 'number', default: 293397, description: 'GeoNames.org numeric ID (e.g., 293397 for Tel Aviv, 281184 for Jerusalem)', displayOptions: { show: { operation: ['checkDate'], locationType: ['custom'], customLocationType: ['geonameid'], }, }, }, { displayName: 'Latitude', name: 'latitude', type: 'number', default: 32.08088, description: 'Latitude coordinate (-90 to 90)', displayOptions: { show: { operation: ['checkDate'], locationType: ['custom'], customLocationType: ['coordinates'], }, }, }, { displayName: 'Longitude', name: 'longitude', type: 'number', default: 34.78057, description: 'Longitude coordinate (-180 to 180)', displayOptions: { show: { operation: ['checkDate'], locationType: ['custom'], customLocationType: ['coordinates'], }, }, }, { displayName: 'ZIP Code', name: 'zipCode', type: 'string', default: '', description: 'United States ZIP code (5 digits, e.g., 10001)', displayOptions: { show: { operation: ['checkDate'], locationType: ['custom'], customLocationType: ['zip'], }, }, }, { displayName: 'Include Minor Holidays', name: 'includeMinorHolidays', type: 'boolean', default: true, description: 'Whether to include minor Jewish holidays: Rosh Chodesh (New Month), Tu BiShvat (New Year of Trees), Lag BaOmer (33rd day of Omer), Tu BeAv (15th of Av), minor fasts (Tzom Gedaliah, Asara BeTevet, Taanit Esther, Shiva Asar BeTammuz), and special Shabbatot (Shekalim, Zachor, Parah, HaChodesh)', displayOptions: { show: { operation: ['checkDate'], }, }, }, { displayName: 'Include Chol Hamoed', name: 'includeCholHamoed', type: 'boolean', default: false, description: 'Whether to include intermediate festival days (Chol Hamoed) during Passover and Sukkot, and modern Israeli holidays: Yom HaShoah (Holocaust Memorial Day), Yom HaZikaron (Memorial Day), Yom HaAtzmaut (Independence Day), Yom Yerushalayim (Jerusalem Day)', displayOptions: { show: { operation: ['checkDate'], }, }, }, { displayName: 'Check Exact Time', name: 'checkExactTime', type: 'boolean', default: false, description: 'Whether to check exact time for Shabbat/holiday entry and exit (otherwise just check date)', displayOptions: { show: { operation: ['checkDate'], }, }, }, { displayName: 'Candle Lighting Minutes', name: 'candleLightingMinutes', type: 'number', default: 18, description: 'Minutes before sunset for candle lighting (default: 18, Jerusalem: 40)', displayOptions: { show: { operation: ['checkDate'], }, }, }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { try { const operation = this.getNodeParameter('operation', i); if (operation === 'checkDate') { const dateInput = this.getNodeParameter('dateInput', i); const locationType = this.getNodeParameter('locationType', i); const includeMinorHolidays = this.getNodeParameter('includeMinorHolidays', i); const includeCholHamoed = this.getNodeParameter('includeCholHamoed', i); const checkExactTime = this.getNodeParameter('checkExactTime', i); const candleLightingMinutes = this.getNodeParameter('candleLightingMinutes', i); const israeliCities = { 'telaviv': 293397, 'jerusalem': 281184, 'haifa': 294801, 'beersheva': 295530, 'eilat': 295277, 'netanya': 293822, 'ashdod': 295629, 'petahtikva': 293703, 'ramatgan': 293807, 'holon': 294946, }; const inputDate = new Date(dateInput); if (isNaN(inputDate.getTime())) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid date provided', { itemIndex: i }); } const year = inputDate.getFullYear(); const month = inputDate.getMonth() + 1; const day = inputDate.getDate(); if (year < 1900 || year > 2100 || month < 1 || month > 12 || day < 1 || day > 31) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Date out of valid range', { itemIndex: i }); } let apiUrl = `/hebcal?v=1&cfg=json&maj=on&min=on&nx=on&ss=on&c=on&s=on&i=on&leyning=off&b=${candleLightingMinutes}&M=on`; const startDate = new Date(inputDate); startDate.setDate(startDate.getDate() - 2); const endDate = new Date(inputDate); endDate.setDate(endDate.getDate() + 2); const formatDate = (date) => { return date.toISOString().split('T')[0]; }; apiUrl += `&start=${formatDate(startDate)}&end=${formatDate(endDate)}`; if (includeMinorHolidays) { apiUrl += `&mf=on`; } if (includeCholHamoed) { apiUrl += `&mod=on`; } if (locationType === 'custom') { const customLocationType = this.getNodeParameter('customLocationType', i); switch (customLocationType) { case 'geonameid': const geonameid = this.getNodeParameter('geonameid', i); apiUrl += `&geo=geoname&geonameid=${geonameid}`; break; case 'coordinates': const latitude = this.getNodeParameter('latitude', i); const longitude = this.getNodeParameter('longitude', i); if (latitude < -90 || latitude > 90) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Latitude must be between -90 and 90', { itemIndex: i }); } if (longitude < -180 || longitude > 180) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Longitude must be between -180 and 180', { itemIndex: i }); } apiUrl += `&geo=pos&latitude=${latitude}&longitude=${longitude}`; break; case 'zip': const zipCode = this.getNodeParameter('zipCode', i); if (!zipCode || zipCode.trim() === '') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'ZIP code is required when using ZIP code location type', { itemIndex: i }); } apiUrl += `&geo=zip&zip=${encodeURIComponent(zipCode)}`; break; } } else { const cityGeonameid = israeliCities[locationType]; if (cityGeonameid) { apiUrl += `&geo=geoname&geonameid=${cityGeonameid}`; } else { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown city: ${locationType}`, { itemIndex: i }); } } const requestOptions = { method: 'GET', baseURL: 'https://www.hebcal.com', url: apiUrl, json: true, }; const response = await this.helpers.httpRequest(requestOptions); let isShabbatOrHoliday = false; const details = []; const categories = []; let candleLightingTime = null; let havdalahTime = null; let sabbathStart = null; let sabbathEnd = null; if (response.items && Array.isArray(response.items)) { for (const item of response.items) { if (item.category === 'candles') { candleLightingTime = item.date; sabbathStart = new Date(item.date); } else if (item.category === 'havdalah') { havdalahTime = item.date; sabbathEnd = new Date(item.date); } } for (const item of response.items) { const itemDate = new Date(item.date); const checkDate = new Date(year, month - 1, day); const isSameDay = (date1, date2) => { return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate(); }; const isInShabbatTimeRange = () => { if (!checkExactTime) return false; if (!sabbathStart || !sabbathEnd) return false; return inputDate >= sabbathStart && inputDate <= sabbathEnd; }; const isRelevantItem = isSameDay(itemDate, checkDate) || isSameDay(itemDate, inputDate) || (checkExactTime && isInShabbatTimeRange()); if (isRelevantItem) { const category = item.category; categories.push(category); if (category === 'candles' || category === 'havdalah' || category === 'parashat' || (category === 'holiday' && (item.subcat === 'major' || item.yomtov))) { if (checkExactTime && (category === 'candles' || category === 'havdalah')) { if (isInShabbatTimeRange()) { isShabbatOrHoliday = true; } } else { isShabbatOrHoliday = true; } } if (includeMinorHolidays && (category === 'roshchodesh' || category === 'minor' || category === 'fast')) { isShabbatOrHoliday = true; } if (includeCholHamoed && (item.subcat === 'modern' || (item.memo && item.memo.toLowerCase().includes('chol hamoed')))) { isShabbatOrHoliday = true; } details.push({ title: item.title, category: item.category, subcat: item.subcat || '', hebrew: item.hebrew || '', date: item.date, yomtov: item.yomtov || false, memo: item.memo || '', }); } } if (checkExactTime && sabbathStart && sabbathEnd && !isShabbatOrHoliday) { if (inputDate >= sabbathStart && inputDate <= sabbathEnd) { isShabbatOrHoliday = true; } } } const outputData = { ...items[i].json, isShabbatOrHoliday, inputDate: dateInput, formattedDate: inputDate.toISOString().split('T')[0], dayOfWeek: inputDate.toLocaleDateString('en-US', { weekday: 'long' }), checkExactTime, categories, details, location: response.location || {}, sabbatTimes: { candleLighting: candleLightingTime, havdalah: havdalahTime, isInShabbatTimeRange: checkExactTime && sabbathStart && sabbathEnd ? (inputDate >= sabbathStart && inputDate <= sabbathEnd) : null, }, settings: { includeMinorHolidays, includeCholHamoed, checkExactTime, candleLightingMinutes, location: locationType, customLocationType: locationType === 'custom' ? this.getNodeParameter('customLocationType', i) : null, cityGeonameid: locationType !== 'custom' ? israeliCities[locationType] : null, }, }; returnData.push({ json: outputData, pairedItem: { item: i }, }); } } catch (error) { if (this.continueOnFail()) { returnData.push({ json: { ...items[i].json, error: error.message, isShabbatOrHoliday: false }, pairedItem: { item: i }, }); } else { throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i, }); } } } return [returnData]; } } exports.ShabatTime = ShabatTime; //# sourceMappingURL=ShabatTime.node.js.map