@effect-ak/tg-bot 1.1.0 → 1.2.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/dist/index.cjs +67 -6
- package/dist/index.d.ts +34 -7
- package/dist/index.js +66 -6
- package/package.json +3 -2
- package/readme.md +127 -120
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ __export(index_exports, {
|
|
|
28
28
|
BotTgClientTag: () => BotTgClientTag,
|
|
29
29
|
BotUpdateHandlersTag: () => BotUpdateHandlersTag,
|
|
30
30
|
HandleUpdateError: () => HandleUpdateError,
|
|
31
|
+
createBotContext: () => createBotContext,
|
|
31
32
|
defineBot: () => defineBot,
|
|
32
33
|
extractUpdate: () => extractUpdate,
|
|
33
34
|
handleEntireBatch: () => handleEntireBatch,
|
|
@@ -2656,6 +2657,26 @@ var BotResponse = class _BotResponse extends TaggedClass("BotResponse") {
|
|
|
2656
2657
|
static ignore = new _BotResponse({});
|
|
2657
2658
|
};
|
|
2658
2659
|
|
|
2660
|
+
// src/internal/bot-context.ts
|
|
2661
|
+
var extractCommand = (update) => {
|
|
2662
|
+
if (typeof update !== "object" || update === null) return void 0;
|
|
2663
|
+
const u = update;
|
|
2664
|
+
if (!u.entities || !u.text) return void 0;
|
|
2665
|
+
const entity = u.entities.find((e) => e.type === "bot_command");
|
|
2666
|
+
if (!entity) return void 0;
|
|
2667
|
+
return u.text.slice(entity.offset, entity.offset + entity.length);
|
|
2668
|
+
};
|
|
2669
|
+
var createBotContext = (update) => {
|
|
2670
|
+
const command = extractCommand(update);
|
|
2671
|
+
return {
|
|
2672
|
+
command,
|
|
2673
|
+
reply: (text, options) => BotResponse.make({ type: "message", text, ...options }),
|
|
2674
|
+
replyWithDocument: (document, options) => BotResponse.make({ type: "document", document, ...options }),
|
|
2675
|
+
replyWithPhoto: (photo, options) => BotResponse.make({ type: "photo", photo, ...options }),
|
|
2676
|
+
ignore: BotResponse.ignore
|
|
2677
|
+
};
|
|
2678
|
+
};
|
|
2679
|
+
|
|
2659
2680
|
// ../../node_modules/.pnpm/effect@3.12.0/node_modules/effect/dist/esm/Effectable.js
|
|
2660
2681
|
var EffectPrototype2 = EffectPrototype;
|
|
2661
2682
|
|
|
@@ -3547,6 +3568,36 @@ var BotPollSettingsTag = class extends Reference2()(
|
|
|
3547
3568
|
};
|
|
3548
3569
|
|
|
3549
3570
|
// src/internal/handle-update.ts
|
|
3571
|
+
var isGuardedHandler = (handler) => typeof handler === "object" && handler !== null && "handle" in handler;
|
|
3572
|
+
var executeSingleGuard = async (guard, update, ctx) => {
|
|
3573
|
+
const input = { update, ctx };
|
|
3574
|
+
if (guard.match) {
|
|
3575
|
+
const matched = await guard.match(input);
|
|
3576
|
+
if (!matched) return null;
|
|
3577
|
+
}
|
|
3578
|
+
return await guard.handle(input);
|
|
3579
|
+
};
|
|
3580
|
+
var executeGuards = async (guards, update, ctx) => {
|
|
3581
|
+
for (const guard of guards) {
|
|
3582
|
+
const result = await executeSingleGuard(guard, update, ctx);
|
|
3583
|
+
if (result !== null) return result;
|
|
3584
|
+
}
|
|
3585
|
+
return BotResponse.ignore;
|
|
3586
|
+
};
|
|
3587
|
+
var executeHandler = (handler, update, ctx) => {
|
|
3588
|
+
if (typeof handler === "function") {
|
|
3589
|
+
return handler(update);
|
|
3590
|
+
}
|
|
3591
|
+
if (Array.isArray(handler)) {
|
|
3592
|
+
return executeGuards(handler, update, ctx);
|
|
3593
|
+
}
|
|
3594
|
+
if (isGuardedHandler(handler)) {
|
|
3595
|
+
return executeSingleGuard(handler, update, ctx).then(
|
|
3596
|
+
(r) => r ?? BotResponse.ignore
|
|
3597
|
+
);
|
|
3598
|
+
}
|
|
3599
|
+
return BotResponse.ignore;
|
|
3600
|
+
};
|
|
3550
3601
|
var extractUpdate = (input) => {
|
|
3551
3602
|
for (const [field, value] of Object.entries(input)) {
|
|
3552
3603
|
if (field == "update_id") {
|
|
@@ -3650,8 +3701,8 @@ var handleOneUpdate = (updateObject, handlers) => gen(function* () {
|
|
|
3650
3701
|
})
|
|
3651
3702
|
);
|
|
3652
3703
|
}
|
|
3653
|
-
const
|
|
3654
|
-
if (!
|
|
3704
|
+
const handler = handlers[`on_${update.type}`];
|
|
3705
|
+
if (!handler) {
|
|
3655
3706
|
return yield* fail3(
|
|
3656
3707
|
new HandleUpdateError({
|
|
3657
3708
|
name: "HandlerNotDefined",
|
|
@@ -3666,9 +3717,10 @@ var handleOneUpdate = (updateObject, handlers) => gen(function* () {
|
|
|
3666
3717
|
message: `${update.text.slice(0, 5)}...`
|
|
3667
3718
|
});
|
|
3668
3719
|
}
|
|
3720
|
+
const ctx = createBotContext(update);
|
|
3669
3721
|
let handleUpdateError;
|
|
3670
3722
|
const handleResult = yield* try_({
|
|
3671
|
-
try: () =>
|
|
3723
|
+
try: () => executeHandler(handler, update, ctx),
|
|
3672
3724
|
catch: (error) => new HandleUpdateError({
|
|
3673
3725
|
name: "BotHandlerError",
|
|
3674
3726
|
update: updateObject,
|
|
@@ -3871,20 +3923,28 @@ var _runBotDaemon = (state) => gen(function* () {
|
|
|
3871
3923
|
|
|
3872
3924
|
// src/internal/launch.ts
|
|
3873
3925
|
var import_tg_bot_client2 = require("@effect-ak/tg-bot-client");
|
|
3926
|
+
var extractMode = (input) => {
|
|
3927
|
+
if (input.mode === "batch") {
|
|
3928
|
+
return { type: "batch", on_batch: input.on_batch };
|
|
3929
|
+
}
|
|
3930
|
+
const { bot_token, mode, poll, ...handlers } = input;
|
|
3931
|
+
return { type: "single", ...handlers };
|
|
3932
|
+
};
|
|
3874
3933
|
var launchBot = (input) => gen(function* () {
|
|
3875
3934
|
const service2 = yield* service(BotRunService);
|
|
3876
3935
|
const client = (0, import_tg_bot_client2.makeTgBotClient)({ bot_token: input.bot_token });
|
|
3877
3936
|
const contextWithClient = make4(BotTgClientTag, client);
|
|
3937
|
+
const mode = extractMode(input);
|
|
3878
3938
|
yield* service2.runBotInBackground.pipe(
|
|
3879
|
-
provideService(BotUpdateHandlersTag,
|
|
3939
|
+
provideService(BotUpdateHandlersTag, mode),
|
|
3880
3940
|
provideService(
|
|
3881
3941
|
BotPollSettingsTag,
|
|
3882
3942
|
BotPollSettings.make(input.poll ?? {})
|
|
3883
3943
|
),
|
|
3884
3944
|
provideContext(contextWithClient)
|
|
3885
3945
|
);
|
|
3886
|
-
const reload = (
|
|
3887
|
-
provideService(BotUpdateHandlersTag,
|
|
3946
|
+
const reload = (mode2) => service2.runBotInBackground.pipe(
|
|
3947
|
+
provideService(BotUpdateHandlersTag, mode2),
|
|
3888
3948
|
provideContext(contextWithClient),
|
|
3889
3949
|
runPromise
|
|
3890
3950
|
);
|
|
@@ -3911,6 +3971,7 @@ var defineBot = (input) => {
|
|
|
3911
3971
|
BotTgClientTag,
|
|
3912
3972
|
BotUpdateHandlersTag,
|
|
3913
3973
|
HandleUpdateError,
|
|
3974
|
+
createBotContext,
|
|
3914
3975
|
defineBot,
|
|
3915
3976
|
extractUpdate,
|
|
3916
3977
|
handleEntireBatch,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as effect_Types from 'effect/Types';
|
|
2
2
|
import { Api, Update } from '@effect-ak/tg-bot-api';
|
|
3
|
-
import * as effect_Cause from 'effect/Cause';
|
|
4
|
-
import * as Micro from 'effect/Micro';
|
|
5
|
-
import * as Data from 'effect/Data';
|
|
6
3
|
import * as Context from 'effect/Context';
|
|
4
|
+
import * as Data from 'effect/Data';
|
|
7
5
|
import { TgBotClient } from '@effect-ak/tg-bot-client';
|
|
6
|
+
import * as effect_Cause from 'effect/Cause';
|
|
7
|
+
import * as Micro from 'effect/Micro';
|
|
8
8
|
|
|
9
9
|
type BotResult = {
|
|
10
10
|
[K in keyof Api]: K extends `send_${infer R}` ? {
|
|
@@ -41,9 +41,15 @@ declare const BotPollSettingsTag_base: Context.ReferenceClass<BotPollSettings, "
|
|
|
41
41
|
declare class BotPollSettingsTag extends BotPollSettingsTag_base {
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
type RunBotInput = RunBotInputSingle | RunBotInputBatch;
|
|
45
|
+
interface RunBotInputSingle extends BotUpdatesHandlers {
|
|
46
|
+
bot_token: string;
|
|
47
|
+
mode: "single";
|
|
48
|
+
poll?: Partial<PollSettings>;
|
|
49
|
+
}
|
|
50
|
+
interface RunBotInputBatch extends HandleBatchUpdateFunction {
|
|
45
51
|
bot_token: string;
|
|
46
|
-
mode:
|
|
52
|
+
mode: "batch";
|
|
47
53
|
poll?: Partial<PollSettings>;
|
|
48
54
|
}
|
|
49
55
|
type ExtractedUpdate<K extends AvailableUpdateTypes> = {
|
|
@@ -51,8 +57,27 @@ type ExtractedUpdate<K extends AvailableUpdateTypes> = {
|
|
|
51
57
|
} & Update[K];
|
|
52
58
|
type AvailableUpdateTypes = Exclude<keyof Update, "update_id">;
|
|
53
59
|
type HandleUpdateFunction<U> = (update: U) => BotResponse | PromiseLike<BotResponse>;
|
|
60
|
+
interface BotContext {
|
|
61
|
+
readonly command: string | undefined;
|
|
62
|
+
readonly reply: (text: string, options?: Omit<BotResponseParams<"message">, "text" | "type">) => BotResponse;
|
|
63
|
+
readonly replyWithDocument: (document: BotResponseParams<"document">["document"], options?: Omit<BotResponseParams<"document">, "document" | "type">) => BotResponse;
|
|
64
|
+
readonly replyWithPhoto: (photo: BotResponseParams<"photo">["photo"], options?: Omit<BotResponseParams<"photo">, "photo" | "type">) => BotResponse;
|
|
65
|
+
readonly ignore: BotResponse;
|
|
66
|
+
}
|
|
67
|
+
type BotResponseParams<T extends string> = Extract<Parameters<typeof BotResponse.make>[0], {
|
|
68
|
+
type: T;
|
|
69
|
+
}>;
|
|
70
|
+
interface HandlerInput<U> {
|
|
71
|
+
readonly update: U;
|
|
72
|
+
readonly ctx: BotContext;
|
|
73
|
+
}
|
|
74
|
+
interface GuardedHandler<U> {
|
|
75
|
+
readonly match?: (input: HandlerInput<U>) => boolean | PromiseLike<boolean>;
|
|
76
|
+
readonly handle: (input: HandlerInput<U>) => BotResponse | PromiseLike<BotResponse>;
|
|
77
|
+
}
|
|
78
|
+
type UpdateHandler<U> = HandleUpdateFunction<U> | GuardedHandler<U> | GuardedHandler<U>[];
|
|
54
79
|
type BotUpdatesHandlers = {
|
|
55
|
-
|
|
80
|
+
[K in AvailableUpdateTypes as `on_${K}`]?: UpdateHandler<NonNullable<Update[K]>>;
|
|
56
81
|
};
|
|
57
82
|
interface HandleBatchUpdateFunction {
|
|
58
83
|
readonly on_batch: (update: Update[]) => boolean | PromiseLike<boolean>;
|
|
@@ -65,6 +90,8 @@ interface BotBatchMode extends HandleBatchUpdateFunction {
|
|
|
65
90
|
}
|
|
66
91
|
type BotMode = BotSingleMode | BotBatchMode;
|
|
67
92
|
|
|
93
|
+
declare const createBotContext: (update: unknown) => BotContext;
|
|
94
|
+
|
|
68
95
|
declare const extractUpdate: <U extends AvailableUpdateTypes>(input: Update) => ExtractedUpdate<U> | undefined;
|
|
69
96
|
declare class BatchUpdateResult extends Data.Class<{
|
|
70
97
|
hasErrors: boolean;
|
|
@@ -110,4 +137,4 @@ declare const runTgChatBot: (input: RunBotInput) => Promise<{
|
|
|
110
137
|
}>;
|
|
111
138
|
declare const defineBot: (input: BotUpdatesHandlers) => BotUpdatesHandlers;
|
|
112
139
|
|
|
113
|
-
export { type AvailableUpdateTypes, BatchUpdateResult, type BotBatchMode, type BotInstance, type BotMode, BotPollSettings, BotPollSettingsTag, BotResponse, BotRunService, type BotSingleMode, BotTgClientTag, BotUpdateHandlersTag, type BotUpdatesHandlers, type ExtractedUpdate, type HandleBatchUpdateFunction, HandleUpdateError, type HandleUpdateFunction, type PollSettings, type RunBotInput, defineBot, extractUpdate, handleEntireBatch, handleOneByOne, handleOneUpdate, handleUpdates, launchBot, runTgChatBot };
|
|
140
|
+
export { type AvailableUpdateTypes, BatchUpdateResult, type BotBatchMode, type BotContext, type BotInstance, type BotMode, BotPollSettings, BotPollSettingsTag, BotResponse, BotRunService, type BotSingleMode, BotTgClientTag, BotUpdateHandlersTag, type BotUpdatesHandlers, type ExtractedUpdate, type GuardedHandler, type HandleBatchUpdateFunction, HandleUpdateError, type HandleUpdateFunction, type HandlerInput, type PollSettings, type RunBotInput, type RunBotInputBatch, type RunBotInputSingle, type UpdateHandler, createBotContext, defineBot, extractUpdate, handleEntireBatch, handleOneByOne, handleOneUpdate, handleUpdates, launchBot, runTgChatBot };
|
package/dist/index.js
CHANGED
|
@@ -2615,6 +2615,26 @@ var BotResponse = class _BotResponse extends TaggedClass("BotResponse") {
|
|
|
2615
2615
|
static ignore = new _BotResponse({});
|
|
2616
2616
|
};
|
|
2617
2617
|
|
|
2618
|
+
// src/internal/bot-context.ts
|
|
2619
|
+
var extractCommand = (update) => {
|
|
2620
|
+
if (typeof update !== "object" || update === null) return void 0;
|
|
2621
|
+
const u = update;
|
|
2622
|
+
if (!u.entities || !u.text) return void 0;
|
|
2623
|
+
const entity = u.entities.find((e) => e.type === "bot_command");
|
|
2624
|
+
if (!entity) return void 0;
|
|
2625
|
+
return u.text.slice(entity.offset, entity.offset + entity.length);
|
|
2626
|
+
};
|
|
2627
|
+
var createBotContext = (update) => {
|
|
2628
|
+
const command = extractCommand(update);
|
|
2629
|
+
return {
|
|
2630
|
+
command,
|
|
2631
|
+
reply: (text, options) => BotResponse.make({ type: "message", text, ...options }),
|
|
2632
|
+
replyWithDocument: (document, options) => BotResponse.make({ type: "document", document, ...options }),
|
|
2633
|
+
replyWithPhoto: (photo, options) => BotResponse.make({ type: "photo", photo, ...options }),
|
|
2634
|
+
ignore: BotResponse.ignore
|
|
2635
|
+
};
|
|
2636
|
+
};
|
|
2637
|
+
|
|
2618
2638
|
// ../../node_modules/.pnpm/effect@3.12.0/node_modules/effect/dist/esm/Effectable.js
|
|
2619
2639
|
var EffectPrototype2 = EffectPrototype;
|
|
2620
2640
|
|
|
@@ -3506,6 +3526,36 @@ var BotPollSettingsTag = class extends Reference2()(
|
|
|
3506
3526
|
};
|
|
3507
3527
|
|
|
3508
3528
|
// src/internal/handle-update.ts
|
|
3529
|
+
var isGuardedHandler = (handler) => typeof handler === "object" && handler !== null && "handle" in handler;
|
|
3530
|
+
var executeSingleGuard = async (guard, update, ctx) => {
|
|
3531
|
+
const input = { update, ctx };
|
|
3532
|
+
if (guard.match) {
|
|
3533
|
+
const matched = await guard.match(input);
|
|
3534
|
+
if (!matched) return null;
|
|
3535
|
+
}
|
|
3536
|
+
return await guard.handle(input);
|
|
3537
|
+
};
|
|
3538
|
+
var executeGuards = async (guards, update, ctx) => {
|
|
3539
|
+
for (const guard of guards) {
|
|
3540
|
+
const result = await executeSingleGuard(guard, update, ctx);
|
|
3541
|
+
if (result !== null) return result;
|
|
3542
|
+
}
|
|
3543
|
+
return BotResponse.ignore;
|
|
3544
|
+
};
|
|
3545
|
+
var executeHandler = (handler, update, ctx) => {
|
|
3546
|
+
if (typeof handler === "function") {
|
|
3547
|
+
return handler(update);
|
|
3548
|
+
}
|
|
3549
|
+
if (Array.isArray(handler)) {
|
|
3550
|
+
return executeGuards(handler, update, ctx);
|
|
3551
|
+
}
|
|
3552
|
+
if (isGuardedHandler(handler)) {
|
|
3553
|
+
return executeSingleGuard(handler, update, ctx).then(
|
|
3554
|
+
(r) => r ?? BotResponse.ignore
|
|
3555
|
+
);
|
|
3556
|
+
}
|
|
3557
|
+
return BotResponse.ignore;
|
|
3558
|
+
};
|
|
3509
3559
|
var extractUpdate = (input) => {
|
|
3510
3560
|
for (const [field, value] of Object.entries(input)) {
|
|
3511
3561
|
if (field == "update_id") {
|
|
@@ -3609,8 +3659,8 @@ var handleOneUpdate = (updateObject, handlers) => gen(function* () {
|
|
|
3609
3659
|
})
|
|
3610
3660
|
);
|
|
3611
3661
|
}
|
|
3612
|
-
const
|
|
3613
|
-
if (!
|
|
3662
|
+
const handler = handlers[`on_${update.type}`];
|
|
3663
|
+
if (!handler) {
|
|
3614
3664
|
return yield* fail3(
|
|
3615
3665
|
new HandleUpdateError({
|
|
3616
3666
|
name: "HandlerNotDefined",
|
|
@@ -3625,9 +3675,10 @@ var handleOneUpdate = (updateObject, handlers) => gen(function* () {
|
|
|
3625
3675
|
message: `${update.text.slice(0, 5)}...`
|
|
3626
3676
|
});
|
|
3627
3677
|
}
|
|
3678
|
+
const ctx = createBotContext(update);
|
|
3628
3679
|
let handleUpdateError;
|
|
3629
3680
|
const handleResult = yield* try_({
|
|
3630
|
-
try: () =>
|
|
3681
|
+
try: () => executeHandler(handler, update, ctx),
|
|
3631
3682
|
catch: (error) => new HandleUpdateError({
|
|
3632
3683
|
name: "BotHandlerError",
|
|
3633
3684
|
update: updateObject,
|
|
@@ -3830,20 +3881,28 @@ var _runBotDaemon = (state) => gen(function* () {
|
|
|
3830
3881
|
|
|
3831
3882
|
// src/internal/launch.ts
|
|
3832
3883
|
import { makeTgBotClient } from "@effect-ak/tg-bot-client";
|
|
3884
|
+
var extractMode = (input) => {
|
|
3885
|
+
if (input.mode === "batch") {
|
|
3886
|
+
return { type: "batch", on_batch: input.on_batch };
|
|
3887
|
+
}
|
|
3888
|
+
const { bot_token, mode, poll, ...handlers } = input;
|
|
3889
|
+
return { type: "single", ...handlers };
|
|
3890
|
+
};
|
|
3833
3891
|
var launchBot = (input) => gen(function* () {
|
|
3834
3892
|
const service2 = yield* service(BotRunService);
|
|
3835
3893
|
const client = makeTgBotClient({ bot_token: input.bot_token });
|
|
3836
3894
|
const contextWithClient = make4(BotTgClientTag, client);
|
|
3895
|
+
const mode = extractMode(input);
|
|
3837
3896
|
yield* service2.runBotInBackground.pipe(
|
|
3838
|
-
provideService(BotUpdateHandlersTag,
|
|
3897
|
+
provideService(BotUpdateHandlersTag, mode),
|
|
3839
3898
|
provideService(
|
|
3840
3899
|
BotPollSettingsTag,
|
|
3841
3900
|
BotPollSettings.make(input.poll ?? {})
|
|
3842
3901
|
),
|
|
3843
3902
|
provideContext(contextWithClient)
|
|
3844
3903
|
);
|
|
3845
|
-
const reload = (
|
|
3846
|
-
provideService(BotUpdateHandlersTag,
|
|
3904
|
+
const reload = (mode2) => service2.runBotInBackground.pipe(
|
|
3905
|
+
provideService(BotUpdateHandlersTag, mode2),
|
|
3847
3906
|
provideContext(contextWithClient),
|
|
3848
3907
|
runPromise
|
|
3849
3908
|
);
|
|
@@ -3869,6 +3928,7 @@ export {
|
|
|
3869
3928
|
BotTgClientTag,
|
|
3870
3929
|
BotUpdateHandlersTag,
|
|
3871
3930
|
HandleUpdateError,
|
|
3931
|
+
createBotContext,
|
|
3872
3932
|
defineBot,
|
|
3873
3933
|
extractUpdate,
|
|
3874
3934
|
handleEntireBatch,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-ak/tg-bot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Telegram Bot runner",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"effect": "^3.12.7"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
|
-
"build": "tsup"
|
|
44
|
+
"build": "tsup",
|
|
45
|
+
"typecheck": "tsc"
|
|
45
46
|
}
|
|
46
47
|
}
|
package/readme.md
CHANGED
|
@@ -60,21 +60,17 @@ yarn add @effect-ak/tg-bot effect
|
|
|
60
60
|
## Quick Start
|
|
61
61
|
|
|
62
62
|
```typescript
|
|
63
|
-
import { runTgChatBot
|
|
63
|
+
import { runTgChatBot } from "@effect-ak/tg-bot"
|
|
64
64
|
|
|
65
65
|
runTgChatBot({
|
|
66
66
|
bot_token: "YOUR_BOT_TOKEN",
|
|
67
|
-
mode:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return BotResponse.make({
|
|
73
|
-
type: "message",
|
|
74
|
-
text: `You said: ${message.text}`
|
|
75
|
-
})
|
|
67
|
+
mode: "single",
|
|
68
|
+
on_message: [
|
|
69
|
+
{
|
|
70
|
+
match: ({ update }) => !!update.text,
|
|
71
|
+
handle: ({ update, ctx }) => ctx.reply(`You said: ${update.text}`)
|
|
76
72
|
}
|
|
77
|
-
|
|
73
|
+
]
|
|
78
74
|
})
|
|
79
75
|
```
|
|
80
76
|
|
|
@@ -84,6 +80,31 @@ runTgChatBot({
|
|
|
84
80
|
|
|
85
81
|
In single mode, the bot processes each update individually with a dedicated handler for each update type.
|
|
86
82
|
|
|
83
|
+
**Handler Format (v2 with guards):**
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
on_message: [
|
|
87
|
+
{
|
|
88
|
+
match: ({ update, ctx }) => ctx.command === "/start", // optional filter
|
|
89
|
+
handle: ({ update, ctx }) => ctx.reply("Welcome!") // handler
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
match: ({ update }) => !!update.text,
|
|
93
|
+
handle: ({ ctx }) => ctx.reply("Got your message!")
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
handle: ({ ctx }) => ctx.ignore // fallback (no match = always runs)
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Context helpers:**
|
|
102
|
+
- `ctx.reply(text, options?)` - Send a message
|
|
103
|
+
- `ctx.replyWithDocument(document, options?)` - Send a document
|
|
104
|
+
- `ctx.replyWithPhoto(photo, options?)` - Send a photo
|
|
105
|
+
- `ctx.command` - Parsed command (e.g., "/start", "/help")
|
|
106
|
+
- `ctx.ignore` - Skip response
|
|
107
|
+
|
|
87
108
|
**Available Handlers:**
|
|
88
109
|
- `on_message` - New incoming message
|
|
89
110
|
- `on_edited_message` - Message was edited
|
|
@@ -100,6 +121,15 @@ In single mode, the bot processes each update individually with a dedicated hand
|
|
|
100
121
|
- `on_chat_member` - Chat member status changed
|
|
101
122
|
- `on_chat_join_request` - Request to join chat
|
|
102
123
|
|
|
124
|
+
**Legacy format (v1 - still supported):**
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
on_message: (message) => {
|
|
128
|
+
if (!message.text) return BotResponse.ignore
|
|
129
|
+
return BotResponse.make({ type: "message", text: "Hello!" })
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
103
133
|
### Batch Mode
|
|
104
134
|
|
|
105
135
|
In batch mode, the bot receives all updates as an array and processes them together.
|
|
@@ -107,13 +137,11 @@ In batch mode, the bot receives all updates as an array and processes them toget
|
|
|
107
137
|
```typescript
|
|
108
138
|
runTgChatBot({
|
|
109
139
|
bot_token: "YOUR_BOT_TOKEN",
|
|
110
|
-
mode:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return true // Continue polling
|
|
116
|
-
}
|
|
140
|
+
mode: "batch",
|
|
141
|
+
on_batch: async (updates) => {
|
|
142
|
+
console.log(`Processing ${updates.length} updates`)
|
|
143
|
+
// Process updates...
|
|
144
|
+
return true // Continue polling
|
|
117
145
|
}
|
|
118
146
|
})
|
|
119
147
|
```
|
|
@@ -155,66 +183,45 @@ All Telegram `send_*` methods are supported: `message`, `photo`, `document`, `vi
|
|
|
155
183
|
### Echo Bot
|
|
156
184
|
|
|
157
185
|
```typescript
|
|
158
|
-
import { runTgChatBot,
|
|
186
|
+
import { runTgChatBot, defineBot } from "@effect-ak/tg-bot"
|
|
159
187
|
|
|
160
188
|
const ECHO_BOT = defineBot({
|
|
161
|
-
on_message:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
reply_parameters: {
|
|
168
|
-
message_id: message.message_id
|
|
169
|
-
}
|
|
170
|
-
})
|
|
171
|
-
}
|
|
189
|
+
on_message: [
|
|
190
|
+
{
|
|
191
|
+
match: ({ update }) => !!update.text,
|
|
192
|
+
handle: ({ update, ctx }) => ctx.reply(update.text!)
|
|
193
|
+
}
|
|
194
|
+
]
|
|
172
195
|
})
|
|
173
196
|
|
|
174
197
|
runTgChatBot({
|
|
175
198
|
bot_token: "YOUR_BOT_TOKEN",
|
|
176
|
-
mode:
|
|
177
|
-
|
|
178
|
-
...ECHO_BOT
|
|
179
|
-
}
|
|
199
|
+
mode: "single",
|
|
200
|
+
...ECHO_BOT
|
|
180
201
|
})
|
|
181
202
|
```
|
|
182
203
|
|
|
183
204
|
### Command Handler
|
|
184
205
|
|
|
185
206
|
```typescript
|
|
186
|
-
import { runTgChatBot
|
|
207
|
+
import { runTgChatBot } from "@effect-ak/tg-bot"
|
|
187
208
|
import { MESSAGE_EFFECTS } from "@effect-ak/tg-bot-client"
|
|
188
209
|
|
|
189
210
|
runTgChatBot({
|
|
190
211
|
bot_token: "YOUR_BOT_TOKEN",
|
|
191
|
-
mode:
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
type: "message",
|
|
203
|
-
text: "Welcome! Send me any message.",
|
|
204
|
-
message_effect_id: MESSAGE_EFFECTS["🎉"]
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
case "/help":
|
|
208
|
-
return BotResponse.make({
|
|
209
|
-
type: "message",
|
|
210
|
-
text: "Available commands:\n/start - Start bot\n/help - Show help"
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
default:
|
|
214
|
-
return BotResponse.ignore
|
|
215
|
-
}
|
|
212
|
+
mode: "single",
|
|
213
|
+
on_message: [
|
|
214
|
+
{
|
|
215
|
+
match: ({ ctx }) => ctx.command === "/start",
|
|
216
|
+
handle: ({ ctx }) => ctx.reply("Welcome! Send me any message.", {
|
|
217
|
+
message_effect_id: MESSAGE_EFFECTS["🎉"]
|
|
218
|
+
})
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
match: ({ ctx }) => ctx.command === "/help",
|
|
222
|
+
handle: ({ ctx }) => ctx.reply("Available commands:\n/start - Start bot\n/help - Show help")
|
|
216
223
|
}
|
|
217
|
-
|
|
224
|
+
]
|
|
218
225
|
})
|
|
219
226
|
```
|
|
220
227
|
|
|
@@ -228,24 +235,22 @@ const client = makeTgBotClient({ bot_token: "YOUR_BOT_TOKEN" })
|
|
|
228
235
|
|
|
229
236
|
runTgChatBot({
|
|
230
237
|
bot_token: "YOUR_BOT_TOKEN",
|
|
238
|
+
mode: "batch",
|
|
231
239
|
poll: {
|
|
232
240
|
batch_size: 100,
|
|
233
241
|
poll_timeout: 60
|
|
234
242
|
},
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
text: `Processed ${messages.length} messages`
|
|
245
|
-
})
|
|
243
|
+
on_batch: async (updates) => {
|
|
244
|
+
const messages = updates
|
|
245
|
+
.map(u => u.message)
|
|
246
|
+
.filter(m => m != null)
|
|
247
|
+
|
|
248
|
+
await client.execute("send_message", {
|
|
249
|
+
chat_id: "ADMIN_CHAT_ID",
|
|
250
|
+
text: `Processed ${messages.length} messages`
|
|
251
|
+
})
|
|
246
252
|
|
|
247
|
-
|
|
248
|
-
}
|
|
253
|
+
return true // Continue polling
|
|
249
254
|
}
|
|
250
255
|
})
|
|
251
256
|
```
|
|
@@ -256,32 +261,24 @@ Advanced usage with Effect for composable async operations:
|
|
|
256
261
|
|
|
257
262
|
```typescript
|
|
258
263
|
import { Effect, Micro, pipe } from "effect"
|
|
259
|
-
import {
|
|
264
|
+
import { launchBot } from "@effect-ak/tg-bot"
|
|
260
265
|
|
|
261
266
|
Effect.gen(function* () {
|
|
262
267
|
const bot = yield* launchBot({
|
|
263
268
|
bot_token: "YOUR_BOT_TOKEN",
|
|
269
|
+
mode: "single",
|
|
264
270
|
poll: {
|
|
265
271
|
log_level: "debug"
|
|
266
272
|
},
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
Effect.sleep("2 seconds"),
|
|
275
|
-
Effect.andThen(() =>
|
|
276
|
-
BotResponse.make({
|
|
277
|
-
type: "message",
|
|
278
|
-
text: "Delayed response!"
|
|
279
|
-
})
|
|
280
|
-
),
|
|
281
|
-
Effect.runPromise
|
|
282
|
-
)
|
|
273
|
+
on_message: [
|
|
274
|
+
{
|
|
275
|
+
match: ({ update }) => !!update.text,
|
|
276
|
+
handle: async ({ ctx }) => {
|
|
277
|
+
await Effect.sleep("2 seconds").pipe(Effect.runPromise)
|
|
278
|
+
return ctx.reply("Delayed response!")
|
|
279
|
+
}
|
|
283
280
|
}
|
|
284
|
-
|
|
281
|
+
]
|
|
285
282
|
})
|
|
286
283
|
|
|
287
284
|
// Access bot fiber for control
|
|
@@ -296,30 +293,31 @@ Effect.gen(function* () {
|
|
|
296
293
|
### Hot Reload
|
|
297
294
|
|
|
298
295
|
```typescript
|
|
299
|
-
import {
|
|
300
|
-
import { launchBot, BotResponse } from "@effect-ak/tg-bot"
|
|
296
|
+
import { runTgChatBot } from "@effect-ak/tg-bot"
|
|
301
297
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
text: "Version 1"
|
|
310
|
-
})
|
|
298
|
+
const bot = await runTgChatBot({
|
|
299
|
+
bot_token: "YOUR_BOT_TOKEN",
|
|
300
|
+
mode: "single",
|
|
301
|
+
on_message: [
|
|
302
|
+
{
|
|
303
|
+
match: ({ update }) => !!update.text,
|
|
304
|
+
handle: ({ ctx }) => ctx.reply("Version 1")
|
|
311
305
|
}
|
|
312
|
-
|
|
306
|
+
]
|
|
307
|
+
})
|
|
313
308
|
|
|
314
|
-
|
|
315
|
-
|
|
309
|
+
// Later, reload with new handlers
|
|
310
|
+
setTimeout(() => {
|
|
311
|
+
bot.reload({
|
|
316
312
|
type: "single",
|
|
317
|
-
on_message:
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
313
|
+
on_message: [
|
|
314
|
+
{
|
|
315
|
+
match: ({ update }) => !!update.text,
|
|
316
|
+
handle: ({ ctx }) => ctx.reply("Version 2 - Hot reloaded!")
|
|
317
|
+
}
|
|
318
|
+
]
|
|
321
319
|
})
|
|
322
|
-
})
|
|
320
|
+
}, 5000)
|
|
323
321
|
```
|
|
324
322
|
|
|
325
323
|
## Configuration
|
|
@@ -331,6 +329,7 @@ Configure how the bot polls for updates:
|
|
|
331
329
|
```typescript
|
|
332
330
|
runTgChatBot({
|
|
333
331
|
bot_token: "YOUR_BOT_TOKEN",
|
|
332
|
+
mode: "single", // or "batch"
|
|
334
333
|
poll: {
|
|
335
334
|
log_level: "debug", // "info" | "debug"
|
|
336
335
|
on_error: "continue", // "stop" | "continue"
|
|
@@ -338,7 +337,7 @@ runTgChatBot({
|
|
|
338
337
|
poll_timeout: 30, // 2-120 seconds
|
|
339
338
|
max_empty_responses: 5 // Stop after N empty responses
|
|
340
339
|
},
|
|
341
|
-
|
|
340
|
+
on_message: [/* ... */] // handlers at top level
|
|
342
341
|
})
|
|
343
342
|
```
|
|
344
343
|
|
|
@@ -366,10 +365,12 @@ Starts the bot with long polling.
|
|
|
366
365
|
|
|
367
366
|
**Parameters:**
|
|
368
367
|
- `bot_token` (string, required): Bot token from @BotFather
|
|
369
|
-
- `mode` (
|
|
368
|
+
- `mode` (`"single"` | `"batch"`, required): Processing mode
|
|
370
369
|
- `poll` (object, optional): Polling configuration
|
|
370
|
+
- `on_message`, `on_callback_query`, etc. (optional): Update handlers (for single mode)
|
|
371
|
+
- `on_batch` (required for batch mode): Batch handler function
|
|
371
372
|
|
|
372
|
-
**Returns:** `Promise<
|
|
373
|
+
**Returns:** `Promise<BotInstance>`
|
|
373
374
|
|
|
374
375
|
### `launchBot(input)`
|
|
375
376
|
|
|
@@ -450,13 +451,19 @@ If a handler throws an error, the bot:
|
|
|
450
451
|
3. Continues processing other updates (if `on_error: "continue"`)
|
|
451
452
|
|
|
452
453
|
```typescript
|
|
453
|
-
on_message:
|
|
454
|
-
|
|
455
|
-
|
|
454
|
+
on_message: [
|
|
455
|
+
{
|
|
456
|
+
match: ({ ctx }) => ctx.command === "/error",
|
|
457
|
+
handle: () => {
|
|
458
|
+
throw new Error("Something went wrong!")
|
|
459
|
+
// Bot will catch this and send error message to user
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
match: ({ update }) => !!update.text,
|
|
464
|
+
handle: ({ ctx }) => ctx.reply("OK")
|
|
456
465
|
}
|
|
457
|
-
|
|
458
|
-
return BotResponse.make({ type: "message", text: "OK" })
|
|
459
|
-
}
|
|
466
|
+
]
|
|
460
467
|
```
|
|
461
468
|
|
|
462
469
|
### Batch Handler Errors
|