Skip to content

Commit

Permalink
team mentions basic (#17270)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmaxim committed May 1, 2019
1 parent 0f9cb8e commit a1d629d
Show file tree
Hide file tree
Showing 38 changed files with 888 additions and 134 deletions.
24 changes: 12 additions & 12 deletions go/chat/boxer.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ func (b *Boxer) unboxV1(ctx context.Context, boxed chat1.MessageBoxed,
}

// Get at mention usernames
atMentions, atMentionUsernames, chanMention, channelNameMentions :=
atMentions, atMentionUsernames, teamRes, chanMention, channelNameMentions :=
b.getAtMentionInfo(ctx, clientHeader.Conv.Tlfid, clientHeader.Conv.TopicType, membersType, body)

ierr = b.compareHeadersMBV1(ctx, boxed.ClientHeader, clientHeader)
Expand All @@ -665,6 +665,7 @@ func (b *Boxer) unboxV1(ctx context.Context, boxed chat1.MessageBoxed,
AtMentionUsernames: atMentionUsernames,
ChannelMention: chanMention,
ChannelNameMentions: channelNameMentions,
TeamMentions: teamRes,
}, nil
}

Expand Down Expand Up @@ -872,7 +873,7 @@ func (b *Boxer) unboxV2orV3orV4(ctx context.Context, boxed chat1.MessageBoxed,
ctx, clientHeader.Sender, clientHeader.SenderDevice)

// Get at mention usernames
atMentions, atMentionUsernames, chanMention, channelNameMentions :=
atMentions, atMentionUsernames, teamRes, chanMention, channelNameMentions :=
b.getAtMentionInfo(ctx, clientHeader.Conv.Tlfid, clientHeader.Conv.TopicType, membersType, body)

clientHeader.HasPairwiseMacs = len(boxed.ClientHeader.PairwiseMacs) > 0
Expand All @@ -894,6 +895,7 @@ func (b *Boxer) unboxV2orV3orV4(ctx context.Context, boxed chat1.MessageBoxed,
AtMentionUsernames: atMentionUsernames,
ChannelMention: chanMention,
ChannelNameMentions: channelNameMentions,
TeamMentions: teamRes,
}, nil
}

Expand Down Expand Up @@ -1159,40 +1161,38 @@ func (b *Boxer) getSenderInfoLocal(ctx context.Context, uid1 gregor1.UID, device
}

func (b *Boxer) getAtMentionInfo(ctx context.Context, tlfID chat1.TLFID, topicType chat1.TopicType,
membersType chat1.ConversationMembersType, body chat1.MessageBody) (atMentions []gregor1.UID, atMentionUsernames []string, chanMention chat1.ChannelMention, channelNameMentions []chat1.ChannelNameMention) {
membersType chat1.ConversationMembersType, body chat1.MessageBody) (atMentions []gregor1.UID, atMentionUsernames []string, teamRes []chat1.MaybeTeamMention, chanMention chat1.ChannelMention, channelNameMentions []chat1.ChannelNameMention) {
if topicType != chat1.TopicType_CHAT {
// only care about chat conversations for these mentions
return atMentions, atMentionUsernames, chanMention, channelNameMentions
return atMentions, atMentionUsernames, teamRes, chanMention, channelNameMentions
}
chanMention = chat1.ChannelMention_NONE
typ, err := body.MessageType()
if err != nil {
return nil, nil, chanMention, nil
return nil, nil, nil, chanMention, nil
}
uid := gregor1.UID(b.G().GetEnv().GetUID().ToBytes())
tcs := b.G().TeamChannelSource
switch typ {
case chat1.MessageType_TEXT:
atMentions, chanMention = utils.GetTextAtMentionedUIDs(ctx, body.Text(), b.G().GetUPAKLoader(),
atMentions, teamRes, chanMention = utils.GetTextAtMentionedItems(ctx, b.G(), body.Text(),
&b.DebugLabeler)
if membersType == chat1.ConversationMembersType_TEAM {
channelNameMentions = utils.ParseChannelNameMentions(ctx, body.Text().Body, uid, tlfID, tcs)
}
case chat1.MessageType_FLIP:
if topicType == chat1.TopicType_CHAT {
atMentions, chanMention = utils.ParseAtMentionedUIDs(ctx, body.Flip().Text,
b.G().GetUPAKLoader(), &b.DebugLabeler)
atMentions, teamRes, chanMention = utils.ParseAtMentionedItems(ctx, b.G(), body.Flip().Text)
}
case chat1.MessageType_EDIT:
atMentions, chanMention = utils.ParseAtMentionedUIDs(ctx, body.Edit().Body, b.G().GetUPAKLoader(),
&b.DebugLabeler)
atMentions, teamRes, chanMention = utils.ParseAtMentionedItems(ctx, b.G(), body.Edit().Body)
if membersType == chat1.ConversationMembersType_TEAM {
channelNameMentions = utils.ParseChannelNameMentions(ctx, body.Edit().Body, uid, tlfID, tcs)
}
case chat1.MessageType_SYSTEM:
atMentions, chanMention = utils.SystemMessageMentions(ctx, body.System(), b.G().GetUPAKLoader())
default:
return nil, nil, chanMention, nil
return nil, nil, nil, chanMention, nil
}

usernames := make(map[string]bool)
Expand All @@ -1206,7 +1206,7 @@ func (b *Boxer) getAtMentionInfo(ctx context.Context, tlfID chat1.TLFID, topicTy
for u := range usernames {
atMentionUsernames = append(atMentionUsernames, u)
}
return atMentions, atMentionUsernames, chanMention, channelNameMentions
return atMentions, atMentionUsernames, teamRes, chanMention, channelNameMentions
}

func (b *Boxer) UnboxMessages(ctx context.Context, boxed []chat1.MessageBoxed, conv types.UnboxConversationInfo) (unboxed []chat1.MessageUnboxed, err error) {
Expand Down
1 change: 1 addition & 0 deletions go/chat/globals/globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type ChatContext struct {
Unfurler types.Unfurler // unfurl messages with URLs
CommandsSource types.ConversationCommandsSource // source for / commands for conversations
CoinFlipManager types.CoinFlipManager // manage /flip games
TeamMentionLoader types.TeamMentionLoader // load potential team mentions
}

type Context struct {
Expand Down
12 changes: 6 additions & 6 deletions go/chat/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -642,20 +642,20 @@ func (s *BlockingSender) Prepare(ctx context.Context, plaintext chat1.MessagePla
if err = checkHeaderBodyTypeMatch(); err != nil {
return res, err
}
atMentions, chanMention = utils.GetTextAtMentionedUIDs(ctx,
plaintext.MessageBody.Text(), s.G().GetUPAKLoader(), &s.DebugLabeler)
atMentions, _, chanMention = utils.GetTextAtMentionedItems(ctx, s.G(),
plaintext.MessageBody.Text(), &s.DebugLabeler)
case chat1.MessageType_FLIP:
if err = checkHeaderBodyTypeMatch(); err != nil {
return res, err
}
atMentions, chanMention = utils.ParseAtMentionedUIDs(ctx,
plaintext.MessageBody.Flip().Text, s.G().GetUPAKLoader(), &s.DebugLabeler)
atMentions, _, chanMention = utils.ParseAtMentionedItems(ctx, s.G(),
plaintext.MessageBody.Flip().Text)
case chat1.MessageType_EDIT:
if err = checkHeaderBodyTypeMatch(); err != nil {
return res, err
}
atMentions, chanMention = utils.ParseAtMentionedUIDs(ctx,
plaintext.MessageBody.Edit().Body, s.G().GetUPAKLoader(), &s.DebugLabeler)
atMentions, _, chanMention = utils.ParseAtMentionedItems(ctx, s.G(),
plaintext.MessageBody.Edit().Body)
case chat1.MessageType_SYSTEM:
if err = checkHeaderBodyTypeMatch(); err != nil {
return res, err
Expand Down
1 change: 1 addition & 0 deletions go/chat/sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ func setupTest(t *testing.T, numUsers int) (context.Context, *kbtest.ChatMockWor
g.Unfurler = types.DummyUnfurler{}
g.StellarLoader = types.DummyStellarLoader{}
g.StellarSender = types.DummyStellarSender{}
g.TeamMentionLoader = types.DummyTeamMentionLoader{}
g.CommandsSource = commands.NewSource(g)
g.CoinFlipManager = NewFlipManager(g, getRI)
g.CoinFlipManager.Start(context.TODO(), uid)
Expand Down
1 change: 1 addition & 0 deletions go/chat/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ func (c *chatTestContext) as(t *testing.T, user *kbtest.FakeUser) *chatTestUserC
g.Unfurler = types.DummyUnfurler{}
g.StellarLoader = types.DummyStellarLoader{}
g.StellarSender = types.DummyStellarSender{}
g.TeamMentionLoader = types.DummyTeamMentionLoader{}
g.CoinFlipManager = NewFlipManager(g, func() chat1.RemoteInterface { return ri })
g.CoinFlipManager.Start(context.TODO(), uid)

Expand Down
142 changes: 142 additions & 0 deletions go/chat/teammentionloader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package chat

import (
"context"
"errors"
"sync"

"github.com/keybase/client/go/chat/globals"
"github.com/keybase/client/go/chat/utils"
"github.com/keybase/client/go/libkb"
"github.com/keybase/client/go/protocol/chat1"
"github.com/keybase/client/go/protocol/gregor1"
)

type teamMentionJob struct {
uid gregor1.UID
teamName string
}

type TeamMentionLoader struct {
sync.Mutex
globals.Contextified
utils.DebugLabeler

started bool
jobCh chan teamMentionJob
shutdownCh chan chan struct{}
}

func NewTeamMentionLoader(g *globals.Context) *TeamMentionLoader {
return &TeamMentionLoader{
Contextified: globals.NewContextified(g),
DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "TeamMentionLoader", false),
jobCh: make(chan teamMentionJob, 100),
shutdownCh: make(chan chan struct{}, 5),
}
}

func (l *TeamMentionLoader) Start(ctx context.Context, uid gregor1.UID) {
defer l.Trace(ctx, func() error { return nil }, "Start")()
l.Lock()
defer l.Unlock()
if l.started {
return
}
l.started = true
go l.loadLoop()
}

func (l *TeamMentionLoader) Stop(ctx context.Context) chan struct{} {
defer l.Trace(ctx, func() error { return nil }, "Stop")()
l.Lock()
defer l.Unlock()
ch := make(chan struct{})
if l.started {
l.shutdownCh <- ch
l.started = false
return ch
}
close(ch)
return ch
}

func (l *TeamMentionLoader) LoadTeamMention(ctx context.Context, uid gregor1.UID, teamName string) (err error) {
defer l.Trace(ctx, func() error { return err }, "LoadTeamMention")()
select {
case l.jobCh <- teamMentionJob{uid: uid, teamName: teamName}:
default:
l.Debug(ctx, "Load: failed to queue job, full")
return errors.New("queue full")
}
return nil
}

type mentionAPIResp struct {
Status libkb.AppStatus `json:"status"`
Name string
InTeam bool `json:"in_team"`
Open bool
Description string
PublicAdmins []string `json:"public_admins"`
NumMembers int `json:"num_members"`
}

func (r *mentionAPIResp) GetAppStatus() *libkb.AppStatus {
return &r.Status
}

func (l *TeamMentionLoader) getChatUI(ctx context.Context) (libkb.ChatUI, error) {
ui, err := l.G().UIRouter.GetChatUI()
if err != nil || ui == nil {
l.Debug(ctx, "getChatUI: no chat UI found: err: %s", err)
if err == nil {
err = errors.New("no chat UI found")
}
return nil, err
}
return ui, nil
}

func (l *TeamMentionLoader) loadMention(ctx context.Context, uid gregor1.UID, teamName string) (err error) {
defer l.Trace(ctx, func() error { return err }, "loadTeamMention: name: %s", teamName)()
ui, err := l.getChatUI(ctx)
if err != nil {
return err
}
arg := libkb.APIArg{
Endpoint: "team/mentiondesc",
SessionType: libkb.APISessionTypeREQUIRED,
Args: libkb.HTTPArgs{"name": libkb.S{Val: teamName}},
}
var resp mentionAPIResp
if err = l.G().API.GetDecode(libkb.NewMetaContext(ctx, l.G().ExternalG()), arg, &resp); err != nil {
return err
}
var info chat1.UITeamMention
info.Open = resp.Open
info.InTeam = resp.InTeam
if len(resp.Description) > 0 {
info.Description = new(string)
*info.Description = resp.Description
}
if resp.NumMembers > 0 {
info.NumMembers = new(int)
*info.NumMembers = resp.NumMembers
}
info.PublicAdmins = resp.PublicAdmins
return ui.ChatTeamMentionUpdate(ctx, teamName, info)
}

func (l *TeamMentionLoader) loadLoop() {
ctx := context.Background()
for {
select {
case job := <-l.jobCh:
l.loadMention(ctx, job.uid, job.teamName)
case ch := <-l.shutdownCh:
close(ch)
return
}
}
}
5 changes: 5 additions & 0 deletions go/chat/types/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,11 @@ type CoinFlipManager interface {
IsFlipConversationCreated(ctx context.Context, outboxID chat1.OutboxID) (chat1.ConversationID, FlipSendStatus)
}

type TeamMentionLoader interface {
Resumable
LoadTeamMention(ctx context.Context, uid gregor1.UID, teamName string) error
}

type InternalError interface {
// verbose error info for debugging but not user display
InternalError() string
Expand Down
13 changes: 13 additions & 0 deletions go/chat/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,16 @@ func (d DummyCoinFlipManager) HasActiveGames(ctx context.Context) bool {
func (d DummyCoinFlipManager) IsFlipConversationCreated(ctx context.Context, outboxID chat1.OutboxID) (chat1.ConversationID, FlipSendStatus) {
return nil, FlipSendStatusError
}

type DummyTeamMentionLoader struct{}

func (d DummyTeamMentionLoader) Start(ctx context.Context, uid gregor1.UID) {}
func (d DummyTeamMentionLoader) Stop(ctx context.Context) chan struct{} {
ch := make(chan struct{})
close(ch)
return ch
}

func (d DummyTeamMentionLoader) LoadTeamMention(ctx context.Context, uid gregor1.UID, teamName string) error {
return nil
}
4 changes: 4 additions & 0 deletions go/chat/utils/dummy_chat_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ func (r DummyChatUI) ChatCommandMarkdown(ctx context.Context, arg chat1.ChatComm
return nil
}

func (r DummyChatUI) ChatTeamMentionUpdate(ctx context.Context, arg chat1.ChatTeamMentionUpdateArg) error {
return nil
}

type DummyChatNotifications struct{}

func (d DummyChatNotifications) NewChatActivity(ctx context.Context, arg chat1.NewChatActivityArg) error {
Expand Down
Loading

0 comments on commit a1d629d

Please sign in to comment.