261 lines
6.6 KiB
Go
261 lines
6.6 KiB
Go
package protocol
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/status-im/status-go/protocol/common"
|
|
"github.com/status-im/status-go/protocol/communities"
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
|
"github.com/status-im/status-go/protocol/transport"
|
|
)
|
|
|
|
func (m *Messenger) GetCurrentUserStatus() (*UserStatus, error) {
|
|
|
|
status := &UserStatus{
|
|
StatusType: int(protobuf.StatusUpdate_ONLINE),
|
|
Clock: 0,
|
|
CustomText: "",
|
|
}
|
|
|
|
err := m.settings.GetCurrentStatus(status)
|
|
if err != nil {
|
|
m.logger.Debug("Error obtaining latest status", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
return status, nil
|
|
}
|
|
|
|
func (m *Messenger) sendUserStatus(ctx context.Context, status UserStatus) error {
|
|
shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !shouldBroadcastUserStatus {
|
|
m.logger.Debug("user status should not be broadcasted")
|
|
return nil
|
|
}
|
|
|
|
status.Clock = uint64(time.Now().Unix())
|
|
|
|
err = m.settings.SaveSetting("current-user-status", status)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
statusUpdate := &protobuf.StatusUpdate{
|
|
Clock: status.Clock,
|
|
StatusType: protobuf.StatusUpdate_StatusType(status.StatusType),
|
|
CustomText: status.CustomText,
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(statusUpdate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)
|
|
|
|
rawMessage := common.RawMessage{
|
|
LocalChatID: contactCodeTopic,
|
|
Payload: encodedMessage,
|
|
MessageType: protobuf.ApplicationMetadataMessage_STATUS_UPDATE,
|
|
ResendAutomatically: true,
|
|
}
|
|
|
|
_, err = m.sender.SendPublic(ctx, contactCodeTopic, rawMessage)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
joinedCommunities, err := m.communitiesManager.Joined()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, community := range joinedCommunities {
|
|
rawMessage.LocalChatID = community.StatusUpdatesChannelID()
|
|
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Messenger) sendCurrentUserStatus(ctx context.Context) {
|
|
err := m.persistence.CleanOlderStatusUpdates()
|
|
if err != nil {
|
|
m.logger.Debug("Error cleaning status updates", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
|
|
if err != nil {
|
|
m.logger.Debug("Error while getting status broadcast setting", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
if !shouldBroadcastUserStatus {
|
|
m.logger.Debug("user status should not be broadcasted")
|
|
return
|
|
}
|
|
|
|
currStatus, err := m.GetCurrentUserStatus()
|
|
if err != nil {
|
|
m.logger.Debug("Error obtaining latest status", zap.Error(err))
|
|
return
|
|
}
|
|
|
|
if err := m.sendUserStatus(ctx, *currStatus); err != nil {
|
|
m.logger.Debug("Error when sending the latest user status", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
func (m *Messenger) sendCurrentUserStatusToCommunity(ctx context.Context, community *communities.Community) error {
|
|
logger := m.logger.Named("sendCurrentUserStatusToCommunity")
|
|
|
|
shouldBroadcastUserStatus, err := m.settings.ShouldBroadcastUserStatus()
|
|
if err != nil {
|
|
logger.Debug("m.settings.ShouldBroadcastUserStatus error", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
if !shouldBroadcastUserStatus {
|
|
logger.Debug("user status should not be broadcasted")
|
|
return nil
|
|
}
|
|
|
|
status, err := m.GetCurrentUserStatus()
|
|
if err != nil {
|
|
logger.Debug("Error obtaining latest status", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
status.Clock = uint64(time.Now().Unix())
|
|
|
|
err = m.settings.SaveSetting("current-user-status", status)
|
|
if err != nil {
|
|
logger.Debug("m.settings.SaveSetting error",
|
|
zap.Any("current-user-status", status),
|
|
zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
statusUpdate := &protobuf.StatusUpdate{
|
|
Clock: status.Clock,
|
|
StatusType: protobuf.StatusUpdate_StatusType(status.StatusType),
|
|
CustomText: status.CustomText,
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(statusUpdate)
|
|
if err != nil {
|
|
logger.Debug("proto.Marshal error",
|
|
zap.Any("protobuf.StatusUpdate", statusUpdate),
|
|
zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
rawMessage := common.RawMessage{
|
|
LocalChatID: community.StatusUpdatesChannelID(),
|
|
Payload: encodedMessage,
|
|
MessageType: protobuf.ApplicationMetadataMessage_STATUS_UPDATE,
|
|
ResendAutomatically: true,
|
|
}
|
|
|
|
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
|
if err != nil {
|
|
logger.Debug("m.sender.SendPublic error", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Messenger) broadcastLatestUserStatus() {
|
|
m.logger.Debug("broadcasting user status")
|
|
ctx := context.Background()
|
|
m.sendCurrentUserStatus(ctx)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-time.After(5 * time.Minute):
|
|
m.sendCurrentUserStatus(ctx)
|
|
case <-m.quit:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (m *Messenger) SetUserStatus(ctx context.Context, newStatus int, newCustomText string) error {
|
|
if len([]rune(newCustomText)) > maxStatusMessageText {
|
|
return fmt.Errorf("custom text shouldn't be longer than %d", maxStatusMessageText)
|
|
}
|
|
|
|
if newStatus != int(protobuf.StatusUpdate_ONLINE) && newStatus != int(protobuf.StatusUpdate_DO_NOT_DISTURB) {
|
|
return fmt.Errorf("unknown status type")
|
|
}
|
|
|
|
currStatus, err := m.GetCurrentUserStatus()
|
|
if err != nil {
|
|
m.logger.Debug("Error obtaining latest status", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
if newStatus == currStatus.StatusType && newCustomText == currStatus.CustomText {
|
|
m.logger.Debug("Status type did not change")
|
|
return nil
|
|
}
|
|
|
|
currStatus.StatusType = newStatus
|
|
currStatus.CustomText = newCustomText
|
|
|
|
return m.sendUserStatus(ctx, *currStatus)
|
|
}
|
|
|
|
func (m *Messenger) HandleStatusUpdate(state *ReceivedMessageState, statusMessage protobuf.StatusUpdate) error {
|
|
if err := ValidateStatusUpdate(statusMessage); err != nil {
|
|
return err
|
|
}
|
|
|
|
if common.IsPubKeyEqual(state.CurrentMessageState.PublicKey, &m.identity.PublicKey) { // Status message is ours
|
|
currentStatus, err := m.GetCurrentUserStatus()
|
|
if err != nil {
|
|
m.logger.Debug("Error obtaining latest status", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
if currentStatus.Clock >= statusMessage.Clock {
|
|
return nil // older status message, or status does not change ignoring it
|
|
}
|
|
newStatus := ToUserStatus(statusMessage)
|
|
err = m.settings.SaveSetting("current-user-status", newStatus)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state.Response.SetCurrentStatus(newStatus)
|
|
} else {
|
|
statusUpdate := ToUserStatus(statusMessage)
|
|
statusUpdate.PublicKey = state.CurrentMessageState.Contact.ID
|
|
|
|
err := m.persistence.InsertStatusUpdate(statusUpdate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state.Response.AddStatusUpdate(statusUpdate)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Messenger) StatusUpdates() ([]UserStatus, error) {
|
|
return m.persistence.StatusUpdates()
|
|
}
|