Tkd-Alex / Twitch-Channel-Points-Miner-v2

A simple script that will watch a stream for you and earn the channel points.
GNU General Public License v3.0
1.21k stars 669 forks source link

Auto claim Moments #428

Open willvisentini opened 2 years ago

willvisentini commented 2 years ago

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

willvisentini commented 2 years ago

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

LeoAlt8 commented 2 years ago

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

K31X commented 2 years ago

Hope this can be done, that would be awesome.

KillaBoi commented 2 years ago

+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

chowder commented 2 years ago

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.

Rakambda commented 2 years ago

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.

chowder commented 2 years ago

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?

Rakambda commented 2 years ago

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 commented 2 years ago

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

chowder commented 2 years ago

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
    }
  }
]
Rakambda commented 2 years ago

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.

chowder commented 2 years ago

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) :)

Rakambda commented 2 years ago

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:

chowder commented 2 years ago

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.

Rakambda commented 2 years ago

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?

chowder commented 2 years ago

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.

ImpalaPUA commented 1 year ago

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?