get_manually_billed_users_details.mjs 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import Settings from '@overleaf/settings'
  2. import recurly from 'recurly'
  3. import fs from 'node:fs'
  4. import { setTimeout } from 'node:timers/promises'
  5. import minimist from 'minimist'
  6. import * as csv from 'csv'
  7. import Stream from 'node:stream/promises'
  8. import { scriptRunner } from '../lib/ScriptRunner.mjs'
  9. const recurlyApiKey = Settings.apis.recurly.apiKey
  10. if (!recurlyApiKey) {
  11. throw new Error('Recurly API key is not set in the settings')
  12. }
  13. const client = new recurly.Client(recurlyApiKey)
  14. function usage() {
  15. console.error(
  16. 'Script to retrieve details of manually billed users from Recurly'
  17. )
  18. console.error('')
  19. console.error('Usage:')
  20. console.error(
  21. ' node scripts/recurly/get_manually_billed_users_details.mjs [options]'
  22. )
  23. console.error('')
  24. console.error('Options:')
  25. console.error(
  26. ' --input, -i <file> Path to CSV file containing subscription_id, period_end, currency (can be exported from Recurly)'
  27. )
  28. console.error(' --output, -o <file> Path to output CSV file')
  29. console.error('')
  30. console.error('Input format:')
  31. console.error(
  32. ' CSV file with the following columns: subscription_id, period_end, currency (header row is skipped)'
  33. )
  34. }
  35. function parseArgs() {
  36. return minimist(process.argv.slice(2), {
  37. alias: { i: 'input', o: 'output' },
  38. string: ['input', 'output'],
  39. })
  40. }
  41. async function enrichRow(row) {
  42. const account = await client.getAccount(`code-${row.account_code}`)
  43. return {
  44. ...row,
  45. email: account.email,
  46. first_name: account.firstName,
  47. last_name: account.lastName,
  48. cc_emails: account.ccEmails,
  49. }
  50. }
  51. async function main() {
  52. const { input: inputPath, output: outputPath, h, help } = parseArgs()
  53. if (help || h || !inputPath || !outputPath) {
  54. usage()
  55. process.exit(0)
  56. }
  57. let processedCount = 0
  58. await Stream.pipeline([
  59. fs.createReadStream(inputPath),
  60. csv.parse({ columns: true }),
  61. async function* (rows) {
  62. for await (const row of rows) {
  63. try {
  64. yield await enrichRow(row)
  65. } catch (error) {
  66. console.error(`Error processing subscription ${row.subscription_id}`)
  67. }
  68. processedCount++
  69. if (processedCount % 1 === 0) {
  70. console.log(`Processed ${processedCount} subscriptions`)
  71. }
  72. await setTimeout(1000)
  73. }
  74. },
  75. csv.stringify({
  76. header: true,
  77. columns: {
  78. subscription_id: 'subscription_id',
  79. current_period_ends_at: 'period_end',
  80. currency: 'currency',
  81. email: 'email',
  82. first_name: 'first_name',
  83. last_name: 'last_name',
  84. cc_emails: 'cc_emails',
  85. },
  86. }),
  87. fs.createWriteStream(outputPath),
  88. ])
  89. console.log(`Processed ${processedCount} subscriptions in total`)
  90. }
  91. try {
  92. await scriptRunner(main)
  93. process.exit(0)
  94. } catch (error) {
  95. console.error(error)
  96. process.exit(1)
  97. }