telegram-badge 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,134 +1,214 @@
1
1
  # 🛡️ Telegram Group Badge Generator
2
2
 
3
+ [🇷🇺 Русский](README.ru.md) | [🇺🇸 English](README.md) | [🇨🇳 中文](README.zh.md)
4
+
3
5
  [![Build Status](https://github.com/chatman-media/telegram-badge/workflows/CI/badge.svg)](https://github.com/chatman-media/telegram-badge/actions)
4
6
  [![npm version](https://badge.fury.io/js/telegram-badge.svg)](https://badge.fury.io/js/telegram-badge)
5
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.5-blue.svg)](https://www.typescriptlang.org/)
6
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
9
 
8
- Этот проект генерирует SVG-бейдж с текущим количеством участников вашей Telegram-группы. Идеально подходит для отображения активности сообщества в README на GitHub или на сайте.
10
+ This project generates SVG badges with the current member count of your Telegram group. Perfect for displaying community activity in GitHub README files or on websites.
9
11
 
10
- ## 🚀 Демо
12
+ ## 🚀 Demo
11
13
 
12
14
  ![Telegram Group Members](https://telegram-badge.vercel.app/api/telegram-badge)
13
15
 
14
16
  ---
15
17
 
16
- ## 📦 Стек
18
+ ## 📦 Tech Stack
17
19
 
18
- - Node.js / Bun
20
+ - Node.js / TypeScript
19
21
  - Telegram Bot API
20
22
  - Vercel (Serverless API)
23
+ - Jest for testing
21
24
 
22
25
  ---
23
26
 
24
- ## 🛠 Установка
27
+ ## 🛠 Installation
25
28
 
26
- 1. Клонируйте репозиторий:
29
+ 1. Clone the repository:
27
30
 
28
31
  ```bash
29
32
  git clone https://github.com/chatman-media/telegram-badge.git
30
33
  cd telegram-badge
31
34
  ```
32
35
 
33
- 2. Установите зависимости:
36
+ 2. Install dependencies:
34
37
 
35
38
  ```bash
36
39
  npm install
37
- # или
40
+ # or
38
41
  bun install
39
42
  ```
40
43
 
41
- 3. Создайте .env файл и добавьте:
44
+ 3. Create a .env file and add:
42
45
 
43
46
  ```bash
44
47
  BOT_TOKEN=your_telegram_bot_token
45
48
  CHAT_ID=@your_group_username_or_chat_id
46
49
  ```
47
50
 
48
- Убедитесь, что бот добавлен в группу как админ.
51
+ **Note:** For public groups/channels, the bot doesn't need to be added as a member. For private groups, the bot must be added to the group.
49
52
 
50
- ## 🧪 Локальный запуск
53
+ ## 🧪 Local Development
51
54
 
52
55
  ```bash
53
56
  npm run dev
54
- # или
57
+ # or
55
58
  bun dev
56
59
  ```
57
60
 
58
- Открой в браузере: http://localhost:3000/api/telegram-badge
61
+ Open in browser: http://localhost:3000/api/telegram-badge
62
+
63
+ ## ☁️ Deploy to Vercel
59
64
 
60
- ## ☁️ Деплой на Vercel
61
- 1. Задеплойте репозиторий на vercel.com
62
- 2. В настройках проекта добавьте переменные окружения:
63
- • BOT_TOKEN
64
- • CHAT_ID
65
+ 1. Deploy the repository to vercel.com
66
+ 2. Add environment variables in project settings:
67
+ - BOT_TOKEN
68
+ - CHAT_ID
65
69
 
66
- ## 🧩 Использование в GitHub README
70
+ ## 🧩 Usage in GitHub README
67
71
 
68
- Добавьте следующую строку в ваш README.md:
72
+ Add the following line to your README.md:
69
73
 
70
74
  ```markdown
71
75
  ![Telegram Group Badge](https://telegram-badge.vercel.app/api/telegram-badge)
72
76
  ```
73
77
 
74
- ### 🎨 Параметры стилизации
78
+ ### 🎨 Styling Parameters
75
79
 
76
- Вы можете настроить внешний вид бейджа с помощью следующих параметров:
80
+ You can customize the badge appearance using the following parameters:
77
81
 
78
- | Параметр | Описание | Значение по умолчанию |
79
- |----------|----------|------------------------|
80
- | `style` | Стиль бейджа | `flat` |
81
- | `label` | Текст метки | `Telegram` |
82
- | `color` | Цвет основной части бейджа | `2AABEE` (цвет Telegram) |
83
- | `labelColor` | Цвет метки бейджа | `555555` |
82
+ | Parameter | Description | Default Value |
83
+ |-----------|-------------|---------------|
84
+ | `style` | Badge style | `flat` |
85
+ | `label` | Label text | `Telegram` |
86
+ | `color` | Main badge color | `2AABEE` (Telegram color) |
87
+ | `labelColor` | Label color | `555555` |
88
+ | `logo` | Show Telegram logo | `true` |
84
89
 
85
- #### Доступные стили:
90
+ #### Available styles:
86
91
 
87
- - `flat` - плоский стиль (по умолчанию)
88
- - `plastic` - объемный стиль
89
- - `flat-square` - плоский квадратный стиль
90
- - `for-the-badge` - широкий стиль с заглавными буквами
91
- - `social` - социальный стиль
92
+ - `flat` - flat style (default)
93
+ - `plastic` - plastic style with gradient
94
+ - `flat-square` - flat square style without rounded corners
95
+ - `for-the-badge` - wide style with uppercase letters
96
+ - `social` - GitHub social style
92
97
 
93
- #### Примеры:
98
+ #### Examples:
94
99
 
95
- Стандартный бейдж:
100
+ Standard badge (flat style):
96
101
  ```
97
102
  https://telegram-badge.vercel.app/api/telegram-badge
98
103
  ```
104
+ ![Flat](https://telegram-badge.vercel.app/api/telegram-badge)
99
105
 
100
- Бейдж с кастомной меткой:
106
+ Badge with plastic style:
101
107
  ```
102
- https://telegram-badge.vercel.app/api/telegram-badge?label=Our%20Group
108
+ https://telegram-badge.vercel.app/api/telegram-badge?style=plastic
103
109
  ```
110
+ ![Plastic](https://telegram-badge.vercel.app/api/telegram-badge?style=plastic)
104
111
 
105
- Бейдж с кастомным цветом:
112
+ Badge with flat-square style:
106
113
  ```
107
- https://telegram-badge.vercel.app/api/telegram-badge?color=FF0000
114
+ https://telegram-badge.vercel.app/api/telegram-badge?style=flat-square
108
115
  ```
116
+ ![Flat-Square](https://telegram-badge.vercel.app/api/telegram-badge?style=flat-square)
109
117
 
110
- Бейдж с кастомным стилем:
118
+ Badge with for-the-badge style:
111
119
  ```
112
120
  https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge
113
121
  ```
122
+ ![For-The-Badge](https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge)
123
+
124
+ Badge with social style:
125
+ ```
126
+ https://telegram-badge.vercel.app/api/telegram-badge?style=social
127
+ ```
128
+ ![Social](https://telegram-badge.vercel.app/api/telegram-badge?style=social)
129
+
130
+ Badge with custom label and color:
131
+ ```
132
+ https://telegram-badge.vercel.app/api/telegram-badge?label=Join%20Chat&color=00FF00
133
+ ```
134
+ ![Custom](https://telegram-badge.vercel.app/api/telegram-badge?label=Join%20Chat&color=00FF00)
135
+
136
+ Fully customized badge:
137
+ ```
138
+ https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge&label=Community&color=FF5733&labelColor=1A1A1A
139
+ ```
140
+ ![Full Custom](https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge&label=Community&color=FF5733&labelColor=1A1A1A)
114
141
 
115
- Полностью кастомизированный бейдж:
142
+ Badge without logo:
143
+ ```
144
+ https://telegram-badge.vercel.app/api/telegram-badge?logo=false
116
145
  ```
117
- https://telegram-badge.vercel.app/api/telegram-badge?style=social&label=Join%20Us&color=FF5733&labelColor=333333
146
+ ![No Logo](https://telegram-badge.vercel.app/api/telegram-badge?logo=false)
147
+
148
+ ## ✨ Features
149
+
150
+ - 👥 Real-time member count display
151
+ - 🎨 Full badge appearance customization
152
+ - 🔒 Support for .env and Vercel environment variables for secure token storage
153
+ - ⚡ Optimized caching for fast loading
154
+ - 🛡️ Error handling with informative messages
155
+ - 🆓 Free on Vercel with normal usage
156
+ - 📡 Can be extended to show activity/message count
157
+ - 🧪 Comprehensive test suite with TypeScript
158
+
159
+ ## 🔧 API Usage
160
+
161
+ ### As npm package:
162
+
163
+ ```bash
164
+ npm install telegram-badge
118
165
  ```
119
166
 
120
- ## 🧠 Возможности
167
+ ```typescript
168
+ import badgeHandler from 'telegram-badge';
121
169
 
122
- - 👥 Отображение количества участников в реальном времени
123
- - 🎨 Полная кастомизация внешнего вида бейджа
124
- - 🔒 Поддержка .env и переменных Vercel для безопасного хранения токенов
125
- - ⚡ Оптимизированное кэширование для быстрой загрузки
126
- - 🛡️ Обработка ошибок с информативными сообщениями
127
- - 🆓 Бесплатно на Vercel при обычной нагрузке
128
- - 📡 Можно расширить до отображения активности / количества сообщений
170
+ // Use in your serverless function
171
+ export default badgeHandler;
172
+ ```
129
173
 
130
-
174
+ ### Direct API calls:
131
175
 
132
- 📜 Лицензия
176
+ ```typescript
177
+ GET /api/telegram-badge?style=flat&label=Members&color=2AABEE&labelColor=555555
178
+ ```
179
+
180
+ ## 🧪 Testing
181
+
182
+ Run the test suite:
183
+
184
+ ```bash
185
+ npm test
186
+ ```
187
+
188
+ Run type checking:
189
+
190
+ ```bash
191
+ npm run type-check
192
+ ```
193
+
194
+ Build the project:
195
+
196
+ ```bash
197
+ npm run build
198
+ ```
199
+
200
+ ## 🤝 Contributing
201
+
202
+ 1. Fork the repository
203
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
204
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
205
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
206
+ 5. Open a Pull Request
207
+
208
+ ## 📜 License
209
+
210
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
211
+
212
+ ---
133
213
 
134
- MIT
214
+ Made with ❤️ by [Chatman Media](https://github.com/chatman-media)
package/README.ru.md ADDED
@@ -0,0 +1,160 @@
1
+ # 🛡️ Telegram Group Badge Generator
2
+
3
+ [🇷🇺 Русский](README.ru.md) | [🇺🇸 English](README.md) | [🇨🇳 中文](README.zh.md)
4
+
5
+ [![Build Status](https://github.com/chatman-media/telegram-badge/workflows/CI/badge.svg)](https://github.com/chatman-media/telegram-badge/actions)
6
+ [![npm version](https://badge.fury.io/js/telegram-badge.svg)](https://badge.fury.io/js/telegram-badge)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.5-blue.svg)](https://www.typescriptlang.org/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ Этот проект генерирует SVG-бейдж с текущим количеством участников вашей Telegram-группы. Идеально подходит для отображения активности сообщества в README на GitHub или на сайте.
11
+
12
+ ## 🚀 Демо
13
+
14
+ ![Telegram Group Members](https://telegram-badge.vercel.app/api/telegram-badge)
15
+
16
+ ---
17
+
18
+ ## 📦 Стек
19
+
20
+ - Node.js / Bun
21
+ - Telegram Bot API
22
+ - Vercel (Serverless API)
23
+
24
+ ---
25
+
26
+ ## 🛠 Установка
27
+
28
+ 1. Клонируйте репозиторий:
29
+
30
+ ```bash
31
+ git clone https://github.com/chatman-media/telegram-badge.git
32
+ cd telegram-badge
33
+ ```
34
+
35
+ 2. Установите зависимости:
36
+
37
+ ```bash
38
+ npm install
39
+ # или
40
+ bun install
41
+ ```
42
+
43
+ 3. Создайте .env файл и добавьте:
44
+
45
+ ```bash
46
+ BOT_TOKEN=your_telegram_bot_token
47
+ CHAT_ID=@your_group_username_or_chat_id
48
+ ```
49
+
50
+ **Примечание:** Для публичных групп/каналов бота не нужно добавлять в группу. Для приватных групп бот должен быть участником.
51
+
52
+ ## 🧪 Локальный запуск
53
+
54
+ ```bash
55
+ npm run dev
56
+ # или
57
+ bun dev
58
+ ```
59
+
60
+ Открой в браузере: http://localhost:3000/api/telegram-badge
61
+
62
+ ## ☁️ Деплой на Vercel
63
+ 1. Задеплойте репозиторий на vercel.com
64
+ 2. В настройках проекта добавьте переменные окружения:
65
+ • BOT_TOKEN
66
+ • CHAT_ID
67
+
68
+ ## 🧩 Использование в GitHub README
69
+
70
+ Добавьте следующую строку в ваш README.md:
71
+
72
+ ```markdown
73
+ ![Telegram Group Badge](https://telegram-badge.vercel.app/api/telegram-badge)
74
+ ```
75
+
76
+ ### 🎨 Параметры стилизации
77
+
78
+ Вы можете настроить внешний вид бейджа с помощью следующих параметров:
79
+
80
+ | Параметр | Описание | Значение по умолчанию |
81
+ |----------|----------|------------------------|
82
+ | `style` | Стиль бейджа | `flat` |
83
+ | `label` | Текст метки | `Telegram` |
84
+ | `color` | Цвет основной части бейджа | `2AABEE` (цвет Telegram) |
85
+ | `labelColor` | Цвет метки бейджа | `555555` |
86
+ | `logo` | Показывать логотип Telegram | `true` |
87
+
88
+ #### Доступные стили:
89
+
90
+ - `flat` - плоский стиль (по умолчанию)
91
+ - `plastic` - объемный стиль с градиентом
92
+ - `flat-square` - плоский стиль без закруглений
93
+ - `for-the-badge` - широкий стиль с заглавными буквами
94
+ - `social` - стиль как у GitHub
95
+
96
+ #### Примеры:
97
+
98
+ Стандартный бейдж (стиль flat):
99
+ ```
100
+ https://telegram-badge.vercel.app/api/telegram-badge
101
+ ```
102
+ ![Flat](https://telegram-badge.vercel.app/api/telegram-badge)
103
+
104
+ Бейдж со стилем plastic:
105
+ ```
106
+ https://telegram-badge.vercel.app/api/telegram-badge?style=plastic
107
+ ```
108
+ ![Plastic](https://telegram-badge.vercel.app/api/telegram-badge?style=plastic)
109
+
110
+ Бейдж со стилем flat-square:
111
+ ```
112
+ https://telegram-badge.vercel.app/api/telegram-badge?style=flat-square
113
+ ```
114
+ ![Flat-Square](https://telegram-badge.vercel.app/api/telegram-badge?style=flat-square)
115
+
116
+ Бейдж со стилем for-the-badge:
117
+ ```
118
+ https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge
119
+ ```
120
+ ![For-The-Badge](https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge)
121
+
122
+ Бейдж со стилем social:
123
+ ```
124
+ https://telegram-badge.vercel.app/api/telegram-badge?style=social
125
+ ```
126
+ ![Social](https://telegram-badge.vercel.app/api/telegram-badge?style=social)
127
+
128
+ Бейдж с кастомной меткой и цветом:
129
+ ```
130
+ https://telegram-badge.vercel.app/api/telegram-badge?label=Наш%20Чат&color=00FF00
131
+ ```
132
+ ![Custom](https://telegram-badge.vercel.app/api/telegram-badge?label=Наш%20Чат&color=00FF00)
133
+
134
+ Полностью кастомизированный бейдж:
135
+ ```
136
+ https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge&label=Сообщество&color=FF5733&labelColor=1A1A1A
137
+ ```
138
+ ![Full Custom](https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge&label=Сообщество&color=FF5733&labelColor=1A1A1A)
139
+
140
+ Бейдж без логотипа:
141
+ ```
142
+ https://telegram-badge.vercel.app/api/telegram-badge?logo=false
143
+ ```
144
+ ![No Logo](https://telegram-badge.vercel.app/api/telegram-badge?logo=false)
145
+
146
+ ## 🧠 Возможности
147
+
148
+ - 👥 Отображение количества участников в реальном времени
149
+ - 🎨 Полная кастомизация внешнего вида бейджа
150
+ - 🔒 Поддержка .env и переменных Vercel для безопасного хранения токенов
151
+ - ⚡ Оптимизированное кэширование для быстрой загрузки
152
+ - 🛡️ Обработка ошибок с информативными сообщениями
153
+ - 🆓 Бесплатно на Vercel при обычной нагрузке
154
+ - 📡 Можно расширить до отображения активности / количества сообщений
155
+
156
+
157
+
158
+ 📜 Лицензия
159
+
160
+ MIT
package/README.zh.md ADDED
@@ -0,0 +1,214 @@
1
+ # 🛡️ Telegram 群组徽章生成器
2
+
3
+ [🇷🇺 Русский](README.ru.md) | [🇺🇸 English](README.md) | [🇨🇳 中文](README.zh.md)
4
+
5
+ [![构建状态](https://github.com/chatman-media/telegram-badge/workflows/CI/badge.svg)](https://github.com/chatman-media/telegram-badge/actions)
6
+ [![npm 版本](https://badge.fury.io/js/telegram-badge.svg)](https://badge.fury.io/js/telegram-badge)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.5-blue.svg)](https://www.typescriptlang.org/)
8
+ [![许可证: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ 本项目生成显示 Telegram 群组当前成员数量的 SVG 徽章。非常适合在 GitHub README 文件或网站上展示社区活跃度。
11
+
12
+ ## 🚀 演示
13
+
14
+ ![Telegram 群组成员](https://telegram-badge.vercel.app/api/telegram-badge)
15
+
16
+ ---
17
+
18
+ ## 📦 技术栈
19
+
20
+ - Node.js / TypeScript
21
+ - Telegram Bot API
22
+ - Vercel(无服务器 API)
23
+ - Jest 测试框架
24
+
25
+ ---
26
+
27
+ ## 🛠 安装
28
+
29
+ 1. 克隆仓库:
30
+
31
+ ```bash
32
+ git clone https://github.com/chatman-media/telegram-badge.git
33
+ cd telegram-badge
34
+ ```
35
+
36
+ 2. 安装依赖:
37
+
38
+ ```bash
39
+ npm install
40
+ # 或
41
+ bun install
42
+ ```
43
+
44
+ 3. 创建 .env 文件并添加:
45
+
46
+ ```bash
47
+ BOT_TOKEN=your_telegram_bot_token
48
+ CHAT_ID=@your_group_username_or_chat_id
49
+ ```
50
+
51
+ **注意:** 对于公开群组/频道,机器人无需添加为成员。对于私有群组,机器人必须是群组成员。
52
+
53
+ ## 🧪 本地开发
54
+
55
+ ```bash
56
+ npm run dev
57
+ # 或
58
+ bun dev
59
+ ```
60
+
61
+ 在浏览器中打开:http://localhost:3000/api/telegram-badge
62
+
63
+ ## ☁️ 部署到 Vercel
64
+
65
+ 1. 将仓库部署到 vercel.com
66
+ 2. 在项目设置中添加环境变量:
67
+ - BOT_TOKEN
68
+ - CHAT_ID
69
+
70
+ ## 🧩 在 GitHub README 中使用
71
+
72
+ 在您的 README.md 中添加以下代码:
73
+
74
+ ```markdown
75
+ ![Telegram 群组徽章](https://telegram-badge.vercel.app/api/telegram-badge)
76
+ ```
77
+
78
+ ### 🎨 样式参数
79
+
80
+ 您可以使用以下参数自定义徽章外观:
81
+
82
+ | 参数 | 描述 | 默认值 |
83
+ |------|------|--------|
84
+ | `style` | 徽章样式 | `flat` |
85
+ | `label` | 标签文本 | `Telegram` |
86
+ | `color` | 主徽章颜色 | `2AABEE`(Telegram 颜色) |
87
+ | `labelColor` | 标签颜色 | `555555` |
88
+ | `logo` | 显示 Telegram 标志 | `true` |
89
+
90
+ #### 可用样式:
91
+
92
+ - `flat` - 扁平样式(默认)
93
+ - `plastic` - 带渐变的立体样式
94
+ - `flat-square` - 无圆角的扁平样式
95
+ - `for-the-badge` - 带大写字母的宽样式
96
+ - `social` - GitHub 社交样式
97
+
98
+ #### 示例:
99
+
100
+ 标准徽章(扁平样式):
101
+ ```
102
+ https://telegram-badge.vercel.app/api/telegram-badge
103
+ ```
104
+ ![Flat](https://telegram-badge.vercel.app/api/telegram-badge)
105
+
106
+ 立体样式徽章:
107
+ ```
108
+ https://telegram-badge.vercel.app/api/telegram-badge?style=plastic
109
+ ```
110
+ ![Plastic](https://telegram-badge.vercel.app/api/telegram-badge?style=plastic)
111
+
112
+ 方形扁平样式徽章:
113
+ ```
114
+ https://telegram-badge.vercel.app/api/telegram-badge?style=flat-square
115
+ ```
116
+ ![Flat-Square](https://telegram-badge.vercel.app/api/telegram-badge?style=flat-square)
117
+
118
+ for-the-badge 样式徽章:
119
+ ```
120
+ https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge
121
+ ```
122
+ ![For-The-Badge](https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge)
123
+
124
+ 社交样式徽章:
125
+ ```
126
+ https://telegram-badge.vercel.app/api/telegram-badge?style=social
127
+ ```
128
+ ![Social](https://telegram-badge.vercel.app/api/telegram-badge?style=social)
129
+
130
+ 自定义标签和颜色的徽章:
131
+ ```
132
+ https://telegram-badge.vercel.app/api/telegram-badge?label=加入聊天&color=00FF00
133
+ ```
134
+ ![Custom](https://telegram-badge.vercel.app/api/telegram-badge?label=加入聊天&color=00FF00)
135
+
136
+ 完全自定义徽章:
137
+ ```
138
+ https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge&label=社区&color=FF5733&labelColor=1A1A1A
139
+ ```
140
+ ![Full Custom](https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge&label=社区&color=FF5733&labelColor=1A1A1A)
141
+
142
+ 无标志徽章:
143
+ ```
144
+ https://telegram-badge.vercel.app/api/telegram-badge?logo=false
145
+ ```
146
+ ![No Logo](https://telegram-badge.vercel.app/api/telegram-badge?logo=false)
147
+
148
+ ## ✨ 功能特性
149
+
150
+ - 👥 实时显示成员数量
151
+ - 🎨 完全自定义徽章外观
152
+ - 🔒 支持 .env 和 Vercel 环境变量以安全存储令牌
153
+ - ⚡ 优化缓存以实现快速加载
154
+ - 🛡️ 错误处理和信息提示
155
+ - 🆓 在 Vercel 上正常使用免费
156
+ - 📡 可扩展显示活动/消息计数
157
+ - 🧪 使用 TypeScript 的完整测试套件
158
+
159
+ ## 🔧 API 使用
160
+
161
+ ### 作为 npm 包:
162
+
163
+ ```bash
164
+ npm install telegram-badge
165
+ ```
166
+
167
+ ```typescript
168
+ import badgeHandler from 'telegram-badge';
169
+
170
+ // 在您的无服务器函数中使用
171
+ export default badgeHandler;
172
+ ```
173
+
174
+ ### 直接 API 调用:
175
+
176
+ ```typescript
177
+ GET /api/telegram-badge?style=flat&label=成员&color=2AABEE&labelColor=555555
178
+ ```
179
+
180
+ ## 🧪 测试
181
+
182
+ 运行测试套件:
183
+
184
+ ```bash
185
+ npm test
186
+ ```
187
+
188
+ 运行类型检查:
189
+
190
+ ```bash
191
+ npm run type-check
192
+ ```
193
+
194
+ 构建项目:
195
+
196
+ ```bash
197
+ npm run build
198
+ ```
199
+
200
+ ## 🤝 贡献
201
+
202
+ 1. Fork 本仓库
203
+ 2. 创建您的功能分支(`git checkout -b feature/amazing-feature`)
204
+ 3. 提交您的更改(`git commit -m 'Add some amazing feature'`)
205
+ 4. 推送到分支(`git push origin feature/amazing-feature`)
206
+ 5. 打开一个 Pull Request
207
+
208
+ ## 📜 许可证
209
+
210
+ 本项目根据 MIT 许可证获得许可 - 有关详细信息,请参阅 [LICENSE](LICENSE) 文件。
211
+
212
+ ---
213
+
214
+ 由 [Chatman Media](https://github.com/chatman-media) 用 ❤️ 制作
@@ -0,0 +1,9 @@
1
+ export interface BadgeFormat {
2
+ label: string;
3
+ message: string;
4
+ color: string;
5
+ labelColor: string;
6
+ style: string;
7
+ logo?: boolean;
8
+ }
9
+ export declare function generateBadgeSVG(format: BadgeFormat): string;
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateBadgeSVG = generateBadgeSVG;
4
+ // Telegram logo as base64 encoded SVG
5
+ const TELEGRAM_LOGO = `<svg xmlns="http://www.w3.org/2000/svg" fill="white" viewBox="0 0 24 24" width="14" height="14">
6
+ <path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.894 8.221l-1.97 9.28c-.145.658-.537.818-1.084.508l-3-2.21-1.446 1.394c-.14.18-.357.295-.6.295-.002 0-.003 0-.005 0l.213-3.054 5.56-5.022c.24-.213-.054-.334-.373-.121l-6.869 4.326-2.96-.924c-.64-.203-.658-.64.135-.954l11.566-4.458c.538-.196 1.006.128.832.941z"/>
7
+ </svg>`;
8
+ function generateBadgeSVG(format) {
9
+ const { label, message, color, labelColor, style, logo } = format;
10
+ const logoSpace = logo ? 25 : 0;
11
+ // Better text width calculation
12
+ // Always add logoSpace to width calculation
13
+ const labelWidth = label.length * 6.5 + 10 + logoSpace;
14
+ const messageWidth = message.length * 6.5 + 10;
15
+ const totalWidth = labelWidth + messageWidth;
16
+ // Create logo element
17
+ const logoElement = logo ? `<image x="5" y="3" width="14" height="14" href="data:image/svg+xml;base64,${Buffer.from(TELEGRAM_LOGO).toString('base64')}"/>` : '';
18
+ // For-the-badge style
19
+ if (style === 'for-the-badge') {
20
+ const logoForBadge = logo ? `<image x="5" y="7" width="14" height="14" href="data:image/svg+xml;base64,${Buffer.from(TELEGRAM_LOGO).toString('base64')}"/>` : '';
21
+ return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${totalWidth}" height="28" role="img" aria-label="${label}: ${message}">
22
+ <title>${label}: ${message}</title>
23
+ <g shape-rendering="crispEdges">
24
+ <rect width="${labelWidth}" height="28" fill="${labelColor}"/>
25
+ <rect x="${labelWidth}" width="${messageWidth}" height="28" fill="${color}"/>
26
+ </g>
27
+ ${logoForBadge}
28
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100">
29
+ <text x="${(labelWidth / 2 + (logo ? 10 : 0)) * 10}" y="175" transform="scale(.1)" fill="#fff" textLength="${label.length * 65}">${label.toUpperCase()}</text>
30
+ <text x="${(labelWidth + messageWidth / 2) * 10}" y="175" font-weight="bold" transform="scale(.1)" fill="#fff" textLength="${message.length * 65}">${message.toUpperCase()}</text>
31
+ </g>
32
+ </svg>`;
33
+ }
34
+ // Flat-square style
35
+ if (style === 'flat-square') {
36
+ return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${totalWidth}" height="20" role="img" aria-label="${label}: ${message}">
37
+ <title>${label}: ${message}</title>
38
+ <g shape-rendering="crispEdges">
39
+ <rect width="${labelWidth}" height="20" fill="${labelColor}"/>
40
+ <rect x="${labelWidth}" width="${messageWidth}" height="20" fill="${color}"/>
41
+ </g>
42
+ ${logoElement}
43
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
44
+ <text x="${(labelWidth / 2 + (logo ? 10 : 0)) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${label.length * 65}">${label}</text>
45
+ <text x="${(labelWidth + messageWidth / 2) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${message.length * 65}">${message}</text>
46
+ </g>
47
+ </svg>`;
48
+ }
49
+ // Social style
50
+ if (style === 'social') {
51
+ const socialLogoElement = logo ? `<image x="7" y="3" width="14" height="14" href="data:image/svg+xml;base64,${Buffer.from(TELEGRAM_LOGO).toString('base64')}"/>` : '';
52
+ return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${totalWidth + 4}" height="20" style="border-radius: 3px" role="img" aria-label="${label}: ${message}">
53
+ <title>${label}: ${message}</title>
54
+ <linearGradient id="s" x2="0" y2="100%">
55
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
56
+ <stop offset="1" stop-opacity=".1"/>
57
+ </linearGradient>
58
+ <clipPath id="r">
59
+ <rect width="${totalWidth + 4}" height="20" rx="3" fill="#fff"/>
60
+ </clipPath>
61
+ <g clip-path="url(#r)">
62
+ <rect width="${labelWidth + 2}" height="20" fill="${labelColor}"/>
63
+ <rect x="${labelWidth + 2}" width="${messageWidth + 2}" height="20" fill="${color}"/>
64
+ <rect width="${totalWidth + 4}" height="20" fill="url(#s)"/>
65
+ </g>
66
+ ${socialLogoElement}
67
+ <g fill="#333" text-anchor="middle" font-family="Helvetica,Arial,sans-serif" font-weight="700" font-size="110">
68
+ <text x="${(labelWidth / 2 + 1 + (logo ? 10 : 0)) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${label.length * 65}">${label}</text>
69
+ <text x="${(labelWidth + messageWidth / 2 + 2) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${message.length * 65}">${message}</text>
70
+ </g>
71
+ </svg>`;
72
+ }
73
+ // Plastic style
74
+ if (style === 'plastic') {
75
+ return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${totalWidth}" height="20" role="img" aria-label="${label}: ${message}">
76
+ <title>${label}: ${message}</title>
77
+ <linearGradient id="s" x2="0" y2="100%">
78
+ <stop offset="0" stop-color="#fff" stop-opacity=".7"/>
79
+ <stop offset=".1" stop-color="#aaa" stop-opacity=".1"/>
80
+ <stop offset=".9" stop-color="#000" stop-opacity=".3"/>
81
+ <stop offset="1" stop-color="#000" stop-opacity=".5"/>
82
+ </linearGradient>
83
+ <clipPath id="r">
84
+ <rect width="${totalWidth}" height="20" rx="4" fill="#fff"/>
85
+ </clipPath>
86
+ <g clip-path="url(#r)">
87
+ <rect width="${labelWidth}" height="20" fill="${labelColor}"/>
88
+ <rect x="${labelWidth}" width="${messageWidth}" height="20" fill="${color}"/>
89
+ <rect width="${totalWidth}" height="20" fill="url(#s)"/>
90
+ </g>
91
+ ${logoElement}
92
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
93
+ <text x="${(labelWidth / 2 + (logo ? 10 : 0)) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${label.length * 65}">${label}</text>
94
+ <text x="${(labelWidth + messageWidth / 2) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${message.length * 65}">${message}</text>
95
+ </g>
96
+ </svg>`;
97
+ }
98
+ // Default flat style
99
+ return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${totalWidth}" height="20" role="img" aria-label="${label}: ${message}">
100
+ <title>${label}: ${message}</title>
101
+ <linearGradient id="s" x2="0" y2="100%">
102
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
103
+ <stop offset="1" stop-opacity=".1"/>
104
+ </linearGradient>
105
+ <clipPath id="r">
106
+ <rect width="${totalWidth}" height="20" rx="3" fill="#fff"/>
107
+ </clipPath>
108
+ <g clip-path="url(#r)">
109
+ <rect width="${labelWidth}" height="20" fill="${labelColor}"/>
110
+ <rect x="${labelWidth}" width="${messageWidth}" height="20" fill="${color}"/>
111
+ <rect width="${totalWidth}" height="20" fill="url(#s)"/>
112
+ </g>
113
+ ${logoElement}
114
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
115
+ <text aria-hidden="true" x="${(labelWidth / 2 + (logo ? 10 : 0)) * 10}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${label.length * 65}">${label}</text>
116
+ <text x="${(labelWidth / 2 + (logo ? 10 : 0)) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${label.length * 65}">${label}</text>
117
+ <text aria-hidden="true" x="${(labelWidth + messageWidth / 2) * 10}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${message.length * 65}">${message}</text>
118
+ <text x="${(labelWidth + messageWidth / 2) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${message.length * 65}">${message}</text>
119
+ </g>
120
+ </svg>`;
121
+ }
@@ -1,2 +1,2 @@
1
- import { Request, Response } from '../types';
2
- export default function handler(req: Request, res: Response): Promise<void>;
1
+ import { VercelRequest, VercelResponse } from '@vercel/node';
2
+ export default function handler(req: VercelRequest, res: VercelResponse): Promise<void>;
@@ -34,8 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.default = handler;
37
- const badge_maker_1 = require("badge-maker");
38
37
  const crypto = __importStar(require("crypto"));
38
+ const badge_generator_1 = require("./badge-generator");
39
39
  const logger = {
40
40
  info: (message, data = {}) => {
41
41
  console.log(`[INFO] ${message}`, data);
@@ -106,11 +106,12 @@ const validateStyleOptions = (options) => {
106
106
  const label = options.label || 'Telegram';
107
107
  const color = options.color || '2AABEE';
108
108
  const labelColor = options.labelColor || '555555';
109
- return { style, label, color, labelColor };
109
+ const logo = options.logo !== false; // Logo by default
110
+ return { style, label, color, labelColor, logo };
110
111
  };
111
112
  const createBadge = (members, options) => {
112
- const { style, label, color, labelColor } = validateStyleOptions(options);
113
- logger.debug('Creating badge', { style, label, color, labelColor });
113
+ const { style, label, color, labelColor, logo } = validateStyleOptions(options);
114
+ logger.debug('Creating badge', { style, label, color, labelColor, logo });
114
115
  const normalizedColor = color.replace(/^#/, '');
115
116
  const normalizedLabelColor = labelColor.replace(/^#/, '');
116
117
  const format = {
@@ -118,9 +119,10 @@ const createBadge = (members, options) => {
118
119
  message: `${members} members`,
119
120
  color: `#${normalizedColor}`,
120
121
  labelColor: `#${normalizedLabelColor}`,
121
- style
122
+ style,
123
+ logo
122
124
  };
123
- return (0, badge_maker_1.makeBadge)(format);
125
+ return (0, badge_generator_1.generateBadgeSVG)(format);
124
126
  };
125
127
  const createErrorBadge = (errorMessage) => {
126
128
  const format = {
@@ -128,9 +130,10 @@ const createErrorBadge = (errorMessage) => {
128
130
  message: errorMessage,
129
131
  color: '#e05d44',
130
132
  labelColor: '#555555',
131
- style: 'flat'
133
+ style: 'flat',
134
+ logo: false
132
135
  };
133
- return (0, badge_maker_1.makeBadge)(format);
136
+ return (0, badge_generator_1.generateBadgeSVG)(format);
134
137
  };
135
138
  const setCacheHeaders = (res, svg) => {
136
139
  res.setHeader("Content-Type", "image/svg+xml");
@@ -146,12 +149,43 @@ const setCacheHeaders = (res, svg) => {
146
149
  logger.debug('Cache headers set');
147
150
  };
148
151
  async function handler(req, res) {
149
- logger.info('Received badge request', {
150
- query: req.query,
151
- userAgent: req.headers['user-agent'],
152
- referer: req.headers['referer'] || 'unknown'
152
+ // Global error handler
153
+ process.on('uncaughtException', (error) => {
154
+ console.error('[UNCAUGHT EXCEPTION]', error);
155
+ if (!res.headersSent) {
156
+ const errorBadge = createErrorBadge('Uncaught Error');
157
+ res.status(500).send(errorBadge);
158
+ }
159
+ });
160
+ process.on('unhandledRejection', (reason) => {
161
+ console.error('[UNHANDLED REJECTION]', reason);
162
+ if (!res.headersSent) {
163
+ const errorBadge = createErrorBadge('Unhandled Rejection');
164
+ res.status(500).send(errorBadge);
165
+ }
153
166
  });
154
167
  try {
168
+ logger.info('Function started', {
169
+ query: req.query,
170
+ userAgent: req.headers['user-agent'],
171
+ referer: req.headers['referer'] || 'unknown',
172
+ env: {
173
+ hasToken: !!process.env.BOT_TOKEN,
174
+ hasChatId: !!process.env.CHAT_ID
175
+ }
176
+ });
177
+ // Early check for environment variables
178
+ if (!process.env.BOT_TOKEN || !process.env.CHAT_ID) {
179
+ logger.error('Missing environment variables', {
180
+ BOT_TOKEN: !!process.env.BOT_TOKEN,
181
+ CHAT_ID: !!process.env.CHAT_ID
182
+ });
183
+ const errorBadge = createErrorBadge('Missing Config');
184
+ res.setHeader("Content-Type", "image/svg+xml");
185
+ res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
186
+ res.status(500).send(errorBadge);
187
+ return;
188
+ }
155
189
  const { token, chatId } = validateEnvironment();
156
190
  logger.debug('Environment validated', { chatId });
157
191
  const ifNoneMatch = req.headers['if-none-match'];
@@ -168,9 +202,10 @@ async function handler(req, res) {
168
202
  logger.info('Member count fetched', { members });
169
203
  const badgeOptions = {
170
204
  style: req.query.style,
171
- label: req.query.label,
172
- color: req.query.color,
173
- labelColor: req.query.labelColor
205
+ label: Array.isArray(req.query.label) ? req.query.label[0] : req.query.label,
206
+ color: Array.isArray(req.query.color) ? req.query.color[0] : req.query.color,
207
+ labelColor: Array.isArray(req.query.labelColor) ? req.query.labelColor[0] : req.query.labelColor,
208
+ logo: req.query.logo !== 'false' // Logo enabled by default, only disabled with logo=false
174
209
  };
175
210
  const svg = createBadge(members, badgeOptions);
176
211
  logger.debug('Badge created');
package/package.json CHANGED
@@ -1,8 +1,15 @@
1
1
  {
2
2
  "name": "telegram-badge",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Generate Telegram group member count badges for GitHub README",
5
- "keywords": ["telegram", "badge", "svg", "group", "members", "api"],
5
+ "keywords": [
6
+ "telegram",
7
+ "badge",
8
+ "svg",
9
+ "group",
10
+ "members",
11
+ "api"
12
+ ],
6
13
  "author": "Chatman Media",
7
14
  "license": "MIT",
8
15
  "repository": {
@@ -23,25 +30,24 @@
23
30
  "LICENSE"
24
31
  ],
25
32
  "scripts": {
26
- "dev": "vercel dev",
27
- "start": "vercel dev",
33
+ "dev": "node -p \"console.log('Use: npx vercel dev')\"",
34
+ "start": "node -p \"console.log('Use: npx vercel dev')\"",
28
35
  "test": "jest",
29
- "build": "tsc",
36
+ "build": "echo 'Build handled by Vercel'",
37
+ "build:local": "tsc",
30
38
  "type-check": "tsc --noEmit",
31
- "prepublishOnly": "npm run build && npm test",
32
- "prepack": "npm run build"
33
- },
34
- "dependencies": {
35
- "badge-maker": "^5.0.2"
39
+ "prepublishOnly": "npm run build:local && npm test",
40
+ "prepack": "npm run build:local"
36
41
  },
37
42
  "devDependencies": {
38
- "@types/jest": "^29.5.12",
43
+ "@types/jest": "^29.5.14",
39
44
  "@types/node": "^24.0.15",
45
+ "@vercel/node": "^2.3.0",
40
46
  "dotenv": "^17.2.0",
41
47
  "jest": "^30.0.4",
42
48
  "node-fetch": "^2.7.0",
43
- "typescript": "^5.5.4",
44
- "ts-jest": "^29.2.5",
45
- "ts-node": "^10.9.2"
49
+ "ts-jest": "^29.4.0",
50
+ "ts-node": "^10.9.2",
51
+ "typescript": "^5.8.3"
46
52
  }
47
53
  }