Duolingo icon indicating copy to clipboard operation
Duolingo copied to clipboard

buy_streak_freeze() always buys a new streak

Open andreasscherbaum opened this issue 4 years ago • 19 comments

Started a couple days ago: every time I run buy_streak_freeze(), I don't get a AlreadyHaveStoreItemException exception anymore, but it started "charging" me Lingots every time - even though I already have a streak equipped.

Anyone knows what's wrong there, or how to fix/prevent that?

andreasscherbaum avatar Aug 18 '20 14:08 andreasscherbaum

Looks like it's not a bug, but an expected behaviour, there is a different response now:

{
  "purchaseId": "d67b790c25fd2203c4ee36617f6141a1",
  "purchaseDate": 1597765436,
  "purchasePrice": 10,
  "id": "streak_freeze",
  "itemName": "streak_freeze",
  "quantity": 3
}

The quantity is just increasing, I assume it's like buying "in advance".

I think getting /2017-06-30/users/{userID}?fields=shopItems and checking for streak_freeze quantity before initiating a purchase is a solution.

igorskh avatar Aug 18 '20 15:08 igorskh

So I can buy streaks in advance for as long as I have Lingots? ;-)

Hmmm

andreasscherbaum avatar Aug 18 '20 16:08 andreasscherbaum

Well, it's still not what I expect buy_streak_freeze() to do ...

andreasscherbaum avatar Aug 18 '20 16:08 andreasscherbaum

So I can buy streaks in advance for as long as I have Lingots? ;-)

I suppose so. I mean, why not? It might be useful in some cases, one can plan few days away in advance. Although, I am not sure if it works this way, it is just my assumption.

Well, it's still not what I expect buy_streak_freeze() to do ...

In my opinion it does what it says. It buys a streak freeze :) You can think about some additional method like restore_streak_freeze(), but it's some additional feature, not API related.

The question is more to Duolingo API developers rather to this library.

igorskh avatar Aug 18 '20 16:08 igorskh

Ok, i've tested it more. It does not go more than 3 purchased items, although lingots are charged..

igorskh avatar Aug 18 '20 16:08 igorskh

@igorskh So basically "quantity" is only known after a purchase?

andreasscherbaum avatar Aug 19 '20 12:08 andreasscherbaum

@igorskh So basically "quantity" is only known after a purchase?

No, it's not, you can always check current quantity here: https://www.duolingo.com/2017-06-30/users/{userID}?fields=shopItems

It returns all purchased items including streak_freeze :

[
    {
      "quantity": 3,
      "purchaseDate": 1597768970,
      "id": "streak_freeze",
      "purchaseId": "2abbdba95ae154068f9202a37529e141",
      "itemName": "streak_freeze",
      "purchasePrice": 0
    }
]

igorskh avatar Aug 19 '20 14:08 igorskh

Looking at the documentation for this library, I don't see a method to retrieve that information. Am I correct?

https://github.com/KartikTalwar/Duolingo

andreasscherbaum avatar Aug 20 '20 15:08 andreasscherbaum

I can confirm the behavior of the API has changed. It buys the streak regardless of whether or not you already have one purchased and deducts additional lingots. It does not appear to give you extra "freezes", just burns through lingots faster. I suggest adding a get_streak_freeze() method that returns True or False if the streak is active (assuming that's possible) or something similar.

efodor avatar Aug 20 '20 21:08 efodor

I've a workaround until the API/library is fixed -

def item_already_equipped(lingo, item):
    if item == 'streak_freeze':
        return lingo.__dict__['user_data'].__dict__['tracking_properties']['num_item_streak_freeze'] > 0
    if item == 'rupee_wager':
        return lingo.__dict__['user_data'].__dict__['tracking_properties']['has_item_rupee_wager']

def process_single_user(username, password):
    import duolingo
    try:
        lingo = duolingo.Duolingo(username, password)
    except ValueError:
        raise Exception("Username Invalid")

    stuff_to_purchase = ['streak_freeze', 'rupee_wager']

    for item in stuff_to_purchase:
        if(item_already_equipped(lingo, item)):
            print("Item "+ item + " already equipped! Skipping...")
            continue
        try:
            print("Trying to Buy " + item + " for " + username)
            lingo.buy_item(item, 'en')
            print("Bought " + item + " for " + username)
        except duolingo.AlreadyHaveStoreItemException: # no longer triggered AFAIK
            print("Item Already Equipped")
        except Exception:
            raise ValueError("Unable to buy " + item)

alexsanjoseph avatar Aug 21 '20 19:08 alexsanjoseph

Did anyone test that quantity of streak_freeze decreases after being used?

I am not entirely sure, but I think I could increase it from 1 to 3.

igorskh avatar Aug 21 '20 19:08 igorskh

Planning to keep my streak on line today for testing. Will report back if it reduces to less than 3

alexsanjoseph avatar Aug 21 '20 19:08 alexsanjoseph

I can confirm that quantity increases

> GET /2017-06-30/users/665585457?fields=shopItems
> Host: www.duolingo.com
> user-agent: insomnia/2020.3.3

< HTTP/2 200 
< date: Fri, 21 Aug 2020 19:20:39 GMT
< content-type: application/json
{
  "shopItems": [
    {
      "quantity": 1,
      "purchaseDate": 1598037464,
      "id": "streak_freeze",
      "itemName": "streak_freeze",
      "purchasePrice": 0
    }
  ]
}


> POST /2017-06-30/users/665585457/shop-items HTTP/2
> Host: www.duolingo.com
> user-agent: insomnia/2020.3.3
| {
| 	"itemName": "streak_freeze",
| 	"learningLanguage": "es"
| }

< HTTP/2 200 
< date: Fri, 21 Aug 2020 19:44:30 GMT
< content-type: application/json
{
  "purchaseDate": 1598039070,
  "purchasePrice": 10,
  "id": "streak_freeze",
  "itemName": "streak_freeze",
  "quantity": 2
}


> GET /2017-06-30/users/665585457?fields=shopItems
> Host: www.duolingo.com
> user-agent: insomnia/2020.3.3

< HTTP/2 200 
< date: Fri, 21 Aug 2020 19:47:19 GMT
< content-type: application/json
{
  "shopItems": [
    {
      "quantity": 2,
      "purchaseDate": 1598039070,
      "id": "streak_freeze",
      "itemName": "streak_freeze",
      "purchasePrice": 0
    }
  ]
}

igorskh avatar Aug 21 '20 19:08 igorskh

From what I see, this is also not returned anymore:

https://github.com/KartikTalwar/Duolingo/blob/6d3e770f0987d3de370519aae030e4bf6fb11984/duolingo.py#L182-L183

It just doesn't return anything now, and script crashes

simplejson.errors.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

igorskh avatar Aug 21 '20 20:08 igorskh

Quantity is decreasing as well, so it makes sense to allow buying more than 1 streak, even current limit to 3 is maybe just a bug in the API, or they are testing, at the end it is not an official feature.

igorskh avatar Aug 23 '20 05:08 igorskh

It appears that if you purchase multiple freezes, the freeze is extended for multiple days. My current quantity is now at 2 (was 3 yesterday), but the UI still showed the freeze in place. It looks like they might be adding a multi day streak feature (or at least it appears to be a hidden feature).

efodor avatar Aug 25 '20 16:08 efodor

@alexsanjoseph how can we use your work around. Could you please explain where we need to paste it and what we need to call to make it happen.

TiloGit avatar Aug 31 '20 20:08 TiloGit

@TiloGit - I run this as a Lambda function in AWS (https://github.com/alexsanjoseph/duolingo-save-streak).

The issue is that Duolingo keeps buying streak freeze even when it has reached the limit. Before this was enforced as a limit from their API and hence we could do EAFP and catch the error without an issue. However, the lingo class already has info on whether to buy it or not, and so you purchase it only when you check and identify that you don't have the particular item in your bag.

def item_already_equipped(lingo, item):
    if item == 'streak_freeze':
        return lingo.__dict__['user_data'].__dict__['tracking_properties']['num_item_streak_freeze'] > 0
    if item == 'rupee_wager':
        return lingo.__dict__['user_data'].__dict__['tracking_properties']['has_item_rupee_wager']

This function does the checking.

alexsanjoseph avatar Sep 01 '20 05:09 alexsanjoseph

here my hack for other ppl info. (not pretty but seem to do the job) https://gist.github.com/TiloGit/51367aca5a4df726a7061288d8430828

TiloGit avatar Sep 01 '20 16:09 TiloGit