diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/auth.json b/auth.json new file mode 100644 index 0000000..520494a --- /dev/null +++ b/auth.json @@ -0,0 +1,4 @@ +{ + "email": "", + "password": "" +} diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..698a6b8 --- /dev/null +++ b/bun.lock @@ -0,0 +1,224 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "test", + "dependencies": { + "puppeteer": "^24.22.2", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + + "@puppeteer/browsers": ["@puppeteer/browsers@2.10.10", "", { "dependencies": { "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.2", "tar-fs": "^3.1.0", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-3ZG500+ZeLql8rE0hjfhkycJjDj0pI/btEh3L9IkWUYcOrgP0xCNRq3HbtbqOPbvDhFaAWD88pDFtlLv8ns8gA=="], + + "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], + + "@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="], + + "@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="], + + "@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="], + + "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], + + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], + + "b4a": ["b4a@1.7.2", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-DyUOdz+E8R6+sruDpQNOaV0y/dBbV6X/8ZkxrDcR0Ifc3BgKlpgG0VAtfOozA0eMtJO5GGe9FsZhueLs00pTww=="], + + "bare-events": ["bare-events@2.7.0", "", {}, "sha512-b3N5eTW1g7vXkw+0CXh/HazGTcO5KYuu/RCNaJbDMPI6LHDi+7qe8EmxKUVe1sUbY2KZOVZFyj62x0OEz9qyAA=="], + + "bare-fs": ["bare-fs@4.4.4", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-Q8yxM1eLhJfuM7KXVP3zjhBvtMJCYRByoTT+wHXjpdMELv0xICFJX+1w4c7csa+WZEOsq4ItJ4RGwvzid6m/dw=="], + + "bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="], + + "bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="], + + "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], + + "bare-url": ["bare-url@2.2.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA=="], + + "basic-ftp": ["basic-ftp@5.0.5", "", {}, "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg=="], + + "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], + + "bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "chromium-bidi": ["chromium-bidi@8.0.0", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-d1VmE0FD7lxZQHzcDUCKZSNRtRwISXDsdg4HjdTR5+Ll5nQ/vzU12JeNmupD6VWffrPSlrnGhEWlLESKH3VO+g=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="], + + "devtools-protocol": ["devtools-protocol@0.0.1495869", "", {}, "sha512-i+bkd9UYFis40RcnkW7XrOprCujXRAHg62IVh/Ah3G8MmNXpCGt1m0dTFhSdx/AVs8XEMbdOGRwdkR1Bcta8AA=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="], + + "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], + + "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], + + "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], + + "get-uri": ["get-uri@6.0.5", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], + + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "pac-proxy-agent": ["pac-proxy-agent@7.2.0", "", { "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", "socks-proxy-agent": "^8.0.5" } }, "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA=="], + + "pac-resolver": ["pac-resolver@7.0.1", "", { "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" } }, "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + + "proxy-agent": ["proxy-agent@6.5.0", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", "socks-proxy-agent": "^8.0.5" } }, "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + + "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], + + "puppeteer": ["puppeteer@24.22.2", "", { "dependencies": { "@puppeteer/browsers": "2.10.10", "chromium-bidi": "8.0.0", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1495869", "puppeteer-core": "24.22.2", "typed-query-selector": "^2.12.0" }, "bin": { "puppeteer": "lib/cjs/puppeteer/node/cli.js" } }, "sha512-tsjIR24nAp/LfEhnBLw11yc0LxzwmB67obPrgqpDZxhub4i5nHxn9pKezcm46d583gdhotSETSA3q3Hbj+ZdNQ=="], + + "puppeteer-core": ["puppeteer-core@24.22.2", "", { "dependencies": { "@puppeteer/browsers": "2.10.10", "chromium-bidi": "8.0.0", "debug": "^4.4.3", "devtools-protocol": "0.0.1495869", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.2.11", "ws": "^8.18.3" } }, "sha512-J1WBOWE2AU57ntwH8EJe10xlpfdimMjmYDDVHna2iiBn85FemU7H6s46Thn+wb7VKqN+YeyYhSjDNE0+R8phoQ=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], + + "socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="], + + "socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="], + + "tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], + + "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typed-query-selector": ["typed-query-selector@2.12.0", "", {}, "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="], + + "webdriver-bidi-protocol": ["webdriver-bidi-protocol@0.2.11", "", {}, "sha512-Y9E1/oi4XMxcR8AT0ZC4OvYntl34SPgwjmELH+owjBr0korAX4jKgZULBWILGCVGdVCQ0dodTToIETozhG8zvA=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + } +} diff --git a/getCourse.ts b/getCourse.ts new file mode 100644 index 0000000..e3ac995 --- /dev/null +++ b/getCourse.ts @@ -0,0 +1,108 @@ +type Course = { + id: number; + start: string; // e.g. "2025-09-24T10:45" (local wall time) + end: string; // e.g. "2025-09-24T12:45" + [k: string]: any; +}; + +import { token } from "./getToken"; + +const TZ = "Europe/Paris"; + +/** Format a Date (interpreted in the given time zone) to YYYYMMDD */ +function formatYMD(date: Date, timeZone = TZ): string { + const fmt = new Intl.DateTimeFormat("en-CA", { + timeZone, + year: "numeric", + month: "2-digit", + day: "2-digit", + }); + // en-CA gives "YYYY-MM-DD" -> strip dashes + return fmt.format(date).replaceAll("-", ""); +} + +/** Return Monday 00:00 and Sunday 23:59:59.999 of the week that contains `now` in TZ */ +function getWeekBounds(now: Date, timeZone = TZ): { monday: Date; sunday: Date } { + // Get the "wall-clock" Y/M/D in TZ + const parts = new Intl.DateTimeFormat("en-GB", { + timeZone, + year: "numeric", + month: "2-digit", + day: "2-digit", + weekday: "short", + }).formatToParts(now); + + const y = Number(parts.find((p) => p.type === "year")!.value); + const m = Number(parts.find((p) => p.type === "month")!.value); + const d = Number(parts.find((p) => p.type === "day")!.value); + const wk = parts.find((p) => p.type === "weekday")!.value; // "Mon".."Sun" + + // Build a Date that represents midnight in TZ by using UTC as a container + // (Date.UTC so it's not shifted by the host machine's local tz) + const midnightInTZ = new Date(Date.UTC(y, m - 1, d, 0, 0, 0, 0)); + + // Map Mon..Sun -> 0..6 (offset from Monday) + const weekdayIdx = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].indexOf(wk); + const monday = new Date(midnightInTZ); + monday.setUTCDate(monday.getUTCDate() - weekdayIdx); + + const sunday = new Date(monday); + sunday.setUTCDate(sunday.getUTCDate() + 6); + sunday.setUTCHours(23, 59, 59, 999); + + return { monday, sunday }; +} + +/** + * Returns the course occurring at the moment this function runs. + * If none is in progress, returns null. + */ +export async function getCurrentCourse(now: Date = new Date()): Promise { + // Compute Mon..Sun bounds in Europe/Paris, then format as YYYYMMDD for the query. + const { monday, sunday } = getWeekBounds(now, TZ); + const startYMD = formatYMD(monday, TZ); // e.g. "20250922" + const endYMD = formatYMD(sunday, TZ); // e.g. "20250928" + + const url = `https://learning.estia.fr/pegasus/index.php?com=emergement&job=get-cours.json&start=${startYMD}&end=${endYMD}`; + + const res = await fetch(url, { + credentials: "include", + headers: { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:142.0) Gecko/20100101 Firefox/142.0", + Accept: "application/json, text/javascript, */*; q=0.01", + "Accept-Language": "en-US,en;q=0.5", + "X-Requested-With": "XMLHttpRequest", + "Sec-GPC": "1", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", + Pragma: "no-cache", + "Cache-Control": "no-cache", + cookie: `PHPSESSID=${token}`, + Referer: "https://learning.estia.fr/pegasus/index.php?com=emergement&job=load-cours-programmes-apprenant", + }, + method: "GET", + mode: "cors", + }); + + if (!res.ok) { + throw new Error(`Failed to fetch courses: ${res.status} ${res.statusText}`); + } + + const courses: Course[] = (await res.json()) as Course[]; + + // Sort by start time just in case (ascending) + courses.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()); + + // Find the one currently in progress: start <= now < end + const current = courses.find((c) => { + const start = new Date(c.start).getTime(); + const end = new Date(c.end).getTime(); + const t = now.getTime(); + return t >= start && t < end; + }); + + console.log("Current course:", current); + + return current ?? null; +} diff --git a/getToken.ts b/getToken.ts new file mode 100644 index 0000000..2c6c752 --- /dev/null +++ b/getToken.ts @@ -0,0 +1,228 @@ +// getPegasusPHPSession.ts +import { secrets, sleep } from "bun"; +import puppeteer, { Browser, Page } from "puppeteer"; +import auth from "./auth.json"; + +// ——— Credentials from Bun secrets (unchanged) ——— +const email = auth.email || (await secrets.get({ service: "estia", name: "email" })) || ""; +const password = auth.password || (await secrets.get({ service: "estia", name: "password" })) || ""; + +// ——— Options ——— +export type GetSessionOpts = { + headless?: true | false | "new"; + debugScreenshots?: boolean; + timeoutMs?: number; +}; + +// ——— Utility: screenshots only when debugging ——— +async function shot(page: Page, name: string, enabled: boolean) { + if (!enabled) return; + try { + await page.screenshot({ path: `./${name}.png`, fullPage: true }); + } catch {} +} + +// ——— Utility: wait for either navigation or one of the selectors ——— +async function waitForNavOrSelector( + page: Page, + selectors: string[], + timeout = 30_000 +): Promise<"navigated" | { selector: string }> { + const selPromise = (async () => { + const start = Date.now(); + while (Date.now() - start < timeout) { + for (const sel of selectors) { + const el = await page.$(sel); + if (el) return { selector: sel as string }; + } + await page.waitForTimeout(150); + } + throw new Error("timeout waiting for selectors"); + })(); + + const navPromise = page + .waitForNavigation({ waitUntil: "domcontentloaded", timeout }) + .then(() => "navigated" as const); + + try { + return await Promise.race([selPromise, navPromise]); + } catch { + // If both rejected (rare), try a small grace wait + await page.waitForTimeout(300); + return "navigated"; + } +} + +// ——— Utility: resilient click that retries across navigations ——— +async function clickResilient(page: Page, selector: string, { timeout = 10_000 }: { timeout?: number } = {}) { + const end = Date.now() + timeout; + while (Date.now() < end) { + try { + await page.waitForSelector(selector, { visible: true, timeout: 1000 }); + await page.click(selector, { delay: 20 }); + return; + } catch (err: any) { + const msg = String(err?.message || ""); + // These are fine — we’ll retry until timeout + if ( + msg.includes("Execution context was destroyed") || + msg.includes("Inspected target navigated or closed") || + msg.includes("Cannot find context with specified id") + ) { + await page.waitForTimeout(150); + continue; + } + // If element isn't there yet, keep looping + if (msg.includes("waiting for selector")) { + await page.waitForTimeout(150); + continue; + } + // Unknown error — bubble up + throw err; + } + } + throw new Error(`Timeout clicking ${selector}`); +} + +// ——— Utility: wait until any page returns to the Pegasus host ——— +async function pickPegasusPage(browser: Browser, timeout = 30_000): Promise { + const end = Date.now() + timeout; + while (Date.now() < end) { + const pages = await browser.pages(); + for (const p of pages) { + const url = p.url(); + if (url.includes("learning.estia.fr")) return p; + } + await new Promise((r) => setTimeout(r, 300)); + } + throw new Error("Timeout waiting to return to learning.estia.fr"); +} + +// ——— Main ——— +export async function getPegasusPHPSession({ + headless = "new", + debugScreenshots = false, + timeoutMs = 60_000, +}: GetSessionOpts = {}): Promise { + let browser: Browser | undefined; + + try { + browser = await puppeteer.launch({ + headless, + args: ["--no-sandbox", "--disable-setuid-sandbox"], + }); + + const page = await browser.newPage(); + page.setDefaultTimeout(timeoutMs); + + // 1) Open Pegasus + await page.goto("https://learning.estia.fr/pegasus/index.php", { + waitUntil: "networkidle2", + }); + await shot(page, "01_pegasus_home", debugScreenshots); + + // 2) Click Microsoft login (allow same-tab or new-tab) + const msBtn = "a.authlink[href*='o365Auth.php']"; + await page.waitForSelector(msBtn, { visible: true }); + await page.click(msBtn); + + // 2.1) If a new AAD tab opens, switch to it + let authPage: Page = page; + const newTarget = await browser + .waitForTarget((t) => /login\.microsoftonline\.com/i.test(t.url()), { + timeout: 10_000, + }) + .catch(() => undefined); + + if (newTarget) { + const p = await newTarget.page(); + if (p) authPage = p; + } else { + // Same tab flow + await page.waitForNavigation({ waitUntil: "domcontentloaded" }).catch(() => {}); + } + await shot(authPage, "02_after_click_ms", debugScreenshots); + + // 3) Email + await authPage.waitForSelector('input[name="loginfmt"]', { visible: true }); + await authPage.type('input[name="loginfmt"]', email, { delay: 20 }); + await authPage.keyboard.press("Enter"); + + // (Optional) tenant account picker + await authPage + .waitForSelector("#i0118, div[role='button'][data-telemetryid='UserTile']", { timeout: 5000 }) + .catch(() => {}); + const tile = await authPage.$("div[role='button'][data-telemetryid='UserTile']"); + if (tile) { + await clickResilient(authPage, "div[role='button'][data-telemetryid='UserTile']", { + timeout: 5_000, + }); + } + + await sleep(2000); // Wait a bit for the transition + + // 4) Password + await authPage.waitForSelector("#i0118", { visible: true }); + await authPage.type("#i0118", password, { delay: 20 }); + await authPage.keyboard.press("Enter"); + await shot(authPage, "03_after_password", debugScreenshots); + + await sleep(2000); // Wait a bit for the transition + + // 5) KMSI (Stay signed in) — prefer "No", else "Yes". + // We *don’t* run big evaluate() here; we click selectors resiliently. + const kmsiCandidates = ["#idBtn_Back", "#idSIButton9"]; + const kmsiOutcome = await waitForNavOrSelector(authPage, kmsiCandidates, 20_000).catch( + () => "navigated" as const + ); + + if (kmsiOutcome !== "navigated") { + const toClick = + kmsiOutcome.selector === "#idBtn_Back" + ? "#idBtn_Back" + : (await authPage.$("#idBtn_Back")) + ? "#idBtn_Back" + : "#idSIButton9"; + try { + await clickResilient(authPage, toClick, { timeout: 8_000 }); + } catch { + // If we miss it due to instant nav, that’s okay + } + } + await shot(authPage, "04_after_kmsi", debugScreenshots); + + // 6) Back to Pegasus (could happen in any tab) + let finalPage: Page; + try { + finalPage = await pickPegasusPage(browser, 30_000); + } catch { + // As a fallback, let the current authPage settle + await authPage.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 10_000 }).catch(() => {}); + finalPage = authPage; + } + + // Network settle + try { + await finalPage.waitForNetworkIdle({ idleTime: 1000, timeout: 30_000 }); + } catch {} + await shot(finalPage, "05_back_on_pegasus", debugScreenshots); + + // 7) Cookies → PHPSESSID + const cookies = await finalPage.cookies("https://learning.estia.fr"); + const php = cookies.find((c) => c.name.toLowerCase() === "phpsessid"); + if (!php?.value) { + throw new Error("PHPSESSID cookie not found. Are you logged in?"); + } + return php.value; + } finally { + if (browser) { + try { + await browser.close(); + } catch {} + } + } +} + +const token = await getPegasusPHPSession({ headless: true, debugScreenshots: false }); + +export { token }; diff --git a/images/shrekJPEG.txt b/images/shrekJPEG.txt new file mode 100644 index 0000000..150d980 --- /dev/null +++ b/images/shrekJPEG.txt @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..5a00825 --- /dev/null +++ b/index.ts @@ -0,0 +1,28 @@ +import { token } from "./getToken"; + +import shrekJPEG from "./images/shrekJPEG.txt"; +import { getCurrentCourse } from "./getCourse"; + +const params = new URLSearchParams({ + COURS_PROGRAMME: await getCurrentCourse().then((c) => (c ? c.COURS_PROGRAMME.toString() : "")), + signatureJSON: `data:image/jpeg;base64,${shrekJPEG}`, // your raw base64 (no spaces) +}); + +console.log( + await fetch("https://learning.estia.fr/pegasus/index.php?com=emergement&job=enregistrer-emergement", { + headers: { + accept: "application/json, text/javascript, */*; q=0.01", + "content-type": "application/x-www-form-urlencoded; charset=UTF-8", + "sec-ch-ua": '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"macOS"', + "x-requested-with": "XMLHttpRequest", + cookie: `PHPSESSID=${token}`, + }, + referrer: "https://learning.estia.fr/pegasus/index.php?com=emergement&job=load-cours-programmes-apprenant", + body: params.toString(), + method: "POST", + mode: "cors", + credentials: "omit", + }).then((response) => response.text()) +); diff --git a/package.json b/package.json new file mode 100644 index 0000000..e4a2200 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "test", + "module": "index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "scripts": { + "start": "bun index.ts", + "build": "bun build --compile index.ts && mv index ~/bin/emarger" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "puppeteer": "^24.22.2" + } +} diff --git a/setMail.ts b/setMail.ts new file mode 100644 index 0000000..feeeab8 --- /dev/null +++ b/setMail.ts @@ -0,0 +1,22 @@ +// Get a secret email and password from input and set it to Bun.secrets + +console.log("Usage: bun run setMail.ts "); +if (process.argv.length !== 4) { + console.error("Expected exactly two arguments: "); + process.exit(1); +} + +const email = process.argv[2]; +const password = process.argv[3]; + +await Bun.secrets.set({ + service: "estia", + name: "email", + value: email || "", +}); + +await Bun.secrets.set({ + service: "estia", + name: "password", + value: password || "", +}); diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..8db7452 --- /dev/null +++ b/test.ts @@ -0,0 +1,4 @@ +import { getCurrentCourse } from "./getCourse"; +import { getPegasusPHPSession } from "./getToken"; + +console.log(await getPegasusPHPSession({ headless: false })); diff --git a/token.txt b/token.txt new file mode 100644 index 0000000..6919c95 --- /dev/null +++ b/token.txt @@ -0,0 +1 @@ +gtlmqhsmoe4ptb1qa7ujq9s2tv \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bfa0fea --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}