github.com/status-im/status-go@v1.1.0/protocol/messenger_curated_communities.go (about) 1 package protocol 2 3 import ( 4 "context" 5 "errors" 6 "reflect" 7 "time" 8 9 "go.uber.org/zap" 10 11 "github.com/ethereum/go-ethereum/accounts/abi/bind" 12 "github.com/status-im/status-go/eth-node/types" 13 "github.com/status-im/status-go/protocol/communities" 14 ) 15 16 const ( 17 curatedCommunitiesUpdateInterval = time.Hour 18 communitiesUpdateFailureInterval = time.Minute 19 ) 20 21 // Regularly gets list of curated communities and signals them to client 22 func (m *Messenger) startCuratedCommunitiesUpdateLoop() { 23 logger := m.logger.Named("curatedCommunitiesUpdateLoop") 24 25 if m.contractMaker == nil { 26 logger.Warn("not starting curated communities loop: contract maker not initialized") 27 return 28 } 29 30 go func() { 31 // Initialize interval to 0 for immediate execution 32 var interval time.Duration = 0 33 34 cache, err := m.communitiesManager.GetCuratedCommunities() 35 if err != nil { 36 logger.Error("failed to start curated communities loop", zap.Error(err)) 37 return 38 } 39 40 for { 41 select { 42 case <-time.After(interval): 43 // Immediate execution on first run, then set to regular interval 44 interval = curatedCommunitiesUpdateInterval 45 46 curatedCommunities, err := m.getCuratedCommunitiesFromContract() 47 if err != nil { 48 interval = communitiesUpdateFailureInterval 49 logger.Error("failed to get curated communities from contract", zap.Error(err)) 50 continue 51 } 52 53 if reflect.DeepEqual(cache.ContractCommunities, curatedCommunities.ContractCommunities) && 54 reflect.DeepEqual(cache.ContractFeaturedCommunities, curatedCommunities.ContractFeaturedCommunities) { 55 // nothing changed 56 continue 57 } 58 59 err = m.communitiesManager.SetCuratedCommunities(curatedCommunities) 60 if err == nil { 61 cache = curatedCommunities 62 } else { 63 logger.Error("failed to save curated communities", zap.Error(err)) 64 } 65 66 response, err := m.fetchCuratedCommunities(curatedCommunities) 67 if err != nil { 68 interval = communitiesUpdateFailureInterval 69 logger.Error("failed to fetch curated communities", zap.Error(err)) 70 continue 71 } 72 73 m.config.messengerSignalsHandler.SendCuratedCommunitiesUpdate(response) 74 75 case <-m.quit: 76 return 77 } 78 } 79 }() 80 } 81 82 func (m *Messenger) getCuratedCommunitiesFromContract() (*communities.CuratedCommunities, error) { 83 if m.contractMaker == nil { 84 return nil, errors.New("contract maker not initialized") 85 } 86 87 testNetworksEnabled, err := m.settings.GetTestNetworksEnabled() 88 if err != nil { 89 return nil, err 90 } 91 92 chainID := uint64(10) // Optimism Mainnet 93 if testNetworksEnabled { 94 chainID = 420 // Optimism Goerli 95 } 96 97 directory, err := m.contractMaker.NewDirectory(chainID) 98 if err != nil { 99 return nil, err 100 } 101 102 callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} 103 104 contractCommunities, err := directory.GetCommunities(callOpts) 105 if err != nil { 106 return nil, err 107 } 108 var contractCommunityIDs []string 109 for _, c := range contractCommunities { 110 contractCommunityIDs = append(contractCommunityIDs, types.HexBytes(c).String()) 111 } 112 113 featuredContractCommunities, err := directory.GetFeaturedCommunities(callOpts) 114 if err != nil { 115 return nil, err 116 } 117 var contractFeaturedCommunityIDs []string 118 for _, c := range featuredContractCommunities { 119 contractFeaturedCommunityIDs = append(contractFeaturedCommunityIDs, types.HexBytes(c).String()) 120 } 121 122 return &communities.CuratedCommunities{ 123 ContractCommunities: contractCommunityIDs, 124 ContractFeaturedCommunities: contractFeaturedCommunityIDs, 125 }, nil 126 } 127 128 func (m *Messenger) fetchCuratedCommunities(curatedCommunities *communities.CuratedCommunities) (*communities.KnownCommunitiesResponse, error) { 129 response, err := m.communitiesManager.GetStoredDescriptionForCommunities(curatedCommunities.ContractCommunities) 130 if err != nil { 131 return nil, err 132 } 133 response.ContractFeaturedCommunities = curatedCommunities.ContractFeaturedCommunities 134 135 // TODO: use mechanism to obtain shard from community ID (https://github.com/status-im/status-desktop/issues/12585) 136 var unknownCommunities []communities.CommunityShard 137 for _, u := range response.UnknownCommunities { 138 unknownCommunities = append(unknownCommunities, communities.CommunityShard{ 139 CommunityID: u, 140 }) 141 } 142 143 go func() { 144 _ = m.fetchCommunities(unknownCommunities) 145 }() 146 147 return response, nil 148 } 149 150 func (m *Messenger) CuratedCommunities() (*communities.KnownCommunitiesResponse, error) { 151 curatedCommunities, err := m.communitiesManager.GetCuratedCommunities() 152 if err != nil { 153 return nil, err 154 } 155 return m.fetchCuratedCommunities(curatedCommunities) 156 }