import { exec } from 'child_process'; import * as fs from 'fs'; // Config for scheduling the next time the main process (sending of horoscope) is run interface Config { daysInterval: number; timeOfDay: string; emailReceiver: string; mailHost: string; mailPort: number; } // Node structure for the Parse Tree (it's a degenerated tree with one or zero children per node) interface Node { type: "Root" | "Hexagram" | "Judgement" | "Images" | "ChangingLines" ; child?: Node; value?: string; } var config : Config; const nodemailer = require('nodemailer'); function executeCommand(): void { exec('iching divine', (error, stdout, stderr) => { console.log(`Begin`); if (error) { console.error(`Error: ${error.message}`); return; } if (stderr) { console.error(`Stderr: ${stderr}`); return; } //Load config once if (config == undefined) { config = loadConfig(); } console.log(`Send E-Mail`); sendEmail(stdout); // Calculate next execution time const nextRunDate = calculateNextRunDate(config.daysInterval, config.timeOfDay); console.log(`Next execution: ${nextRunDate}`); // Start Scheduling scheduleNextRun(nextRunDate); }); } // Run function once initially, called from Dockerfile executeCommand(); // Load the Configuration function loadConfig(): Config { console.log(`Load Config`); const data = fs.readFileSync('config.json', 'utf-8'); return JSON.parse(data); } // Send E-Mail async function sendEmail(content: string) { // Create Transporter const transporter = nodemailer.createTransport({ //host: "mailhog.mailhog.svc.cluster.local", // MailHog ClusterIP host: config.mailHost, //port: 1025, // Standard-MailHog SMTP-Port port: config.mailPort, secure: false // MailHog needs no encryption }); try { const info = await transporter.sendMail({ from: '"The Oracle" ', to: config.emailReceiver, subject: "Your Horoscope Is Ready", text: content, html: html(content) }); console.log("E-Mail sent: ", info.messageId); } catch (error) { console.error("Error Sending E-Mail:", error); } } //Calculate the time for the next execution of the main process, depending on configuration. //Returns the time of the next process loop. function calculateNextRunDate(daysInterval: number, timeOfDay: string): Date { const currentDate = new Date(); // Splitte die Uhrzeit in Stunden und Minuten const [hours, minutes] = timeOfDay.split(':').map(Number); // Berechne das Datum für die nächste Ausführung currentDate.setHours(hours, minutes, 0, 0); // Setze die Uhrzeit auf Y (z.B. 14:30) // Wenn die Uhrzeit bereits vorbei ist, verschiebe das Datum auf den nächsten Tag if (currentDate < new Date()) { currentDate.setDate(currentDate.getDate() + 1); // Setze auf den nächsten Tag } // Berechne das Datum für den nächsten Ausführungszeitpunkt unter Berücksichtigung von X Tagen currentDate.setDate(currentDate.getDate() + daysInterval); return currentDate; //TODO: JUST FOR DEBUGGING!!! /*currentDate.setMinutes(currentDate.getMinutes() + 2); return currentDate;*/ } //Schedule the next process loop. function scheduleNextRun(nextRunDate: Date) { const now = new Date(); const timeUntilNextRun = nextRunDate.getTime() - now.getTime(); if (timeUntilNextRun > 0) { setTimeout(() => { console.log('Prozess wird ausgeführt!'); //run the main process executeCommand(); }, timeUntilNextRun); } } // Generate 1) Parse Tree and 2) HTML export function html(inputText: string): string { const parseTree = parse(inputText); const htmlOutput = render(parseTree); return htmlOutput; } // Generate the Parse Tree function parse(input: string): Node { console.log("Parse input text"); const root: Node = { type: "Root"}; var currentNode: Node = root; const lines = input.split("\n"); for (const line of lines) { if (line.startsWith("Hexagram")) { const hexagram: Node = { type: "Hexagram"}; currentNode.child = hexagram; currentNode = hexagram; currentNode.value = "

" + line + "

"; } else if (line.startsWith("Judgement")) { const judgement: Node = { type: "Judgement"}; currentNode.child = judgement; currentNode = judgement; currentNode.value = "

" + line + "

"; } else if (line.startsWith("Images")) { const images: Node = { type: "Images"}; currentNode.child = images; currentNode = images; currentNode.value = "

" + line + "

"; } else if (line.startsWith("~") && currentNode.type != "ChangingLines") { const changingLines: Node = { type: "ChangingLines"}; currentNode.child = changingLines; currentNode = changingLines; currentNode.value = line; // + "
"; TODO: try without this
} else { currentNode.value = currentNode.value + line + "
"; } } return root; } // Generate HTML from Parse Tree function render(node: Node): string { if (node == undefined) { console.log("...finished...") return ""; } console.log("Render node" + node.type); var outputHTML: string = ""; switch (node.type) { case "Root": return render(node.child!); case "Hexagram": node.value = node.value?.replace("

", "

"); node.value = node.value?.replace("

", "

"); node.value = node.value?.replace("
", " - "); outputHTML = "

" + node.value + "

"; outputHTML = outputHTML + render(node.child!); return outputHTML; case "Images": outputHTML = "

" + node.value + "

"; //EXTRA closing div outputHTML = outputHTML + render(node.child!); return outputHTML; case "ChangingLines" : const regex = new RegExp("~", "g"); node.value = node.value?.replace(regex, ""); //outputHTML = "

" + node.value + "


"; outputHTML = "

" + node.value + "


"; outputHTML = outputHTML + render(node.child!); return outputHTML; default: outputHTML = "

" + node.value + "

"; outputHTML = outputHTML + render(node.child!); return outputHTML; } }