Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add chat API support #278

Merged
merged 8 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/funny-lamps-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@livekit/rtc-node": patch
---

Add chat API support
1 change: 1 addition & 0 deletions packages/livekit-rtc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ export { StreamState, TrackKind, TrackSource } from './proto/track_pb.js';
export { VideoBufferType, VideoRotation, VideoCodec } from './proto/video_frame_pb.js';
export { ParticipantKind } from './proto/participant_pb.js';
export { dispose } from './ffi_client.js';
export type { ChatMessage } from './types.js';
13 changes: 8 additions & 5 deletions packages/livekit-rtc/src/napi/native.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@

/* auto-generated by NAPI-RS */

export function livekitInitialize(callback: (data: Uint8Array) => void, captureLogs: boolean): void;
export function livekitFfiRequest(data: Uint8Array): Uint8Array;
export function livekitRetrievePtr(handle: Uint8Array): bigint;
export function livekitCopyBuffer(ptr: bigint, len: number): Uint8Array;
export function livekitDispose(): Promise<void>;
export declare function livekitInitialize(
callback: (data: Uint8Array) => void,
captureLogs: boolean,
): void;
export declare function livekitFfiRequest(data: Uint8Array): Uint8Array;
export declare function livekitRetrievePtr(handle: Uint8Array): bigint;
export declare function livekitCopyBuffer(ptr: bigint, len: number): Uint8Array;
export declare function livekitDispose(): Promise<void>;
export declare class FfiHandle {
constructor(handle: bigint);
dispose(): void;
Expand Down
79 changes: 78 additions & 1 deletion packages/livekit-rtc/src/participant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// SPDX-License-Identifier: Apache-2.0
import { FfiClient, FfiHandle } from './ffi_client.js';
import type { OwnedParticipant, ParticipantInfo, ParticipantKind } from './proto/participant_pb.js';
import type {
import {
ChatMessage as ChatMessageModel,
PublishDataCallback,
PublishDataResponse,
PublishSipDtmfCallback,
Expand All @@ -12,6 +13,8 @@ import type {
PublishTrackResponse,
PublishTranscriptionCallback,
PublishTranscriptionResponse,
SendChatMessageCallback,
SendChatMessageResponse,
SetLocalAttributesCallback,
SetLocalAttributesResponse,
SetLocalMetadataCallback,
Expand All @@ -23,11 +26,13 @@ import type {
UnpublishTrackResponse,
} from './proto/room_pb.js';
import {
EditChatMessageRequest,
TranscriptionSegment as ProtoTranscriptionSegment,
PublishDataRequest,
PublishSipDtmfRequest,
PublishTrackRequest,
PublishTranscriptionRequest,
SendChatMessageRequest,
SetLocalAttributesRequest,
SetLocalMetadataRequest,
SetLocalNameRequest,
Expand All @@ -37,6 +42,7 @@ import type { LocalTrack } from './track.js';
import type { RemoteTrackPublication, TrackPublication } from './track_publication.js';
import { LocalTrackPublication } from './track_publication.js';
import type { Transcription } from './transcription.js';
import { ChatMessage } from './types.js';

export abstract class Participant {
/** @internal */
Expand Down Expand Up @@ -185,6 +191,77 @@ export class LocalParticipant extends Participant {
});
}

/**
*
*/
async sendChatMessage(
text: string,
destinationIdentities?: Array<string>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this default to all? Worth adding docstrings

senderIdentity?: string,
): Promise<ChatMessage> {
const req = new SendChatMessageRequest({
localParticipantHandle: this.ffi_handle.handle,
message: text,
destinationIdentities,
senderIdentity,
});

const res = FfiClient.instance.request<SendChatMessageResponse>({
message: { case: 'sendChatMessage', value: req },
});

const cb = await FfiClient.instance.waitFor<SendChatMessageCallback>((ev) => {
return ev.message.case == 'chatMessage' && ev.message.value.asyncId == res.asyncId;
});

if (cb.error) {
throw new Error(cb.error);
}
const { id, timestamp, editTimestamp, message } = cb.chatMessage;
return { id, timestamp: Number(timestamp), editTimestamp: Number(editTimestamp), message };
}

/**
* Sends a chat message to participants in the room
*
* @param text - The text content of the chat message.
* @param destinationIdentities - An optional array of recipient identities to whom the message will be sent. If omitted, the message is broadcast to all participants.
* @param senderIdentity - An optional identity of the sender. If omitted, the default sender identity is used.
*
*/
async editChatMessage(
editText: string,
originalMessage: ChatMessage,
destinationIdentities?: Array<string>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems kinda weird that you can give different values here than to the original. this might also cause participants who join a call after an initial message is sent but before an edit to receive the edit and maybe have strange behavior? not sure if there's an easy way to solve though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, had the same feeling and opted to omit destinationIdentites on client-sdk-js altogether for now because of that

senderIdentity?: string,
): Promise<ChatMessage> {
const req = new EditChatMessageRequest({
localParticipantHandle: this.ffi_handle.handle,
editText,
originalMessage: new ChatMessageModel({
...originalMessage,
timestamp: BigInt(originalMessage.timestamp),
editTimestamp: BigInt(originalMessage.editTimestamp),
}),
destinationIdentities,
senderIdentity,
});

const res = FfiClient.instance.request<SendChatMessageResponse>({
message: { case: 'editChatMessage', value: req },
});

const cb = await FfiClient.instance.waitFor<SendChatMessageCallback>((ev) => {
return ev.message.case == 'chatMessage' && ev.message.value.asyncId == res.asyncId;
});

if (cb.error) {
throw new Error(cb.error);
}
const { id, timestamp, editTimestamp, message } = cb.chatMessage;
return { id, timestamp: Number(timestamp), editTimestamp: Number(editTimestamp), message };
}

async updateName(name: string) {
const req = new SetLocalNameRequest({
localParticipantHandle: this.ffi_handle.handle,
Expand Down
30 changes: 29 additions & 1 deletion packages/livekit-rtc/src/proto/ffi_pb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
import { Message, proto3, protoInt64 } from "@bufbuild/protobuf";
import { ConnectCallback, ConnectRequest, ConnectResponse, DisconnectCallback, DisconnectRequest, DisconnectResponse, GetSessionStatsCallback, GetSessionStatsRequest, GetSessionStatsResponse, PublishDataCallback, PublishDataRequest, PublishDataResponse, PublishSipDtmfCallback, PublishSipDtmfRequest, PublishSipDtmfResponse, PublishTrackCallback, PublishTrackRequest, PublishTrackResponse, PublishTranscriptionCallback, PublishTranscriptionRequest, PublishTranscriptionResponse, RoomEvent, SetLocalAttributesCallback, SetLocalAttributesRequest, SetLocalAttributesResponse, SetLocalMetadataCallback, SetLocalMetadataRequest, SetLocalMetadataResponse, SetLocalNameCallback, SetLocalNameRequest, SetLocalNameResponse, SetSubscribedRequest, SetSubscribedResponse, UnpublishTrackCallback, UnpublishTrackRequest, UnpublishTrackResponse } from "./room_pb.js";
import { ConnectCallback, ConnectRequest, ConnectResponse, DisconnectCallback, DisconnectRequest, DisconnectResponse, EditChatMessageRequest, GetSessionStatsCallback, GetSessionStatsRequest, GetSessionStatsResponse, PublishDataCallback, PublishDataRequest, PublishDataResponse, PublishSipDtmfCallback, PublishSipDtmfRequest, PublishSipDtmfResponse, PublishTrackCallback, PublishTrackRequest, PublishTrackResponse, PublishTranscriptionCallback, PublishTranscriptionRequest, PublishTranscriptionResponse, RoomEvent, SendChatMessageCallback, SendChatMessageRequest, SendChatMessageResponse, SetLocalAttributesCallback, SetLocalAttributesRequest, SetLocalAttributesResponse, SetLocalMetadataCallback, SetLocalMetadataRequest, SetLocalMetadataResponse, SetLocalNameCallback, SetLocalNameRequest, SetLocalNameResponse, SetSubscribedRequest, SetSubscribedResponse, UnpublishTrackCallback, UnpublishTrackRequest, UnpublishTrackResponse } from "./room_pb.js";
import { CreateAudioTrackRequest, CreateAudioTrackResponse, CreateVideoTrackRequest, CreateVideoTrackResponse, EnableRemoteTrackRequest, EnableRemoteTrackResponse, GetStatsCallback, GetStatsRequest, GetStatsResponse, LocalTrackMuteRequest, LocalTrackMuteResponse, TrackEvent } from "./track_pb.js";
import { CaptureVideoFrameRequest, CaptureVideoFrameResponse, NewVideoSourceRequest, NewVideoSourceResponse, NewVideoStreamRequest, NewVideoStreamResponse, VideoConvertRequest, VideoConvertResponse, VideoStreamEvent, VideoStreamFromParticipantRequest, VideoStreamFromParticipantResponse } from "./video_frame_pb.js";
import { AudioStreamEvent, AudioStreamFromParticipantRequest, AudioStreamFromParticipantResponse, CaptureAudioFrameCallback, CaptureAudioFrameRequest, CaptureAudioFrameResponse, ClearAudioBufferRequest, ClearAudioBufferResponse, FlushSoxResamplerRequest, FlushSoxResamplerResponse, NewAudioResamplerRequest, NewAudioResamplerResponse, NewAudioSourceRequest, NewAudioSourceResponse, NewAudioStreamRequest, NewAudioStreamResponse, NewSoxResamplerRequest, NewSoxResamplerResponse, PushSoxResamplerRequest, PushSoxResamplerResponse, RemixAndResampleRequest, RemixAndResampleResponse } from "./audio_frame_pb.js";
Expand Down Expand Up @@ -285,6 +285,18 @@ export class FfiRequest extends Message<FfiRequest> {
*/
value: FlushSoxResamplerRequest;
case: "flushSoxResampler";
} | {
/**
* @generated from field: livekit.proto.SendChatMessageRequest send_chat_message = 36;
*/
value: SendChatMessageRequest;
case: "sendChatMessage";
} | {
/**
* @generated from field: livekit.proto.EditChatMessageRequest edit_chat_message = 37;
*/
value: EditChatMessageRequest;
case: "editChatMessage";
} | { case: undefined; value?: undefined } = { case: undefined };

constructor(data?: PartialMessage<FfiRequest>) {
Expand Down Expand Up @@ -329,6 +341,8 @@ export class FfiRequest extends Message<FfiRequest> {
{ no: 33, name: "new_sox_resampler", kind: "message", T: NewSoxResamplerRequest, oneof: "message" },
{ no: 34, name: "push_sox_resampler", kind: "message", T: PushSoxResamplerRequest, oneof: "message" },
{ no: 35, name: "flush_sox_resampler", kind: "message", T: FlushSoxResamplerRequest, oneof: "message" },
{ no: 36, name: "send_chat_message", kind: "message", T: SendChatMessageRequest, oneof: "message" },
{ no: 37, name: "edit_chat_message", kind: "message", T: EditChatMessageRequest, oneof: "message" },
]);

static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): FfiRequest {
Expand Down Expand Up @@ -569,6 +583,12 @@ export class FfiResponse extends Message<FfiResponse> {
*/
value: FlushSoxResamplerResponse;
case: "flushSoxResampler";
} | {
/**
* @generated from field: livekit.proto.SendChatMessageResponse send_chat_message = 36;
*/
value: SendChatMessageResponse;
case: "sendChatMessage";
} | { case: undefined; value?: undefined } = { case: undefined };

constructor(data?: PartialMessage<FfiResponse>) {
Expand Down Expand Up @@ -613,6 +633,7 @@ export class FfiResponse extends Message<FfiResponse> {
{ no: 33, name: "new_sox_resampler", kind: "message", T: NewSoxResamplerResponse, oneof: "message" },
{ no: 34, name: "push_sox_resampler", kind: "message", T: PushSoxResamplerResponse, oneof: "message" },
{ no: 35, name: "flush_sox_resampler", kind: "message", T: FlushSoxResamplerResponse, oneof: "message" },
{ no: 36, name: "send_chat_message", kind: "message", T: SendChatMessageResponse, oneof: "message" },
]);

static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): FfiResponse {
Expand Down Expand Up @@ -763,6 +784,12 @@ export class FfiEvent extends Message<FfiEvent> {
*/
value: PublishSipDtmfCallback;
case: "publishSipDtmf";
} | {
/**
* @generated from field: livekit.proto.SendChatMessageCallback chat_message = 22;
*/
value: SendChatMessageCallback;
case: "chatMessage";
} | { case: undefined; value?: undefined } = { case: undefined };

constructor(data?: PartialMessage<FfiEvent>) {
Expand Down Expand Up @@ -793,6 +820,7 @@ export class FfiEvent extends Message<FfiEvent> {
{ no: 19, name: "get_session_stats", kind: "message", T: GetSessionStatsCallback, oneof: "message" },
{ no: 20, name: "panic", kind: "message", T: Panic, oneof: "message" },
{ no: 21, name: "publish_sip_dtmf", kind: "message", T: PublishSipDtmfCallback, oneof: "message" },
{ no: 22, name: "chat_message", kind: "message", T: SendChatMessageCallback, oneof: "message" },
]);

static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): FfiEvent {
Expand Down
Loading
Loading