Apply the same subscription cycle as Fanbox
I must admit that I had to take a few moments to understand how the script works with subscriptions because I use it with my Discord server. After a few months of use, I understood that the script grants a full month's subscription to the user starting from the last transaction date + the days configured in the leeway_days variable in order to make everything reasonable and fair for all users.
The problem is that Fanbox doesn't work this way, and doesn't consider the full month from the last transaction date. If a user pledges at the end of the month, he'll be charged at the beginning of the following month.
In reality, a subscription is only valid if there is a transaction during the current month or a few days at the beginning of the following month (while the Fanbox automatic system renews the user's subscription).
Is it possible to offer this option in this script? I've tried to modify the script so that it follows this logic, but I still have bugs.
I know that theoretically I should give the full month to a user who subscribes to my page, but unfortunately if a subscriber decides not to renew his subscription, he's got what he wanted. There's no need to offer them more time.
Source: https://fanbox.pixiv.help/hc/en-us/articles/360000664842-When-are-pledges-charged
Fanbox's API provides a whole list of currently subscribed users. This list can be checked every day, and if a user is simply not subscribed, access can be removed. This should be able to give you exactly what you want, and you just need to convey to your users to remain subscribed for continued access. Would that be fine?
I've just written the code to do this. Testing it now and I'll probably push the additions soon.
Added in 07220f0. You'll have to add a new parameter to your config (check the config template). Please give it a try.
Fanbox's API provides a whole list of currently subscribed users. This list can be checked every day, and if a user is simply not subscribed, access can be removed. This should be able to give you exactly what you want, and you just need to convey to your users to remain subscribed for continued access. Would that be fine?
Not really, because some people pay once for the month and then unsubscribe straight away, so they're no longer on the fan list but have still paid for the month.
If you unsubscribe immediately on Fanbox, then you lose access on Fanbox immediately, I'm fairly certain 🤔. Anyway I can change it to check if hasSupportTransactionThisMonth is true (from the Fanbox API). I think that would fix it
Actually it's a little more complicated to change. May take a little bit more time.
Thinking about it more, I've been avoiding trying to implement it this way because it would end up causing excessive calls to the Fanbox API, because I would have to ask every day if hasSupportTransactionThisMonth is true, and this has to be done for every user (one call per user legacy/manage/supporter/user; this is the call that is made when you click on an individual user on your fan list page). I think that it ends up causing more churn and more API calls, and I worry that too many calls at once will make Fanbox restrict your account for strange activity. Conversely, making the relationship.getFan API call once per day is a completely reasonable call frequency (this is the call that populates your fan list page: https://www.fanbox.cc/manage/relationships). Implementing it in the way you describe would have been a non-issue if the fan list call relationship.getFan would have all of the relevant information in it, but alas, it does not 😔. I hope that this is a reasonable explanation of why I wanted to implement it in the way that I did above.
I think if you just explain to your users to "stay subscribed to retain access", then it should be easy for them to understand. Then it's on the user to unsubscribe when they have gathered what they want, or at the end of the month. I do not think it costs anything for a user to re-subscribe in the same month to the same plan either. The yen difference should be zero (since the cost of changing plan levels in the same month charges the difference between plans, only if the next plan subscribed to costs more).
Ok. First of all, I'd like to express my gratitude for the development of this script, which is very useful when you need to make the link between Discord and Fanbox, just as it's possible to make the link between Discord and Patreon. It's a genius idea.
I'd also like to thank you for adding the parameter only_check_current_sub, very useful but in my case I won't be able to use it.
I've found a workaround, in fact here's the complete main.py code. Based on the article here, which stipulates the period between the first and fifth of the month: https://fanbox.pixiv.help/hc/en-us/articles/360000664842-When-are-pledges-charged
In the compute_plan_id function, if the current date is between the first of the month and the fifth of the month (here the leeway_days parameter), I check whether there is a transaction in the current month or the previous month. But after the leeway_date, I simply check for transactions in the current month.
Since we're already loading the user_data, I check if the user has a supportingPlan, and if not, I execute the complute_plan_id function to check transactions in the selected period.
It's complex, but it really depends on Fanbox's pledge system.
Okay I see now, I think your idea might work fine. If you can post a diff instead of the whole code (or fork this repository and make a pull request), that would help me integrate your changes in and add configs options for it.
Also it's important to note that get_fanbox_user_data will grab cached data stored in the database, instead of grabbing the latest user data. It only grabs the latest when a user makes an access request, or if it seems like their subscription has expired (and the user still has a role).
Alright, I've integrated your PR in at 0e2ec04 and did some local tests to verify its behavior. The behavior is as you had implemented it, except for some minor cleanup that I did. I added a config option to enable this behavior only_check_recent_txns.
I had to remove this from compute_role because this incorrectly assumes that user_data is fresh:
if user_data.get('supportingPlan'):
plan_id = user_data['supportingPlan']['id']
Basically, compute_plan_id may be called twice when the auto_role_update process is running. The first time to check if we might be in a stale state, and then the second time after we have refreshed to the most recent user_data from Fanbox. With the code above, a user will end up with access indefinitely because we might skip the first attempt to compute_plan_id and never again refresh user_data. The reason I do this confusing stale data stuff is again for reducing the amount of calls to the Fanbox API.
I tested the script and it works, but I have a problem for one user only and his situation is special. He became a member of my page on April 30, 2024 and for some reason, at the moment he still has a Fan status but the transaction is from last month, so the script removes his role.
What I've understood is that if we check the user_data and check if there's a supportingPlan after checking the transactions, we may end up in a loop where a user will end up with access indefinitely because the user_data will never be refreshed?
Okay, we can do that, after the logic determines that there are no transactions in this month, then do one more final check with the fresh data to see if supportingPlan is set and use that as an override. The trick here is where exactly you have to make this check. I'll fix that up shortly.
The caveat is that this will cause this person to be checked every day of the month, and it would basically work like only_check_current_sub, so the day they unsubscribe, they will lose access.
60f4f11 should fix this issue now. Your user who had his role removed will have to send his ID to the bot again and he should get his access back.
Thanks for everything! Everything works now. Much appreciated. Congratulations again on a job well done.