feat: the extension picks a Discord source's posters from who posts in the channel, stored by id (#4488)
CI and images / lint (push) Successful in 3s
CI and images / extension-version (push) Successful in 3s
CI and images / extension-test (push) Successful in 18s
CI and images / frontend-build (push) Successful in 26s
CI and images / backend-lint-and-test (push) Successful in 32s
CI and images / integration (push) Successful in 2m24s
CI and images / build-agent (push) Successful in 5s
CI and images / sign-extension (push) Successful in 6m13s
CI and images / build-web (push) Successful in 1m38s
CI and images / smoke-web (push) Successful in 54s
CI and images / promote (push) Successful in 2s

On a Discord channel page the Add panel lists who posted in the newest 200
messages, the creator the server is named for (or owned by) ticked. On a
channel FabledCurator already follows, the chip opens the same list with Save
and Open artist. The list is kept as user ids, so a rename never quietly stops
it matching; the name each was picked under rides beside it for display, and
the Subscriptions dialog shows it.

- DiscordClient.recent_posters + rank_posters (owner / name-matches-server;
  bots never suggested; it suggests, the operator ticks).
- GET/POST /api/extension/discord/posters; quick-add takes discord_authors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-28 09:47:27 -04:00
co-authored by Claude Opus 5.5
parent 1efece0f21
commit ef591edf10
11 changed files with 593 additions and 2 deletions
+57
View File
@@ -424,3 +424,60 @@ def test_the_adapter_authenticates_with_the_token_and_keys_by_identity(tmp_path)
_msg(9, attachments=[{"id": "300", "url": "https://cdn/a/1.png"}])
)
assert _ledger_key(media) == "9:300"
# -- the poster picker (#4488) -------------------------------------------------
def _person(pid, username, global_name=None, **extra):
return {"id": pid, "username": username, "global_name": global_name,
"messages": 1, "images": 0, **extra}
def test_the_creator_a_server_is_named_for_is_suggested():
ranked = dc.rank_posters("Todding's Server", "99", [
_person("8", "jakeboii", "Jake Boii", images=3),
_person("7", "todding", "Todding", images=1),
])
assert ranked[0]["id"] == "7" and ranked[0]["suggested"] is True
assert ranked[0]["reasons"] == ["name matches the server"]
assert ranked[1]["suggested"] is False
def test_owning_the_server_and_matching_its_name_outranks_either_alone():
ranked = dc.rank_posters("The Official Todding Discord", "7", [
_person("5", "toddingfan"),
_person("7", "t0dd", "Todding"),
])
assert [r["id"] for r in ranked if r["suggested"]] == ["7"]
assert ranked[0]["reasons"] == ["owns the server", "name matches the server"]
def test_no_signal_suggests_no_one_and_bots_never():
assert not any(r["suggested"] for r in dc.rank_posters("Art Club", None, [
_person("1", "alice"), _person("2", "bob"),
]))
ranked = dc.rank_posters("Todding's Server", None, [_person("3", "toddingbot", bot=True)])
assert ranked[0]["suggested"] is False
def test_recent_posters_tallies_a_shallow_window_by_id():
creator = {"id": "7", "username": "todding", "global_name": "Todding"}
other = {"id": "8", "username": "jakeboii", "global_name": "Jake Boii"}
art = [{"url": "https://cdn/a/1.png"}]
page = [_msg(i, author=creator, attachments=art) for i in range(300, 240, -1)]
page += [_msg(i, author=other) for i in range(240, 200, -1)]
routes = {
"/guilds/1": _ok({"id": "1", "name": "Todding's Server", "owner_id": "7"}),
("/channels/2/messages", None): _ok(page),
("/channels/2/messages", "201"): _ok([_msg(150, author=other)]),
}
client = _client(routes)
got = client.recent_posters("1", "2", max_messages=150)
assert got["server"] == "Todding's Server" and got["owner_id"] == "7"
assert got["scanned"] == 101
first = got["posters"][0]
assert (first["id"], first["messages"], first["images"], first["suggested"]) == ("7", 60, 60, True)
assert got["posters"][1]["messages"] == 41
# It asked for no more than the window: 100, then the 50 left of 150.
limits = [p.get("limit") for e, p in client._session.calls if e.endswith("/messages")]
assert limits == [100, 50]