@jilimb0/tgwrapper 0.4.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 +38 -0
- package/dist/adapters/aws-lambda-handler.d.ts +29 -0
- package/dist/adapters/aws-lambda-handler.d.ts.map +1 -0
- package/dist/adapters/aws-lambda-handler.js +39 -0
- package/dist/adapters/aws-lambda-handler.js.map +1 -0
- package/dist/adapters/cloudflare-worker-handler.d.ts +7 -0
- package/dist/adapters/cloudflare-worker-handler.d.ts.map +1 -0
- package/dist/adapters/cloudflare-worker-handler.js +24 -0
- package/dist/adapters/cloudflare-worker-handler.js.map +1 -0
- package/dist/adapters/node-http-handler.d.ts +20 -0
- package/dist/adapters/node-http-handler.d.ts.map +1 -0
- package/dist/adapters/node-http-handler.js +41 -0
- package/dist/adapters/node-http-handler.js.map +1 -0
- package/dist/adapters/webhook-handler.d.ts +14 -0
- package/dist/adapters/webhook-handler.d.ts.map +1 -0
- package/dist/adapters/webhook-handler.js +54 -0
- package/dist/adapters/webhook-handler.js.map +1 -0
- package/dist/core/api-client.d.ts +18 -0
- package/dist/core/api-client.d.ts.map +1 -0
- package/dist/core/api-client.js +139 -0
- package/dist/core/api-client.js.map +1 -0
- package/dist/core/bot-kernel.d.ts +29 -0
- package/dist/core/bot-kernel.d.ts.map +1 -0
- package/dist/core/bot-kernel.js +99 -0
- package/dist/core/bot-kernel.js.map +1 -0
- package/dist/core/circuit-breaker.d.ts +19 -0
- package/dist/core/circuit-breaker.d.ts.map +1 -0
- package/dist/core/circuit-breaker.js +59 -0
- package/dist/core/circuit-breaker.js.map +1 -0
- package/dist/core/context.d.ts +29 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +67 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/errors.d.ts +21 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +45 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/fsm/session-manager.d.ts +22 -0
- package/dist/fsm/session-manager.d.ts.map +1 -0
- package/dist/fsm/session-manager.js +79 -0
- package/dist/fsm/session-manager.js.map +1 -0
- package/dist/guards/bounded-concurrency.d.ts +14 -0
- package/dist/guards/bounded-concurrency.d.ts.map +1 -0
- package/dist/guards/bounded-concurrency.js +48 -0
- package/dist/guards/bounded-concurrency.js.map +1 -0
- package/dist/guards/token-bucket-rate-limiter.d.ts +11 -0
- package/dist/guards/token-bucket-rate-limiter.d.ts.map +1 -0
- package/dist/guards/token-bucket-rate-limiter.js +27 -0
- package/dist/guards/token-bucket-rate-limiter.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/observability/ecs-logger.d.ts +18 -0
- package/dist/observability/ecs-logger.d.ts.map +1 -0
- package/dist/observability/ecs-logger.js +42 -0
- package/dist/observability/ecs-logger.js.map +1 -0
- package/dist/observability/metrics.d.ts +12 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +31 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/router/router.d.ts +30 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +62 -0
- package/dist/router/router.js.map +1 -0
- package/dist/runtime/bot-runtime.d.ts +25 -0
- package/dist/runtime/bot-runtime.d.ts.map +1 -0
- package/dist/runtime/bot-runtime.js +59 -0
- package/dist/runtime/bot-runtime.js.map +1 -0
- package/dist/storage/memory-session-storage.d.ts +12 -0
- package/dist/storage/memory-session-storage.d.ts.map +1 -0
- package/dist/storage/memory-session-storage.js +53 -0
- package/dist/storage/memory-session-storage.js.map +1 -0
- package/dist/storage/redis-session-storage.d.ts +45 -0
- package/dist/storage/redis-session-storage.d.ts.map +1 -0
- package/dist/storage/redis-session-storage.js +101 -0
- package/dist/storage/redis-session-storage.js.map +1 -0
- package/dist/tenant/key-namespace.d.ts +7 -0
- package/dist/tenant/key-namespace.d.ts.map +1 -0
- package/dist/tenant/key-namespace.js +7 -0
- package/dist/tenant/key-namespace.js.map +1 -0
- package/dist/types/core.d.ts +106 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/core.js +2 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/telegram.d.ts +2 -0
- package/dist/types/telegram.d.ts.map +1 -0
- package/dist/types/telegram.js +2 -0
- package/dist/types/telegram.js.map +1 -0
- package/dist/update-loop/polling.d.ts +13 -0
- package/dist/update-loop/polling.d.ts.map +1 -0
- package/dist/update-loop/polling.js +40 -0
- package/dist/update-loop/polling.js.map +1 -0
- package/dist/update-loop/update-validator.d.ts +4 -0
- package/dist/update-loop/update-validator.d.ts.map +1 -0
- package/dist/update-loop/update-validator.js +17 -0
- package/dist/update-loop/update-validator.js.map +1 -0
- package/dist/update-loop/webhook.d.ts +15 -0
- package/dist/update-loop/webhook.d.ts.map +1 -0
- package/dist/update-loop/webhook.js +30 -0
- package/dist/update-loop/webhook.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Framework Core
|
|
2
|
+
|
|
3
|
+
Production-grade Telegram Bot Framework with serverless-first architecture, native FSM, optimistic locking, and ecosystem packages.
|
|
4
|
+
|
|
5
|
+
## Packages
|
|
6
|
+
|
|
7
|
+
- `@jilimb0/tgwrapper` - framework kernel, transport, router, FSM, adapters
|
|
8
|
+
- `@jilimb0/tgwrapper-adapter-redis` - ioredis-backed atomic session storage
|
|
9
|
+
- `@jilimb0/tgwrapper-observability` - ECS JSON logger and metrics helpers
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm install
|
|
15
|
+
pnpm test
|
|
16
|
+
pnpm build
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Integration tests (Redis)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
docker compose -f docker-compose.redis.yml up -d
|
|
23
|
+
REDIS_URL=redis://127.0.0.1:6379 pnpm test:integration
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Deploy examples
|
|
27
|
+
|
|
28
|
+
- Node HTTP: `pnpm --dir examples/node-http dev`
|
|
29
|
+
- AWS Lambda: `pnpm --dir examples/aws-lambda deploy`
|
|
30
|
+
- Cloudflare Worker: `pnpm --dir examples/cloudflare-worker deploy`
|
|
31
|
+
|
|
32
|
+
All examples require env vars (`BOT_TOKEN`, `WEBHOOK_SECRET`) and do not hardcode secrets.
|
|
33
|
+
|
|
34
|
+
## Release
|
|
35
|
+
|
|
36
|
+
Releases are CI-gated only via Changesets workflow.
|
|
37
|
+
Manual local publishing is not part of supported flow.
|
|
38
|
+
See `/Users/jilimbo/Documents/Personal/TGWrapper/docs/RELEASE_POLICY.md` for semver and process details.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { WebhookHandler } from './webhook-handler.js';
|
|
2
|
+
export interface ApiGatewayV2Event {
|
|
3
|
+
version: string;
|
|
4
|
+
routeKey: string;
|
|
5
|
+
rawPath: string;
|
|
6
|
+
rawQueryString: string;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
requestContext: {
|
|
9
|
+
http: {
|
|
10
|
+
method: string;
|
|
11
|
+
path: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
body?: string;
|
|
15
|
+
isBase64Encoded: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ApiGatewayV2Response {
|
|
18
|
+
statusCode: number;
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
body: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class AwsLambdaHandler {
|
|
23
|
+
private readonly handler;
|
|
24
|
+
constructor(handler: WebhookHandler);
|
|
25
|
+
handle(event: ApiGatewayV2Event): Promise<ApiGatewayV2Response>;
|
|
26
|
+
private normalizeHeaders;
|
|
27
|
+
private decodeBase64;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=aws-lambda-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-lambda-handler.d.ts","sourceRoot":"","sources":["../../src/adapters/aws-lambda-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,EAAE;QACd,IAAI,EAAE;YACJ,MAAM,EAAE,MAAM,CAAC;YACf,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;KACH,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;gBAEtB,OAAO,EAAE,cAAc;IAI7B,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAsB5E,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,YAAY;CAOrB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export class AwsLambdaHandler {
|
|
2
|
+
handler;
|
|
3
|
+
constructor(handler) {
|
|
4
|
+
this.handler = handler;
|
|
5
|
+
}
|
|
6
|
+
async handle(event) {
|
|
7
|
+
const rawBody = event.body
|
|
8
|
+
? event.isBase64Encoded
|
|
9
|
+
? this.decodeBase64(event.body)
|
|
10
|
+
: event.body
|
|
11
|
+
: '';
|
|
12
|
+
const request = {
|
|
13
|
+
method: event.requestContext.http.method,
|
|
14
|
+
headers: this.normalizeHeaders(event.headers ?? {}),
|
|
15
|
+
rawBody,
|
|
16
|
+
path: event.rawPath
|
|
17
|
+
};
|
|
18
|
+
const response = await this.handler.handle(request);
|
|
19
|
+
return {
|
|
20
|
+
statusCode: response.status,
|
|
21
|
+
headers: response.headers,
|
|
22
|
+
body: response.body
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
normalizeHeaders(headers) {
|
|
26
|
+
const normalized = {};
|
|
27
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
28
|
+
normalized[key.toLowerCase()] = value;
|
|
29
|
+
}
|
|
30
|
+
return normalized;
|
|
31
|
+
}
|
|
32
|
+
decodeBase64(value) {
|
|
33
|
+
if (typeof atob === 'function') {
|
|
34
|
+
return atob(value);
|
|
35
|
+
}
|
|
36
|
+
throw new Error('Base64 decoding is not available in this runtime.');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=aws-lambda-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-lambda-handler.js","sourceRoot":"","sources":["../../src/adapters/aws-lambda-handler.ts"],"names":[],"mappings":"AAyBA,MAAM,OAAO,gBAAgB;IACV,OAAO,CAAiB;IAEzC,YAAmB,OAAuB;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,KAAwB;QAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI;YACxB,CAAC,CAAC,KAAK,CAAC,eAAe;gBACrB,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC;gBAC/B,CAAC,CAAC,KAAK,CAAC,IAAI;YACd,CAAC,CAAC,EAAE,CAAC;QAEP,MAAM,OAAO,GAAmB;YAC9B,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM;YACxC,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;YACnD,OAAO;YACP,IAAI,EAAE,KAAK,CAAC,OAAO;SACpB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpD,OAAO;YACL,UAAU,EAAE,QAAQ,CAAC,MAAM;YAC3B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC;IACJ,CAAC;IAEO,gBAAgB,CAAC,OAA+B;QACtD,MAAM,UAAU,GAAuC,EAAE,CAAC;QAC1D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,UAAU,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;QACxC,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAEO,YAAY,CAAC,KAAa;QAChC,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { WebhookHandler } from './webhook-handler.js';
|
|
2
|
+
export declare class CloudflareWorkerHandler {
|
|
3
|
+
private readonly handler;
|
|
4
|
+
constructor(handler: WebhookHandler);
|
|
5
|
+
handle(request: Request): Promise<Response>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=cloudflare-worker-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-worker-handler.d.ts","sourceRoot":"","sources":["../../src/adapters/cloudflare-worker-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;gBAEtB,OAAO,EAAE,cAAc;IAI7B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAmBzD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class CloudflareWorkerHandler {
|
|
2
|
+
handler;
|
|
3
|
+
constructor(handler) {
|
|
4
|
+
this.handler = handler;
|
|
5
|
+
}
|
|
6
|
+
async handle(request) {
|
|
7
|
+
const headers = {};
|
|
8
|
+
request.headers.forEach((value, key) => {
|
|
9
|
+
headers[key] = value;
|
|
10
|
+
});
|
|
11
|
+
const mapped = {
|
|
12
|
+
method: request.method,
|
|
13
|
+
headers,
|
|
14
|
+
rawBody: await request.text(),
|
|
15
|
+
path: new URL(request.url).pathname
|
|
16
|
+
};
|
|
17
|
+
const response = await this.handler.handle(mapped);
|
|
18
|
+
return new Response(response.body, {
|
|
19
|
+
status: response.status,
|
|
20
|
+
headers: response.headers
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=cloudflare-worker-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-worker-handler.js","sourceRoot":"","sources":["../../src/adapters/cloudflare-worker-handler.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,uBAAuB;IACjB,OAAO,CAAiB;IAEzC,YAAmB,OAAuB;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,OAAgB;QAClC,MAAM,OAAO,GAAuC,EAAE,CAAC;QACvD,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACrC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAmB;YAC7B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO;YACP,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,EAAE;YAC7B,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ;SACpC,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACnD,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;YACjC,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;SAC1B,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { WebhookHandler } from './webhook-handler.js';
|
|
2
|
+
export interface NodeLikeIncomingMessage {
|
|
3
|
+
method?: string;
|
|
4
|
+
headers: Record<string, string | string[] | undefined>;
|
|
5
|
+
on(event: 'data', listener: (chunk: unknown) => void): void;
|
|
6
|
+
on(event: 'end', listener: () => void): void;
|
|
7
|
+
on(event: 'error', listener: (error: Error) => void): void;
|
|
8
|
+
}
|
|
9
|
+
export interface NodeLikeServerResponse {
|
|
10
|
+
statusCode: number;
|
|
11
|
+
setHeader(name: string, value: string): void;
|
|
12
|
+
end(body?: string): void;
|
|
13
|
+
}
|
|
14
|
+
export declare class NodeHttpHandler {
|
|
15
|
+
private readonly handler;
|
|
16
|
+
constructor(handler: WebhookHandler);
|
|
17
|
+
handle(req: NodeLikeIncomingMessage, res: NodeLikeServerResponse): Promise<void>;
|
|
18
|
+
private toChunkString;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=node-http-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-http-handler.d.ts","sourceRoot":"","sources":["../../src/adapters/node-http-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5D,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC7C,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;CAC5D;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;gBAEtB,OAAO,EAAE,cAAc;IAI7B,MAAM,CAAC,GAAG,EAAE,uBAAuB,EAAE,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B7F,OAAO,CAAC,aAAa;CAWtB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export class NodeHttpHandler {
|
|
2
|
+
handler;
|
|
3
|
+
constructor(handler) {
|
|
4
|
+
this.handler = handler;
|
|
5
|
+
}
|
|
6
|
+
async handle(req, res) {
|
|
7
|
+
const chunks = [];
|
|
8
|
+
await new Promise((resolve, reject) => {
|
|
9
|
+
req.on('data', (chunk) => {
|
|
10
|
+
chunks.push(this.toChunkString(chunk));
|
|
11
|
+
});
|
|
12
|
+
req.on('end', () => resolve());
|
|
13
|
+
req.on('error', (error) => reject(error));
|
|
14
|
+
});
|
|
15
|
+
const headers = {};
|
|
16
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
17
|
+
headers[key] = Array.isArray(value) ? value[0] : value;
|
|
18
|
+
}
|
|
19
|
+
const request = {
|
|
20
|
+
method: req.method ?? 'GET',
|
|
21
|
+
headers,
|
|
22
|
+
rawBody: chunks.join('')
|
|
23
|
+
};
|
|
24
|
+
const response = await this.handler.handle(request);
|
|
25
|
+
res.statusCode = response.status;
|
|
26
|
+
for (const [header, value] of Object.entries(response.headers)) {
|
|
27
|
+
res.setHeader(header, value);
|
|
28
|
+
}
|
|
29
|
+
res.end(response.body);
|
|
30
|
+
}
|
|
31
|
+
toChunkString(chunk) {
|
|
32
|
+
if (typeof chunk === 'string') {
|
|
33
|
+
return chunk;
|
|
34
|
+
}
|
|
35
|
+
if (chunk instanceof Uint8Array) {
|
|
36
|
+
return new TextDecoder().decode(chunk);
|
|
37
|
+
}
|
|
38
|
+
return String(chunk);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=node-http-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-http-handler.js","sourceRoot":"","sources":["../../src/adapters/node-http-handler.ts"],"names":[],"mappings":"AAiBA,MAAM,OAAO,eAAe;IACT,OAAO,CAAiB;IAEzC,YAAmB,OAAuB;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,GAA4B,EAAE,GAA2B;QAC3E,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAc,EAAE,EAAE;gBAChC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/B,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAuC,EAAE,CAAC;QACvD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACzD,CAAC;QAED,MAAM,OAAO,GAAmB;YAC9B,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,KAAK;YAC3B,OAAO;YACP,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;SACzB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpD,GAAG,CAAC,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;QACjC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAEO,aAAa,CAAC,KAAc;QAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;YAChC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { WebhookHandlerOptions, WebhookRequest, WebhookResponse } from '../types/core.js';
|
|
2
|
+
import type { Update } from '../types/telegram.js';
|
|
3
|
+
export interface UpdateHandler {
|
|
4
|
+
handleUpdate(update: Update): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare class WebhookHandler {
|
|
7
|
+
private readonly options;
|
|
8
|
+
private readonly updateHandler;
|
|
9
|
+
constructor(updateHandler: UpdateHandler, options: WebhookHandlerOptions);
|
|
10
|
+
handle(request: WebhookRequest): Promise<WebhookResponse>;
|
|
11
|
+
private response;
|
|
12
|
+
private readHeader;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=webhook-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/adapters/webhook-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAGnD,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;gBAE3B,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,qBAAqB;IAQlE,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IA+BtE,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,UAAU;CAGnB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { CoreError } from '../core/errors.js';
|
|
2
|
+
import { isValidTelegramUpdate } from '../update-loop/update-validator.js';
|
|
3
|
+
export class WebhookHandler {
|
|
4
|
+
options;
|
|
5
|
+
updateHandler;
|
|
6
|
+
constructor(updateHandler, options) {
|
|
7
|
+
this.updateHandler = updateHandler;
|
|
8
|
+
this.options = {
|
|
9
|
+
secretToken: options.secretToken,
|
|
10
|
+
secretHeader: options.secretHeader ?? 'x-telegram-bot-api-secret-token'
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
async handle(request) {
|
|
14
|
+
if (request.method.toUpperCase() !== 'POST') {
|
|
15
|
+
return this.response(405, { error: 'method_not_allowed' });
|
|
16
|
+
}
|
|
17
|
+
const headerValue = this.readHeader(request.headers, this.options.secretHeader);
|
|
18
|
+
if (!headerValue || headerValue !== this.options.secretToken) {
|
|
19
|
+
return this.response(401, { error: 'invalid_signature' });
|
|
20
|
+
}
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(request.rawBody);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return this.response(400, { error: 'invalid_json' });
|
|
27
|
+
}
|
|
28
|
+
if (!isValidTelegramUpdate(parsed)) {
|
|
29
|
+
return this.response(400, { error: 'invalid_update' });
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
await this.updateHandler.handleUpdate(parsed);
|
|
33
|
+
return this.response(200, { ok: true });
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const message = error instanceof Error ? error.message : 'unknown';
|
|
37
|
+
const code = error instanceof CoreError ? error.code : 'INTERNAL';
|
|
38
|
+
return this.response(500, { error: 'handler_failed', code, message });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
response(status, body) {
|
|
42
|
+
return {
|
|
43
|
+
status,
|
|
44
|
+
headers: {
|
|
45
|
+
'content-type': 'application/json; charset=utf-8'
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify(body)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
readHeader(headers, name) {
|
|
51
|
+
return headers[name] ?? headers[name.toLowerCase()] ?? headers[name.toUpperCase()];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=webhook-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-handler.js","sourceRoot":"","sources":["../../src/adapters/webhook-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAM3E,MAAM,OAAO,cAAc;IACR,OAAO,CAAkC;IACzC,aAAa,CAAgB;IAE9C,YAAmB,aAA4B,EAAE,OAA8B;QAC7E,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG;YACb,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,iCAAiC;SACxE,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,OAAuB;QACzC,IAAI,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAChF,IAAI,CAAC,WAAW,IAAI,WAAW,KAAK,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;YACnE,MAAM,IAAI,GAAG,KAAK,YAAY,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC;YAClE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,MAAc,EAAE,IAA6B;QAC5D,OAAO;YACL,MAAM;YACN,OAAO,EAAE;gBACP,cAAc,EAAE,iCAAiC;aAClD;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC;IACJ,CAAC;IAEO,UAAU,CAAC,OAA2C,EAAE,IAAY;QAC1E,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACrF,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ApiClientOptions, JsonObject } from '../types/core.js';
|
|
2
|
+
export declare class ApiClient {
|
|
3
|
+
private readonly token;
|
|
4
|
+
private readonly baseUrl;
|
|
5
|
+
private readonly logger?;
|
|
6
|
+
private readonly metrics?;
|
|
7
|
+
private readonly retry;
|
|
8
|
+
private readonly fetchImpl;
|
|
9
|
+
private readonly mockResponder?;
|
|
10
|
+
private readonly circuitBreaker;
|
|
11
|
+
constructor(options: ApiClientOptions);
|
|
12
|
+
callApi<TResponse>(method: string, payload: JsonObject): Promise<TResponse>;
|
|
13
|
+
private executeRequest;
|
|
14
|
+
private isRetryable;
|
|
15
|
+
private resolveRetryDelay;
|
|
16
|
+
private delay;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/core/api-client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAC;AASnF,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAA8B;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAoC;IACnE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;gBAE7B,OAAO,EAAE,gBAAgB;IAW/B,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;YAgE1E,cAAc;IAsC5B,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,iBAAiB;YAkBX,KAAK;CAKpB"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { CircuitBreaker } from './circuit-breaker.js';
|
|
2
|
+
import { CircuitOpenError, CoreError, TelegramApiError } from './errors.js';
|
|
3
|
+
const DEFAULT_RETRY = {
|
|
4
|
+
maxRetries: 3,
|
|
5
|
+
baseDelayMs: 300,
|
|
6
|
+
maxDelayMs: 5_000,
|
|
7
|
+
jitterRatio: 0.2
|
|
8
|
+
};
|
|
9
|
+
export class ApiClient {
|
|
10
|
+
token;
|
|
11
|
+
baseUrl;
|
|
12
|
+
logger;
|
|
13
|
+
metrics;
|
|
14
|
+
retry;
|
|
15
|
+
fetchImpl;
|
|
16
|
+
mockResponder;
|
|
17
|
+
circuitBreaker;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.token = options.token;
|
|
20
|
+
this.baseUrl = options.baseUrl ?? 'https://api.telegram.org';
|
|
21
|
+
this.logger = options.logger;
|
|
22
|
+
this.metrics = options.metrics;
|
|
23
|
+
this.retry = { ...DEFAULT_RETRY, ...options.retry };
|
|
24
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
25
|
+
this.mockResponder = options.mockResponder;
|
|
26
|
+
this.circuitBreaker = new CircuitBreaker(options.circuitBreaker);
|
|
27
|
+
}
|
|
28
|
+
async callApi(method, payload) {
|
|
29
|
+
const requestId = crypto.randomUUID();
|
|
30
|
+
const started = Date.now();
|
|
31
|
+
for (let attempt = 1; attempt <= this.retry.maxRetries + 1; attempt += 1) {
|
|
32
|
+
try {
|
|
33
|
+
this.circuitBreaker.beforeRequest(Date.now());
|
|
34
|
+
const response = await this.executeRequest(method, payload);
|
|
35
|
+
const durationMs = Date.now() - started;
|
|
36
|
+
this.circuitBreaker.onSuccess();
|
|
37
|
+
this.metrics?.observe('telegram_api_latency_ms', durationMs, { method });
|
|
38
|
+
this.logger?.log({
|
|
39
|
+
level: 'info',
|
|
40
|
+
event: 'telegram_api_call_success',
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
requestId,
|
|
43
|
+
data: {
|
|
44
|
+
method,
|
|
45
|
+
attempt,
|
|
46
|
+
duration_ms: durationMs
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return response;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof CircuitOpenError) {
|
|
53
|
+
this.metrics?.increment('circuit_breaker_open_count', 1, { method });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.circuitBreaker.onFailure(Date.now());
|
|
57
|
+
}
|
|
58
|
+
const retryDelay = this.resolveRetryDelay(error, attempt);
|
|
59
|
+
const shouldRetry = retryDelay !== null && attempt <= this.retry.maxRetries;
|
|
60
|
+
if (shouldRetry) {
|
|
61
|
+
this.metrics?.increment('transport_retries', 1, { method });
|
|
62
|
+
}
|
|
63
|
+
this.logger?.log({
|
|
64
|
+
level: 'error',
|
|
65
|
+
event: 'telegram_api_call_error',
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
requestId,
|
|
68
|
+
data: {
|
|
69
|
+
method,
|
|
70
|
+
attempt,
|
|
71
|
+
duration_ms: Date.now() - started,
|
|
72
|
+
retry_ms: retryDelay ?? 0,
|
|
73
|
+
retrying: shouldRetry,
|
|
74
|
+
message: error instanceof Error ? error.message : 'unknown'
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (!shouldRetry) {
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
await this.delay(retryDelay);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw new CoreError('NETWORK_ERROR', 'Unexpected retry loop state', false);
|
|
84
|
+
}
|
|
85
|
+
async executeRequest(method, payload) {
|
|
86
|
+
if (this.mockResponder) {
|
|
87
|
+
return this.mockResponder(method, payload);
|
|
88
|
+
}
|
|
89
|
+
let response;
|
|
90
|
+
try {
|
|
91
|
+
response = await this.fetchImpl(`${this.baseUrl}/bot${this.token}/${method}`, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'content-type': 'application/json'
|
|
95
|
+
},
|
|
96
|
+
body: JSON.stringify(payload)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : 'Unknown network error';
|
|
101
|
+
throw new CoreError('NETWORK_ERROR', message, true);
|
|
102
|
+
}
|
|
103
|
+
const body = (await response.json());
|
|
104
|
+
if (!body.ok) {
|
|
105
|
+
throw new TelegramApiError(body.error_code ?? response.status, body.description ?? 'Unknown error', body.parameters);
|
|
106
|
+
}
|
|
107
|
+
if (!('result' in body)) {
|
|
108
|
+
throw new CoreError('VALIDATION_ERROR', 'Telegram API response has no result field.', false);
|
|
109
|
+
}
|
|
110
|
+
return body.result;
|
|
111
|
+
}
|
|
112
|
+
isRetryable(error) {
|
|
113
|
+
if (error instanceof CoreError) {
|
|
114
|
+
return error.retryable;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
resolveRetryDelay(error, attempt) {
|
|
119
|
+
if (error instanceof TelegramApiError && error.errorCode === 429) {
|
|
120
|
+
const retryAfter = error.parameters?.retry_after;
|
|
121
|
+
if (typeof retryAfter === 'number') {
|
|
122
|
+
return retryAfter * 1000;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (this.isRetryable(error)) {
|
|
126
|
+
const exponential = this.retry.baseDelayMs * Math.pow(2, attempt - 1);
|
|
127
|
+
const bounded = Math.min(exponential, this.retry.maxDelayMs);
|
|
128
|
+
const jitterFactor = 1 + (Math.random() * 2 - 1) * this.retry.jitterRatio;
|
|
129
|
+
return Math.max(0, Math.floor(bounded * jitterFactor));
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
async delay(ms) {
|
|
134
|
+
await new Promise((resolve) => {
|
|
135
|
+
setTimeout(resolve, ms);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/core/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG5E,MAAM,aAAa,GAAiB;IAClC,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,KAAK;IACjB,WAAW,EAAE,GAAG;CACjB,CAAC;AAEF,MAAM,OAAO,SAAS;IACH,KAAK,CAAS;IACd,OAAO,CAAS;IAChB,MAAM,CAA8B;IACpC,OAAO,CAA+B;IACtC,KAAK,CAAe;IACpB,SAAS,CAAe;IACxB,aAAa,CAAqC;IAClD,cAAc,CAAiB;IAEhD,YAAmB,OAAyB;QAC1C,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,0BAA0B,CAAC;QAC7D,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACpD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;QAC5C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACnE,CAAC;IAEM,KAAK,CAAC,OAAO,CAAY,MAAc,EAAE,OAAmB;QACjE,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE3B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC;gBACH,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC9C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,CAAY,MAAM,EAAE,OAAO,CAAC,CAAC;gBACvE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;gBACxC,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;gBAEhC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,yBAAyB,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;gBAEzE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC;oBACf,KAAK,EAAE,MAAM;oBACb,KAAK,EAAE,2BAA2B;oBAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,SAAS;oBACT,IAAI,EAAE;wBACJ,MAAM;wBACN,OAAO;wBACP,WAAW,EAAE,UAAU;qBACxB;iBACF,CAAC,CAAC;gBACH,OAAO,QAAQ,CAAC;YAClB,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;oBACtC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,4BAA4B,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;gBACvE,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC5C,CAAC;gBAED,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBAC1D,MAAM,WAAW,GAAG,UAAU,KAAK,IAAI,IAAI,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;gBAC5E,IAAI,WAAW,EAAE,CAAC;oBAChB,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC9D,CAAC;gBAED,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC;oBACf,KAAK,EAAE,OAAO;oBACd,KAAK,EAAE,yBAAyB;oBAChC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,SAAS;oBACT,IAAI,EAAE;wBACJ,MAAM;wBACN,OAAO;wBACP,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;wBACjC,QAAQ,EAAE,UAAU,IAAI,CAAC;wBACzB,QAAQ,EAAE,WAAW;wBACrB,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;qBAC5D;iBACF,CAAC,CAAC;gBAEH,IAAI,CAAC,WAAW,EAAE,CAAC;oBACjB,MAAM,KAAK,CAAC;gBACd,CAAC;gBAED,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,6BAA6B,EAAE,KAAK,CAAC,CAAC;IAC7E,CAAC;IAEO,KAAK,CAAC,cAAc,CAAY,MAAc,EAAE,OAAmB;QACzE,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAuB,CAAC;QACnE,CAAC;QAED,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO,OAAO,IAAI,CAAC,KAAK,IAAI,MAAM,EAAE,EAAE;gBAC5E,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;aAC9B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;YACjF,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAMlC,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,IAAI,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,IAAI,eAAe,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACvH,CAAC;QAED,IAAI,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,SAAS,CAAC,kBAAkB,EAAE,4CAA4C,EAAE,KAAK,CAAC,CAAC;QAC/F,CAAC;QAED,OAAO,IAAI,CAAC,MAAmB,CAAC;IAClC,CAAC;IAEO,WAAW,CAAC,KAAc;QAChC,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;YAC/B,OAAO,KAAK,CAAC,SAAS,CAAC;QACzB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,iBAAiB,CAAC,KAAc,EAAE,OAAe;QACvD,IAAI,KAAK,YAAY,gBAAgB,IAAI,KAAK,CAAC,SAAS,KAAK,GAAG,EAAE,CAAC;YACjE,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,EAAE,WAAW,CAAC;YACjD,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACnC,OAAO,UAAU,GAAG,IAAI,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;YACtE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC7D,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;YAC1E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC;QACzD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,EAAU;QAC5B,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC5B,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Context } from './context.js';
|
|
2
|
+
import type { ApiClient } from './api-client.js';
|
|
3
|
+
import type { JsonObject } from '../types/core.js';
|
|
4
|
+
import type { SessionManager } from '../fsm/session-manager.js';
|
|
5
|
+
import type { TreeRouter } from '../router/router.js';
|
|
6
|
+
import type { Update } from '../types/telegram.js';
|
|
7
|
+
interface BotKernelOptions<TState extends string, TData extends JsonObject> {
|
|
8
|
+
apiClient: ApiClient;
|
|
9
|
+
sessionManager: SessionManager<TState, TData>;
|
|
10
|
+
router: TreeRouter<Context<TState, TData>>;
|
|
11
|
+
resolveSessionKey: (update: Update) => string | null;
|
|
12
|
+
transitions?: Record<TState, readonly TState[]>;
|
|
13
|
+
onTransition?: (from: TState | null, to: TState, sessionKey: string) => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare class BotKernel<TState extends string, TData extends JsonObject> {
|
|
16
|
+
private readonly apiClient;
|
|
17
|
+
private readonly sessionManager;
|
|
18
|
+
private readonly router;
|
|
19
|
+
private readonly resolveSessionKey;
|
|
20
|
+
private readonly transitions;
|
|
21
|
+
private readonly onTransition;
|
|
22
|
+
constructor(options: BotKernelOptions<TState, TData>);
|
|
23
|
+
handleUpdate(update: Update): Promise<void>;
|
|
24
|
+
private extractRoutingMeta;
|
|
25
|
+
private extractCommand;
|
|
26
|
+
private assertTransitionAllowed;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=bot-kernel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot-kernel.d.ts","sourceRoot":"","sources":["../../src/core/bot-kernel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,UAAU,gBAAgB,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,SAAS,UAAU;IACxE,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC9C,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IAC3C,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvF;AAED,qBAAa,SAAS,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,SAAS,UAAU;IACpE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgC;IAC/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAC5D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAoC;IACtE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoC;IAChE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuF;gBAEjG,OAAO,EAAE,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC;IAS9C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDxD,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,uBAAuB;CAWhC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Context } from './context.js';
|
|
2
|
+
export class BotKernel {
|
|
3
|
+
apiClient;
|
|
4
|
+
sessionManager;
|
|
5
|
+
router;
|
|
6
|
+
resolveSessionKey;
|
|
7
|
+
transitions;
|
|
8
|
+
onTransition;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.apiClient = options.apiClient;
|
|
11
|
+
this.sessionManager = options.sessionManager;
|
|
12
|
+
this.router = options.router;
|
|
13
|
+
this.resolveSessionKey = options.resolveSessionKey;
|
|
14
|
+
this.transitions = options.transitions ?? {};
|
|
15
|
+
this.onTransition = options.onTransition;
|
|
16
|
+
}
|
|
17
|
+
async handleUpdate(update) {
|
|
18
|
+
const sessionKey = this.resolveSessionKey(update);
|
|
19
|
+
if (!sessionKey) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
await this.sessionManager.runInSession(sessionKey, async (session) => {
|
|
23
|
+
let ctxRef = null;
|
|
24
|
+
const ctx = new Context({
|
|
25
|
+
update,
|
|
26
|
+
session,
|
|
27
|
+
apiClient: this.apiClient,
|
|
28
|
+
sceneController: {
|
|
29
|
+
enter: async (nextState) => {
|
|
30
|
+
this.assertTransitionAllowed(session.current_state, nextState);
|
|
31
|
+
const previous = session.current_state;
|
|
32
|
+
const previousHooks = previous ? this.router.getSceneHooks(previous) : undefined;
|
|
33
|
+
const nextHooks = this.router.getSceneHooks(nextState);
|
|
34
|
+
if (previousHooks?.onLeave && ctxRef) {
|
|
35
|
+
await previousHooks.onLeave(ctxRef);
|
|
36
|
+
}
|
|
37
|
+
session.current_state = nextState;
|
|
38
|
+
if (nextHooks?.onEnter && ctxRef) {
|
|
39
|
+
await nextHooks.onEnter(ctxRef);
|
|
40
|
+
}
|
|
41
|
+
if (this.onTransition) {
|
|
42
|
+
await this.onTransition(previous, nextState, sessionKey);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
leave: async () => {
|
|
46
|
+
const previous = session.current_state;
|
|
47
|
+
const previousHooks = previous ? this.router.getSceneHooks(previous) : undefined;
|
|
48
|
+
if (previousHooks?.onLeave && ctxRef) {
|
|
49
|
+
await previousHooks.onLeave(ctxRef);
|
|
50
|
+
}
|
|
51
|
+
session.current_state = null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
ctxRef = ctx;
|
|
56
|
+
await this.router.dispatch(Object.assign(ctx, this.extractRoutingMeta(ctx)));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
extractRoutingMeta(ctx) {
|
|
60
|
+
const meta = {
|
|
61
|
+
currentState: ctx.session.current_state
|
|
62
|
+
};
|
|
63
|
+
const text = ctx.message?.text;
|
|
64
|
+
if (text !== undefined) {
|
|
65
|
+
meta.text = text;
|
|
66
|
+
}
|
|
67
|
+
const command = this.extractCommand(text, ctx.message?.entities);
|
|
68
|
+
if (command !== undefined) {
|
|
69
|
+
meta.command = command;
|
|
70
|
+
}
|
|
71
|
+
const callbackData = ctx.callbackQuery?.data;
|
|
72
|
+
if (callbackData !== undefined) {
|
|
73
|
+
meta.callbackData = callbackData;
|
|
74
|
+
}
|
|
75
|
+
return meta;
|
|
76
|
+
}
|
|
77
|
+
extractCommand(text, entities) {
|
|
78
|
+
if (!text || !entities) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const commandEntity = entities.find((entity) => entity.type === 'bot_command' && entity.offset === 0);
|
|
82
|
+
if (!commandEntity) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const raw = text.slice(0, commandEntity.length);
|
|
86
|
+
return raw.includes('@') ? raw.split('@')[0] : raw;
|
|
87
|
+
}
|
|
88
|
+
assertTransitionAllowed(from, to) {
|
|
89
|
+
if (!from) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const allowed = this.transitions[from];
|
|
93
|
+
if (!allowed || allowed.includes(to)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
throw new Error(`Transition from ${from} to ${to} is not allowed.`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=bot-kernel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot-kernel.js","sourceRoot":"","sources":["../../src/core/bot-kernel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CircuitBreakerOptions } from '../types/core.js';
|
|
2
|
+
type CircuitState = 'closed' | 'open' | 'half_open';
|
|
3
|
+
export declare class CircuitBreaker {
|
|
4
|
+
private state;
|
|
5
|
+
private failures;
|
|
6
|
+
private openedAt;
|
|
7
|
+
private halfOpenRequests;
|
|
8
|
+
private readonly options;
|
|
9
|
+
constructor(options?: Partial<CircuitBreakerOptions>);
|
|
10
|
+
beforeRequest(nowMs: number): void;
|
|
11
|
+
onSuccess(): void;
|
|
12
|
+
onFailure(nowMs: number): void;
|
|
13
|
+
snapshot(): {
|
|
14
|
+
state: CircuitState;
|
|
15
|
+
failures: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|