diff --git a/bulk-telegram-sticker/.gitignore b/bulk-telegram-sticker/.gitignore index f9736b0..aaadf73 100644 --- a/bulk-telegram-sticker/.gitignore +++ b/bulk-telegram-sticker/.gitignore @@ -1,3 +1,32 @@ -.idea +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Code coverage profiles and other test artifacts +*.out +coverage.* +*.coverprofile +profile.cov + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +go.work.sum + +# env file .env -*.session \ No newline at end of file + +# Editor/IDE +# .idea/ +# .vscode/ diff --git a/bulk-telegram-sticker/README.md b/bulk-telegram-sticker/README.md index 170a28a..ee1be41 100644 --- a/bulk-telegram-sticker/README.md +++ b/bulk-telegram-sticker/README.md @@ -1,108 +1,33 @@ -# Bulk Telegram Sticker Pack Creator +# Bulk Telegram Sticker Pack Creator (Go) -This tool helps you create and upload sticker packs to Telegram using a two-step process: -1. Prepare and process your images -2. Create and upload the sticker pack +Small, Go-based tool to prepare images and upload a Telegram sticker pack. -## Setup +## The Original Python Version -1. Install the required dependencies: -```bash -pip install -r requirements.txt -``` +*In 2025, I rewrote this project using Go. The original Python version of this project can be found at the `feature/python` branch.* -2. Get your Telegram API credentials: - - Go to https://my.telegram.org/auth - - Log in with your phone number - - Go to 'API development tools' - - Create a new application - - Copy the `API_ID` and `API_HASH` +**Note: I haven't tested the Go version. And Telegram Stickers now have app with convenient features, so maybe you don't need these scripts anymore.** -3. Create a `.env` file in the project root with your credentials: -``` -API_ID=your_api_id -API_HASH=your_api_hash -PHONE=your_phone_number -``` +## Prerequisites +- Go 1.24+ installed +- Create a `.env` in the project root with `API_ID`, `API_HASH`, and `PHONE` (see `.env.example`). -## Usage +## Quick steps +1. Ensure module deps are present: + ```pwsh + go mod tidy + ``` +2. Prepare images (reads `input/`, writes `output/`): + ```pwsh + go run prepare.go + ``` + This produces WebP 512×512 files and `output/sticker_config.json`. +3. Edit `output/sticker_config.json` to set `title`, `short_name` (must be unique), and emojis. +4. Upload the pack via the Go program: + ```pwsh + go run main.go + ``` -### Step 1: Prepare Images - -1. Create an `input` directory in the project root -2. Place your sticker images in the `input` directory (supported formats: PNG, JPG, JPEG) -3. Run the preparation script: -```bash -python prepare.py -``` - -This will: -- Convert all images to WebP format (512x512 pixels) -- Create an `output` directory with the processed images -- Generate a `sticker_config.json` file in the `output` directory - -4. Edit the `output/sticker_config.json` file to: - - Change the sticker pack title and short name - - Modify emojis for each sticker - - Add or remove stickers from the pack - -Example configuration: -```json -{ - "sticker_pack": { - "title": "My Awesome Stickers", - "short_name": "my_awesome_stickers", - "stickers": [ - { - "original": "sticker1.png", - "webp": "sticker1.webp", - "emoji": "😀" - }, - { - "original": "sticker2.png", - "webp": "sticker2.webp", - "emoji": "😎" - } - ] - } -} -``` - -### Step 2: Create Sticker Pack - -1. Run the main script to create and upload the sticker pack: -```bash -python main.py -``` - -2. The first time you run the script, you'll need to authenticate with your phone number -3. The script will create the sticker pack and upload all stickers according to the configuration - -## Important Notes - -- The `short_name` must be unique across all Telegram sticker packs -- Input images will be automatically resized to 512x512 pixels while maintaining aspect ratio -- Images are converted to WebP format with transparency support -- Make sure your input images are clear and of good quality -- The first time you run the script, you'll need to authenticate with your phone number - -## Troubleshooting - -Due to the nature of interacting with Telegram's Sticker bot, some issues may occur: - -1. **Manual Intervention Required**: Sometimes the script may fail to complete the process automatically. In such cases: - - The script will indicate that manual completion is needed - - You can continue the process manually by messaging @Stickers - - Follow the bot's prompts to complete the sticker pack creation - -2. **Common Issues**: - - Bot response delays: The script includes delays between actions, but sometimes the bot may need more time - - Network issues: Ensure you have a stable internet connection - - Rate limiting: If you create multiple packs, Telegram may temporarily limit your actions - -3. **If the Script Fails**: - - Check the error message for specific details - - You can start a new conversation with @Stickers and complete the process manually - - The bot will guide you through the remaining steps - -Remember that while the script automates most of the process, you may need to complete some steps manually if unexpected issues arise. +## Notes +- `short_name` must be unique across Telegram. +- If `@Stickers` asks for additional steps, follow its prompts to finish manually. diff --git a/bulk-telegram-sticker/go.mod b/bulk-telegram-sticker/go.mod new file mode 100644 index 0000000..86be5a9 --- /dev/null +++ b/bulk-telegram-sticker/go.mod @@ -0,0 +1,64 @@ +module bulk-telegram-sticker + +go 1.24.0 + +toolchain go1.24.10 + +require ( + github.com/celestix/gotgproto v1.0.0-beta22 + github.com/chai2010/webp v1.4.0 + github.com/disintegration/imaging v1.6.2 + github.com/gotd/td v0.132.0 + github.com/joho/godotenv v1.5.1 +) + +require ( + github.com/AnimeKaizoku/cacher v1.0.3-0.20250508132714-ddc7471efeef // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/coder/websocket v1.8.14 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/glebarez/go-sqlite v1.22.0 // indirect + github.com/glebarez/sqlite v1.11.0 // indirect + github.com/go-faster/errors v0.7.1 // indirect + github.com/go-faster/jx v1.1.0 // indirect + github.com/go-faster/xor v1.0.0 // indirect + github.com/go-faster/yaml v0.4.6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gotd/ige v0.2.2 // indirect + github.com/gotd/neo v0.1.5 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/ogen-go/ogen v1.15.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/segmentio/asm v1.2.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect + golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.46.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect + golang.org/x/tools v0.38.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gorm.io/gorm v1.25.12 // indirect + modernc.org/libc v1.61.8 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.8.2 // indirect + modernc.org/sqlite v1.34.4 // indirect + rsc.io/qr v0.2.0 // indirect +) diff --git a/bulk-telegram-sticker/go.sum b/bulk-telegram-sticker/go.sum new file mode 100644 index 0000000..51e06f7 --- /dev/null +++ b/bulk-telegram-sticker/go.sum @@ -0,0 +1,158 @@ +github.com/AnimeKaizoku/cacher v1.0.3-0.20250508132714-ddc7471efeef h1:y8llZexgesBazUg/zdIlrZKHPFJ2zk9YxlLAJ5x4DpQ= +github.com/AnimeKaizoku/cacher v1.0.3-0.20250508132714-ddc7471efeef/go.mod h1:jw0de/b0K6W7Y3T9rHCMGVKUf6oG7hENNcssxYcZTCc= +github.com/celestix/gotgproto v1.0.0-beta22 h1:Iu78cFA08nV8+flmxKs9CJ3W73+HG30fx0nLOs5A6fI= +github.com/celestix/gotgproto v1.0.0-beta22/go.mod h1:JYC9Js/5KLUhFR5M2RslQi2DFAcF7EdrgJMXo0YrzGQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/chai2010/webp v1.4.0 h1:6DA2pkkRUPnbOHvvsmGI3He1hBKf/bkRlniAiSGuEko= +github.com/chai2010/webp v1.4.0/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU= +github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= +github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= +github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= +github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= +github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= +github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= +github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= +github.com/go-faster/jx v1.1.0 h1:ZsW3wD+snOdmTDy9eIVgQdjUpXRRV4rqW8NS3t+20bg= +github.com/go-faster/jx v1.1.0/go.mod h1:vKDNikrKoyUmpzaJ0OkIkRQClNHFX/nF3dnTJZb3skg= +github.com/go-faster/xor v0.3.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ= +github.com/go-faster/xor v1.0.0 h1:2o8vTOgErSGHP3/7XwA5ib1FTtUsNtwCoLLBjl31X38= +github.com/go-faster/xor v1.0.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ= +github.com/go-faster/yaml v0.4.6 h1:lOK/EhI04gCpPgPhgt0bChS6bvw7G3WwI8xxVe0sw9I= +github.com/go-faster/yaml v0.4.6/go.mod h1:390dRIvV4zbnO7qC9FGo6YYutc+wyyUSHBgbXL52eXk= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gotd/ige v0.2.2 h1:XQ9dJZwBfDnOGSTxKXBGP4gMud3Qku2ekScRjDWWfEk= +github.com/gotd/ige v0.2.2/go.mod h1:tuCRb+Y5Y3eNTo3ypIfNpQ4MFjrnONiL2jN2AKZXmb0= +github.com/gotd/neo v0.1.5 h1:oj0iQfMbGClP8xI59x7fE/uHoTJD7NZH9oV1WNuPukQ= +github.com/gotd/neo v0.1.5/go.mod h1:9A2a4bn9zL6FADufBdt7tZt+WMhvZoc5gWXihOPoiBQ= +github.com/gotd/td v0.132.0 h1:Iqm3S2b+8kDgA9237IDXRxj7sryUpvy+4Cr50/0tpx4= +github.com/gotd/td v0.132.0/go.mod h1:4CDGYS+rDtOqotRheGaF9MS5g6jaUewvSXqBNJnx8SQ= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/ogen-go/ogen v1.15.2 h1:Hy5XNcDgWur758Kf0+DTQFN8cyBOs58EjDD3NMqih54= +github.com/ogen-go/ogen v1.15.2/go.mod h1:bS+BP2cV7+IGjOM24znBmh+PrpZvYFXA7o3BNF4Hj2E= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= +modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.23.12 h1:UF08a38c4B+K3VoGipBrVWLFUCHd8+X20QZtFAIlQNk= +modernc.org/ccgo/v4 v4.23.12/go.mod h1:vdN4h2WR5aEoNondUx26K7G8X+nuBscYnAEWSRmN2/0= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.6.1 h1:+Qf6xdG8l7B27TQ8D8lw/iFMUj1RXRBOuMUWziJOsk8= +modernc.org/gc/v2 v2.6.1/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/libc v1.61.8 h1:50KrjlFFoKq9ABh+bNVUf5SfVfQ4NY7CEyFBh65qc60= +modernc.org/libc v1.61.8/go.mod h1:XloulGc0yIRM+91kbwrp7jNi/mfYPAvDOD2qwzWEij0= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= +modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8= +modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= +nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= +rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= diff --git a/bulk-telegram-sticker/main.go b/bulk-telegram-sticker/main.go new file mode 100644 index 0000000..ab8823b --- /dev/null +++ b/bulk-telegram-sticker/main.go @@ -0,0 +1,214 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/celestix/gotgproto" + "github.com/celestix/gotgproto/sessionMaker" + "github.com/gotd/td/telegram/message" + "github.com/gotd/td/tg" + "github.com/joho/godotenv" +) + +type Sticker struct { + Original string `json:"original"` + Webp string `json:"webp"` + Emoji string `json:"emoji"` +} + +type Config struct { + Title string `json:"title"` + ShortName string `json:"short_name"` + Stickers []Sticker `json:"stickers"` +} + +func loadEnv() (string, string, string, error) { + if err := godotenv.Load(); err != nil { + return "", "", "", fmt.Errorf("load .env: %w", err) + } + + apiIDStr := os.Getenv("API_ID") + apiID, err := strconv.Atoi(apiIDStr) + if err != nil || apiID == 0 { + return "", "", "", fmt.Errorf("invalid API_ID: %s", apiIDStr) + } + + apiHash := strings.TrimSpace(os.Getenv("API_HASH")) + if apiHash == "" { + return "", "", "", fmt.Errorf("API_HASH not set") + } + + phone := strings.TrimSpace(os.Getenv("PHONE")) + if phone == "" { + return "", "", "", fmt.Errorf("PHONE not set") + } + + return apiHash, phone, fmt.Sprintf("%d", apiID), nil +} + +func loadConfig(configPath string) (*Config, error) { + data, err := os.ReadFile(configPath) + if err != nil { + return nil, fmt.Errorf("read config: %w", err) + } + + var config Config + if err := json.Unmarshal(data, &config); err != nil { + return nil, fmt.Errorf("unmarshal config: %w", err) + } + + if config.Title == "" || config.ShortName == "" || len(config.Stickers) == 0 { + return nil, fmt.Errorf("invalid config: missing title, short_name or stickers") + } + + return &config, nil +} + +func main() { + apiHash, phone, apiIDStr, err := loadEnv() + if err != nil { + fmt.Println(err) + return + } + + fmt.Printf("API_ID: %s\n", apiIDStr) + fmt.Printf("API_HASH: %s\n...\n", apiHash[:8]) + fmt.Printf("PHONE: %s\n", phone) + + client, err := gotgproto.NewClient( + 0, // appId will be ignored for phone auth + apiHash, + gotgproto.ClientTypePhone(phone), + &gotgproto.ClientOpts{ + Session: sessionMaker.SimpleSession(), + InMemory: true, + }, + ) + if err != nil { + fmt.Printf("Error creating client: %v\n", err) + return + } + defer client.Stop() + + ctx := client.CreateContext() + + configPath := filepath.Join("output", "sticker_config.json") + if _, err := os.Stat(configPath); os.IsNotExist(err) { + fmt.Println("Run 'go run prepare.go' first!") + return + } + + config, err := loadConfig(configPath) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println("Authenticating...") + if err := client.Idle(); err != nil { + fmt.Printf("Auth failed: %v\n", err) + return + } + + stickerBot, err := ctx.ResolveUsername("@Stickers") + if err != nil { + fmt.Printf("Resolve @Stickers: %v\n", err) + return + } + + fmt.Printf("Creating pack '%s' (%s)\n", config.Title, config.ShortName) + + // /newpack + _, err = ctx.SendMessage(stickerBot.GetID(), &tg.MessagesSendMessageRequest{Message: "/newpack"}) + if err != nil { + fmt.Printf("Send /newpack: %v\n", err) + return + } + time.Sleep(1 * time.Second) + + // Title + _, err = ctx.SendMessage(stickerBot.GetID(), &tg.MessagesSendMessageRequest{Message: config.Title}) + if err != nil { + fmt.Printf("Send title: %v\n", err) + return + } + time.Sleep(1 * time.Second) + + // Short name + _, err = ctx.SendMessage(stickerBot.GetID(), &tg.MessagesSendMessageRequest{Message: config.ShortName}) + if err != nil { + fmt.Printf("Send short_name: %v\n", err) + return + } + time.Sleep(1 * time.Second) + + // Username (short_name) + _, err = ctx.SendMessage(stickerBot.GetID(), &tg.MessagesSendMessageRequest{Message: config.ShortName}) + if err != nil { + fmt.Printf("Send username: %v\n", err) + return + } + time.Sleep(1 * time.Second) + + outputDir := "output" + for _, sticker := range config.Stickers { + stickerPath := filepath.Join(outputDir, sticker.Webp) + if _, err := os.Stat(stickerPath); os.IsNotExist(err) { + fmt.Printf("Warning: %s not found\n", stickerPath) + continue + } + + // Upload file + uploader := message.NewUploader(ctx.Raw) + inputFile, err := uploader.FromPath(ctx, stickerPath) + if err != nil { + fmt.Printf("Upload %s: %v\n", sticker.Webp, err) + continue + } + + media := &tg.InputMediaUploadedDocument{ + File: inputFile, + MimeType: "image/webp", + } + _, err = ctx.SendMedia(stickerBot.GetID(), &tg.MessagesSendMediaRequest{ + Media: media, + Message: sticker.Emoji, + }) + if err != nil { + fmt.Printf("Send sticker %s: %v\n", sticker.Webp, err) + continue + } + time.Sleep(2 * time.Second) + + // Send emoji again + time.Sleep(1 * time.Second) + _, err = ctx.SendMessage(stickerBot.GetID(), &tg.MessagesSendMessageRequest{Message: sticker.Emoji}) + if err != nil { + fmt.Printf("Send emoji for %s: %v\n", sticker.Webp, err) + } + time.Sleep(1 * time.Second) + } + + // /publish + _, err = ctx.SendMessage(stickerBot.GetID(), &tg.MessagesSendMessageRequest{Message: "/publish"}) + if err != nil { + fmt.Printf("Send /publish: %v\n", err) + return + } + time.Sleep(1 * time.Second) + + // Yes + _, err = ctx.SendMessage(stickerBot.GetID(), &tg.MessagesSendMessageRequest{Message: "Yes"}) + if err != nil { + fmt.Printf("Send Yes: %v\n", err) + return + } + + fmt.Printf("Sticker pack '%s' created successfully!\n", config.ShortName) +} diff --git a/bulk-telegram-sticker/main.py b/bulk-telegram-sticker/main.py deleted file mode 100644 index 4eaaed4..0000000 --- a/bulk-telegram-sticker/main.py +++ /dev/null @@ -1,156 +0,0 @@ -import os -import asyncio -import json -from telethon import TelegramClient, events -from telethon.tl.types import InputPeerUser -from telethon.tl.functions.messages import SendMessageRequest -from pathlib import Path -from dotenv import load_dotenv - -# Load environment variables from .env file -load_dotenv() - -# Telegram API credentials -API_ID = os.getenv('API_ID') -API_HASH = os.getenv('API_HASH') -PHONE = os.getenv('PHONE') - -# Sticker bot username -STICKER_BOT_USERNAME = "@Stickers" - -print(f"API_ID: {API_ID}") -print(f"API_HASH: {API_HASH}") -print(f"PHONE: {PHONE}") - -# Initialize the client -client = TelegramClient(PHONE, API_ID, API_HASH) - -def load_config(config_path): - """Load and validate configuration file""" - try: - with open(config_path, 'r', encoding='utf-8') as f: - config = json.load(f) - - # Validate required fields - if not all(key in config for key in ['title', 'short_name', 'stickers']): - raise ValueError("Missing required fields in configuration") - - return config - except Exception as e: - print(f"Error loading configuration: {str(e)}") - return None - -async def create_sticker_pack(config): - """ - Create a new sticker pack by interacting with the Sticker bot - - Args: - config (dict): Configuration dictionary containing sticker pack details - """ - try: - # Start the client - await client.start(phone=PHONE) - - # Get the sticker bot entity - sticker_bot = await client.get_entity(STICKER_BOT_USERNAME) - - # Start new sticker pack creation - await client(SendMessageRequest( - peer=sticker_bot, - message="/newpack" - )) - - # Wait for bot to ask for pack name - await asyncio.sleep(1) - - # Send pack name - await client(SendMessageRequest( - peer=sticker_bot, - message=config['title'] - )) - - # Wait for bot to ask for short name - await asyncio.sleep(1) - - # Send short name - await client(SendMessageRequest( - peer=sticker_bot, - message=config['short_name'] - )) - - # Wait for bot to ask for username - await asyncio.sleep(1) - - # Send username (using short name) - await client(SendMessageRequest( - peer=sticker_bot, - message=config['short_name'] - )) - - output_dir = "output" - - # Upload stickers one by one - for sticker in config['stickers']: - sticker_path = os.path.join(output_dir, sticker['webp']) - if not os.path.exists(sticker_path): - print(f"Warning: Sticker file not found: {sticker_path}") - continue - - # Send sticker file - await client.send_file( - sticker_bot, - sticker_path, - caption=sticker['emoji'] - ) - - # Wait for bot to process the sticker - await asyncio.sleep(2) - - # Skip any additional prompts - await asyncio.sleep(1) - await client(SendMessageRequest( - peer=sticker_bot, - message=sticker['emoji'] - )) - await asyncio.sleep(1) - - # Send /publish command to finalize the pack - await client(SendMessageRequest( - peer=sticker_bot, - message="/publish" - )) - - # Wait for bot to ask for confirmation - await asyncio.sleep(1) - - # Confirm publishing - await client(SendMessageRequest( - peer=sticker_bot, - message="Yes" - )) - - print(f"Sticker pack created successfully: {config['short_name']}") - return config['short_name'] - - except Exception as e: - print(f"Error creating sticker pack: {str(e)}") - print("Note: If the script fails, you may need to complete the process manually with the Sticker bot.") - return None - finally: - await client.disconnect() - -async def main(): - # Load configuration - config_path = os.path.join("output", "sticker_config.json") - if not os.path.exists(config_path): - print("Configuration file not found! Please run prepare.py first.") - return - - config = load_config(config_path) - if not config: - return - - await create_sticker_pack(config) - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/bulk-telegram-sticker/prepare.go b/bulk-telegram-sticker/prepare.go new file mode 100644 index 0000000..290aab0 --- /dev/null +++ b/bulk-telegram-sticker/prepare.go @@ -0,0 +1,137 @@ +package main + +import ( + "encoding/json" + "fmt" + "image" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/chai2010/webp" + "github.com/disintegration/imaging" +) + +type Sticker struct { + Original string `json:"original"` + Webp string `json:"webp"` + Emoji string `json:"emoji"` +} + +type Config struct { + Title string `json:"title"` + ShortName string `json:"short_name"` + Stickers []Sticker `json:"stickers"` +} + +func convertToWebp(inputPath, outputPath string, size image.Point) error { + srcImg, err := imaging.Open(inputPath) + if err != nil { + return fmt.Errorf("open %s: %w", inputPath, err) + } + + // Convert to RGBA + rgba := imaging.ToRGBA(srcImg) + + // Resize maintaining aspect ratio + resized := imaging.Fit(rgba, size.X, size.Y, imaging.Lanczos) + + // Pad to exact size with transparent background + dst := image.NewRGBA(size) + offset := imaging.Center(size.X, size.Y, resized.Bounds().Dx(), resized.Bounds().Dy()) + drawer := imaging.NewDrawer(dst) + drawer.Draw(offset.X, offset.Y, resized) + + // Save as WebP + opts := &webp.Options{Lossless: false, Quality: 90} + if err := webp.EncodeFile(outputPath, dst, opts); err != nil { + return fmt.Errorf("encode webp %s: %w", outputPath, err) + } + return nil +} + +func processImages(inputDir, outputDir string) ([]Sticker, error) { + if err := os.MkdirAll(outputDir, 0755); err != nil { + return nil, err + } + + var processed []Sticker + err := filepath.WalkDir(inputDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + ext := strings.ToLower(filepath.Ext(path)) + if ext != ".png" && ext != ".jpg" && ext != ".jpeg" { + return nil + } + + filename := d.Name() + nameWithoutExt := strings.TrimSuffix(filename, filepath.Ext(filename)) + webpPath := filepath.Join(outputDir, nameWithoutExt+".webp") + + if err := convertToWebp(path, webpPath, image.Pt(512, 512)); err != nil { + fmt.Printf("Error converting %s: %v\n", filename, err) + return nil + } + + processed = append(processed, Sticker{ + Original: filename, + Webp: nameWithoutExt + ".webp", + Emoji: "😀", // Default + }) + fmt.Printf("Processed %s -> %s\n", filename, nameWithoutExt+".webp") + return nil + }) + return processed, err +} + +func main() { + inputDir := "input" + outputDir := "output" + + if err := os.MkdirAll(inputDir, 0755); err != nil { + fmt.Printf("Error creating input dir: %v\n", err) + return + } + + fmt.Println("Processing images...") + stickers, err := processImages(inputDir, outputDir) + if err != nil { + fmt.Printf("Error processing images: %v\n", err) + return + } + if len(stickers) == 0 { + fmt.Println("No images found in input directory!") + return + } + + config := Config{ + Title: "My Awesome Stickers", + ShortName: "my_awesome_stickers", + Stickers: stickers, + } + + configPath := filepath.Join(outputDir, "sticker_config.json") + data, err := json.MarshalIndent(config, "", " ") + if err != nil { + fmt.Printf("Error marshaling config: %v\n", err) + return + } + + if err := os.WriteFile(configPath, data, 0644); err != nil { + fmt.Printf("Error writing config: %v\n", err) + return + } + + fmt.Printf("Processed %d images\n", len(stickers)) + fmt.Printf("Configuration generated: %s\n", configPath) + fmt.Println("\nEdit sticker_config.json to customize:") + fmt.Println("1. Change title and short_name") + fmt.Println("2. Set custom emojis for stickers") + fmt.Println("3. Add/remove stickers") + fmt.Println("\nThen run: go run main.go") +} diff --git a/bulk-telegram-sticker/prepare.py b/bulk-telegram-sticker/prepare.py deleted file mode 100644 index 12c2b4d..0000000 --- a/bulk-telegram-sticker/prepare.py +++ /dev/null @@ -1,97 +0,0 @@ -import os -import json -from PIL import Image -import glob -from pathlib import Path - -def convert_to_webp(input_path, output_path, size=(512, 512)): - """Convert image to WebP format with specified size""" - try: - with Image.open(input_path) as img: - # Convert to RGBA if necessary - if img.mode != 'RGBA': - img = img.convert('RGBA') - - # Resize image maintaining aspect ratio - img.thumbnail(size, Image.Resampling.LANCZOS) - - # Create a new image with padding if necessary - new_img = Image.new('RGBA', size, (0, 0, 0, 0)) - offset = ((size[0] - img.size[0]) // 2, (size[1] - img.size[1]) // 2) - new_img.paste(img, offset) - - # Save as WebP - new_img.save(output_path, 'WEBP', quality=90) - return True - except Exception as e: - print(f"Error converting {input_path}: {str(e)}") - return False - -def process_images(input_dir, output_dir): - """Process all images in input directory and convert to WebP""" - # Create output directory if it doesn't exist - Path(output_dir).mkdir(parents=True, exist_ok=True) - - # Supported input formats - input_formats = ['*.png', '*.jpg', '*.jpeg'] - processed_files = [] - - # Process each image - for format in input_formats: - for input_path in glob.glob(os.path.join(input_dir, format)): - filename = os.path.basename(input_path) - name_without_ext = os.path.splitext(filename)[0] - output_path = os.path.join(output_dir, f"{name_without_ext}.webp") - - if convert_to_webp(input_path, output_path): - processed_files.append({ - "original": filename, - "webp": f"{name_without_ext}.webp", - "emoji": "😀" # Default emoji - }) - - return processed_files - -def generate_config(processed_files, output_dir): - """Generate configuration file""" - config = { - "title": "My Awesome Stickers", - "short_name": "my_awesome_stickers", - "stickers": processed_files - } - - # Save config file - config_path = os.path.join(output_dir, "sticker_config.json") - with open(config_path, 'w', encoding='utf-8') as f: - json.dump(config, f, indent=4, ensure_ascii=False) - - return config_path - -def main(): - # Setup directories - input_dir = "input" - output_dir = "output" - - # Create input directory if it doesn't exist - Path(input_dir).mkdir(parents=True, exist_ok=True) - - print("Processing images...") - processed_files = process_images(input_dir, output_dir) - - if not processed_files: - print("No images found in input directory!") - return - - print(f"Processed {len(processed_files)} images") - - # Generate configuration file - config_path = generate_config(processed_files, output_dir) - print(f"\nConfiguration file generated: {config_path}") - print("\nYou can now edit the configuration file to:") - print("1. Change the sticker pack title and short name") - print("2. Modify emojis for each sticker") - print("3. Add or remove stickers from the pack") - print("\nAfter editing, run main.py to create the sticker pack") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/bulk-telegram-sticker/requirements.txt b/bulk-telegram-sticker/requirements.txt deleted file mode 100644 index 456d6ff..0000000 --- a/bulk-telegram-sticker/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -telethon>=1.28.5 -python-dotenv>=0.19.0 -Pillow>=10.0.0 \ No newline at end of file