Confused about "channel:bot" for eventSub

Goal: My game is launched, broadcaster authenticates via browser, my bot joins broadcaster to read&write in chat. Either using 2 “apps” : one “Game Integration” and one “Chat Bot” or just using one app.

Problems:
request user-token with “channel:bot” scope, use that token with corresponding client-id to send chat message:
{“error”:“Unauthorized”,“status”:401,“message”:“User access token requires the user:write:chat scope.”}

If I add “user:write:chat” it appears as “write messages in your username” during user-authorization (in browser). But I want to write as the bot, not as the broadcaster!

Similar problem when I want to read (subscribe to channel:chat:message via EventSub) - it shows up as “read messages and appear in your username”. Not sure if there is a way to read messages “as a bot”?

It works when I use “user:read:chat” or “user:write:chat”. But that’ll happen in the broadcasters name and show a confusing warning during auth-flow in browser.

while “channel:bot” just shows “join your chat as a bot” - won’t that suffice? Again, I want to read and write as the bot, in the broadcasters channel.

Documentation says:


channel.chat.message

(NEW) The channel.chat.message subscription type sends a notification when any user sends a message to a channel’s chat room.

Authorization

Requires user:read:chat scope from the chatting user. If app access token used, then additionally requires user:bot scope from chatting user, and either channel:bot scope from broadcaster or moderator status.


  • who is the “chatting user” ? isn’t that my bot?
  • “if app access token is used…” everytime I try (or ask chatGPT) it seems like I need user-level scopes so I can’t actually use a app-access-token (via credentials, id&secret)…

I get “channel:bot” from user - it’s fine.
I don’t want to get “user:read:chat” and “user:write:chat” from user, as that looks overkill to do that in the users name.

Is there a way to achieve this?

Well theres your problem. I don’t expect chatGPT to know the nuances here.

We/people of this forum/discord spend way too long detangling people from AI as it just doesn’t know

For the described application you would be using eventsub over websockets.

So, as per my guide (twitch_misc/eventsub/websockets/web/chat at main · BarryCarlyon/twitch_misc · GitHub) you need only:

  • user:read:chat to read chat

The problem here is that your game cannot generate a token to send as your bot account becuase the streamer only has access to their own account not your bot account.

So games often will request the user:write:chat and send game prompts as the broadcaster.

The alternative is the use of a relay server, which then allows you to have a token for your bot ready to go (and secure) but if that token dies you’ll need to reseed it.

For a game the optimal solution is:

  • use DCF - Device Code Flow, to generate a token
  • request only read chat scope(s) from the broadcaster (user:read:chat)
  • connect to and read chat as the broadcaster via eventsub webscokets
  • send game prompts in the game itself not the chat.

if you need to send game prompts in chat then you willl need a secondary service, or you have to send as the streamer, as theres no way inside your game for the game to get a token for the bot account you would want to use to send as.

Becuase you asked the streamer to authenticate with this scope, to send messages to a chat as the streamer

This scope only pertains to eventsub webhooks/conduits.

And this scope lets another system connect to the chat not the game as the game doesn’t have the bots token to work with (and shouldn’t)

Only if your token represents the bot, and you are doing the oAuth flow as your bot which a broadcaster can’t as they are logged into Twitch as themself, not your bot.

To reiterate:

But the person sat in front of the game is the broadcaster, not your bot.

This is what games have done in the past.

Becuase without an external server to handle the bot token, theres no way for the game to have the bots token to send to chat with, so they send with the token they have access to, the broadcasters

An app access tokens can’t/shouldn’t be generated inside your game, becuase that would leak your client secret and/or leak a token that could have access to any channel authorised to the clientID and thus open to abuse

Thank you for the explanations. I now read Authenticating and Setting up EventSub | Twitch Developers and did the following:
I authorized my bot to act in my name (granted user:bot user:read:chat user:write:chat), generated a user-access-token with a refresh-token. I securely stored that on the server that received the redirect. I can now do https GET and let my server refresh the token, then return it decrypted to the Game. The game’s code can now send and receive messages in “my name, as the bot” in any broadcasters channel, as it seems.

So, what is “channel:bot” for then?

DO NOT DO THIS. That is leaking the bots token to the broadcaster.

As someone can intercept and abuse the token or revoke it.

It should go “game tells the server to send a chat message, and the server sends the chat message” using the inbound broadcasters token to verify (in your code the destination cahnnel) and send to the target channel (using the bots token).

To oversimplfy: EventSub Chat topics over Webhooks (or Conduits)

I see. I do have a small protection. The php that returns the decrypted token via HTTPS does require a password to be sent. But someone with Wireshark, or an attacker that decompiles my game, can probably obtain that token and do bad things.

So I will request user-token from broadcaster (Need that for follow, cheer, subscribe…) and send that token to my server, verify it and get the “login” to make sure the php is not abused by someone? That sounds like a plan.

yup

also theres the 50 rule

if 51 people are playing the game the 51st player will invalidate the 1st persons token and the bot won’t work

So again: Don’t do this, you need to get the server to send instead

I rewrote my code. Flow is now like this:

Broadcaster starts game, grants some scopes via browser (user flow). This gives me a user-token from the broadcaster. This is requested with client_id from “Game App”. Reading&Writing chat messages is done by the bot, that has a different client_id & client_secret.

Reading messages:
Game starts websocket session, fetches session_id from welcome message. session_id and userToken is sent to my Server. Server validates the user token, uses “user_id” the transmitted session_id in the subscription API call. It also decrypts and (if required) refreshes the app access token, that has “user:bot user:chat:read user:chat:write” scopes.

Afaik, my Server endpoint can’t be abused to send messages in my (bots) name to anyone unauthorized, as a valid token is required, so the hacker could only send messages to himself.

Sending Messages:
I again send the userToken and the message to my server. Server again decrypts (and refreshes if required) app access token. Message is sent to “user_id” from validated user token.


Refreshing Token works by using the refreshToken and client_id + client_secret.


Summary: The client_id & client_secret never leave the Server. The Access&Refresh Tokens are encrypted on the server and only decrypted by the Server when it is used. They are never returned in a response to the Game or anything. The game’s websocket session is established to twitch directly, so receiving messages doesn’t involve my Server as a middleman, but subscribing to the channel:chat:message is done on my Server, so the Game doesn’t know anything about secrets or tokens required for the subscription.

Sounds good?

Seems good to me

I will note that client_id’s are public so no need to hide that (they form part of the URL when the broadcaster logs in)

The Encrption is probably not needed here.

This should be doing using the users own token inside your game your server call isn’t needed here

So what I would do is:

  • auth my bot account to my clientID, with scopes user:write:chat and user:bot, this allows me to generate an app access to send with on the server

the game

  • streamer opens the game
  • streamer does DCF auth, with the same clientID as above, with the scopes channel:bot and user:read:chat
  • connect to eventsub websocket inside the game with the resulting token
  • when I want to send a chat message, I HTTP POST (verb doesn’t really matter here) to my server with the broadcasters resulting token
  • my server will verify the token and extract the broadcaster ID in the verify response
  • my server uses/regenerates an app access/client credentials token and calls the send chat end point with the broadcaster ID from the verify response in the previous step, and my bots user ID as the sender_id
  • I’ll also show the prompt/text in game as well for vod viewers (and/or if the streamer is multistreaming so other platform peoples know whats going on)

Conceptually you don’t need to store the app access token as you can just hold it in memory or (re)generate each time with your clientID/clientSecret.

So the ClientSecret is what needs protected but encrypting it is a waste of cycles here.

  • The broadcasters own token and refresh token are stored in the game
  • I don’t store a user token for the bot
  • App Access/client credentials token(s) are generated on the fly

Keep in mind that your bot account is liable for any and all messages sent, including ones that may potentially violates Twitch’s ToS or Community Guidelines, so rather than just sending the message that the user sends to your server you may want to ensure that the message that is sent is one that is sanitized by your server in a way that prevents user-submitted content (from your game, or a malicious users). Otherwise it could potentially be used in a way that results in your bot account being terminated.

So when I say “post to the server”

I mean “this game event is happening”

Then you lookup what text to send and any relevant localisations

Thank you. The game will send a short “hello” and “look at your channel point rewards” because the game changes/replaces them (using the channel:manage:redemptions scope). There will be no user-input within the game that is sent in the name of my bot. So that should be no concern here :slight_smile: