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 { chatIdToContentTopic } from "./contentTopic";
|
||||||
import { createSymKeyFromPassword } from "./encryption";
|
import { createSymKeyFromPassword } from "./encryption";
|
||||||
|
|
||||||
|
@ -24,10 +24,16 @@ export class Chat {
|
||||||
return chatIdToContentTopic(this.id);
|
return chatIdToContentTopic(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createMessage(text: string): ChatMessage {
|
public createMessage(text: string, mediaContent?: MediaContent): ChatMessage {
|
||||||
const { timestamp, clock } = this._nextClockAndTimestamp();
|
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);
|
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";
|
} from "./proto/communities/v1/chat_message";
|
||||||
import { ImageType, MessageType } from "./proto/communities/v1/enums";
|
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 {
|
export class ChatMessage {
|
||||||
private constructor(public proto: proto.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(
|
public static createMessage(
|
||||||
clock: number,
|
clock: number,
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
text: string,
|
text: string,
|
||||||
chatId: string
|
chatId: string,
|
||||||
|
mediaContent?: MediaContent
|
||||||
): ChatMessage {
|
): ChatMessage {
|
||||||
const proto = {
|
let sticker, image, audio;
|
||||||
clock, // ms?
|
let contentType = ChatMessage_ContentType.CONTENT_TYPE_TEXT_PLAIN;
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = {
|
const proto = {
|
||||||
clock, // ms?
|
clock, // ms?
|
||||||
timestamp, //ms?
|
timestamp, //ms?
|
||||||
|
@ -70,91 +113,10 @@ export class ChatMessage {
|
||||||
/** The type of message (public/one-to-one/private-group-chat) */
|
/** The type of message (public/one-to-one/private-group-chat) */
|
||||||
messageType: MessageType.MESSAGE_TYPE_COMMUNITY_CHAT,
|
messageType: MessageType.MESSAGE_TYPE_COMMUNITY_CHAT,
|
||||||
/** The type of the content of the message */
|
/** The type of the content of the message */
|
||||||
contentType: ChatMessage_ContentType.CONTENT_TYPE_IMAGE,
|
contentType,
|
||||||
sticker: undefined,
|
sticker,
|
||||||
image: {
|
image,
|
||||||
payload: image,
|
audio,
|
||||||
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,
|
|
||||||
},
|
|
||||||
community: undefined, // Used to share a community
|
community: undefined, // Used to share a community
|
||||||
grant: undefined,
|
grant: undefined,
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { CreateOptions as WakuCreateOptions } from "js-waku/build/main/lib/waku"
|
||||||
|
|
||||||
import { ApplicationMetadataMessage } from "./application_metadata_message";
|
import { ApplicationMetadataMessage } from "./application_metadata_message";
|
||||||
import { Chat } from "./chat";
|
import { Chat } from "./chat";
|
||||||
import { ChatMessage } from "./chat_message";
|
import { ChatMessage, MediaContent } from "./chat_message";
|
||||||
import { Identity } from "./identity";
|
import { Identity } from "./identity";
|
||||||
import { ApplicationMetadataMessage_Type } from "./proto/status/v1/application_metadata_message";
|
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.
|
* 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);
|
const chat = this.chatsById.get(chatId);
|
||||||
if (!chat) throw `Failed to send message, chat not joined: ${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(
|
const appMetadataMessage = ApplicationMetadataMessage.create(
|
||||||
chatMessage.encode(),
|
chatMessage.encode(),
|
||||||
|
|
Loading…
Reference in New Issue