telegram-badge 1.0.0 → 1.0.1

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,218 @@
1
1
  # 🛡️ Telegram Group Badge Generator
2
2
 
3
+ [🇷🇺 Русская версия](README.ru.md) | [🇺🇸 English](README.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:
116
143
  ```
117
- https://telegram-badge.vercel.app/api/telegram-badge?style=social&label=Join%20Us&color=FF5733&labelColor=333333
144
+ https://telegram-badge.vercel.app/api/telegram-badge?logo=false
118
145
  ```
146
+ ![No Logo](https://telegram-badge.vercel.app/api/telegram-badge?logo=false)
119
147
 
120
- ## 🧠 Возможности
148
+ ## Features
121
149
 
122
- - 👥 Отображение количества участников в реальном времени
123
- - 🎨 Полная кастомизация внешнего вида бейджа
124
- - 🔒 Поддержка .env и переменных Vercel для безопасного хранения токенов
125
- - ⚡ Оптимизированное кэширование для быстрой загрузки
126
- - 🛡️ Обработка ошибок с информативными сообщениями
127
- - 🆓 Бесплатно на Vercel при обычной нагрузке
128
- - 📡 Можно расширить до отображения активности / количества сообщений
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
129
158
 
130
-
159
+ ## 🔧 API Usage
160
+
161
+ ### As npm package:
162
+
163
+ ```bash
164
+ npm install telegram-badge
165
+ ```
166
+
167
+ ```typescript
168
+ import badgeHandler from 'telegram-badge';
169
+
170
+ // Use in your serverless function
171
+ export default badgeHandler;
172
+ ```
173
+
174
+ ### Direct API calls:
175
+
176
+ ```typescript
177
+ GET /api/telegram-badge?style=flat&label=Members&color=2AABEE&labelColor=555555
178
+ ```
131
179
 
132
- 📜 Лицензия
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
+ ## 📚 Documentation
201
+
202
+ For detailed documentation in Russian, see [README.ru.md](README.ru.md).
203
+
204
+ ## 🤝 Contributing
205
+
206
+ 1. Fork the repository
207
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
208
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
209
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
210
+ 5. Open a Pull Request
211
+
212
+ ## 📜 License
213
+
214
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
215
+
216
+ ---
133
217
 
134
- MIT
218
+ Made with ❤️ by [Chatman Media](https://github.com/chatman-media)
package/README.ru.md ADDED
@@ -0,0 +1,153 @@
1
+ # 🛡️ Telegram Group Badge Generator
2
+
3
+ [🇷🇺 Русская версия](README.ru.md) | [🇺🇸 English](README.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
+
87
+ #### Доступные стили:
88
+
89
+ - `flat` - плоский стиль (по умолчанию)
90
+ - `plastic` - объемный стиль с градиентом
91
+ - `flat-square` - плоский стиль без закруглений
92
+ - `for-the-badge` - широкий стиль с заглавными буквами
93
+ - `social` - стиль как у GitHub
94
+
95
+ #### Примеры:
96
+
97
+ Стандартный бейдж (стиль flat):
98
+ ```
99
+ https://telegram-badge.vercel.app/api/telegram-badge
100
+ ```
101
+ ![Flat](https://telegram-badge.vercel.app/api/telegram-badge)
102
+
103
+ Бейдж со стилем plastic:
104
+ ```
105
+ https://telegram-badge.vercel.app/api/telegram-badge?style=plastic
106
+ ```
107
+ ![Plastic](https://telegram-badge.vercel.app/api/telegram-badge?style=plastic)
108
+
109
+ Бейдж со стилем flat-square:
110
+ ```
111
+ https://telegram-badge.vercel.app/api/telegram-badge?style=flat-square
112
+ ```
113
+ ![Flat-Square](https://telegram-badge.vercel.app/api/telegram-badge?style=flat-square)
114
+
115
+ Бейдж со стилем for-the-badge:
116
+ ```
117
+ https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge
118
+ ```
119
+ ![For-The-Badge](https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge)
120
+
121
+ Бейдж со стилем social:
122
+ ```
123
+ https://telegram-badge.vercel.app/api/telegram-badge?style=social
124
+ ```
125
+ ![Social](https://telegram-badge.vercel.app/api/telegram-badge?style=social)
126
+
127
+ Бейдж с кастомной меткой и цветом:
128
+ ```
129
+ https://telegram-badge.vercel.app/api/telegram-badge?label=Наш%20Чат&color=00FF00
130
+ ```
131
+ ![Custom](https://telegram-badge.vercel.app/api/telegram-badge?label=Наш%20Чат&color=00FF00)
132
+
133
+ Полностью кастомизированный бейдж:
134
+ ```
135
+ https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge&label=Сообщество&color=FF5733&labelColor=1A1A1A
136
+ ```
137
+ ![Full Custom](https://telegram-badge.vercel.app/api/telegram-badge?style=for-the-badge&label=Сообщество&color=FF5733&labelColor=1A1A1A)
138
+
139
+ ## 🧠 Возможности
140
+
141
+ - 👥 Отображение количества участников в реальном времени
142
+ - 🎨 Полная кастомизация внешнего вида бейджа
143
+ - 🔒 Поддержка .env и переменных Vercel для безопасного хранения токенов
144
+ - ⚡ Оптимизированное кэширование для быстрой загрузки
145
+ - 🛡️ Обработка ошибок с информативными сообщениями
146
+ - 🆓 Бесплатно на Vercel при обычной нагрузке
147
+ - 📡 Можно расширить до отображения активности / количества сообщений
148
+
149
+
150
+
151
+ 📜 Лицензия
152
+
153
+ MIT
@@ -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,119 @@
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
+ // Calculate logo space
11
+ const logoSpace = logo ? 20 : 0;
12
+ // Better text width calculation
13
+ const labelWidth = label.length * 6.5 + 20 + logoSpace;
14
+ const messageWidth = message.length * 6.5 + 20;
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="8" y="7" width="14" height="14" href="data:image/svg+xml;base64,${Buffer.from(TELEGRAM_LOGO).toString('base64')}"/>` : '';
21
+ const textOffset = logo ? 10 : 0;
22
+ 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}">
23
+ <title>${label}: ${message}</title>
24
+ <g shape-rendering="crispEdges">
25
+ <rect width="${labelWidth}" height="28" fill="${labelColor}"/>
26
+ <rect x="${labelWidth}" width="${messageWidth}" height="28" fill="${color}"/>
27
+ </g>
28
+ ${logoForBadge}
29
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100">
30
+ <text x="${(labelWidth / 2 + textOffset) * 10}" y="185" transform="scale(.1)" fill="#fff" textLength="${(labelWidth - 20 - logoSpace) * 10}">${label.toUpperCase()}</text>
31
+ <text x="${(labelWidth + messageWidth / 2) * 10}" y="185" font-weight="bold" transform="scale(.1)" fill="#fff" textLength="${(messageWidth - 20) * 10}">${message.toUpperCase()}</text>
32
+ </g>
33
+ </svg>`;
34
+ }
35
+ // Flat-square style
36
+ if (style === 'flat-square') {
37
+ 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}">
38
+ <title>${label}: ${message}</title>
39
+ <g shape-rendering="crispEdges">
40
+ <rect width="${labelWidth}" height="20" fill="${labelColor}"/>
41
+ <rect x="${labelWidth}" width="${messageWidth}" height="20" fill="${color}"/>
42
+ </g>
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 * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${(labelWidth - 10) * 10}">${label}</text>
45
+ <text x="${(labelWidth + messageWidth / 2) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${(messageWidth - 10) * 10}">${message}</text>
46
+ </g>
47
+ </svg>`;
48
+ }
49
+ // Social style
50
+ if (style === 'social') {
51
+ 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}">
52
+ <title>${label}: ${message}</title>
53
+ <linearGradient id="s" x2="0" y2="100%">
54
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
55
+ <stop offset="1" stop-opacity=".1"/>
56
+ </linearGradient>
57
+ <clipPath id="r">
58
+ <rect width="${totalWidth + 4}" height="20" rx="3" fill="#fff"/>
59
+ </clipPath>
60
+ <g clip-path="url(#r)">
61
+ <rect width="${labelWidth + 2}" height="20" fill="${labelColor}"/>
62
+ <rect x="${labelWidth + 2}" width="${messageWidth + 2}" height="20" fill="${color}"/>
63
+ <rect width="${totalWidth + 4}" height="20" fill="url(#s)"/>
64
+ </g>
65
+ <g fill="#333" text-anchor="middle" font-family="Helvetica,Arial,sans-serif" font-weight="700" font-size="110">
66
+ <text x="${(labelWidth / 2 + 1) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${(labelWidth - 10) * 10}">${label}</text>
67
+ <text x="${(labelWidth + messageWidth / 2 + 2) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${(messageWidth - 10) * 10}">${message}</text>
68
+ </g>
69
+ </svg>`;
70
+ }
71
+ // Plastic style
72
+ if (style === 'plastic') {
73
+ 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}">
74
+ <title>${label}: ${message}</title>
75
+ <linearGradient id="s" x2="0" y2="100%">
76
+ <stop offset="0" stop-color="#fff" stop-opacity=".7"/>
77
+ <stop offset=".1" stop-color="#aaa" stop-opacity=".1"/>
78
+ <stop offset=".9" stop-color="#000" stop-opacity=".3"/>
79
+ <stop offset="1" stop-color="#000" stop-opacity=".5"/>
80
+ </linearGradient>
81
+ <clipPath id="r">
82
+ <rect width="${totalWidth}" height="20" rx="4" fill="#fff"/>
83
+ </clipPath>
84
+ <g clip-path="url(#r)">
85
+ <rect width="${labelWidth}" height="20" fill="${labelColor}"/>
86
+ <rect x="${labelWidth}" width="${messageWidth}" height="20" fill="${color}"/>
87
+ <rect width="${totalWidth}" height="20" fill="url(#s)"/>
88
+ </g>
89
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
90
+ <text x="${labelWidth / 2 * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${(labelWidth - 10) * 10}">${label}</text>
91
+ <text x="${(labelWidth + messageWidth / 2) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${(messageWidth - 10) * 10}">${message}</text>
92
+ </g>
93
+ </svg>`;
94
+ }
95
+ // Default flat style
96
+ const textOffset = logo ? 10 : 0;
97
+ 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}">
98
+ <title>${label}: ${message}</title>
99
+ <linearGradient id="s" x2="0" y2="100%">
100
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
101
+ <stop offset="1" stop-opacity=".1"/>
102
+ </linearGradient>
103
+ <clipPath id="r">
104
+ <rect width="${totalWidth}" height="20" rx="3" fill="#fff"/>
105
+ </clipPath>
106
+ <g clip-path="url(#r)">
107
+ <rect width="${labelWidth}" height="20" fill="${labelColor}"/>
108
+ <rect x="${labelWidth}" width="${messageWidth}" height="20" fill="${color}"/>
109
+ <rect width="${totalWidth}" height="20" fill="url(#s)"/>
110
+ </g>
111
+ ${logoElement}
112
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
113
+ <text aria-hidden="true" x="${(labelWidth / 2 + textOffset) * 10}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${(labelWidth - 10 - logoSpace) * 10}">${label}</text>
114
+ <text x="${(labelWidth / 2 + textOffset) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${(labelWidth - 10 - logoSpace) * 10}">${label}</text>
115
+ <text aria-hidden="true" x="${(labelWidth + messageWidth / 2) * 10}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="${(messageWidth - 10) * 10}">${message}</text>
116
+ <text x="${(labelWidth + messageWidth / 2) * 10}" y="140" transform="scale(.1)" fill="#fff" textLength="${(messageWidth - 10) * 10}">${message}</text>
117
+ </g>
118
+ </svg>`;
119
+ }
@@ -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.0.1",
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
  }