diff --git a/multistream-alerts/icons/badges/twitch-channel-point.png b/multistream-alerts/icons/badges/twitch-channel-point.png
new file mode 100644
index 0000000..67d0a3e
Binary files /dev/null and b/multistream-alerts/icons/badges/twitch-channel-point.png differ
diff --git a/multistream-alerts/icons/badges/youtube-broadcaster.svg b/multistream-alerts/icons/badges/youtube-broadcaster.svg
new file mode 100644
index 0000000..655ca1c
--- /dev/null
+++ b/multistream-alerts/icons/badges/youtube-broadcaster.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multistream-alerts/icons/badges/youtube-member.svg b/multistream-alerts/icons/badges/youtube-member.svg
new file mode 100644
index 0000000..12720b9
--- /dev/null
+++ b/multistream-alerts/icons/badges/youtube-member.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multistream-alerts/icons/badges/youtube-moderator.svg b/multistream-alerts/icons/badges/youtube-moderator.svg
new file mode 100644
index 0000000..45b2718
--- /dev/null
+++ b/multistream-alerts/icons/badges/youtube-moderator.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multistream-alerts/icons/badges/youtube-verified.svg b/multistream-alerts/icons/badges/youtube-verified.svg
new file mode 100644
index 0000000..5e85909
--- /dev/null
+++ b/multistream-alerts/icons/badges/youtube-verified.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multistream-alerts/icons/platforms/kofi.png b/multistream-alerts/icons/platforms/kofi.png
new file mode 100644
index 0000000..8812cac
Binary files /dev/null and b/multistream-alerts/icons/platforms/kofi.png differ
diff --git a/multistream-alerts/icons/platforms/patreon.png b/multistream-alerts/icons/platforms/patreon.png
new file mode 100644
index 0000000..22c9748
Binary files /dev/null and b/multistream-alerts/icons/platforms/patreon.png differ
diff --git a/multistream-alerts/icons/platforms/tipeeeStream.png b/multistream-alerts/icons/platforms/tipeeeStream.png
new file mode 100644
index 0000000..cd981f3
Binary files /dev/null and b/multistream-alerts/icons/platforms/tipeeeStream.png differ
diff --git a/multistream-alerts/icons/platforms/twitch.png b/multistream-alerts/icons/platforms/twitch.png
new file mode 100644
index 0000000..dcbd6c8
Binary files /dev/null and b/multistream-alerts/icons/platforms/twitch.png differ
diff --git a/multistream-alerts/icons/platforms/youtube.png b/multistream-alerts/icons/platforms/youtube.png
new file mode 100644
index 0000000..5abdb7b
Binary files /dev/null and b/multistream-alerts/icons/platforms/youtube.png differ
diff --git a/multistream-alerts/index.html b/multistream-alerts/index.html
new file mode 100644
index 0000000..ab66f44
--- /dev/null
+++ b/multistream-alerts/index.html
@@ -0,0 +1,28 @@
+
+
+
+ Connecting...
+
+
+

+
+ hotarubi_TA
+
+ gifted a Tier 3 sub
+
+ to nutty
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/multistream-alerts/script.js b/multistream-alerts/script.js
new file mode 100644
index 0000000..654cdf4
--- /dev/null
+++ b/multistream-alerts/script.js
@@ -0,0 +1,1154 @@
+////////////////
+// PARAMETERS //
+////////////////
+
+const queryString = window.location.search;
+const urlParams = new URLSearchParams(queryString);
+
+const sbServerAddress = urlParams.get("address") || "127.0.0.1";
+const sbServerPort = urlParams.get("port") || "8080";
+const avatarMap = new Map();
+
+const alertBox = document.getElementById('alertBox');
+const avatarElement = document.getElementById('avatar');
+const usernameLabel = document.getElementById('username');
+const descriptionLabel = document.getElementById('description');
+const attributeLabel = document.getElementById('attribute');
+
+/////////////
+// OPTIONS //
+/////////////
+
+const showPlatform = GetBooleanParam("showPlatform", true);
+const showAvatar = GetBooleanParam("showAvatar", true);
+const showTimestamps = GetBooleanParam("showTimestamps", true);
+const showBadges = GetBooleanParam("showBadges", true);
+const showPronouns = GetBooleanParam("showPronouns", true);
+const showUsername = GetBooleanParam("showUsername", true);
+const showMessage = GetBooleanParam("showMessage", true);
+const font = urlParams.get("font") || "";
+const fontSize = urlParams.get("fontSize") || "30";
+const lineSpacing = urlParams.get("lineSpacing") || "1.7";
+const background = urlParams.get("background") || "#000000";
+const opacity = urlParams.get("opacity") || "0.85";
+
+const hideAfter = GetIntParam("hideAfter", 0);
+const excludeCommands = GetBooleanParam("excludeCommands", true);
+const ignoreChatters = urlParams.get("ignoreChatters") || "";
+const scrollDirection = GetIntParam("scrollDirection", 1);
+const inlineChat = GetBooleanParam("inlineChat", false);
+const imageEmbedPermissionLevel = GetIntParam("imageEmbedPermissionLevel", 20);
+
+const showTwitchMessages = GetBooleanParam("showTwitchMessages", true);
+const showTwitchAnnouncements = GetBooleanParam("showTwitchAnnouncements", true);
+const showTwitchSubs = GetBooleanParam("showTwitchSubs", true);
+const showTwitchChannelPointRedemptions = GetBooleanParam("showTwitchChannelPointRedemptions", true);
+const showTwitchRaids = GetBooleanParam("showTwitchRaids", true);
+const showTwitchSharedChat = GetIntParam("showTwitchSharedChat", 2);
+
+const showYouTubeMessages = GetBooleanParam("showYouTubeMessages", true);
+const showYouTubeSuperChats = GetBooleanParam("showYouTubeSuperChats", true);
+const showYouTubeSuperStickers = GetBooleanParam("showYouTubeSuperStickers", true);
+const showYouTubeMemberships = GetBooleanParam("showYouTubeMemberships", true);
+
+const showStreamlabsDonations = GetBooleanParam("showStreamlabsDonations", true)
+const showStreamElementsTips = GetBooleanParam("showStreamElementsTips", true);
+const showPatreonMemberships = GetBooleanParam("showPatreonMemberships", true);
+const showKofiDonations = GetBooleanParam("showKofiDonations", true);
+const showTipeeeStreamDonations = GetBooleanParam("showTipeeeStreamDonations", true);
+const showFourthwallAlerts = GetBooleanParam("showFourthwallAlerts", true);
+
+const furryMode = GetBooleanParam("furryMode", false);
+
+// Set fonts for the widget
+document.body.style.fontFamily = font;
+document.body.style.fontSize = `${fontSize}px`;
+
+// // Set line spacing
+// document.documentElement.style.setProperty('--line-spacing', `${lineSpacing}em`);
+
+// // Set the background color
+// const opacity255 = Math.round(parseFloat(opacity) * 255);
+// let hexOpacity = opacity255.toString(16);
+// if (hexOpacity.length < 2) {
+// hexOpacity = "0" + hexOpacity;
+// }
+// document.body.style.background = `${background}${hexOpacity}`;
+
+// // Get a list of chatters to ignore
+// const ignoreUserList = ignoreChatters.split(',').map(item => item.trim().toLowerCase()) || [];
+
+// // Set the scroll direction
+// switch (scrollDirection) {
+// case 1:
+// document.getElementById('messageList').classList.add('normalScrollDirection');
+// break;
+// case 2:
+// document.getElementById('messageList').classList.add('reverseScrollDirection');
+// break;
+// }
+
+
+
+
+/////////////////////////
+// STREAMER.BOT CLIENT //
+/////////////////////////
+
+const client = new StreamerbotClient({
+ host: sbServerAddress,
+ port: sbServerPort,
+
+ onConnect: (data) => {
+ console.log(`Streamer.bot successfully connected to ${sbServerAddress}:${sbServerPort}`)
+ console.debug(data);
+ SetConnectionStatus(true);
+ },
+
+ onDisconnect: () => {
+ console.error(`Streamer.bot disconnected from ${sbServerAddress}:${sbServerPort}`)
+ SetConnectionStatus(false);
+ }
+});
+
+client.on('Twitch.Cheer', (response) => {
+ console.debug(response.data);
+ TwitchChatMessage(response.data);
+})
+
+client.on('Twitch.Sub', (response) => {
+ console.debug(response.data);
+ TwitchSub(response.data);
+})
+
+client.on('Twitch.ReSub', (response) => {
+ console.debug(response.data);
+ TwitchResub(response.data);
+})
+
+client.on('Twitch.GiftSub', (response) => {
+ console.debug(response.data);
+ TwitchGiftSub(response.data);
+})
+
+client.on('Twitch.RewardRedemption', (response) => {
+ console.debug(response.data);
+ TwitchRewardRedemption(response.data);
+})
+
+client.on('Twitch.Raid', (response) => {
+ console.debug(response.data);
+ TwitchRaid(response.data);
+})
+
+client.on('YouTube.SuperChat', (response) => {
+ console.debug(response.data);
+ YouTubeSuperChat(response.data);
+})
+
+client.on('YouTube.SuperSticker', (response) => {
+ console.debug(response.data);
+ YouTubeSuperSticker(response.data);
+})
+
+client.on('YouTube.NewSponsor', (response) => {
+ console.debug(response.data);
+ YouTubeNewSponsor(response.data);
+})
+
+client.on('YouTube.GiftMembershipReceived', (response) => {
+ console.debug(response.data);
+ YouTubeGiftMembershipReceived(response.data);
+})
+
+client.on('Streamlabs.Donation', (response) => {
+ console.debug(response.data);
+ StreamlabsDonation(response.data);
+})
+
+client.on('StreamElements.Tip', (response) => {
+ console.debug(response.data);
+ StreamElementsTip(response.data);
+})
+
+client.on('Patreon.PledgeCreated', (response) => {
+ console.debug(response.data);
+ PatreonPledgeCreated(response.data);
+})
+
+client.on('Kofi.Donation', (response) => {
+ console.debug(response.data);
+ KofiDonation(response.data);
+})
+
+client.on('Kofi.Subscription', (response) => {
+ console.debug(response.data);
+ KofiSubscription(response.data);
+})
+
+client.on('Kofi.Resubscription', (response) => {
+ console.debug(response.data);
+ KofiResubscription(response.data);
+})
+
+client.on('Kofi.ShopOrder', (response) => {
+ console.debug(response.data);
+ KofiShopOrder(response.data);
+})
+
+client.on('TipeeeStream.Donation', (response) => {
+ console.debug(response.data);
+ TipeeeStreamDonation(response.data);
+})
+
+client.on('Fourthwall.OrderPlaced', (response) => {
+ console.debug(response.data);
+ FourthwallOrderPlaced(response.data);
+})
+
+client.on('Fourthwall.Donation', (response) => {
+ console.debug(response.data);
+ FourthwallDonation(response.data);
+})
+
+client.on('Fourthwall.SubscriptionPurchased', (response) => {
+ console.debug(response.data);
+ FourthwallSubscriptionPurchased(response.data);
+})
+
+client.on('Fourthwall.GiftPurchase', (response) => {
+ console.debug(response.data);
+ FourthwallGiftPurchase(response.data);
+})
+
+client.on('Fourthwall.GiftDrawStarted', (response) => {
+ console.debug(response.data);
+ FourthwallGiftDrawStarted(response.data);
+})
+
+client.on('Fourthwall.GiftDrawEnded', (response) => {
+ console.debug(response.data);
+ FourthwallGiftDrawEnded(response.data);
+})
+
+
+
+///////////////////////
+// MULTICHAT OVERLAY //
+///////////////////////
+
+async function TwitchSub(data) {
+ if (!showTwitchSubs)
+ return;
+
+ // Set the text
+ const username = data.user.name;
+ const subTier = data.sub_tier;
+ const isPrime = data.is_prime;
+
+ // Render avatars
+ if (showAvatar) {
+ const avatarURL = await GetAvatar(username);
+ avatarElement.src = avatarURL;
+ }
+
+ if (!isPrime)
+ UpdateAlertBox('twitch', `${username}`, `subscribed with Tier ${subTier.charAt(0)}`);
+ else
+ UpdateAlertBox('twitch', `${username}`, `used their Prime Sub`);
+}
+
+async function TwitchResub(data) {
+ if (!showTwitchSubs)
+ return;
+
+ // Set the text
+ const username = data.user.name;
+ const subTier = data.subTier;
+ const isPrime = data.isPrime;
+ const cumulativeMonths = data.cumulativeMonths;
+ const message = data.text;
+
+ // Render avatars
+ if (showAvatar) {
+ const avatarURL = await GetAvatar(username);
+ avatarElement.src = avatarURL;
+ }
+
+ if (!isPrime)
+ UpdateAlertBox(
+ 'twitch',
+ `${username}`,
+ `resubscribed with Tier ${subTier.charAt(0)}`,
+ `${cumulativeMonths} months`,
+ message
+ );
+ else
+ UpdateAlertBox(
+ 'twitch',
+ `${username}`,
+ `used their Prime Sub`,
+ `${cumulativeMonths} months`,
+ message
+ );
+
+ AddMessageItem(instance, data.messageId);
+}
+
+async function TwitchGiftSub(data) {
+ if (!showTwitchSubs)
+ return;
+
+ // Set the text
+ const username = data.user.name;
+ const subTier = data.subTier;
+ const recipient = data.recipient.name;
+ const cumlativeTotal = data.cumlativeTotal;
+
+ // Render avatars
+ if (showAvatar) {
+ const avatarURL = await GetAvatar(username);
+ avatarElement.src = avatarURL;
+ }
+
+ let messageText = '';
+ if (cumlativeTotal > 0)
+ messageText = `They've gifted ${cumlativeTotal} subs in total!`;
+
+ UpdateAlertBox(
+ 'twitch',
+ `${username}`,
+ `gifted a Tier ${subTier.charAt(0)} subscription`,
+ `to ${recipient}`,
+ messageText
+ );
+}
+
+async function TwitchRewardRedemption(data) {
+ if (!showTwitchChannelPointRedemptions)
+ return;
+
+ const username = data.user_name;
+ const rewardName = data.reward.title;
+ const cost = data.reward.cost;
+ const userInput = data.user_input;
+ const channelPointIcon = `