mirror of
https://github.com/tiennm99/MTTools.git
synced 2026-10-05 05:01:19 +00:00
feat(go): rewrite in go
This commit is contained in:
1 parent
947178086d
commit
8fcc74f6eb
9 files changed
+630
-359
No files matched your search
@@ -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
@@ -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.
|
||||
@@ -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
|
||||
)
|
||||
@@ -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=
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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())
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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()
|
||||
@@ -1,3 +0,0 @@
|
||||
telethon>=1.28.5
|
||||
python-dotenv>=0.19.0
|
||||
Pillow>=10.0.0
|
||||
Reference in new issue
Block a user