Add Audio, Image and Sticker messages
This commit is contained in:
parent
304a1021b1
commit
e6b3ec616a
|
@ -1,4 +1,4 @@
|
|||
import { ChatMessage } from "./chat_message";
|
||||
import { ChatMessage, MediaContent } from "./chat_message";
|
||||
import { chatIdToContentTopic } from "./contentTopic";
|
||||
import { createSymKeyFromPassword } from "./encryption";
|
||||
|
||||
|
@ -24,10 +24,16 @@ export class Chat {
|
|||
return chatIdToContentTopic(this.id);
|
||||
}
|
||||
|
||||
public createMessage(text: string): ChatMessage {
|
||||
public createMessage(text: string, mediaContent?: MediaContent): ChatMessage {
|
||||
const { timestamp, clock } = this._nextClockAndTimestamp();
|
||||
|
||||
const message = ChatMessage.createMessage(clock, timestamp, text, this.id);
|
||||
const message = ChatMessage.createMessage(
|
||||
clock,
|
||||
timestamp,
|
||||
text,
|
||||
this.id,
|
||||
mediaContent
|
||||
);
|
||||
|
||||
this._updateClockFromMessage(message);
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
import { expect } from "chai";
|
||||
|
||||
import {
|
||||
AudioContent,
|
||||
ChatMessage,
|
||||
ContentType,
|
||||
ImageContent,
|
||||
StickerContent,
|
||||
} from "./chat_message";
|
||||
import {
|
||||
AudioMessage_AudioType,
|
||||
ChatMessage_ContentType,
|
||||
} from "./proto/communities/v1/chat_message";
|
||||
import { ImageType } from "./proto/communities/v1/enums";
|
||||
|
||||
describe("Chat Message", () => {
|
||||
it("Encode & decode Image message", () => {
|
||||
const payload = Buffer.from([1, 1]);
|
||||
|
||||
const imageContent: ImageContent = {
|
||||
image: payload,
|
||||
imageType: ImageType.IMAGE_TYPE_PNG,
|
||||
contentType: ContentType.Image,
|
||||
};
|
||||
|
||||
const message = ChatMessage.createMessage(
|
||||
1,
|
||||
1,
|
||||
"Some text",
|
||||
"chat-id",
|
||||
imageContent
|
||||
);
|
||||
|
||||
const buf = message.encode();
|
||||
const dec = ChatMessage.decode(buf);
|
||||
|
||||
expect(dec.contentType).eq(ChatMessage_ContentType.CONTENT_TYPE_IMAGE);
|
||||
expect(dec.image?.payload?.toString()).eq(payload.toString());
|
||||
expect(dec.image?.type).eq(ImageType.IMAGE_TYPE_PNG);
|
||||
});
|
||||
|
||||
it("Encode & decode Audio message", () => {
|
||||
const payload = Buffer.from([1, 1]);
|
||||
const durationMs = 12345;
|
||||
|
||||
const audioContent: AudioContent = {
|
||||
audio: payload,
|
||||
audioType: AudioMessage_AudioType.AUDIO_TYPE_AAC,
|
||||
durationMs,
|
||||
contentType: ContentType.Audio,
|
||||
};
|
||||
|
||||
const message = ChatMessage.createMessage(
|
||||
1,
|
||||
1,
|
||||
"Some text",
|
||||
"chat-id",
|
||||
audioContent
|
||||
);
|
||||
|
||||
const buf = message.encode();
|
||||
const dec = ChatMessage.decode(buf);
|
||||
|
||||
expect(dec.contentType).eq(ChatMessage_ContentType.CONTENT_TYPE_AUDIO);
|
||||
expect(dec.audio?.payload?.toString()).eq(payload.toString());
|
||||
expect(dec.audio?.type).eq(ImageType.IMAGE_TYPE_PNG);
|
||||
expect(dec.audio?.durationMs).eq(durationMs);
|
||||
});
|
||||
|
||||
it("Encode & decode Sticker message", () => {
|
||||
const hash = "deadbeef";
|
||||
const pack = 12345;
|
||||
|
||||
const stickerContent: StickerContent = {
|
||||
hash,
|
||||
pack,
|
||||
contentType: ContentType.Sticker,
|
||||
};
|
||||
|
||||
const message = ChatMessage.createMessage(
|
||||
1,
|
||||
1,
|
||||
"Some text",
|
||||
"chat-id",
|
||||
stickerContent
|
||||
);
|
||||
|
||||
const buf = message.encode();
|
||||
const dec = ChatMessage.decode(buf);
|
||||
|
||||
expect(dec.contentType).eq(ChatMessage_ContentType.CONTENT_TYPE_STICKER);
|
||||
expect(dec.sticker?.hash).eq(hash);
|
||||
expect(dec.sticker?.pack).eq(pack);
|
||||
});
|
||||
});
|
|
@ -10,53 +10,96 @@ import {
|
|||
} from "./proto/communities/v1/chat_message";
|
||||
import { ImageType, MessageType } from "./proto/communities/v1/enums";
|
||||
|
||||
export type MediaContent = StickerContent | ImageContent | AudioContent;
|
||||
|
||||
export enum ContentType {
|
||||
Sticker,
|
||||
Image,
|
||||
Audio,
|
||||
}
|
||||
|
||||
export interface StickerContent {
|
||||
hash: string;
|
||||
pack: number;
|
||||
contentType: ContentType.Sticker;
|
||||
}
|
||||
|
||||
export interface ImageContent {
|
||||
image: Uint8Array;
|
||||
imageType: ImageType;
|
||||
contentType: ContentType.Image;
|
||||
}
|
||||
|
||||
export interface AudioContent {
|
||||
audio: Uint8Array;
|
||||
audioType: AudioMessage_AudioType;
|
||||
durationMs: number;
|
||||
contentType: ContentType.Audio;
|
||||
}
|
||||
|
||||
function isSticker(content: MediaContent): content is StickerContent {
|
||||
return content.contentType === ContentType.Sticker;
|
||||
}
|
||||
|
||||
function isImage(content: MediaContent): content is ImageContent {
|
||||
return content.contentType === ContentType.Image;
|
||||
}
|
||||
|
||||
function isAudio(content: MediaContent): content is AudioContent {
|
||||
return content.contentType === ContentType.Audio;
|
||||
}
|
||||
|
||||
export class ChatMessage {
|
||||
private constructor(public proto: proto.ChatMessage) {}
|
||||
|
||||
/**
|
||||
* Create a chat message to be sent to an Open (permission = no membership) community
|
||||
* Create a chat message to be sent to an Open (permission = no membership) community.
|
||||
*
|
||||
* @throws string If mediaContent is malformed
|
||||
*/
|
||||
public static createMessage(
|
||||
clock: number,
|
||||
timestamp: number,
|
||||
text: string,
|
||||
chatId: string
|
||||
chatId: string,
|
||||
mediaContent?: MediaContent
|
||||
): ChatMessage {
|
||||
const proto = {
|
||||
clock, // ms?
|
||||
timestamp, //ms?
|
||||
text,
|
||||
/** Id of the message that we are replying to */
|
||||
responseTo: "",
|
||||
/** Ens name of the sender */
|
||||
ensName: "",
|
||||
/** Public Key of the community (TBC) **/
|
||||
chatId,
|
||||
/** The type of message (public/one-to-one/private-group-chat) */
|
||||
messageType: MessageType.MESSAGE_TYPE_COMMUNITY_CHAT,
|
||||
/** The type of the content of the message */
|
||||
contentType: ChatMessage_ContentType.CONTENT_TYPE_TEXT_PLAIN,
|
||||
sticker: undefined,
|
||||
image: undefined,
|
||||
audio: undefined,
|
||||
community: undefined, // Used to share a community
|
||||
grant: undefined,
|
||||
};
|
||||
let sticker, image, audio;
|
||||
let contentType = ChatMessage_ContentType.CONTENT_TYPE_TEXT_PLAIN;
|
||||
|
||||
return new ChatMessage(proto);
|
||||
if (mediaContent) {
|
||||
if (isSticker(mediaContent)) {
|
||||
if (!mediaContent.hash || !mediaContent.pack)
|
||||
throw "Malformed Sticker Content";
|
||||
sticker = {
|
||||
hash: mediaContent.hash,
|
||||
pack: mediaContent.pack,
|
||||
};
|
||||
contentType = ChatMessage_ContentType.CONTENT_TYPE_STICKER;
|
||||
} else if (isImage(mediaContent)) {
|
||||
if (!mediaContent.image || !mediaContent.imageType)
|
||||
throw "Malformed Image Content";
|
||||
image = {
|
||||
payload: mediaContent.image,
|
||||
type: mediaContent.imageType,
|
||||
};
|
||||
contentType = ChatMessage_ContentType.CONTENT_TYPE_IMAGE;
|
||||
} else if (isAudio(mediaContent)) {
|
||||
if (
|
||||
!mediaContent.audio ||
|
||||
!mediaContent.audioType ||
|
||||
!mediaContent.durationMs
|
||||
)
|
||||
throw "Malformed Audio Content";
|
||||
audio = {
|
||||
payload: mediaContent.audio,
|
||||
type: mediaContent.audioType,
|
||||
durationMs: mediaContent.durationMs,
|
||||
};
|
||||
contentType = ChatMessage_ContentType.CONTENT_TYPE_AUDIO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a an image chat message to be sent to an Open (permission = no membership) community
|
||||
*/
|
||||
public static createImageMessage(
|
||||
clock: number,
|
||||
timestamp: number,
|
||||
text: string,
|
||||
chatId: string,
|
||||
image: Uint8Array,
|
||||
imageType: ImageType
|
||||
): ChatMessage {
|
||||
const proto = {
|
||||
clock, // ms?
|
||||
timestamp, //ms?
|
||||
|
@ -70,91 +113,10 @@ export class ChatMessage {
|
|||
/** The type of message (public/one-to-one/private-group-chat) */
|
||||
messageType: MessageType.MESSAGE_TYPE_COMMUNITY_CHAT,
|
||||
/** The type of the content of the message */
|
||||
contentType: ChatMessage_ContentType.CONTENT_TYPE_IMAGE,
|
||||
sticker: undefined,
|
||||
image: {
|
||||
payload: image,
|
||||
type: imageType,
|
||||
},
|
||||
audio: undefined,
|
||||
community: undefined, // Used to share a community
|
||||
grant: undefined,
|
||||
};
|
||||
|
||||
return new ChatMessage(proto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a a sticker chat message to be sent to an Open (permission = no membership) community
|
||||
*/
|
||||
public static createStickerMessage(
|
||||
clock: number,
|
||||
timestamp: number,
|
||||
text: string,
|
||||
chatId: string,
|
||||
stickerHash: string,
|
||||
stickerPack: number
|
||||
): ChatMessage {
|
||||
const proto = {
|
||||
clock, // ms?
|
||||
timestamp, //ms?
|
||||
text,
|
||||
/** Id of the message that we are replying to */
|
||||
responseTo: "",
|
||||
/** Ens name of the sender */
|
||||
ensName: "",
|
||||
/** Public Key of the community (TBC) **/
|
||||
chatId,
|
||||
/** The type of message (public/one-to-one/private-group-chat) */
|
||||
messageType: MessageType.MESSAGE_TYPE_COMMUNITY_CHAT,
|
||||
/** The type of the content of the message */
|
||||
contentType: ChatMessage_ContentType.CONTENT_TYPE_STICKER,
|
||||
sticker: {
|
||||
hash: stickerHash,
|
||||
pack: stickerPack,
|
||||
},
|
||||
image: undefined,
|
||||
audio: undefined,
|
||||
community: undefined, // Used to share a community
|
||||
grant: undefined,
|
||||
};
|
||||
|
||||
return new ChatMessage(proto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a a sticker chat message to be sent to an Open (permission = no membership) community
|
||||
*/
|
||||
public static createAudioMessage(
|
||||
clock: number,
|
||||
timestamp: number,
|
||||
text: string,
|
||||
chatId: string,
|
||||
audio: Uint8Array,
|
||||
audioType: AudioMessage_AudioType,
|
||||
audioDurationMs: number
|
||||
): ChatMessage {
|
||||
const proto = {
|
||||
clock, // ms?
|
||||
timestamp, //ms?
|
||||
text,
|
||||
/** Id of the message that we are replying to */
|
||||
responseTo: "",
|
||||
/** Ens name of the sender */
|
||||
ensName: "",
|
||||
/** Public Key of the community (TBC) **/
|
||||
chatId,
|
||||
/** The type of message (public/one-to-one/private-group-chat) */
|
||||
messageType: MessageType.MESSAGE_TYPE_COMMUNITY_CHAT,
|
||||
/** The type of the content of the message */
|
||||
contentType: ChatMessage_ContentType.CONTENT_TYPE_AUDIO,
|
||||
sticker: undefined,
|
||||
image: undefined,
|
||||
audio: {
|
||||
payload: audio,
|
||||
type: audioType,
|
||||
durationMs: audioDurationMs,
|
||||
},
|
||||
contentType,
|
||||
sticker,
|
||||
image,
|
||||
audio,
|
||||
community: undefined, // Used to share a community
|
||||
grant: undefined,
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ import { CreateOptions as WakuCreateOptions } from "js-waku/build/main/lib/waku"
|
|||
|
||||
import { ApplicationMetadataMessage } from "./application_metadata_message";
|
||||
import { Chat } from "./chat";
|
||||
import { ChatMessage } from "./chat_message";
|
||||
import { ChatMessage, MediaContent } from "./chat_message";
|
||||
import { Identity } from "./identity";
|
||||
import { ApplicationMetadataMessage_Type } from "./proto/status/v1/application_metadata_message";
|
||||
|
||||
|
@ -71,15 +71,16 @@ export class Messenger {
|
|||
|
||||
/**
|
||||
* Sends a message on the given chat Id.
|
||||
*
|
||||
* @param text
|
||||
* @param chatId
|
||||
*/
|
||||
public async sendMessage(text: string, chatId: string): Promise<void> {
|
||||
public async sendMessage(
|
||||
text: string,
|
||||
chatId: string,
|
||||
mediaContent?: MediaContent
|
||||
): Promise<void> {
|
||||
const chat = this.chatsById.get(chatId);
|
||||
if (!chat) throw `Failed to send message, chat not joined: ${chatId}`;
|
||||
|
||||
const chatMessage = chat.createMessage(text);
|
||||
const chatMessage = chat.createMessage(text, mediaContent);
|
||||
|
||||
const appMetadataMessage = ApplicationMetadataMessage.create(
|
||||
chatMessage.encode(),
|
||||
|
|
Loading…
Reference in New Issue