get_manually_billed_users_details.mjs 2.7 KB

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