Twitch-Channel-Points-Miner-v2 icon indicating copy to clipboard operation
Twitch-Channel-Points-Miner-v2 copied to clipboard

Auto claim Moments

Open willvisentini opened this issue 3 years ago • 17 comments

I would like to ask for an enhancement to auto-claim moments.

willvisentini avatar Feb 22 '22 02:02 willvisentini

https://help.twitch.tv/s/article/moments

willvisentini avatar Feb 22 '22 02:02 willvisentini

Great idea! I'm really looking forward to the implementation.

LeoAlt8 avatar Feb 22 '22 03:02 LeoAlt8

I think that this feature deserves a higher priority because of how scarce Twitch moments are. This is definitely open to debate, though.

overlisted avatar Apr 09 '22 20:04 overlisted

Hope this can be done, that would be awesome.

K31X avatar May 08 '22 14:05 K31X

+1, would be better to autoclaim twitch moments if possible, sometimes you tab out for a few seconds and you lose the ability to claim them

KillaBoi avatar Jun 01 '22 19:06 KillaBoi

So I'm extremely keen on implementing this myself, and have started digging into how Moments are received and claimed.

I'm fairly certain that Moments come from the PubSub API by listening to the community-moments-channel-v1 topic.

They are then later claimed through the GQL API using the following mutation:

mutation CommunityMomentCallout_Claim($input: ClaimCommunityMomentInput!) {
  claimCommunityMoment(input: $input) {
    moment {
      id
    }
    error
  }
}

Now I'm missing a few pieces here - (1) the schema for ClaimCommunityMomentInput, and (2) the structure of the messages that come from the community-moments-channel-v1 topic.

Any of the following will be really helpful in helping me implement this:

  1. Give me the names of some channel that frequently create Moments
  2. Example requests/responses from the GQL claim API (this can be retrieved from the Chrome network tab right before hitting the claim button)

Or even better, if anyone here personally owns a channel with Moments - please reach out to me, I just need to capture the data for one generated Moment.

chowder avatar Aug 29 '22 00:08 chowder

For the PubSub topic I came to the same conclusion a month ago. It's probably the right direction. However I wasn't able to capture any packets. As this action is limited to a few per months, it's hard to predict when this happens. I though of modifying the bot to listen that topic and log whatever arrives but I don't know if it'll be enough. We also need the claiming part (you at least found the mutation, that's a good step I guess) which first has to be done in the browser and so can't really be automated.

Rakambda avatar Aug 29 '22 06:08 Rakambda

So I was really lucky yesterday and managed to capture the GQL request to redeem a Moment:

[
  {
    "operationName": "CommunityMomentCallout_Claim",
    "variables": {
      "input": {
        "momentID": $MOMENT_ID
      }
    },
    "extensions": {
      "persistedQuery": {
        "version": 1,
        "sha256Hash": "e2d67415aead910f7f9ceb45a77b750a1e1d9622c936d832328a0689e054db62"
      }
    }
  }
]

However, very unfortunately I wasn't able to capture in incoming package on the PubSub websocket - but digging through the frontend JavaScript I found the following:

// ...

{
    CommunityMomentStart: "active",
    CommunityMomentClaim: "claimed",
}

// ...

if (l.type === E.qU.CommunityMomentStart) {
                        var c = {
                            momentID: l.data.moment_id,
                            type: N.S.CommunityMoment,
                            userID: null !== (i = null == t ? void 0 : t.id) && void 0 !== i ? i : null,
                            userLogin: null !== (a = null == t ? void 0 : t.login) && void 0 !== a ? a : null
                        }

Using this I think it would be reasonable to deduce that an example response from the topic might be something like:

{
  "type": "MESSAGE",
  "data": {
    "topic": "community-moments-channel-v1.<channel_id>",
    "message": "{\"type\":\"active\",\"moment_id\":<moment_id>}"
  }
}

@RakSrinaNa What do you think?

chowder avatar Aug 29 '22 09:08 chowder

I can't really say much, maybe, maybe not. But this seems to be similar to what twitch do with other endpoints. Can give a shot at it in a branch and see over time. (I have my own fork where I'll do the implem following those info, if I find any feedback on it I'll share here obviously)

Rakambda avatar Aug 29 '22 13:08 Rakambda

Though the format of the json payload from the GQL is probably different. I don't think there's a message field.

You can see examples of other messages from twitch: https://github.com/RakSrinaNa/ChannelPointsMiner/tree/main/miner/src/test/resources/api/gql

Rakambda avatar Aug 29 '22 13:08 Rakambda

Yes, you're right. Just to clarify - the JSON payload snippet is what I'm expecting from the PubSub API when listening to the community-moments-channel-v1 topic

This is the response for the GQL CommunityMomentCallout_Claim operation that I was able to capture:

[
  {
    "data": {
      "claimCommunityMoment": {
        "moment": {
          "id": $MOMENT_ID,
          "__typename": "CommunityMoment"
        },
        "error": null,
        "__typename": "ClaimCommunityMomentPayload"
      }
    },
    "extensions": {
      "durationMilliseconds": 9,
      "operationName": "CommunityMomentCallout_Claim",
      "requestID": $REQUEST_ID
    }
  }
]

chowder avatar Aug 29 '22 13:08 chowder

My bad for the above link. What Twitch send us is through the WS (PubSub) not GQL. So it'd be more like those: https://github.com/RakSrinaNa/ChannelPointsMiner/tree/main/miner/src/test/resources/api/ws

So it'd be more like the following I think:

{
  "type": "MESSAGE",
  "data": {
    "topic": "community-moments-channel-v1.<channel_id>",
    "message": "{\"type\":\"active\",\"data\":{\"moment_id\":<moment_id>}}"
  }
}

However the type=active is probably wrong. It should be the value of E.qU.CommunityMomentStart (community-moment-start?) from the JS if you can look it up.

Rakambda avatar Aug 29 '22 15:08 Rakambda

The value of E.qU.CommunityMomentStart does look to be just "active" if I'm reading it right. (Just doing a string search on the Twitch JS files)

But yes agreed - I made an error, the moment_id key should be in a data object.

Thanks for the help. I'll implement this in my own fork and revert the next time I get Moment (might be a while) :)

chowder avatar Aug 29 '22 15:08 chowder

~~Can you share the URL of the JS page ? I'd be curious to inspect it too.~~

Seems to be in features.chat.components.chat-command-handlers.component-8c9fbcdf482be806bb8a.js

Other things to note from the JS:

  • There is another type claimed which maybe comes from the community-moments-user-v1 topic
  • Possible errors seems to be
            e.INVALID_MOMENT_ID = "INVALID_MOMENT_ID",
            e.CLAIM_PERIOD_EXPIRED = "CLAIM_PERIOD_EXPIRED",
            e.ALREADY_CLAIMED = "ALREADY_CLAIMED",
            e.INVALID_REQUEST = "INVALID_REQUEST",

Rakambda avatar Aug 29 '22 16:08 Rakambda

So mining for 15+ channels who frequently create Moments paid off. :smile:

Here's the payload from the PubSub API when there's a moment active:

{
  "type": "active",
  "data": {
    "moment_id": "0ad4b5b8-be38-4245-b20d-af357aec18e9",
    "channel_id": "215105998",
    "clip_slug": "SparklySmokyFriesFloof-jwMEwyARRfqhEImG"
  }
}

(Moment link: https://clips.twitch.tv/SparklySmokyFriesFloof-jwMEwyARRfqhEImG)

Everything else about claiming Moments is as we already knew it from the conversations above.

chowder avatar Sep 07 '22 18:09 chowder

What a man 👏 You've done quite a lot for this, respect.

I'm curious, can you give a list of streamers that "frequently create Moments" so that it'll be easier to test implementations?

Rakambda avatar Sep 07 '22 19:09 Rakambda

Here were the list of streamers that I was testing this out with:

https://paste.ee/r/NdGA9/0

Alternatively this Google search query will give you pretty good results as well.

chowder avatar Sep 07 '22 19:09 chowder

This addon collects moments automatically on open tabs,

https://chrome.google.com/webstore/detail/automatic-twitch-drops-mo/kfhgpagdjjoieckminnmigmpeclkdmjm

Can you see how they auto collect it and implement it in this python script?

ImpalaPUA avatar Nov 30 '22 15:11 ImpalaPUA