github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/home/home.go (about)

     1  // Copyright 2019 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package home
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"math/rand"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/keybase/client/go/contacts"
    15  
    16  	"github.com/keybase/client/go/gregor"
    17  	"github.com/keybase/client/go/libkb"
    18  	"github.com/keybase/client/go/protocol/gregor1"
    19  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    20  	"golang.org/x/net/context"
    21  )
    22  
    23  type homeCache struct {
    24  	obj      keybase1.HomeScreen
    25  	cachedAt time.Time
    26  }
    27  
    28  type peopleCache struct {
    29  	all       []keybase1.HomeUserSummary
    30  	lastShown []keybase1.HomeUserSummary
    31  	cachedAt  time.Time
    32  }
    33  
    34  type Home struct {
    35  	libkb.Contextified
    36  
    37  	sync.Mutex
    38  	homeCache   *homeCache
    39  	peopleCache *peopleCache
    40  }
    41  
    42  type rawGetHome struct {
    43  	libkb.AppStatusEmbed
    44  	Home keybase1.HomeScreen `json:"home"`
    45  }
    46  
    47  func NewHome(g *libkb.GlobalContext) *Home {
    48  	home := &Home{Contextified: libkb.NewContextified(g)}
    49  	g.AddLogoutHook(home, "home")
    50  	g.AddDbNukeHook(home, "home")
    51  	return home
    52  }
    53  
    54  func homeRetry(a libkb.APIArg) libkb.APIArg {
    55  	a.RetryCount = 3
    56  	a.InitialTimeout = 4 * time.Second
    57  	a.RetryMultiplier = 1.1
    58  	return a
    59  }
    60  
    61  func decodeContactNotifications(mctx libkb.MetaContext, home keybase1.
    62  	HomeScreen) (decoded keybase1.HomeScreen, err error) {
    63  	items := home.Items
    64  	for i, item := range items {
    65  		t, err := item.Data.T()
    66  		if err != nil {
    67  			mctx.Warning("Could not determine home screen item type %v: %v",
    68  				item, err)
    69  			continue
    70  		}
    71  		if t == keybase1.HomeScreenItemType_PEOPLE {
    72  			peopleItem := item.Data.People()
    73  			innerT, err := peopleItem.T()
    74  			if err != nil {
    75  				mctx.Warning(
    76  					"Could not determine home screen inner item type %v: %v",
    77  					item, err)
    78  				continue
    79  			}
    80  			if innerT == keybase1.HomeScreenPeopleNotificationType_CONTACT {
    81  				contact := peopleItem.Contact()
    82  				decryptedContact,
    83  					err := contacts.DecryptContactBlob(mctx,
    84  					contact.ResolvedContactBlob)
    85  				if err != nil {
    86  					return home, err
    87  				}
    88  
    89  				contact.Username = decryptedContact.ResolvedUser.Username
    90  				contact.Description = decryptedContact.Description
    91  				item.Data = keybase1.NewHomeScreenItemDataWithPeople(
    92  					keybase1.NewHomeScreenPeopleNotificationWithContact(contact))
    93  				items[i] = item
    94  			} else if innerT == keybase1.HomeScreenPeopleNotificationType_CONTACT_MULTI {
    95  				contactMulti := peopleItem.ContactMulti()
    96  				contactList := contactMulti.Contacts
    97  				for i, contact := range contactList {
    98  					decryptedContact,
    99  						err := contacts.DecryptContactBlob(mctx,
   100  						contact.ResolvedContactBlob)
   101  					if err != nil {
   102  						return home, err
   103  					}
   104  
   105  					contactList[i].Username = decryptedContact.ResolvedUser.Username
   106  					contactList[i].Description = decryptedContact.Description
   107  				}
   108  				item.Data = keybase1.NewHomeScreenItemDataWithPeople(
   109  					keybase1.NewHomeScreenPeopleNotificationWithContactMulti(
   110  						contactMulti))
   111  				items[i] = item
   112  			}
   113  		}
   114  	}
   115  
   116  	home.Items = items
   117  	return home, nil
   118  }
   119  
   120  func (h *Home) getToCache(ctx context.Context, markedViewed bool, numPeopleWanted int, skipPeople bool) (err error) {
   121  	mctx := libkb.NewMetaContext(ctx, h.G())
   122  	defer mctx.Trace("Home#getToCache", &err)()
   123  
   124  	numPeopleToRequest := 100
   125  	if numPeopleWanted > numPeopleToRequest {
   126  		numPeopleToRequest = numPeopleWanted
   127  	}
   128  	if skipPeople {
   129  		numPeopleToRequest = 0
   130  	}
   131  	arg := libkb.NewAPIArg("home")
   132  	arg.SessionType = libkb.APISessionTypeREQUIRED
   133  	arg.Args = libkb.HTTPArgs{
   134  		"record_visit": libkb.B{Val: markedViewed},
   135  		"num_people":   libkb.I{Val: numPeopleToRequest},
   136  	}
   137  	var raw rawGetHome
   138  	if err = mctx.G().API.GetDecode(mctx, homeRetry(arg), &raw); err != nil {
   139  		return err
   140  	}
   141  	home, err := decodeContactNotifications(mctx, raw.Home)
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	newPeopleCache := &peopleCache{
   147  		all: home.FollowSuggestions,
   148  	}
   149  
   150  	if h.peopleCache != nil {
   151  		newPeopleCache.lastShown = h.peopleCache.lastShown
   152  		newPeopleCache.cachedAt = h.peopleCache.cachedAt
   153  	}
   154  	h.peopleCache = newPeopleCache
   155  
   156  	mctx.Debug("| %d follow suggestions returned", len(home.FollowSuggestions))
   157  	home.FollowSuggestions = nil
   158  
   159  	h.homeCache = &homeCache{
   160  		obj:      home,
   161  		cachedAt: h.G().GetClock().Now(),
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func (h *Home) Get(ctx context.Context, markViewed bool, numPeopleWanted int) (ret keybase1.HomeScreen, err error) {
   168  	ctx = libkb.WithLogTag(ctx, "HOME")
   169  	defer h.G().CTrace(ctx, "Home#Get", &err)()
   170  
   171  	// 10 people by default
   172  	if numPeopleWanted < 0 {
   173  		numPeopleWanted = 10
   174  	}
   175  
   176  	h.Lock()
   177  	defer h.Unlock()
   178  
   179  	useCache, people := h.peopleCache.isValid(ctx, h.G(), numPeopleWanted)
   180  	if useCache {
   181  		useCache = h.homeCache.isValid(ctx, h.G())
   182  	}
   183  
   184  	if useCache && markViewed {
   185  		err := h.bustHomeCacheIfBadgedFollowers(ctx)
   186  		if err != nil {
   187  			return ret, err
   188  		}
   189  		useCache = h.homeCache != nil
   190  		// If we blew up our cache, get out of here and refetch, proceed with
   191  		// marking the view.
   192  		if useCache {
   193  			h.G().Log.CDebugf(ctx, "| cache is good; going to server to mark view")
   194  			if err := h.markViewedAPICall(ctx); err != nil {
   195  				h.G().Log.CInfof(ctx, "Error marking home as viewed: %s", err.Error())
   196  			}
   197  		}
   198  	}
   199  
   200  	if !useCache {
   201  		h.G().Log.CDebugf(ctx, "| cache is no good; going fetching from server")
   202  		// If we've already found the people we need to show in the cache,
   203  		// there's no reason to reload them.
   204  		skipLoadPeople := len(people) > 0
   205  		if err = h.getToCache(ctx, markViewed, numPeopleWanted, skipLoadPeople); err != nil {
   206  			return ret, err
   207  		}
   208  	}
   209  
   210  	// Prime the return object with whatever was cached for home
   211  	tmp := h.homeCache.obj
   212  
   213  	if people != nil {
   214  		tmp.FollowSuggestions = people
   215  	} else {
   216  		err := h.peopleCache.loadInto(ctx, h.G(), &tmp, numPeopleWanted)
   217  		if err != nil {
   218  			return ret, err
   219  		}
   220  	}
   221  
   222  	// Return a deep copy of the tmp object, so that the caller can't
   223  	// change it or race against other Go routines.
   224  	ret = tmp.DeepCopy()
   225  
   226  	return ret, nil
   227  }
   228  
   229  func (p *peopleCache) loadInto(ctx context.Context, g *libkb.GlobalContext, out *keybase1.HomeScreen, numPeopleWanted int) error {
   230  	if numPeopleWanted > len(p.all) {
   231  		numPeopleWanted = len(p.all)
   232  		g.Log.CDebugf(ctx, "| didn't get enough people loaded, so short-changing at %d", numPeopleWanted)
   233  	}
   234  	out.FollowSuggestions = p.all[0:numPeopleWanted]
   235  	p.all = p.all[numPeopleWanted:]
   236  	p.lastShown = out.FollowSuggestions
   237  	p.cachedAt = g.GetClock().Now()
   238  	return nil
   239  }
   240  
   241  func (h *homeCache) isValid(ctx context.Context, g *libkb.GlobalContext) bool {
   242  	if h == nil {
   243  		g.Log.CDebugf(ctx, "| homeCache == nil, therefore isn't valid")
   244  		return false
   245  	}
   246  	diff := g.GetClock().Now().Sub(h.cachedAt)
   247  	if diff >= libkb.HomeCacheTimeout {
   248  		g.Log.CDebugf(ctx, "| homeCache was stale (cached %s ago)", diff)
   249  		return false
   250  	}
   251  	g.Log.CDebugf(ctx, "| homeCache was valid (cached %s ago)", diff)
   252  	return true
   253  }
   254  
   255  func (p *peopleCache) isValid(ctx context.Context, g *libkb.GlobalContext, numPeopleWanted int) (bool, []keybase1.HomeUserSummary) {
   256  	if p == nil {
   257  		g.Log.CDebugf(ctx, "| peopleCache = nil, therefore isn't valid")
   258  		return false, nil
   259  	}
   260  	diff := g.GetClock().Now().Sub(p.cachedAt)
   261  	if diff < libkb.HomePeopleCacheTimeout && numPeopleWanted <= len(p.lastShown) {
   262  		g.Log.CDebugf(ctx, "| peopleCache is valid, just returning last viewed")
   263  		return true, p.lastShown
   264  	}
   265  	if numPeopleWanted <= len(p.all) {
   266  		g.Log.CDebugf(ctx, "| people cache is valid, can pop from all")
   267  		return true, nil
   268  	}
   269  	return false, nil
   270  }
   271  
   272  func (h *Home) skipTodoType(ctx context.Context, typ keybase1.HomeScreenTodoType) (err error) {
   273  	mctx := libkb.NewMetaContext(ctx, h.G())
   274  	defer mctx.Trace("Home#skipTodoType", &err)()
   275  
   276  	_, err = mctx.G().API.Post(mctx, homeRetry(libkb.APIArg{
   277  		Endpoint:    "home/todo/skip",
   278  		SessionType: libkb.APISessionTypeREQUIRED,
   279  		Args: libkb.HTTPArgs{
   280  			"type": libkb.I{Val: int(typ)},
   281  		},
   282  	}))
   283  
   284  	return err
   285  }
   286  
   287  func (h *Home) DismissAnnouncement(ctx context.Context, id keybase1.HomeScreenAnnouncementID) (err error) {
   288  	mctx := libkb.NewMetaContext(ctx, h.G())
   289  	defer mctx.Trace("Home#DismissAnnouncement", &err)()
   290  
   291  	_, err = mctx.G().API.Post(mctx, homeRetry(libkb.APIArg{
   292  		Endpoint:    "home/todo/skip",
   293  		SessionType: libkb.APISessionTypeREQUIRED,
   294  		Args: libkb.HTTPArgs{
   295  			"announcement": libkb.I{Val: int(id)},
   296  		},
   297  	}))
   298  
   299  	return err
   300  }
   301  
   302  func (h *Home) bustCache(ctx context.Context, bustPeople bool) {
   303  	h.G().Log.CDebugf(ctx, "Home#bustCache")
   304  	h.Lock()
   305  	defer h.Unlock()
   306  	h.homeCache = nil
   307  	if bustPeople {
   308  		h.peopleCache = nil
   309  	}
   310  }
   311  
   312  func (h *Home) bustHomeCacheIfBadgedFollowers(ctx context.Context) (err error) {
   313  	defer h.G().CTrace(ctx, "+ Home#bustHomeCacheIfBadgedFollowers", &err)()
   314  
   315  	if h.homeCache == nil {
   316  		h.G().Log.CDebugf(ctx, "| nil home cache, nothing to bust")
   317  		return nil
   318  	}
   319  
   320  	bust := false
   321  	for i, item := range h.homeCache.obj.Items {
   322  		if !item.Badged {
   323  			continue
   324  		}
   325  		if typ, err := item.Data.T(); err != nil {
   326  			bust = true
   327  			h.G().Log.CDebugf(ctx, "| in bustHomeCacheIfBadgedFollowers: bad item: %v", err)
   328  			break
   329  		} else if typ == keybase1.HomeScreenItemType_PEOPLE {
   330  			bust = true
   331  			h.G().Log.CDebugf(ctx, "| in bustHomeCacheIfBadgedFollowers: found badged home people item @%d", i)
   332  			break
   333  		}
   334  	}
   335  
   336  	if bust {
   337  		h.G().Log.CDebugf(ctx, "| busting home cache")
   338  		h.homeCache = nil
   339  	} else {
   340  		h.G().Log.CDebugf(ctx, "| not busting home cache")
   341  	}
   342  
   343  	return nil
   344  }
   345  
   346  func (h *Home) SkipTodoType(ctx context.Context, typ keybase1.HomeScreenTodoType) (err error) {
   347  	var which string
   348  	var ok bool
   349  	if which, ok = keybase1.HomeScreenTodoTypeRevMap[typ]; !ok {
   350  		which = fmt.Sprintf("unknown=%d", int(typ))
   351  	}
   352  	defer h.G().CTrace(ctx, fmt.Sprintf("home#SkipTodoType(%s)", which), &err)()
   353  	h.bustCache(ctx, false)
   354  	return h.skipTodoType(ctx, typ)
   355  }
   356  
   357  func (h *Home) MarkViewed(ctx context.Context) (err error) {
   358  	defer h.G().CTrace(ctx, "Home#MarkViewed", &err)()
   359  	h.Lock()
   360  	defer h.Unlock()
   361  	return h.markViewedWithLock(ctx)
   362  }
   363  
   364  func (h *Home) markViewedWithLock(ctx context.Context) (err error) {
   365  	defer h.G().CTrace(ctx, "Home#markViewedWithLock", &err)()
   366  	err = h.bustHomeCacheIfBadgedFollowers(ctx)
   367  	if err != nil {
   368  		return err
   369  	}
   370  	return h.markViewedAPICall(ctx)
   371  }
   372  
   373  func (h *Home) markViewedAPICall(ctx context.Context) (err error) {
   374  	mctx := libkb.NewMetaContext(ctx, h.G())
   375  	defer mctx.Trace("Home#markViewedAPICall", &err)()
   376  
   377  	if _, err = mctx.G().API.Post(mctx, homeRetry(libkb.APIArg{
   378  		Endpoint:    "home/visit",
   379  		SessionType: libkb.APISessionTypeREQUIRED,
   380  		Args:        libkb.HTTPArgs{},
   381  	})); err != nil {
   382  		mctx.Warning("Unable to home#markViewedAPICall: %v", err)
   383  	}
   384  	return nil
   385  }
   386  
   387  func (h *Home) ActionTaken(ctx context.Context) (err error) {
   388  	defer h.G().CTrace(ctx, "Home#ActionTaken", &err)()
   389  	h.bustCache(ctx, false)
   390  	return err
   391  }
   392  
   393  func (h *Home) OnLogout(m libkb.MetaContext) error {
   394  	h.bustCache(m.Ctx(), true)
   395  	return nil
   396  }
   397  
   398  func (h *Home) OnDbNuke(m libkb.MetaContext) error {
   399  	h.bustCache(m.Ctx(), true)
   400  	return nil
   401  }
   402  
   403  type updateGregorMessage struct {
   404  	Version              int `json:"version"`
   405  	AnnouncementsVersion int `json:"announcements_version"`
   406  }
   407  
   408  func (h *Home) updateUI(ctx context.Context) (err error) {
   409  	defer h.G().CTrace(ctx, "Home#updateUI", &err)()
   410  	var ui keybase1.HomeUIInterface
   411  	if h.G().UIRouter == nil {
   412  		h.G().Log.CDebugf(ctx, "no UI router, swallowing update")
   413  		return nil
   414  	}
   415  	ui, err = h.G().UIRouter.GetHomeUI()
   416  	if err != nil {
   417  		return err
   418  	}
   419  	if ui == nil {
   420  		h.G().Log.CDebugf(ctx, "no registered HomeUI, swallowing update")
   421  		return nil
   422  	}
   423  	err = ui.HomeUIRefresh(context.Background())
   424  	return err
   425  }
   426  
   427  func (h *Home) handleUpdate(ctx context.Context, item gregor.Item) (err error) {
   428  	defer h.G().CTrace(ctx, "Home#handleUpdate", &err)()
   429  	var msg updateGregorMessage
   430  	if err = json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
   431  		h.G().Log.Debug("error unmarshaling home.update item: %s", err.Error())
   432  		return err
   433  	}
   434  
   435  	h.G().Log.CDebugf(ctx, "home.update unmarshaled: %+v", msg)
   436  
   437  	h.handleUpdateWithVersions(ctx, msg.Version, msg.AnnouncementsVersion, true /* send up update UI */)
   438  	return nil
   439  }
   440  
   441  func (h *Home) handleUpdateWithVersions(ctx context.Context, homeVersion int, announcementsVersion int, refreshHome bool) {
   442  
   443  	h.Lock()
   444  	defer func() {
   445  		if refreshHome {
   446  			_ = h.updateUI(ctx)
   447  		}
   448  		h.Unlock()
   449  	}()
   450  
   451  	if h.homeCache == nil {
   452  		return
   453  	}
   454  	h.G().Log.CDebugf(ctx, "home gregor msg state: (version=%d,announcementsVersion=%d)", h.homeCache.obj.Version, h.homeCache.obj.AnnouncementsVersion)
   455  	if homeVersion > h.homeCache.obj.Version || announcementsVersion > h.homeCache.obj.AnnouncementsVersion {
   456  		h.G().Log.CDebugf(ctx, "home gregor msg: clearing cache (new version is <%d,%d>)", homeVersion, announcementsVersion)
   457  		h.homeCache = nil
   458  		refreshHome = true
   459  	}
   460  }
   461  
   462  func (h *Home) IsAlive() bool {
   463  	return true
   464  }
   465  
   466  func (h *Home) Name() string {
   467  	return "Home"
   468  }
   469  
   470  func (h *Home) handleUpdateState(ctx context.Context, item gregor.Item) (err error) {
   471  	defer h.G().CTrace(ctx, "Home#handleUpdateState", &err)()
   472  	var msg libkb.HomeStateBody
   473  	if err = json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
   474  		h.G().Log.Debug("error unmarshaling home.update item: %s", err.Error())
   475  		return err
   476  	}
   477  
   478  	h.G().Log.CDebugf(ctx, "home.state unmarshaled: %+v", msg)
   479  	h.handleUpdateWithVersions(ctx, msg.Version, msg.AnnouncementsVersion, false /* send up update UI */)
   480  	return nil
   481  }
   482  
   483  func (h *Home) Create(ctx context.Context, cli gregor1.IncomingInterface, category string, ibm gregor.Item) (bool, error) {
   484  	switch category {
   485  	case "home.update":
   486  		return true, h.handleUpdate(ctx, ibm)
   487  	case "home.state":
   488  		// MK 2020.03.17: This case fixes a race that we observed in the wild, with **announcements**.
   489  		// The issue is that if you view the home page via home/get and then an announcement is inserted
   490  		// at roughly the same time. The home/get can then trigger a gregor since the announcement version
   491  		// bumped. That'll bump the badge state and show a 1 on the home tab. But then when you visit it,
   492  		// home/get will not be repolled, since there is a fresh home state that just got loaded. You'll have
   493  		// to wait for 1 hour in which you have a phantom badge. To break this race, we'll clear out the
   494  		// home state if home.state says the state has moved forward (though we mainly intend that message
   495  		// to drive badging).
   496  		// Note that we return false since we still want this message to be handled by other parts
   497  		// of the system (like the badger).
   498  		return false, h.handleUpdateState(ctx, ibm)
   499  	default:
   500  		if strings.HasPrefix(category, "home.") {
   501  			return false, fmt.Errorf("unknown home handler category: %q", category)
   502  		}
   503  		return false, nil
   504  	}
   505  }
   506  
   507  func (h *Home) Dismiss(ctx context.Context, cli gregor1.IncomingInterface, category string, ibm gregor.Item) (bool, error) {
   508  	return true, nil
   509  }
   510  
   511  type rawPollHome struct {
   512  	Status       libkb.AppStatus `json:"status"`
   513  	NextPollSecs int             `json:"next_poll_secs"`
   514  }
   515  
   516  func (r *rawPollHome) GetAppStatus() *libkb.AppStatus {
   517  	return &r.Status
   518  }
   519  
   520  func (h *Home) RunUpdateLoop(m libkb.MetaContext) {
   521  	go h.updateLoopThread(m)
   522  }
   523  
   524  func (h *Home) updateLoopThread(m libkb.MetaContext) {
   525  	m = m.WithLogTag("HULT")
   526  	m.Debug("Starting Home#updateLoopThread")
   527  	slp := time.Minute * (time.Duration(5) + time.Duration((rand.Int() % 10)))
   528  	var err error
   529  	for {
   530  		m.Debug("Sleeping %v until next poll", slp)
   531  		m.G().Clock().Sleep(slp)
   532  		slp, err = h.pollOnce(m)
   533  		if _, ok := err.(libkb.DeviceRequiredError); ok {
   534  			slp = time.Duration(1) * time.Minute
   535  		} else if err != nil {
   536  			slp = time.Duration(15) * time.Minute
   537  			m.Debug("Hit an error in home update loop: %v", err)
   538  		}
   539  	}
   540  }
   541  
   542  func (h *Home) pollOnce(m libkb.MetaContext) (d time.Duration, err error) {
   543  	defer m.Trace("Home#pollOnce", &err)()
   544  
   545  	if !m.HasAnySession() {
   546  		m.Debug("No-op, since don't have keys (and/or am not logged in)")
   547  		return time.Duration(0), libkb.DeviceRequiredError{}
   548  	}
   549  
   550  	var raw rawPollHome
   551  	err = m.G().API.GetDecode(m, libkb.APIArg{
   552  		Endpoint:    "home/poll",
   553  		SessionType: libkb.APISessionTypeREQUIRED,
   554  		Args:        libkb.HTTPArgs{},
   555  	}, &raw)
   556  	if err != nil {
   557  		m.Warning("Unable to Home#pollOnce: %v", err)
   558  		return time.Duration(0), err
   559  	}
   560  	return time.Duration(raw.NextPollSecs) * time.Second, nil
   561  }
   562  
   563  func findBadUserInUsers(m libkb.MetaContext, l []keybase1.HomeUserSummary, badUIDs map[keybase1.UID]bool) bool {
   564  	for _, s := range l {
   565  		if badUIDs[s.Uid] {
   566  			m.Debug("found blocked uid=%s", s.Uid)
   567  			return true
   568  		}
   569  	}
   570  	return false
   571  }
   572  
   573  func (h *Home) UserBlocked(m libkb.MetaContext, badUIDs map[keybase1.UID]bool) (err error) {
   574  	h.Lock()
   575  	defer h.Unlock()
   576  
   577  	if !h.peopleCache.hasBadUser(m, badUIDs) {
   578  		m.Debug("UserBlocked didn't result in any home user suggestions getting blocked, so no-op")
   579  		return nil
   580  	}
   581  
   582  	h.peopleCache = nil
   583  	m.Debug("UserBlocked forced home change, updating UI")
   584  	tmp := h.updateUI(m.Ctx())
   585  	if tmp != nil {
   586  		m.Debug("error updating home UI, but ignoring: %s", tmp)
   587  	}
   588  	return nil
   589  }
   590  
   591  func (p *peopleCache) hasBadUser(m libkb.MetaContext, badUIDs map[keybase1.UID]bool) bool {
   592  	if p == nil {
   593  		m.Debug("nothing to do, people cache is empty")
   594  		return false
   595  	}
   596  
   597  	if findBadUserInUsers(m, p.all, badUIDs) {
   598  		m.Debug("Found blocked user in people cache (all)")
   599  		return true
   600  	}
   601  
   602  	// @maxtaco 2019.11.25: As @jzila points out, there isn't a way for the blocked user to be
   603  	// in this list, but not the all list above. But let's play it safe and check anyways,
   604  	// to err on the side of forcing a refresh.
   605  	if findBadUserInUsers(m, p.lastShown, badUIDs) {
   606  		m.Debug("Found blocked user in people cache (lastShown)")
   607  		return true
   608  	}
   609  
   610  	return false
   611  }