<commit_before>package chat

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"sync"

	"golang.org/x/sync/errgroup"

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

type DummyIdentifyNotifier struct{}

func (d DummyIdentifyNotifier) Reset()             {}
func (d DummyIdentifyNotifier) ResetOnGUIConnect() {}
func (d DummyIdentifyNotifier) Send(ctx context.Context, update keybase1.CanonicalTLFNameAndIDWithBreaks) {
}

type SimpleIdentifyNotifier struct {
	globals.Contextified
	utils.DebugLabeler
	storage *storage.Storage
}

func NewSimpleIdentifyNotifier(g *globals.Context) *SimpleIdentifyNotifier {
	return &SimpleIdentifyNotifier{
		Contextified: globals.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "SimpleIdentifyNotifier", false),
		storage:      storage.New(g, g.ConvSource),
	}
}

func (d *SimpleIdentifyNotifier) Reset()             {}
func (d *SimpleIdentifyNotifier) ResetOnGUIConnect() {}

func (d *SimpleIdentifyNotifier) Send(ctx context.Context, update keybase1.CanonicalTLFNameAndIDWithBreaks) {
	// Send to storage as well (charge forward on error)
	if err := d.storage.UpdateTLFIdentifyBreak(ctx, update.TlfID.ToBytes(), update.Breaks.Breaks); err != nil {
		d.Debug(ctx, "failed to update storage with TLF identify info: %s", err.Error())
	}
	d.G().NotifyRouter.HandleChatIdentifyUpdate(ctx, update)
}

type CachingIdentifyNotifier struct {
	globals.Contextified
	utils.DebugLabeler

	sync.RWMutex
	storage    *storage.Storage
	identCache map[string]keybase1.CanonicalTLFNameAndIDWithBreaks
}

func NewCachingIdentifyNotifier(g *globals.Context) *CachingIdentifyNotifier {
	return &CachingIdentifyNotifier{
		Contextified: globals.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "CachingIdentifyNotifier", false),
		identCache:   make(map[string]keybase1.CanonicalTLFNameAndIDWithBreaks),
		storage:      storage.New(g, g.ConvSource),
	}
}

func (i *CachingIdentifyNotifier) ResetOnGUIConnect() {
	i.G().ConnectionManager.RegisterLabelCallback(func(typ keybase1.ClientType) {
		switch typ {
		case keybase1.ClientType_GUI_HELPER, keybase1.ClientType_GUI_MAIN:
			i.Reset()
		}
	})
}

func (i *CachingIdentifyNotifier) Reset() {
	i.identCache = make(map[string]keybase1.CanonicalTLFNameAndIDWithBreaks)
}

func (i *CachingIdentifyNotifier) Send(ctx context.Context, update keybase1.CanonicalTLFNameAndIDWithBreaks) {

	// Send to storage as well (charge forward on error)
	if err := i.storage.UpdateTLFIdentifyBreak(ctx, update.TlfID.ToBytes(), update.Breaks.Breaks); err != nil {
		i.Debug(ctx, "failed to update storage with TLF identify info: %s", err.Error())
	}

	// Send notification to GUI about identify status
	tlfName := update.CanonicalName.String()
	i.RLock()
	stored, ok := i.identCache[tlfName]
	i.RUnlock()
	if ok {
		// We have the exact update stored, don't send it again
		if stored.Eq(update) {
			i.Debug(ctx, "hit cache, not sending notify: %s", tlfName)
			return
		}
	}
	i.Lock()
	i.identCache[tlfName] = update
	i.Unlock()

	i.Debug(ctx, "cache miss, sending notify: %s dat: %v", tlfName, update)
	i.G().NotifyRouter.HandleChatIdentifyUpdate(ctx, update)
}

type IdentifyChangedHandler struct {
	globals.Contextified
	utils.DebugLabeler
}

func NewIdentifyChangedHandler(g *globals.Context) *IdentifyChangedHandler {
	return &IdentifyChangedHandler{
		Contextified: globals.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "IdentifyChangedHandler", false),
	}
}

var errNoConvForUser = errors.New("user not found in inbox")

func (h *IdentifyChangedHandler) getUsername(ctx context.Context, uid keybase1.UID) (string, error) {
	u, err := h.G().GetUPAKLoader().LookupUsername(ctx, uid)
	return u.String(), err
}

func (h *IdentifyChangedHandler) getTLFtoCrypt(ctx context.Context, uid gregor1.UID) (string, chat1.TLFID, error) {

	me := h.G().Env.GetUID()
	inbox := storage.NewInbox(h.G(), me.ToBytes())

	_, allConvs, err := inbox.ReadAll(ctx)
	if err != nil {
		return "", nil, err
	}

	for _, conv := range allConvs {
		if conv.Conv.Includes(uid) {<commit_msg>check for nil uid before processing (#11983)<commit_after>package chat

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"sync"

	"golang.org/x/sync/errgroup"

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

type DummyIdentifyNotifier struct{}

func (d DummyIdentifyNotifier) Reset()             {}
func (d DummyIdentifyNotifier) ResetOnGUIConnect() {}
func (d DummyIdentifyNotifier) Send(ctx context.Context, update keybase1.CanonicalTLFNameAndIDWithBreaks) {
}

type SimpleIdentifyNotifier struct {
	globals.Contextified
	utils.DebugLabeler
	storage *storage.Storage
}

func NewSimpleIdentifyNotifier(g *globals.Context) *SimpleIdentifyNotifier {
	return &SimpleIdentifyNotifier{
		Contextified: globals.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "SimpleIdentifyNotifier", false),
		storage:      storage.New(g, g.ConvSource),
	}
}

func (d *SimpleIdentifyNotifier) Reset()             {}
func (d *SimpleIdentifyNotifier) ResetOnGUIConnect() {}

func (d *SimpleIdentifyNotifier) Send(ctx context.Context, update keybase1.CanonicalTLFNameAndIDWithBreaks) {
	// Send to storage as well (charge forward on error)
	if err := d.storage.UpdateTLFIdentifyBreak(ctx, update.TlfID.ToBytes(), update.Breaks.Breaks); err != nil {
		d.Debug(ctx, "failed to update storage with TLF identify info: %s", err.Error())
	}
	d.G().NotifyRouter.HandleChatIdentifyUpdate(ctx, update)
}

type CachingIdentifyNotifier struct {
	globals.Contextified
	utils.DebugLabeler

	sync.RWMutex
	storage    *storage.Storage
	identCache map[string]keybase1.CanonicalTLFNameAndIDWithBreaks
}

func NewCachingIdentifyNotifier(g *globals.Context) *CachingIdentifyNotifier {
	return &CachingIdentifyNotifier{
		Contextified: globals.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "CachingIdentifyNotifier", false),
		identCache:   make(map[string]keybase1.CanonicalTLFNameAndIDWithBreaks),
		storage:      storage.New(g, g.ConvSource),
	}
}

func (i *CachingIdentifyNotifier) ResetOnGUIConnect() {
	i.G().ConnectionManager.RegisterLabelCallback(func(typ keybase1.ClientType) {
		switch typ {
		case keybase1.ClientType_GUI_HELPER, keybase1.ClientType_GUI_MAIN:
			i.Reset()
		}
	})
}

func (i *CachingIdentifyNotifier) Reset() {
	i.identCache = make(map[string]keybase1.CanonicalTLFNameAndIDWithBreaks)
}

func (i *CachingIdentifyNotifier) Send(ctx context.Context, update keybase1.CanonicalTLFNameAndIDWithBreaks) {

	// Send to storage as well (charge forward on error)
	if err := i.storage.UpdateTLFIdentifyBreak(ctx, update.TlfID.ToBytes(), update.Breaks.Breaks); err != nil {
		i.Debug(ctx, "failed to update storage with TLF identify info: %s", err.Error())
	}

	// Send notification to GUI about identify status
	tlfName := update.CanonicalName.String()
	i.RLock()
	stored, ok := i.identCache[tlfName]
	i.RUnlock()
	if ok {
		// We have the exact update stored, don't send it again
		if stored.Eq(update) {
			i.Debug(ctx, "hit cache, not sending notify: %s", tlfName)
			return
		}
	}
	i.Lock()
	i.identCache[tlfName] = update
	i.Unlock()

	i.Debug(ctx, "cache miss, sending notify: %s dat: %v", tlfName, update)
	i.G().NotifyRouter.HandleChatIdentifyUpdate(ctx, update)
}

type IdentifyChangedHandler struct {
	globals.Contextified
	utils.DebugLabeler
}

func NewIdentifyChangedHandler(g *globals.Context) *IdentifyChangedHandler {
	return &IdentifyChangedHandler{
		Contextified: globals.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "IdentifyChangedHandler", false),
	}
}

var errNoConvForUser = errors.New("user not found in inbox")

func (h *IdentifyChangedHandler) getUsername(ctx context.Context, uid keybase1.UID) (string, error) {
	u, err := h.G().GetUPAKLoader().LookupUsername(ctx, uid)
	return u.String(), err
}

func (h *IdentifyChangedHandler) getTLFtoCrypt(ctx context.Context, uid gregor1.UID) (string, chat1.TLFID, error) {

	me := h.G().ActiveDevice.UID()
	if me.IsNil() {
		return "", nil, libkb.LoggedInError{}
	}
	inbox := storage.NewInbox(h.G(), me.ToBytes())

	_, allConvs, err := inbox.ReadAll(ctx)
	if err != nil {
		return "", nil, err
	}

	for _, conv := range allConvs {
		if conv.Conv.Includes(uid) {
{'fraction_non_alphanumeric': 0.12543671813366392, 'fraction_numerical': 0.003944550884706413, 'mean_word_length': 5.2625264643613265, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5131253', 'n_tokens_mistral': 3203, 'n_tokens_neox': 3114, 'n_words': 617}