Merge pull request #48 from status-im/message-types

This commit is contained in:
Franck Royer 2021-10-07 15:21:42 +11:00 committed by GitHub
commit 6ff3f99f79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 197 additions and 16 deletions

View File

@ -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);

View File

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

View File

@ -3,24 +3,103 @@ import { Reader } from "protobufjs";
import * as proto from "./proto/communities/v1/chat_message";
import {
AudioMessage,
AudioMessage_AudioType,
ChatMessage_ContentType,
ImageMessage,
StickerMessage,
} from "./proto/communities/v1/chat_message";
import { 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 {
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 {
let sticker, image, audio;
let contentType = ChatMessage_ContentType.CONTENT_TYPE_TEXT_PLAIN;
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;
}
}
const proto = {
clock, // ms?
timestamp, //ms?
@ -34,10 +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_COMMUNITY,
sticker: undefined,
image: undefined,
audio: undefined,
contentType,
sticker,
image,
audio,
community: undefined, // Used to share a community
grant: undefined,
};

View File

@ -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(),