github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/commands/location.go (about)

     1  package commands
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/keybase/client/go/chat/globals"
    11  	"github.com/keybase/client/go/chat/utils"
    12  	"github.com/keybase/client/go/protocol/chat1"
    13  	"github.com/keybase/client/go/protocol/gregor1"
    14  	"github.com/keybase/clockwork"
    15  )
    16  
    17  type Location struct {
    18  	*baseCommand
    19  	sync.Mutex
    20  	displayed bool
    21  	clock     clockwork.Clock
    22  }
    23  
    24  func NewLocation(g *globals.Context) *Location {
    25  	return &Location{
    26  		baseCommand: newBaseCommand(g, "location", "", "Post your current location", true),
    27  		clock:       clockwork.NewRealClock(),
    28  	}
    29  }
    30  
    31  func (h *Location) SetClock(clock clockwork.Clock) {
    32  	h.clock = clock
    33  }
    34  
    35  func (h *Location) isLiveLocation(toks []string) *gregor1.Time {
    36  	if len(toks) != 3 {
    37  		return nil
    38  	}
    39  	if toks[1] != "live" {
    40  		return nil
    41  	}
    42  	dur, err := time.ParseDuration(toks[2])
    43  	if err != nil {
    44  		return nil
    45  	}
    46  	rtime := gregor1.ToTime(h.clock.Now().Add(dur))
    47  	return &rtime
    48  }
    49  
    50  func (h *Location) isStop(toks []string) bool {
    51  	if len(toks) != 2 {
    52  		return false
    53  	}
    54  	return toks[1] == "stop"
    55  }
    56  
    57  func (h *Location) Execute(ctx context.Context, uid gregor1.UID, convID chat1.ConversationID,
    58  	tlfName, text string, replyTo *chat1.MessageID) (err error) {
    59  	defer h.Trace(ctx, &err, "Location")()
    60  	if !h.Match(ctx, text) {
    61  		return ErrInvalidCommand
    62  	}
    63  	toks := strings.Split(text, " ")
    64  	if h.isStop(toks) {
    65  		h.G().LiveLocationTracker.StopAllTracking(ctx)
    66  		err := h.getChatUI().ChatCommandStatus(ctx, convID, "All location tracking stopped",
    67  			chat1.UICommandStatusDisplayTyp_STATUS, nil)
    68  		if err != nil {
    69  			h.Debug(ctx, "Execute: error with command status: %+v", err)
    70  		}
    71  		return nil
    72  	}
    73  	var liveLocation chat1.LiveLocation
    74  	liveLocationEndTime := h.isLiveLocation(toks)
    75  	if liveLocationEndTime != nil {
    76  		liveLocation.EndTime = *liveLocationEndTime
    77  	}
    78  	if _, err := h.G().ChatHelper.SendMsgByIDNonblock(ctx, convID, tlfName,
    79  		chat1.NewMessageBodyWithText(chat1.MessageText{
    80  			Body:         text,
    81  			LiveLocation: &liveLocation,
    82  		}), chat1.MessageType_TEXT, nil, replyTo); err != nil {
    83  		return err
    84  	}
    85  	return nil
    86  }
    87  
    88  func (h *Location) Preview(ctx context.Context, uid gregor1.UID, convID chat1.ConversationID,
    89  	tlfName, text string) {
    90  	h.Lock()
    91  	defer h.Unlock()
    92  	defer h.Trace(ctx, nil, "Preview")()
    93  	if !h.Match(ctx, text) {
    94  		if h.displayed {
    95  			err := h.getChatUI().ChatCommandMarkdown(ctx, convID, nil)
    96  			if err != nil {
    97  				h.Debug(ctx, "Preview: error with markdown: %+v", err)
    98  			}
    99  			h.displayed = false
   100  		}
   101  		return
   102  	}
   103  	usage := fmt.Sprintf(locationUsage, "```", "```")
   104  	err := h.getChatUI().ChatCommandMarkdown(ctx, convID, &chat1.UICommandMarkdown{
   105  		Body:  utils.DecorateWithLinks(ctx, utils.EscapeForDecorate(ctx, usage)),
   106  		Title: &locationTitle,
   107  	})
   108  	if err != nil {
   109  		h.Debug(ctx, "Preview: error with markdown: %+v", err)
   110  	}
   111  	h.displayed = true
   112  }
   113  
   114  var locationTitle = `*/location*
   115  Post your current location and a map rendered through the use of Google Maps.
   116  `
   117  
   118  var locationUsage = `We take care to guard your privacy: https://keybase.io/docs/chat/location.
   119  Variations: %s
   120  /location
   121  /location live 1h
   122  /location stop%s
   123  `