diff --git a/calendar.ics b/calendar.ics new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json index 98db0ab..a454044 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,12 @@ "cron": "^2.3.0", "dotenv": "^16.0.3", "express": "^4.18.2", + "googleapis": "^122.0.0", "ical": "^0.8.0", "ics": "^3.1.0", "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "node-cron": "^3.0.2", "node-html-parser": "^6.1.5", "uuid": "^9.0.0" }, @@ -125,11 +128,30 @@ "node": ">= 0.6" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "engines": { + "node": ">=8" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -145,6 +167,33 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "engines": { + "node": "*" + } + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -200,6 +249,11 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -297,6 +351,22 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -381,6 +451,14 @@ "node": ">=12" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -486,6 +564,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==" + }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -569,6 +657,32 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", @@ -582,6 +696,80 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/google-auth-library": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", + "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==", + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.3.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/googleapis": { + "version": "122.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-122.0.0.tgz", + "integrity": "sha512-n8Gt7j9LzSkhQEGPOrcLBKxllTvW/0v6oILuwszL/zqgelNsGJYXVqPJllgJJ6RM7maJ6T35UBeYqI6GQ/IlJg==", + "dependencies": { + "google-auth-library": "^8.0.2", + "googleapis-common": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/googleapis-common": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-6.0.4.tgz", + "integrity": "sha512-m4ErxGE8unR1z0VajT6AYk3s6a9gIMM6EkDZfkPnES8joeOlEtFEJeF8IyZkb0tjPXkktUfYrE4b3Li1DNyOwA==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^5.0.1", + "google-auth-library": "^8.0.2", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "dependencies": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -627,6 +815,18 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ical": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/ical/-/ical-0.8.0.tgz", @@ -668,6 +868,44 @@ "node": ">= 0.10" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -678,6 +916,17 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/luxon": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", @@ -745,6 +994,22 @@ "node": "*" } }, + "node_modules/moment-timezone": { + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/nanoclone": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", @@ -775,6 +1040,52 @@ "node": ">= 0.6" } }, + "node_modules/node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "dependencies": { + "uuid": "8.3.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/node-cron/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-html-parser": { "version": "6.1.5", "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.5.tgz", @@ -849,6 +1160,20 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1011,6 +1336,11 @@ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1044,6 +1374,11 @@ "node": ">= 0.8" } }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -1068,6 +1403,25 @@ "node": ">= 0.8" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/yup": { "version": "0.32.11", "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", diff --git a/package.json b/package.json index ad70349..c8b60ca 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,12 @@ "cron": "^2.3.0", "dotenv": "^16.0.3", "express": "^4.18.2", + "googleapis": "^122.0.0", "ical": "^0.8.0", "ics": "^3.1.0", "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "node-cron": "^3.0.2", "node-html-parser": "^6.1.5", "uuid": "^9.0.0" }, diff --git a/src/GameSource.ts b/src/GameSource.ts index 553d1de..999fd91 100644 --- a/src/GameSource.ts +++ b/src/GameSource.ts @@ -30,15 +30,12 @@ export default class GameSource { const gameDate = headerSplit[2].trim(); const gameTime = headerSplit[3].trim(); - const startOriginal = moment(gameDate + gameTime, "DD/MM/YYYYHH:mm").format("DD/MM/YYYY HH:mm"); - const endOriginal = moment(gameDate + gameTime, "DD/MM/YYYYHH:mm").add(2, "hours").format("DD/MM/YYYY HH:mm"); - - const start = [moment(startOriginal, 'DD/MM/YYYYHH:mm').year(), moment(startOriginal, 'DD/MM/YYYYHH:mm').month() + 1, moment(startOriginal, 'DD/MM/YYYYHH:mm').date(), moment(startOriginal, 'DD/MM/YYYYHH:mm').hour(), moment(startOriginal, 'DD/MM/YYYYHH:mm').minute()]; - const end = [moment(endOriginal, 'DD/MM/YYYYHH:mm').year(), moment(endOriginal, 'DD/MM/YYYYHH:mm').month() + 1, moment(endOriginal, 'DD/MM/YYYYHH:mm').date(), moment(endOriginal, 'DD/MM/YYYYHH:mm').hour(), moment(endOriginal, 'DD/MM/YYYYHH:mm').minute()] + const start = moment(gameDate + gameTime, "DD/MM/YYYYHH:mm").toISOString(); + const end = moment(gameDate + gameTime, "DD/MM/YYYYHH:mm").add(2, "hours").toISOString(); games.push({ summary: 'Maccabi Haifa F.C.', - location: "Sammy Ofer Stadium", + location: "Ofir's stadium", description: `${teamsPlaying[0]} vs. ${teamsPlaying[1]}`, start: { dateTime: start, diff --git a/src/GoogleCalendar b/src/GoogleCalendar deleted file mode 100644 index ffba0cb..0000000 --- a/src/GoogleCalendar +++ /dev/null @@ -1,65 +0,0 @@ -// import { JWT } from 'google-auth-library'; -// import { google } from 'googleapis'; -// import { GoogleCalendarEvent } from './types/index'; - -// require('dotenv').config(); -// const env = process.env; - - -// export default class GoogleCalendar { -// clientSecret: string = env.GOOGLE_CLIENT_SECRET; -// clientId: string = env.GOOGLE_CLIENT_ID; -// calenderId: string = env.GOOGLE_CALENDAR_ID; -// calendar: any; -// clientEmail: string = env.GOOGLE_CLIENT_EMAIL; -// googlePrivateKey: string = env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n'); -// token: any; -// JWT_client: JWT; - -// async init() { -// console.log("INIT GOOGLE CALENDAR") -// const jwtClient = await this.authorize(); -// this.calendar = google.calendar({ version: 'v3', auth: jwtClient }); -// console.log("DONE INIT GOOGLE CALENDAR") -// } - -// async authorize() { -// console.log("AUTHORIZE GOOGLE CALENDAR") -// this.JWT_client = new JWT({ -// email: this.clientEmail, -// key: this.googlePrivateKey, -// scopes: [ -// 'https://www.googleapis.com/auth/calendar', -// ] -// }); -// 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: GoogleCalendarEvent[]) { -// setTimeout(async () => { -// upcomingEvents.forEach((event: GoogleCalendarEvent) => { -// console.log("UPDATE NEW EVENT", upcomingEvents) -// const options = { -// auth: this.JWT_client, -// calendarId: this.calenderId, -// resource: event, -// } -// this.calendar.events.insert(options, function (err: any, event: any) { -// if (err) { -// console.log('There was an error contacting the Calendar service: ' + err); -// return; -// } -// console.log(event.description + ' created'); -// }); - -// }) -// }, 3000) - -// } -// } diff --git a/src/GoogleCalendar.ts b/src/GoogleCalendar.ts new file mode 100644 index 0000000..dfcd8d3 --- /dev/null +++ b/src/GoogleCalendar.ts @@ -0,0 +1,93 @@ +import { JWT } from 'google-auth-library'; +import { google } from 'googleapis'; +import { GoogleCalendarEvent } from './types/index'; + +require('dotenv').config(); +const env = process.env; + +export default class GoogleCalendar { + gamesMap: any = {}; + clientSecret: string = env.GOOGLE_CLIENT_SECRET; + clientId: string = env.GOOGLE_CLIENT_ID; + calenderId: string = env.GOOGLE_CALENDAR_ID; + calendar: any; + clientEmail: string = env.GOOGLE_CLIENT_EMAIL; + googlePrivateKey: string = env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n'); + token: any; + JWT_client: JWT; + + async init() { + console.log("INIT GOOGLE CALENDAR") + const jwtClient = await this.authorize(); + this.calendar = google.calendar({ version: 'v3', auth: jwtClient }); + } + + async authorize() { + console.log("AUTHORIZE GOOGLE CALENDAR") + this.JWT_client = new 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: GoogleCalendarEvent[]) { + // console.log(upcomingEvents) + setTimeout(async () => { + upcomingEvents.forEach(async (event: GoogleCalendarEvent) => { + 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: any, event: any) { + if (err) { + console.log('There was an error contacting the Calendar service: ' + err); + return; + } + console.log(event.description + ' created'); + }); + + }) + }, 3000) + + } + + async isDuplicateEvent(startTime: string, endTime: string, title: string) { + 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; + } + } +} diff --git a/src/Ics.ts b/src/Ics.ts deleted file mode 100644 index 086e119..0000000 --- a/src/Ics.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { GoogleCalendarEvent } from "./types"; -import * as ics from 'ics'; - -const uuid = require('uuid').v4(); - - -export default class Ics { - - generateIcsOutputFromGames = (games: GoogleCalendarEvent[]) => { - 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; - } - - generateIcsOutputFromGame = (game: GoogleCalendarEvent) => { - const { summary, location, description, start, end } = game; - const icsEvent: ics.EventAttributes = { - 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; - } - - convertIcsToIcal = (icsEvents: string) => { - const icalEvents = icsEvents.replace(/BEGIN:VEVENT/g, 'BEGIN:VEVENT\r\nUID:' + uuid); - return icalEvents; - } -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index a1eacf9..31e9146 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,70 +1,52 @@ -import GameSource from './GameSource'; -import fs from 'fs'; -import Ics from './Ics'; -import express from 'express' -import env from 'dotenv'; +import GameSource from "./GameSource"; +import GoogleCalendar from "./GoogleCalendar"; +import env from "dotenv"; +import cron from "node-cron"; env.config(); class App { gameSource: GameSource; - ics: Ics; googleToken: string; + googleCalendar: GoogleCalendar; constructor() { this.gameSource = new GameSource(); - this.ics = new Ics(); + this.googleCalendar = new GoogleCalendar(); } async startCronJob() { - console.log("START CRON JOB") - const CronJob = require('cron').CronJob; - const job = new CronJob( - "* * * * *", // every day at 10:00, - async () => { - console.log("Staring a new job") - const outputFileLocation = 'public/maccabi-haifa-fc.ics'; - console.log("Getting games from Haifa") + console.log("START CRON JOB"); + + await this.googleCalendar.init(); + + // Schedule the job to run daily at a specific time (e.g., 1:00 AM) + cron.schedule("* * * * *", async () => { + try { const games = await app.gameSource.getGamesFromHaifa(); - console.log("Generating ICS file") - const icsEvents = app.ics.generateIcsOutputFromGames([games[0]]); - console.log("Writing ICS file to " + outputFileLocation) - fs.writeFileSync(outputFileLocation, icsEvents); - console.log("Done Ics file") - // console.log("converting ics file to ical file") - // const outputIcalFileLocation = 'public/maccabi-haifa-fc.ical'; - // const icalEvents = app.ics.convertIcsToIcal(icsEvents); - // console.log("Writing Ical file to " + outputIcalFileLocation) - // fs.writeFileSync(outputIcalFileLocation, icalEvents); + for (const game of games) { + const isDuplicateEvent = await this.googleCalendar.isDuplicateEvent( + game.start.dateTime, + game.end.dateTime, + game.summary + ); - }, - null, - true, - 'Asia/Jerusalem' - ); - - } - - async startWebServer() { - const webServer = express(); - webServer.use(express.static('public')) - - webServer.listen(process.env.PORT, () => { - console.log(`Calender app listening on port ${process.env.PORT}!`) - }) - - webServer.use(function (req, res, next) { - res.status(404).send("This is not the page you are looking for...") - }) + 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(); - - - +// app.startWebServer(); diff --git a/src/types/index.ts b/src/types/index.ts index 72fc622..6111f8e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,11 +3,11 @@ export interface GoogleCalendarEvent { location: string; description: string; start: { - dateTime: number[]; + dateTime: string; timeZone: string; }; end: { - dateTime: number[]; + dateTime: string; timeZone: string; }; } \ No newline at end of file