Skip to main content

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.

Websocket Webhooks

Webhook URL - https://ws.chatrpg.com/pubsub/subscribe

  • Chat Messages
  • Chatter 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 IDs
  • clientId - Your Client ID for your extension
  • clientSecret - Your Client Secret for your extension
  • topics - 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 Streamer
  • socketConnections - Gets all Connections to ChatRPG on Streamer's ChatRPG Page
  • twitchSubscriptions - Gets all Twitch Subscriptions on a Streamer's Twitch Page
  • twitchBits - Gets all Twitch Bits donated on Streamer's Twitch Page
  • youtubeSuperChats - 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 Page
  • tiktokGifts - Gets all Tiktok Gift Redemptions on Streamer's Tiktok Live
  • tiktokSubscriptions - 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.

Fetch Streamers using your Extension

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);
}
};
Subscribe to Websocket Webhook

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.

Unsubscribe a Single Streamer

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.

Remove Topic from ALL Streamers

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:

Unsubscribe from Webhook

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,
},
};