add methods for sending messages

This commit is contained in:
Pavel Prichodko 2022-06-09 14:44:23 +02:00 committed by Felicio Mununga
parent 025787a7ec
commit 373d87e148
No known key found for this signature in database
GPG Key ID: 0EB8D75C775AB6F1
2 changed files with 184 additions and 2 deletions

View File

@ -15,10 +15,12 @@
// todo?: ignore messages of not yet approved users // todo?: ignore messages of not yet approved users
// todo?: ignore messages with invalid signature // todo?: ignore messages with invalid signature
import { Waku } from 'js-waku' import { hexToBytes } from 'ethereum-cryptography/utils'
import { Waku, WakuMessage } from 'js-waku'
import { Account } from './account' import { Account } from './account'
import { Community } from './client/community/community' import { Community } from './client/community/community'
import * as ams from './proto/status/v1/application_metadata_message'
export interface ClientOptions { export interface ClientOptions {
publicKey: string publicKey: string
@ -67,6 +69,9 @@ class Client {
public createAccount = (): Account => { public createAccount = (): Account => {
this.account = new Account() this.account = new Account()
// TODO: joining part of creation of an account
// await this.community.requestToJoin()
return this.account return this.account
} }
@ -74,6 +79,36 @@ class Client {
// public deleteAccount = () => { // public deleteAccount = () => {
// this.account = undefined // this.account = undefined
// } // }
public sendMessage = async (
type: keyof typeof ams.ApplicationMetadataMessage_Type,
payload: Uint8Array,
contentTopic: string,
symKey: Uint8Array
) => {
if (!this.waku) {
throw new Error('Waku not started')
}
if (!this.account) {
throw new Error('Account not created')
}
const signature = await this.account.sign(payload)
const message = ams.ApplicationMetadataMessage.encode({
type: ams.ApplicationMetadataMessage_Type[type],
signature,
payload,
}).finish()
const wakuMesage = await WakuMessage.fromBytes(message, contentTopic, {
sigPrivKey: hexToBytes(this.account.privateKey),
symKey,
})
await this.waku.relay.send(wakuMesage)
}
} }
export async function createClient(options: ClientOptions): Promise<Client> { export async function createClient(options: ClientOptions): Promise<Client> {

View File

@ -1,6 +1,11 @@
import { waku_message } from 'js-waku' import { waku_message } from 'js-waku'
import { hexToBytes } from 'js-waku/build/main/lib/utils'
import difference from 'lodash/difference' import difference from 'lodash/difference'
import { CommunityRequestToJoin, MessageType } from '~/protos/communities'
import { EmojiReaction } from '~/protos/emoji-reaction'
import { ChatMessage } from '../../../protos/chat-message'
import { idToContentTopic } from '../../contentTopic' import { idToContentTopic } from '../../contentTopic'
import { createSymKeyFromPassword } from '../../encryption' import { createSymKeyFromPassword } from '../../encryption'
import { createChannelContentTopics } from './create-channel-content-topics' import { createChannelContentTopics } from './create-channel-content-topics'
@ -8,10 +13,10 @@ import { fetchChannelChatMessages } from './fetch-channel-chat-messages'
import { handleChannelChatMessage } from './handle-channel-chat-message' import { handleChannelChatMessage } from './handle-channel-chat-message'
import { handleCommunity } from './handle-community' import { handleCommunity } from './handle-community'
import type { ChatMessage } from '../../../protos/chat-message'
import type { Client } from '../../client' import type { Client } from '../../client'
import type { CommunityDescription } from '../../wire/community_description' import type { CommunityDescription } from '../../wire/community_description'
import type { Reactions } from './get-reactions' import type { Reactions } from './get-reactions'
import type { ImageMessage } from '~/src/proto/communities/v1/chat_message'
import type { Waku, WakuMessage } from 'js-waku' import type { Waku, WakuMessage } from 'js-waku'
export type CommunityMetadataType = CommunityDescription['proto'] export type CommunityMetadataType = CommunityDescription['proto']
@ -237,4 +242,146 @@ export class Community {
delete this.channelMessagesCallbacks[channelId] delete this.channelMessagesCallbacks[channelId]
} }
} }
public sendTextMessage = async (chatUuid: string, message: string) => {
const chat = this.communityMetadata.chats[chatUuid]
if (!chat) {
throw new Error('Chat not found')
}
const chatId = `${this.communityPublicKey}${chatUuid}`
const channelContentTopic = idToContentTopic(chatId)
const symKey = await createSymKeyFromPassword(chatId)
// TODO: protos does not support optional fields
// @ts-ignore
const payload = ChatMessage.encode({
clock: BigInt(Date.now()),
timestamp: BigInt(Date.now()),
text: message, // string
responseTo: '', // string
ensName: '', // string
chatId: chatId, // string
messageType: MessageType.COMMUNITY_CHAT,
contentType: ChatMessage.ContentType.TEXT_PLAIN,
// sticker: '', // StickerMessage
// image: '', // ImageMessage
// audio: '', // AudioMessage
// community: '', // Uint8Array
// grant: '', // Uint8Array
// displayName: '', // string
})
await this.client.sendMessage(
'TYPE_CHAT_MESSAGE',
payload,
channelContentTopic,
symKey
)
}
public sendImageMessage = async (chatUuid: string, image: ImageMessage) => {
const chat = this.communityMetadata.chats[chatUuid]
if (!chat) {
throw new Error('Chat not found')
}
// TODO: move to chat instance
const chatId = `${this.communityPublicKey}${chatUuid}`
const channelContentTopic = idToContentTopic(chatId)
const symKey = await createSymKeyFromPassword(chatId)
const payload = ChatMessage.encode({
clock: BigInt(Date.now()),
timestamp: BigInt(Date.now()),
responseTo: '', // string
ensName: '', // string
chatId: chatId, // string
messageType: MessageType.COMMUNITY_CHAT,
contentType: ChatMessage.ContentType.IMAGE,
image: {
type: image.type,
payload: image.payload,
},
// sticker: '', // StickerMessage
// image: '', // ImageMessage
// audio: '', // AudioMessage
// community: '', // Uint8Array
// grant: '', // Uint8Array
// displayName: '', // string
})
await this.client.sendMessage(
'TYPE_CHAT_MESSAGE',
payload,
channelContentTopic,
symKey
)
}
public sendReaction = async (
chatUuid: string,
messageId: string,
reaction: EmojiReaction.Type
) => {
const chat = this.communityMetadata.chats[chatUuid]
if (!chat) {
throw new Error('Chat not found')
}
// TODO: move to chat instance
const chatId = `${this.communityPublicKey}${chatUuid}`
const channelContentTopic = idToContentTopic(chatId)
const symKey = await createSymKeyFromPassword(chatId)
const payload = EmojiReaction.encode({
clock: BigInt(Date.now()),
chatId,
messageType: MessageType.COMMUNITY_CHAT,
messageId,
type: reaction,
// TODO: get message by id and derive state
retracted: false,
grant: new Uint8Array([]),
})
await this.client.sendMessage(
'TYPE_EMOJI_REACTION',
payload,
channelContentTopic,
symKey
)
}
public requestToJoin = async (chatUuid: string) => {
if (!this.client.account) {
throw new Error('Account not found')
}
const chat = this.communityMetadata.chats[chatUuid]
if (!chat) {
throw new Error('Chat not found')
}
// TODO: move to chat instance
const chatId = `${this.communityPublicKey}${chatUuid}`
const payload = CommunityRequestToJoin.encode({
chatId,
clock: BigInt(Date.now()),
communityId: hexToBytes(this.communityPublicKey),
ensName: '',
})
await this.client.sendMessage(
'TYPE_COMMUNITY_REQUEST_TO_JOIN',
payload,
this.communityContentTopic,
this.communityDecryptionKey
)
}
} }