@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
TypeScript
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;
}