@effect-ak/tg-bot-client 0.3.3 → 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/dist/index.d.ts +82 -38
- package/dist/index.js +151 -72
- package/dist/index.mjs +150 -72
- package/package.json +1 -1
- package/readme.md +76 -43
package/dist/index.d.ts
CHANGED
|
@@ -3,20 +3,32 @@ import * as effect_Types from 'effect/Types';
|
|
|
3
3
|
import * as Micro from 'effect/Micro';
|
|
4
4
|
import * as Context from 'effect/Context';
|
|
5
5
|
|
|
6
|
-
type
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
type TgBotClientSettingsInput = {
|
|
7
|
+
bot_token: string;
|
|
8
|
+
base_url?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
declare const TgBotClientConfig_base: Context.TagClass<TgBotClientConfig, "TgBotClientConfig", Required<TgBotClientSettingsInput>>;
|
|
12
|
+
declare class TgBotClientConfig extends TgBotClientConfig_base {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type ErrorReason = Readonly<{
|
|
16
|
+
type: "NotOkResponse";
|
|
17
|
+
errorCode?: number;
|
|
18
|
+
details?: string;
|
|
10
19
|
} | {
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
type: "UnexpectedResponse";
|
|
21
|
+
response: unknown;
|
|
13
22
|
} | {
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
type: "ClientInternalError";
|
|
24
|
+
cause: unknown;
|
|
16
25
|
} | {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
26
|
+
type: "UnableToGetFile";
|
|
27
|
+
cause: unknown;
|
|
28
|
+
} | {
|
|
29
|
+
type: "BotHandlerError";
|
|
30
|
+
cause: unknown;
|
|
31
|
+
}>;
|
|
20
32
|
declare const TgBotClientError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
|
|
21
33
|
readonly _tag: "TgBotClientError";
|
|
22
34
|
} & Readonly<A>;
|
|
@@ -26,11 +38,6 @@ declare class TgBotClientError extends TgBotClientError_base<{
|
|
|
26
38
|
static readonly missingSuccess: TgBotClientError;
|
|
27
39
|
}
|
|
28
40
|
|
|
29
|
-
type TgBotClientSettingsInput = {
|
|
30
|
-
bot_token: string;
|
|
31
|
-
base_url?: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
41
|
interface AffiliateInfo {
|
|
35
42
|
commission_per_mille: number;
|
|
36
43
|
amount: number;
|
|
@@ -539,6 +546,7 @@ interface Gift {
|
|
|
539
546
|
id: string;
|
|
540
547
|
sticker: Sticker;
|
|
541
548
|
star_count: number;
|
|
549
|
+
upgrade_star_count?: number;
|
|
542
550
|
total_count?: number;
|
|
543
551
|
remaining_count?: number;
|
|
544
552
|
}
|
|
@@ -616,7 +624,6 @@ interface InlineQueryResultArticle {
|
|
|
616
624
|
input_message_content: InputMessageContent;
|
|
617
625
|
reply_markup?: InlineKeyboardMarkup;
|
|
618
626
|
url?: string;
|
|
619
|
-
hide_url?: boolean;
|
|
620
627
|
description?: string;
|
|
621
628
|
thumbnail_url?: string;
|
|
622
629
|
thumbnail_width?: number;
|
|
@@ -1767,6 +1774,8 @@ interface Api {
|
|
|
1767
1774
|
pin_chat_message(_: PinChatMessageInput): boolean;
|
|
1768
1775
|
promote_chat_member(_: PromoteChatMemberInput): boolean;
|
|
1769
1776
|
refund_star_payment(_: RefundStarPaymentInput): boolean;
|
|
1777
|
+
remove_chat_verification(_: RemoveChatVerificationInput): boolean;
|
|
1778
|
+
remove_user_verification(_: RemoveUserVerificationInput): boolean;
|
|
1770
1779
|
reopen_forum_topic(_: ReopenForumTopicInput): boolean;
|
|
1771
1780
|
reopen_general_forum_topic(_: ReopenGeneralForumTopicInput): boolean;
|
|
1772
1781
|
replace_sticker_in_set(_: ReplaceStickerInSetInput): boolean;
|
|
@@ -1827,6 +1836,8 @@ interface Api {
|
|
|
1827
1836
|
unpin_all_general_forum_topic_messages(_: UnpinAllGeneralForumTopicMessagesInput): boolean;
|
|
1828
1837
|
unpin_chat_message(_: UnpinChatMessageInput): boolean;
|
|
1829
1838
|
upload_sticker_file(_: UploadStickerFileInput): File$1;
|
|
1839
|
+
verify_chat(_: VerifyChatInput): boolean;
|
|
1840
|
+
verify_user(_: VerifyUserInput): boolean;
|
|
1830
1841
|
}
|
|
1831
1842
|
interface AddStickerToSetInput {
|
|
1832
1843
|
user_id: number;
|
|
@@ -2207,6 +2218,12 @@ interface RefundStarPaymentInput {
|
|
|
2207
2218
|
user_id: number;
|
|
2208
2219
|
telegram_payment_charge_id: string;
|
|
2209
2220
|
}
|
|
2221
|
+
interface RemoveChatVerificationInput {
|
|
2222
|
+
chat_id: number | string;
|
|
2223
|
+
}
|
|
2224
|
+
interface RemoveUserVerificationInput {
|
|
2225
|
+
user_id: number;
|
|
2226
|
+
}
|
|
2210
2227
|
interface ReopenForumTopicInput {
|
|
2211
2228
|
chat_id: number | string;
|
|
2212
2229
|
message_thread_id: number;
|
|
@@ -2344,6 +2361,7 @@ interface SendGameInput {
|
|
|
2344
2361
|
interface SendGiftInput {
|
|
2345
2362
|
user_id: number;
|
|
2346
2363
|
gift_id: string;
|
|
2364
|
+
pay_for_upgrade?: boolean;
|
|
2347
2365
|
text?: string;
|
|
2348
2366
|
text_parse_mode?: "HTML" | "MarkdownV2";
|
|
2349
2367
|
text_entities?: MessageEntity[];
|
|
@@ -2724,34 +2742,64 @@ interface UploadStickerFileInput {
|
|
|
2724
2742
|
sticker: InputFile;
|
|
2725
2743
|
sticker_format: "static" | "animated" | "video";
|
|
2726
2744
|
}
|
|
2745
|
+
interface VerifyChatInput {
|
|
2746
|
+
chat_id: number | string;
|
|
2747
|
+
custom_description?: string;
|
|
2748
|
+
}
|
|
2749
|
+
interface VerifyUserInput {
|
|
2750
|
+
user_id: number;
|
|
2751
|
+
custom_description?: string;
|
|
2752
|
+
}
|
|
2727
2753
|
|
|
2728
2754
|
type AvailableUpdateTypes = Exclude<keyof Update, 'update_id'>;
|
|
2729
2755
|
type LogLevel = "info" | "debug";
|
|
2730
|
-
type
|
|
2756
|
+
type BotResult = {
|
|
2731
2757
|
[K in keyof Api]: K extends `send_${infer R}` ? {
|
|
2732
|
-
type:
|
|
2758
|
+
type: R;
|
|
2733
2759
|
} & Omit<Parameters<Api[K]>[0], 'chat_id'> : never;
|
|
2734
2760
|
}[keyof Api];
|
|
2735
|
-
|
|
2736
|
-
|
|
2761
|
+
declare const BotResponse_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Readonly<A> & {
|
|
2762
|
+
readonly _tag: "BotResponse";
|
|
2737
2763
|
};
|
|
2764
|
+
declare class BotResponse extends BotResponse_base<{
|
|
2765
|
+
readonly response?: BotResult;
|
|
2766
|
+
}> {
|
|
2767
|
+
static make(result: BotResult): BotResponse;
|
|
2768
|
+
static readonly ignore: BotResponse;
|
|
2769
|
+
}
|
|
2770
|
+
type HandleUpdateFunction<U> = (update: U) => BotResponse | PromiseLike<BotResponse>;
|
|
2771
|
+
type MessageHandlers = {
|
|
2772
|
+
readonly [K in AvailableUpdateTypes as `on_${K}`]?: HandleUpdateFunction<NonNullable<Update[K]>>;
|
|
2773
|
+
};
|
|
2774
|
+
interface BotMessageHandlers extends MessageHandlers {
|
|
2775
|
+
}
|
|
2738
2776
|
|
|
2739
2777
|
type BotMessageHandlerShape = Context.Tag.Service<BotMessageHandler>;
|
|
2740
|
-
declare const BotMessageHandler_base: Context.TagClass<BotMessageHandler, "BotMessageHandler", {
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
} & BotMessageHandlers>;
|
|
2778
|
+
declare const BotMessageHandler_base: Context.TagClass<BotMessageHandler, "BotMessageHandler", Readonly<{
|
|
2779
|
+
log_level?: LogLevel;
|
|
2780
|
+
batch_size?: number;
|
|
2781
|
+
timeout?: number;
|
|
2782
|
+
max_empty_responses?: number;
|
|
2783
|
+
on_error?: "stop" | "continue";
|
|
2784
|
+
}> & BotMessageHandlers>;
|
|
2747
2785
|
declare class BotMessageHandler extends BotMessageHandler_base {
|
|
2748
2786
|
}
|
|
2749
2787
|
|
|
2788
|
+
declare const HandleUpdateError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
|
|
2789
|
+
readonly _tag: "HandleUpdateError";
|
|
2790
|
+
} & Readonly<A>;
|
|
2791
|
+
declare class HandleUpdateError extends HandleUpdateError_base<{
|
|
2792
|
+
name: "UnknownUpdate" | "HandlerNotDefined" | "BotHandlerError";
|
|
2793
|
+
update: Update;
|
|
2794
|
+
cause?: unknown;
|
|
2795
|
+
}> {
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2750
2798
|
type BotInstance = Micro.Micro.Success<ReturnType<typeof BotFactoryServiceDefault["runBot"]>>;
|
|
2751
2799
|
declare const BotFactoryService_base: Context.TagClass<BotFactoryService, "BotFactoryService", {
|
|
2752
2800
|
runBot: (input: RunBotInput) => Micro.Micro<{
|
|
2753
2801
|
readonly reload: (input: Partial<RunBotInput>) => Promise<void>;
|
|
2754
|
-
readonly fiber: () => Micro.MicroFiber<unknown, TgBotClientError> | undefined;
|
|
2802
|
+
readonly fiber: () => Micro.MicroFiber<unknown, TgBotClientError | HandleUpdateError> | undefined;
|
|
2755
2803
|
}, string, never>;
|
|
2756
2804
|
}>;
|
|
2757
2805
|
declare class BotFactoryService extends BotFactoryService_base {
|
|
@@ -2764,29 +2812,25 @@ type RunBotInput = ({
|
|
|
2764
2812
|
declare const BotFactoryServiceDefault: {
|
|
2765
2813
|
runBot: (input: RunBotInput) => Micro.Micro<{
|
|
2766
2814
|
readonly reload: (input: Partial<RunBotInput>) => Promise<void>;
|
|
2767
|
-
readonly fiber: () => Micro.MicroFiber<unknown, TgBotClientError> | undefined;
|
|
2815
|
+
readonly fiber: () => Micro.MicroFiber<unknown, TgBotClientError | HandleUpdateError> | undefined;
|
|
2768
2816
|
}, string, never>;
|
|
2769
2817
|
};
|
|
2770
2818
|
|
|
2771
|
-
declare const TgBotClientConfig_base: Context.TagClass<TgBotClientConfig, "TgBotClientConfig", Required<TgBotClientSettingsInput>>;
|
|
2772
|
-
declare class TgBotClientConfig extends TgBotClientConfig_base {
|
|
2773
|
-
}
|
|
2774
|
-
|
|
2775
2819
|
type BotUpdatePollerServiceInterface = Micro.Micro.Success<typeof BotUpdatesPollerServiceDefault>;
|
|
2776
2820
|
declare const BotUpdatePollerService_base: Context.TagClass<BotUpdatePollerService, "BotUpdatePollerService", {
|
|
2777
2821
|
readonly runBot: Micro.Micro<void, never, BotMessageHandler | TgBotClientConfig>;
|
|
2778
|
-
readonly getFiber: () => Micro.MicroFiber<unknown, TgBotClientError> | undefined;
|
|
2822
|
+
readonly getFiber: () => Micro.MicroFiber<unknown, TgBotClientError | HandleUpdateError> | undefined;
|
|
2779
2823
|
}>;
|
|
2780
2824
|
declare class BotUpdatePollerService extends BotUpdatePollerService_base {
|
|
2781
2825
|
}
|
|
2782
2826
|
declare const BotUpdatesPollerServiceDefault: Micro.Micro<{
|
|
2783
2827
|
readonly runBot: Micro.Micro<void, never, BotMessageHandler | TgBotClientConfig>;
|
|
2784
|
-
readonly getFiber: () => Micro.MicroFiber<unknown, TgBotClientError> | undefined;
|
|
2828
|
+
readonly getFiber: () => Micro.MicroFiber<unknown, TgBotClientError | HandleUpdateError> | undefined;
|
|
2785
2829
|
}, never, never>;
|
|
2786
2830
|
|
|
2787
2831
|
declare const runTgChatBot: (input: RunBotInput) => Promise<{
|
|
2788
2832
|
readonly reload: (input: Partial<RunBotInput>) => Promise<void>;
|
|
2789
|
-
readonly fiber: () => Micro.MicroFiber<unknown, TgBotClientError> | undefined;
|
|
2833
|
+
readonly fiber: () => Micro.MicroFiber<unknown, TgBotClientError | HandleUpdateError> | undefined;
|
|
2790
2834
|
}>;
|
|
2791
2835
|
|
|
2792
2836
|
declare const getFile: (fileId: string) => Micro.Micro<File, TgBotClientError, TgBotClientConfig>;
|
|
@@ -2819,4 +2863,4 @@ type MessageEffect = keyof typeof MESSAGE_EFFECTS;
|
|
|
2819
2863
|
declare const messageEffectIdCodes: MessageEffect[];
|
|
2820
2864
|
declare const isMessageEffect: (input: unknown) => input is MessageEffect;
|
|
2821
2865
|
|
|
2822
|
-
export { type AddStickerToSetInput, type AffiliateInfo, type Animation, type AnswerCallbackQueryInput, type AnswerInlineQueryInput, type AnswerPreCheckoutQueryInput, type AnswerShippingQueryInput, type AnswerWebAppQueryInput, type Api, type ApproveChatJoinRequestInput, type Audio, type AvailableUpdateTypes, type BackgroundFill, type BackgroundFillFreeformGradient, type BackgroundFillGradient, type BackgroundFillSolid, type BackgroundType, type BackgroundTypeChatTheme, type BackgroundTypeFill, type BackgroundTypePattern, type BackgroundTypeWallpaper, type BanChatMemberInput, type BanChatSenderChatInput, type Birthdate, type BotCommand, type BotCommandScope, type BotCommandScopeAllChatAdministrators, type BotCommandScopeAllGroupChats, type BotCommandScopeAllPrivateChats, type BotCommandScopeChat, type BotCommandScopeChatAdministrators, type BotCommandScopeChatMember, type BotCommandScopeDefault, type BotDescription, BotFactoryService, BotFactoryServiceDefault, type BotInstance, type BotMessageHandlers, type BotName,
|
|
2866
|
+
export { type AddStickerToSetInput, type AffiliateInfo, type Animation, type AnswerCallbackQueryInput, type AnswerInlineQueryInput, type AnswerPreCheckoutQueryInput, type AnswerShippingQueryInput, type AnswerWebAppQueryInput, type Api, type ApproveChatJoinRequestInput, type Audio, type AvailableUpdateTypes, type BackgroundFill, type BackgroundFillFreeformGradient, type BackgroundFillGradient, type BackgroundFillSolid, type BackgroundType, type BackgroundTypeChatTheme, type BackgroundTypeFill, type BackgroundTypePattern, type BackgroundTypeWallpaper, type BanChatMemberInput, type BanChatSenderChatInput, type Birthdate, type BotCommand, type BotCommandScope, type BotCommandScopeAllChatAdministrators, type BotCommandScopeAllGroupChats, type BotCommandScopeAllPrivateChats, type BotCommandScopeChat, type BotCommandScopeChatAdministrators, type BotCommandScopeChatMember, type BotCommandScopeDefault, type BotDescription, BotFactoryService, BotFactoryServiceDefault, type BotInstance, type BotMessageHandlers, type BotName, BotResponse, type BotShortDescription, BotUpdatePollerService, type BotUpdatePollerServiceInterface, BotUpdatesPollerServiceDefault, type BusinessConnection, type BusinessIntro, type BusinessLocation, type BusinessMessagesDeleted, type BusinessOpeningHours, type BusinessOpeningHoursInterval, type CallbackGame, type CallbackQuery, type Chat, type ChatAdministratorRights, type ChatBackground, type ChatBoost, type ChatBoostAdded, type ChatBoostRemoved, type ChatBoostSource, type ChatBoostSourceGiftCode, type ChatBoostSourceGiveaway, type ChatBoostSourcePremium, type ChatBoostUpdated, type ChatFullInfo, type ChatInviteLink, type ChatJoinRequest, type ChatLocation, type ChatMember, type ChatMemberAdministrator, type ChatMemberBanned, type ChatMemberLeft, type ChatMemberMember, type ChatMemberOwner, type ChatMemberRestricted, type ChatMemberUpdated, type ChatPermissions, type ChatPhoto, type ChatShared, type ChosenInlineResult, type CloseForumTopicInput, type CloseGeneralForumTopicInput, type CloseInput, type Contact, type CopyMessageInput, type CopyMessagesInput, type CopyTextButton, type CreateChatInviteLinkInput, type CreateChatSubscriptionInviteLinkInput, type CreateForumTopicInput, type CreateInvoiceLinkInput, type CreateNewStickerSetInput, type DeclineChatJoinRequestInput, type DeleteChatPhotoInput, type DeleteChatStickerSetInput, type DeleteForumTopicInput, type DeleteMessageInput, type DeleteMessagesInput, type DeleteMyCommandsInput, type DeleteStickerFromSetInput, type DeleteStickerSetInput, type DeleteWebhookInput, type Dice, type Document, type EditChatInviteLinkInput, type EditChatSubscriptionInviteLinkInput, type EditForumTopicInput, type EditGeneralForumTopicInput, type EditMessageCaptionInput, type EditMessageLiveLocationInput, type EditMessageMediaInput, type EditMessageReplyMarkupInput, type EditMessageTextInput, type EditUserStarSubscriptionInput, type EncryptedCredentials, type EncryptedPassportElement, type ExportChatInviteLinkInput, type ExternalReplyInfo, type File$1 as File, type ForceReply, type ForumTopic, type ForumTopicClosed, type ForumTopicCreated, type ForumTopicEdited, type ForumTopicReopened, type ForwardMessageInput, type ForwardMessagesInput, type Game, type GameHighScore, type GeneralForumTopicHidden, type GeneralForumTopicUnhidden, type GetAvailableGiftsInput, type GetBusinessConnectionInput, type GetChatAdministratorsInput, type GetChatInput, type GetChatMemberCountInput, type GetChatMemberInput, type GetChatMenuButtonInput, type GetCustomEmojiStickersInput, type GetFileInput, type GetForumTopicIconStickersInput, type GetGameHighScoresInput, type GetMeInput, type GetMyCommandsInput, type GetMyDefaultAdministratorRightsInput, type GetMyDescriptionInput, type GetMyNameInput, type GetMyShortDescriptionInput, type GetStarTransactionsInput, type GetStickerSetInput, type GetUpdatesInput, type GetUserChatBoostsInput, type GetUserProfilePhotosInput, type GetWebhookInfoInput, type Gift, type Gifts, type Giveaway, type GiveawayCompleted, type GiveawayCreated, type GiveawayWinners, type HandleUpdateFunction, type HideGeneralForumTopicInput, type InaccessibleMessage, type InlineKeyboardButton, type InlineKeyboardMarkup, type InlineQuery, type InlineQueryResult, type InlineQueryResultArticle, type InlineQueryResultAudio, type InlineQueryResultCachedAudio, type InlineQueryResultCachedDocument, type InlineQueryResultCachedGif, type InlineQueryResultCachedMpeg4Gif, type InlineQueryResultCachedPhoto, type InlineQueryResultCachedSticker, type InlineQueryResultCachedVideo, type InlineQueryResultCachedVoice, type InlineQueryResultContact, type InlineQueryResultDocument, type InlineQueryResultGame, type InlineQueryResultGif, type InlineQueryResultLocation, type InlineQueryResultMpeg4Gif, type InlineQueryResultPhoto, type InlineQueryResultVenue, type InlineQueryResultVideo, type InlineQueryResultVoice, type InlineQueryResultsButton, type InputContactMessageContent, type InputFile, type InputInvoiceMessageContent, type InputLocationMessageContent, type InputMedia, type InputMediaAnimation, type InputMediaAudio, type InputMediaDocument, type InputMediaPhoto, type InputMediaVideo, type InputMessageContent, type InputPaidMedia, type InputPaidMediaPhoto, type InputPaidMediaVideo, type InputPollOption, type InputSticker, type InputTextMessageContent, type InputVenueMessageContent, type Invoice, type KeyboardButton, type KeyboardButtonPollType, type KeyboardButtonRequestChat, type KeyboardButtonRequestUsers, type LabeledPrice, type LeaveChatInput, type LinkPreviewOptions, type Location, type LogLevel, type LogOutInput, type LoginUrl, MESSAGE_EFFECTS, type MaskPosition, type MaybeInaccessibleMessage, type MenuButton, type MenuButtonCommands, type MenuButtonDefault, type MenuButtonWebApp, type Message, type MessageAutoDeleteTimerChanged, type MessageEffect, type MessageEntity, type MessageId, type MessageOrigin, type MessageOriginChannel, type MessageOriginChat, type MessageOriginHiddenUser, type MessageOriginUser, type MessageReactionCountUpdated, type MessageReactionUpdated, type OrderInfo, type PaidMedia, type PaidMediaInfo, type PaidMediaPhoto, type PaidMediaPreview, type PaidMediaPurchased, type PaidMediaVideo, type PassportData, type PassportElementError, type PassportElementErrorDataField, type PassportElementErrorFile, type PassportElementErrorFiles, type PassportElementErrorFrontSide, type PassportElementErrorReverseSide, type PassportElementErrorSelfie, type PassportElementErrorTranslationFile, type PassportElementErrorTranslationFiles, type PassportElementErrorUnspecified, type PassportFile, type PhotoSize, type PinChatMessageInput, type Poll, type PollAnswer, type PollOption, type PreCheckoutQuery, type PreparedInlineMessage, type PromoteChatMemberInput, type ProximityAlertTriggered, type ReactionCount, type ReactionType, type ReactionTypeCustomEmoji, type ReactionTypeEmoji, type ReactionTypePaid, type RefundStarPaymentInput, type RefundedPayment, type RemoveChatVerificationInput, type RemoveUserVerificationInput, type ReopenForumTopicInput, type ReopenGeneralForumTopicInput, type ReplaceStickerInSetInput, type ReplyKeyboardMarkup, type ReplyKeyboardRemove, type ReplyParameters, type ResponseParameters, type RestrictChatMemberInput, type RevenueWithdrawalState, type RevenueWithdrawalStateFailed, type RevenueWithdrawalStatePending, type RevenueWithdrawalStateSucceeded, type RevokeChatInviteLinkInput, type RunBotInput, type SavePreparedInlineMessageInput, type SendAnimationInput, type SendAudioInput, type SendChatActionInput, type SendContactInput, type SendDiceInput, type SendDocumentInput, type SendGameInput, type SendGiftInput, type SendInvoiceInput, type SendLocationInput, type SendMediaGroupInput, type SendMessageInput, type SendPaidMediaInput, type SendPhotoInput, type SendPollInput, type SendStickerInput, type SendVenueInput, type SendVideoInput, type SendVideoNoteInput, type SendVoiceInput, type SentWebAppMessage, type SetChatAdministratorCustomTitleInput, type SetChatDescriptionInput, type SetChatMenuButtonInput, type SetChatPermissionsInput, type SetChatPhotoInput, type SetChatStickerSetInput, type SetChatTitleInput, type SetCustomEmojiStickerSetThumbnailInput, type SetGameScoreInput, type SetMessageReactionInput, type SetMyCommandsInput, type SetMyDefaultAdministratorRightsInput, type SetMyDescriptionInput, type SetMyNameInput, type SetMyShortDescriptionInput, type SetPassportDataErrorsInput, type SetStickerEmojiListInput, type SetStickerKeywordsInput, type SetStickerMaskPositionInput, type SetStickerPositionInSetInput, type SetStickerSetThumbnailInput, type SetStickerSetTitleInput, type SetUserEmojiStatusInput, type SetWebhookInput, type SharedUser, type ShippingAddress, type ShippingOption, type ShippingQuery, type StarTransaction, type StarTransactions, type Sticker, type StickerSet, type StopMessageLiveLocationInput, type StopPollInput, type Story, type SuccessfulPayment, type SwitchInlineQueryChosenChat, type TextQuote, type TgBotClient, type TransactionPartner, type TransactionPartnerAffiliateProgram, type TransactionPartnerFragment, type TransactionPartnerOther, type TransactionPartnerTelegramAds, type TransactionPartnerTelegramApi, type TransactionPartnerUser, type UnbanChatMemberInput, type UnbanChatSenderChatInput, type UnhideGeneralForumTopicInput, type UnpinAllChatMessagesInput, type UnpinAllForumTopicMessagesInput, type UnpinAllGeneralForumTopicMessagesInput, type UnpinChatMessageInput, type Update, type UploadStickerFileInput, type User, type UserChatBoosts, type UserProfilePhotos, type UsersShared, type Venue, type VerifyChatInput, type VerifyUserInput, type Video, type VideoChatEnded, type VideoChatParticipantsInvited, type VideoChatScheduled, type VideoChatStarted, type VideoNote, type Voice, type WebAppData, type WebAppInfo, type WebhookInfo, type WriteAccessAllowed, defaultBaseUrl, isMessageEffect, makeTgBotClient, messageEffectIdCodes, runTgChatBot };
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
BotFactoryService: () => BotFactoryService,
|
|
34
34
|
BotFactoryServiceDefault: () => BotFactoryServiceDefault,
|
|
35
|
+
BotResponse: () => BotResponse,
|
|
35
36
|
BotUpdatePollerService: () => BotUpdatePollerService,
|
|
36
37
|
BotUpdatesPollerServiceDefault: () => BotUpdatesPollerServiceDefault,
|
|
37
38
|
MESSAGE_EFFECTS: () => MESSAGE_EFFECTS,
|
|
@@ -56,7 +57,7 @@ var BotMessageHandler = class extends Context.Tag("BotMessageHandler")() {
|
|
|
56
57
|
var Micro4 = __toESM(require("effect/Micro"));
|
|
57
58
|
var Context3 = __toESM(require("effect/Context"));
|
|
58
59
|
|
|
59
|
-
// src/bot/update-poller/poll-
|
|
60
|
+
// src/bot/update-poller/poll-updates.ts
|
|
60
61
|
var Micro3 = __toESM(require("effect/Micro"));
|
|
61
62
|
|
|
62
63
|
// src/bot/update-poller/settings.ts
|
|
@@ -64,8 +65,8 @@ var makeSettingsFrom = (input) => {
|
|
|
64
65
|
let limit = input.batch_size ?? 10;
|
|
65
66
|
let timeout = input.timeout ?? 10;
|
|
66
67
|
let max_empty_responses = input.max_empty_responses;
|
|
67
|
-
let update_types = input.update_types;
|
|
68
68
|
let log_level = input.log_level;
|
|
69
|
+
let on_error = input.on_error;
|
|
69
70
|
if (limit < 10 || limit > 100) {
|
|
70
71
|
console.warn("Wrong limit, must be in [10..100], using 10 instead");
|
|
71
72
|
limit = 10;
|
|
@@ -82,24 +83,35 @@ var makeSettingsFrom = (input) => {
|
|
|
82
83
|
console.warn("Wrong max_empty_responses, must be in [2..infinity], using infinity");
|
|
83
84
|
max_empty_responses = void 0;
|
|
84
85
|
}
|
|
85
|
-
if (!update_types) {
|
|
86
|
-
console.info("Handling only messages, ignoring others");
|
|
87
|
-
update_types = ["message"];
|
|
88
|
-
}
|
|
89
86
|
if (!log_level) {
|
|
90
87
|
log_level = "info";
|
|
91
88
|
}
|
|
92
|
-
|
|
89
|
+
if (!on_error) {
|
|
90
|
+
on_error = "stop";
|
|
91
|
+
}
|
|
92
|
+
const config = {
|
|
93
93
|
limit,
|
|
94
94
|
timeout,
|
|
95
95
|
max_empty_responses,
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
log_level,
|
|
97
|
+
on_error
|
|
98
98
|
};
|
|
99
|
+
console.log("bot configuration", config);
|
|
100
|
+
return config;
|
|
99
101
|
};
|
|
100
102
|
|
|
101
|
-
// src/bot/update-poller/fetch-
|
|
103
|
+
// src/bot/update-poller/fetch-and-handle.ts
|
|
102
104
|
var Micro2 = __toESM(require("effect/Micro"));
|
|
105
|
+
var Data = __toESM(require("effect/Data"));
|
|
106
|
+
|
|
107
|
+
// src/bot/message-handler/types.ts
|
|
108
|
+
var import_Data = require("effect/Data");
|
|
109
|
+
var BotResponse = class _BotResponse extends (0, import_Data.TaggedClass)("BotResponse") {
|
|
110
|
+
static make(result) {
|
|
111
|
+
return new _BotResponse({ response: result });
|
|
112
|
+
}
|
|
113
|
+
static ignore = new _BotResponse({});
|
|
114
|
+
};
|
|
103
115
|
|
|
104
116
|
// src/bot/message-handler/utils.ts
|
|
105
117
|
var extractUpdate = (input) => {
|
|
@@ -120,8 +132,8 @@ var Micro = __toESM(require("effect/Micro"));
|
|
|
120
132
|
var String = __toESM(require("effect/String"));
|
|
121
133
|
|
|
122
134
|
// src/client/errors.ts
|
|
123
|
-
var
|
|
124
|
-
var TgBotClientError = class _TgBotClientError extends
|
|
135
|
+
var import_Data2 = require("effect/Data");
|
|
136
|
+
var TgBotClientError = class _TgBotClientError extends (0, import_Data2.TaggedError)("TgBotClientError") {
|
|
125
137
|
static missingSuccess = new _TgBotClientError({
|
|
126
138
|
reason: {
|
|
127
139
|
type: "ClientInternalError",
|
|
@@ -217,8 +229,10 @@ var execute = (method, input) => Micro.gen(function* () {
|
|
|
217
229
|
return response.result;
|
|
218
230
|
});
|
|
219
231
|
|
|
220
|
-
// src/bot/update-poller/fetch-
|
|
221
|
-
var
|
|
232
|
+
// src/bot/update-poller/fetch-and-handle.ts
|
|
233
|
+
var HandleUpdateError = class extends Data.TaggedError("HandleUpdateError") {
|
|
234
|
+
};
|
|
235
|
+
var fetchAndHandle = ({ state, settings, handlers }) => Micro2.gen(function* () {
|
|
222
236
|
const updateId = state.lastUpdateId;
|
|
223
237
|
if (settings.log_level == "debug") {
|
|
224
238
|
console.debug("getting updates", state);
|
|
@@ -229,82 +243,146 @@ var fetchUpdates = ({ state, settings, handlers }) => Micro2.gen(function* () {
|
|
|
229
243
|
}).pipe(
|
|
230
244
|
Micro2.andThen((_) => _.sort((_2) => _2.update_id))
|
|
231
245
|
);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
for (const updateObject of updates) {
|
|
235
|
-
const update = extractUpdate(updateObject);
|
|
236
|
-
if (!update) {
|
|
237
|
-
console.warn("Unknown update", update);
|
|
238
|
-
hasError = true;
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
const handler = handlers[`on_${update.type}`];
|
|
242
|
-
if (!handler) {
|
|
243
|
-
if (settings.update_types.includes(update.type)) {
|
|
244
|
-
console.error("Handler for update not defined", update);
|
|
245
|
-
hasError = true;
|
|
246
|
-
break;
|
|
247
|
-
} else {
|
|
248
|
-
if (settings.log_level == "debug") {
|
|
249
|
-
console.debug("Ignored update", update);
|
|
250
|
-
}
|
|
251
|
-
lastSuccessId = updateObject.update_id;
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
if (update.type == "message" && "text" in update) {
|
|
256
|
-
console.info("Got new message", {
|
|
257
|
-
chatId: update.chat.id,
|
|
258
|
-
chatType: update.chat.type,
|
|
259
|
-
message: `${update.text.slice(0, 5)}...`
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
const handleResult = handler(update);
|
|
263
|
-
if ("chat" in update && handleResult) {
|
|
264
|
-
const response = yield* execute(`send_${handleResult.type}`, {
|
|
265
|
-
...handleResult,
|
|
266
|
-
chat_id: update.chat.id
|
|
267
|
-
});
|
|
268
|
-
if (settings.log_level == "debug" && "text") {
|
|
269
|
-
console.debug("bot response", response);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (!handleResult && settings.log_level == "debug") {
|
|
273
|
-
console.debug("handler returned no response for update", { update });
|
|
274
|
-
}
|
|
275
|
-
;
|
|
276
|
-
lastSuccessId = updateObject.update_id;
|
|
246
|
+
if (updates.length) {
|
|
247
|
+
console.debug(`got a batch of updates (${updates.length})`);
|
|
277
248
|
}
|
|
278
|
-
|
|
249
|
+
const lastUpdateId = updates.map((_) => _.update_id).sort().at(-1);
|
|
250
|
+
if (!lastUpdateId) return { updates: [], lastUpdateId: void 0 };
|
|
251
|
+
const hasError = yield* Micro2.forEach(
|
|
252
|
+
updates,
|
|
253
|
+
(update) => handleUpdate(update, settings, handlers).pipe(
|
|
254
|
+
Micro2.catchAll((error) => {
|
|
255
|
+
console.log("error", {
|
|
256
|
+
updateId: update.update_id,
|
|
257
|
+
updateKey: Object.keys(update).at(1),
|
|
258
|
+
name: error._tag
|
|
259
|
+
});
|
|
260
|
+
return Micro2.succeed(true);
|
|
261
|
+
})
|
|
262
|
+
),
|
|
263
|
+
{
|
|
264
|
+
concurrency: 10
|
|
265
|
+
}
|
|
266
|
+
).pipe(
|
|
267
|
+
Micro2.andThen((result) => result.every((error) => error == null))
|
|
268
|
+
);
|
|
269
|
+
if (lastUpdateId) {
|
|
279
270
|
yield* execute("get_updates", {
|
|
280
|
-
offset:
|
|
271
|
+
offset: lastUpdateId,
|
|
281
272
|
limit: 0
|
|
282
273
|
});
|
|
283
274
|
if (settings.log_level == "debug") {
|
|
284
|
-
console.debug("committed offset",
|
|
275
|
+
console.debug("committed offset", lastUpdateId);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return { updates, lastUpdateId, hasError };
|
|
279
|
+
});
|
|
280
|
+
var handleUpdate = (updateObject, settings, handlers) => Micro2.gen(function* () {
|
|
281
|
+
const update = extractUpdate(updateObject);
|
|
282
|
+
if (!update) {
|
|
283
|
+
return yield* Micro2.fail(
|
|
284
|
+
new HandleUpdateError({
|
|
285
|
+
name: "UnknownUpdate",
|
|
286
|
+
update: updateObject
|
|
287
|
+
})
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
const handler = handlers[`on_${update.type}`];
|
|
291
|
+
if (!handler) {
|
|
292
|
+
return yield* Micro2.fail(
|
|
293
|
+
new HandleUpdateError({
|
|
294
|
+
name: "HandlerNotDefined",
|
|
295
|
+
update: updateObject
|
|
296
|
+
})
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
if (update.type == "message" && "text" in update) {
|
|
300
|
+
console.info("Got a new text message", {
|
|
301
|
+
chatId: update.chat.id,
|
|
302
|
+
chatType: update.chat.type,
|
|
303
|
+
message: `${update.text.slice(0, 5)}...`
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
let handleUpdateError;
|
|
307
|
+
const handleResult = yield* Micro2.try({
|
|
308
|
+
try: () => handler(update),
|
|
309
|
+
catch: (error) => new HandleUpdateError({
|
|
310
|
+
name: "BotHandlerError",
|
|
311
|
+
update: updateObject,
|
|
312
|
+
cause: error
|
|
313
|
+
})
|
|
314
|
+
}).pipe(
|
|
315
|
+
Micro2.andThen((handleResult2) => {
|
|
316
|
+
if (handleResult2 instanceof Promise) {
|
|
317
|
+
return Micro2.tryPromise({
|
|
318
|
+
try: () => handleResult2,
|
|
319
|
+
catch: (error) => new HandleUpdateError({
|
|
320
|
+
name: "BotHandlerError",
|
|
321
|
+
update: updateObject,
|
|
322
|
+
cause: error
|
|
323
|
+
})
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
return Micro2.succeed(handleResult2);
|
|
327
|
+
}),
|
|
328
|
+
Micro2.catchAll((error) => {
|
|
329
|
+
handleUpdateError = error;
|
|
330
|
+
return Micro2.succeed(
|
|
331
|
+
BotResponse.make({
|
|
332
|
+
type: "message",
|
|
333
|
+
text: `Some internal error has happend(${error.name}) while handling this message`,
|
|
334
|
+
message_effect_id: MESSAGE_EFFECTS["\u{1F4A9}"],
|
|
335
|
+
...updateObject.message?.message_id ? {
|
|
336
|
+
reply_parameters: {
|
|
337
|
+
message_id: updateObject.message?.message_id
|
|
338
|
+
}
|
|
339
|
+
} : void 0,
|
|
340
|
+
reply_markup: {
|
|
341
|
+
inline_keyboard: [
|
|
342
|
+
[
|
|
343
|
+
{ text: "Repeat", callback_data: "fix:123" }
|
|
344
|
+
]
|
|
345
|
+
]
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
);
|
|
349
|
+
})
|
|
350
|
+
);
|
|
351
|
+
if (!handleResult && settings.log_level == "debug") {
|
|
352
|
+
console.log(`Bot response is undefined for update with ID #${updateObject.update_id}.`);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
;
|
|
356
|
+
if ("chat" in update && handleResult.response) {
|
|
357
|
+
const response = yield* execute(`send_${handleResult.response.type}`, {
|
|
358
|
+
...handleResult.response,
|
|
359
|
+
chat_id: update.chat.id
|
|
360
|
+
});
|
|
361
|
+
if (settings.log_level == "debug" && "text") {
|
|
362
|
+
console.debug("bot response", response);
|
|
285
363
|
}
|
|
286
364
|
}
|
|
287
|
-
return
|
|
365
|
+
return handleUpdateError;
|
|
288
366
|
});
|
|
289
367
|
|
|
290
|
-
// src/bot/update-poller/poll-
|
|
291
|
-
var
|
|
368
|
+
// src/bot/update-poller/poll-updates.ts
|
|
369
|
+
var pollUpdates = (input) => {
|
|
292
370
|
const state = {
|
|
293
371
|
lastUpdateId: void 0,
|
|
294
372
|
emptyResponses: 0
|
|
295
373
|
};
|
|
296
374
|
const settings = makeSettingsFrom(input.settings);
|
|
297
375
|
return Micro3.delay(1e3)(
|
|
298
|
-
|
|
376
|
+
fetchAndHandle({
|
|
299
377
|
state,
|
|
300
378
|
settings,
|
|
301
379
|
handlers: input.settings
|
|
302
380
|
})
|
|
303
381
|
).pipe(
|
|
304
382
|
Micro3.repeat({
|
|
305
|
-
while: ({ updates,
|
|
306
|
-
if (hasError) {
|
|
307
|
-
console.info("
|
|
383
|
+
while: ({ updates, lastUpdateId, hasError }) => {
|
|
384
|
+
if (hasError === true && settings.on_error == "stop") {
|
|
385
|
+
console.info("Could not handle some messages, quitting");
|
|
308
386
|
return false;
|
|
309
387
|
}
|
|
310
388
|
if (updates.length == 0) {
|
|
@@ -317,8 +395,8 @@ var pollAndHandle = (input) => {
|
|
|
317
395
|
state.emptyResponses = 0;
|
|
318
396
|
}
|
|
319
397
|
;
|
|
320
|
-
if (
|
|
321
|
-
state.lastUpdateId =
|
|
398
|
+
if (lastUpdateId) {
|
|
399
|
+
state.lastUpdateId = lastUpdateId + 1;
|
|
322
400
|
}
|
|
323
401
|
return true;
|
|
324
402
|
}
|
|
@@ -337,7 +415,7 @@ var BotUpdatesPollerServiceDefault = Micro4.gen(function* () {
|
|
|
337
415
|
const runBot = Micro4.gen(function* () {
|
|
338
416
|
console.log("run bot");
|
|
339
417
|
const messageHandler = yield* Micro4.service(BotMessageHandler);
|
|
340
|
-
const startFiber =
|
|
418
|
+
const startFiber = pollUpdates({
|
|
341
419
|
settings: messageHandler
|
|
342
420
|
}).pipe(
|
|
343
421
|
Micro4.forkDaemon,
|
|
@@ -484,6 +562,7 @@ var makeTgBotClient = (input) => {
|
|
|
484
562
|
0 && (module.exports = {
|
|
485
563
|
BotFactoryService,
|
|
486
564
|
BotFactoryServiceDefault,
|
|
565
|
+
BotResponse,
|
|
487
566
|
BotUpdatePollerService,
|
|
488
567
|
BotUpdatesPollerServiceDefault,
|
|
489
568
|
MESSAGE_EFFECTS,
|
package/dist/index.mjs
CHANGED
|
@@ -11,7 +11,7 @@ var BotMessageHandler = class extends Context.Tag("BotMessageHandler")() {
|
|
|
11
11
|
import * as Micro4 from "effect/Micro";
|
|
12
12
|
import * as Context3 from "effect/Context";
|
|
13
13
|
|
|
14
|
-
// src/bot/update-poller/poll-
|
|
14
|
+
// src/bot/update-poller/poll-updates.ts
|
|
15
15
|
import * as Micro3 from "effect/Micro";
|
|
16
16
|
|
|
17
17
|
// src/bot/update-poller/settings.ts
|
|
@@ -19,8 +19,8 @@ var makeSettingsFrom = (input) => {
|
|
|
19
19
|
let limit = input.batch_size ?? 10;
|
|
20
20
|
let timeout = input.timeout ?? 10;
|
|
21
21
|
let max_empty_responses = input.max_empty_responses;
|
|
22
|
-
let update_types = input.update_types;
|
|
23
22
|
let log_level = input.log_level;
|
|
23
|
+
let on_error = input.on_error;
|
|
24
24
|
if (limit < 10 || limit > 100) {
|
|
25
25
|
console.warn("Wrong limit, must be in [10..100], using 10 instead");
|
|
26
26
|
limit = 10;
|
|
@@ -37,24 +37,35 @@ var makeSettingsFrom = (input) => {
|
|
|
37
37
|
console.warn("Wrong max_empty_responses, must be in [2..infinity], using infinity");
|
|
38
38
|
max_empty_responses = void 0;
|
|
39
39
|
}
|
|
40
|
-
if (!update_types) {
|
|
41
|
-
console.info("Handling only messages, ignoring others");
|
|
42
|
-
update_types = ["message"];
|
|
43
|
-
}
|
|
44
40
|
if (!log_level) {
|
|
45
41
|
log_level = "info";
|
|
46
42
|
}
|
|
47
|
-
|
|
43
|
+
if (!on_error) {
|
|
44
|
+
on_error = "stop";
|
|
45
|
+
}
|
|
46
|
+
const config = {
|
|
48
47
|
limit,
|
|
49
48
|
timeout,
|
|
50
49
|
max_empty_responses,
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
log_level,
|
|
51
|
+
on_error
|
|
53
52
|
};
|
|
53
|
+
console.log("bot configuration", config);
|
|
54
|
+
return config;
|
|
54
55
|
};
|
|
55
56
|
|
|
56
|
-
// src/bot/update-poller/fetch-
|
|
57
|
+
// src/bot/update-poller/fetch-and-handle.ts
|
|
57
58
|
import * as Micro2 from "effect/Micro";
|
|
59
|
+
import * as Data from "effect/Data";
|
|
60
|
+
|
|
61
|
+
// src/bot/message-handler/types.ts
|
|
62
|
+
import { TaggedClass } from "effect/Data";
|
|
63
|
+
var BotResponse = class _BotResponse extends TaggedClass("BotResponse") {
|
|
64
|
+
static make(result) {
|
|
65
|
+
return new _BotResponse({ response: result });
|
|
66
|
+
}
|
|
67
|
+
static ignore = new _BotResponse({});
|
|
68
|
+
};
|
|
58
69
|
|
|
59
70
|
// src/bot/message-handler/utils.ts
|
|
60
71
|
var extractUpdate = (input) => {
|
|
@@ -75,8 +86,8 @@ import * as Micro from "effect/Micro";
|
|
|
75
86
|
import * as String from "effect/String";
|
|
76
87
|
|
|
77
88
|
// src/client/errors.ts
|
|
78
|
-
import
|
|
79
|
-
var TgBotClientError = class _TgBotClientError extends
|
|
89
|
+
import { TaggedError } from "effect/Data";
|
|
90
|
+
var TgBotClientError = class _TgBotClientError extends TaggedError("TgBotClientError") {
|
|
80
91
|
static missingSuccess = new _TgBotClientError({
|
|
81
92
|
reason: {
|
|
82
93
|
type: "ClientInternalError",
|
|
@@ -172,8 +183,10 @@ var execute = (method, input) => Micro.gen(function* () {
|
|
|
172
183
|
return response.result;
|
|
173
184
|
});
|
|
174
185
|
|
|
175
|
-
// src/bot/update-poller/fetch-
|
|
176
|
-
var
|
|
186
|
+
// src/bot/update-poller/fetch-and-handle.ts
|
|
187
|
+
var HandleUpdateError = class extends Data.TaggedError("HandleUpdateError") {
|
|
188
|
+
};
|
|
189
|
+
var fetchAndHandle = ({ state, settings, handlers }) => Micro2.gen(function* () {
|
|
177
190
|
const updateId = state.lastUpdateId;
|
|
178
191
|
if (settings.log_level == "debug") {
|
|
179
192
|
console.debug("getting updates", state);
|
|
@@ -184,82 +197,146 @@ var fetchUpdates = ({ state, settings, handlers }) => Micro2.gen(function* () {
|
|
|
184
197
|
}).pipe(
|
|
185
198
|
Micro2.andThen((_) => _.sort((_2) => _2.update_id))
|
|
186
199
|
);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
for (const updateObject of updates) {
|
|
190
|
-
const update = extractUpdate(updateObject);
|
|
191
|
-
if (!update) {
|
|
192
|
-
console.warn("Unknown update", update);
|
|
193
|
-
hasError = true;
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
const handler = handlers[`on_${update.type}`];
|
|
197
|
-
if (!handler) {
|
|
198
|
-
if (settings.update_types.includes(update.type)) {
|
|
199
|
-
console.error("Handler for update not defined", update);
|
|
200
|
-
hasError = true;
|
|
201
|
-
break;
|
|
202
|
-
} else {
|
|
203
|
-
if (settings.log_level == "debug") {
|
|
204
|
-
console.debug("Ignored update", update);
|
|
205
|
-
}
|
|
206
|
-
lastSuccessId = updateObject.update_id;
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (update.type == "message" && "text" in update) {
|
|
211
|
-
console.info("Got new message", {
|
|
212
|
-
chatId: update.chat.id,
|
|
213
|
-
chatType: update.chat.type,
|
|
214
|
-
message: `${update.text.slice(0, 5)}...`
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
const handleResult = handler(update);
|
|
218
|
-
if ("chat" in update && handleResult) {
|
|
219
|
-
const response = yield* execute(`send_${handleResult.type}`, {
|
|
220
|
-
...handleResult,
|
|
221
|
-
chat_id: update.chat.id
|
|
222
|
-
});
|
|
223
|
-
if (settings.log_level == "debug" && "text") {
|
|
224
|
-
console.debug("bot response", response);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
if (!handleResult && settings.log_level == "debug") {
|
|
228
|
-
console.debug("handler returned no response for update", { update });
|
|
229
|
-
}
|
|
230
|
-
;
|
|
231
|
-
lastSuccessId = updateObject.update_id;
|
|
200
|
+
if (updates.length) {
|
|
201
|
+
console.debug(`got a batch of updates (${updates.length})`);
|
|
232
202
|
}
|
|
233
|
-
|
|
203
|
+
const lastUpdateId = updates.map((_) => _.update_id).sort().at(-1);
|
|
204
|
+
if (!lastUpdateId) return { updates: [], lastUpdateId: void 0 };
|
|
205
|
+
const hasError = yield* Micro2.forEach(
|
|
206
|
+
updates,
|
|
207
|
+
(update) => handleUpdate(update, settings, handlers).pipe(
|
|
208
|
+
Micro2.catchAll((error) => {
|
|
209
|
+
console.log("error", {
|
|
210
|
+
updateId: update.update_id,
|
|
211
|
+
updateKey: Object.keys(update).at(1),
|
|
212
|
+
name: error._tag
|
|
213
|
+
});
|
|
214
|
+
return Micro2.succeed(true);
|
|
215
|
+
})
|
|
216
|
+
),
|
|
217
|
+
{
|
|
218
|
+
concurrency: 10
|
|
219
|
+
}
|
|
220
|
+
).pipe(
|
|
221
|
+
Micro2.andThen((result) => result.every((error) => error == null))
|
|
222
|
+
);
|
|
223
|
+
if (lastUpdateId) {
|
|
234
224
|
yield* execute("get_updates", {
|
|
235
|
-
offset:
|
|
225
|
+
offset: lastUpdateId,
|
|
236
226
|
limit: 0
|
|
237
227
|
});
|
|
238
228
|
if (settings.log_level == "debug") {
|
|
239
|
-
console.debug("committed offset",
|
|
229
|
+
console.debug("committed offset", lastUpdateId);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return { updates, lastUpdateId, hasError };
|
|
233
|
+
});
|
|
234
|
+
var handleUpdate = (updateObject, settings, handlers) => Micro2.gen(function* () {
|
|
235
|
+
const update = extractUpdate(updateObject);
|
|
236
|
+
if (!update) {
|
|
237
|
+
return yield* Micro2.fail(
|
|
238
|
+
new HandleUpdateError({
|
|
239
|
+
name: "UnknownUpdate",
|
|
240
|
+
update: updateObject
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
const handler = handlers[`on_${update.type}`];
|
|
245
|
+
if (!handler) {
|
|
246
|
+
return yield* Micro2.fail(
|
|
247
|
+
new HandleUpdateError({
|
|
248
|
+
name: "HandlerNotDefined",
|
|
249
|
+
update: updateObject
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
if (update.type == "message" && "text" in update) {
|
|
254
|
+
console.info("Got a new text message", {
|
|
255
|
+
chatId: update.chat.id,
|
|
256
|
+
chatType: update.chat.type,
|
|
257
|
+
message: `${update.text.slice(0, 5)}...`
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
let handleUpdateError;
|
|
261
|
+
const handleResult = yield* Micro2.try({
|
|
262
|
+
try: () => handler(update),
|
|
263
|
+
catch: (error) => new HandleUpdateError({
|
|
264
|
+
name: "BotHandlerError",
|
|
265
|
+
update: updateObject,
|
|
266
|
+
cause: error
|
|
267
|
+
})
|
|
268
|
+
}).pipe(
|
|
269
|
+
Micro2.andThen((handleResult2) => {
|
|
270
|
+
if (handleResult2 instanceof Promise) {
|
|
271
|
+
return Micro2.tryPromise({
|
|
272
|
+
try: () => handleResult2,
|
|
273
|
+
catch: (error) => new HandleUpdateError({
|
|
274
|
+
name: "BotHandlerError",
|
|
275
|
+
update: updateObject,
|
|
276
|
+
cause: error
|
|
277
|
+
})
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return Micro2.succeed(handleResult2);
|
|
281
|
+
}),
|
|
282
|
+
Micro2.catchAll((error) => {
|
|
283
|
+
handleUpdateError = error;
|
|
284
|
+
return Micro2.succeed(
|
|
285
|
+
BotResponse.make({
|
|
286
|
+
type: "message",
|
|
287
|
+
text: `Some internal error has happend(${error.name}) while handling this message`,
|
|
288
|
+
message_effect_id: MESSAGE_EFFECTS["\u{1F4A9}"],
|
|
289
|
+
...updateObject.message?.message_id ? {
|
|
290
|
+
reply_parameters: {
|
|
291
|
+
message_id: updateObject.message?.message_id
|
|
292
|
+
}
|
|
293
|
+
} : void 0,
|
|
294
|
+
reply_markup: {
|
|
295
|
+
inline_keyboard: [
|
|
296
|
+
[
|
|
297
|
+
{ text: "Repeat", callback_data: "fix:123" }
|
|
298
|
+
]
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
);
|
|
303
|
+
})
|
|
304
|
+
);
|
|
305
|
+
if (!handleResult && settings.log_level == "debug") {
|
|
306
|
+
console.log(`Bot response is undefined for update with ID #${updateObject.update_id}.`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
;
|
|
310
|
+
if ("chat" in update && handleResult.response) {
|
|
311
|
+
const response = yield* execute(`send_${handleResult.response.type}`, {
|
|
312
|
+
...handleResult.response,
|
|
313
|
+
chat_id: update.chat.id
|
|
314
|
+
});
|
|
315
|
+
if (settings.log_level == "debug" && "text") {
|
|
316
|
+
console.debug("bot response", response);
|
|
240
317
|
}
|
|
241
318
|
}
|
|
242
|
-
return
|
|
319
|
+
return handleUpdateError;
|
|
243
320
|
});
|
|
244
321
|
|
|
245
|
-
// src/bot/update-poller/poll-
|
|
246
|
-
var
|
|
322
|
+
// src/bot/update-poller/poll-updates.ts
|
|
323
|
+
var pollUpdates = (input) => {
|
|
247
324
|
const state = {
|
|
248
325
|
lastUpdateId: void 0,
|
|
249
326
|
emptyResponses: 0
|
|
250
327
|
};
|
|
251
328
|
const settings = makeSettingsFrom(input.settings);
|
|
252
329
|
return Micro3.delay(1e3)(
|
|
253
|
-
|
|
330
|
+
fetchAndHandle({
|
|
254
331
|
state,
|
|
255
332
|
settings,
|
|
256
333
|
handlers: input.settings
|
|
257
334
|
})
|
|
258
335
|
).pipe(
|
|
259
336
|
Micro3.repeat({
|
|
260
|
-
while: ({ updates,
|
|
261
|
-
if (hasError) {
|
|
262
|
-
console.info("
|
|
337
|
+
while: ({ updates, lastUpdateId, hasError }) => {
|
|
338
|
+
if (hasError === true && settings.on_error == "stop") {
|
|
339
|
+
console.info("Could not handle some messages, quitting");
|
|
263
340
|
return false;
|
|
264
341
|
}
|
|
265
342
|
if (updates.length == 0) {
|
|
@@ -272,8 +349,8 @@ var pollAndHandle = (input) => {
|
|
|
272
349
|
state.emptyResponses = 0;
|
|
273
350
|
}
|
|
274
351
|
;
|
|
275
|
-
if (
|
|
276
|
-
state.lastUpdateId =
|
|
352
|
+
if (lastUpdateId) {
|
|
353
|
+
state.lastUpdateId = lastUpdateId + 1;
|
|
277
354
|
}
|
|
278
355
|
return true;
|
|
279
356
|
}
|
|
@@ -292,7 +369,7 @@ var BotUpdatesPollerServiceDefault = Micro4.gen(function* () {
|
|
|
292
369
|
const runBot = Micro4.gen(function* () {
|
|
293
370
|
console.log("run bot");
|
|
294
371
|
const messageHandler = yield* Micro4.service(BotMessageHandler);
|
|
295
|
-
const startFiber =
|
|
372
|
+
const startFiber = pollUpdates({
|
|
296
373
|
settings: messageHandler
|
|
297
374
|
}).pipe(
|
|
298
375
|
Micro4.forkDaemon,
|
|
@@ -438,6 +515,7 @@ var makeTgBotClient = (input) => {
|
|
|
438
515
|
export {
|
|
439
516
|
BotFactoryService,
|
|
440
517
|
BotFactoryServiceDefault,
|
|
518
|
+
BotResponse,
|
|
441
519
|
BotUpdatePollerService,
|
|
442
520
|
BotUpdatesPollerServiceDefault,
|
|
443
521
|
MESSAGE_EFFECTS,
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -5,27 +5,25 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Motivation
|
|
9
9
|
|
|
10
|
-
The official documentation is available as a comprehensive [HTML page](https://core.telegram.org/bots/api), providing basic navigation. While functional, relying solely on this format can be somewhat inconvenient during bot development
|
|
10
|
+
The official documentation is available as a comprehensive [HTML page](https://core.telegram.org/bots/api), providing basic navigation. While functional, relying solely on this format can be somewhat inconvenient during bot **development**.
|
|
11
11
|
|
|
12
|
-
This client facilitates interaction with the Telegram Bot API
|
|
12
|
+
This client facilitates interaction with the **Telegram Bot API**. It was created primarily because **Telegram** does not offer an official TypeScript **SDK** for their **API**.
|
|
13
13
|
|
|
14
|
-
##
|
|
15
|
-
- **
|
|
16
|
-
- **[
|
|
17
|
-
- **Complete**: The entire API is generated from [the official documentation](https://core.telegram.org/bots/api) using a [code generator](./codegen/main.ts)
|
|
14
|
+
## Highlights:
|
|
15
|
+
- **[Client](#client)**: Light TypeScript client
|
|
16
|
+
- **[ChatBot runner](#chatbot-runner)**: Focus on the logic of your chat bot
|
|
17
|
+
- **Complete and Up-to-Date Telegram Bot API**: The entire API is generated from [the official documentation](https://core.telegram.org/bots/api) using a [code generator](./codegen/main.ts), ensuring this client remains in sync and supports every method and type provided by the **Telegram Bot API**.
|
|
18
18
|
- **Readable Method Names**: Method names, such as `setChatAdministratorCustomTitle`, are converted to snake_case for easier code readability, e.g., `set_chat_administrator_custom_title`.
|
|
19
19
|
- **Type Mapping**: Types from the documentation are converted to TypeScript types:
|
|
20
|
-
- `Integer`
|
|
21
|
-
- `True`
|
|
22
|
-
- `String or Number`
|
|
23
|
-
- Enumerated types, such as `Type of the chat
|
|
24
|
-
- And
|
|
20
|
+
- `Integer` → `number`
|
|
21
|
+
- `True` → `boolean`
|
|
22
|
+
- `String or Number` → `string | number`
|
|
23
|
+
- Enumerated types, such as `"Type of the chat can be either “private”, “group”, “supergroup” or “channel”"`, are converted to a standard union of literal types `"private" | "group" | "supergroup" | "channel"`
|
|
24
|
+
- And more...
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
#### Creating a Client
|
|
26
|
+
## Client
|
|
29
27
|
|
|
30
28
|
```typescript
|
|
31
29
|
import { makeTgBotClient } from "@effect-ak/tg-bot-client"
|
|
@@ -35,7 +33,7 @@ const client = makeTgBotClient({
|
|
|
35
33
|
});
|
|
36
34
|
```
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
### Executing api methods
|
|
39
37
|
|
|
40
38
|
`client` has an `execute` method which requires two arguments
|
|
41
39
|
|
|
@@ -93,16 +91,23 @@ const file =
|
|
|
93
91
|
});
|
|
94
92
|
```
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
## ChatBot runner
|
|
95
|
+
|
|
96
|
+
### How this library helps
|
|
97
|
+
|
|
98
|
+
A chatbot is essentially a **function** that is triggered by every user message, whether it's a text message, a reaction, or a payment update. Lets name this function as **handler function**
|
|
99
|
+
|
|
100
|
+
This library handles the task of reading these **updates** from the Telegram Bot API's **message queue**. It then invokes the appropriate **handler function** with the received update.
|
|
101
|
+
|
|
102
|
+
### Playground
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
Develop/Run chat bots in your browser via **[Chat Bot Playground](https://effect-ak.github.io/telegram-bot-playground/)**
|
|
99
105
|
|
|
100
|
-
|
|
106
|
+
### Local run
|
|
101
107
|
|
|
102
|
-
|
|
108
|
+
You can write the logic for your chatbot and **run it locally** and message to your bot via **Telegram** messenger.
|
|
103
109
|
|
|
104
|
-
|
|
105
|
-
- **Sequential Message Processing:** Messages in the queue are read one by one, and the handler is invoked for each message. If an error occurs in the handler, the next message remains in the queue, and the bot stops running. When the handler successfully processes a message, it proceeds to the next one.
|
|
110
|
+
Take a look at examples [here](example)
|
|
106
111
|
|
|
107
112
|
### Setup Instructions
|
|
108
113
|
|
|
@@ -123,38 +128,66 @@ The Telegram bot supports both push and pull notification models for messages. T
|
|
|
123
128
|
Create a file named `bot.js` and add your bot's logic as shown below:
|
|
124
129
|
|
|
125
130
|
```typescript
|
|
126
|
-
import { MESSAGE_EFFECTS, runTgChatBot } from "@effect-ak/tg-bot-client"
|
|
131
|
+
import { MESSAGE_EFFECTS, runTgChatBot, BotResponse } from "@effect-ak/tg-bot-client"
|
|
127
132
|
|
|
128
133
|
runTgChatBot({
|
|
129
134
|
type: "fromJsonFile",
|
|
130
135
|
on_message: (msg) => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
|
|
137
|
+
if (!msg.text) return BotResponse.ignore;
|
|
138
|
+
|
|
139
|
+
if (msg?.text === "bye") {
|
|
140
|
+
return BotResponse.make({
|
|
141
|
+
type: "message",
|
|
142
|
+
text: "See you later!",
|
|
143
|
+
message_effect_id: MESSAGE_EFFECTS["❤️"]
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return BotResponse.make({
|
|
148
|
+
type: "message",
|
|
149
|
+
text: "I'm a simple bot"
|
|
150
|
+
})
|
|
143
151
|
}
|
|
144
152
|
})
|
|
145
153
|
```
|
|
146
154
|
|
|
147
|
-
**Explanation:**
|
|
148
|
-
- **Import Statements:** Import necessary modules from the `@effect-ak/tg-bot-client` package.
|
|
149
|
-
- **`runTgChatBot` Function:** Initializes the Telegram chatbot using the configuration from the `config.json` file.
|
|
150
|
-
- **`on_message` Handler:** Defines the logic for handling incoming messages.
|
|
151
|
-
- If the message text is `"bye"`, the bot responds with `"See you later!"` and adds a heart emoji effect.
|
|
152
|
-
- For any other message, the bot responds with `"I'm a simple bot"`.
|
|
153
|
-
|
|
154
155
|
3. **Run the Bot**
|
|
155
156
|
|
|
156
157
|
To start your chatbot, execute the following command in your terminal:
|
|
157
158
|
|
|
158
159
|
```bash
|
|
159
160
|
node bot.js
|
|
160
|
-
```
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### How It Works: Pull Model
|
|
164
|
+
|
|
165
|
+
The Telegram bot supports both **push** and **pull** notification models for messages. This package uses the **pull** model for several reasons:
|
|
166
|
+
|
|
167
|
+
- **Run chat bots anywhere without exposing public ports or URLs:** The Telegram **push** model requires you as a developer to specify a public URL where updates will be sent.
|
|
168
|
+
For example, **pull** allows running chat bots in a web browser, which doesn't have a public URL.
|
|
169
|
+
|
|
170
|
+
- **Leveraging Telegram's infrastructure:** Telegram keeps new updates for 24 hours and gives you plenty of time to process them.
|
|
171
|
+
|
|
172
|
+
#### Few details and clarifications
|
|
173
|
+
|
|
174
|
+
Developer is responsible only for **Handler Function**.
|
|
175
|
+
|
|
176
|
+
**ChatBot runner** reads updates from the queue and shifts **ID** of last proceeded update so that **handler function** won't be triggered multiple times for the same update.
|
|
177
|
+
|
|
178
|
+
```mermaid
|
|
179
|
+
graph TD
|
|
180
|
+
HandlerFunction[/**Handler Function**/]
|
|
181
|
+
Library[**ChatBot runner**]
|
|
182
|
+
TgBot[Telegram chat bot]
|
|
183
|
+
MessageQueue[**Bot updates queue**<br>_api.telegram.org/bot/updates_]
|
|
184
|
+
User -->|Sends Update, e.g a text message| TgBot
|
|
185
|
+
TgBot -->|Stores Update for 24 hours | MessageQueue
|
|
186
|
+
|
|
187
|
+
subgraph Pull Updates
|
|
188
|
+
Library -->|Fetches Update | MessageQueue
|
|
189
|
+
Library -->|Invokes| HandlerFunction
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
```
|