Hello, for the past couple days I’ve been trying to learn how to make twitch extensions. I’m still a beginner with this and would appreciate any support.
onClick of a button on the extension panel I’m trying to send a text msg to chat. Below is the function “sendToChat” that I call.
I’m getting this error 400 message and not sure what to do next.
token / channelId / clientid I set in a different function, they seem to be correct from console logs.
componentDidMount(){
if(this.twitch){
this.twitch.onAuthorized((auth)=>{
token = auth.token;
channelId = auth.channelId;
clientid = auth.clientId;
sendToChat(){
var headers = {
"Authorization": "Bearer " + token,
"Client-ID": clientid,
"Content-Type": "application/json"
}
let url = 'https://api.twitch.tv/extensions/' + clientid + '/0.0.1/channels/' + channelId + '/chat';
var textToBeSentToChat = JSON.stringify("Test text");
const requestOptions = {
method: 'POST',
headers:{ "Authorization": "Bearer " + token,
"Client-ID": clientid,
"Content-Type": "application/json"},
body: textToBeSentToChat
};
fetch(url, requestOptions)
.then(res => {
if (res.ok) {
return res.json();
} else {
return Promise.reject({ status: res.status, statusText: res.statusText });
}
})
.then(res => console.log(res))
.catch(err => console.log('Error message:', err));
}
That won’t work as the frontend JWT’s do not have permission
You need a JWT Signed with the “external” role.
Signed JWT created by an Extension Backend Service (EBS), following the requirements documented in Signing the JWT . A signed JWT must include the role
and channel_id
fields documented in JWT Schema . role
must be set to "external"
and channel_id
must match the broadcaster ID in the request URL.
The token you are using isn’t valid for this call
So I added the following code into my “sendToChat” function by following the documentation to the best of my ability. Found an old example on this post:
Signed JWT created by the EBS, following the requirements documented in Signing the JWT (in Building Extensions ) or Twitch JWT containing the broadcaster role. The channel_id inside the JWT must match the channel ID in the request URL.
But doesn’t it means that I need to use a JWT that I sign on my EBS (and according to the docs in “Building Extensions” it means to use “role”: “external”), OR to use a different Twitch JWT that already contains the broadcaster role?
However I still have the exact same 400 error. So either I made a mistake in the code or put it in the wrong place. Ty for the help so far.
var jwtPayload = {
'exp': Math.floor(new Date().getTime() / 1000) + 60,
'user_id': channelId,
'role': 'external'
}
const jwt = require('jsonwebtoken');
var signature = jwt.sign(jwtPayload, token)
console.log("sign: " + signature)
const requestOptions = {
method: 'POST',
headers:{ "Authorization": "Bearer " + signature,
"Client-ID": clientid,
"Content-Type": "application/json"},
body: textToBeSentToChat
};
Check that channelId
is a “string” and not a “number” so
channelId = '123';
and not
channelId = 123;
Check that you did base64 decode the token
first before use?
Check that Chat is “enabled” on the extension in the console.
Your payload
body: textToBeSentToChat
is unclear and may not match the request to send as documented
NinusTTV:
const requestOptions = {
method: 'POST',
headers:{ "Authorization": "Bearer " + signature,
"Client-ID": clientid,
"Content-Type": "application/json"},
body: textToBeSentToChat
};
Where is the rest of the call?
Here is an “up to date script” using the new API, use got
for communications
`use strict`;
const fs = require('fs');
const path = require('path');
let config = {
"client_id": "REMOVED",
"extension_secret": "REMOVED",
"owner_id": "15185913",
"version": "REMOVED"
}
let broadcaster_id = 'REMOVED';
const jwt = require('jsonwebtoken');
const got = require('got');
const secret = Buffer.from(config.extension_secret, 'base64');
let sigPayload = {
'exp': Math.floor(new Date().getTime() / 1000) + 4,
'user_id': config.owner_id,
'role': 'external'
}
let sig = jwt.sign(sigPayload, secret);
got({
url: 'https://api.twitch.tv/helix/extensions/chat',
method: 'POST',
headers: {
'Client-ID': config.client_id,
'Authorization': 'Bearer ' + sig,
'Content-Type': 'application/json'
},
searchParams: {
broadcaster_id
},
body: JSON.stringify({
text: 'Test Message',
extension_id: config.client_id,
extension_version: config.version
}),
responseType: 'json'
})
.then(resp => {
console.log('Result', resp.statusCode, resp.body, resp.headers['ratelimit-remaining'], '/', resp.headers['ratelimit-limit']);
})
.catch(err => {
if (err.response) {
console.error('API ERROR', err.response.statusCode, err.response.body);
return;
}
console.error('BAD ERROR', err);
});
1 Like