Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emote modifiers #23

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/@types/betterttv.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ namespace BetterTTV {
userId: string
}

type GlobalEmote = ChannelEmote & {
modifier: boolean
}

type SharedEmote = Emote & {
user: {
id: string
Expand Down
1 change: 1 addition & 0 deletions src/@types/slime2.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ namespace Slime2 {
name: string
images: Emote.Images
source: Emote.Source
isModifier: boolean
}

type EmoteMap = Map<string, Emote>
Expand Down
2 changes: 1 addition & 1 deletion src/@types/twitch.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ namespace Twitch {
type Type =
| { type: 'text' }
| { type: 'cheer'; cheer: Cheermote }
| { type: 'emote'; emote: Slime2.Event.Message.Emote }
| { type: 'emote'; emote: Slime2.Event.Message.Emote; modifier?: string }
}

type Cheermote = {
Expand Down
9 changes: 9 additions & 0 deletions src/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,12 @@ html {
opacity: 0;
}
}

@keyframes slime2-emote-modifier-party {
from {
filter: sepia(0.5) hue-rotate(0deg) saturate(2.5);
}
to {
filter: sepia(0.5) hue-rotate(360deg) saturate(2.5);
}
}
69 changes: 69 additions & 0 deletions src/services/emotes/BetterTTV.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,73 @@ export default async function getChannelEmotes(
): Promise<Slime2.Event.Message.EmoteMap> {
const emoteMap = new Map<string, Slime2.Event.Message.Emote>()

setEmotes(<BetterTTV.GlobalEmote[]>[
{
id: '6468f7acaee1f7f47567708e',
code: 'c!',
imageType: 'png',
animated: false,
userId: '5561169bd6b9d206222a8c19',
modifier: true,
},
{
id: '6468f845aee1f7f47567709b',
code: 'h!',
imageType: 'png',
animated: false,
userId: '5561169bd6b9d206222a8c19',
modifier: true,
},
{
id: '6468f869aee1f7f4756770a8',
code: 'l!',
imageType: 'png',
animated: false,
userId: '5561169bd6b9d206222a8c19',
modifier: true,
},
{
id: '6468f883aee1f7f4756770b5',
code: 'r!',
imageType: 'png',
animated: false,
userId: '5561169bd6b9d206222a8c19',
modifier: true,
},
{
id: '6468f89caee1f7f4756770c2',
code: 'v!',
imageType: 'png',
animated: false,
userId: '5561169bd6b9d206222a8c19',
modifier: true,
},
{
id: '6468f8d1aee1f7f4756770cf',
code: 'z!',
imageType: 'png',
animated: false,
userId: '5561169bd6b9d206222a8c19',
modifier: true,
},
{
id: '64e3b31920cb0d25d950a9f9',
code: 'w!',
imageType: 'png',
animated: false,
userId: '5561169bd6b9d206222a8c19',
modifier: true,
},
{
id: '65cbe7dbaed093b2eaf87c65',
code: 'p!',
imageType: 'png',
animated: false,
userId: '5561169bd6b9d206222a8c19',
modifier: true,
},
])

const user = await bttvApi
.get<BetterTTV.UserResponse>(`/users/${platform}/${userId}`)
.then(response => response.data)
Expand All @@ -31,6 +98,8 @@ export default async function getChannelEmotes(
static: buildEmoteUrls(emote.id, true),
},
source: 'betterttv',
isModifier:
'modifier' in emote ? (<BetterTTV.GlobalEmote>emote).modifier : false,
})
})
}
Expand Down
1 change: 1 addition & 0 deletions src/services/emotes/FrankerFaceZ.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default async function getChannelEmotes(
static: buildEmoteUrls(emote, true),
},
source: 'frankerfacez',
isModifier: false,
})
})
})
Expand Down
1 change: 1 addition & 0 deletions src/services/emotes/YouTube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export function getGlobalEmotes(): Slime2.Event.Message.EmoteMap {
static: buildEmoteUrls(emoji.image),
},
source: 'youtube',
isModifier: false,
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export function useEmotePart() {
static: buildEmoteUrls(id, true),
},
source: 'twitch',
isModifier: false,
},
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/services/platforms/twitch/chat/transforms/useMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import useUser from './useUser'
/**
* Hook that returns the function {@link transform}
*/
export default function useMessage() {
export default function useMessage(enableEmoteModifiers: boolean = false) {
const { data: channelPointRewards } = useChannelPointRewards()
const transformText = useText()
const transformText = useText(enableEmoteModifiers)
const transformUser = useUser()

/**
Expand Down
96 changes: 95 additions & 1 deletion src/services/platforms/twitch/chat/transforms/useText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useTextPart } from './useTextPart'
/**
* Hook that returns the function {@link transform}
*/
export default function useText() {
export default function useText(enableEmoteModifiers: boolean = false) {
const { data: cheermotes } = useCheermotes()
const transformTextPart = useTextPart()
const transformCheerPart = useCheerPart()
Expand Down Expand Up @@ -47,8 +47,102 @@ export default function useText() {
}
})

let emoteModifiersQueued: Slime2.Event.Message.Emote[] = []
for (let i = 0; i < parts.length; i++) {
const part = parts[i]
if (part.type === 'emote') {
if (part.emote.isModifier) {
emoteModifiersQueued.push(part.emote)
if (
i + 1 < parts.length &&
parts[i + 1].type === 'text' &&
parts[i + 1].text.trim() === ''
) {
// Drop whitespace seperating the modifier from the following emote
parts.splice(i + 1, 1)
}
} else if (emoteModifiersQueued.length > 0) {
i -= emoteModifiersQueued.length
parts.splice(i, emoteModifiersQueued.length)

part.modifier = ''
emoteModifiersQueued.forEach(emote => {
switch (emote.name) {
case 'c!': // Cursed
part.modifier +=
'filter: grayscale(1) brightness(.7) contrast(2.5);'
break
case 'h!': // Horizontal Flip
part.modifier += 'transform: scaleX(-1);'
break
case 'l!': // Left Rotate
part.modifier += 'transform: rotate(-90deg);'
break
case 'p!': // Party
part.modifier +=
'animation: slime2-emote-modifier-party 1.5s linear infinite;'
break
case 'r!': // Right Rotate
part.modifier += 'transform: rotate(90deg);'
break
case 'v!': // Vertical Flip
part.modifier += 'transform: scaleY(-1);'
break
case 'w!': // Wide
part.modifier += 'transform: scaleX(3);'
// We can't hardcod emote size to make widened emote spacing work.
// Instead, add blank emotes before and after this emote, for easy spacing no matter what widget
parts.splice(i + 1, 0, BLANK_EMOTE_PART)
parts.splice(i, 0, BLANK_EMOTE_PART)
i += 1
break
case 'z!': // Zero Space
// Simply remove the whitespace-only text part before this emote
if (
i - 1 < parts.length &&
parts[i - 1].type === 'text' &&
parts[i - 1].text.trim() === ''
) {
parts.splice(i - 1, 1)
}
break
}
})
emoteModifiersQueued = []

if (part.modifier === '') part.modifier = undefined
}
} else if (emoteModifiersQueued.length > 0) {
emoteModifiersQueued = []
}
}
console.log(parts)

return parts
}

return transform
}

const BLANK_EMOTE_PART: Twitch.Event.Message.Part = {
type: 'emote',
text: '',
emote: {
id: '',
name: ':blank:',
images: {
default: {
x1: 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
x2: 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
x4: 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
},
static: {
x1: 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
x2: 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
x4: 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
},
},
source: 'betterttv',
isModifier: false,
},
}
2 changes: 1 addition & 1 deletion src/services/platforms/twitch/chat/useEmulate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function useEmulateTwitchMessage() {
const emotes = [
...channelEmotes!,
...Array.from(thirdPartyEmoteMap!.values()),
]
].filter(e => !e.isModifier)

const date = new Date()
const first = Random.chance(5) // 5% chance of being first time chat
Expand Down
1 change: 1 addition & 0 deletions src/services/platforms/twitch/useChannelEmotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default function useChannelEmotes() {
static: buildEmoteUrls(id, true),
},
source: 'twitch',
isModifier: false,
}
})
},
Expand Down