160 lines
4.5 KiB
Go
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)
|
|
}
|