From 52ee8efa9e1c86509938fb413d60af2102c5e67c Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Wed, 30 Sep 2026 20:04:13 +0700 Subject: [PATCH] Revert "Merge branch 'feature/bulk-telegram-sticker-go' into main" Keeps the branch in history only; main's content is unchanged. --- bulk-telegram-sticker/.gitignore | 35 +--- bulk-telegram-sticker/README.md | 127 ++++++++++++--- bulk-telegram-sticker/go.mod | 65 -------- bulk-telegram-sticker/go.sum | 164 ------------------- bulk-telegram-sticker/main.go | 214 ------------------------- bulk-telegram-sticker/main.py | 156 ++++++++++++++++++ bulk-telegram-sticker/prepare.go | 137 ---------------- bulk-telegram-sticker/prepare.py | 97 +++++++++++ bulk-telegram-sticker/requirements.txt | 3 + 9 files changed, 358 insertions(+), 640 deletions(-) delete mode 100644 bulk-telegram-sticker/go.mod delete mode 100644 bulk-telegram-sticker/go.sum delete mode 100644 bulk-telegram-sticker/main.go create mode 100644 bulk-telegram-sticker/main.py delete mode 100644 bulk-telegram-sticker/prepare.go create mode 100644 bulk-telegram-sticker/prepare.py create mode 100644 bulk-telegram-sticker/requirements.txt diff --git a/bulk-telegram-sticker/.gitignore b/bulk-telegram-sticker/.gitignore index 089a213..f9736b0 100644 --- a/bulk-telegram-sticker/.gitignore +++ b/bulk-telegram-sticker/.gitignore @@ -1,36 +1,3 @@ .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 - -# Editor/IDE -# .idea/ -# .vscode/ +*.session \ No newline at end of file diff --git a/bulk-telegram-sticker/README.md b/bulk-telegram-sticker/README.md index f3f96af..170a28a 100644 --- a/bulk-telegram-sticker/README.md +++ b/bulk-telegram-sticker/README.md @@ -1,33 +1,108 @@ -# Bulk Telegram Sticker Pack Creator (Go) +# Bulk Telegram Sticker Pack Creator -Small, Go-based tool to prepare images and upload a Telegram sticker pack. +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 -## The Original Python Version +## Setup -*In 2025, I rewrote this project using Go. The original Python version of this project can be found at the `feature/python` branch.* +1. Install the required dependencies: +```bash +pip install -r requirements.txt +``` -**Note: This Go version isn't runnable, check Python version if you really want to use. Telegram Stickers now have app with convenient features, so maybe you don't need these scripts anymore.** +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` -## Prerequisites -- Go 1.24+ installed -- Create a `.env` in the project root with `API_ID`, `API_HASH`, and `PHONE` (see `.env.example`). +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 +``` -## 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 - ``` +## Usage -## Notes -- `short_name` must be unique across Telegram. -- If `@Stickers` asks for additional steps, follow its prompts to finish manually. +### 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. diff --git a/bulk-telegram-sticker/go.mod b/bulk-telegram-sticker/go.mod deleted file mode 100644 index f5681ac..0000000 --- a/bulk-telegram-sticker/go.mod +++ /dev/null @@ -1,65 +0,0 @@ -module github.com/tiennm99/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.134.0 - github.com/joho/godotenv v1.5.1 -) - -require ( - github.com/AnimeKaizoku/cacher v1.0.3 // 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.2.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.1 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/ncruces/go-strftime v1.0.0 // indirect - github.com/ogen-go/ogen v1.17.0 // 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.1 // indirect - github.com/shopspring/decimal v1.4.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.1 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect - golang.org/x/image v0.33.0 // indirect - golang.org/x/mod v0.30.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect - golang.org/x/tools v0.39.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gorm.io/gorm v1.31.1 // indirect - modernc.org/libc v1.67.1 // indirect - modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.11.0 // indirect - modernc.org/sqlite v1.40.1 // indirect - rsc.io/qr v0.2.0 // indirect -) diff --git a/bulk-telegram-sticker/go.sum b/bulk-telegram-sticker/go.sum deleted file mode 100644 index afc8a22..0000000 --- a/bulk-telegram-sticker/go.sum +++ /dev/null @@ -1,164 +0,0 @@ -github.com/AnimeKaizoku/cacher v1.0.3 h1:foNAmLfY/DXfA4yEy4uP6WK2Ni7JC+s3QhZv72Dn6zs= -github.com/AnimeKaizoku/cacher v1.0.3/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.2.0 h1:T2YHJPrFaYu21fJtUxC9GzmluKu8rVIFDwwGBKTDseI= -github.com/go-faster/jx v1.2.0/go.mod h1:UWLOVDmMG597a5tBFPLIWJdUxz5/2emOpfsj9Neg0PE= -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-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= -github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= -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.134.0 h1:ObbMjb2x3FhcOqKcCyWz4LvrPAgSFNHG+HUXS6KnyYQ= -github.com/gotd/td v0.134.0/go.mod h1:XKti40wWKgtKMcTye5VuVEHCWf6EECyDPcG8WP1fmgw= -github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -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.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= -github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= -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 v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= -github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/ogen-go/ogen v1.17.0 h1:Vc69BgL6rfsS+4r2gskmn1/N4Ca9Ta4TzoimCtc2M/4= -github.com/ogen-go/ogen v1.17.0/go.mod h1:dHFr2Wf6cA7tSxMI+zPC21UR5hAlDw8ZYUkK3PziURY= -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.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= -github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -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.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= -golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= -golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= -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.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= -gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= -modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= -modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= -modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM= -modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA= -modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= -modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= -modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE= -modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= -modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= -modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= -modernc.org/libc v1.67.1 h1:bFaqOaa5/zbWYJo8aW0tXPX21hXsngG2M7mckCnFSVk= -modernc.org/libc v1.67.1/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA= -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.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= -modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -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.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY= -modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= -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 deleted file mode 100644 index ab8823b..0000000 --- a/bulk-telegram-sticker/main.go +++ /dev/null @@ -1,214 +0,0 @@ -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 new file mode 100644 index 0000000..4eaaed4 --- /dev/null +++ b/bulk-telegram-sticker/main.py @@ -0,0 +1,156 @@ +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 deleted file mode 100644 index 290aab0..0000000 --- a/bulk-telegram-sticker/prepare.go +++ /dev/null @@ -1,137 +0,0 @@ -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 new file mode 100644 index 0000000..12c2b4d --- /dev/null +++ b/bulk-telegram-sticker/prepare.py @@ -0,0 +1,97 @@ +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 new file mode 100644 index 0000000..456d6ff --- /dev/null +++ b/bulk-telegram-sticker/requirements.txt @@ -0,0 +1,3 @@ +telethon>=1.28.5 +python-dotenv>=0.19.0 +Pillow>=10.0.0 \ No newline at end of file