@effect-uai/telegram 0.17.0 → 0.18.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 +1 -1
- package/dist/Telegram.d.mts +2 -2
- package/dist/Telegram.mjs +5 -4
- package/dist/Telegram.mjs.map +1 -1
- package/dist/{api-CnvvfWKq.d.mts → api-o1OT-Mk0.d.mts} +2 -2
- package/dist/{api-CnvvfWKq.d.mts.map → api-o1OT-Mk0.d.mts.map} +1 -1
- package/dist/dist-DHer1cql.mjs +1533 -0
- package/dist/dist-DHer1cql.mjs.map +1 -0
- package/dist/internal/api.d.mts +1 -1
- package/dist/internal/api.mjs +1 -1
- package/dist/internal/api.mjs.map +1 -1
- package/dist/internal/events.test.mjs +8275 -7091
- package/dist/internal/events.test.mjs.map +1 -1
- package/dist/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-EAsCxrDo.mjs} +2 -1
- package/package.json +4 -4
- package/src/Telegram.ts +3 -3
- package/src/internal/api.ts +1 -1
- package/dist/magic-string.es-CUAnjKYC.mjs +0 -1014
- package/dist/magic-string.es-CUAnjKYC.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ media and reactions out. Long-polling, so no public URL, and no SDK.
|
|
|
12
12
|
pnpm add @effect-uai/telegram @effect-uai/core effect
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
ESM-only. Requires `effect
|
|
15
|
+
ESM-only. Requires `effect@>=4.0.1` and `@effect-uai/core` as peers.
|
|
16
16
|
|
|
17
17
|
## Usage
|
|
18
18
|
|
package/dist/Telegram.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { r as Config$1 } from "./api-
|
|
1
|
+
import { r as Config$1 } from "./api-o1OT-Mk0.mjs";
|
|
2
2
|
import { t as BotIdentity } from "./events-BkljYfG6.mjs";
|
|
3
3
|
import { Context, Duration, Effect, Layer, Scope } from "effect";
|
|
4
|
-
import { HttpClient } from "effect/
|
|
4
|
+
import { HttpClient } from "effect/http";
|
|
5
5
|
import { Messenger, MessengerLimits, MessengerService } from "@effect-uai/core/Messenger";
|
|
6
6
|
import { StreamViaEditsOptions } from "@effect-uai/core/MessengerAdapter";
|
|
7
7
|
import * as MessengerError from "@effect-uai/core/MessengerError";
|
package/dist/Telegram.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
|
|
2
2
|
import { call, decoded, provider, upload } from "./internal/api.mjs";
|
|
3
3
|
import { Update, allowedUpdates, toEvents } from "./internal/events.mjs";
|
|
4
|
-
import { Array, Context, Duration, Effect,
|
|
5
|
-
import {
|
|
4
|
+
import { Array, Context, Duration, Effect, Layer, Match, Option, Queue, Ref, Schedule, Schema, Stream } from "effect";
|
|
5
|
+
import { Base64 } from "effect/encoding";
|
|
6
|
+
import { HttpClient } from "effect/http";
|
|
6
7
|
import { CurrentConversation, MessageId, Messenger } from "@effect-uai/core/Messenger";
|
|
7
8
|
import { splitForLimit, streamViaEdits } from "@effect-uai/core/MessengerAdapter";
|
|
8
9
|
import * as MessengerError from "@effect-uai/core/MessengerError";
|
|
@@ -132,7 +133,7 @@ const make = (cfg) => Effect.gen(function* () {
|
|
|
132
133
|
return Match.value(body.media).pipe(Match.tag("url", ({ url }) => call$1(method, {
|
|
133
134
|
...params,
|
|
134
135
|
[field]: url
|
|
135
|
-
})), Match.tag("bytes", ({ bytes, mimeType }) => send(bytes, mimeType)), Match.tag("base64", ({ base64, mimeType }) => Effect.fromResult(
|
|
136
|
+
})), Match.tag("bytes", ({ bytes, mimeType }) => send(bytes, mimeType)), Match.tag("base64", ({ base64, mimeType }) => Effect.fromResult(Base64.decode(base64)).pipe(Effect.mapError(invalidMedia), Effect.flatMap((bytes) => send(bytes, mimeType)))), Match.exhaustive);
|
|
136
137
|
};
|
|
137
138
|
const sendRaw = (payload) => Effect.gen(function* () {
|
|
138
139
|
const { method, params } = yield* Schema.decodeUnknownEffect(RawCall)(payload).pipe(Effect.mapError(() => new MessengerError.MessengerRequestFailed({
|
package/dist/Telegram.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Telegram.mjs","names":["Events.Update","Api.provider","call","Api.call","upload","Api.upload","Api.decoded","toEvents","Events.toEvents","Events.allowedUpdates","Arr"],"sources":["../src/Telegram.ts"],"sourcesContent":["import {\n Array as Arr,\n type Cause,\n Context,\n Duration,\n Effect,\n Encoding,\n Layer,\n Match,\n Option,\n Queue,\n Ref,\n Schedule,\n Schema,\n type Scope,\n Stream,\n} from \"effect\"\nimport { HttpClient } from \"effect/unstable/http\"\nimport type { MediaSource } from \"@effect-uai/core/Media\"\nimport {\n CurrentConversation,\n type ConversationRef,\n type InboundEvent,\n MessageId,\n Messenger,\n type MessengerLimits,\n type MessengerService,\n type Outbound,\n type OutboundBody,\n} from \"@effect-uai/core/Messenger\"\nimport {\n type StreamViaEditsOptions,\n splitForLimit,\n streamViaEdits,\n} from \"@effect-uai/core/MessengerAdapter\"\nimport * as MessengerError from \"@effect-uai/core/MessengerError\"\nimport * as Api from \"./internal/api.js\"\nimport * as Events from \"./internal/events.js\"\n\nexport type { BotIdentity } from \"./internal/events.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Telegram's own markup modes. `Text` bodies are sent verbatim under one. */\nexport type ParseMode = \"HTML\" | \"MarkdownV2\" | \"Markdown\"\n\nexport type Config = Api.Config & {\n /**\n * Markup the model is prompted to write. Default `\"HTML\"`; `\"plain\"` sends\n * text with no `parse_mode` at all.\n */\n readonly parseMode?: ParseMode | \"plain\"\n /** Long-poll wait per `getUpdates` call. Default 30 seconds. */\n readonly pollTimeout?: Duration.Input\n /** Tuning for `stream`, which is post-then-edit on Telegram. */\n readonly stream?: StreamViaEditsOptions\n}\n\nexport type TelegramService = MessengerService & {\n /** From `getMe` at layer build: the identity behind `addressed`. */\n readonly bot: Events.BotIdentity\n}\n\n/**\n * Provider-typed tag. Yield this for the bot identity; yield the generic\n * `Messenger` tag for provider-portable code. Both are registered by {@link layer}.\n */\nexport class Telegram extends Context.Service<Telegram, TelegramService>()(\n \"@betalyra/effect-uai/providers/telegram/Telegram\",\n) {}\n\nexport const limits: MessengerLimits = { maxText: 4096, maxCaption: 1024 }\n\n// ---------------------------------------------------------------------------\n// Wire results\n// ---------------------------------------------------------------------------\n\nconst Me = Schema.Struct({ id: Schema.Number, username: Schema.String })\nconst Sent = Schema.Struct({ message_id: Schema.Number })\nconst Updates = Schema.Array(Events.Update)\nconst RawCall = Schema.Struct({\n method: Schema.String,\n params: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n})\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nconst isApiFailure = (e: Api.ApiError): e is Api.ApiFailure => e._tag === \"TelegramApiFailure\"\n\nconst describes = (needle: string) => (e: Api.ApiError) =>\n isApiFailure(e) && e.description.includes(needle)\n\nconst reasonOf = (e: Api.ApiError): string => (isApiFailure(e) ? e.description : \"rate limited\")\n\n/** An API rejection becomes the verb's `MessengerRequestFailed`; typed errors pass through. */\nconst requestFailed =\n (operation: MessengerError.MessengerOperation) =>\n (e: Api.ApiError | MessengerError.MessengerError): MessengerError.MessengerError =>\n Match.value(e).pipe(\n Match.tag(\n \"TelegramApiFailure\",\n (failure) =>\n new MessengerError.MessengerRequestFailed({\n provider: Api.provider,\n operation,\n reason: failure.description,\n raw: failure.raw,\n }),\n ),\n Match.orElse((typed) => typed),\n )\n\nconst unsupported = (capability: string, reason: string) =>\n new MessengerError.MessengerUnsupported({ provider: Api.provider, capability, reason })\n\n// 401 is a dead token and 409 a second poller on it; nothing else ends the stream.\nconst isFatalPoll = (e: Api.ApiError) => isApiFailure(e) && (e.code === 401 || e.code === 409)\n\n// Transient poll failures wait five seconds, or exactly what a 429 asked for.\nconst pollBackoff: Schedule.Schedule<number, Api.ApiError> = Schedule.forever.pipe(\n Schedule.setInputType<Api.ApiError>(),\n Schedule.modifyDelay(({ input }) =>\n Effect.succeed(input._tag === \"MessengerRateLimited\" ? input.retryAfter : Duration.seconds(5)),\n ),\n)\n\n// ---------------------------------------------------------------------------\n// Outbound params\n// ---------------------------------------------------------------------------\n\nconst target = (at: ConversationRef): Api.Params => ({\n chat_id: at.channel,\n ...(at.thread !== undefined && { message_thread_id: Number(at.thread) }),\n})\n\nconst replyParams = (msg: Outbound): Api.Params =>\n msg.replyTo === undefined ? {} : { reply_parameters: { message_id: Number(msg.replyTo) } }\n\nconst parseParams = (cfg: Config): Api.Params =>\n cfg.parseMode === \"plain\" ? {} : { parse_mode: cfg.parseMode ?? \"HTML\" }\n\ntype MediaRoute = { readonly method: string; readonly field: string }\n\n/** The endpoint a `MediaSource` goes through, decided by its MIME type. */\nconst mediaRoute = (media: MediaSource): MediaRoute =>\n Match.value(media.mimeType ?? \"\").pipe(\n Match.when(\n (m) => m.startsWith(\"image/\"),\n (): MediaRoute => ({ method: \"sendPhoto\", field: \"photo\" }),\n ),\n Match.when(\n (m) => m.startsWith(\"audio/\"),\n (): MediaRoute => ({ method: \"sendAudio\", field: \"audio\" }),\n ),\n Match.when(\n (m) => m.startsWith(\"video/\"),\n (): MediaRoute => ({ method: \"sendVideo\", field: \"video\" }),\n ),\n Match.orElse((): MediaRoute => ({ method: \"sendDocument\", field: \"document\" })),\n )\n\nconst defaultFilename = (media: MediaSource): string =>\n `file.${media.mimeType?.split(\"/\")[1] ?? \"bin\"}`\n\nconst invalidMedia = (raw: unknown) =>\n new MessengerError.MessengerRequestFailed({\n provider: Api.provider,\n operation: \"post\",\n reason: \"invalid base64 media\",\n raw,\n })\n\n// ---------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------\n\nexport const make = (\n cfg: Config,\n): Effect.Effect<\n TelegramService,\n MessengerError.MessengerConnectFailed,\n HttpClient.HttpClient | Scope.Scope\n> =>\n Effect.gen(function* () {\n // Captured once so the verbs need no `HttpClient` of their own.\n const client = yield* HttpClient.HttpClient\n const withClient = Effect.provideService(HttpClient.HttpClient, client)\n const call = (method: string, params?: Api.Params) => withClient(Api.call(cfg)(method, params))\n const upload = (method: string, params: Api.Params, file: Api.Upload) =>\n withClient(Api.upload(cfg)(method, params, file))\n\n const bot = yield* call(\"getMe\").pipe(\n Api.decoded(\"getMe\", Me),\n Effect.mapError(\n (e) =>\n new MessengerError.MessengerConnectFailed({\n provider: Api.provider,\n reason: reasonOf(e),\n raw: e.raw,\n }),\n ),\n )\n\n // -- inbound -----------------------------------------------------------\n\n const inbox = yield* Queue.unbounded<InboundEvent, MessengerError.MessengerError | Cause.Done>()\n const offset = yield* Ref.make(0)\n const toEvents = Events.toEvents(bot)\n\n // Answered before the event is offered, so the client's spinner never\n // waits on the recipe.\n const acknowledge = (update: Events.Update) =>\n Match.value(update).pipe(\n Match.when({ callback_query: Match.defined }, ({ callback_query }) =>\n call(\"answerCallbackQuery\", { callback_query_id: callback_query.id }).pipe(Effect.ignore),\n ),\n Match.orElse(() => Effect.void),\n )\n\n const poll = Effect.gen(function* () {\n const updates = yield* call(\"getUpdates\", {\n offset: yield* Ref.get(offset),\n timeout: Duration.toSeconds(cfg.pollTimeout ?? \"30 seconds\"),\n allowed_updates: Events.allowedUpdates,\n }).pipe(Api.decoded(\"getUpdates\", Updates))\n yield* Effect.forEach(updates, (update) =>\n Effect.gen(function* () {\n yield* Ref.set(offset, update.update_id + 1)\n yield* acknowledge(update)\n yield* Queue.offerAll(inbox, toEvents(update))\n }),\n )\n })\n\n // Transient failures (network, 5xx, 429) back off and poll again; fatal\n // ones end the stream with `MessengerTransportClosed`.\n yield* poll.pipe(\n Effect.retry({ while: (e) => !isFatalPoll(e), schedule: pollBackoff }),\n Effect.forever,\n Effect.catch((e) =>\n Queue.fail(\n inbox,\n new MessengerError.MessengerTransportClosed({\n provider: Api.provider,\n reason: reasonOf(e),\n raw: e.raw,\n }),\n ),\n ),\n Effect.ensuring(Queue.end(inbox)),\n Effect.forkScoped,\n )\n\n // -- outbound ----------------------------------------------------------\n\n // Telegram rejects markup it cannot parse; the same text then goes out plain.\n const sendText = (method: string, params: Api.Params) =>\n call(method, { ...params, ...parseParams(cfg) }).pipe(\n Effect.catchIf(describes(\"can't parse entities\"), () => call(method, params)),\n )\n\n const sendMedia = (\n at: ConversationRef,\n msg: Outbound,\n body: Extract<OutboundBody, { _tag: \"Media\" }>,\n ) => {\n const { method, field } = mediaRoute(body.media)\n const params = {\n ...target(at),\n ...replyParams(msg),\n ...(body.caption !== undefined && { caption: body.caption, ...parseParams(cfg) }),\n }\n const send = (bytes: Uint8Array, mimeType: string) =>\n upload(method, params, {\n field,\n bytes,\n filename: body.filename ?? defaultFilename(body.media),\n mimeType,\n })\n return Match.value(body.media).pipe(\n Match.tag(\"url\", ({ url }) => call(method, { ...params, [field]: url })),\n Match.tag(\"bytes\", ({ bytes, mimeType }) => send(bytes, mimeType)),\n Match.tag(\"base64\", ({ base64, mimeType }) =>\n Effect.fromResult(Encoding.decodeBase64(base64)).pipe(\n Effect.mapError(invalidMedia),\n Effect.flatMap((bytes) => send(bytes, mimeType)),\n ),\n ),\n Match.exhaustive,\n )\n }\n\n const sendRaw = (payload: unknown) =>\n Effect.gen(function* () {\n const { method, params } = yield* Schema.decodeUnknownEffect(RawCall)(payload).pipe(\n Effect.mapError(\n () =>\n new MessengerError.MessengerRequestFailed({\n provider: Api.provider,\n operation: \"post\",\n reason: \"raw payload must be { method, params? }\",\n raw: payload,\n }),\n ),\n )\n return yield* call(method, params)\n })\n\n // The reply lands on the first chunk; the id is the last one's.\n const sendChunks = (at: ConversationRef, msg: Outbound, text: string) =>\n Effect.gen(function* () {\n const [first, ...rest] = splitForLimit(text, limits.maxText)\n const send = (chunk: string, extra: Api.Params) =>\n sendText(\"sendMessage\", { ...target(at), ...extra, text: chunk }).pipe(\n Api.decoded(\"sendMessage\", Sent),\n )\n const head = yield* send(first, replyParams(msg))\n const tail = yield* Effect.forEach(rest, (chunk) => send(chunk, {}))\n return Option.getOrElse(Arr.last(tail), () => head)\n })\n\n const post: MessengerService[\"post\"] = (msg) =>\n Effect.gen(function* () {\n const at = yield* CurrentConversation\n const sent = yield* Match.value(msg.body).pipe(\n Match.tag(\"Text\", ({ text }) => sendChunks(at, msg, text)),\n Match.tag(\"Media\", (body) =>\n sendMedia(at, msg, body).pipe(Api.decoded(\"sendMedia\", Sent)),\n ),\n Match.tag(\"Raw\", ({ payload }) => sendRaw(payload).pipe(Api.decoded(\"raw\", Sent))),\n Match.exhaustive,\n )\n return MessageId(String(sent.message_id))\n }).pipe(Effect.mapError(requestFailed(\"post\")))\n\n const edit: MessengerService[\"edit\"] = (ref, next) =>\n Match.value(next.body).pipe(\n Match.tag(\"Text\", ({ text }) =>\n sendText(\"editMessageText\", {\n chat_id: ref.conversation.channel,\n message_id: Number(ref.id),\n text,\n }).pipe(Effect.asVoid, Effect.mapError(requestFailed(\"edit\"))),\n ),\n Match.tag(\"Raw\", ({ payload }) =>\n sendRaw(payload).pipe(Effect.asVoid, Effect.mapError(requestFailed(\"edit\"))),\n ),\n Match.tag(\"Media\", () =>\n Effect.fail(unsupported(\"media edits\", \"send a new message instead\")),\n ),\n Match.exhaustive,\n )\n\n // Telegram allows a fixed emoji set; anything else is REACTION_INVALID.\n const react: MessengerService[\"react\"] = (ref, emoji) =>\n call(\"setMessageReaction\", {\n chat_id: ref.conversation.channel,\n message_id: Number(ref.id),\n reaction: [{ type: \"emoji\", emoji }],\n }).pipe(\n Effect.asVoid,\n Effect.catchIf(describes(\"REACTION_INVALID\"), () =>\n Effect.fail(unsupported(\"reaction\", `${emoji} is not in Telegram's reaction set`)),\n ),\n Effect.mapError(requestFailed(\"react\")),\n )\n\n // The indicator lasts about five seconds, so it is re-sent every four\n // until the scope closes. The first send is awaited so a bad chat fails here.\n const typing: MessengerService[\"typing\"] = Effect.gen(function* () {\n const at = yield* CurrentConversation\n const once = call(\"sendChatAction\", { ...target(at), action: \"typing\" })\n yield* once.pipe(Effect.mapError(requestFailed(\"typing\")))\n yield* once.pipe(\n Effect.ignore,\n Effect.schedule(Schedule.spaced(\"4 seconds\")),\n Effect.forkScoped,\n )\n })\n\n return {\n bot,\n events: Stream.fromQueue(inbox),\n post,\n edit,\n react,\n typing,\n stream: streamViaEdits({ post, edit, limits }, cfg.stream),\n limits,\n }\n })\n\n// ---------------------------------------------------------------------------\n// Layer\n// ---------------------------------------------------------------------------\n\n/**\n * One poller, registered under both the `Telegram` and `Messenger` tags. The\n * long-poll loop starts when the layer is built and stops when its scope\n * closes; `events` ends with it. Telegram allows one poller per token.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<\n Telegram | Messenger,\n MessengerError.MessengerConnectFailed,\n HttpClient.HttpClient\n> =>\n Layer.effectContext(\n Effect.map(make(cfg), (service) =>\n Context.make(Telegram, service).pipe(Context.add(Messenger, service)),\n ),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqEA,IAAa,WAAb,cAA8B,QAAQ,QAAmC,CAAC,CACxE,kDACF,CAAC,CAAC,CAAC;AAEH,MAAa,SAA0B;CAAE,SAAS;CAAM,YAAY;AAAK;AAMzE,MAAM,KAAK,OAAO,OAAO;CAAE,IAAI,OAAO;CAAQ,UAAU,OAAO;AAAO,CAAC;AACvE,MAAM,OAAO,OAAO,OAAO,EAAE,YAAY,OAAO,OAAO,CAAC;AACxD,MAAM,UAAU,OAAO,MAAMA,MAAa;AAC1C,MAAM,UAAU,OAAO,OAAO;CAC5B,QAAQ,OAAO;CACf,QAAQ,OAAO,SAAS,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC;AACtE,CAAC;AAMD,MAAM,gBAAgB,MAAyC,EAAE,SAAS;AAE1E,MAAM,aAAa,YAAoB,MACrC,aAAa,CAAC,KAAK,EAAE,YAAY,SAAS,MAAM;AAElD,MAAM,YAAY,MAA6B,aAAa,CAAC,IAAI,EAAE,cAAc;;AAGjF,MAAM,iBACH,eACA,MACC,MAAM,MAAM,CAAC,CAAC,CAAC,KACb,MAAM,IACJ,uBACC,YACC,IAAI,eAAe,uBAAuB;CAC9BC;CACV;CACA,QAAQ,QAAQ;CAChB,KAAK,QAAQ;AACf,CAAC,CACL,GACA,MAAM,QAAQ,UAAU,KAAK,CAC/B;AAEJ,MAAM,eAAe,YAAoB,WACvC,IAAI,eAAe,qBAAqB;CAAYA;CAAc;CAAY;AAAO,CAAC;AAGxF,MAAM,eAAe,MAAoB,aAAa,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,SAAS;AAG1F,MAAM,cAAuD,SAAS,QAAQ,KAC5E,SAAS,aAA2B,GACpC,SAAS,aAAa,EAAE,YACtB,OAAO,QAAQ,MAAM,SAAS,yBAAyB,MAAM,aAAa,SAAS,QAAQ,CAAC,CAAC,CAC/F,CACF;AAMA,MAAM,UAAU,QAAqC;CACnD,SAAS,GAAG;CACZ,GAAI,GAAG,WAAW,KAAA,KAAa,EAAE,mBAAmB,OAAO,GAAG,MAAM,EAAE;AACxE;AAEA,MAAM,eAAe,QACnB,IAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,kBAAkB,EAAE,YAAY,OAAO,IAAI,OAAO,EAAE,EAAE;AAE3F,MAAM,eAAe,QACnB,IAAI,cAAc,UAAU,CAAC,IAAI,EAAE,YAAY,IAAI,aAAa,OAAO;;AAKzE,MAAM,cAAc,UAClB,MAAM,MAAM,MAAM,YAAY,EAAE,CAAC,CAAC,KAChC,MAAM,MACH,MAAM,EAAE,WAAW,QAAQ,UACT;CAAE,QAAQ;CAAa,OAAO;AAAQ,EAC3D,GACA,MAAM,MACH,MAAM,EAAE,WAAW,QAAQ,UACT;CAAE,QAAQ;CAAa,OAAO;AAAQ,EAC3D,GACA,MAAM,MACH,MAAM,EAAE,WAAW,QAAQ,UACT;CAAE,QAAQ;CAAa,OAAO;AAAQ,EAC3D,GACA,MAAM,cAA0B;CAAE,QAAQ;CAAgB,OAAO;AAAW,EAAE,CAChF;AAEF,MAAM,mBAAmB,UACvB,QAAQ,MAAM,UAAU,MAAM,GAAG,CAAC,CAAC,MAAM;AAE3C,MAAM,gBAAgB,QACpB,IAAI,eAAe,uBAAuB;CAC9BA;CACV,WAAW;CACX,QAAQ;CACR;AACF,CAAC;AAMH,MAAa,QACX,QAMA,OAAO,IAAI,aAAa;CAEtB,MAAM,SAAS,OAAO,WAAW;CACjC,MAAM,aAAa,OAAO,eAAe,WAAW,YAAY,MAAM;CACtE,MAAMC,UAAQ,QAAgB,WAAwB,WAAWC,KAAS,GAAG,CAAC,CAAC,QAAQ,MAAM,CAAC;CAC9F,MAAMC,YAAU,QAAgB,QAAoB,SAClD,WAAWC,OAAW,GAAG,CAAC,CAAC,QAAQ,QAAQ,IAAI,CAAC;CAElD,MAAM,MAAM,OAAOH,OAAK,OAAO,CAAC,CAAC,KAC/BI,QAAY,SAAS,EAAE,GACvB,OAAO,UACJ,MACC,IAAI,eAAe,uBAAuB;EAC9BL;EACV,QAAQ,SAAS,CAAC;EAClB,KAAK,EAAE;CACT,CAAC,CACL,CACF;CAIA,MAAM,QAAQ,OAAO,MAAM,UAAoE;CAC/F,MAAM,SAAS,OAAO,IAAI,KAAK,CAAC;CAChC,MAAMM,aAAWC,SAAgB,GAAG;CAIpC,MAAM,eAAe,WACnB,MAAM,MAAM,MAAM,CAAC,CAAC,KAClB,MAAM,KAAK,EAAE,gBAAgB,MAAM,QAAQ,IAAI,EAAE,qBAC/CN,OAAK,uBAAuB,EAAE,mBAAmB,eAAe,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,MAAM,CAC1F,GACA,MAAM,aAAa,OAAO,IAAI,CAChC;CAmBF,OAjBa,OAAO,IAAI,aAAa;EACnC,MAAM,UAAU,OAAOA,OAAK,cAAc;GACxC,QAAQ,OAAO,IAAI,IAAI,MAAM;GAC7B,SAAS,SAAS,UAAU,IAAI,eAAe,YAAY;GAC3D,iBAAiBO;EACnB,CAAC,CAAC,CAAC,KAAKH,QAAY,cAAc,OAAO,CAAC;EAC1C,OAAO,OAAO,QAAQ,UAAU,WAC9B,OAAO,IAAI,aAAa;GACtB,OAAO,IAAI,IAAI,QAAQ,OAAO,YAAY,CAAC;GAC3C,OAAO,YAAY,MAAM;GACzB,OAAO,MAAM,SAAS,OAAOC,WAAS,MAAM,CAAC;EAC/C,CAAC,CACH;CACF,CAIU,CAAC,CAAC,KACV,OAAO,MAAM;EAAE,QAAQ,MAAM,CAAC,YAAY,CAAC;EAAG,UAAU;CAAY,CAAC,GACrE,OAAO,SACP,OAAO,OAAO,MACZ,MAAM,KACJ,OACA,IAAI,eAAe,yBAAyB;EAChCN;EACV,QAAQ,SAAS,CAAC;EAClB,KAAK,EAAE;CACT,CAAC,CACH,CACF,GACA,OAAO,SAAS,MAAM,IAAI,KAAK,CAAC,GAChC,OAAO,UACT;CAKA,MAAM,YAAY,QAAgB,WAChCC,OAAK,QAAQ;EAAE,GAAG;EAAQ,GAAG,YAAY,GAAG;CAAE,CAAC,CAAC,CAAC,KAC/C,OAAO,QAAQ,UAAU,sBAAsB,SAASA,OAAK,QAAQ,MAAM,CAAC,CAC9E;CAEF,MAAM,aACJ,IACA,KACA,SACG;EACH,MAAM,EAAE,QAAQ,UAAU,WAAW,KAAK,KAAK;EAC/C,MAAM,SAAS;GACb,GAAG,OAAO,EAAE;GACZ,GAAG,YAAY,GAAG;GAClB,GAAI,KAAK,YAAY,KAAA,KAAa;IAAE,SAAS,KAAK;IAAS,GAAG,YAAY,GAAG;GAAE;EACjF;EACA,MAAM,QAAQ,OAAmB,aAC/BE,SAAO,QAAQ,QAAQ;GACrB;GACA;GACA,UAAU,KAAK,YAAY,gBAAgB,KAAK,KAAK;GACrD;EACF,CAAC;EACH,OAAO,MAAM,MAAM,KAAK,KAAK,CAAC,CAAC,KAC7B,MAAM,IAAI,QAAQ,EAAE,UAAUF,OAAK,QAAQ;GAAE,GAAG;IAAS,QAAQ;EAAI,CAAC,CAAC,GACvE,MAAM,IAAI,UAAU,EAAE,OAAO,eAAe,KAAK,OAAO,QAAQ,CAAC,GACjE,MAAM,IAAI,WAAW,EAAE,QAAQ,eAC7B,OAAO,WAAW,SAAS,aAAa,MAAM,CAAC,CAAC,CAAC,KAC/C,OAAO,SAAS,YAAY,GAC5B,OAAO,SAAS,UAAU,KAAK,OAAO,QAAQ,CAAC,CACjD,CACF,GACA,MAAM,UACR;CACF;CAEA,MAAM,WAAW,YACf,OAAO,IAAI,aAAa;EACtB,MAAM,EAAE,QAAQ,WAAW,OAAO,OAAO,oBAAoB,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,KAC7E,OAAO,eAEH,IAAI,eAAe,uBAAuB;GAC9BD;GACV,WAAW;GACX,QAAQ;GACR,KAAK;EACP,CAAC,CACL,CACF;EACA,OAAO,OAAOC,OAAK,QAAQ,MAAM;CACnC,CAAC;CAGH,MAAM,cAAc,IAAqB,KAAe,SACtD,OAAO,IAAI,aAAa;EACtB,MAAM,CAAC,OAAO,GAAG,QAAQ,cAAc,MAAM,OAAO,OAAO;EAC3D,MAAM,QAAQ,OAAe,UAC3B,SAAS,eAAe;GAAE,GAAG,OAAO,EAAE;GAAG,GAAG;GAAO,MAAM;EAAM,CAAC,CAAC,CAAC,KAChEI,QAAY,eAAe,IAAI,CACjC;EACF,MAAM,OAAO,OAAO,KAAK,OAAO,YAAY,GAAG,CAAC;EAChD,MAAM,OAAO,OAAO,OAAO,QAAQ,OAAO,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC;EACnE,OAAO,OAAO,UAAUI,MAAI,KAAK,IAAI,SAAS,IAAI;CACpD,CAAC;CAEH,MAAM,QAAkC,QACtC,OAAO,IAAI,aAAa;EACtB,MAAM,KAAK,OAAO;EAClB,MAAM,OAAO,OAAO,MAAM,MAAM,IAAI,IAAI,CAAC,CAAC,KACxC,MAAM,IAAI,SAAS,EAAE,WAAW,WAAW,IAAI,KAAK,IAAI,CAAC,GACzD,MAAM,IAAI,UAAU,SAClB,UAAU,IAAI,KAAK,IAAI,CAAC,CAAC,KAAKJ,QAAY,aAAa,IAAI,CAAC,CAC9D,GACA,MAAM,IAAI,QAAQ,EAAE,cAAc,QAAQ,OAAO,CAAC,CAAC,KAAKA,QAAY,OAAO,IAAI,CAAC,CAAC,GACjF,MAAM,UACR;EACA,OAAO,UAAU,OAAO,KAAK,UAAU,CAAC;CAC1C,CAAC,CAAC,CAAC,KAAK,OAAO,SAAS,cAAc,MAAM,CAAC,CAAC;CAEhD,MAAM,QAAkC,KAAK,SAC3C,MAAM,MAAM,KAAK,IAAI,CAAC,CAAC,KACrB,MAAM,IAAI,SAAS,EAAE,WACnB,SAAS,mBAAmB;EAC1B,SAAS,IAAI,aAAa;EAC1B,YAAY,OAAO,IAAI,EAAE;EACzB;CACF,CAAC,CAAC,CAAC,KAAK,OAAO,QAAQ,OAAO,SAAS,cAAc,MAAM,CAAC,CAAC,CAC/D,GACA,MAAM,IAAI,QAAQ,EAAE,cAClB,QAAQ,OAAO,CAAC,CAAC,KAAK,OAAO,QAAQ,OAAO,SAAS,cAAc,MAAM,CAAC,CAAC,CAC7E,GACA,MAAM,IAAI,eACR,OAAO,KAAK,YAAY,eAAe,4BAA4B,CAAC,CACtE,GACA,MAAM,UACR;CAGF,MAAM,SAAoC,KAAK,UAC7CJ,OAAK,sBAAsB;EACzB,SAAS,IAAI,aAAa;EAC1B,YAAY,OAAO,IAAI,EAAE;EACzB,UAAU,CAAC;GAAE,MAAM;GAAS;EAAM,CAAC;CACrC,CAAC,CAAC,CAAC,KACD,OAAO,QACP,OAAO,QAAQ,UAAU,kBAAkB,SACzC,OAAO,KAAK,YAAY,YAAY,GAAG,MAAM,mCAAmC,CAAC,CACnF,GACA,OAAO,SAAS,cAAc,OAAO,CAAC,CACxC;CAIF,MAAM,SAAqC,OAAO,IAAI,aAAa;EACjE,MAAM,KAAK,OAAO;EAClB,MAAM,OAAOA,OAAK,kBAAkB;GAAE,GAAG,OAAO,EAAE;GAAG,QAAQ;EAAS,CAAC;EACvE,OAAO,KAAK,KAAK,OAAO,SAAS,cAAc,QAAQ,CAAC,CAAC;EACzD,OAAO,KAAK,KACV,OAAO,QACP,OAAO,SAAS,SAAS,OAAO,WAAW,CAAC,GAC5C,OAAO,UACT;CACF,CAAC;CAED,OAAO;EACL;EACA,QAAQ,OAAO,UAAU,KAAK;EAC9B;EACA;EACA;EACA;EACA,QAAQ,eAAe;GAAE;GAAM;GAAM;EAAO,GAAG,IAAI,MAAM;EACzD;CACF;AACF,CAAC;;;;;;AAWH,MAAa,SACX,QAMA,MAAM,cACJ,OAAO,IAAI,KAAK,GAAG,IAAI,YACrB,QAAQ,KAAK,UAAU,OAAO,CAAC,CAAC,KAAK,QAAQ,IAAI,WAAW,OAAO,CAAC,CACtE,CACF"}
|
|
1
|
+
{"version":3,"file":"Telegram.mjs","names":["Events.Update","Api.provider","call","Api.call","upload","Api.upload","Api.decoded","toEvents","Events.toEvents","Events.allowedUpdates","Arr"],"sources":["../src/Telegram.ts"],"sourcesContent":["import {\n Array as Arr,\n type Cause,\n Context,\n Duration,\n Effect,\n Layer,\n Match,\n Option,\n Queue,\n Ref,\n Schedule,\n Schema,\n type Scope,\n Stream,\n} from \"effect\"\nimport { Base64 } from \"effect/encoding\"\nimport { HttpClient } from \"effect/http\"\nimport type { MediaSource } from \"@effect-uai/core/Media\"\nimport {\n CurrentConversation,\n type ConversationRef,\n type InboundEvent,\n MessageId,\n Messenger,\n type MessengerLimits,\n type MessengerService,\n type Outbound,\n type OutboundBody,\n} from \"@effect-uai/core/Messenger\"\nimport {\n type StreamViaEditsOptions,\n splitForLimit,\n streamViaEdits,\n} from \"@effect-uai/core/MessengerAdapter\"\nimport * as MessengerError from \"@effect-uai/core/MessengerError\"\nimport * as Api from \"./internal/api.js\"\nimport * as Events from \"./internal/events.js\"\n\nexport type { BotIdentity } from \"./internal/events.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Telegram's own markup modes. `Text` bodies are sent verbatim under one. */\nexport type ParseMode = \"HTML\" | \"MarkdownV2\" | \"Markdown\"\n\nexport type Config = Api.Config & {\n /**\n * Markup the model is prompted to write. Default `\"HTML\"`; `\"plain\"` sends\n * text with no `parse_mode` at all.\n */\n readonly parseMode?: ParseMode | \"plain\"\n /** Long-poll wait per `getUpdates` call. Default 30 seconds. */\n readonly pollTimeout?: Duration.Input\n /** Tuning for `stream`, which is post-then-edit on Telegram. */\n readonly stream?: StreamViaEditsOptions\n}\n\nexport type TelegramService = MessengerService & {\n /** From `getMe` at layer build: the identity behind `addressed`. */\n readonly bot: Events.BotIdentity\n}\n\n/**\n * Provider-typed tag. Yield this for the bot identity; yield the generic\n * `Messenger` tag for provider-portable code. Both are registered by {@link layer}.\n */\nexport class Telegram extends Context.Service<Telegram, TelegramService>()(\n \"@betalyra/effect-uai/providers/telegram/Telegram\",\n) {}\n\nexport const limits: MessengerLimits = { maxText: 4096, maxCaption: 1024 }\n\n// ---------------------------------------------------------------------------\n// Wire results\n// ---------------------------------------------------------------------------\n\nconst Me = Schema.Struct({ id: Schema.Number, username: Schema.String })\nconst Sent = Schema.Struct({ message_id: Schema.Number })\nconst Updates = Schema.Array(Events.Update)\nconst RawCall = Schema.Struct({\n method: Schema.String,\n params: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n})\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nconst isApiFailure = (e: Api.ApiError): e is Api.ApiFailure => e._tag === \"TelegramApiFailure\"\n\nconst describes = (needle: string) => (e: Api.ApiError) =>\n isApiFailure(e) && e.description.includes(needle)\n\nconst reasonOf = (e: Api.ApiError): string => (isApiFailure(e) ? e.description : \"rate limited\")\n\n/** An API rejection becomes the verb's `MessengerRequestFailed`; typed errors pass through. */\nconst requestFailed =\n (operation: MessengerError.MessengerOperation) =>\n (e: Api.ApiError | MessengerError.MessengerError): MessengerError.MessengerError =>\n Match.value(e).pipe(\n Match.tag(\n \"TelegramApiFailure\",\n (failure) =>\n new MessengerError.MessengerRequestFailed({\n provider: Api.provider,\n operation,\n reason: failure.description,\n raw: failure.raw,\n }),\n ),\n Match.orElse((typed) => typed),\n )\n\nconst unsupported = (capability: string, reason: string) =>\n new MessengerError.MessengerUnsupported({ provider: Api.provider, capability, reason })\n\n// 401 is a dead token and 409 a second poller on it; nothing else ends the stream.\nconst isFatalPoll = (e: Api.ApiError) => isApiFailure(e) && (e.code === 401 || e.code === 409)\n\n// Transient poll failures wait five seconds, or exactly what a 429 asked for.\nconst pollBackoff: Schedule.Schedule<number, Api.ApiError> = Schedule.forever.pipe(\n Schedule.setInputType<Api.ApiError>(),\n Schedule.modifyDelay(({ input }) =>\n Effect.succeed(input._tag === \"MessengerRateLimited\" ? input.retryAfter : Duration.seconds(5)),\n ),\n)\n\n// ---------------------------------------------------------------------------\n// Outbound params\n// ---------------------------------------------------------------------------\n\nconst target = (at: ConversationRef): Api.Params => ({\n chat_id: at.channel,\n ...(at.thread !== undefined && { message_thread_id: Number(at.thread) }),\n})\n\nconst replyParams = (msg: Outbound): Api.Params =>\n msg.replyTo === undefined ? {} : { reply_parameters: { message_id: Number(msg.replyTo) } }\n\nconst parseParams = (cfg: Config): Api.Params =>\n cfg.parseMode === \"plain\" ? {} : { parse_mode: cfg.parseMode ?? \"HTML\" }\n\ntype MediaRoute = { readonly method: string; readonly field: string }\n\n/** The endpoint a `MediaSource` goes through, decided by its MIME type. */\nconst mediaRoute = (media: MediaSource): MediaRoute =>\n Match.value(media.mimeType ?? \"\").pipe(\n Match.when(\n (m) => m.startsWith(\"image/\"),\n (): MediaRoute => ({ method: \"sendPhoto\", field: \"photo\" }),\n ),\n Match.when(\n (m) => m.startsWith(\"audio/\"),\n (): MediaRoute => ({ method: \"sendAudio\", field: \"audio\" }),\n ),\n Match.when(\n (m) => m.startsWith(\"video/\"),\n (): MediaRoute => ({ method: \"sendVideo\", field: \"video\" }),\n ),\n Match.orElse((): MediaRoute => ({ method: \"sendDocument\", field: \"document\" })),\n )\n\nconst defaultFilename = (media: MediaSource): string =>\n `file.${media.mimeType?.split(\"/\")[1] ?? \"bin\"}`\n\nconst invalidMedia = (raw: unknown) =>\n new MessengerError.MessengerRequestFailed({\n provider: Api.provider,\n operation: \"post\",\n reason: \"invalid base64 media\",\n raw,\n })\n\n// ---------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------\n\nexport const make = (\n cfg: Config,\n): Effect.Effect<\n TelegramService,\n MessengerError.MessengerConnectFailed,\n HttpClient.HttpClient | Scope.Scope\n> =>\n Effect.gen(function* () {\n // Captured once so the verbs need no `HttpClient` of their own.\n const client = yield* HttpClient.HttpClient\n const withClient = Effect.provideService(HttpClient.HttpClient, client)\n const call = (method: string, params?: Api.Params) => withClient(Api.call(cfg)(method, params))\n const upload = (method: string, params: Api.Params, file: Api.Upload) =>\n withClient(Api.upload(cfg)(method, params, file))\n\n const bot = yield* call(\"getMe\").pipe(\n Api.decoded(\"getMe\", Me),\n Effect.mapError(\n (e) =>\n new MessengerError.MessengerConnectFailed({\n provider: Api.provider,\n reason: reasonOf(e),\n raw: e.raw,\n }),\n ),\n )\n\n // -- inbound -----------------------------------------------------------\n\n const inbox = yield* Queue.unbounded<InboundEvent, MessengerError.MessengerError | Cause.Done>()\n const offset = yield* Ref.make(0)\n const toEvents = Events.toEvents(bot)\n\n // Answered before the event is offered, so the client's spinner never\n // waits on the recipe.\n const acknowledge = (update: Events.Update) =>\n Match.value(update).pipe(\n Match.when({ callback_query: Match.defined }, ({ callback_query }) =>\n call(\"answerCallbackQuery\", { callback_query_id: callback_query.id }).pipe(Effect.ignore),\n ),\n Match.orElse(() => Effect.void),\n )\n\n const poll = Effect.gen(function* () {\n const updates = yield* call(\"getUpdates\", {\n offset: yield* Ref.get(offset),\n timeout: Duration.toSeconds(cfg.pollTimeout ?? \"30 seconds\"),\n allowed_updates: Events.allowedUpdates,\n }).pipe(Api.decoded(\"getUpdates\", Updates))\n yield* Effect.forEach(updates, (update) =>\n Effect.gen(function* () {\n yield* Ref.set(offset, update.update_id + 1)\n yield* acknowledge(update)\n yield* Queue.offerAll(inbox, toEvents(update))\n }),\n )\n })\n\n // Transient failures (network, 5xx, 429) back off and poll again; fatal\n // ones end the stream with `MessengerTransportClosed`.\n yield* poll.pipe(\n Effect.retry({ while: (e) => !isFatalPoll(e), schedule: pollBackoff }),\n Effect.forever,\n Effect.catch((e) =>\n Queue.fail(\n inbox,\n new MessengerError.MessengerTransportClosed({\n provider: Api.provider,\n reason: reasonOf(e),\n raw: e.raw,\n }),\n ),\n ),\n Effect.ensuring(Queue.end(inbox)),\n Effect.forkScoped,\n )\n\n // -- outbound ----------------------------------------------------------\n\n // Telegram rejects markup it cannot parse; the same text then goes out plain.\n const sendText = (method: string, params: Api.Params) =>\n call(method, { ...params, ...parseParams(cfg) }).pipe(\n Effect.catchIf(describes(\"can't parse entities\"), () => call(method, params)),\n )\n\n const sendMedia = (\n at: ConversationRef,\n msg: Outbound,\n body: Extract<OutboundBody, { _tag: \"Media\" }>,\n ) => {\n const { method, field } = mediaRoute(body.media)\n const params = {\n ...target(at),\n ...replyParams(msg),\n ...(body.caption !== undefined && { caption: body.caption, ...parseParams(cfg) }),\n }\n const send = (bytes: Uint8Array, mimeType: string) =>\n upload(method, params, {\n field,\n bytes,\n filename: body.filename ?? defaultFilename(body.media),\n mimeType,\n })\n return Match.value(body.media).pipe(\n Match.tag(\"url\", ({ url }) => call(method, { ...params, [field]: url })),\n Match.tag(\"bytes\", ({ bytes, mimeType }) => send(bytes, mimeType)),\n Match.tag(\"base64\", ({ base64, mimeType }) =>\n Effect.fromResult(Base64.decode(base64)).pipe(\n Effect.mapError(invalidMedia),\n Effect.flatMap((bytes) => send(bytes, mimeType)),\n ),\n ),\n Match.exhaustive,\n )\n }\n\n const sendRaw = (payload: unknown) =>\n Effect.gen(function* () {\n const { method, params } = yield* Schema.decodeUnknownEffect(RawCall)(payload).pipe(\n Effect.mapError(\n () =>\n new MessengerError.MessengerRequestFailed({\n provider: Api.provider,\n operation: \"post\",\n reason: \"raw payload must be { method, params? }\",\n raw: payload,\n }),\n ),\n )\n return yield* call(method, params)\n })\n\n // The reply lands on the first chunk; the id is the last one's.\n const sendChunks = (at: ConversationRef, msg: Outbound, text: string) =>\n Effect.gen(function* () {\n const [first, ...rest] = splitForLimit(text, limits.maxText)\n const send = (chunk: string, extra: Api.Params) =>\n sendText(\"sendMessage\", { ...target(at), ...extra, text: chunk }).pipe(\n Api.decoded(\"sendMessage\", Sent),\n )\n const head = yield* send(first, replyParams(msg))\n const tail = yield* Effect.forEach(rest, (chunk) => send(chunk, {}))\n return Option.getOrElse(Arr.last(tail), () => head)\n })\n\n const post: MessengerService[\"post\"] = (msg) =>\n Effect.gen(function* () {\n const at = yield* CurrentConversation\n const sent = yield* Match.value(msg.body).pipe(\n Match.tag(\"Text\", ({ text }) => sendChunks(at, msg, text)),\n Match.tag(\"Media\", (body) =>\n sendMedia(at, msg, body).pipe(Api.decoded(\"sendMedia\", Sent)),\n ),\n Match.tag(\"Raw\", ({ payload }) => sendRaw(payload).pipe(Api.decoded(\"raw\", Sent))),\n Match.exhaustive,\n )\n return MessageId(String(sent.message_id))\n }).pipe(Effect.mapError(requestFailed(\"post\")))\n\n const edit: MessengerService[\"edit\"] = (ref, next) =>\n Match.value(next.body).pipe(\n Match.tag(\"Text\", ({ text }) =>\n sendText(\"editMessageText\", {\n chat_id: ref.conversation.channel,\n message_id: Number(ref.id),\n text,\n }).pipe(Effect.asVoid, Effect.mapError(requestFailed(\"edit\"))),\n ),\n Match.tag(\"Raw\", ({ payload }) =>\n sendRaw(payload).pipe(Effect.asVoid, Effect.mapError(requestFailed(\"edit\"))),\n ),\n Match.tag(\"Media\", () =>\n Effect.fail(unsupported(\"media edits\", \"send a new message instead\")),\n ),\n Match.exhaustive,\n )\n\n // Telegram allows a fixed emoji set; anything else is REACTION_INVALID.\n const react: MessengerService[\"react\"] = (ref, emoji) =>\n call(\"setMessageReaction\", {\n chat_id: ref.conversation.channel,\n message_id: Number(ref.id),\n reaction: [{ type: \"emoji\", emoji }],\n }).pipe(\n Effect.asVoid,\n Effect.catchIf(describes(\"REACTION_INVALID\"), () =>\n Effect.fail(unsupported(\"reaction\", `${emoji} is not in Telegram's reaction set`)),\n ),\n Effect.mapError(requestFailed(\"react\")),\n )\n\n // The indicator lasts about five seconds, so it is re-sent every four\n // until the scope closes. The first send is awaited so a bad chat fails here.\n const typing: MessengerService[\"typing\"] = Effect.gen(function* () {\n const at = yield* CurrentConversation\n const once = call(\"sendChatAction\", { ...target(at), action: \"typing\" })\n yield* once.pipe(Effect.mapError(requestFailed(\"typing\")))\n yield* once.pipe(\n Effect.ignore,\n Effect.schedule(Schedule.spaced(\"4 seconds\")),\n Effect.forkScoped,\n )\n })\n\n return {\n bot,\n events: Stream.fromQueue(inbox),\n post,\n edit,\n react,\n typing,\n stream: streamViaEdits({ post, edit, limits }, cfg.stream),\n limits,\n }\n })\n\n// ---------------------------------------------------------------------------\n// Layer\n// ---------------------------------------------------------------------------\n\n/**\n * One poller, registered under both the `Telegram` and `Messenger` tags. The\n * long-poll loop starts when the layer is built and stops when its scope\n * closes; `events` ends with it. Telegram allows one poller per token.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<\n Telegram | Messenger,\n MessengerError.MessengerConnectFailed,\n HttpClient.HttpClient\n> =>\n Layer.effectContext(\n Effect.map(make(cfg), (service) =>\n Context.make(Telegram, service).pipe(Context.add(Messenger, service)),\n ),\n )\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect, Redacted, Schema } from "effect";
|
|
2
|
-
import { HttpClient } from "effect/
|
|
2
|
+
import { HttpClient } from "effect/http";
|
|
3
3
|
import * as MessengerError from "@effect-uai/core/MessengerError";
|
|
4
4
|
//#region src/internal/api.d.ts
|
|
5
5
|
declare const provider = "telegram";
|
|
@@ -39,4 +39,4 @@ declare const upload: (cfg: Config) => (method: string, params: Params, file: Up
|
|
|
39
39
|
declare const decoded: <A>(method: string, schema: Schema.Decoder<A>) => <E, R>(result: Effect.Effect<unknown, E, R>) => Effect.Effect<A, E | ApiFailure, R>;
|
|
40
40
|
//#endregion
|
|
41
41
|
export { Upload as a, provider as c, Params as i, upload as l, ApiFailure as n, call as o, Config as r, decoded as s, ApiError as t };
|
|
42
|
-
//# sourceMappingURL=api-
|
|
42
|
+
//# sourceMappingURL=api-o1OT-Mk0.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-
|
|
1
|
+
{"version":3,"file":"api-o1OT-Mk0.d.mts","names":[],"sources":["../src/internal/api.ts"],"mappings":";;;;cAKa;KAED;WACD,OAAO,SAAS;;WAEhB;;;;;;;;;;cAQE,mBAAmB;WACrB;WACA;WACA;WACA;;KAGC,WAAW,aAAa,eAAe;KAUvC,SAAS,SAAS;;KAGlB;WACD;WACA,OAAO;WACP;WACA;;;cAwCE,OAAI,KACT,YAAM,gBACG,SAAU,WAAc,OAAO,gBAAgB,UAAU,WAAW;;cAyBxE,SAAM,KACX,YAAM,gBAEI,QACN,QAAM,MACR,WACL,OAAO,gBAAgB,UAAU,WAAW;;cASpC,UACV,GAAC,gBAAgB,QAAU,OAAO,QAAQ,QAC1C,GAAG,GAAC,QAAU,OAAO,gBAAgB,GAAG,OAAK,OAAO,OAAO,GAAG,IAAI,YAAY"}
|