feat(go): rewrite in go

This commit is contained in:
tiennm99 committed 2025-11-30 20:52:54 +07:00
1 parent 947178086d
commit 8fcc74f6eb
9 files changed
+630 -359

No files matched your search

+31 -2
View File
@@ -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
# Editor/IDE
# .idea/
# .vscode/
+26 -101
View File
@@ -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.
+64
View File
@@ -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
)
+158
View File
@@ -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=
+214
View File
@@ -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)
}
-156
View File
@@ -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())
+137
View File
@@ -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")
}
-97
View File
@@ -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()
-3
View File
@@ -1,3 +0,0 @@
telethon>=1.28.5
python-dotenv>=0.19.0
Pillow>=10.0.0