feat_: phase-1 of use single content-topic to send messages for all community chats
This commit is contained in:
parent
768cda8de9
commit
103b415144
|
@ -19,6 +19,7 @@ type NewMessage struct {
|
||||||
TargetPeer string `json:"targetPeer"`
|
TargetPeer string `json:"targetPeer"`
|
||||||
Ephemeral bool `json:"ephemeral"`
|
Ephemeral bool `json:"ephemeral"`
|
||||||
Priority *int `json:"priority"`
|
Priority *int `json:"priority"`
|
||||||
|
ContentTopicOverride string `json:"contentTopicOverride"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is the RPC representation of a whisper message.
|
// Message is the RPC representation of a whisper message.
|
||||||
|
|
|
@ -665,6 +665,7 @@ func (s *MessageSender) dispatchCommunityChatMessage(ctx context.Context, rawMes
|
||||||
PowTarget: calculatePoW(payload),
|
PowTarget: calculatePoW(payload),
|
||||||
PowTime: whisperPoWTime,
|
PowTime: whisperPoWTime,
|
||||||
PubsubTopic: rawMessage.PubsubTopic,
|
PubsubTopic: rawMessage.PubsubTopic,
|
||||||
|
ContentTopicOverride: rawMessage.ContentTopicOverride,
|
||||||
}
|
}
|
||||||
|
|
||||||
if rawMessage.BeforeDispatch != nil {
|
if rawMessage.BeforeDispatch != nil {
|
||||||
|
@ -765,6 +766,7 @@ func (s *MessageSender) SendPublic(
|
||||||
newMessage.Ephemeral = rawMessage.Ephemeral
|
newMessage.Ephemeral = rawMessage.Ephemeral
|
||||||
newMessage.PubsubTopic = rawMessage.PubsubTopic
|
newMessage.PubsubTopic = rawMessage.PubsubTopic
|
||||||
newMessage.Priority = rawMessage.Priority
|
newMessage.Priority = rawMessage.Priority
|
||||||
|
newMessage.ContentTopicOverride = rawMessage.ContentTopicOverride
|
||||||
|
|
||||||
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||||
|
|
||||||
|
|
|
@ -84,4 +84,5 @@ type RawMessage struct {
|
||||||
ResendType ResendType
|
ResendType ResendType
|
||||||
ResendMethod ResendMethod
|
ResendMethod ResendMethod
|
||||||
Priority *MessagePriority
|
Priority *MessagePriority
|
||||||
|
ContentTopicOverride string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1567,6 +1567,12 @@ func (o *Community) setPrivateKey(pk *ecdsa.PrivateKey) {
|
||||||
o.config.PrivateKey = pk
|
o.config.PrivateKey = pk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (o *Community) UniversalChatID() string {
|
||||||
|
// Using Member updates channelID as chatID to act as a universal content-topic for all chats in the community as explained here https://forum.vac.dev/t/status-communities-review-and-proposed-usage-of-waku-content-topics/335
|
||||||
|
// This is to match filter criteria of community with the content-topic usage.
|
||||||
|
// This specific topic is chosen as existing users before the change are already subscribed to this and will not get affected by it.
|
||||||
|
return o.MemberUpdateChannelID()
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Community) SetResendAccountsClock(clock uint64) {
|
func (o *Community) SetResendAccountsClock(clock uint64) {
|
||||||
o.config.CommunityDescription.ResendAccountsClock = clock
|
o.config.CommunityDescription.ResendAccountsClock = clock
|
||||||
|
|
|
@ -4194,6 +4194,22 @@ func (m *Manager) GetOwnedCommunitiesChatIDs() (map[string]bool, error) {
|
||||||
return chatIDs, nil
|
return chatIDs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetOwnedCommunitiesUniversalChatIDs() (map[string]bool, error) {
|
||||||
|
ownedCommunities, err := m.Controlled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
chatIDs := make(map[string]bool)
|
||||||
|
for _, c := range ownedCommunities {
|
||||||
|
if c.Joined() {
|
||||||
|
chatIDs[c.UniversalChatID()] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chatIDs, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) StoreWakuMessage(message *types.Message) error {
|
func (m *Manager) StoreWakuMessage(message *types.Message) error {
|
||||||
return m.persistence.SaveWakuMessage(message)
|
return m.persistence.SaveWakuMessage(message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -354,6 +354,10 @@ func (m *ArchiveManager) StartHistoryArchiveTasksInterval(community *Community,
|
||||||
m.logger.Error("failed to get community chat topics ", zap.Error(err))
|
m.logger.Error("failed to get community chat topics ", zap.Error(err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// adding the content-topic used for all community chats as all chat messages will be published on this topic.
|
||||||
|
// since member updates would not be too frequent i.e only addition/deletion would add a new message,
|
||||||
|
// this shouldn't cause too much increase in size of archive generated.
|
||||||
|
topics = append(topics, m.transport.FilterByChatID(community.UniversalChatID()).ContentTopic)
|
||||||
|
|
||||||
ts := time.Now().Unix()
|
ts := time.Now().Unix()
|
||||||
to := time.Unix(ts, 0)
|
to := time.Unix(ts, 0)
|
||||||
|
|
|
@ -2152,7 +2152,8 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestImportDecryptedArchiveMe
|
||||||
startDate := messageDate.Add(-time.Minute)
|
startDate := messageDate.Add(-time.Minute)
|
||||||
endDate := messageDate.Add(time.Minute)
|
endDate := messageDate.Add(time.Minute)
|
||||||
topic := types.BytesToTopic(transport.ToTopic(chat.ID))
|
topic := types.BytesToTopic(transport.ToTopic(chat.ID))
|
||||||
topics := []types.TopicType{topic}
|
communityCommonTopic := types.BytesToTopic(transport.ToTopic(community.UniversalChatID()))
|
||||||
|
topics := []types.TopicType{topic, communityCommonTopic}
|
||||||
|
|
||||||
torrentConfig := params.TorrentConfig{
|
torrentConfig := params.TorrentConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
|
|
@ -2268,7 +2268,8 @@ func (m *Messenger) dispatchMessage(ctx context.Context, rawMessage common.RawMe
|
||||||
)
|
)
|
||||||
return rawMessage, fmt.Errorf("can't post message type '%d' on chat '%s'", rawMessage.MessageType, chat.ID)
|
return rawMessage, fmt.Errorf("can't post message type '%d' on chat '%s'", rawMessage.MessageType, chat.ID)
|
||||||
}
|
}
|
||||||
|
//setting content-topic over-ride for community messages to use memberUpdatesChannelID
|
||||||
|
rawMessage.ContentTopicOverride = community.UniversalChatID()
|
||||||
logger.Debug("sending community chat message", zap.String("chatName", chat.Name))
|
logger.Debug("sending community chat message", zap.String("chatName", chat.Name))
|
||||||
isCommunityEncrypted, err := m.communitiesManager.IsEncrypted(chat.CommunityID)
|
isCommunityEncrypted, err := m.communitiesManager.IsEncrypted(chat.CommunityID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3589,6 +3590,15 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Info("failed to retrieve admin communities", zap.Error(err))
|
logger.Info("failed to retrieve admin communities", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
//fetch universal chatIDs as well.
|
||||||
|
controlledCommunitiesUniversalChatIDs, err := m.communitiesManager.GetOwnedCommunitiesUniversalChatIDs()
|
||||||
|
if err != nil {
|
||||||
|
logger.Info("failed to retrieve admin communities", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
for chatID, flag := range controlledCommunitiesUniversalChatIDs {
|
||||||
|
controlledCommunitiesChatIDs[chatID] = flag
|
||||||
|
}
|
||||||
|
|
||||||
iterator := m.retrievedMessagesIteratorFactory(chatWithMessages)
|
iterator := m.retrievedMessagesIteratorFactory(chatWithMessages)
|
||||||
for iterator.HasNext() {
|
for iterator.HasNext() {
|
||||||
|
|
|
@ -2714,6 +2714,13 @@ func (m *Messenger) UpdateCommunityFilters(community *communities.Community) err
|
||||||
|
|
||||||
publicFiltersToInit = append(publicFiltersToInit, defaultFilters...)
|
publicFiltersToInit = append(publicFiltersToInit, defaultFilters...)
|
||||||
|
|
||||||
|
for _, filter := range defaultFilters {
|
||||||
|
_, err := m.transport.RemoveFilterByChatID(filter.ChatID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for chatID := range community.Chats() {
|
for chatID := range community.Chats() {
|
||||||
communityChatID := community.IDString() + chatID
|
communityChatID := community.IDString() + chatID
|
||||||
_, err := m.transport.RemoveFilterByChatID(communityChatID)
|
_, err := m.transport.RemoveFilterByChatID(communityChatID)
|
||||||
|
@ -3949,6 +3956,10 @@ func (m *Messenger) InitHistoryArchiveTasks(communities []*communities.Community
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
topics = append(topics, filter.ContentTopic)
|
topics = append(topics, filter.ContentTopic)
|
||||||
}
|
}
|
||||||
|
// adding the content-topic used for member updates.
|
||||||
|
// since member updates would not be too frequent i.e only addition/deletion would add a new message,
|
||||||
|
// this shouldn't cause too much increase in size of archive generated.
|
||||||
|
filters = append(filters, m.transport.FilterByChatID(c.UniversalChatID()))
|
||||||
|
|
||||||
// First we need to know the timestamp of the latest waku message
|
// First we need to know the timestamp of the latest waku message
|
||||||
// we've received for this community, so we can request messages we've
|
// we've received for this community, so we can request messages we've
|
||||||
|
|
|
@ -99,7 +99,7 @@ func (f *FiltersManager) Init(
|
||||||
|
|
||||||
// Add public, one-to-one and negotiated filters.
|
// Add public, one-to-one and negotiated filters.
|
||||||
for _, fi := range filtersToInit {
|
for _, fi := range filtersToInit {
|
||||||
_, err := f.LoadPublic(fi.ChatID, fi.PubsubTopic)
|
_, err := f.LoadPublic(fi.ChatID, fi.PubsubTopic, fi.ContentTopicOverrideID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -125,13 +125,14 @@ func (f *FiltersManager) Init(
|
||||||
type FiltersToInitialize struct {
|
type FiltersToInitialize struct {
|
||||||
ChatID string
|
ChatID string
|
||||||
PubsubTopic string
|
PubsubTopic string
|
||||||
|
ContentTopicOverrideID string //litte hacky but this is used to override content-topic in filtersManager.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FiltersManager) InitPublicFilters(publicFiltersToInit []FiltersToInitialize) ([]*Filter, error) {
|
func (f *FiltersManager) InitPublicFilters(publicFiltersToInit []FiltersToInitialize) ([]*Filter, error) {
|
||||||
var filters []*Filter
|
var filters []*Filter
|
||||||
// Add public, one-to-one and negotiated filters.
|
// Add public, one-to-one and negotiated filters.
|
||||||
for _, pf := range publicFiltersToInit {
|
for _, pf := range publicFiltersToInit {
|
||||||
f, err := f.LoadPublic(pf.ChatID, pf.PubsubTopic)
|
f, err := f.LoadPublic(pf.ChatID, pf.PubsubTopic, pf.ContentTopicOverrideID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -455,7 +456,7 @@ func (f *FiltersManager) LoadNegotiated(secret types.NegotiatedSecret) (*Filter,
|
||||||
}
|
}
|
||||||
|
|
||||||
keyString := hex.EncodeToString(secret.Key)
|
keyString := hex.EncodeToString(secret.Key)
|
||||||
filter, err := f.addSymmetric(keyString, "")
|
filter, err := f.addSymmetric(keyString, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register negotiated topic", zap.Error(err))
|
f.logger.Debug("could not register negotiated topic", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -534,11 +535,16 @@ func (f *FiltersManager) PersonalTopicFilter() *Filter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadPublic adds a filter for a public chat.
|
// LoadPublic adds a filter for a public chat.
|
||||||
func (f *FiltersManager) LoadPublic(chatID string, pubsubTopic string) (*Filter, error) {
|
func (f *FiltersManager) LoadPublic(chatID string, pubsubTopic string, contentTopicID string) (*Filter, error) {
|
||||||
f.mutex.Lock()
|
f.mutex.Lock()
|
||||||
defer f.mutex.Unlock()
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
if chat, ok := f.filters[chatID]; ok {
|
chatIDToLoad := chatID
|
||||||
|
if contentTopicID != "" {
|
||||||
|
chatIDToLoad = contentTopicID
|
||||||
|
}
|
||||||
|
|
||||||
|
if chat, ok := f.filters[chatIDToLoad]; ok {
|
||||||
if chat.PubsubTopic != pubsubTopic {
|
if chat.PubsubTopic != pubsubTopic {
|
||||||
f.logger.Debug("updating pubsub topic for filter",
|
f.logger.Debug("updating pubsub topic for filter",
|
||||||
zap.String("chatID", chatID),
|
zap.String("chatID", chatID),
|
||||||
|
@ -547,13 +553,13 @@ func (f *FiltersManager) LoadPublic(chatID string, pubsubTopic string) (*Filter,
|
||||||
zap.String("newTopic", pubsubTopic),
|
zap.String("newTopic", pubsubTopic),
|
||||||
)
|
)
|
||||||
chat.PubsubTopic = pubsubTopic
|
chat.PubsubTopic = pubsubTopic
|
||||||
f.filters[chatID] = chat
|
f.filters[chatIDToLoad] = chat //TODO: Do we need to update watchers as well on modification?
|
||||||
}
|
}
|
||||||
|
|
||||||
return chat, nil
|
return chat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
filterAndTopic, err := f.addSymmetric(chatID, pubsubTopic)
|
filterAndTopic, err := f.addSymmetric(chatID, pubsubTopic, contentTopicID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register public chat topic", zap.String("chatID", chatID), zap.Error(err))
|
f.logger.Debug("could not register public chat topic", zap.String("chatID", chatID), zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -592,7 +598,7 @@ func (f *FiltersManager) LoadContactCode(pubKey *ecdsa.PublicKey) (*Filter, erro
|
||||||
return f.filters[chatID], nil
|
return f.filters[chatID], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
contactCodeFilter, err := f.addSymmetric(chatID, "")
|
contactCodeFilter, err := f.addSymmetric(chatID, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register contact code topic", zap.String("chatID", chatID), zap.Error(err))
|
f.logger.Debug("could not register contact code topic", zap.String("chatID", chatID), zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -615,7 +621,7 @@ func (f *FiltersManager) LoadContactCode(pubKey *ecdsa.PublicKey) (*Filter, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// addSymmetric adds a symmetric key filter
|
// addSymmetric adds a symmetric key filter
|
||||||
func (f *FiltersManager) addSymmetric(chatID string, pubsubTopic string) (*RawFilter, error) {
|
func (f *FiltersManager) addSymmetric(chatID string, pubsubTopic string, contentTopicID string) (*RawFilter, error) {
|
||||||
var symKeyID string
|
var symKeyID string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -644,6 +650,12 @@ func (f *FiltersManager) addSymmetric(chatID string, pubsubTopic string) (*RawFi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if contentTopicID != "" {
|
||||||
|
//add receive filter for the single default contentTopic for all community chats
|
||||||
|
topic = ToTopic(contentTopicID)
|
||||||
|
topics = append(topics, topic)
|
||||||
|
}
|
||||||
|
|
||||||
id, err := f.service.Subscribe(&types.SubscriptionOptions{
|
id, err := f.service.Subscribe(&types.SubscriptionOptions{
|
||||||
SymKeyID: symKeyID,
|
SymKeyID: symKeyID,
|
||||||
PoW: minPow,
|
PoW: minPow,
|
||||||
|
|
|
@ -191,7 +191,7 @@ func (t *Transport) ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*Fil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) JoinPublic(chatID string) (*Filter, error) {
|
func (t *Transport) JoinPublic(chatID string) (*Filter, error) {
|
||||||
return t.filters.LoadPublic(chatID, "")
|
return t.filters.LoadPublic(chatID, "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) LeavePublic(chatID string) error {
|
func (t *Transport) LeavePublic(chatID string) error {
|
||||||
|
@ -223,6 +223,8 @@ func (t *Transport) GetStats() types.StatsSummary {
|
||||||
return t.waku.GetStats()
|
return t.waku.GetStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// With change in filter used for communities, messages are indexed here with common filter and not their own chatID filter.
|
||||||
|
// The caller should not use chatID from the filter to determine chatID of the message, rather should dervice it from messaage itself.
|
||||||
func (t *Transport) RetrieveRawAll() (map[Filter][]*types.Message, error) {
|
func (t *Transport) RetrieveRawAll() (map[Filter][]*types.Message, error) {
|
||||||
result := make(map[Filter][]*types.Message)
|
result := make(map[Filter][]*types.Message)
|
||||||
logger := t.logger.With(zap.String("site", "retrieveRawAll"))
|
logger := t.logger.With(zap.String("site", "retrieveRawAll"))
|
||||||
|
@ -276,16 +278,16 @@ func (t *Transport) RetrieveRawAll() (map[Filter][]*types.Message, error) {
|
||||||
// SendPublic sends a new message using the Whisper service.
|
// SendPublic sends a new message using the Whisper service.
|
||||||
// For public filters, chat name is used as an ID as well as
|
// For public filters, chat name is used as an ID as well as
|
||||||
// a topic.
|
// a topic.
|
||||||
|
// In case of communities a single topic is used to send all messages.
|
||||||
func (t *Transport) SendPublic(ctx context.Context, newMessage *types.NewMessage, chatName string) ([]byte, error) {
|
func (t *Transport) SendPublic(ctx context.Context, newMessage *types.NewMessage, chatName string) ([]byte, error) {
|
||||||
if err := t.addSig(newMessage); err != nil {
|
if err := t.addSig(newMessage); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
//passing content-topic override, it will be used if set. otherwise chatName will be used to load filter.
|
||||||
filter, err := t.filters.LoadPublic(chatName, newMessage.PubsubTopic)
|
filter, err := t.filters.LoadPublic(chatName, newMessage.PubsubTopic, newMessage.ContentTopicOverride)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage.SymKeyID = filter.SymKeyID
|
newMessage.SymKeyID = filter.SymKeyID
|
||||||
newMessage.Topic = filter.ContentTopic
|
newMessage.Topic = filter.ContentTopic
|
||||||
newMessage.PubsubTopic = filter.PubsubTopic
|
newMessage.PubsubTopic = filter.PubsubTopic
|
||||||
|
@ -362,7 +364,8 @@ func (t *Transport) SendCommunityMessage(ctx context.Context, newMessage *types.
|
||||||
}
|
}
|
||||||
|
|
||||||
// We load the filter to make sure we can post on it
|
// We load the filter to make sure we can post on it
|
||||||
filter, err := t.filters.LoadPublic(PubkeyToHex(publicKey)[2:], newMessage.PubsubTopic)
|
//passing content-topic override, it will be used if set. otherwise chatName will be used to load filter.
|
||||||
|
filter, err := t.filters.LoadPublic(PubkeyToHex(publicKey)[2:], newMessage.PubsubTopic, newMessage.ContentTopicOverride)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue