diff --git a/main.ts b/main.ts index 37f6668..0d7281d 100755 --- a/main.ts +++ b/main.ts @@ -1,11 +1,27 @@ #!/usr/bin/env -S npx tsx -import run from "./src/run.js"; +import { notify } from "./src/bot.js"; +import { getFirstAvailableAppointment } from "./src/appointments.js"; -async function main() { - while (true) { - await run(); - } +async function run() { + const availableAppointment = await getFirstAvailableAppointment(); + if (!availableAppointment) { + console.log("no appt found"); + return; + } + + if (new Date() > availableAppointment) { + console.log("found one but already expired", availableAppointment.toISOString()); + return; + } + + console.log("found one", availableAppointment.toISOString()); + await notify(availableAppointment); } -main(); +const sleep = async (ms: number) => new Promise(r => setTimeout(r, ms)); + +while (true) { + await run(); + await sleep(60_000); +} diff --git a/package-lock.json b/package-lock.json index 5c84615..c89d281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,21 @@ { - "name": "cosmosarb", - "version": "1.0.0", + "name": "bagdad", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "cosmosarb", - "version": "1.0.0", - "license": "ISC", + "name": "bagdad", + "version": "0.0.1", "dependencies": { "dotenv": "16.4.5", - "got": "14.4.3", + "got": "14.4.4", "telegraf": "4.16.3", - "tsx": "4.19.1", - "twilio": "5.3.4" + "tsx": "4.19.2" }, "devDependencies": { - "@tsconfig/node20": "20.1.4", - "@types/node": "22.7.6", + "@tsconfig/node22": "22.0.0", + "@types/node": "22.9.0", "typescript": "5.6.3" } }, @@ -441,10 +439,10 @@ "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==", "license": "MIT" }, - "node_modules/@tsconfig/node20": { - "version": "20.1.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", - "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", + "node_modules/@tsconfig/node22": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.0.tgz", + "integrity": "sha512-twLQ77zevtxobBOD4ToAtVmuYrpeYUh3qh+TEp+08IWhpsrIflVHqQ1F1CiPxQGL7doCdBIOOCF+1Tm833faNg==", "dev": true, "license": "MIT" }, @@ -455,13 +453,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", - "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.19.8" } }, "node_modules/abort-controller": { @@ -476,35 +474,6 @@ "node": ">=6.5" } }, - "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==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", @@ -521,12 +490,6 @@ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "license": "MIT" }, - "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==", - "license": "BSD-3-Clause" - }, "node_modules/buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -560,43 +523,6 @@ "node": ">=18" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "license": "MIT" - }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -650,32 +576,6 @@ "node": ">=10" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -688,36 +588,6 @@ "url": "https://dotenvx.com" } }, - "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==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/esbuild": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", @@ -766,40 +636,6 @@ "node": ">=6" } }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/form-data-encoder": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", @@ -823,34 +659,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/get-stream": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", @@ -879,22 +687,10 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/got": { - "version": "14.4.3", - "resolved": "https://registry.npmjs.org/got/-/got-14.4.3.tgz", - "integrity": "sha512-iTC0Z87yxSijWTh/IpvGpwOhIQK7+GgWkYrMRoN/hB9qeRj9RPuLGODwevs0p5idUf7nrxCVa5IlOmK3b8z+KA==", + "version": "14.4.4", + "resolved": "https://registry.npmjs.org/got/-/got-14.4.4.tgz", + "integrity": "sha512-tqiF7eSgTBwQkxb1LxsEpva8TaMYVisbhplrFVmw9GQE3855Z+MH/mnsXLLOkDxR6hZJRFMj5VTAZ8lmTF8ZOA==", "license": "MIT", "dependencies": { "@sindresorhus/is": "^7.0.1", @@ -916,54 +712,6 @@ "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -983,19 +731,6 @@ "node": ">=10.19.0" } }, - "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==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/is-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", @@ -1014,49 +749,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "license": "MIT" }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -1066,48 +758,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, "node_modules/lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -1120,27 +770,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-response": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", @@ -1200,18 +829,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/p-cancelable": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", @@ -1230,27 +847,6 @@ "node": ">=10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -1293,26 +889,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-compare": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", @@ -1331,59 +907,6 @@ "node": ">= 0.10" } }, - "node_modules/scmp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", - "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==", - "license": "BSD-3-Clause" - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/telegraf": { "version": "4.16.3", "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", @@ -1413,9 +936,9 @@ "license": "MIT" }, "node_modules/tsx": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz", - "integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", "license": "MIT", "dependencies": { "esbuild": "~0.23.0", @@ -1431,24 +954,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/twilio": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/twilio/-/twilio-5.3.4.tgz", - "integrity": "sha512-Uu5AXZiPjpwGqn1NLTjxn1lidA+J17bkJSK2KAsg98bSFu0tkzVIQELzolxZVDu5BRcFeeFm9Zs5+5YzGoMhEg==", - "license": "MIT", - "dependencies": { - "axios": "^1.7.4", - "dayjs": "^1.11.9", - "https-proxy-agent": "^5.0.0", - "jsonwebtoken": "^9.0.2", - "qs": "^6.9.4", - "scmp": "^2.1.0", - "xmlbuilder": "^13.0.2" - }, - "engines": { - "node": ">=14.0" - } - }, "node_modules/type-fest": { "version": "4.26.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", @@ -1497,15 +1002,6 @@ "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } - }, - "node_modules/xmlbuilder": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", - "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", - "license": "MIT", - "engines": { - "node": ">=6.0" - } } } } diff --git a/package.json b/package.json index 6b61e83..cc44bd3 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,21 @@ { - "name": "cosmosarb", - "version": "1.0.0", - "description": "", + "name": "bagdad", + "version": "0.0.1", "type": "module", "module": "main.ts", "scripts": { - "start": "./main.ts", + "start": "tsx ./main.ts", "dev": "tsx watch ./main.ts" }, - "keywords": [], - "author": "m5r", - "license": "ISC", "devDependencies": { - "@tsconfig/node20": "20.1.4", - "@types/node": "22.7.6", + "@tsconfig/node22": "22.0.0", + "@types/node": "22.9.0", "typescript": "5.6.3" }, "dependencies": { "dotenv": "16.4.5", - "got": "14.4.3", + "got": "14.4.4", "telegraf": "4.16.3", - "tsx": "4.19.1", - "twilio": "5.3.4" + "tsx": "4.19.2" } } diff --git a/src/appointments.ts b/src/appointments.ts new file mode 100644 index 0000000..ca1c33e --- /dev/null +++ b/src/appointments.ts @@ -0,0 +1,62 @@ +import got from "got"; + +import { addCachedAppointment, getCachedAppointments } from "./cache.js"; + +export async function getFirstAvailableAppointment(): Promise { + const availableAppointments = await getAppointments(); + const knownAppointments = getCachedAppointments(); + + for (const appointmentDate of Object.keys(availableAppointments)) { + const appointments = availableAppointments[appointmentDate]; + const hasAppointments = appointments.length > 0; + if (hasAppointments) { + for (const appointment of appointments) { + const appointmentTime = appointment.startHM; + const knownAppointment = knownAppointments.find(([date, time]) => date === appointmentDate && time === appointmentTime); + if (!knownAppointment) { + addCachedAppointment([appointmentDate, appointmentTime]); + const [year, month, day] = appointmentDate.split("-").map(s => parseInt(s, 10)); + const hours = Math.trunc(appointmentTime); + const minutes = (appointmentTime % hours) * 60; + + return new Date(year, month - 1, day, hours, minutes); + } + } + } + } + + return null; +} + +async function getAppointments() { + type ApiAppointment = { + startHM: number; + staffUnavailable: {}; + shopUnavailable: boolean; + queriesFitStaffs: string[][]; + isFit: boolean; + start: number; + } + type Response = { + [date: string]: ApiAppointment[]; + } + + const { body } = await got.post( + "https://api.wavy.fr/shops/c5593def-a89c-4296-ac87-f484d8a22ecb/booking/v2/slot", + { + body: JSON.stringify({ + startDate: "2024-06-05T22:00:00.000Z", + endDate: "2024-06-22T21:59:59.999Z", + itemsQuery: [{ + id: "5cb74891ff800f00007f32d5", + priceValue: 16, + staffIds: ["5cb721b52561230000f9f5dd","5cb721b52561230000f9f5dd"], + }], + }), + headers: { "Content-Type": "application/json;charset=utf-8" }, + responseType: "json", + }, + ); + + return body; +} diff --git a/src/bot.ts b/src/bot.ts new file mode 100644 index 0000000..1d6aa9c --- /dev/null +++ b/src/bot.ts @@ -0,0 +1,18 @@ +import dotenv from "dotenv"; +import { Telegraf } from "telegraf"; + +const pad = (str: number) => str.toString().padStart(2, "0"); +const format = (date: Date) => `RDV chez Bag dispo le ${pad(date.getDate())}/${pad(date.getMonth() + 1)}/${pad(date.getFullYear())} à ${pad(date.getHours())}:${pad(date.getMinutes())}.\nhttps://booking.wavy.pro/hairnewscoiffure`; + +dotenv.config(); + +const bot = new Telegraf(process.env.TELEGRAM_BOT_TOKEN!); +bot.launch(); + +// Enable graceful stop +process.once("SIGINT", () => bot.stop("SIGINT")) +process.once("SIGTERM", () => bot.stop("SIGTERM")) + +export async function notify(date: Date) { + await bot.telegram.sendMessage(716477900, format(date)); +} \ No newline at end of file diff --git a/src/cache.ts b/src/cache.ts index 5f04e05..1b4124b 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,24 +1,20 @@ import fs from "fs"; -if (!fs.existsSync("./cache.json")) { - fs.writeFileSync("./cache.json", JSON.stringify([]), "utf8"); -} - -type SlotDate = string; -type SlotTime = number; -type Slot = [SlotDate, SlotTime]; +type AppointmentDate = string; +type AppointmentTime = number; +type Appointment = [AppointmentDate, AppointmentTime]; if (!fs.existsSync("./cache.json")) { fs.writeFileSync("./cache.json", JSON.stringify([]), "utf8"); } -let knownSlots: Slot[] = JSON.parse(fs.readFileSync("./cache.json", "utf8").toString()); +let cachedAppointments: Appointment[] = JSON.parse(fs.readFileSync("./cache.json", "utf8").toString()); -export function getKnownSlots() { - return knownSlots; +export function getCachedAppointments() { + return cachedAppointments; } -export function addKnownSlot(slot: Slot) { - knownSlots.push(slot); - fs.writeFileSync("./cache.json", JSON.stringify(knownSlots), "utf8"); +export function addCachedAppointment(appointment: Appointment) { + cachedAppointments.push(appointment); + fs.writeFileSync("./cache.json", JSON.stringify(cachedAppointments), "utf8"); } diff --git a/src/notify.ts b/src/notify.ts deleted file mode 100644 index 2c0cb11..0000000 --- a/src/notify.ts +++ /dev/null @@ -1,41 +0,0 @@ -import dotenv from "dotenv"; -import twilio from "twilio"; -import { Telegraf } from "telegraf"; - -const pad = (str: number) => str.toString().padStart(2, "0"); -const format = (date: Date) => `RDV chez Bag dispo le ${pad(date.getDate())}/${pad(date.getMonth() + 1)}/${pad(date.getFullYear())} à ${pad(date.getHours())}:${pad(date.getMinutes())}.\nhttps://booking.wavy.pro/hairnewscoiffure`; - -dotenv.config(); - -/* const twilioClient = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); - -async function sendSMS(date: Date, to: string) { - await twilioClient.messages.create({ - body: format(date), - to, - from: "+33757592025", - }); -} - -// const notifiers = ["+33613370787", "+33673899359"] as const; -const notifiers = ["+33613370787"] as const; - -export default async function notify(date: Date) { - await Promise.all(notifiers.map(async phoneNumber => sendSMS(date, phoneNumber))); -} -*/ - -const bot = new Telegraf(process.env.TELEGRAM_BOT_TOKEN!); -bot.launch(); - -// Enable graceful stop -process.once("SIGINT", () => bot.stop("SIGINT")) -process.once("SIGTERM", () => bot.stop("SIGTERM")) - -const notifiers = [ - async (date: Date) => bot.telegram.sendMessage(716477900, format(date)), -]; - -export default async function notify(date: Date) { - await Promise.all(notifiers.map(notifier => notifier(date))); -} \ No newline at end of file diff --git a/src/run.ts b/src/run.ts deleted file mode 100644 index f8c9050..0000000 --- a/src/run.ts +++ /dev/null @@ -1,23 +0,0 @@ -import notify from "./notify.js"; -import { getFirstAvailableSlot, getSlots } from "./slots.js"; - -export default async function run() { - const slots = await getSlots(); - const availableSlot = getFirstAvailableSlot(slots); - if (!availableSlot) { - console.log("no appt found"); - return sleep(); - } - - if (new Date() > availableSlot) { - console.log("found one but already expired", availableSlot.toISOString()); - return sleep(); - } - - console.log("found one", availableSlot.toISOString()); - await notify(availableSlot); -} - -const SECOND = 1000; -const MINUTE = 60 * SECOND; -const sleep = async (ms = 1 * MINUTE) => new Promise(r => setTimeout(r, ms)); diff --git a/src/slots.ts b/src/slots.ts deleted file mode 100644 index fadab42..0000000 --- a/src/slots.ts +++ /dev/null @@ -1,64 +0,0 @@ -import got from "got"; - -import { addKnownSlot, getKnownSlots } from "./cache.js"; - -export function getFirstAvailableSlot(response: Response): Date | null { - const knownSlots = getKnownSlots(); - - for (const slotDate of Object.keys(response)) { - const slots = response[slotDate]; - const hasSlots = slots.length > 0; - if (hasSlots) { - for (const slot of slots) { - const slotTime = slot.startHM; - const knownSlot = knownSlots.find(([d, t]) => d === slotDate && t === slotTime); - if (!knownSlot) { - addKnownSlot([slotDate, slotTime]); - const [year, month, day] = slotDate.split("-").map(s => parseInt(s, 10)); - const hours = Math.trunc(slotTime); - const minutes = (slotTime % hours) * 60; - - return new Date(year, month - 1, day, hours, minutes); - } - } - } - } - - return null; -} - -export async function getSlots() { - const { body } = await got.post( - "https://api.wavy.fr/shops/c5593def-a89c-4296-ac87-f484d8a22ecb/booking/v2/slot", - { - body: JSON.stringify({ - startDate: "2024-06-05T22:00:00.000Z", - endDate: "2024-06-22T21:59:59.999Z", - itemsQuery: [{ - id: "5cb74891ff800f00007f32d5", - priceValue: 16, - staffIds: ["5cb721b52561230000f9f5dd","5cb721b52561230000f9f5dd"], - }], - }), - headers: { "Content-Type": "application/json;charset=utf-8" }, - responseType: "json", - }, - ); - - console.log("body", body) - - return body; -} - -type Slot = { - startHM: number; - staffUnavailable: {}; - shopUnavailable: boolean; - queriesFitStaffs: string[][]; - isFit: boolean; - start: number; -} - -type Response = { - [date: string]: Slot[]; -} diff --git a/tsconfig.json b/tsconfig.json index 6c676e5..b507688 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node20/tsconfig.json", + "extends": "@tsconfig/node22/tsconfig.json", "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext",