A simple tool which lets you scrape twitter accounts and crosspost them to bluesky accounts! Comes with a CLI and a webapp for managing profiles! Works with images/videos/link embeds/threads.

fix: compress images that exceed Bluesky's 1MB limit

jack c305e279 92a7d8fb

+573 -3
+532 -1
package-lock.json
··· 23 23 "iso-639-1": "^3.1.2", 24 24 "jsonwebtoken": "^9.0.3", 25 25 "node-cron": "^4.2.1", 26 - "puppeteer-core": "^24.34.0" 26 + "puppeteer-core": "^24.34.0", 27 + "sharp": "^0.34.5" 27 28 }, 28 29 "devDependencies": { 29 30 "@biomejs/biome": "^1.9.4", ··· 34 35 "@types/inquirer": "^9.0.9", 35 36 "@types/jsonwebtoken": "^9.0.10", 36 37 "@types/node": "^22.10.2", 38 + "@types/sharp": "^0.31.1", 37 39 "tsx": "^4.19.2", 38 40 "typescript": "^5.7.2" 39 41 } ··· 279 281 ], 280 282 "engines": { 281 283 "node": ">=14.21.3" 284 + } 285 + }, 286 + "node_modules/@emnapi/runtime": { 287 + "version": "1.7.1", 288 + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", 289 + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", 290 + "license": "MIT", 291 + "optional": true, 292 + "dependencies": { 293 + "tslib": "^2.4.0" 282 294 } 283 295 }, 284 296 "node_modules/@esbuild/aix-ppc64": { ··· 723 735 "node": ">=18" 724 736 } 725 737 }, 738 + "node_modules/@img/colour": { 739 + "version": "1.0.0", 740 + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", 741 + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", 742 + "license": "MIT", 743 + "engines": { 744 + "node": ">=18" 745 + } 746 + }, 747 + "node_modules/@img/sharp-darwin-arm64": { 748 + "version": "0.34.5", 749 + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", 750 + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", 751 + "cpu": [ 752 + "arm64" 753 + ], 754 + "license": "Apache-2.0", 755 + "optional": true, 756 + "os": [ 757 + "darwin" 758 + ], 759 + "engines": { 760 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 761 + }, 762 + "funding": { 763 + "url": "https://opencollective.com/libvips" 764 + }, 765 + "optionalDependencies": { 766 + "@img/sharp-libvips-darwin-arm64": "1.2.4" 767 + } 768 + }, 769 + "node_modules/@img/sharp-darwin-x64": { 770 + "version": "0.34.5", 771 + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", 772 + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", 773 + "cpu": [ 774 + "x64" 775 + ], 776 + "license": "Apache-2.0", 777 + "optional": true, 778 + "os": [ 779 + "darwin" 780 + ], 781 + "engines": { 782 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 783 + }, 784 + "funding": { 785 + "url": "https://opencollective.com/libvips" 786 + }, 787 + "optionalDependencies": { 788 + "@img/sharp-libvips-darwin-x64": "1.2.4" 789 + } 790 + }, 791 + "node_modules/@img/sharp-libvips-darwin-arm64": { 792 + "version": "1.2.4", 793 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", 794 + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", 795 + "cpu": [ 796 + "arm64" 797 + ], 798 + "license": "LGPL-3.0-or-later", 799 + "optional": true, 800 + "os": [ 801 + "darwin" 802 + ], 803 + "funding": { 804 + "url": "https://opencollective.com/libvips" 805 + } 806 + }, 807 + "node_modules/@img/sharp-libvips-darwin-x64": { 808 + "version": "1.2.4", 809 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", 810 + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", 811 + "cpu": [ 812 + "x64" 813 + ], 814 + "license": "LGPL-3.0-or-later", 815 + "optional": true, 816 + "os": [ 817 + "darwin" 818 + ], 819 + "funding": { 820 + "url": "https://opencollective.com/libvips" 821 + } 822 + }, 823 + "node_modules/@img/sharp-libvips-linux-arm": { 824 + "version": "1.2.4", 825 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", 826 + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", 827 + "cpu": [ 828 + "arm" 829 + ], 830 + "license": "LGPL-3.0-or-later", 831 + "optional": true, 832 + "os": [ 833 + "linux" 834 + ], 835 + "funding": { 836 + "url": "https://opencollective.com/libvips" 837 + } 838 + }, 839 + "node_modules/@img/sharp-libvips-linux-arm64": { 840 + "version": "1.2.4", 841 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", 842 + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", 843 + "cpu": [ 844 + "arm64" 845 + ], 846 + "license": "LGPL-3.0-or-later", 847 + "optional": true, 848 + "os": [ 849 + "linux" 850 + ], 851 + "funding": { 852 + "url": "https://opencollective.com/libvips" 853 + } 854 + }, 855 + "node_modules/@img/sharp-libvips-linux-ppc64": { 856 + "version": "1.2.4", 857 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", 858 + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", 859 + "cpu": [ 860 + "ppc64" 861 + ], 862 + "license": "LGPL-3.0-or-later", 863 + "optional": true, 864 + "os": [ 865 + "linux" 866 + ], 867 + "funding": { 868 + "url": "https://opencollective.com/libvips" 869 + } 870 + }, 871 + "node_modules/@img/sharp-libvips-linux-riscv64": { 872 + "version": "1.2.4", 873 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", 874 + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", 875 + "cpu": [ 876 + "riscv64" 877 + ], 878 + "license": "LGPL-3.0-or-later", 879 + "optional": true, 880 + "os": [ 881 + "linux" 882 + ], 883 + "funding": { 884 + "url": "https://opencollective.com/libvips" 885 + } 886 + }, 887 + "node_modules/@img/sharp-libvips-linux-s390x": { 888 + "version": "1.2.4", 889 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", 890 + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", 891 + "cpu": [ 892 + "s390x" 893 + ], 894 + "license": "LGPL-3.0-or-later", 895 + "optional": true, 896 + "os": [ 897 + "linux" 898 + ], 899 + "funding": { 900 + "url": "https://opencollective.com/libvips" 901 + } 902 + }, 903 + "node_modules/@img/sharp-libvips-linux-x64": { 904 + "version": "1.2.4", 905 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", 906 + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", 907 + "cpu": [ 908 + "x64" 909 + ], 910 + "license": "LGPL-3.0-or-later", 911 + "optional": true, 912 + "os": [ 913 + "linux" 914 + ], 915 + "funding": { 916 + "url": "https://opencollective.com/libvips" 917 + } 918 + }, 919 + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { 920 + "version": "1.2.4", 921 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", 922 + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", 923 + "cpu": [ 924 + "arm64" 925 + ], 926 + "license": "LGPL-3.0-or-later", 927 + "optional": true, 928 + "os": [ 929 + "linux" 930 + ], 931 + "funding": { 932 + "url": "https://opencollective.com/libvips" 933 + } 934 + }, 935 + "node_modules/@img/sharp-libvips-linuxmusl-x64": { 936 + "version": "1.2.4", 937 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", 938 + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", 939 + "cpu": [ 940 + "x64" 941 + ], 942 + "license": "LGPL-3.0-or-later", 943 + "optional": true, 944 + "os": [ 945 + "linux" 946 + ], 947 + "funding": { 948 + "url": "https://opencollective.com/libvips" 949 + } 950 + }, 951 + "node_modules/@img/sharp-linux-arm": { 952 + "version": "0.34.5", 953 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", 954 + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", 955 + "cpu": [ 956 + "arm" 957 + ], 958 + "license": "Apache-2.0", 959 + "optional": true, 960 + "os": [ 961 + "linux" 962 + ], 963 + "engines": { 964 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 965 + }, 966 + "funding": { 967 + "url": "https://opencollective.com/libvips" 968 + }, 969 + "optionalDependencies": { 970 + "@img/sharp-libvips-linux-arm": "1.2.4" 971 + } 972 + }, 973 + "node_modules/@img/sharp-linux-arm64": { 974 + "version": "0.34.5", 975 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", 976 + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", 977 + "cpu": [ 978 + "arm64" 979 + ], 980 + "license": "Apache-2.0", 981 + "optional": true, 982 + "os": [ 983 + "linux" 984 + ], 985 + "engines": { 986 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 987 + }, 988 + "funding": { 989 + "url": "https://opencollective.com/libvips" 990 + }, 991 + "optionalDependencies": { 992 + "@img/sharp-libvips-linux-arm64": "1.2.4" 993 + } 994 + }, 995 + "node_modules/@img/sharp-linux-ppc64": { 996 + "version": "0.34.5", 997 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", 998 + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", 999 + "cpu": [ 1000 + "ppc64" 1001 + ], 1002 + "license": "Apache-2.0", 1003 + "optional": true, 1004 + "os": [ 1005 + "linux" 1006 + ], 1007 + "engines": { 1008 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1009 + }, 1010 + "funding": { 1011 + "url": "https://opencollective.com/libvips" 1012 + }, 1013 + "optionalDependencies": { 1014 + "@img/sharp-libvips-linux-ppc64": "1.2.4" 1015 + } 1016 + }, 1017 + "node_modules/@img/sharp-linux-riscv64": { 1018 + "version": "0.34.5", 1019 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", 1020 + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", 1021 + "cpu": [ 1022 + "riscv64" 1023 + ], 1024 + "license": "Apache-2.0", 1025 + "optional": true, 1026 + "os": [ 1027 + "linux" 1028 + ], 1029 + "engines": { 1030 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1031 + }, 1032 + "funding": { 1033 + "url": "https://opencollective.com/libvips" 1034 + }, 1035 + "optionalDependencies": { 1036 + "@img/sharp-libvips-linux-riscv64": "1.2.4" 1037 + } 1038 + }, 1039 + "node_modules/@img/sharp-linux-s390x": { 1040 + "version": "0.34.5", 1041 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", 1042 + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", 1043 + "cpu": [ 1044 + "s390x" 1045 + ], 1046 + "license": "Apache-2.0", 1047 + "optional": true, 1048 + "os": [ 1049 + "linux" 1050 + ], 1051 + "engines": { 1052 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1053 + }, 1054 + "funding": { 1055 + "url": "https://opencollective.com/libvips" 1056 + }, 1057 + "optionalDependencies": { 1058 + "@img/sharp-libvips-linux-s390x": "1.2.4" 1059 + } 1060 + }, 1061 + "node_modules/@img/sharp-linux-x64": { 1062 + "version": "0.34.5", 1063 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", 1064 + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", 1065 + "cpu": [ 1066 + "x64" 1067 + ], 1068 + "license": "Apache-2.0", 1069 + "optional": true, 1070 + "os": [ 1071 + "linux" 1072 + ], 1073 + "engines": { 1074 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1075 + }, 1076 + "funding": { 1077 + "url": "https://opencollective.com/libvips" 1078 + }, 1079 + "optionalDependencies": { 1080 + "@img/sharp-libvips-linux-x64": "1.2.4" 1081 + } 1082 + }, 1083 + "node_modules/@img/sharp-linuxmusl-arm64": { 1084 + "version": "0.34.5", 1085 + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", 1086 + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", 1087 + "cpu": [ 1088 + "arm64" 1089 + ], 1090 + "license": "Apache-2.0", 1091 + "optional": true, 1092 + "os": [ 1093 + "linux" 1094 + ], 1095 + "engines": { 1096 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1097 + }, 1098 + "funding": { 1099 + "url": "https://opencollective.com/libvips" 1100 + }, 1101 + "optionalDependencies": { 1102 + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" 1103 + } 1104 + }, 1105 + "node_modules/@img/sharp-linuxmusl-x64": { 1106 + "version": "0.34.5", 1107 + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", 1108 + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", 1109 + "cpu": [ 1110 + "x64" 1111 + ], 1112 + "license": "Apache-2.0", 1113 + "optional": true, 1114 + "os": [ 1115 + "linux" 1116 + ], 1117 + "engines": { 1118 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1119 + }, 1120 + "funding": { 1121 + "url": "https://opencollective.com/libvips" 1122 + }, 1123 + "optionalDependencies": { 1124 + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" 1125 + } 1126 + }, 1127 + "node_modules/@img/sharp-wasm32": { 1128 + "version": "0.34.5", 1129 + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", 1130 + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", 1131 + "cpu": [ 1132 + "wasm32" 1133 + ], 1134 + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", 1135 + "optional": true, 1136 + "dependencies": { 1137 + "@emnapi/runtime": "^1.7.0" 1138 + }, 1139 + "engines": { 1140 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1141 + }, 1142 + "funding": { 1143 + "url": "https://opencollective.com/libvips" 1144 + } 1145 + }, 1146 + "node_modules/@img/sharp-win32-arm64": { 1147 + "version": "0.34.5", 1148 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", 1149 + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", 1150 + "cpu": [ 1151 + "arm64" 1152 + ], 1153 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1154 + "optional": true, 1155 + "os": [ 1156 + "win32" 1157 + ], 1158 + "engines": { 1159 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1160 + }, 1161 + "funding": { 1162 + "url": "https://opencollective.com/libvips" 1163 + } 1164 + }, 1165 + "node_modules/@img/sharp-win32-ia32": { 1166 + "version": "0.34.5", 1167 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", 1168 + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", 1169 + "cpu": [ 1170 + "ia32" 1171 + ], 1172 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1173 + "optional": true, 1174 + "os": [ 1175 + "win32" 1176 + ], 1177 + "engines": { 1178 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1179 + }, 1180 + "funding": { 1181 + "url": "https://opencollective.com/libvips" 1182 + } 1183 + }, 1184 + "node_modules/@img/sharp-win32-x64": { 1185 + "version": "0.34.5", 1186 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", 1187 + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", 1188 + "cpu": [ 1189 + "x64" 1190 + ], 1191 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1192 + "optional": true, 1193 + "os": [ 1194 + "win32" 1195 + ], 1196 + "engines": { 1197 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1198 + }, 1199 + "funding": { 1200 + "url": "https://opencollective.com/libvips" 1201 + } 1202 + }, 726 1203 "node_modules/@inquirer/ansi": { 727 1204 "version": "2.0.2", 728 1205 "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.2.tgz", ··· 1245 1722 "license": "MIT", 1246 1723 "dependencies": { 1247 1724 "@types/http-errors": "*", 1725 + "@types/node": "*" 1726 + } 1727 + }, 1728 + "node_modules/@types/sharp": { 1729 + "version": "0.31.1", 1730 + "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.31.1.tgz", 1731 + "integrity": "sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==", 1732 + "dev": true, 1733 + "license": "MIT", 1734 + "dependencies": { 1248 1735 "@types/node": "*" 1249 1736 } 1250 1737 }, ··· 3507 3994 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 3508 3995 "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 3509 3996 "license": "ISC" 3997 + }, 3998 + "node_modules/sharp": { 3999 + "version": "0.34.5", 4000 + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", 4001 + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", 4002 + "hasInstallScript": true, 4003 + "license": "Apache-2.0", 4004 + "dependencies": { 4005 + "@img/colour": "^1.0.0", 4006 + "detect-libc": "^2.1.2", 4007 + "semver": "^7.7.3" 4008 + }, 4009 + "engines": { 4010 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 4011 + }, 4012 + "funding": { 4013 + "url": "https://opencollective.com/libvips" 4014 + }, 4015 + "optionalDependencies": { 4016 + "@img/sharp-darwin-arm64": "0.34.5", 4017 + "@img/sharp-darwin-x64": "0.34.5", 4018 + "@img/sharp-libvips-darwin-arm64": "1.2.4", 4019 + "@img/sharp-libvips-darwin-x64": "1.2.4", 4020 + "@img/sharp-libvips-linux-arm": "1.2.4", 4021 + "@img/sharp-libvips-linux-arm64": "1.2.4", 4022 + "@img/sharp-libvips-linux-ppc64": "1.2.4", 4023 + "@img/sharp-libvips-linux-riscv64": "1.2.4", 4024 + "@img/sharp-libvips-linux-s390x": "1.2.4", 4025 + "@img/sharp-libvips-linux-x64": "1.2.4", 4026 + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", 4027 + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", 4028 + "@img/sharp-linux-arm": "0.34.5", 4029 + "@img/sharp-linux-arm64": "0.34.5", 4030 + "@img/sharp-linux-ppc64": "0.34.5", 4031 + "@img/sharp-linux-riscv64": "0.34.5", 4032 + "@img/sharp-linux-s390x": "0.34.5", 4033 + "@img/sharp-linux-x64": "0.34.5", 4034 + "@img/sharp-linuxmusl-arm64": "0.34.5", 4035 + "@img/sharp-linuxmusl-x64": "0.34.5", 4036 + "@img/sharp-wasm32": "0.34.5", 4037 + "@img/sharp-win32-arm64": "0.34.5", 4038 + "@img/sharp-win32-ia32": "0.34.5", 4039 + "@img/sharp-win32-x64": "0.34.5" 4040 + } 3510 4041 }, 3511 4042 "node_modules/side-channel": { 3512 4043 "version": "1.1.0",
+3 -1
package.json
··· 39 39 "iso-639-1": "^3.1.2", 40 40 "jsonwebtoken": "^9.0.3", 41 41 "node-cron": "^4.2.1", 42 - "puppeteer-core": "^24.34.0" 42 + "puppeteer-core": "^24.34.0", 43 + "sharp": "^0.34.5" 43 44 }, 44 45 "devDependencies": { 45 46 "@biomejs/biome": "^1.9.4", ··· 50 51 "@types/inquirer": "^9.0.9", 51 52 "@types/jsonwebtoken": "^9.0.10", 52 53 "@types/node": "^22.10.2", 54 + "@types/sharp": "^0.31.1", 53 55 "tsx": "^4.19.2", 54 56 "typescript": "^5.7.2" 55 57 }
+38 -1
src/index.ts
··· 12 12 import iso6391 from 'iso-639-1'; 13 13 import os from 'node:os'; 14 14 import puppeteer from 'puppeteer-core'; 15 + import sharp from 'sharp'; 15 16 import { getConfig } from './config-manager.js'; 16 17 17 18 // ESM __dirname equivalent ··· 285 286 } 286 287 287 288 async function uploadToBluesky(agent: BskyAgent, buffer: Buffer, mimeType: string): Promise<BlobRef> { 288 - const { data } = await agent.uploadBlob(buffer, { encoding: mimeType }); 289 + let finalBuffer = buffer; 290 + let finalMimeType = mimeType; 291 + const MAX_SIZE = 950 * 1024; // 950KB safety margin 292 + 293 + if (buffer.length > MAX_SIZE && (mimeType.startsWith('image/') || mimeType === 'application/octet-stream')) { 294 + console.log(`[UPLOAD] ⚖️ Image too large (${(buffer.length / 1024).toFixed(2)} KB). Compressing...`); 295 + try { 296 + const image = sharp(buffer); 297 + const metadata = await image.metadata(); 298 + 299 + let pipeline = image; 300 + // If it's a very large resolution, downscale it slightly 301 + if (metadata.width && metadata.width > 2000) { 302 + pipeline = pipeline.resize(2000, undefined, { withoutEnlargement: true }); 303 + } 304 + 305 + finalBuffer = await pipeline 306 + .jpeg({ quality: 85, mozjpeg: true }) 307 + .toBuffer(); 308 + finalMimeType = 'image/jpeg'; 309 + 310 + console.log(`[UPLOAD] ✅ Compressed to ${(finalBuffer.length / 1024).toFixed(2)} KB`); 311 + 312 + // If still too large, aggressive compression 313 + if (finalBuffer.length > MAX_SIZE) { 314 + finalBuffer = await sharp(buffer) 315 + .resize(1200, undefined, { withoutEnlargement: true }) 316 + .jpeg({ quality: 70 }) 317 + .toBuffer(); 318 + console.log(`[UPLOAD] ⚠️ Required aggressive compression: ${(finalBuffer.length / 1024).toFixed(2)} KB`); 319 + } 320 + } catch (err) { 321 + console.warn(`[UPLOAD] ⚠️ Compression failed, attempting original upload:`, (err as Error).message); 322 + } 323 + } 324 + 325 + const { data } = await agent.uploadBlob(finalBuffer, { encoding: finalMimeType }); 289 326 return data.blob; 290 327 } 291 328