Using Chat and Event Webhooks
What are Chat and Event Webhooks?
These Webhooks are used to get access to things that are going on in Chat and Events like Donations and Subscriptions.
Webhook URL - https://ws.chatrpg.com/pubsub/subscribe
Chat MessagesChatter Connections (Twitch + ChatRPG only)
Body - Must be included
url- The endpoint URL where you want to receive webhook notifications.streamerIds- An array of Streamer ChatRPG IDsclientId- Your Client ID for your extensionclientSecret- Your Client Secret for your extensiontopics- An Object of the topics you want to subscribe to, of type boolean
Topics - You must include at least 1 - Failure to do so will unsubscribe your URL:
chatMessages- Gets all Chat Messages sent from Twitch, Kick, Youtube, ChatRPG and Tiktok to StreamersocketConnections- Gets all Connections to ChatRPG on Streamer's ChatRPG PagetwitchSubscriptions- Gets all Twitch Subscriptions on a Streamer's Twitch PagetwitchBits- Gets all Twitch Bits donated on Streamer's Twitch PageyoutubeSuperChats- Gets all Youtube Super Chats on Streamer's Youtube Stream(s)youtubeSponsors- Gets all Youtube Membership Redemptions on Streamer's Youtube Stream(s)twitchChannelPoints- Gets all Channel Point Redemptions in Streamer's Twitch PagetiktokGifts- Gets all Tiktok Gift Redemptions on Streamer's Tiktok LivetiktokSubscriptions- Gets all Tiktok Subscriptions on Streamer's Tiktok Live (I don't know if this actually works lol)
How can I connect to Chat and Event Webhooks?
It is recommended you get all the streamers that are using your extension, and then pass in their ChatRPG IDs as an array.
Webhooks will send information about the Streamers you request using their ID.
https://server.chatrpg.com/api/extensions/streamersusingextension
export const getStreamersUsingExtension = async () => {
try {
const response = await axios.post(`https://server.chatrpg.com/api/extensions/streamersusingextension`, {
clientId: process.env.CHATRPG_CLIENT,
clientSecret: process.env.CHATRPG_SECRET,
});
const streamers = response.data;
console.log('Streamers using the extension: ', streamers);
//Payload Example:
// {
// chatrpg_id: 'CRPG1COWSEP',
// chatrpg_username: 'Cowsep',
// twitch_username: 'Cowsep'
// },
if (response.data && response.data.length) {
const streamerIds = response.data.map(streamer => streamer.chatrpg_id);
console.log('Streamers using points extension: ', streamerIds);
await subscribeStreamersToWebhooks(streamerIds); //After Grabbing all the streamers, then subscribe to them.
} else {
console.log('No streamers found or error fetching streamers.');
}
} catch (error) {
console.error('Error fetching streamers:', error);
}
};
https://ws.chatrpg.com/pubsub/subscribe
You only need to include the topics you want, and set them to true.
If you wish to have more security, you can pass in the optional jwtSecret field, when you receive a notification you will have to decode it.
Keep in mind if you wish to add or remove a jwtSecret you must use this route, and to remove the jwtSecret simply subscribe without it.
const subscribeStreamersToWebhooks = async (streamerIds: string[]): Promise<void> => {
const subscriptionUrl = `https://ws.chatrpg.com/pubsub/subscribe`; //The ChatRPG pubsub URL
console.log(`Subscribing streamers to pubsub at ${subscriptionUrl}`);
try {
const response = await axios.post(subscriptionUrl, {
url: `${process.env.THIS_APPLICATION_URL}/webhook`, //Your URL
streamerIds: streamerIds, // Send array of IDs
clientId: process.env.CHATRPG_CLIENT,
clientSecret: process.env.CHATRPG_SECRET,
topics: { //List of topics you're interested in
chatMessages: true, //Alerts you when Chat Messages happen - we're enabling this event!
socketConnections: false, //Alerts you when a User Connects or Disconnects - we're disabling this event!
//Any events you don't want to listen to just omit! If its already subscribed it'll auto-extend, if not it just won't add it.
},
jwtSecret: "mybeautifulsecretjwt" //Optional JWT that all payloads will be secured with
});
if (response.status === 200) {
console.log(`Successfully subscribed streamers to all necessary webhooks.`);
} else {
console.log(`Failed to subscribe streamers. Status code: ${response.status}`);
}
} catch (error) {
console.error(`Error in subscribeStreamersToWebhooks: ${error.message}`);
}
};
Then you should process the webhook messages however you see fit.
You can discern which message is which by using the "type" field.
- chatMessages =
body.type === messages - socketConnections =
body.type === socketConnections
Will include your jwtSecret as well - need to add to documentation how to use it.
router.post('/', async (req, res) => {
try {
console.log(`Received payload: ${JSON.stringify(req.body)}`);
//Example Message Payload: {"type":"messages","streamer_chatrpg_id":"CRPG1COWSEP","message":"test","platform":"Twitch","user_chatrpg_id":null,"twitch_id":"48465437"}
//Example Socket Payload: {"type":"socketConnections","connections":[{"streamer_chatrpg_id":"CRPG1COWSEP","user_chatrpg_id":"CRPG1COWSEP","user_twitch_id":"48465437","connected":false,"tabId":"ahfszi5"}]}
//Keep in mind that the socketConnections are an array.
if (req.body.type === 'messages') {
console.log(`Received message: ${req.body.message} from ${req.body.user_chatrpg_id} or ${req.body.twitch_id} in streamer ${req.body.streamer_chatrpg_id} from ${req.body.platform}`);
} else if (req.body.type === 'socketConnections' && Array.isArray(req.body.connections)) {
console.log(`Processing all connections update`, JSON.stringify(req.body.connections));
} else {
console.log(`Invalid payload:`, JSON.stringify(req.body));
return res.status(400).send('Invalid or incomplete payload');
}
res.status(200).send('Received Playload Successfully');
} catch (error) {
console.error('Server error:', error);
res.status(500).send('Server error');
}
});
How do I remove a Streamer's ChatRPG ID?
You can stop listening to topics on Streamer IDs as well. This is useful if they disable your extension for example. Its worth noting you'll stop receiving information about streamers that remove or disable your extension.
https://ws.chatrpg.com/pubsub/unsubscribechatrpgid
const unsubscribeStreamersToWebhooks = async (streamerIds: string[]): Promise<void> => {
const unsubscriptionUrl = `https://ws.chatrpg.com/pubsub/unsubscribechatrpgid`; //The ChatRPG pubsub URL
console.log(`Unsubscribing streamers to pubsub at ${unsubscriptionUrl}`);
try {
const response = await axios.post(unsubscriptionUrl, {
url: `${process.env.THIS_APPLICATION_URL}/webhook`, //Your URL
streamerIds: streamerIds, // Send array of IDs
clientId: process.env.CHATRPG_CLIENT,
clientSecret: process.env.CHATRPG_SECRET,
});
if (response.status === 200) {
console.log(`Successfully removed streamers from all webhooks.`);
} else {
console.log(`Failed to subscribe streamers. Status code: ${response.status}`);
}
} catch (error) {
console.error(`Error in subscribeStreamersToWebhooks: ${error.message}`);
}
};
How do I remove my Topic?
You can easily remove a topic by simply using this endpoint. Please note, topics are not on a per streamer basis.
https://ws.chatrpg.com/pubsub/removetopic
const unsubscribeFromTopics = async (): Promise<void> => {
// URL of the ChatRPG pubsub system for removing streamer IDs
const subscriptionUrl = `https://ws.chatrpg.com/pubsub/removetopic`;
console.log(`Attempting to unsubscribe streamers from topics at ${subscriptionUrl}`);
const exampleTopics = ['chatMessages', 'socketConnections'];
try {
// Send a POST request to unsubscribe the streamers
const response = await axios.post(subscriptionUrl, {
url: `${process.env.THIS_APPLICATION_URL}/webhook`, // Endpoint on your server that you want to unsubscribe
topics: exampleTopics, //Topics you want to unsubscribe
clientId: process.env.CHATRPG_CLIENT, // Client ID for the ChatRPG service
clientSecret: process.env.CHATRPG_SECRET, // Secret for authentication
});
if (response.status === 200) {
console.log(`Successfully unsubscribed streamers from that topic.`);
} else {
console.log(`Failed to unsubscribe streamers. Status code: ${response.status}`);
}
} catch (error) {
// Log any errors that occur during the HTTP request
console.error(`Error in unsubscribeStreamersFromWebhooks: ${error.message}`);
}
};
How do I remove myself from the Subscription?
URLs are removed automatically after 10 days but if you resubscribe or add any ChatRPG ID it will renew the expiration date 10 days out on all ChatRPG IDs.
If you want to remove it you can use this end point:
https://ws.chatrpg.com/pubsub/unsubscribe
const UnsubscribeFromChatRPGWebhooks = async (): Promise<void> => {
const unsubscriptionUrl = `https://ws.chatrpg.com/pubsub/unsubscribe`; //The ChatRPG pubsub URL
console.log(`Attempting to unsubscribing streamers to pubsub at ${unsubscriptionUrl}`);
try {
const response = await axios.post(unsubscriptionUrl, {
url: `${process.env.THIS_APPLICATION_URL}/webhook`, //Your URL
clientId: process.env.CHATRPG_CLIENT,
clientSecret: process.env.CHATRPG_SECRET,
});
if (response.status === 200) {
console.log(`Successfully unsubscribed from ChatRPG's webhooks.`);
} else {
console.log(`Failed to unsubscribe from ChatRPG's webhooks Status code: ${response.status}`);
}
} catch (error) {
console.error(`Error in subscribeStreamersToWebhooks: ${error.message}`);
}
};
How do I know what I'm receiving in the Payload from Alerts?
Alert payloads can vary widely because of the platforms we're getting alerts from, and because of that I tried to centralize them into a nice easy to read data structure.
Lets take a look:
Twitch Subscriptions
const subscriptionData = {
topic: "twitchSubscriptions" as keyof ITopicOptions,
data: {
username: TwitchSubscriptionData.event.user_name,
userid: TwitchSubscriptionData.event.user_id,
channelname: TwitchSubscriptionData.event.broadcaster_user_name,
channelid: TwitchSubscriptionData.event.broadcaster_user_id,
time: new Date().toISOString(),
plan: TwitchSubscriptionData.event.tier,
message: TwitchSubscriptionData.event.message ? TwitchSubscriptionData.event.message.text : ""
is_gift: TwitchSubscriptionData.event.is_gift,
months: TwitchSubscriptionData.event.cumulative_months || 1
}
};
Twitch Bits Event
const bitsData = {
topic: "twitchBits" as keyof ITopicOptions,
type: "twitch-bits",
data: {
username: eventSubData.event.user_name,
channelname: eventSubData.event.broadcaster_user_name,
userid: eventSubData.event.user_id,
channelid: eventSubData.event.broadcaster_user_id,
time: new Date(eventSubData.subscription.created_at).toISOString(),
message: eventSubData.event.message,
bits_used: eventSubData.event.bits,
total_bits_used: eventSubData.event.bits,
is_anonymous: eventSubData.event.is_anonymous,
badge_entitlement: null
},
message_id: eventSubData.subscription.id
};
Twitch Channel Points Event
const redemptionData = {
topic: "twitchChannelPoints" as keyof ITopicOptions,
data: {
username: TwitchChannelPointRedemptionData.event.user_name,
userid: TwitchChannelPointRedemptionData.event.user_id,
channelid: TwitchChannelPointRedemptionData.event.broadcaster_user_id,
title: TwitchChannelPointRedemptionData.event.reward.title,
prompt: TwitchChannelPointRedemptionData.event.reward.prompt,
cost: TwitchChannelPointRedemptionData.event.reward.cost
}
}
Youtube Super Chat Event
const subscriptionData = {
topic: "youtubeSuperChats" as keyof ITopicOptions, // Set the topic directly using the type from the interface
data: {
username: author,
message: userComment, // The message from the user, if any
amount: amount, // The amount of the Super Chat
currency: currency, // The currency of the Super Chat
}
};
Youtube Sponsor / Member Events
const subscriptionData = {
topic: "youtubeSponsors" as keyof ITopicOptions, // Set the topic directly using the type from the interface
data: {
username: author,
}
};
Tiktok Gift Events
const subscriptionData = {
topic: "tiktokGifts" as keyof ITopicOptions, // Set the topic directly using the type from the interface
data: {
username: data.nickname,
giftName: data.giftName,
giftAmount: data.repeatCount,
giftDiamondCount: data.diamondCount,
giftPictureUrl: data.giftPictureUrl,
},
};
Tiktok Subscriptions (Unsure if working)
const tiktokEventData = {
topic: "tiktokSubscriptions" as keyof ITopicOptions, // Set the topic directly using the type from the interface
data: {
username: data.uniqueId,
},
};