feat(guilds): /guild — the module's own chat command #14
@@ -42,13 +42,22 @@ async function levelFor(actor) {
|
||||
return visibility.viewerLevel({ user: { id: actor.userId, role: actor.role } })
|
||||
}
|
||||
|
||||
// The nudge §9 answer 5 asks for, and only when it is TRUE. An unlinked caller
|
||||
// who was told nothing because the shard publishes nothing is not helped by
|
||||
// being invited to link; the prompt appears when linking is what would actually
|
||||
// change the answer.
|
||||
// The nudge §9 answer 5 asks for, and only when it is TRUE.
|
||||
//
|
||||
// **Linking reaches exactly two rungs and no further.** Signing in gets a caller
|
||||
// to `logged_in` and linking a game account to `player`; `staff` and `admin` are
|
||||
// roles an operator grants and no amount of linking will earn. So a shard that
|
||||
// gates guilds to staff refuses an unlinked caller WITHOUT the invitation —
|
||||
// telling them to link would be telling them to do something that changes
|
||||
// nothing, which is worse than saying no.
|
||||
//
|
||||
// The live walk found this: gated to `staff`, the refusal still read "this shard
|
||||
// shows guild information to linked players".
|
||||
const LINKING_REACHES = new Set(['logged_in', 'player'])
|
||||
|
||||
function linkPrompt(actor, audience) {
|
||||
if (actor.isLinked) return null
|
||||
if (audience === 'anonymous') return null
|
||||
if (!LINKING_REACHES.has(audience)) return null
|
||||
return 'Link your account on the site to see more — this shard shows guild information to linked players.'
|
||||
}
|
||||
|
||||
@@ -154,7 +163,10 @@ async function handler({ options, actor }) {
|
||||
if (!wanted) {
|
||||
const top = [...rows].sort((a, b) => (b.members || 0) - (a.members || 0)).slice(0, LIST_LIMIT)
|
||||
return {
|
||||
title: `Guilds on ${core.baseUrl.replace(/^https?:\/\//, '')}`,
|
||||
// Not "Guilds on <host>": `ctx.site` carries a base URL and no brand name,
|
||||
// so naming the deployment here can only mean printing its hostname into
|
||||
// an embed title, which is noise on a shard's own Discord server.
|
||||
title: 'Guilds on this shard',
|
||||
fields: top.map((g) => ({
|
||||
name: g.abbr ? `${g.name} [${g.abbr}]` : g.name,
|
||||
value: `${g.members || 0} members · ${g.online || 0} online`,
|
||||
|
||||
@@ -88,6 +88,14 @@ test('an unlinked caller is invited to link — but only when linking would chan
|
||||
stub({ audience: 'anonymous', level: 'anonymous' })
|
||||
const open = await command.handler({ options: {}, actor: anonymous })
|
||||
assert.equal(open.notice, null)
|
||||
|
||||
// Gated to staff: linking reaches `player` and stops there, so the invitation
|
||||
// would be an instruction to do something that changes nothing. Found on the
|
||||
// live rig, where a staff-gated shard still offered it.
|
||||
stub({ audience: 'staff', level: 'anonymous' })
|
||||
const unreachable = await command.handler({ options: {}, actor: anonymous })
|
||||
assert.match(unreachable.text, /not shown to your account/)
|
||||
assert.equal(unreachable.notice, null)
|
||||
})
|
||||
|
||||
test('a stale board answers offline rather than reporting what it still holds', async () => {
|
||||
@@ -99,6 +107,7 @@ test('a stale board answers offline rather than reporting what it still holds',
|
||||
test('no argument lists the largest guilds', async () => {
|
||||
stub()
|
||||
const res = await command.handler({ options: {}, actor: anonymous })
|
||||
assert.equal(res.title, 'Guilds on this shard')
|
||||
assert.equal(res.fields.length, 2)
|
||||
assert.match(res.fields[0].name, /Knights of the Codex/)
|
||||
assert.match(res.fields[0].value, /12 members · 3 online/)
|
||||
|
||||
Reference in New Issue
Block a user