@effect-ak/tg-bot-client 0.6.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +294 -0
- package/dist/index.cjs +209 -0
- package/dist/index.d.cts +98 -0
- package/dist/index.d.ts +63 -28
- package/dist/index.js +169 -1
- package/package.json +19 -56
- package/src/client-file.ts +73 -0
- package/src/client.ts +34 -0
- package/src/config.ts +10 -0
- package/src/const.ts +20 -0
- package/src/errors.ts +18 -0
- package/src/execute.ts +81 -0
- package/src/guards.ts +27 -0
- package/src/index.ts +8 -0
- package/src/utils.ts +3 -0
- package/test/execute.test.ts +102 -0
- package/test/file.test.ts +23 -0
- package/test/fixture.ts +35 -0
- package/tsconfig.json +12 -0
- package/tsup.config.json +10 -0
- package/dist/bot-bundle.js +0 -22
- package/dist/bot-bundle.mjs +0 -22
- package/dist/bot.d.ts +0 -111
- package/dist/bot.js +0 -1
- package/dist/bot.mjs +0 -1
- package/dist/config-3uU0YevV.d.ts +0 -3250
- package/dist/index.mjs +0 -1
- package/dist/webapp.d.ts +0 -392
- package/dist/webapp.js +0 -1
- package/dist/webapp.mjs +0 -0
- package/readme.md +0 -236
package/dist/index.d.ts
CHANGED
|
@@ -1,51 +1,84 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { A as Api, T as TgBotClientError, a as TgBotApiToken, U as Update } from './config-3uU0YevV.js';
|
|
3
|
-
export { bK as AcceptedGiftTypes, c as AddStickerToSetInput, bL as AffiliateInfo, bJ as AllowedUpdateName, bM as Animation, d as AnswerCallbackQueryInput, e as AnswerInlineQueryInput, f as AnswerPreCheckoutQueryInput, g as AnswerShippingQueryInput, h as AnswerWebAppQueryInput, i as ApproveChatJoinRequestInput, j as ApproveSuggestedPostInput, bN as Audio, bO as BackgroundFill, bP as BackgroundFillFreeformGradient, bQ as BackgroundFillGradient, bR as BackgroundFillSolid, bS as BackgroundType, bT as BackgroundTypeChatTheme, bU as BackgroundTypeFill, bV as BackgroundTypePattern, bW as BackgroundTypeWallpaper, B as BanChatMemberInput, k as BanChatSenderChatInput, bX as Birthdate, bY as BotCommand, bZ as BotCommandScope, b_ as BotCommandScopeAllChatAdministrators, b$ as BotCommandScopeAllGroupChats, c0 as BotCommandScopeAllPrivateChats, c1 as BotCommandScopeChat, c2 as BotCommandScopeChatAdministrators, c3 as BotCommandScopeChatMember, c4 as BotCommandScopeDefault, c5 as BotDescription, c6 as BotName, c7 as BotShortDescription, c8 as BusinessBotRights, c9 as BusinessConnection, ca as BusinessIntro, cb as BusinessLocation, cc as BusinessMessagesDeleted, cd as BusinessOpeningHours, ce as BusinessOpeningHoursInterval, cf as CallbackGame, cg as CallbackQuery, ch as Chat, ci as ChatAdministratorRights, cj as ChatBackground, ck as ChatBoost, cl as ChatBoostAdded, cm as ChatBoostRemoved, cn as ChatBoostSource, co as ChatBoostSourceGiftCode, cp as ChatBoostSourceGiveaway, cq as ChatBoostSourcePremium, cr as ChatBoostUpdated, cs as ChatFullInfo, ct as ChatInviteLink, cu as ChatJoinRequest, cv as ChatLocation, cw as ChatMember, cx as ChatMemberAdministrator, cy as ChatMemberBanned, cz as ChatMemberLeft, cA as ChatMemberMember, cB as ChatMemberOwner, cC as ChatMemberRestricted, cD as ChatMemberUpdated, cE as ChatPermissions, cF as ChatPhoto, cG as ChatShared, cH as Checklist, cI as ChecklistTask, cJ as ChecklistTasksAdded, cK as ChecklistTasksDone, cL as ChosenInlineResult, l as CloseForumTopicInput, m as CloseGeneralForumTopicInput, C as CloseInput, cM as Contact, n as ConvertGiftToStarsInput, o as CopyMessageInput, p as CopyMessagesInput, cN as CopyTextButton, q as CreateChatInviteLinkInput, r as CreateChatSubscriptionInviteLinkInput, s as CreateForumTopicInput, t as CreateInvoiceLinkInput, u as CreateNewStickerSetInput, D as DeclineChatJoinRequestInput, v as DeclineSuggestedPostInput, w as DeleteBusinessMessagesInput, x as DeleteChatPhotoInput, y as DeleteChatStickerSetInput, z as DeleteForumTopicInput, E as DeleteMessageInput, F as DeleteMessagesInput, G as DeleteMyCommandsInput, H as DeleteStickerFromSetInput, I as DeleteStickerSetInput, J as DeleteStoryInput, K as DeleteWebhookInput, cO as Dice, cP as DirectMessagePriceChanged, cQ as DirectMessagesTopic, cR as Document, L as EditChatInviteLinkInput, M as EditChatSubscriptionInviteLinkInput, N as EditForumTopicInput, O as EditGeneralForumTopicInput, P as EditMessageCaptionInput, Q as EditMessageChecklistInput, R as EditMessageLiveLocationInput, S as EditMessageMediaInput, V as EditMessageReplyMarkupInput, W as EditMessageTextInput, X as EditStoryInput, Y as EditUserStarSubscriptionInput, cS as EncryptedCredentials, cT as EncryptedPassportElement, Z as ExportChatInviteLinkInput, cU as ExternalReplyInfo, cV as File, cW as ForceReply, cX as ForumTopic, cY as ForumTopicClosed, cZ as ForumTopicCreated, c_ as ForumTopicEdited, c$ as ForumTopicReopened, _ as ForwardMessageInput, $ as ForwardMessagesInput, d0 as Game, d1 as GameHighScore, d2 as GeneralForumTopicHidden, d3 as GeneralForumTopicUnhidden, a0 as GetAvailableGiftsInput, a1 as GetBusinessAccountGiftsInput, a2 as GetBusinessAccountStarBalanceInput, a3 as GetBusinessConnectionInput, a5 as GetChatAdministratorsInput, a4 as GetChatInput, a7 as GetChatMemberCountInput, a6 as GetChatMemberInput, a8 as GetChatMenuButtonInput, a9 as GetCustomEmojiStickersInput, aa as GetFileInput, ab as GetForumTopicIconStickersInput, ac as GetGameHighScoresInput, ad as GetMeInput, ae as GetMyCommandsInput, af as GetMyDefaultAdministratorRightsInput, ag as GetMyDescriptionInput, ah as GetMyNameInput, ai as GetMyShortDescriptionInput, aj as GetMyStarBalanceInput, ak as GetStarTransactionsInput, al as GetStickerSetInput, am as GetUpdatesInput, an as GetUserChatBoostsInput, ao as GetUserProfilePhotosInput, ap as GetWebhookInfoInput, d4 as Gift, d5 as GiftInfo, aq as GiftPremiumSubscriptionInput, d6 as Gifts, d7 as Giveaway, d8 as GiveawayCompleted, d9 as GiveawayCreated, da as GiveawayWinners, ar as HideGeneralForumTopicInput, db as InaccessibleMessage, dc as InlineKeyboardButton, dd as InlineKeyboardMarkup, de as InlineQuery, df as InlineQueryResult, dg as InlineQueryResultArticle, dh as InlineQueryResultAudio, di as InlineQueryResultCachedAudio, dj as InlineQueryResultCachedDocument, dk as InlineQueryResultCachedGif, dl as InlineQueryResultCachedMpeg4Gif, dm as InlineQueryResultCachedPhoto, dn as InlineQueryResultCachedSticker, dp as InlineQueryResultCachedVideo, dq as InlineQueryResultCachedVoice, dr as InlineQueryResultContact, ds as InlineQueryResultDocument, dt as InlineQueryResultGame, du as InlineQueryResultGif, dv as InlineQueryResultLocation, dw as InlineQueryResultMpeg4Gif, dx as InlineQueryResultPhoto, dz as InlineQueryResultVenue, dA as InlineQueryResultVideo, dB as InlineQueryResultVoice, dy as InlineQueryResultsButton, dC as InputChecklist, dD as InputChecklistTask, dE as InputContactMessageContent, dF as InputFile, dG as InputInvoiceMessageContent, dH as InputLocationMessageContent, dI as InputMedia, dJ as InputMediaAnimation, dK as InputMediaAudio, dL as InputMediaDocument, dM as InputMediaPhoto, dN as InputMediaVideo, dO as InputMessageContent, dP as InputPaidMedia, dQ as InputPaidMediaPhoto, dR as InputPaidMediaVideo, dS as InputPollOption, dT as InputProfilePhoto, dU as InputProfilePhotoAnimated, dV as InputProfilePhotoStatic, dW as InputSticker, dX as InputStoryContent, dY as InputStoryContentPhoto, dZ as InputStoryContentVideo, d_ as InputTextMessageContent, d$ as InputVenueMessageContent, e0 as Invoice, e1 as KeyboardButton, e2 as KeyboardButtonPollType, e3 as KeyboardButtonRequestChat, e4 as KeyboardButtonRequestUsers, e5 as LabeledPrice, as as LeaveChatInput, e6 as LinkPreviewOptions, e7 as Location, e8 as LocationAddress, at as LogOutInput, e9 as LoginUrl, ea as MaskPosition, eb as MaybeInaccessibleMessage, ec as MenuButton, ed as MenuButtonCommands, ee as MenuButtonDefault, ef as MenuButtonWebApp, eg as Message, eh as MessageAutoDeleteTimerChanged, ei as MessageEntity, ej as MessageId, ek as MessageOrigin, el as MessageOriginChannel, em as MessageOriginChat, en as MessageOriginHiddenUser, eo as MessageOriginUser, ep as MessageReactionCountUpdated, eq as MessageReactionUpdated, er as OrderInfo, es as OwnedGift, et as OwnedGiftRegular, ev as OwnedGiftUnique, eu as OwnedGifts, ew as PaidMedia, ex as PaidMediaInfo, ey as PaidMediaPhoto, ez as PaidMediaPreview, eA as PaidMediaPurchased, eB as PaidMediaVideo, eC as PaidMessagePriceChanged, eD as PassportData, eE as PassportElementError, eF as PassportElementErrorDataField, eG as PassportElementErrorFile, eH as PassportElementErrorFiles, eI as PassportElementErrorFrontSide, eJ as PassportElementErrorReverseSide, eK as PassportElementErrorSelfie, eL as PassportElementErrorTranslationFile, eM as PassportElementErrorTranslationFiles, eN as PassportElementErrorUnspecified, eO as PassportFile, eP as PhotoSize, au as PinChatMessageInput, eQ as Poll, eR as PollAnswer, eS as PollOption, av as PostStoryInput, eT as PreCheckoutQuery, eU as PreparedInlineMessage, aw as PromoteChatMemberInput, eV as ProximityAlertTriggered, eW as ReactionCount, eX as ReactionType, eY as ReactionTypeCustomEmoji, eZ as ReactionTypeEmoji, e_ as ReactionTypePaid, ax as ReadBusinessMessageInput, ay as RefundStarPaymentInput, e$ as RefundedPayment, az as RemoveBusinessAccountProfilePhotoInput, aA as RemoveChatVerificationInput, aB as RemoveUserVerificationInput, aC as ReopenForumTopicInput, aD as ReopenGeneralForumTopicInput, aE as ReplaceStickerInSetInput, f0 as ReplyKeyboardMarkup, f1 as ReplyKeyboardRemove, f2 as ReplyParameters, f3 as ResponseParameters, aF as RestrictChatMemberInput, f4 as RevenueWithdrawalState, f5 as RevenueWithdrawalStateFailed, f6 as RevenueWithdrawalStatePending, f7 as RevenueWithdrawalStateSucceeded, aG as RevokeChatInviteLinkInput, aH as SavePreparedInlineMessageInput, aI as SendAnimationInput, aJ as SendAudioInput, aK as SendChatActionInput, aL as SendChecklistInput, aM as SendContactInput, aN as SendDiceInput, aO as SendDocumentInput, aP as SendGameInput, aQ as SendGiftInput, aR as SendInvoiceInput, aS as SendLocationInput, aT as SendMediaGroupInput, aU as SendMessageInput, aV as SendPaidMediaInput, aW as SendPhotoInput, aX as SendPollInput, aY as SendStickerInput, aZ as SendVenueInput, a_ as SendVideoInput, a$ as SendVideoNoteInput, b0 as SendVoiceInput, f8 as SentWebAppMessage, b1 as SetBusinessAccountBioInput, b2 as SetBusinessAccountGiftSettingsInput, b3 as SetBusinessAccountNameInput, b4 as SetBusinessAccountProfilePhotoInput, b5 as SetBusinessAccountUsernameInput, b6 as SetChatAdministratorCustomTitleInput, b7 as SetChatDescriptionInput, b8 as SetChatMenuButtonInput, b9 as SetChatPermissionsInput, ba as SetChatPhotoInput, bb as SetChatStickerSetInput, bc as SetChatTitleInput, bd as SetCustomEmojiStickerSetThumbnailInput, be as SetGameScoreInput, bf as SetMessageReactionInput, bg as SetMyCommandsInput, bh as SetMyDefaultAdministratorRightsInput, bi as SetMyDescriptionInput, bj as SetMyNameInput, bk as SetMyShortDescriptionInput, bl as SetPassportDataErrorsInput, bm as SetStickerEmojiListInput, bn as SetStickerKeywordsInput, bo as SetStickerMaskPositionInput, bp as SetStickerPositionInSetInput, bq as SetStickerSetThumbnailInput, br as SetStickerSetTitleInput, bs as SetUserEmojiStatusInput, bt as SetWebhookInput, f9 as SharedUser, fa as ShippingAddress, fb as ShippingOption, fc as ShippingQuery, fd as StarAmount, fe as StarTransaction, ff as StarTransactions, fg as Sticker, fh as StickerSet, bu as StopMessageLiveLocationInput, bv as StopPollInput, fi as Story, fj as StoryArea, fk as StoryAreaPosition, fl as StoryAreaType, fm as StoryAreaTypeLink, fn as StoryAreaTypeLocation, fo as StoryAreaTypeSuggestedReaction, fp as StoryAreaTypeUniqueGift, fq as StoryAreaTypeWeather, fr as SuccessfulPayment, fs as SuggestedPostApprovalFailed, ft as SuggestedPostApproved, fu as SuggestedPostDeclined, fv as SuggestedPostInfo, fw as SuggestedPostPaid, fx as SuggestedPostParameters, fy as SuggestedPostPrice, fz as SuggestedPostRefunded, fA as SwitchInlineQueryChosenChat, fB as TextQuote, b as TgBotApiBaseUrl, fC as TransactionPartner, fD as TransactionPartnerAffiliateProgram, fE as TransactionPartnerChat, fF as TransactionPartnerFragment, fG as TransactionPartnerOther, fH as TransactionPartnerTelegramAds, fI as TransactionPartnerTelegramApi, fJ as TransactionPartnerUser, bw as TransferBusinessAccountStarsInput, bx as TransferGiftInput, by as UnbanChatMemberInput, bz as UnbanChatSenderChatInput, bA as UnhideGeneralForumTopicInput, fK as UniqueGift, fL as UniqueGiftBackdrop, fM as UniqueGiftBackdropColors, fN as UniqueGiftInfo, fO as UniqueGiftModel, fP as UniqueGiftSymbol, bB as UnpinAllChatMessagesInput, bC as UnpinAllForumTopicMessagesInput, bD as UnpinAllGeneralForumTopicMessagesInput, bE as UnpinChatMessageInput, bF as UpgradeGiftInput, bG as UploadStickerFileInput, fQ as User, fR as UserChatBoosts, fS as UserProfilePhotos, fT as UsersShared, fU as Venue, bH as VerifyChatInput, bI as VerifyUserInput, fV as Video, fW as VideoChatEnded, fX as VideoChatParticipantsInvited, fY as VideoChatScheduled, fZ as VideoChatStarted, f_ as VideoNote, f$ as Voice, g0 as WebAppData, g1 as WebAppInfo, g2 as WebhookInfo, g3 as WriteAccessAllowed } from './config-3uU0YevV.js';
|
|
4
|
-
import 'effect/Cause';
|
|
5
|
-
import 'effect/Types';
|
|
6
|
-
import 'effect/Data';
|
|
7
|
-
import 'effect/Context';
|
|
1
|
+
import { Api } from '@effect-ak/tg-bot-api';
|
|
8
2
|
|
|
9
|
-
|
|
3
|
+
interface TgBotConfig {
|
|
4
|
+
botToken: string;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
declare const getBaseUrl: (config?: Pick<TgBotConfig, "baseUrl">) => string;
|
|
8
|
+
|
|
9
|
+
declare function executeTgBotMethod<M extends keyof Api>(params: {
|
|
10
|
+
config: TgBotConfig;
|
|
11
|
+
method: M;
|
|
12
|
+
input: Parameters<Api[M]>[0];
|
|
13
|
+
}): Promise<ReturnType<Api[M]>>;
|
|
14
|
+
declare const makePayload: (body: object) => FormData | undefined;
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
interface GetFile {
|
|
12
17
|
fileId: string;
|
|
13
18
|
type?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
declare const getFileAsBase64String: ({ fileId, type, }: GetFile & {
|
|
17
|
-
type: string;
|
|
18
|
-
}) => Micro.Micro<{
|
|
19
|
-
encoded: string;
|
|
20
|
-
file_name: string;
|
|
21
|
-
}, TgBotClientError, TgBotApiToken>;
|
|
22
|
-
declare const getFileBytes: (fileId: string) => Micro.Micro<{
|
|
19
|
+
}
|
|
20
|
+
interface FileBytes {
|
|
23
21
|
content: ArrayBuffer;
|
|
24
22
|
file_name: string;
|
|
25
|
-
|
|
23
|
+
base64String: () => string;
|
|
24
|
+
}
|
|
25
|
+
interface FileContext {
|
|
26
|
+
config: TgBotConfig;
|
|
27
|
+
execute: <M extends keyof Api>(method: M, input: Parameters<Api[M]>[0]) => Promise<ReturnType<Api[M]>>;
|
|
28
|
+
}
|
|
29
|
+
declare const getFileBytes: (fileId: string, context: FileContext) => Promise<FileBytes>;
|
|
30
|
+
declare const getFile: (input: GetFile, context: FileContext) => Promise<File>;
|
|
26
31
|
|
|
27
32
|
interface TgBotClient {
|
|
28
33
|
readonly execute: <M extends keyof Api>(method: M, input: Parameters<Api[M]>[0]) => Promise<ReturnType<Api[M]>>;
|
|
29
34
|
readonly getFile: (input: GetFile) => Promise<File>;
|
|
30
35
|
}
|
|
31
|
-
|
|
36
|
+
interface MakeTgClient {
|
|
32
37
|
bot_token: string;
|
|
33
|
-
|
|
38
|
+
base_url?: string;
|
|
39
|
+
}
|
|
34
40
|
declare function makeTgBotClient(config: MakeTgClient): TgBotClient;
|
|
35
41
|
|
|
36
|
-
type
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
type ErrorReason = {
|
|
43
|
+
_tag: "NotOkResponse";
|
|
44
|
+
errorCode?: number;
|
|
45
|
+
details?: string;
|
|
46
|
+
} | {
|
|
47
|
+
_tag: "UnexpectedResponse";
|
|
48
|
+
response: unknown;
|
|
49
|
+
} | {
|
|
50
|
+
_tag: "ClientInternalError";
|
|
51
|
+
cause: unknown;
|
|
52
|
+
} | {
|
|
53
|
+
_tag: "UnableToGetFile";
|
|
54
|
+
cause: unknown;
|
|
55
|
+
} | {
|
|
56
|
+
_tag: "BotHandlerError";
|
|
57
|
+
cause: unknown;
|
|
58
|
+
} | {
|
|
59
|
+
_tag: "NotJsonResponse";
|
|
60
|
+
response: unknown;
|
|
39
61
|
};
|
|
62
|
+
declare class TgBotClientError extends Error {
|
|
63
|
+
readonly _tag = "TgBotClientError";
|
|
64
|
+
readonly cause: ErrorReason;
|
|
65
|
+
constructor(options: {
|
|
66
|
+
cause: ErrorReason;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface FileContent {
|
|
71
|
+
file_content: Uint8Array<ArrayBuffer>;
|
|
72
|
+
file_name: string;
|
|
73
|
+
}
|
|
40
74
|
declare const isFileContent: (input: unknown) => input is FileContent;
|
|
41
|
-
|
|
75
|
+
interface TgBotApiResponseSchema {
|
|
42
76
|
ok: boolean;
|
|
43
77
|
error_code?: number;
|
|
44
78
|
description?: string;
|
|
45
79
|
result?: unknown;
|
|
46
|
-
}
|
|
80
|
+
}
|
|
47
81
|
declare const isTgBotApiResponse: (input: unknown) => input is TgBotApiResponseSchema;
|
|
48
|
-
declare const isTgBotApiUpdate: (input: unknown) => input is Update;
|
|
49
82
|
|
|
50
83
|
declare const TG_BOT_API_URL = "https://api.telegram.org";
|
|
51
84
|
declare const MESSAGE_EFFECTS: {
|
|
@@ -60,4 +93,6 @@ type MessageEffect = keyof typeof MESSAGE_EFFECTS;
|
|
|
60
93
|
declare const messageEffectIdCodes: MessageEffect[];
|
|
61
94
|
declare const isMessageEffect: (input: unknown) => input is MessageEffect;
|
|
62
95
|
|
|
63
|
-
|
|
96
|
+
declare const snakeToCamel: (str: string) => string;
|
|
97
|
+
|
|
98
|
+
export { type FileBytes, type FileContent, type GetFile, MESSAGE_EFFECTS, type MakeTgClient, type MessageEffect, TG_BOT_API_URL, type TgBotApiResponseSchema, type TgBotClient, TgBotClientError, type TgBotConfig, executeTgBotMethod, getBaseUrl, getFile, getFileBytes, isFileContent, isMessageEffect, isTgBotApiResponse, makePayload, makeTgBotClient, messageEffectIdCodes, snakeToCamel };
|
package/dist/index.js
CHANGED
|
@@ -1 +1,169 @@
|
|
|
1
|
-
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var TgBotClientError = class extends Error {
|
|
3
|
+
_tag = "TgBotClientError";
|
|
4
|
+
cause;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
super(`TgBotClientError: ${options.cause._tag}`);
|
|
7
|
+
this.cause = options.cause;
|
|
8
|
+
this.name = "TgBotClientError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/guards.ts
|
|
13
|
+
var isFileContent = (input) => typeof input == "object" && input != null && "file_content" in input && input.file_content instanceof Uint8Array && "file_name" in input && typeof input.file_name == "string";
|
|
14
|
+
var isTgBotApiResponse = (input) => typeof input == "object" && input != null && "ok" in input && typeof input.ok == "boolean";
|
|
15
|
+
|
|
16
|
+
// src/const.ts
|
|
17
|
+
var TG_BOT_API_URL = "https://api.telegram.org";
|
|
18
|
+
var MESSAGE_EFFECTS = {
|
|
19
|
+
"\u{1F525}": "5104841245755180586",
|
|
20
|
+
"\u{1F44D}": "5107584321108051014",
|
|
21
|
+
"\u{1F44E}": "5104858069142078462",
|
|
22
|
+
"\u2764\uFE0F": "5159385139981059251",
|
|
23
|
+
"\u{1F389}": "5046509860389126442",
|
|
24
|
+
"\u{1F4A9}": "5046589136895476101"
|
|
25
|
+
};
|
|
26
|
+
var messageEffectIdCodes = Object.keys(
|
|
27
|
+
MESSAGE_EFFECTS
|
|
28
|
+
);
|
|
29
|
+
var isMessageEffect = (input) => {
|
|
30
|
+
return typeof input === "string" && input in MESSAGE_EFFECTS;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/config.ts
|
|
34
|
+
var getBaseUrl = (config) => {
|
|
35
|
+
return config?.baseUrl ?? TG_BOT_API_URL;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/utils.ts
|
|
39
|
+
var snakeToCamel = (str) => {
|
|
40
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/execute.ts
|
|
44
|
+
async function executeTgBotMethod(params) {
|
|
45
|
+
const { config, method, input } = params;
|
|
46
|
+
const baseUrl = getBaseUrl(config);
|
|
47
|
+
const botToken = config.botToken;
|
|
48
|
+
let httpResponse;
|
|
49
|
+
try {
|
|
50
|
+
httpResponse = await fetch(
|
|
51
|
+
`${baseUrl}/bot${botToken}/${snakeToCamel(method)}`,
|
|
52
|
+
{
|
|
53
|
+
body: makePayload(input) ?? null,
|
|
54
|
+
method: "POST"
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
} catch (cause) {
|
|
58
|
+
throw new TgBotClientError({
|
|
59
|
+
cause: { _tag: "ClientInternalError", cause }
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
let response;
|
|
63
|
+
try {
|
|
64
|
+
response = await httpResponse.json();
|
|
65
|
+
} catch {
|
|
66
|
+
throw new TgBotClientError({
|
|
67
|
+
cause: { _tag: "NotJsonResponse", response: httpResponse }
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (!isTgBotApiResponse(response)) {
|
|
71
|
+
throw new TgBotClientError({
|
|
72
|
+
cause: { _tag: "UnexpectedResponse", response }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (!httpResponse.ok) {
|
|
76
|
+
throw new TgBotClientError({
|
|
77
|
+
cause: {
|
|
78
|
+
_tag: "NotOkResponse",
|
|
79
|
+
...response.error_code ? { errorCode: response.error_code } : {},
|
|
80
|
+
...response.description ? { details: response.description } : {}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return response.result;
|
|
85
|
+
}
|
|
86
|
+
var makePayload = (body) => {
|
|
87
|
+
const entries = Object.entries(body);
|
|
88
|
+
if (entries.length == 0) return void 0;
|
|
89
|
+
const result = new FormData();
|
|
90
|
+
for (const [key, value] of entries) {
|
|
91
|
+
if (!value) continue;
|
|
92
|
+
if (typeof value != "object") {
|
|
93
|
+
result.append(key, `${value}`);
|
|
94
|
+
} else if (isFileContent(value)) {
|
|
95
|
+
result.append(key, new Blob([value.file_content]), value.file_name);
|
|
96
|
+
} else {
|
|
97
|
+
result.append(key, JSON.stringify(value));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/client-file.ts
|
|
104
|
+
var getFileBytes = async (fileId, context) => {
|
|
105
|
+
const { config, execute } = context;
|
|
106
|
+
const response = await execute("get_file", { file_id: fileId });
|
|
107
|
+
const file_path = response.file_path;
|
|
108
|
+
if (!file_path || file_path.length === 0) {
|
|
109
|
+
throw new TgBotClientError({
|
|
110
|
+
cause: {
|
|
111
|
+
_tag: "UnableToGetFile",
|
|
112
|
+
cause: "File path not defined"
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const file_name = file_path.replaceAll("/", "-");
|
|
117
|
+
const baseUrl = getBaseUrl(config);
|
|
118
|
+
const botToken = config.botToken;
|
|
119
|
+
const url = `${baseUrl}/file/bot${botToken}/${file_path}`;
|
|
120
|
+
let content;
|
|
121
|
+
try {
|
|
122
|
+
content = await fetch(url).then((_) => _.arrayBuffer());
|
|
123
|
+
} catch (cause) {
|
|
124
|
+
throw new TgBotClientError({
|
|
125
|
+
cause: { _tag: "UnableToGetFile", cause }
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const base64String = () => Buffer.from(content).toString("base64");
|
|
129
|
+
return {
|
|
130
|
+
content,
|
|
131
|
+
file_name,
|
|
132
|
+
base64String
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
var getFile = async (input, context) => {
|
|
136
|
+
const { content, file_name } = await getFileBytes(input.fileId, context);
|
|
137
|
+
return new File([content], file_name, {
|
|
138
|
+
...input.type ? { type: input.type } : {}
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/client.ts
|
|
143
|
+
function makeTgBotClient(config) {
|
|
144
|
+
const tgConfig = {
|
|
145
|
+
botToken: config.bot_token,
|
|
146
|
+
...config.base_url ? { baseUrl: config.base_url } : {}
|
|
147
|
+
};
|
|
148
|
+
const execute = (method, input) => executeTgBotMethod({ config: tgConfig, method, input });
|
|
149
|
+
return {
|
|
150
|
+
execute,
|
|
151
|
+
getFile: (input) => getFile(input, { config: tgConfig, execute })
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export {
|
|
155
|
+
MESSAGE_EFFECTS,
|
|
156
|
+
TG_BOT_API_URL,
|
|
157
|
+
TgBotClientError,
|
|
158
|
+
executeTgBotMethod,
|
|
159
|
+
getBaseUrl,
|
|
160
|
+
getFile,
|
|
161
|
+
getFileBytes,
|
|
162
|
+
isFileContent,
|
|
163
|
+
isMessageEffect,
|
|
164
|
+
isTgBotApiResponse,
|
|
165
|
+
makePayload,
|
|
166
|
+
makeTgBotClient,
|
|
167
|
+
messageEffectIdCodes,
|
|
168
|
+
snakeToCamel
|
|
169
|
+
};
|
package/package.json
CHANGED
|
@@ -1,75 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-ak/tg-bot-client",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"description": "Type-safe HTTP client for Telegram Bot API",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup"
|
|
9
|
+
},
|
|
5
10
|
"author": {
|
|
6
11
|
"name": "Aleksandr Kondaurov",
|
|
7
12
|
"email": "kondaurov.dev@gmail.com"
|
|
8
13
|
},
|
|
9
|
-
"publishConfig": {
|
|
10
|
-
"access": "public"
|
|
11
|
-
},
|
|
12
|
-
"description": "TypeScript types for Telegram Bot Api and Telegram.Webapp",
|
|
13
|
-
"files": [
|
|
14
|
-
"dist/*.js",
|
|
15
|
-
"dist/*.mjs",
|
|
16
|
-
"dist/*.d.ts",
|
|
17
|
-
"!dist/bot-bundle.d.ts",
|
|
18
|
-
"!dist/bot-bundle.d.mts"
|
|
19
|
-
],
|
|
20
|
-
"keywords": [
|
|
21
|
-
"telegram typescript types",
|
|
22
|
-
"telegram bot api client",
|
|
23
|
-
"telegram webapp"
|
|
24
|
-
],
|
|
25
14
|
"repository": {
|
|
26
15
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/effect-ak/tg-bot-client"
|
|
16
|
+
"url": "https://github.com/effect-ak/tg-bot-client",
|
|
17
|
+
"directory": "packages/client"
|
|
28
18
|
},
|
|
29
19
|
"bugs": {
|
|
30
20
|
"url": "https://github.com/effect-ak/tg-bot-client/issues"
|
|
31
21
|
},
|
|
32
|
-
"
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
33
25
|
"types": "./dist/index.d.ts",
|
|
34
|
-
"main": "./dist/index.
|
|
35
|
-
"module": "./dist/index.
|
|
26
|
+
"main": "./dist/index.cjs",
|
|
27
|
+
"module": "./dist/index.js",
|
|
36
28
|
"exports": {
|
|
37
29
|
".": {
|
|
38
30
|
"types": "./dist/index.d.ts",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
},
|
|
42
|
-
"./bot": {
|
|
43
|
-
"types": "./dist/bot.d.ts",
|
|
44
|
-
"require": "./dist/bot.js",
|
|
45
|
-
"import": "./dist/bot.mjs"
|
|
46
|
-
},
|
|
47
|
-
"./bot-bundle": {
|
|
48
|
-
"types": "./dist/bot.d.ts",
|
|
49
|
-
"require": "./dist/bot-bundle.js",
|
|
50
|
-
"import": "./dist/bot-bundle.mjs"
|
|
51
|
-
},
|
|
52
|
-
"./webapp": {
|
|
53
|
-
"types": "./dist/webapp.d.ts",
|
|
54
|
-
"require": "./dist/webapp.js",
|
|
55
|
-
"import": "./dist/webapp.mjs"
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"require": "./dist/index.cjs"
|
|
56
33
|
}
|
|
57
34
|
},
|
|
58
|
-
"
|
|
59
|
-
"@
|
|
60
|
-
"@types/js-yaml": "^4.0.9",
|
|
61
|
-
"js-yaml": "^4.1.0",
|
|
62
|
-
"node-html-parser": "^6.1.13",
|
|
63
|
-
"openapi-types": "^12.1.3",
|
|
64
|
-
"ts-morph": "^24.0.0",
|
|
65
|
-
"tsx": "^4.19.3",
|
|
66
|
-
"typescript": "^5.8.3",
|
|
67
|
-
"vite-tsconfig-paths": "^5.1.4",
|
|
68
|
-
"vitest": "^3.1.1",
|
|
69
|
-
"effect": "3.12.0",
|
|
70
|
-
"tsup": "^8.5.0"
|
|
71
|
-
},
|
|
72
|
-
"peerDependencies": {
|
|
73
|
-
"effect": "^3.12.0"
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@effect-ak/tg-bot-api": "workspace:*"
|
|
74
37
|
}
|
|
75
|
-
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Api } from "@effect-ak/tg-bot-api"
|
|
2
|
+
import { TgBotClientError } from "./errors"
|
|
3
|
+
import type { TgBotConfig } from "./config"
|
|
4
|
+
import { getBaseUrl } from "./config"
|
|
5
|
+
|
|
6
|
+
export interface GetFile {
|
|
7
|
+
fileId: string
|
|
8
|
+
type?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface FileBytes {
|
|
12
|
+
content: ArrayBuffer
|
|
13
|
+
file_name: string
|
|
14
|
+
base64String: () => string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface FileContext {
|
|
18
|
+
config: TgBotConfig
|
|
19
|
+
execute: <M extends keyof Api>(
|
|
20
|
+
method: M,
|
|
21
|
+
input: Parameters<Api[M]>[0]
|
|
22
|
+
) => Promise<ReturnType<Api[M]>>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const getFileBytes = async (
|
|
26
|
+
fileId: string,
|
|
27
|
+
context: FileContext
|
|
28
|
+
): Promise<FileBytes> => {
|
|
29
|
+
const { config, execute } = context
|
|
30
|
+
const response = await execute("get_file", { file_id: fileId })
|
|
31
|
+
const file_path = response.file_path
|
|
32
|
+
|
|
33
|
+
if (!file_path || file_path.length === 0) {
|
|
34
|
+
throw new TgBotClientError({
|
|
35
|
+
cause: {
|
|
36
|
+
_tag: "UnableToGetFile",
|
|
37
|
+
cause: "File path not defined"
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const file_name = file_path.replaceAll("/", "-")
|
|
43
|
+
const baseUrl = getBaseUrl(config)
|
|
44
|
+
const botToken = config.botToken
|
|
45
|
+
const url = `${baseUrl}/file/bot${botToken}/${file_path}`
|
|
46
|
+
|
|
47
|
+
let content: ArrayBuffer
|
|
48
|
+
try {
|
|
49
|
+
content = await fetch(url).then((_) => _.arrayBuffer())
|
|
50
|
+
} catch (cause) {
|
|
51
|
+
throw new TgBotClientError({
|
|
52
|
+
cause: { _tag: "UnableToGetFile", cause }
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const base64String = () => Buffer.from(content).toString("base64")
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
content,
|
|
60
|
+
file_name,
|
|
61
|
+
base64String
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const getFile = async (
|
|
66
|
+
input: GetFile,
|
|
67
|
+
context: FileContext
|
|
68
|
+
): Promise<File> => {
|
|
69
|
+
const { content, file_name } = await getFileBytes(input.fileId, context)
|
|
70
|
+
return new File([content], file_name, {
|
|
71
|
+
...(input.type ? { type: input.type } : {})
|
|
72
|
+
})
|
|
73
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Api } from "@effect-ak/tg-bot-api"
|
|
2
|
+
import { executeTgBotMethod } from "./execute"
|
|
3
|
+
import type { TgBotConfig } from "./config"
|
|
4
|
+
import { getFile as getFileImpl, type GetFile } from "./client-file"
|
|
5
|
+
|
|
6
|
+
export interface TgBotClient {
|
|
7
|
+
readonly execute: <M extends keyof Api>(
|
|
8
|
+
method: M,
|
|
9
|
+
input: Parameters<Api[M]>[0]
|
|
10
|
+
) => Promise<ReturnType<Api[M]>>
|
|
11
|
+
readonly getFile: (input: GetFile) => Promise<File>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface MakeTgClient {
|
|
15
|
+
bot_token: string
|
|
16
|
+
base_url?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function makeTgBotClient(config: MakeTgClient): TgBotClient {
|
|
20
|
+
const tgConfig: TgBotConfig = {
|
|
21
|
+
botToken: config.bot_token,
|
|
22
|
+
...(config.base_url ? { baseUrl: config.base_url } : {})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const execute = <M extends keyof Api>(
|
|
26
|
+
method: M,
|
|
27
|
+
input: Parameters<Api[M]>[0]
|
|
28
|
+
) => executeTgBotMethod({ config: tgConfig, method, input })
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
execute,
|
|
32
|
+
getFile: (input: GetFile) => getFileImpl(input, { config: tgConfig, execute })
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/config.ts
ADDED
package/src/const.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const TG_BOT_API_URL = "https://api.telegram.org"
|
|
2
|
+
|
|
3
|
+
export const MESSAGE_EFFECTS = {
|
|
4
|
+
"🔥": "5104841245755180586",
|
|
5
|
+
"👍": "5107584321108051014",
|
|
6
|
+
"👎": "5104858069142078462",
|
|
7
|
+
"❤️": "5159385139981059251",
|
|
8
|
+
"🎉": "5046509860389126442",
|
|
9
|
+
"💩": "5046589136895476101"
|
|
10
|
+
} as const
|
|
11
|
+
|
|
12
|
+
export type MessageEffect = keyof typeof MESSAGE_EFFECTS
|
|
13
|
+
|
|
14
|
+
export const messageEffectIdCodes = Object.keys(
|
|
15
|
+
MESSAGE_EFFECTS
|
|
16
|
+
) as MessageEffect[]
|
|
17
|
+
|
|
18
|
+
export const isMessageEffect = (input: unknown): input is MessageEffect => {
|
|
19
|
+
return typeof input === "string" && input in MESSAGE_EFFECTS
|
|
20
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type ErrorReason =
|
|
2
|
+
| { _tag: "NotOkResponse"; errorCode?: number; details?: string }
|
|
3
|
+
| { _tag: "UnexpectedResponse"; response: unknown }
|
|
4
|
+
| { _tag: "ClientInternalError"; cause: unknown }
|
|
5
|
+
| { _tag: "UnableToGetFile"; cause: unknown }
|
|
6
|
+
| { _tag: "BotHandlerError"; cause: unknown }
|
|
7
|
+
| { _tag: "NotJsonResponse"; response: unknown }
|
|
8
|
+
|
|
9
|
+
export class TgBotClientError extends Error {
|
|
10
|
+
readonly _tag = "TgBotClientError"
|
|
11
|
+
readonly cause: ErrorReason
|
|
12
|
+
|
|
13
|
+
constructor(options: { cause: ErrorReason }) {
|
|
14
|
+
super(`TgBotClientError: ${options.cause._tag}`)
|
|
15
|
+
this.cause = options.cause
|
|
16
|
+
this.name = "TgBotClientError"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/execute.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Api } from "@effect-ak/tg-bot-api"
|
|
2
|
+
|
|
3
|
+
import { TgBotClientError } from "./errors"
|
|
4
|
+
import { isFileContent, isTgBotApiResponse } from "./guards"
|
|
5
|
+
import type { TgBotConfig } from "./config"
|
|
6
|
+
import { getBaseUrl } from "./config"
|
|
7
|
+
import { snakeToCamel } from "./utils"
|
|
8
|
+
|
|
9
|
+
export async function executeTgBotMethod<M extends keyof Api>(params: {
|
|
10
|
+
config: TgBotConfig
|
|
11
|
+
method: M
|
|
12
|
+
input: Parameters<Api[M]>[0]
|
|
13
|
+
}): Promise<ReturnType<Api[M]>> {
|
|
14
|
+
const { config, method, input } = params
|
|
15
|
+
const baseUrl = getBaseUrl(config)
|
|
16
|
+
const botToken = config.botToken
|
|
17
|
+
|
|
18
|
+
let httpResponse: Response
|
|
19
|
+
try {
|
|
20
|
+
httpResponse = await fetch(
|
|
21
|
+
`${baseUrl}/bot${botToken}/${snakeToCamel(method)}`,
|
|
22
|
+
{
|
|
23
|
+
body: makePayload(input) ?? null,
|
|
24
|
+
method: "POST"
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
} catch (cause) {
|
|
28
|
+
throw new TgBotClientError({
|
|
29
|
+
cause: { _tag: "ClientInternalError", cause }
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let response: unknown
|
|
34
|
+
try {
|
|
35
|
+
response = await httpResponse.json()
|
|
36
|
+
} catch {
|
|
37
|
+
throw new TgBotClientError({
|
|
38
|
+
cause: { _tag: "NotJsonResponse", response: httpResponse }
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!isTgBotApiResponse(response)) {
|
|
43
|
+
throw new TgBotClientError({
|
|
44
|
+
cause: { _tag: "UnexpectedResponse", response }
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!httpResponse.ok) {
|
|
49
|
+
throw new TgBotClientError({
|
|
50
|
+
cause: {
|
|
51
|
+
_tag: "NotOkResponse",
|
|
52
|
+
...(response.error_code ? { errorCode: response.error_code } : {}),
|
|
53
|
+
...(response.description ? { details: response.description } : {})
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return response.result as ReturnType<Api[M]>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const makePayload = (body: object): FormData | undefined => {
|
|
62
|
+
const entries = Object.entries(body)
|
|
63
|
+
|
|
64
|
+
if (entries.length == 0) return undefined
|
|
65
|
+
|
|
66
|
+
const result = new FormData()
|
|
67
|
+
|
|
68
|
+
for (const [key, value] of entries) {
|
|
69
|
+
if (!value) continue
|
|
70
|
+
|
|
71
|
+
if (typeof value != "object") {
|
|
72
|
+
result.append(key, `${value}`)
|
|
73
|
+
} else if (isFileContent(value)) {
|
|
74
|
+
result.append(key, new Blob([value.file_content]), value.file_name)
|
|
75
|
+
} else {
|
|
76
|
+
result.append(key, JSON.stringify(value))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result
|
|
81
|
+
}
|