diff --git a/lib/webPush.js b/lib/webPush.js index 2a5e9cca4..e8bf06832 100644 --- a/lib/webPush.js +++ b/lib/webPush.js @@ -2,6 +2,7 @@ import webPush from 'web-push' import removeMd from 'remove-markdown' import { COMMENT_DEPTH_LIMIT, FOUND_BLURBS, LOST_BLURBS } from './constants' import { msatsToSats, numWithUnits } from './format' +import { nextBillingWithGrace } from '@/lib/territory' import models from '@/api/models' import { isMuted } from '@/lib/user' import { Prisma } from '@prisma/client' @@ -103,6 +104,7 @@ async function sendUserNotification (userId, notification) { const subscriptions = await models.pushSubscription.findMany({ where: { userId, ...userFilter } }) + console.log('notification', payload) await Promise.allSettled( subscriptions.map(subscription => sendNotification(subscription, payload)) ) @@ -373,16 +375,49 @@ export const notifyTerritoryTransfer = async ({ models, sub, to }) => { } } +export const notifyTerritoryStatusChange = async ({ sub }) => { + const dueDate = nextBillingWithGrace(sub) + const days = Math.ceil((new Date(dueDate) - new Date()) / (1000 * 60 * 60 * 24)) + const timeLeft = days === 1 ? 'tomorrow' : `in ${days} days` + const title = sub.status === 'ACTIVE' + ? 'your territory is active again' + : sub.status === 'GRACE' + ? `your territory payment for ~${sub.name} is due or your territory will be archived ${timeLeft}` + : `~${sub.name} has been archived!` + + try { + await sendUserNotification(sub.userId, { title, tag: `TERRITORY_STATUS_CHANGE-${sub.name}` }) + } catch (err) { + console.error(err) + } +} + +export const notifyTerritoryRevenue = async (subAct) => { + const fmt = msats => numWithUnits(msatsToSats(msats, { abbreviate: false })) + const title = `you earned ${fmt(subAct.msats)} in revenue from ~${subAct.subName}` + try { + await sendUserNotification(subAct.userId, { title, tag: `TERRITORY_REVENUE-${subAct.subName}` }) + } catch (err) { + console.error(err) + } +} + +// TODO: needs testing, fix rewards not working first export async function notifyEarner (userId, earnings) { const fmt = msats => numWithUnits(msatsToSats(msats, { abbreviate: false })) + // TODO: remove + console.log('notifying earners', JSON.stringify(earnings, null, 2)) + const title = `you stacked ${fmt(earnings.msats)} in rewards` const tag = 'EARN' let body = '' if (earnings.POST) body += `#${earnings.POST.bestRank} among posts with ${fmt(earnings.POST.msats)} in total\n` if (earnings.COMMENT) body += `#${earnings.COMMENT.bestRank} among comments with ${fmt(earnings.COMMENT.msats)} in total\n` if (earnings.TIP_POST) body += `#${earnings.TIP_POST.bestRank} in post zapping with ${fmt(earnings.TIP_POST.msats)} in total\n` - if (earnings.TIP_COMMENT) body += `#${earnings.TIP_COMMENT.bestRank} in comment zapping with ${fmt(earnings.TIP_COMMENT.msats)} in total` + if (earnings.TIP_COMMENT) body += `#${earnings.TIP_COMMENT.bestRank} in comment zapping with ${fmt(earnings.TIP_COMMENT.msats)} in total\n` + if (earnings.FOREVER_REFERRAL) body += `#${earnings.FOREVER_REFERRAL.bestRank} in referral rewards with ${fmt(earnings.FOREVER_REFERRAL.msats)} in total\n` + if (earnings.ONE_DAY_REFERRAL) body += `#${earnings.ONE_DAY_REFERRAL.bestRank} in referral rewards with ${fmt(earnings.ONE_DAY_REFERRAL.msats)} in total` try { await sendUserNotification(userId, { title, tag, body }) diff --git a/worker/territory.js b/worker/territory.js index 3aa386f96..9d859bc5d 100644 --- a/worker/territory.js +++ b/worker/territory.js @@ -3,9 +3,10 @@ import performPaidAction from '@/api/paidAction' import { PAID_ACTION_PAYMENT_METHODS } from '@/lib/constants' import { nextBillingWithGrace } from '@/lib/territory' import { datePivot } from '@/lib/time' +import { notifyTerritoryStatusChange, notifyTerritoryRevenue } from '@/lib/webPush' export async function territoryBilling ({ data: { subName }, boss, models }) { - const sub = await models.sub.findUnique({ + let sub = await models.sub.findUnique({ where: { name: subName } @@ -13,7 +14,7 @@ export async function territoryBilling ({ data: { subName }, boss, models }) { async function territoryStatusUpdate () { if (sub.status !== 'STOPPED') { - await models.sub.update({ + sub = await models.sub.update({ include: { user: true }, where: { name: subName @@ -24,7 +25,8 @@ export async function territoryBilling ({ data: { subName }, boss, models }) { } }) } - + // send push notification with the new status + await notifyTerritoryStatusChange({ sub }) // retry billing in one day await boss.send('territoryBilling', { subName }, { startAfter: datePivot(new Date(), { days: 1 }) }) } @@ -44,6 +46,9 @@ export async function territoryBilling ({ data: { subName }, boss, models }) { }) if (!result) { throw new Error('not enough fee credits to auto-renew territory') + } else if (sub.status === 'GRACE' && result.status === 'ACTIVE') { + // if the sub was in grace and we successfully auto-renewed it, send a push notification + await notifyTerritoryStatusChange({ sub }) } } catch (e) { console.error(e) @@ -90,4 +95,17 @@ export async function territoryRevenue ({ models }) { "stackedMsats" = users."stackedMsats" + "SubActResultTotal".total_msats FROM "SubActResultTotal" WHERE users.id = "SubActResultTotal"."userId"` + + const territoryRevenue = await models.subAct.findMany({ + where: { + createdAt: { // retrieve revenue calculated in the last hour + gte: datePivot(new Date(), { hours: -1 }) + }, + type: 'REVENUE' + } + }) + + await Promise.allSettled( + territoryRevenue.map(subAct => notifyTerritoryRevenue(subAct)) + ) }