feat: flag accounts Reddit considers unestablished and re-test them
New accounts cannot start chats until Reddit considers them established. Until now the bot would burn a candidate on every invitation Reddit was always going to reject, with nothing in the dashboard explaining why the account looked idle. Two signals feed the verdict. A periodic profile check reads the account's own age, karma and email verification from about.json and compares them against configurable thresholds — that is what flags a brand new account before it wastes anything. A rejected invitation is authoritative: Reddit has told us directly, so the account is held out of sending with a backoff that doubles per rejection, and a delivered invitation clears the flag outright. An unestablished account takes no new posts (canAcceptPost), raises an attention item, reports an `unestablished` sending state, and shows its age/karma evidence on the Accounts page. An unchecked account is always allowed through, so a first run is never blocked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c43e534cf1
commit
89eb4583f8
25 files changed
+1425
-11
No files matched your search
@@ -21,6 +21,20 @@ const account = {
|
||||
lastImportedAt: 2,
|
||||
};
|
||||
|
||||
const establishment = {
|
||||
state: 'unestablished' as const,
|
||||
reasons: ['Account is 2 days old (needs 7 days)', 'Total karma is 4 (needs 20)'],
|
||||
accountAgeDays: 2,
|
||||
linkKarma: 1,
|
||||
commentKarma: 3,
|
||||
totalKarma: 4,
|
||||
hasVerifiedEmail: false,
|
||||
profileCheckedAt: 1_700_000_000_000,
|
||||
lastProbeAt: null,
|
||||
lastProbeFailure: null,
|
||||
retryAt: Date.now() + 4 * 60 * 60_000,
|
||||
};
|
||||
|
||||
const server = setupServer(
|
||||
http.get('/api/login/status', () => HttpResponse.json({ ready: true, loggedIn: false, status: 'login-required' })),
|
||||
http.get('/api/accounts', () => HttpResponse.json([])),
|
||||
@@ -81,6 +95,56 @@ describe('accounts page', () => {
|
||||
expect(screen.getByText('Waiting for a refreshed session')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('explains why an unestablished account is not sending, with the profile evidence', async () => {
|
||||
server.use(http.get('/api/accounts', () => HttpResponse.json([{ ...account, establishment }])));
|
||||
renderAccounts();
|
||||
|
||||
expect(await screen.findByText('u/example-account is not established yet.')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Reddit will not accept chat invitations from a new account/)).toBeInTheDocument();
|
||||
expect(screen.getByText('Not established')).toBeInTheDocument();
|
||||
expect(screen.getByText(
|
||||
'Account is 2 days old (needs 7 days); Total karma is 4 (needs 20)',
|
||||
)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Next re-test in about 4 hours/)).toBeInTheDocument();
|
||||
|
||||
const metrics = screen.getByText('Comment karma').closest('dl')!;
|
||||
expect(within(metrics).getByText('2 days')).toBeInTheDocument();
|
||||
expect(within(metrics).getByText('3')).toBeInTheDocument();
|
||||
expect(within(metrics).getByText('No')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('marks an account that has cleared the thresholds as established', async () => {
|
||||
server.use(http.get('/api/accounts', () => HttpResponse.json([{
|
||||
...account,
|
||||
establishment: {
|
||||
...establishment,
|
||||
state: 'established',
|
||||
reasons: [],
|
||||
accountAgeDays: 400,
|
||||
commentKarma: 900,
|
||||
totalKarma: 1_200,
|
||||
hasVerifiedEmail: true,
|
||||
retryAt: null,
|
||||
},
|
||||
}])));
|
||||
renderAccounts();
|
||||
|
||||
expect(await screen.findByText('Established')).toBeInTheDocument();
|
||||
expect(screen.queryByText(/not established yet/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Outbound sending is on hold/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText('1,200')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows no establishment detail before the first check has run', async () => {
|
||||
server.use(http.get('/api/accounts', () => HttpResponse.json([{ ...account, establishment: null }])));
|
||||
renderAccounts();
|
||||
|
||||
expect(await screen.findByRole('link', { name: 'u/example-account' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('Not established')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Established')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Comment karma')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('says plainly when no accounts have been added', async () => {
|
||||
renderAccounts();
|
||||
|
||||
|
||||
Reference in new issue
Block a user