stacker.news/scripts/welcome.js

198 lines
4.7 KiB
JavaScript
Raw Normal View History

2025-01-19 23:50:16 +01:00
#!/usr/bin/env node
function usage () {
console.log('Usage: scripts/welcome.js <fetch-after> [--prod]')
process.exit(1)
}
let args = process.argv.slice(2)
const useProd = args.indexOf('--prod') !== -1
const SN_API_URL = useProd ? 'https://stacker.news' : 'http://localhost:3000'
args = args.filter(arg => arg !== '--prod')
console.log('> url:', SN_API_URL)
2025-01-19 23:50:16 +01:00
// this is the item id of the last bio that was included in the previous post of the series
const FETCH_AFTER = args[0]
console.log('> fetch-after:', FETCH_AFTER)
if (!FETCH_AFTER) {
usage()
}
2025-01-19 23:50:16 +01:00
2025-02-10 01:30:58 +01:00
const SN_API_KEY = process.env.SN_API_KEY
if (!SN_API_KEY) {
console.log('SN_API_KEY must be set in environment')
process.exit(1)
}
2025-01-19 23:50:16 +01:00
async function gql (query, variables = {}) {
const response = await fetch(`${SN_API_URL}/api/graphql`, {
method: 'POST',
2025-02-10 01:30:58 +01:00
headers: { 'Content-Type': 'application/json', 'x-api-key': SN_API_KEY },
2025-01-19 23:50:16 +01:00
body: JSON.stringify({ query, variables })
})
if (response.status !== 200) {
throw new Error(`request failed: ${response.statusText}`)
}
const json = await response.json()
if (json.errors) {
throw new Error(json.errors[0].message)
}
return json.data
}
2025-02-10 01:30:58 +01:00
async function assertSettings () {
const { me } = await gql(`
query me {
me {
id
name
privates {
wildWestMode
satsFilter
}
}
}
`)
console.log(`> logged in as @${me.name}`)
if (!me.privates.wildWestMode) {
throw new Error('wild west mode must be enabled')
}
if (me.privates.satsFilter !== 0) {
throw new Error('sats filter must be set to 0')
}
}
2025-01-19 23:50:16 +01:00
function fetchRecentBios () {
// fetch all recent bios. we assume here there won't be more than 21
// since the last bio we already included in a post as defined by FETCH_AFTER.
return gql(
`query NewBios {
items(sort: "recent", type: "bios", limit: 21) {
items {
id
title
createdAt
user {
name
since
nitems
optional {
stacked
}
}
}
}
}`
)
}
function filterBios (bios) {
const newBios = bios.filter(b => b.id > FETCH_AFTER)
if (newBios.length === bios.length) {
throw new Error('last bio not found. increase limit')
}
return newBios
}
async function populate (bios) {
return await Promise.all(
bios.map(
async bio => {
bio.user.since = await fetchItem(bio.user.since)
bio.user.items = await fetchUserItems(bio.user.name)
bio.user.credits = sumBy(bio.user.items, 'credits')
bio.user.sats = sumBy(bio.user.items, 'sats') - bio.user.credits
2025-02-10 01:39:13 +01:00
if (bio.user.sats > 0 || bio.user.credits > 0) {
bio.user.satstandard = bio.user.sats / (bio.user.sats + bio.user.credits)
} else {
bio.user.satstandard = 0
}
return bio
}
)
)
}
2025-01-19 23:50:16 +01:00
async function printTable (bios) {
console.log('| nym | bio (stacking since) | items | sats/ccs stacked | sat standard |')
console.log('| --- | -------------------- | ----- | ---------------- | ------------ |')
2025-01-19 23:50:16 +01:00
for (const bio of bios) {
const { user } = bio
const bioCreatedAt = formatDate(bio.createdAt)
let col2 = dateLink(bio)
if (Number(bio.id) !== user.since.id) {
const sinceCreatedAt = formatDate(user.since.createdAt)
2025-01-19 23:50:16 +01:00
// stacking since might not be the same item as the bio
// but it can still have been created on the same day
if (bioCreatedAt !== sinceCreatedAt) {
col2 += ` (${dateLink(user.since)})`
2025-01-19 23:50:16 +01:00
}
}
console.log(`| @${user.name} | ${col2} | ${user.nitems} | ${user.sats}/${user.credits} | ${user.satstandard.toFixed(2)} |`)
2025-01-19 23:50:16 +01:00
}
console.log(`${bios.length} rows`)
return bios
}
function formatDate (date) {
return new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
}
function sumBy (arr, key) {
return arr.reduce((acc, item) => acc + item[key], 0)
}
2025-01-19 23:50:16 +01:00
function itemLink (id) {
return `https://stacker.news/items/${id}`
}
function dateLink (item) {
return `[${formatDate(item.createdAt)}](${itemLink(item.id)})`
}
2025-01-19 23:50:16 +01:00
async function fetchItem (id) {
const data = await gql(`
query Item($id: ID!) {
item(id: $id) {
id
createdAt
}
}`, { id }
)
return data.item
}
async function fetchUserItems (name) {
const data = await gql(`
query UserItems($name: String!) {
items(sort: "user", name: $name) {
items {
id
createdAt
sats
credits
}
}
}`, { name }
)
return data.items.items
}
2025-02-10 01:30:58 +01:00
assertSettings()
.then(fetchRecentBios)
2025-01-19 23:50:16 +01:00
.then(data => filterBios(data.items.items))
.then(populate)
2025-01-19 23:50:16 +01:00
.then(printTable)
.catch(console.error)