@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 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 ErrorReason = {
7
- readonly type: "NotOkResponse";
8
- readonly errorCode?: number;
9
- readonly details?: string;
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
- readonly type: "UnexpectedResponse";
12
- readonly response: unknown;
20
+ type: "UnexpectedResponse";
21
+ response: unknown;
13
22
  } | {
14
- readonly type: "ClientInternalError";
15
- readonly cause: unknown;
23
+ type: "ClientInternalError";
24
+ cause: unknown;
16
25
  } | {
17
- readonly type: "UnableToGetFile";
18
- readonly cause: unknown;
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 BotResponse = {
2756
+ type BotResult = {
2731
2757
  [K in keyof Api]: K extends `send_${infer R}` ? {
2732
- type: Lowercase<R>;
2758
+ type: R;
2733
2759
  } & Omit<Parameters<Api[K]>[0], 'chat_id'> : never;
2734
2760
  }[keyof Api];
2735
- type BotMessageHandlers = {
2736
- [K in AvailableUpdateTypes as `on_${K}`]?: (update: NonNullable<Update[K]>) => BotResponse | undefined;
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
- readonly log_level?: LogLevel;
2742
- readonly update_types?: AvailableUpdateTypes[];
2743
- readonly batch_size?: number;
2744
- readonly timeout?: number;
2745
- readonly max_empty_responses?: number;
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, type 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 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 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 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 };
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-and-handle.ts
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
- return {
89
+ if (!on_error) {
90
+ on_error = "stop";
91
+ }
92
+ const config = {
93
93
  limit,
94
94
  timeout,
95
95
  max_empty_responses,
96
- update_types,
97
- log_level
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-updates.ts
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 Data = __toESM(require("effect/Data"));
124
- var TgBotClientError = class _TgBotClientError extends Data.TaggedError("TgBotClientError") {
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-updates.ts
221
- var fetchUpdates = ({ state, settings, handlers }) => Micro2.gen(function* () {
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
- let lastSuccessId = void 0;
233
- let hasError = false;
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
- if (hasError && lastSuccessId) {
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: lastSuccessId,
271
+ offset: lastUpdateId,
281
272
  limit: 0
282
273
  });
283
274
  if (settings.log_level == "debug") {
284
- console.debug("committed offset", lastSuccessId);
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 { updates, lastSuccessId, hasError };
365
+ return handleUpdateError;
288
366
  });
289
367
 
290
- // src/bot/update-poller/poll-and-handle.ts
291
- var pollAndHandle = (input) => {
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
- fetchUpdates({
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, lastSuccessId, hasError }) => {
306
- if (hasError) {
307
- console.info("error in handler, quitting");
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 (lastSuccessId) {
321
- state.lastUpdateId = lastSuccessId + 1;
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 = pollAndHandle({
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-and-handle.ts
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
- return {
43
+ if (!on_error) {
44
+ on_error = "stop";
45
+ }
46
+ const config = {
48
47
  limit,
49
48
  timeout,
50
49
  max_empty_responses,
51
- update_types,
52
- log_level
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-updates.ts
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 * as Data from "effect/Data";
79
- var TgBotClientError = class _TgBotClientError extends Data.TaggedError("TgBotClientError") {
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-updates.ts
176
- var fetchUpdates = ({ state, settings, handlers }) => Micro2.gen(function* () {
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
- let lastSuccessId = void 0;
188
- let hasError = false;
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
- if (hasError && lastSuccessId) {
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: lastSuccessId,
225
+ offset: lastUpdateId,
236
226
  limit: 0
237
227
  });
238
228
  if (settings.log_level == "debug") {
239
- console.debug("committed offset", lastSuccessId);
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 { updates, lastSuccessId, hasError };
319
+ return handleUpdateError;
243
320
  });
244
321
 
245
- // src/bot/update-poller/poll-and-handle.ts
246
- var pollAndHandle = (input) => {
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
- fetchUpdates({
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, lastSuccessId, hasError }) => {
261
- if (hasError) {
262
- console.info("error in handler, quitting");
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 (lastSuccessId) {
276
- state.lastUpdateId = lastSuccessId + 1;
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 = pollAndHandle({
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-ak/tg-bot-client",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "homepage": "https://effect-ak.github.io/telegram-bot-client",
5
5
  "author": {
6
6
  "name": "Aleksandr Kondaurov",
package/readme.md CHANGED
@@ -5,27 +5,25 @@
5
5
 
6
6
 
7
7
 
8
- ### Motivation
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. It was created primarily because Telegram does not offer an official SDK for their 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
- ## Features:
15
- - **Typesafe Client**: This is a clean client written in TypeScript with no abstractions.
16
- - **[Playground](https://effect-ak.github.io/telegram-bot-playground/)**: Develop/Run chat bots in your browser
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` becomes `number`
21
- - `True` becomes `boolean`
22
- - `String or Number` becomes `string | number`
23
- - Enumerated types, such as `Type of the chat, can be either “private”, “group”, “supergroup” or “channel”` becomes a standard union of literal types `"private"| "group" | "supergroup" | "channel"`
24
- - And so on
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
- ### Usage example
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
- #### Executing api methods
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
- ### Chatbot Support
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
- You can write the logic for your chatbot and run it locally.
104
+ Develop/Run chat bots in your browser via **[Chat Bot Playground](https://effect-ak.github.io/telegram-bot-playground/)**
99
105
 
100
- Take a look at [example](./example/echo-bot.ts)
106
+ ### Local run
101
107
 
102
- The Telegram bot supports both push and pull notification models for messages. This package uses the **pull** model for several reasons:
108
+ You can write the logic for your chatbot and **run it locally** and message to your bot via **Telegram** messenger.
103
109
 
104
- - **Flexibility in Handler Deployment:** Allows you to run the bot handler on any JS platform (NodeJs, Browser)
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
- if (msg?.text === "bye") {
132
- return {
133
- type: "message",
134
- text: "See you later!",
135
- message_effect_id: MESSAGE_EFFECTS["❤️"]
136
- }
137
- }
138
-
139
- return {
140
- type: "message",
141
- text: "I'm a simple bot"
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
+ ```