status-go/protocol/messenger_curated_communiti...

160 lines
4.5 KiB
Go

package protocol
import (
"context"
"errors"
"reflect"
"time"
"go.uber.org/zap"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
gocommon "github.com/status-im/status-go/common"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/communities"
)
const (
curatedCommunitiesUpdateInterval = time.Hour
communitiesUpdateFailureInterval = time.Minute
)
// Regularly gets list of curated communities and signals them to client
func (m *Messenger) startCuratedCommunitiesUpdateLoop() {
logger := m.logger.Named("curatedCommunitiesUpdateLoop")
if m.contractMaker == nil {
logger.Warn("not starting curated communities loop: contract maker not initialized")
return
}
go func() {
defer gocommon.LogOnPanic()
// Initialize interval to 0 for immediate execution
var interval time.Duration = 0
cache, err := m.communitiesManager.GetCuratedCommunities()
if err != nil {
logger.Error("failed to start curated communities loop", zap.Error(err))
return
}
for {
select {
case <-time.After(interval):
// Immediate execution on first run, then set to regular interval
interval = curatedCommunitiesUpdateInterval
curatedCommunities, err := m.getCuratedCommunitiesFromContract()
if err != nil {
interval = communitiesUpdateFailureInterval
logger.Error("failed to get curated communities from contract", zap.Error(err))
continue
}
if reflect.DeepEqual(cache.ContractCommunities, curatedCommunities.ContractCommunities) &&
reflect.DeepEqual(cache.ContractFeaturedCommunities, curatedCommunities.ContractFeaturedCommunities) {
// nothing changed
continue
}
err = m.communitiesManager.SetCuratedCommunities(curatedCommunities)
if err == nil {
cache = curatedCommunities
} else {
logger.Error("failed to save curated communities", zap.Error(err))
}
response, err := m.fetchCuratedCommunities(curatedCommunities)
if err != nil {
interval = communitiesUpdateFailureInterval
logger.Error("failed to fetch curated communities", zap.Error(err))
continue
}
m.config.messengerSignalsHandler.SendCuratedCommunitiesUpdate(response)
case <-m.quit:
return
}
}
}()
}
func (m *Messenger) getCuratedCommunitiesFromContract() (*communities.CuratedCommunities, error) {
if m.contractMaker == nil {
return nil, errors.New("contract maker not initialized")
}
testNetworksEnabled, err := m.settings.GetTestNetworksEnabled()
if err != nil {
return nil, err
}
chainID := uint64(10) // Optimism Mainnet
if testNetworksEnabled {
chainID = 420 // Optimism Goerli
}
directory, err := m.contractMaker.NewDirectory(chainID)
if err != nil {
return nil, err
}
callOpts := &bind.CallOpts{Context: context.Background(), Pending: false}
contractCommunities, err := directory.GetCommunities(callOpts)
if err != nil {
return nil, err
}
var contractCommunityIDs []string
for _, c := range contractCommunities {
contractCommunityIDs = append(contractCommunityIDs, types.HexBytes(c).String())
}
featuredContractCommunities, err := directory.GetFeaturedCommunities(callOpts)
if err != nil {
return nil, err
}
var contractFeaturedCommunityIDs []string
for _, c := range featuredContractCommunities {
contractFeaturedCommunityIDs = append(contractFeaturedCommunityIDs, types.HexBytes(c).String())
}
return &communities.CuratedCommunities{
ContractCommunities: contractCommunityIDs,
ContractFeaturedCommunities: contractFeaturedCommunityIDs,
}, nil
}
func (m *Messenger) fetchCuratedCommunities(curatedCommunities *communities.CuratedCommunities) (*communities.KnownCommunitiesResponse, error) {
response, err := m.communitiesManager.GetStoredDescriptionForCommunities(curatedCommunities.ContractCommunities)
if err != nil {
return nil, err
}
response.ContractFeaturedCommunities = curatedCommunities.ContractFeaturedCommunities
// TODO: use mechanism to obtain shard from community ID (https://github.com/status-im/status-desktop/issues/12585)
var unknownCommunities []communities.CommunityShard
for _, u := range response.UnknownCommunities {
unknownCommunities = append(unknownCommunities, communities.CommunityShard{
CommunityID: u,
})
}
go func() {
defer gocommon.LogOnPanic()
_ = m.fetchCommunities(unknownCommunities)
}()
return response, nil
}
func (m *Messenger) CuratedCommunities() (*communities.KnownCommunitiesResponse, error) {
curatedCommunities, err := m.communitiesManager.GetCuratedCommunities()
if err != nil {
return nil, err
}
return m.fetchCuratedCommunities(curatedCommunities)
}