antfu-collective/sponsorkit

bug(opencollective): `monthlyDollars` and `isOneTime` are not correct

robingenz opened this issue Β· 13 comments

Describe the bug

First of all, thank you very much for this great project!

I am trying to retrieve the sponsorship details of this collective: https://opencollective.com/capawesome

The following two properties of the retrieved sponsorships are often incorrect: isOneTime and monthlyDollars.

Example 1:

Transaction: https://opencollective.com/capawesome/contributions/660052

The transaction was executed once (non-recurring). However, I receive the following values:

{
  "sponsor": {
    "name": "CTS Software",
    "type": "User",
    "login": "guest-2cf739c1",
    "avatarUrl": "https://images.opencollective.com/guest-2cf739c1/avatar/460.png",
    "websiteUrl": null,
    "linkUrl": "https://opencollective.com/guest-2cf739c1",
    "avatarUrlHighRes": "...",
    "avatarUrlMediumRes": "...",
    "avatarUrlLowRes": "..."
  },
  "isOneTime": false,
  "monthlyDollars": 5000,
  "privacyLevel": "PUBLIC",
  "tierName": "⚑ Custom Plugin",
  "createdAt": "2023-06-01T15:04:33.286Z",
  "provider": "opencollective"
}

isOneTime has the value false, but should be true.

Example 2:

Transactions:

This sponsor contributes 300$ per month and has contributed twice so far. However, I receive the following values:

{
  "sponsor": {
    "name": "AppScreens",
    "type": "User",
    "login": "appscreens",
    "avatarUrl": "https://images.opencollective.com/appscreens/62776aa/avatar/460.png",
    "websiteUrl": "https://appscreens.com/",
    "linkUrl": "https://opencollective.com/appscreens",
    "avatarUrlHighRes": "...",
    "avatarUrlMediumRes": "...",
    "avatarUrlLowRes": "..."
  },
  "isOneTime": true,
  "monthlyDollars": 600,
  "privacyLevel": "PUBLIC",
  "tierName": "",
  "createdAt": "2023-09-17T22:16:49.416Z",
  "provider": "opencollective"
}

monthlyDollars has the value 600, but should have 300.

Reproduction

https://github.com/capawesome-team/static

System Info

System:
    OS: macOS 14.0
    CPU: (10) arm64 Apple M1 Pro
    Memory: 136.63 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.18.2 - /opt/homebrew/opt/node@18/bin/node
    npm: 9.8.1 - /opt/homebrew/opt/node@18/bin/npm
    pnpm: 8.10.2 - /opt/homebrew/bin/pnpm
  Browsers:
    Brave Browser: 96.1.32.115
    Chrome: 119.0.6045.159
    Safari: 17.0

Used Package Manager

npm

Validations

  • Follow our Code of Conduct
  • Read the Contributing Guide.
  • Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • Check that this is a concrete bug. For Q&A, please open a GitHub Discussion instead.
  • The provided reproduction is a minimal reproducible of the bug.
antfu commented

I don't use OC myself. It would be great if you can directly look into it and send PRs. Thanks!

All right, I'll have a look at it.

Hello I am looking into this issue.

I have made a fork of the repo isbecker/sponsorkit.

opencollective.ts contains the GraphQL query that gets the information from OC API.

I checked the OC API docs and I noticed that the Tier type, has a frequency field.

So I am fairly certain that adding that field into the query that the opencollective.ts file is making (makeQuery), that at least, when the Tier is not a null value, the frequency field will properly show the value of ONETIME in the case when a sponsor was a one-time donor.

I think that the logic in isOneTime is a bit buggy as is right now.

I have made a commit on my fork: isbecker@5191dc7, feel free to take a look πŸ™‚

Unfortunately, I have not had time to test the changes in the codebase, as I have not done the necessary set up yet. I was looking through the contribution guide and I have not gone through it all yet.

However, I did test the frequency query on the capawesome collective to verify that it does work. There are some oddities, though.

One such issue, the monthlyDollars calculation does not seem straightforward to me yet, based on the OC API docs that I read through.

I will try to take a look at getting things set up to run sponsorkit on my machine so that I can fiddle more.

I made a bit of progress on getting things running locally so that I can test and also a bit of progress on getting the correct data for monthlyDollars.

Need to take a break, but I hope to have something ready soon.

@robingenz here is a PNG generated with my locally modified code. This contains just the OC sponsors.
image

I seem to have some issue with the GitHub sponsors not correctly getting the tier/monthly amount, probably due to the way I have my GH token set up or something else that I have misconfigured; I don't think the GH sponsors are important to show this progress anyway.

Please let me know how it looks πŸ™‚. I have more work to do to clean things up, but hopefully I am on the right track here.

@isbecker Great progress!

I seem to have some issue with the GitHub sponsors not correctly getting the tier/monthly amount, probably due to the way I have my GH token set up or something else that I have misconfigured; I don't think the GH sponsors are important to show this progress anyway.

Yeah, that's fine. The GH sponsors integration is working without problems.

Please let me know how it looks πŸ™‚. I have more work to do to clean things up, but hopefully I am on the right track here.

This looks really good so far. The only thing I notice is that sponsors are also listed who have only sponsored once (e. g. https://opencollective.com/capawesome/contributions/660052) in the past. These should only be displayed if it is the current month in which the one-time amount was sponsored. For example, a sponsor who sponsored once a year ago should not still be shown. But I'm not sure how hard it is to implement this.
Also, I would like to be able to filter these out using isOneTime. So isOneTime should just have the correct value. From the screenshot I don't see whether this already works.

@isbecker Great progress!

Thanks πŸ™

These should only be displayed if it is the current month in which the one-time amount was sponsored.

There is a config option config.includePastSponsors which I think should govern this. I will adopt that (the GitHub provider uses it currently).

Also, I would like to be able to filter these out using isOneTime. So isOneTime should just have the correct value. From the screenshot I don't see whether this already works.

I believe that isOneTime is functioning correctly now. I will post an example when I'm back at my computer.


edit

Here's an example of the sponsor info coming out of the opencollective.ts code. I guess this gets further enriched by later steps in the sponsorkit process, because it seems to missing a few things, so please ignore that πŸ˜‰.

{ 
  "sponsor": {
    "name": "CTS Software",
    "type": "User",
    "login": "guest-2cf739c1",
    "avatarUrl": "https://images.opencollective.com/guest-2cf739c1/avatar/460.png",
    "linkUrl": "https://opencollective.com/guest-2cf739c1"
  },
  "isOneTime": true,
  "monthlyDollars": 5000,
  "privacyLevel": "PUBLIC",
  "tierName": "⚑ Custom Plugin"
}

This brings up a point I wanted to ask about. monthlyDollars does not seem relevant for a one-time donation. @antfu what do you think the proper way of representing that should be? Perhaps make a new field in the Sponsorhip interface?

I guess the way that GitHub sponsors works, people are either sponsoring right now or not. I'm not sure, but I suspect the OC API has a lot more info available.

edit Actually, I think the one edge case that monthlyDollars does not cover is a yearly sponsorship. What should the logic be for that case?

Here is what the list of sponsors for capawesome collective looks like, if we just query for the current month. I guess this is in the same spirit as the way that GitHub sponsors works, because that is by default only the current sponsors.

[
  {
    "sponsor": {
      "name": "AppScreens",
      "type": "User",
      "login": "appscreens",
      "avatarUrl": "https://images.opencollective.com/appscreens/62776aa/avatar/460.png",
      "websiteUrl": "https://appscreens.com/",
      "linkUrl": "https://opencollective.com/appscreens"
    },
    "isOneTime": false,
    "monthlyDollars": 300,
    "privacyLevel": "PUBLIC",
    "createdAt": "2023-12-02T13:04:49.224Z"
  },
  {
    "sponsor": {
      "name": "NFC21",
      "type": "Organization",
      "login": "nfc21",
      "avatarUrl": "https://images.opencollective.com/nfc21/9d1232a/logo/460.png",
      "websiteUrl": "https://nfc21.de",
      "linkUrl": "https://opencollective.com/nfc21"
    },
    "isOneTime": false,
    "monthlyDollars": 50,
    "privacyLevel": "PUBLIC",
    "createdAt": "2023-12-01T17:10:56.410Z"
  },
  {
    "sponsor": {
      "name": "Colorful Casting",
      "type": "Organization",
      "login": "colorful-casting",
      "avatarUrl": "https://images.opencollective.com/colorful-casting/4e2c691/logo/460.png",
      "websiteUrl": "https://colorfulcast.com",
      "linkUrl": "https://opencollective.com/colorful-casting"
    },
    "isOneTime": true,
    "monthlyDollars": 900,
    "privacyLevel": "PUBLIC",
    "createdAt": "2023-12-01T15:18:44.698Z"
  }
]

So for capawesome collective, this is what the current month's sponsor would look like

image

@isbecker These are the sponsors of the current month but it’s missing the sponsors who sponsor monthly but have not yet sponsored this month (e.g. https://opencollective.com/capawesome/contributions/625520).

These are the sponsors of the current month but it’s missing the sponsors who sponsor monthly but have not yet sponsored this month

πŸ€” you're right.

I think there are multiple queries that need to happen in order to get everything correctly, which is going to make it a little more complex than what I've been attempting.

I am learning some things about GraphQL!

Question

The current code for querying OpenCollective supports 2 modes of operation:

  • collective
  • account (aka "user" or "individual")

I am struggling to understand the use-case that would require this code to work on an "account" basis,
because Open Collective, I think, does not support donating to individuals. Sponsors can only sponsor
a "collective". At least, that seems to be what my research has determined on the matter.

❓ Is it possible to remove that account-based query functionality and switch to only supporting
queries on collectives?

I think those are the only ones that make any sense for OC anyway.

I am struggling to understand the use-case that would require this code to work on an "account" basis,
because Open Collective, I think, does not support donating to individuals. Sponsors can only sponsor
a "collective". At least, that seems to be what my research has determined on the matter.

I was also confused by that. But i found this PR: #32
I'm not sure whether this issue affects accounts at all or only collectives. In any case, my issue only refers to collectives.

Since "collective' objects are also "account" objects, I think I can simplify the query somewhat by only querying accounts.

Anyway, I made some changes to the querying and here is the result. Notably, https://opencollective.com/capawesome/contributions/625520 shows up (and won't have a transaction for this order until the 16th).
image

I think this one is correct. I need to check some other collectives and make sure the use-case brought up in #32 still works

I think this one is correct.

Yes, it looks like you've made it! I am looking forward to your PR.