import config from "./config.ts"; import { getUserByEmail } from "./utils/firebase.ts"; import { insertObject, newPostgres, updateObject } from "./utils/postgres.ts"; import { getAllActiveSubscriptions, getAllCustomers } from "./utils/stripe.ts"; import { Client as DiscordClient, IntentsBitField } from "discord.js"; let lastSubs = ""; let currentSubs = ""; const postgres2 = newPostgres(); // set up the Discord admin bot const discordBot = new DiscordClient({ intents: [IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMembers], }); if (config.DISCORD_ADMIN_BOT_TOKEN) { discordBot.login(config.DISCORD_ADMIN_BOT_TOKEN); // discordBot.once('ready', () => { // console.log(`Discord Bot "${discordBot?.user?.username}" ready`); // }); } if (process.env.NODE_ENV === "development") { setTimeout(syncSubscribers, 1000); } setInterval(syncSubscribers, 60 * 1000); async function syncSubscribers() { if (!config.STRIPE_SECRET_KEY || !config.FIREBASE_ADMIN_SDK_CONFIG) { return; } console.time("syncSubscribers"); // Fetch subs, customers from stripe const [subs, customers] = await Promise.all([ getAllActiveSubscriptions(), getAllCustomers(), ]); const emailMap = new Map(); customers.forEach((cust) => { emailMap.set(cust.id, cust.email); }); console.log("%s subs in Stripe", subs.length); const uidMap = new Map(); for (let i = 0; i < subs.length; i += 50) { // Batch customers and fetch firebase data const batch = subs.slice(i, i + 50); const fbUsers = await Promise.all( batch .map((sub) => emailMap.get(sub.customer) ? getUserByEmail(emailMap.get(sub.customer)) : null, ) .filter(Boolean), ); fbUsers.forEach((user) => { uidMap.set(user?.email, user?.uid); }); } let noUID = 0; // Create sub objects let result = subs .map((sub) => { let uid = uidMap.get(emailMap.get(sub.customer)); if (!uid) { uid = emailMap.get(sub.customer); noUID += 1; } return { customerId: sub.customer, email: emailMap.get(sub.customer), status: sub.status, uid, }; }) .filter((sub) => sub.uid); console.log("%s subs to insert", result.length); console.log("%s subs do not have UID, using email", noUID); const newResult = result.filter( (sub, index) => index === result.findIndex((other) => sub.uid === other.uid), ); console.log("%s deduped subs to insert", newResult.length); if (result.length !== newResult.length) { // Log the difference console.log(result.filter((x) => !newResult.includes(x))); } result = newResult; currentSubs = result .map((sub) => sub.uid) .sort() .join(); // Upsert to DB // console.log(result); if (currentSubs !== lastSubs) { try { await postgres2?.query("BEGIN TRANSACTION"); await postgres2?.query("DELETE FROM subscriber"); await postgres2?.query('UPDATE room SET "isSubRoom" = false'); for (let row of result) { await insertObject(postgres2, "subscriber", row); await updateObject( postgres2, "room", { isSubRoom: true }, { owner: row.uid }, ); } await postgres2?.query("COMMIT"); lastSubs = currentSubs; } catch (e) { console.error(e); await postgres2?.query("ROLLBACK"); } } if ( discordBot.isReady() && config.DISCORD_ADMIN_BOT_SERVER_ID && config.DISCORD_ADMIN_BOT_SUB_ROLE_ID ) { console.log("setting discord roles"); // Update the sub status of users in Discord // Join the current subs with linked accounts const guild = discordBot.guilds.cache.get( config.DISCORD_ADMIN_BOT_SERVER_ID, ); const role = guild?.roles.cache.get(config.DISCORD_ADMIN_BOT_SUB_ROLE_ID); const toUpdate = ( await postgres2.query( `SELECT la.accountid from subscriber JOIN link_account la ON subscriber.uid = la.uid WHERE la.kind = 'discord'`, ) ).rows; console.log("%s users to set sub role", toUpdate.length); for (let row of toUpdate) { try { const user = await guild?.members.fetch(row.accountid); if (user && role) { console.log("assigning role %s to user %s", role, user.id); await user.roles.add(role); } } catch (e: any) { console.log(e.message); } } } console.timeEnd("syncSubscribers"); }