diff --git a/.gitignore b/.gitignore index 9f68798..e5e1e79 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ #javascript build files # public/**/* -dist/**/* # Tmp files # tmp @@ -11,7 +10,6 @@ tmp ## ENV Vars ## .env -config/client_google_auth.json ## Docker ## build_image.sh diff --git a/dist/GameSource.js b/dist/GameSource.js new file mode 100644 index 0000000..1026499 --- /dev/null +++ b/dist/GameSource.js @@ -0,0 +1,57 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +require('dotenv').config(); +const axios_1 = __importDefault(require("axios")); +const node_html_parser_1 = require("node-html-parser"); +const moment_1 = __importDefault(require("moment")); // require +// This calss will be the game source. +// search for upcomming games +class GameSource { + async getGamesFromHaifa() { + const sourceUrl = `https://mhaifafc.com/games?lang=en`; + const result = await axios_1.default.get(sourceUrl); + const parsedResult = (0, node_html_parser_1.parse)(result.data.toString()); + const gameBoxs = parsedResult.querySelectorAll(".game-box"); + const games = []; + for (let gameBox of gameBoxs) { + const teamsPlaying = gameBox.querySelectorAll(".team-name").map((team) => team.text); + const regex = /[\r\n\s]+/g; + const gameHeader = gameBox.querySelector(".game-header").text.replace(regex, " ").trim(); + const headerSplit = gameHeader.split(","); + // In data, if there is no time, it means it's the last game for the calender + const lastGameForCalender = headerSplit.length < 4; + if (lastGameForCalender) + break; + const gameDate = headerSplit[2].trim(); + const gameTime = headerSplit[3].trim(); + const start = (0, moment_1.default)(gameDate + gameTime, "DD/MM/YYYYHH:mm").toISOString(); + const end = (0, moment_1.default)(gameDate + gameTime, "DD/MM/YYYYHH:mm").add(2, "hours").toISOString(); + games.push({ + summary: 'Maccabi Haifa F.C.', + location: "Ofir's stadium", + description: `${teamsPlaying[0]} vs. ${teamsPlaying[1]}`, + start: { + dateTime: start, + timeZone: 'Asia/Jerusalem' + }, + end: { + dateTime: end, + timeZone: 'Asia/Jerusalem' + } + }); + } + return games; + } + getOpponentIndexByStadium(stadium) { + if (stadium === "Sammy Ofer Stadium") { + return 1; + } + else { + return 0; + } + } +} +exports.default = GameSource; diff --git a/dist/GoogleCalendar.js b/dist/GoogleCalendar.js new file mode 100644 index 0000000..640c9ed --- /dev/null +++ b/dist/GoogleCalendar.js @@ -0,0 +1,87 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const google_auth_library_1 = require("google-auth-library"); +const googleapis_1 = require("googleapis"); +require('dotenv').config(); +const env = process.env; +class GoogleCalendar { + constructor() { + this.gamesMap = {}; + this.clientSecret = env.GOOGLE_CLIENT_SECRET; + this.clientId = env.GOOGLE_CLIENT_ID; + this.calenderId = env.GOOGLE_CALENDAR_ID; + this.clientEmail = env.GOOGLE_CLIENT_EMAIL; + this.googlePrivateKey = env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n'); + } + async init() { + console.log("INIT GOOGLE CALENDAR"); + const jwtClient = await this.authorize(); + this.calendar = googleapis_1.google.calendar({ version: 'v3', auth: jwtClient }); + } + async authorize() { + console.log("AUTHORIZE GOOGLE CALENDAR"); + this.JWT_client = new google_auth_library_1.JWT({ + email: this.clientEmail, + key: this.googlePrivateKey, + scopes: [ + 'https://www.googleapis.com/auth/calendar', + 'https://www.googleapis.com/auth/calendar.events' + ] + }); + const { access_token } = await this.JWT_client.authorize(); + this.token = access_token; + if (!this.token) { + throw new Error('Failed to connect to google calendar'); + } + console.log("GOOGLE CALENDAR AUTHORIZED SUCCESSFULLY"); + return this.JWT_client; + } + async updateNewEvent(upcomingEvents) { + // console.log(upcomingEvents) + setTimeout(async () => { + upcomingEvents.forEach(async (event) => { + console.log("UPDATE NEW EVENT", upcomingEvents); + const options = { + auth: this.JWT_client, + calendarId: this.calenderId, + resource: { + summary: event.summary, + location: event.location, + description: event.description, + start: { dateTime: event.start.dateTime, timeZone: 'Asia/Jerusalem' }, + end: { dateTime: event.end.dateTime, timeZone: 'Asia/Jerusalem' } + }, + }; + await this.calendar.events.insert(options, function (err, event) { + if (err) { + console.log('There was an error contacting the Calendar service: ' + err); + return; + } + console.log(event.description + ' created'); + }); + }); + }, 3000); + } + async isDuplicateEvent(startTime, endTime, title) { + if (this.gamesMap[startTime]) { + console.log("duplicate event"); + return true; + } + this.gamesMap[startTime] = true; + console.log("checking for duplicate event"); + try { + const response = await this.calendar.events.list({ + calendarId: this.calenderId, + timeMin: startTime, + timeMax: endTime, + q: title, // Search for events with the same title + }); + return response.data.items.length > 0; + } + catch (error) { + console.error(error.message); + return false; + } + } +} +exports.default = GoogleCalendar; diff --git a/dist/Ics.js b/dist/Ics.js new file mode 100644 index 0000000..2de47c6 --- /dev/null +++ b/dist/Ics.js @@ -0,0 +1,69 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const ics = __importStar(require("ics")); +const uuid = require('uuid').v4(); +class Ics { + constructor() { + this.generateIcsOutputFromGames = (games) => { + let output = []; + games.forEach((game) => { + output.push(this.generateIcsOutputFromGame(game)); + }); + console.log(output); + const { error, value } = ics.createEvents(output); + if (error) { + console.log(error); + return ''; + } + console.log(value); + return value; + }; + this.generateIcsOutputFromGame = (game) => { + const { summary, location, description, start, end } = game; + const icsEvent = { + title: description, + description, + location, + start: [start.dateTime[0], start.dateTime[1], start.dateTime[2], start.dateTime[3], start.dateTime[4]], + end: [end.dateTime[0], end.dateTime[1], end.dateTime[2], end.dateTime[3], end.dateTime[4]], + status: 'CONFIRMED', + busyStatus: "BUSY", + productId: '-//kfir.dayanhub.com', + recurrenceRule: '', + attendees: [], + alarms: [], + categories: [], + organizer: { name: 'Maccabi Haifa F.C.', email: '' }, + }; + return icsEvent; + }; + this.convertIcsToIcal = (icsEvents) => { + const icalEvents = icsEvents.replace(/BEGIN:VEVENT/g, 'BEGIN:VEVENT\r\nUID:' + uuid); + return icalEvents; + }; + } +} +exports.default = Ics; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..00828af --- /dev/null +++ b/dist/index.js @@ -0,0 +1,42 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const GameSource_1 = __importDefault(require("./GameSource")); +const GoogleCalendar_1 = __importDefault(require("./GoogleCalendar")); +const dotenv_1 = __importDefault(require("dotenv")); +const node_cron_1 = __importDefault(require("node-cron")); +dotenv_1.default.config(); +class App { + constructor() { + this.gameSource = new GameSource_1.default(); + this.googleCalendar = new GoogleCalendar_1.default(); + } + async startCronJob() { + console.log("START CRON JOB"); + await this.googleCalendar.init(); + // Schedule the job to run daily at a specific time (e.g., 1:00 AM) + node_cron_1.default.schedule("* * * * *", async () => { + try { + const games = await app.gameSource.getGamesFromHaifa(); + for (const game of games) { + const isDuplicateEvent = await this.googleCalendar.isDuplicateEvent(game.start.dateTime, game.end.dateTime, game.summary); + if (!isDuplicateEvent) { + console.log("Event does not exist"); + await this.googleCalendar.updateNewEvent([game]); + } + else { + console.log("Event already exists"); + } + } + } + catch (error) { + console.error("Error in cron job:", error.message); + } + }); + } +} +const app = new App(); +app.startCronJob(); +// app.startWebServer(); diff --git a/dist/types/index.js b/dist/types/index.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/types/index.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true });