Skip to content

Commit d85d001

Browse files
committed
autocomplete: Add "human vs. bot users" criterion
In @-mention autocomplete, users are suggested based on: 1. Recent activity in the current topic/stream. 2. Recent DM conversations. 3. Human vs. Bot users (human users come first).
1 parent 1964388 commit d85d001

File tree

2 files changed

+80
-10
lines changed

2 files changed

+80
-10
lines changed

lib/model/autocomplete.dart

+20-3
Original file line numberDiff line numberDiff line change
@@ -236,13 +236,16 @@ class MentionAutocompleteView extends ChangeNotifier {
236236
required PerAccountStore store,
237237
}) {
238238
if (streamId != null) {
239-
final result = compareByRecency(userA, userB,
239+
final recencyResult = compareByRecency(userA, userB,
240240
streamId: streamId,
241241
topic: topic,
242242
store: store);
243-
if (result != 0) return result;
243+
if (recencyResult != 0) return recencyResult;
244244
}
245-
return compareByDms(userA, userB, store: store);
245+
final dmsResult = compareByDms(userA, userB, store: store);
246+
if (dmsResult != 0) return dmsResult;
247+
248+
return compareByBotStatus(userA, userB);
246249
}
247250

248251
/// Determines which of the two users has more recent activity (messages sent
@@ -310,6 +313,20 @@ class MentionAutocompleteView extends ChangeNotifier {
310313
};
311314
}
312315

316+
/// Compares the bot status of two users and returns an integer indicating their order.
317+
///
318+
/// Returns `-1` if `userA` is human and `userB` is bot, returns `1` if `userA`
319+
/// is bot and `userB` is human, and returns `0` if both users have the same
320+
/// bot status.
321+
@visibleForTesting
322+
static int compareByBotStatus(User userA, User userB) {
323+
return switch ((userA.isBot, userB.isBot)) {
324+
(false, true) => -1,
325+
(true, false) => 1,
326+
_ => 0,
327+
};
328+
}
329+
313330
@override
314331
void dispose() {
315332
store.autocompleteViewManager.unregisterMentionAutocomplete(this);

test/model/autocomplete_test.dart

+60-7
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,26 @@ void main() {
490490
});
491491
});
492492

493+
group('compareByBotStatus', () {
494+
final humanUser = eg.user(isBot: false);
495+
final botUser = eg.user(isBot: true);
496+
497+
int compareAB(User a, User b) => MentionAutocompleteView.compareByBotStatus(a, b);
498+
499+
test('userA is human, userB is bot -> favor userA', () {
500+
check(compareAB(humanUser, botUser)).isLessThan(0);
501+
});
502+
503+
test('userA is bot, userB is human -> favor userB', () {
504+
check(compareAB(botUser, humanUser)).isGreaterThan(0);
505+
});
506+
507+
test('both users have the same bot status -> favor none', () {
508+
check(compareAB(humanUser, humanUser)).equals(0);
509+
check(compareAB(botUser, botUser)).equals(0);
510+
});
511+
});
512+
493513
group('ranking across signals', () {
494514
void checkPrecedes(Narrow narrow, User userA, Iterable<User> usersB) {
495515
final view = MentionAutocompleteView.init(store: store, narrow: narrow);
@@ -509,8 +529,17 @@ void main() {
509529
}
510530
}
511531

512-
test('TopicNarrow: topic recency > stream recency > DM recency', () async {
513-
final users = List.generate(5, (i) => eg.user());
532+
test('TopicNarrow: topic recency > stream recency > DM recency '
533+
'> human vs. bot user', () async {
534+
final users = [
535+
eg.user(),
536+
eg.user(),
537+
eg.user(isBot: true),
538+
eg.user(),
539+
eg.user(),
540+
eg.user(),
541+
eg.user(isBot: true),
542+
];
514543
final stream = eg.stream();
515544
final narrow = TopicNarrow(stream.streamId, 'this');
516545
await prepare(users: users, messages: [
@@ -525,25 +554,43 @@ void main() {
525554
checkPrecedes(narrow, users[1], users.skip(2));
526555
checkPrecedes(narrow, users[2], users.skip(3));
527556
checkRankEqual(narrow, [users[3], users[4]]);
557+
checkPrecedes(narrow, users[5], users.skip(6));
528558
});
529559

530-
test('ChannelNarrow: stream recency > DM recency', () async {
531-
final users = List.generate(4, (i) => eg.user());
560+
test('ChannelNarrow: stream recency > DM recency > human vs. bot user', () async {
561+
final users = [
562+
eg.user(isBot: true),
563+
eg.user(),
564+
eg.user(),
565+
eg.user(),
566+
eg.user(),
567+
eg.user(isBot: true),
568+
];
532569
final stream = eg.stream();
533570
final narrow = ChannelNarrow(stream.streamId);
534571
await prepare(users: users, messages: [
535572
eg.streamMessage(sender: users[1], stream: stream),
536573
eg.streamMessage(sender: users[0], stream: stream),
537574
eg.dmMessage(from: users[2], to: [users[3], eg.selfUser]),
538575
eg.dmMessage(from: users[1], to: [eg.selfUser]),
576+
eg.dmMessage(from: users[4], to: [users[5], eg.selfUser]),
539577
]);
540578
checkPrecedes(narrow, users[0], users.skip(1));
541579
checkPrecedes(narrow, users[1], users.skip(2));
542580
checkRankEqual(narrow, [users[2], users[3]]);
581+
checkPrecedes(narrow, users[4], users.skip(5));
543582
});
544583

545-
test('DmNarrow: DM recency > this-conversation recency or stream recency', () async {
546-
final users = List.generate(4, (i) => eg.user());
584+
test('DmNarrow: DM recency > this-conversation recency or stream recency '
585+
'or human vs. bot user', () async {
586+
final users = [
587+
eg.user(isBot: true),
588+
eg.user(),
589+
eg.user(),
590+
eg.user(),
591+
eg.user(),
592+
eg.user(isBot: true),
593+
];
547594
await prepare(users: users, messages: [
548595
eg.dmMessage(from: users[3], to: [eg.selfUser]),
549596
eg.dmMessage(from: users[1], to: [users[2], eg.selfUser]),
@@ -562,6 +609,7 @@ void main() {
562609
checkRankEqual(narrow, [users[1], users[2]]);
563610
checkPrecedes(narrow, users[1], users.skip(3));
564611
checkPrecedes(narrow, users[2], users.skip(3));
612+
checkPrecedes(narrow, users[4], users.skip(5));
565613
}
566614
});
567615

@@ -605,6 +653,8 @@ void main() {
605653
eg.user(userId: 3, fullName: 'User Three'),
606654
eg.user(userId: 4, fullName: 'User Four'),
607655
eg.user(userId: 5, fullName: 'User Five'),
656+
eg.user(userId: 6, fullName: 'User Six', isBot: true),
657+
eg.user(userId: 7, fullName: 'User Seven'),
608658
];
609659

610660
await prepare(users: users, messages: [
@@ -620,14 +670,17 @@ void main() {
620670
// The order should be:
621671
// 1. Users most recent in the current topic/stream.
622672
// 2. Users most recent in the DM conversations.
673+
// 3. Human vs. Bot users (human users come first).
623674
check(await getResults(topicNarrow, MentionAutocompleteQuery('')))
624-
.deepEquals([1, 5, 4, 2, 3]);
675+
.deepEquals([1, 5, 4, 2, 3, 7, 6]);
625676

626677
// Check the ranking applies also to results filtered by a query.
627678
check(await getResults(topicNarrow, MentionAutocompleteQuery('t')))
628679
.deepEquals([2, 3]);
629680
check(await getResults(topicNarrow, MentionAutocompleteQuery('f')))
630681
.deepEquals([5, 4]);
682+
check(await getResults(topicNarrow, MentionAutocompleteQuery('s')))
683+
.deepEquals([7, 6]);
631684
});
632685
});
633686
}

0 commit comments

Comments
 (0)