This commit is contained in:
m5r 2024-11-15 21:42:19 +01:00
parent e3c8aa7084
commit aa1150bfb5
Signed by: mokhtar
GPG Key ID: 1509B54946D08A95
10 changed files with 141 additions and 686 deletions

28
main.ts
View File

@ -1,11 +1,27 @@
#!/usr/bin/env -S npx tsx #!/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() { async function run() {
while (true) { const availableAppointment = await getFirstAvailableAppointment();
await run(); 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);
}

548
package-lock.json generated
View File

@ -1,23 +1,21 @@
{ {
"name": "cosmosarb", "name": "bagdad",
"version": "1.0.0", "version": "0.0.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "cosmosarb", "name": "bagdad",
"version": "1.0.0", "version": "0.0.1",
"license": "ISC",
"dependencies": { "dependencies": {
"dotenv": "16.4.5", "dotenv": "16.4.5",
"got": "14.4.3", "got": "14.4.4",
"telegraf": "4.16.3", "telegraf": "4.16.3",
"tsx": "4.19.1", "tsx": "4.19.2"
"twilio": "5.3.4"
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node20": "20.1.4", "@tsconfig/node22": "22.0.0",
"@types/node": "22.7.6", "@types/node": "22.9.0",
"typescript": "5.6.3" "typescript": "5.6.3"
} }
}, },
@ -441,10 +439,10 @@
"integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==", "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@tsconfig/node20": { "node_modules/@tsconfig/node22": {
"version": "20.1.4", "version": "22.0.0",
"resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.0.tgz",
"integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", "integrity": "sha512-twLQ77zevtxobBOD4ToAtVmuYrpeYUh3qh+TEp+08IWhpsrIflVHqQ1F1CiPxQGL7doCdBIOOCF+1Tm833faNg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@ -455,13 +453,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.7.6", "version": "22.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
"integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.19.2" "undici-types": "~6.19.8"
} }
}, },
"node_modules/abort-controller": { "node_modules/abort-controller": {
@ -476,35 +474,6 @@
"node": ">=6.5" "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": { "node_modules/buffer-alloc": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
@ -521,12 +490,6 @@
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
"license": "MIT" "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": { "node_modules/buffer-fill": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
@ -560,43 +523,6 @@
"node": ">=18" "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": { "node_modules/debug": {
"version": "4.3.7", "version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
@ -650,32 +576,6 @@
"node": ">=10" "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": { "node_modules/dotenv": {
"version": "16.4.5", "version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
@ -688,36 +588,6 @@
"url": "https://dotenvx.com" "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": { "node_modules/esbuild": {
"version": "0.23.1", "version": "0.23.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
@ -766,40 +636,6 @@
"node": ">=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": { "node_modules/form-data-encoder": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", "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": "^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": { "node_modules/get-stream": {
"version": "9.0.1", "version": "9.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "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" "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": { "node_modules/got": {
"version": "14.4.3", "version": "14.4.4",
"resolved": "https://registry.npmjs.org/got/-/got-14.4.3.tgz", "resolved": "https://registry.npmjs.org/got/-/got-14.4.4.tgz",
"integrity": "sha512-iTC0Z87yxSijWTh/IpvGpwOhIQK7+GgWkYrMRoN/hB9qeRj9RPuLGODwevs0p5idUf7nrxCVa5IlOmK3b8z+KA==", "integrity": "sha512-tqiF7eSgTBwQkxb1LxsEpva8TaMYVisbhplrFVmw9GQE3855Z+MH/mnsXLLOkDxR6hZJRFMj5VTAZ8lmTF8ZOA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@sindresorhus/is": "^7.0.1", "@sindresorhus/is": "^7.0.1",
@ -916,54 +712,6 @@
"url": "https://github.com/sindresorhus/got?sponsor=1" "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": { "node_modules/http-cache-semantics": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
@ -983,19 +731,6 @@
"node": ">=10.19.0" "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": { "node_modules/is-stream": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
@ -1014,49 +749,6 @@
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"license": "MIT" "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": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -1066,48 +758,6 @@
"json-buffer": "3.0.1" "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": { "node_modules/lowercase-keys": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
@ -1120,27 +770,6 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/mimic-response": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
@ -1200,18 +829,6 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/p-cancelable": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz",
@ -1230,27 +847,6 @@
"node": ">=10" "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": { "node_modules/quick-lru": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@ -1293,26 +889,6 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/safe-compare": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz",
@ -1331,59 +907,6 @@
"node": ">= 0.10" "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": { "node_modules/telegraf": {
"version": "4.16.3", "version": "4.16.3",
"resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz",
@ -1413,9 +936,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/tsx": { "node_modules/tsx": {
"version": "4.19.1", "version": "4.19.2",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz",
"integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==", "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "~0.23.0", "esbuild": "~0.23.0",
@ -1431,24 +954,6 @@
"fsevents": "~2.3.3" "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": { "node_modules/type-fest": {
"version": "4.26.1", "version": "4.26.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz",
@ -1497,15 +1002,6 @@
"tr46": "~0.0.3", "tr46": "~0.0.3",
"webidl-conversions": "^3.0.0" "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"
}
} }
} }
} }

View File

@ -1,26 +1,21 @@
{ {
"name": "cosmosarb", "name": "bagdad",
"version": "1.0.0", "version": "0.0.1",
"description": "",
"type": "module", "type": "module",
"module": "main.ts", "module": "main.ts",
"scripts": { "scripts": {
"start": "./main.ts", "start": "tsx ./main.ts",
"dev": "tsx watch ./main.ts" "dev": "tsx watch ./main.ts"
}, },
"keywords": [],
"author": "m5r",
"license": "ISC",
"devDependencies": { "devDependencies": {
"@tsconfig/node20": "20.1.4", "@tsconfig/node22": "22.0.0",
"@types/node": "22.7.6", "@types/node": "22.9.0",
"typescript": "5.6.3" "typescript": "5.6.3"
}, },
"dependencies": { "dependencies": {
"dotenv": "16.4.5", "dotenv": "16.4.5",
"got": "14.4.3", "got": "14.4.4",
"telegraf": "4.16.3", "telegraf": "4.16.3",
"tsx": "4.19.1", "tsx": "4.19.2"
"twilio": "5.3.4"
} }
} }

62
src/appointments.ts Normal file
View File

@ -0,0 +1,62 @@
import got from "got";
import { addCachedAppointment, getCachedAppointments } from "./cache.js";
export async function getFirstAvailableAppointment(): Promise<Date | null> {
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<Response>(
"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;
}

18
src/bot.ts Normal file
View File

@ -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));
}

View File

@ -1,24 +1,20 @@
import fs from "fs"; import fs from "fs";
if (!fs.existsSync("./cache.json")) { type AppointmentDate = string;
fs.writeFileSync("./cache.json", JSON.stringify([]), "utf8"); type AppointmentTime = number;
} type Appointment = [AppointmentDate, AppointmentTime];
type SlotDate = string;
type SlotTime = number;
type Slot = [SlotDate, SlotTime];
if (!fs.existsSync("./cache.json")) { if (!fs.existsSync("./cache.json")) {
fs.writeFileSync("./cache.json", JSON.stringify([]), "utf8"); 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() { export function getCachedAppointments() {
return knownSlots; return cachedAppointments;
} }
export function addKnownSlot(slot: Slot) { export function addCachedAppointment(appointment: Appointment) {
knownSlots.push(slot); cachedAppointments.push(appointment);
fs.writeFileSync("./cache.json", JSON.stringify(knownSlots), "utf8"); fs.writeFileSync("./cache.json", JSON.stringify(cachedAppointments), "utf8");
} }

View File

@ -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)));
}

View File

@ -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));

View File

@ -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<Response>(
"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[];
}

View File

@ -1,5 +1,5 @@
{ {
"extends": "@tsconfig/node20/tsconfig.json", "extends": "@tsconfig/node22/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"module": "NodeNext", "module": "NodeNext",
"moduleResolution": "NodeNext", "moduleResolution": "NodeNext",