UNPKG

@microsoft/teams.apps

Version:

<p> <a href="https://www.npmjs.com/package/@microsoft/teams.apps" target="_blank"> <img src="https://img.shields.io/npm/v/@microsoft/teams.apps/latest" /> </a> <a href="https://www.npmjs.com/package/@microsoft/teams.apps?activeTab=code

233 lines (232 loc) • 10 kB
import { ActivityParams, ChannelData, Client, ConversationReference, IMessageActivity, IMessageActivityInput, ITypingActivity, ITypingActivityInput, SentActivity, TextFormat } from '@microsoft/teams.api'; import { EventEmitter, ILogger } from '@microsoft/teams.common'; import { IStreamer, IStreamerEvents, StreamOptions } from '../types'; /** * HTTP-based streaming implementation for Microsoft Teams activities. * * Allows sending typing indicators and messages in chunks to Teams. * Queues incoming activities and flushes them periodically to avoid * rate limits. * * Flow: * 1. `emit()` adds activities to the queue and starts a flush if none scheduled. * 2. `flush()` starts by cancelling any pending flush, then drains the entire queue under a lock. * 3. Informative typing updates are sent immediately, but only while no message text has accumulated. * 4. Message text is combined and sent as a typing activity. * 5. `flush()` schedules another flush 500ms later if more items remain in queue. * 6. `close()` waits for the queue to empty and sends the final message activity. */ export declare class HttpStream implements IStreamer { readonly events: EventEmitter<IStreamerEvents>; protected client: Client; protected ref: ConversationReference; protected replyChain?: StreamOptions['replyChain']; protected index: number; protected id?: string; protected text: string; protected channelData: ChannelData; protected finalActivity?: IMessageActivityInput; protected queue: Array<IMessageActivityInput | ITypingActivityInput>; private _result?; private _timeout?; private _logger; private _flushing; private _timedOut; private _retired; private readonly _totalTimeout; /** * @deprecated Teams no longer supports stopping a stream; always returns false. */ get canceled(): boolean; /** * Whether the stream has timed out. * Set when the service reports ten minutes have elapsed since the first * informative or content chunk. */ get timedOut(): boolean; /** * Whether the current streamed message has been finalized. * * Closing is idempotent until the next emit or update reopens the stream. */ get closed(): boolean; constructor(client: Client, ref: ConversationReference, logger?: ILogger, options?: StreamOptions); /** * Emit a new activity or text to the stream. * An explicit replyToId on the final message is preserved; otherwise, * the conversation reference's activity ID is used. * @param activity Activity object or string message. */ /** * @deprecated Use MessageActivityInput or TypingActivityInput instead. */ emit(activity: IMessageActivity | ITypingActivity): void; emit(activity: IMessageActivityInput | ITypingActivityInput | string): void; /** * Send a typing/status update without adding to the main text. * @param text Status text (ex. "Thinking...") * @param textFormat Format of `text` (ex. `'extendedmarkdown'`). Omit or pass `null` * to use the Teams default (`'markdown'`). */ update(text: string, textFormat?: TextFormat | null): void; /** * Discard accumulated streamed text and any pending text deltas. The * final message sent by `close()` will not include them. Intermediate * typing chunks already shipped to Teams are not undone, but the final * message updates the same activity id, so the user sees the cleared * state. Pending non-message activities (typing updates, structured * message emits that follow) are not affected. */ clearText(): void; /** * Close the stream by sending the final message. * Waits for all queued activities to flush. * A Plan timeout or final Plan conflict discards the failed cycle and raises once. * Further closes return undefined until the next emit or update starts a fresh cycle. */ close(): Promise<SentActivity | undefined>; /** * Send through retry, excluding terminal stream errors and HTTP 409 Plan conflicts. * @param activity ActivityParams to send. */ protected sendWithRetry(activity: ActivityParams): Promise<{ id: string; text?: string; textFormat?: TextFormat; attachmentLayout?: import("@microsoft/teams.api").AttachmentLayout; attachments?: import("@microsoft/teams.api").Attachment[]; suggestedActions?: import("@microsoft/teams.api").SuggestedActions; type: "message"; recipient?: import("@microsoft/teams.api").Account; replyToId?: string; entities?: import("@microsoft/teams.api").Entity[]; channelData?: ChannelData; from: ConversationReference["bot"]; conversation: ConversationReference["conversation"]; } | { id: string; text?: string; textFormat?: TextFormat; type: "typing"; recipient?: import("@microsoft/teams.api").Account; replyToId?: string; entities?: import("@microsoft/teams.api").Entity[]; channelData?: ChannelData; from: ConversationReference["bot"]; conversation: ConversationReference["conversation"]; }>; /** * Send the buffered content as a plain final message. * * Drops the `streaminfo` entity (added by `addStreamFinal()` on the normal * close path) and the stream channel data so `id` is kept and the send routes * through the update path instead of creating a duplicate message. * Plan completion requires a POST: callers must resolve it through Plan.complete() * after a timeout instead of retrying this ordinary-message fallback. */ protected sendFinal(): Promise<{ id: string; text?: string; textFormat?: TextFormat; attachmentLayout?: import("@microsoft/teams.api").AttachmentLayout; attachments?: import("@microsoft/teams.api").Attachment[]; suggestedActions?: import("@microsoft/teams.api").SuggestedActions; type: "message"; recipient?: import("@microsoft/teams.api").Account; replyToId?: string; entities?: import("@microsoft/teams.api").Entity[]; channelData?: ChannelData; from: ConversationReference["bot"]; conversation: ConversationReference["conversation"]; } | { id: string; text?: string; textFormat?: TextFormat; type: "typing"; recipient?: import("@microsoft/teams.api").Account; replyToId?: string; entities?: import("@microsoft/teams.api").Entity[]; channelData?: ChannelData; from: ConversationReference["bot"]; conversation: ConversationReference["conversation"]; }>; /** * Prepare the stream instance to start a new stream cycle after close(). */ protected resetForNextStream(): void; /** * Flush queued activities. * Drains the entire queue in a single pass. */ protected flush(): Promise<void>; /** * Push a new chunk to the stream. * @param activity Typing activity input to send. */ protected pushStreamChunk(activity: ITypingActivityInput): Promise<void>; /** * Creates the first message of the stream as a reply in the reply chain (thread). * In a channel, when the service answers HTTP 404 and the reply chain id is a plain non-zero number, creates the message in the inbound conversation instead. * Any other error is thrown, including a 404 outside a channel. */ protected createInReplyChain(replyChain: NonNullable<StreamOptions['replyChain']>, payload: ActivityParams & { from: ConversationReference['bot']; conversation: ConversationReference['conversation']; }): Promise<{ id: string; text?: string; textFormat?: TextFormat; attachmentLayout?: import("@microsoft/teams.api").AttachmentLayout; attachments?: import("@microsoft/teams.api").Attachment[]; suggestedActions?: import("@microsoft/teams.api").SuggestedActions; type: "message"; recipient?: import("@microsoft/teams.api").Account; replyToId?: string; entities?: import("@microsoft/teams.api").Entity[]; channelData?: ChannelData; from: ConversationReference["bot"]; conversation: ConversationReference["conversation"]; } | { id: string; text?: string; textFormat?: TextFormat; type: "typing"; recipient?: import("@microsoft/teams.api").Account; replyToId?: string; entities?: import("@microsoft/teams.api").Entity[]; channelData?: ChannelData; from: ConversationReference["bot"]; conversation: ConversationReference["conversation"]; }>; /** * Send or update a streaming activity * @param activity ActivityParams to send. */ protected send(activity: ActivityParams): Promise<{ id: string; text?: string; textFormat?: TextFormat; attachmentLayout?: import("@microsoft/teams.api").AttachmentLayout; attachments?: import("@microsoft/teams.api").Attachment[]; suggestedActions?: import("@microsoft/teams.api").SuggestedActions; type: "message"; recipient?: import("@microsoft/teams.api").Account; replyToId?: string; entities?: import("@microsoft/teams.api").Entity[]; channelData?: ChannelData; from: ConversationReference["bot"]; conversation: ConversationReference["conversation"]; } | { id: string; text?: string; textFormat?: TextFormat; type: "typing"; recipient?: import("@microsoft/teams.api").Account; replyToId?: string; entities?: import("@microsoft/teams.api").Entity[]; channelData?: ChannelData; from: ConversationReference["bot"]; conversation: ConversationReference["conversation"]; }>; private resetCurrentStream; }