add methods for sending messages
This commit is contained in:
parent
e2cfc3ca6e
commit
56a44eb255
|
@ -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> {
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue