@botgate/botgate-stats-reporter 1.0.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/LICENSE +21 -0
- package/README.md +708 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +214 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 BotGate
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
# 📊 BotGate Stats Reporter
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@botgate/stats-reporter)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
Pacote oficial do **BotGate** para reportar estatísticas do seu bot Discord automaticamente para a plataforma BotGate.
|
|
8
|
+
|
|
9
|
+
## ✨ Funcionalidades
|
|
10
|
+
|
|
11
|
+
- ✅ **Envio automático** de estatísticas (servidores, usuários, shards)
|
|
12
|
+
- ⏰ **Intervalo configurável** de atualização
|
|
13
|
+
- 🔄 **Retry automático** em caso de falha
|
|
14
|
+
- 🔐 **Verificação de API key**
|
|
15
|
+
- 📝 **Logs detalhados** (modo debug)
|
|
16
|
+
- 🎯 **TypeScript completo** com tipos exportados
|
|
17
|
+
- 🚀 **Fácil integração** com Discord.js
|
|
18
|
+
- 💪 **Robusto e confiável**
|
|
19
|
+
|
|
20
|
+
## 📦 Instalação
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @botgate/stats-reporter
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
ou
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
yarn add @botgate/stats-reporter
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 🚀 Uso Básico
|
|
33
|
+
|
|
34
|
+
### JavaScript
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const { Client, GatewayIntentBits } = require("discord.js");
|
|
38
|
+
const { BotGateReporter } = require("@botgate/stats-reporter");
|
|
39
|
+
|
|
40
|
+
const client = new Client({
|
|
41
|
+
intents: [GatewayIntentBits.Guilds],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const reporter = new BotGateReporter({
|
|
45
|
+
botId: "YOUR_BOT_ID",
|
|
46
|
+
apiKey: "YOUR_API_KEY",
|
|
47
|
+
debug: true, // Opcional
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
client.once("ready", () => {
|
|
51
|
+
console.log(`Bot logado como ${client.user.tag}`);
|
|
52
|
+
reporter.start(client);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
client.login("YOUR_BOT_TOKEN");
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### TypeScript
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Client, GatewayIntentBits } from "discord.js";
|
|
62
|
+
import { BotGateReporter } from "@botgate/stats-reporter";
|
|
63
|
+
|
|
64
|
+
const client = new Client({
|
|
65
|
+
intents: [GatewayIntentBits.Guilds],
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const reporter = new BotGateReporter({
|
|
69
|
+
botId: "YOUR_BOT_ID",
|
|
70
|
+
apiKey: "YOUR_API_KEY",
|
|
71
|
+
debug: true, // Opcional
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
client.once("ready", () => {
|
|
75
|
+
console.log(`Bot logado como ${client.user.tag}`);
|
|
76
|
+
reporter.start(client);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
client.login("YOUR_BOT_TOKEN");
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 📖 Documentação Completa
|
|
83
|
+
|
|
84
|
+
### Configuração
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
interface BotGateConfig {
|
|
88
|
+
// Obrigatório
|
|
89
|
+
botId: string; // ID do bot no Discord
|
|
90
|
+
apiKey: string; // API key do BotGate
|
|
91
|
+
|
|
92
|
+
// Opcional
|
|
93
|
+
debug?: boolean; // Ativar logs (padrão: false)
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Métodos Principais
|
|
98
|
+
|
|
99
|
+
#### `start(client: Client): void`
|
|
100
|
+
|
|
101
|
+
Inicia o reporter e começa a enviar estatísticas automaticamente.
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
reporter.start(client);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### `stop(): void`
|
|
108
|
+
|
|
109
|
+
Para o reporter e cancela atualizações automáticas.
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
reporter.stop();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### `sendStats(): Promise<BotGateResponse>`
|
|
116
|
+
|
|
117
|
+
Envia estatísticas manualmente (sem aguardar o intervalo).
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
try {
|
|
121
|
+
const response = await reporter.sendStats();
|
|
122
|
+
console.log("Stats enviadas:", response);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error("Erro:", error);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### `verifyApiKey(): Promise<boolean>`
|
|
129
|
+
|
|
130
|
+
Verifica se a API key é válida.
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const isValid = await reporter.verifyApiKey();
|
|
134
|
+
if (isValid) {
|
|
135
|
+
console.log("API key válida!");
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### `getBotInfo(): Promise<BotInfo>`
|
|
140
|
+
|
|
141
|
+
Obtém informações do bot no BotGate.
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
const botInfo = await reporter.getBotInfo();
|
|
145
|
+
console.log("Nome:", botInfo.name);
|
|
146
|
+
console.log("Votos:", botInfo.stats.votes);
|
|
147
|
+
console.log("Avaliação:", botInfo.stats.rating);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### `getConfig(): Readonly<Required<BotGateConfig>>`
|
|
151
|
+
|
|
152
|
+
Retorna a configuração atual.
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
const config = reporter.getConfig();
|
|
156
|
+
console.log("Intervalo:", config.updateInterval);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### `isActive(): boolean`
|
|
160
|
+
|
|
161
|
+
Verifica se o reporter está ativo.
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
if (reporter.isActive()) {
|
|
165
|
+
console.log("Reporter está rodando");
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### `getFailedAttempts(): number`
|
|
170
|
+
|
|
171
|
+
Retorna o número de tentativas falhadas consecutivas.
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
const failures = reporter.getFailedAttempts();
|
|
175
|
+
console.log("Falhas consecutivas:", failures);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### `refreshTier(): Promise<boolean>`
|
|
179
|
+
|
|
180
|
+
Atualiza o tier e intervalo de atualização consultando a API.
|
|
181
|
+
|
|
182
|
+
**Use este método após fazer upgrade do tier** para aplicar o novo intervalo sem reiniciar o bot.
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
// Após fazer upgrade para Premium no site
|
|
186
|
+
await reporter.refreshTier();
|
|
187
|
+
// O intervalo será automaticamente ajustado (ex: 30min → 5min)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
> **💡 Dica**: Quando você faz upgrade do tier (Free → Premium → Business), chame `refreshTier()` para que o reporter comece a enviar stats com o novo intervalo imediatamente!
|
|
191
|
+
|
|
192
|
+
## 🔑 Obtendo sua API Key
|
|
193
|
+
|
|
194
|
+
1. Acesse [BotGate](https://botgate.com)
|
|
195
|
+
2. Faça login com sua conta Discord
|
|
196
|
+
3. Vá para o painel do seu bot
|
|
197
|
+
4. Copie sua API key na seção "Configurações"
|
|
198
|
+
|
|
199
|
+
⚠️ **Importante**: Nunca compartilhe sua API key publicamente!
|
|
200
|
+
|
|
201
|
+
## 📊 Estatísticas Enviadas
|
|
202
|
+
|
|
203
|
+
O reporter envia automaticamente:
|
|
204
|
+
|
|
205
|
+
- **Número de servidores** (`serverCount`)
|
|
206
|
+
- **Número total de usuários** (`userCount`)
|
|
207
|
+
- **Número de shards** (`shardCount`)
|
|
208
|
+
- **Timestamp** do envio
|
|
209
|
+
|
|
210
|
+
## 🔄 Retry Automático
|
|
211
|
+
|
|
212
|
+
O reporter tenta enviar as estatísticas até 3 vezes (configurável) em caso de falha:
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
const reporter = new BotGateReporter({
|
|
216
|
+
botId: "YOUR_BOT_ID",
|
|
217
|
+
apiKey: "YOUR_API_KEY",
|
|
218
|
+
retryAttempts: 3, // Tentar 3 vezes
|
|
219
|
+
retryDelay: 5000, // Aguardar 5s entre tentativas
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## 🐛 Debug
|
|
224
|
+
|
|
225
|
+
Ative o modo debug para ver logs detalhados:
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
const reporter = new BotGateReporter({
|
|
229
|
+
botId: "YOUR_BOT_ID",
|
|
230
|
+
apiKey: "YOUR_API_KEY",
|
|
231
|
+
debug: true, // ✅ Ativar logs
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Exemplo de logs:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
[BotGate Reporter] [2026-01-18T14:30:00.000Z] ✅ BotGate Reporter initialized
|
|
239
|
+
[BotGate Reporter] [2026-01-18T14:30:05.000Z] 🤖 Bot ready: MyBot#1234
|
|
240
|
+
[BotGate Reporter] [2026-01-18T14:30:06.000Z] 📤 Stats sent successfully (attempt 1)
|
|
241
|
+
{
|
|
242
|
+
"servers": 1250,
|
|
243
|
+
"users": 50000,
|
|
244
|
+
"shards": 1
|
|
245
|
+
}
|
|
246
|
+
[BotGate Reporter] [2026-01-18T14:30:06.000Z] ⏰ Auto-update enabled (every 30 minutes)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## 🛡️ Tratamento de Erros
|
|
250
|
+
|
|
251
|
+
O reporter lida automaticamente com erros comuns:
|
|
252
|
+
|
|
253
|
+
```javascript
|
|
254
|
+
client.once("ready", async () => {
|
|
255
|
+
reporter.start(client);
|
|
256
|
+
|
|
257
|
+
// Verificar se a API key é válida
|
|
258
|
+
const isValid = await reporter.verifyApiKey();
|
|
259
|
+
if (!isValid) {
|
|
260
|
+
console.error("❌ API key inválida!");
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## 🔧 Exemplos Avançados
|
|
267
|
+
|
|
268
|
+
### Envio Manual em Eventos
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
client.on("guildCreate", async (guild) => {
|
|
272
|
+
console.log(`➕ Entrou no servidor: ${guild.name}`);
|
|
273
|
+
|
|
274
|
+
// Enviar stats imediatamente
|
|
275
|
+
await reporter.sendStats();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
client.on("guildDelete", async (guild) => {
|
|
279
|
+
console.log(`➖ Saiu do servidor: ${guild.name}`);
|
|
280
|
+
|
|
281
|
+
// Enviar stats imediatamente
|
|
282
|
+
await reporter.sendStats();
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Atualizar Tier Após Upgrade
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
// Comando para atualizar o tier após fazer upgrade no site
|
|
290
|
+
client.on("messageCreate", async (message) => {
|
|
291
|
+
if (
|
|
292
|
+
message.content === "!refresh-tier" &&
|
|
293
|
+
message.author.id === "SEU_USER_ID"
|
|
294
|
+
) {
|
|
295
|
+
message.reply("🔄 Atualizando tier...");
|
|
296
|
+
|
|
297
|
+
const success = await reporter.refreshTier();
|
|
298
|
+
|
|
299
|
+
if (success) {
|
|
300
|
+
message.reply("✅ Tier atualizado! Novo intervalo aplicado.");
|
|
301
|
+
} else {
|
|
302
|
+
message.reply("❌ Erro ao atualizar tier.");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Graceful Shutdown
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
process.on("SIGINT", () => {
|
|
312
|
+
console.log("🛑 Encerrando...");
|
|
313
|
+
reporter.stop();
|
|
314
|
+
client.destroy();
|
|
315
|
+
process.exit(0);
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Monitoramento de Falhas
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
setInterval(() => {
|
|
323
|
+
const failures = reporter.getFailedAttempts();
|
|
324
|
+
if (failures > 5) {
|
|
325
|
+
console.error(`⚠️ Muitas falhas consecutivas: ${failures}`);
|
|
326
|
+
// Enviar alerta, etc.
|
|
327
|
+
}
|
|
328
|
+
}, 60000); // Verificar a cada minuto
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## 🌐 API Endpoints Completos
|
|
332
|
+
|
|
333
|
+
O reporter se comunica com a API v1 do BotGate. Todos os endpoints requerem autenticação via API key no header `Authorization: Bearer <api_key>`.
|
|
334
|
+
|
|
335
|
+
### 📤 POST `/api/v1/bots/stats`
|
|
336
|
+
|
|
337
|
+
Envia estatísticas do bot (servidores, usuários, shards).
|
|
338
|
+
|
|
339
|
+
**Request Body:**
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"botId": "123456789012345678",
|
|
344
|
+
"serverCount": 1250,
|
|
345
|
+
"userCount": 50000,
|
|
346
|
+
"shardCount": 1,
|
|
347
|
+
"timestamp": 1705678901234
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Response:**
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"success": true,
|
|
356
|
+
"message": "Stats updated successfully",
|
|
357
|
+
"data": {
|
|
358
|
+
"botId": "123456789012345678",
|
|
359
|
+
"botName": "MyBot",
|
|
360
|
+
"serverCount": 1250,
|
|
361
|
+
"userCount": 50000,
|
|
362
|
+
"shardCount": 1,
|
|
363
|
+
"updatedAt": "2026-01-19T07:00:00.000Z"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### ✅ GET `/api/v1/verify`
|
|
369
|
+
|
|
370
|
+
Verifica se a API key é válida e retorna informações do tier.
|
|
371
|
+
|
|
372
|
+
**Response:**
|
|
373
|
+
|
|
374
|
+
```json
|
|
375
|
+
{
|
|
376
|
+
"success": true,
|
|
377
|
+
"message": "API key is valid",
|
|
378
|
+
"data": {
|
|
379
|
+
"botId": "123456789012345678",
|
|
380
|
+
"botName": "MyBot",
|
|
381
|
+
"tier": {
|
|
382
|
+
"name": "free",
|
|
383
|
+
"apiCallsUsed": 150,
|
|
384
|
+
"apiCallsLimit": 1000,
|
|
385
|
+
"updateInterval": "30 minutes",
|
|
386
|
+
"analyticsLevel": "basic",
|
|
387
|
+
"historyDays": 7,
|
|
388
|
+
"features": {
|
|
389
|
+
"customWebhooks": false,
|
|
390
|
+
"prioritySupport": false,
|
|
391
|
+
"badge": "none"
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 🤖 GET `/api/v1/bots/:botId`
|
|
399
|
+
|
|
400
|
+
Obtém informações completas do bot.
|
|
401
|
+
|
|
402
|
+
**Response:**
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"success": true,
|
|
407
|
+
"data": {
|
|
408
|
+
"id": "123456789012345678",
|
|
409
|
+
"name": "MyBot",
|
|
410
|
+
"avatar": "...",
|
|
411
|
+
"shortDescription": "Um bot incrível!",
|
|
412
|
+
"stats": {
|
|
413
|
+
"servers": 1250,
|
|
414
|
+
"users": 50000,
|
|
415
|
+
"shards": 1,
|
|
416
|
+
"rating": 4.8,
|
|
417
|
+
"reviews": 42
|
|
418
|
+
},
|
|
419
|
+
"owner": {
|
|
420
|
+
"id": "987654321098765432",
|
|
421
|
+
"username": "DevName"
|
|
422
|
+
},
|
|
423
|
+
"categories": [...],
|
|
424
|
+
"features": [...],
|
|
425
|
+
"commands": [...]
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### 🗳️ GET `/api/v1/bots/:botId/votes`
|
|
431
|
+
|
|
432
|
+
Retorna informações detalhadas sobre votos.
|
|
433
|
+
|
|
434
|
+
**Query Parameters:**
|
|
435
|
+
|
|
436
|
+
- `limit` - Número de últimos votantes (padrão: 10, máx: 50)
|
|
437
|
+
|
|
438
|
+
**Response:**
|
|
439
|
+
|
|
440
|
+
```json
|
|
441
|
+
{
|
|
442
|
+
"success": true,
|
|
443
|
+
"data": {
|
|
444
|
+
"botId": "123456789012345678",
|
|
445
|
+
"botName": "MyBot",
|
|
446
|
+
"total": 5420,
|
|
447
|
+
"monthly": 342,
|
|
448
|
+
"weekly": 87,
|
|
449
|
+
"today": 12,
|
|
450
|
+
"recentVoters": [...],
|
|
451
|
+
"monthlyHistory": [...]
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### 📊 GET `/api/v1/bots/:botId/analytics`
|
|
457
|
+
|
|
458
|
+
Retorna métricas e analytics detalhadas (requer tier Free ou superior).
|
|
459
|
+
|
|
460
|
+
**Response:**
|
|
461
|
+
|
|
462
|
+
```json
|
|
463
|
+
{
|
|
464
|
+
"success": true,
|
|
465
|
+
"data": {
|
|
466
|
+
"botId": "123456789012345678",
|
|
467
|
+
"botName": "MyBot",
|
|
468
|
+
"growth": {
|
|
469
|
+
"servers": {
|
|
470
|
+
"current": 1250,
|
|
471
|
+
"today": 15,
|
|
472
|
+
"week": 120,
|
|
473
|
+
"month": 450,
|
|
474
|
+
"percentageChange": {
|
|
475
|
+
"daily": "1.2",
|
|
476
|
+
"weekly": "10.6",
|
|
477
|
+
"monthly": "56.3"
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
"votes": {...}
|
|
481
|
+
},
|
|
482
|
+
"engagement": {
|
|
483
|
+
"votesPerDay": 12.5,
|
|
484
|
+
"reviewsPerWeek": 2.3,
|
|
485
|
+
"averageRating": 4.8
|
|
486
|
+
},
|
|
487
|
+
"trends": {
|
|
488
|
+
"peakDays": ["Saturday", "Sunday", "Friday"],
|
|
489
|
+
"dailyVotesLast7Days": [...]
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### 📈 GET `/api/v1/bots/:botId/stats/history`
|
|
496
|
+
|
|
497
|
+
Retorna histórico de estatísticas para gráficos.
|
|
498
|
+
|
|
499
|
+
**Query Parameters:**
|
|
500
|
+
|
|
501
|
+
- `period` - Período: 'daily', 'weekly', 'monthly', 'all' (padrão: 'all')
|
|
502
|
+
|
|
503
|
+
**Response:**
|
|
504
|
+
|
|
505
|
+
```json
|
|
506
|
+
{
|
|
507
|
+
"success": true,
|
|
508
|
+
"data": {
|
|
509
|
+
"botId": "123456789012345678",
|
|
510
|
+
"botName": "MyBot",
|
|
511
|
+
"daily": [
|
|
512
|
+
{
|
|
513
|
+
"date": "2026-01-19",
|
|
514
|
+
"servers": 1250,
|
|
515
|
+
"votes": 5420,
|
|
516
|
+
"rating": "4.80"
|
|
517
|
+
}
|
|
518
|
+
],
|
|
519
|
+
"weekly": [...],
|
|
520
|
+
"monthly": [...]
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### 📋 GET `/api/v1/usage`
|
|
526
|
+
|
|
527
|
+
Retorna informações sobre o uso atual da API.
|
|
528
|
+
|
|
529
|
+
**Response:**
|
|
530
|
+
|
|
531
|
+
```json
|
|
532
|
+
{
|
|
533
|
+
"success": true,
|
|
534
|
+
"data": {
|
|
535
|
+
"bot": {
|
|
536
|
+
"id": "123456789012345678",
|
|
537
|
+
"name": "MyBot"
|
|
538
|
+
},
|
|
539
|
+
"tier": {
|
|
540
|
+
"name": "free",
|
|
541
|
+
"displayName": "Free",
|
|
542
|
+
"features": {...}
|
|
543
|
+
},
|
|
544
|
+
"usage": {
|
|
545
|
+
"apiCalls": {
|
|
546
|
+
"used": 150,
|
|
547
|
+
"limit": 1000,
|
|
548
|
+
"remaining": 850,
|
|
549
|
+
"percentage": 15.0,
|
|
550
|
+
"status": "healthy",
|
|
551
|
+
"message": "Uso normal"
|
|
552
|
+
},
|
|
553
|
+
"reset": {
|
|
554
|
+
"at": "2026-02-01T00:00:00.000Z",
|
|
555
|
+
"inDays": 13,
|
|
556
|
+
"inHours": 312
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
"updates": {
|
|
560
|
+
"interval": "30 minutes",
|
|
561
|
+
"lastUpdate": "2026-01-19T03:20:00.000Z",
|
|
562
|
+
"nextUpdateAllowedAt": "2026-01-19T03:50:00.000Z",
|
|
563
|
+
"minutesUntilNextUpdate": 25,
|
|
564
|
+
"canUpdateNow": false
|
|
565
|
+
},
|
|
566
|
+
"statistics": {
|
|
567
|
+
"avgCallsPerDay": 12.5,
|
|
568
|
+
"estimatedDaysRemaining": 68,
|
|
569
|
+
"usageHistory": [...]
|
|
570
|
+
},
|
|
571
|
+
"upgrade": null
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
## 💎 Tiers e Limites
|
|
577
|
+
|
|
578
|
+
O BotGate oferece 3 tiers com diferentes limites e recursos:
|
|
579
|
+
|
|
580
|
+
### 🆓 Free Tier
|
|
581
|
+
|
|
582
|
+
- ✅ **1,500 chamadas de API/mês**
|
|
583
|
+
- ⏰ **Intervalo mínimo de atualização: 30 minutos**
|
|
584
|
+
- 📊 Analytics **básicas**
|
|
585
|
+
- 📅 Histórico de **7 dias**
|
|
586
|
+
- 🎯 Perfeito para começar!
|
|
587
|
+
|
|
588
|
+
### ⭐ Premium Tier ($9.99/mês)
|
|
589
|
+
|
|
590
|
+
- ✅ **10,000 chamadas de API/mês** (6.6x mais)
|
|
591
|
+
- ⏰ **Intervalo mínimo de atualização: 5 minutos** (6x mais rápido)
|
|
592
|
+
- 📊 Analytics **avançadas**
|
|
593
|
+
- 📅 Histórico de **90 dias**
|
|
594
|
+
- 🎨 Badge **Premium**
|
|
595
|
+
- 🔔 Webhooks customizados
|
|
596
|
+
- 💬 Suporte prioritário
|
|
597
|
+
|
|
598
|
+
### 🚀 Business Tier ($29.99/mês)
|
|
599
|
+
|
|
600
|
+
- ✅ **100,000 chamadas de API/mês** (66x mais)
|
|
601
|
+
- ⏰ **Intervalo mínimo de atualização: 1 minuto** (30x mais rápido)
|
|
602
|
+
- 📊 Analytics **enterprise**
|
|
603
|
+
- 📅 Histórico de **365 dias**
|
|
604
|
+
- 👑 Badge **Verified**
|
|
605
|
+
- 🎯 Domínio customizado
|
|
606
|
+
- 🚫 Sem anúncios
|
|
607
|
+
- 🔔 Webhooks customizados
|
|
608
|
+
- 💬 Suporte prioritário 24/7
|
|
609
|
+
|
|
610
|
+
> **💡 Ajuste Automático de Intervalo:**
|
|
611
|
+
>
|
|
612
|
+
> O reporter **detecta automaticamente** o seu tier ao iniciar e ajusta o intervalo de atualização:
|
|
613
|
+
>
|
|
614
|
+
> - 🆓 **Free**: Envia stats a cada **30 minutos**
|
|
615
|
+
> - ⭐ **Premium**: Envia stats a cada **5 minutos** (6x mais rápido!)
|
|
616
|
+
> - 🚀 **Business**: Envia stats a cada **1 minuto** (30x mais rápido!)
|
|
617
|
+
>
|
|
618
|
+
> **Após fazer upgrade**, você tem 2 opções:
|
|
619
|
+
>
|
|
620
|
+
> 1. **Reiniciar o bot** - O novo intervalo será aplicado automaticamente
|
|
621
|
+
> 2. **Chamar `reporter.refreshTier()`** - Atualiza sem reiniciar (recomendado!)
|
|
622
|
+
|
|
623
|
+
### 📊 Comparação de Limites
|
|
624
|
+
|
|
625
|
+
| Recurso | Free | Premium | Business |
|
|
626
|
+
| ---------------- | ---------- | ----------- | ---------- |
|
|
627
|
+
| Chamadas API/mês | 1,500 | 10,000 | 100,000 |
|
|
628
|
+
| Intervalo mínimo | 30 min | 5 min | 1 min |
|
|
629
|
+
| Analytics | Básicas | Avançadas | Enterprise |
|
|
630
|
+
| Histórico | 7 dias | 90 dias | 365 dias |
|
|
631
|
+
| Webhooks | ❌ | ✅ | ✅ |
|
|
632
|
+
| Domínio custom | ❌ | ❌ | ✅ |
|
|
633
|
+
| Badge | Nenhum | Premium | Verified |
|
|
634
|
+
| Suporte | Comunidade | Prioritário | 24/7 |
|
|
635
|
+
|
|
636
|
+
## 🔒 Rate Limiting
|
|
637
|
+
|
|
638
|
+
A API implementa rate limiting baseado no tier:
|
|
639
|
+
|
|
640
|
+
- **Free**: 1,500 chamadas/mês, reset no dia 1º de cada mês
|
|
641
|
+
- **Premium**: 10,000 chamadas/mês
|
|
642
|
+
- **Business**: 100,000 chamadas/mês
|
|
643
|
+
|
|
644
|
+
Quando o limite é atingido, a API retorna:
|
|
645
|
+
|
|
646
|
+
```json
|
|
647
|
+
{
|
|
648
|
+
"success": false,
|
|
649
|
+
"error": "API limit exceeded",
|
|
650
|
+
"message": "You have reached your monthly limit of 1500 API calls",
|
|
651
|
+
"currentUsage": 1500,
|
|
652
|
+
"limit": 1500,
|
|
653
|
+
"resetIn": "13 days",
|
|
654
|
+
"resetAt": "2026-02-01T00:00:00.000Z",
|
|
655
|
+
"upgrade": {
|
|
656
|
+
"message": "Upgrade to Premium for 10,000 calls/month",
|
|
657
|
+
"url": "https://botgate.com/pricing"
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## 📝 Tipos TypeScript
|
|
663
|
+
|
|
664
|
+
Todos os tipos estão exportados e disponíveis:
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
import {
|
|
668
|
+
BotGateReporter,
|
|
669
|
+
BotGateConfig,
|
|
670
|
+
BotStats,
|
|
671
|
+
BotGateResponse,
|
|
672
|
+
BotInfo,
|
|
673
|
+
createReporter,
|
|
674
|
+
} from "@botgate/stats-reporter";
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
## 🤝 Contribuindo
|
|
678
|
+
|
|
679
|
+
Contribuições são bem-vindas! Por favor:
|
|
680
|
+
|
|
681
|
+
1. Fork o repositório
|
|
682
|
+
2. Crie uma branch para sua feature (`git checkout -b feature/MinhaFeature`)
|
|
683
|
+
3. Commit suas mudanças (`git commit -m 'Adiciona MinhaFeature'`)
|
|
684
|
+
4. Push para a branch (`git push origin feature/MinhaFeature`)
|
|
685
|
+
5. Abra um Pull Request
|
|
686
|
+
|
|
687
|
+
## 🔗 Links
|
|
688
|
+
|
|
689
|
+
- [Website do BotGate](https://www.botgate.coden8n.shop/)
|
|
690
|
+
- [Discord de Suporte](https://discord.gg/xK4r9HqKKf)
|
|
691
|
+
- [GitHub](https://github.com/nathan-lucca/botgate-stats-reporter)
|
|
692
|
+
- [NPM](https://www.npmjs.com/package/@botgate/stats-reporter)
|
|
693
|
+
|
|
694
|
+
## 💬 Suporte
|
|
695
|
+
|
|
696
|
+
Precisa de ajuda? Entre em contato:
|
|
697
|
+
|
|
698
|
+
- 💬 Discord: [Servidor de Suporte](https://discord.gg/xK4r9HqKKf)
|
|
699
|
+
- 🌐 Website: [BotGate](https://www.botgate.coden8n.shop/)
|
|
700
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/nathan-lucca/botgate-stats-reporter/issues)
|
|
701
|
+
|
|
702
|
+
## 🙏 Agradecimentos
|
|
703
|
+
|
|
704
|
+
Obrigado por usar o BotGate Stats Reporter! ❤️
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
Feito com ❤️ pela equipe BotGate
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Client } from "discord.js";
|
|
2
|
+
export interface BotGateConfig {
|
|
3
|
+
botId: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
debug?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface BotStats {
|
|
8
|
+
botId: string;
|
|
9
|
+
serverCount: number;
|
|
10
|
+
userCount: number;
|
|
11
|
+
shardCount: number;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
}
|
|
14
|
+
export interface BotGateResponse<T = any> {
|
|
15
|
+
success: boolean;
|
|
16
|
+
message?: string;
|
|
17
|
+
data?: T;
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare class BotGateReporter {
|
|
21
|
+
private client;
|
|
22
|
+
private config;
|
|
23
|
+
private axios;
|
|
24
|
+
private statsIntervalId;
|
|
25
|
+
private heartbeatIntervalId;
|
|
26
|
+
private isRunning;
|
|
27
|
+
private failedAttempts;
|
|
28
|
+
private currentTier;
|
|
29
|
+
constructor(config: BotGateConfig);
|
|
30
|
+
start(client: Client): void;
|
|
31
|
+
stop(): void;
|
|
32
|
+
sendStats(): Promise<BotGateResponse>;
|
|
33
|
+
sendHeartbeat(): Promise<BotGateResponse>;
|
|
34
|
+
verifyApiKey(): Promise<boolean>;
|
|
35
|
+
getBotInfo(botId?: string): Promise<BotGateResponse>;
|
|
36
|
+
getBotVotes(botId?: string, limit?: number): Promise<BotGateResponse>;
|
|
37
|
+
getBotAnalytics(botId?: string): Promise<BotGateResponse>;
|
|
38
|
+
getStatsHistory(botId?: string, period?: "daily" | "weekly" | "monthly" | "all"): Promise<BotGateResponse>;
|
|
39
|
+
getApiUsage(): Promise<BotGateResponse>;
|
|
40
|
+
private onReady;
|
|
41
|
+
private setupAutoUpdate;
|
|
42
|
+
private manageHeartbeat;
|
|
43
|
+
private collectStats;
|
|
44
|
+
private postWithRetry;
|
|
45
|
+
private syncFromResponse;
|
|
46
|
+
private updateIntervalFromTier;
|
|
47
|
+
private log;
|
|
48
|
+
private formatError;
|
|
49
|
+
}
|
|
50
|
+
export default BotGateReporter;
|
|
51
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAqBpC,MAAM,WAAW,aAAa;IAE5B,KAAK,EAAE,MAAM,CAAC;IAGd,MAAM,EAAE,MAAM,CAAC;IAGf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAmBD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,GAAG;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,mBAAmB,CAA+B;IAC1D,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,WAAW,CAAkB;gBAOzB,MAAM,EAAE,aAAa;IAiC1B,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAkB3B,IAAI,IAAI,IAAI;IAcN,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IAarC,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC;IAOzC,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IAmBhC,UAAU,CACrB,KAAK,GAAE,MAA0B,GAChC,OAAO,CAAC,eAAe,CAAC;IAad,WAAW,CACtB,KAAK,GAAE,MAA0B,EACjC,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,eAAe,CAAC;IAWd,eAAe,CAC1B,KAAK,GAAE,MAA0B,GAChC,OAAO,CAAC,eAAe,CAAC;IASd,eAAe,CAC1B,KAAK,GAAE,MAA0B,EACjC,MAAM,GAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAa,GACrD,OAAO,CAAC,eAAe,CAAC;IAYd,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;YActC,OAAO;IAQrB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,YAAY;YAYN,aAAa;IA6C3B,OAAO,CAAC,gBAAgB;IA4BxB,OAAO,CAAC,sBAAsB;IAgB9B,OAAO,CAAC,GAAG;IAUX,OAAO,CAAC,WAAW;CAMpB;AAED,eAAe,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
export class BotGateReporter {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.client = null;
|
|
5
|
+
this.statsIntervalId = null;
|
|
6
|
+
this.heartbeatIntervalId = null;
|
|
7
|
+
this.isRunning = false;
|
|
8
|
+
this.failedAttempts = 0;
|
|
9
|
+
this.currentTier = "free";
|
|
10
|
+
if (!config.botId)
|
|
11
|
+
throw new Error("[BotGate Reporter] botId is required");
|
|
12
|
+
if (!config.apiKey)
|
|
13
|
+
throw new Error("[BotGate Reporter] apiKey is required");
|
|
14
|
+
this.config = {
|
|
15
|
+
botId: config.botId,
|
|
16
|
+
apiKey: config.apiKey,
|
|
17
|
+
apiUrl: "https://botgate-api-987684559046.us-central1.run.app",
|
|
18
|
+
updateInterval: 30 * 60 * 1000,
|
|
19
|
+
debug: config.debug || false,
|
|
20
|
+
retryAttempts: 3,
|
|
21
|
+
retryDelay: 5000,
|
|
22
|
+
};
|
|
23
|
+
this.axios = axios.create({
|
|
24
|
+
baseURL: this.config.apiUrl,
|
|
25
|
+
timeout: 10000,
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
"User-Agent": `BotGate-Stats-Reporter/1.1.0 (Bot: ${this.config.botId})`,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
this.log("✅ BotGate Reporter initialized", { botId: this.config.botId });
|
|
33
|
+
}
|
|
34
|
+
start(client) {
|
|
35
|
+
if (this.isRunning)
|
|
36
|
+
return;
|
|
37
|
+
this.client = client;
|
|
38
|
+
this.isRunning = true;
|
|
39
|
+
if (client.isReady()) {
|
|
40
|
+
this.onReady();
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
client.once("ready", () => this.onReady());
|
|
44
|
+
}
|
|
45
|
+
this.log("🚀 Reporter started");
|
|
46
|
+
}
|
|
47
|
+
stop() {
|
|
48
|
+
if (this.statsIntervalId)
|
|
49
|
+
clearInterval(this.statsIntervalId);
|
|
50
|
+
if (this.heartbeatIntervalId)
|
|
51
|
+
clearInterval(this.heartbeatIntervalId);
|
|
52
|
+
this.statsIntervalId = null;
|
|
53
|
+
this.heartbeatIntervalId = null;
|
|
54
|
+
this.isRunning = false;
|
|
55
|
+
this.log("🛑 Reporter stopped");
|
|
56
|
+
}
|
|
57
|
+
async sendStats() {
|
|
58
|
+
if (!this.client?.isReady()) {
|
|
59
|
+
throw new Error("[BotGate Reporter] Discord client is not ready");
|
|
60
|
+
}
|
|
61
|
+
const stats = this.collectStats();
|
|
62
|
+
return await this.postWithRetry("/api/v1/bots/stats", stats);
|
|
63
|
+
}
|
|
64
|
+
async sendHeartbeat() {
|
|
65
|
+
return await this.postWithRetry("/api/v1/heartbeat", {});
|
|
66
|
+
}
|
|
67
|
+
async verifyApiKey() {
|
|
68
|
+
try {
|
|
69
|
+
const response = await this.axios.get("/api/v1/verify");
|
|
70
|
+
if (response.data.success && response.data.data?.tier) {
|
|
71
|
+
this.syncFromResponse(response.data.data);
|
|
72
|
+
}
|
|
73
|
+
return response.data.success === true;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.log("❌ API key verification failed", this.formatError(error));
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async getBotInfo(botId = this.config.botId) {
|
|
81
|
+
const response = await this.axios.get(`/api/v1/bots/${botId}`);
|
|
82
|
+
if (response.data.success) {
|
|
83
|
+
this.syncFromResponse(response.data.data);
|
|
84
|
+
}
|
|
85
|
+
return response.data;
|
|
86
|
+
}
|
|
87
|
+
async getBotVotes(botId = this.config.botId, limit = 10) {
|
|
88
|
+
const response = await this.axios.get(`/api/v1/bots/${botId}/votes`, {
|
|
89
|
+
params: { limit },
|
|
90
|
+
});
|
|
91
|
+
return response.data;
|
|
92
|
+
}
|
|
93
|
+
async getBotAnalytics(botId = this.config.botId) {
|
|
94
|
+
const response = await this.axios.get(`/api/v1/bots/${botId}/analytics`);
|
|
95
|
+
return response.data;
|
|
96
|
+
}
|
|
97
|
+
async getStatsHistory(botId = this.config.botId, period = "all") {
|
|
98
|
+
const response = await this.axios.get(`/api/v1/bots/${botId}/stats/history`, { params: { period } });
|
|
99
|
+
return response.data;
|
|
100
|
+
}
|
|
101
|
+
async getApiUsage() {
|
|
102
|
+
const response = await this.axios.get("/api/v1/usage");
|
|
103
|
+
if (response.data.success) {
|
|
104
|
+
this.syncFromResponse(response.data.data);
|
|
105
|
+
}
|
|
106
|
+
return response.data;
|
|
107
|
+
}
|
|
108
|
+
async onReady() {
|
|
109
|
+
this.log(`🤖 Bot ready: ${this.client?.user?.tag}`);
|
|
110
|
+
await this.verifyApiKey();
|
|
111
|
+
await this.sendStats();
|
|
112
|
+
}
|
|
113
|
+
setupAutoUpdate() {
|
|
114
|
+
if (this.statsIntervalId)
|
|
115
|
+
clearInterval(this.statsIntervalId);
|
|
116
|
+
this.statsIntervalId = setInterval(() => this.sendStats(), this.config.updateInterval);
|
|
117
|
+
this.log(`⏰ Auto-stats enabled (${this.config.updateInterval / 60000} min)`);
|
|
118
|
+
}
|
|
119
|
+
manageHeartbeat() {
|
|
120
|
+
if (this.currentTier === "business") {
|
|
121
|
+
if (this.heartbeatIntervalId)
|
|
122
|
+
return;
|
|
123
|
+
this.sendHeartbeat();
|
|
124
|
+
this.heartbeatIntervalId = setInterval(() => this.sendHeartbeat(), 5 * 60 * 1000);
|
|
125
|
+
this.log("💓 Business Heartbeat enabled (every 5 min)");
|
|
126
|
+
}
|
|
127
|
+
else if (this.heartbeatIntervalId) {
|
|
128
|
+
clearInterval(this.heartbeatIntervalId);
|
|
129
|
+
this.heartbeatIntervalId = null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
collectStats() {
|
|
133
|
+
const guilds = this.client.guilds.cache;
|
|
134
|
+
return {
|
|
135
|
+
botId: this.config.botId,
|
|
136
|
+
serverCount: guilds.size,
|
|
137
|
+
userCount: guilds.reduce((acc, g) => acc + (g.memberCount || 0), 0),
|
|
138
|
+
shardCount: this.client.shard?.count || 1,
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async postWithRetry(url, data, attempt = 1) {
|
|
143
|
+
try {
|
|
144
|
+
const response = await this.axios.post(url, data);
|
|
145
|
+
const responseData = response.data;
|
|
146
|
+
if (responseData.success) {
|
|
147
|
+
this.syncFromResponse(responseData.data);
|
|
148
|
+
}
|
|
149
|
+
this.failedAttempts = 0;
|
|
150
|
+
return { success: true, data: responseData };
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
const status = error.response?.status;
|
|
154
|
+
if (status === 403 || status === 429) {
|
|
155
|
+
this.log(`⚠️ Tier/Frequency limit reached (${status}). Syncing and waiting for next cycle...`);
|
|
156
|
+
await this.verifyApiKey();
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
error: `Rate limited or tier mismatch (${status})`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (attempt < this.config.retryAttempts) {
|
|
163
|
+
await new Promise((r) => setTimeout(r, this.config.retryDelay));
|
|
164
|
+
return this.postWithRetry(url, data, attempt + 1);
|
|
165
|
+
}
|
|
166
|
+
this.failedAttempts++;
|
|
167
|
+
return { success: false, error: error.message };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
syncFromResponse(data) {
|
|
171
|
+
if (!data)
|
|
172
|
+
return;
|
|
173
|
+
const tierObject = data.tier || data;
|
|
174
|
+
const tierName = tierObject.name || tierObject.tier || data.tier;
|
|
175
|
+
const intervalMinutes = tierObject.updateIntervalMinutes ||
|
|
176
|
+
data.updates?.updateIntervalMinutes ||
|
|
177
|
+
data.capabilities?.updateIntervalMinutes;
|
|
178
|
+
if (tierName && tierName !== this.currentTier) {
|
|
179
|
+
this.log(`🔄 Plan change detected: ${this.currentTier} -> ${tierName}`);
|
|
180
|
+
this.currentTier = tierName;
|
|
181
|
+
if (intervalMinutes) {
|
|
182
|
+
this.updateIntervalFromTier({
|
|
183
|
+
updateInterval: `${intervalMinutes} minutes`,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
this.manageHeartbeat();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
updateIntervalFromTier(tierData) {
|
|
190
|
+
const minutes = parseInt(tierData.updateInterval?.replace(" minutes", "") || "30");
|
|
191
|
+
const newInterval = minutes * 60 * 1000;
|
|
192
|
+
if (this.config.updateInterval !== newInterval) {
|
|
193
|
+
this.config.updateInterval = newInterval;
|
|
194
|
+
if (this.isRunning) {
|
|
195
|
+
this.setupAutoUpdate();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
log(message, data) {
|
|
200
|
+
if (this.config.debug) {
|
|
201
|
+
console.log(`[BotGate Reporter] [${new Date().toISOString()}] ${message}`);
|
|
202
|
+
if (data)
|
|
203
|
+
console.log(JSON.stringify(data, null, 2));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
formatError(error) {
|
|
207
|
+
return {
|
|
208
|
+
message: error.response?.data?.message || error.message,
|
|
209
|
+
status: error.response?.status,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
export default BotGateReporter;
|
|
214
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@botgate/botgate-stats-reporter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Módulo oficial do BotGate para reportar estatísticas do seu bot Discord automaticamente",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"prepublishOnly": "npm run build",
|
|
19
|
+
"test": "jest",
|
|
20
|
+
"lint": "eslint src/**/*.ts"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"discord",
|
|
24
|
+
"bot",
|
|
25
|
+
"botgate",
|
|
26
|
+
"stats",
|
|
27
|
+
"statistics",
|
|
28
|
+
"discord.js",
|
|
29
|
+
"monitoring"
|
|
30
|
+
],
|
|
31
|
+
"author": "BotGate Team",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/nathan-lucca/botgate-stats-reporter.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/nathan-lucca/botgate-stats-reporter/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "http://docs-botgate.vercel.app/",
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"discord.js": "^14.0.0"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"axios": "^1.6.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.0.0",
|
|
49
|
+
"typescript": "^5.0.0",
|
|
50
|
+
"eslint": "^8.0.0",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
52
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
53
|
+
"jest": "^29.0.0"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=16.0.0"
|
|
57
|
+
},
|
|
58
|
+
"files": [
|
|
59
|
+
"dist",
|
|
60
|
+
"README.md",
|
|
61
|
+
"LICENSE"
|
|
62
|
+
]
|
|
63
|
+
}
|