mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-08 07:10:54 +00:00
4.20.20
* Changed: Show the verified badge on rich chat embeds for Twitch partner channels. * API Added: More flexible support for tokens when building chat embeds. * API Added: Experiment for using the API to look-up links, rather than the socket cluster.
This commit is contained in:
parent
a4fa1d1491
commit
05e8428a4a
7 changed files with 96 additions and 12 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.20.19",
|
"version": "4.20.20",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -7,12 +7,20 @@
|
||||||
{"value": false, "weight": 100}
|
{"value": false, "weight": 100}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"all_points": {
|
"api_links": {
|
||||||
"name": "Override Channel Points Rendering",
|
"name": "API-Based Link Lookups",
|
||||||
"description": "Override rendering for all channel points messages, even when no message is present.",
|
"description": "Use the new API to look up links instead of the socket cluster.",
|
||||||
"groups": [
|
"groups": [
|
||||||
{"value": true, "weight": 50},
|
{"value": true, "weight": 50},
|
||||||
{"value": false, "weight": 50}
|
{"value": false, "weight": 50}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"all_points": {
|
||||||
|
"name": "Override Channel Points Rendering",
|
||||||
|
"description": "Override rendering for all channel points messages, even when no message is present.",
|
||||||
|
"groups": [
|
||||||
|
{"value": true, "weight": 100},
|
||||||
|
{"value": false, "weight": 0}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import {has, timeout} from 'utilities/object';
|
import {has, timeout} from 'utilities/object';
|
||||||
|
import {ALLOWED_ATTRIBUTES, ALLOWED_TAGS} from 'utilities/constants';
|
||||||
|
|
||||||
const ERROR_IMAGE = 'https://static-cdn.jtvnw.net/emoticons/v1/58765/2.0';
|
const ERROR_IMAGE = 'https://static-cdn.jtvnw.net/emoticons/v1/58765/2.0';
|
||||||
|
|
||||||
|
@ -95,9 +96,34 @@ export default {
|
||||||
else if ( typeof token !== 'object' )
|
else if ( typeof token !== 'object' )
|
||||||
out.push(token);
|
out.push(token);
|
||||||
|
|
||||||
else {
|
else if ( token.type === 't') {
|
||||||
const el = h(token.tag || 'span', {
|
const content = {};
|
||||||
|
if ( token.content )
|
||||||
|
for(const [key,val] of Object.entries(token.content))
|
||||||
|
content[key] = this.renderTokens(val, h);
|
||||||
|
|
||||||
|
out = out.concat(this.tList(token.key, token.phrase, content));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const tag = token.tag || 'span';
|
||||||
|
if ( ! ALLOWED_TAGS.includes(tag) ) {
|
||||||
|
console.log('Skipping disallowed tag', tag);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrs = {};
|
||||||
|
if ( token.attrs ) {
|
||||||
|
for(const [key,val] of Object.entries(token.attrs)) {
|
||||||
|
if ( ! ALLOWED_ATTRIBUTES.includes(key) && ! key.startsWith('data-') )
|
||||||
|
console.log('Skipping disallowed attribute', key);
|
||||||
|
else
|
||||||
|
attrs[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = h(tag, {
|
||||||
class: token.class,
|
class: token.class,
|
||||||
|
attrs
|
||||||
}, this.renderTokens(token.content, h));
|
}, this.renderTokens(token.content, h));
|
||||||
|
|
||||||
out.push(el);
|
out.push(el);
|
||||||
|
|
|
@ -1536,10 +1536,15 @@ export default class Chat extends Module {
|
||||||
cbs[success ? 0 : 1](data);
|
cbs[success ? 0 : 1](data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.experiments.getAssignment('api_links') )
|
||||||
|
timeout(fetch(`https://api-test.frankerfacez.com/v2/link?url=${encodeURIComponent(url)}`).then(r => r.json()), 15000)
|
||||||
|
.then(data => handle(true, data))
|
||||||
|
.catch(err => handle(false, err));
|
||||||
|
|
||||||
timeout(this.socket.call('get_link', url), 15000)
|
else
|
||||||
.then(data => handle(true, data))
|
timeout(this.socket.call('get_link', url), 15000)
|
||||||
.catch(err => handle(false, err));
|
.then(data => handle(true, data))
|
||||||
|
.catch(err => handle(false, err));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -143,6 +143,16 @@ export const Users = {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( user.roles?.isPartner ) {
|
||||||
|
if ( ! title_tokens )
|
||||||
|
title_tokens = [title];
|
||||||
|
|
||||||
|
title_tokens = {tag: 'div', class: 'tw-flex tw-align-items-center', content: [
|
||||||
|
{tag: 'div', content: title_tokens},
|
||||||
|
{tag: 'figure', class: 'tw-mg-l-05 ffz-i-verified tw-c-text-link', content: []}
|
||||||
|
]};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: token.url,
|
url: token.url,
|
||||||
accent: user.primaryColorHex ? `#${user.primaryColorHex}` : null,
|
accent: user.primaryColorHex ? `#${user.primaryColorHex}` : null,
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {timeout, has} from 'utilities/object';
|
import {timeout, has} from 'utilities/object';
|
||||||
|
import {ALLOWED_ATTRIBUTES, ALLOWED_TAGS} from 'utilities/constants';
|
||||||
|
|
||||||
const ERROR_IMAGE = 'https://static-cdn.jtvnw.net/emoticons/v1/58765/2.0';
|
const ERROR_IMAGE = 'https://static-cdn.jtvnw.net/emoticons/v1/58765/2.0';
|
||||||
|
|
||||||
|
@ -104,9 +105,34 @@ export default class RichContent extends Module {
|
||||||
else if ( typeof token !== 'object' )
|
else if ( typeof token !== 'object' )
|
||||||
out.push(token);
|
out.push(token);
|
||||||
|
|
||||||
else {
|
else if ( token.type === 't' ) {
|
||||||
const el = createElement(token.tag || 'span', {
|
const content = {};
|
||||||
className: token.class
|
if ( token.content )
|
||||||
|
for(const [key,val] of Object.entries(token.content))
|
||||||
|
content[key] = this.renderTokens(val);
|
||||||
|
|
||||||
|
out = out.concat(t.i18n.tList(token.key, token.phrase, content));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const tag = token.tag || 'span';
|
||||||
|
if ( ! ALLOWED_TAGS.includes(tag) ) {
|
||||||
|
console.log('Skipping disallowed tag', tag);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrs = {};
|
||||||
|
if ( token.attrs ) {
|
||||||
|
for(const [key,val] of Object.entries(token.attrs)) {
|
||||||
|
if ( ! ALLOWED_ATTRIBUTES.includes(key) && ! key.startsWith('data-') )
|
||||||
|
console.log('Skipping disallowed attribute', key);
|
||||||
|
else
|
||||||
|
attrs[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = createElement(tag, {
|
||||||
|
className: token.class,
|
||||||
|
...attrs
|
||||||
}, this.renderTokens(token.content));
|
}, this.renderTokens(token.content));
|
||||||
|
|
||||||
out.push(el);
|
out.push(el);
|
||||||
|
|
|
@ -18,6 +18,15 @@ export const LV_SERVER = 'https://cbenni.com/api';
|
||||||
export const LV_SOCKET_SERVER = 'wss://cbenni.com/socket.io/';
|
export const LV_SOCKET_SERVER = 'wss://cbenni.com/socket.io/';
|
||||||
|
|
||||||
|
|
||||||
|
export const ALLOWED_TAGS = [
|
||||||
|
'strong', 'em', 'i', 'b', 'time', 'br', 'hr', 'div', 'span', 'img', 'figure', 'p', 'a', 'video', 'audio', 'blockquote', 'heading', 'section', 'nav', 'footer', 'aside', 'article', 'source', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ALLOWED_ATTRIBUTES = [
|
||||||
|
'datetime', 'src', 'href', 'style', 'alt', 'title', 'height', 'width', 'srcset', 'autoplay', 'volume', 'muted', 'loop', 'poster', 'type'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
export const KEYS = {
|
export const KEYS = {
|
||||||
Enter: 13,
|
Enter: 13,
|
||||||
Escape: 27,
|
Escape: 27,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue