2

Is there any way to use eg BetterTTV or FranzerFaceZ (FFZ) on a website other than twitch.tv to replace emotes?

I want to show onstream alerts on twitch (implemented via browser source). The messages sent often contain emotes like OMEGALUL or PogU.

I know I could download the images and replace the emotes in the string. That is already working. However, there are always some emotes missing (because I would need to implement them all).

So is there a way to use one of the emote replacement systems on a page other than twitch?

So eg this message

Hey PogU :)

should become

Hey <img src="...."> :)

I did not find any possibilities, but as there is at least for BetterTTV an option to implement the javascript file for there has to be a way.

Alexander Taubenkorb
  • 3,031
  • 2
  • 28
  • 30

1 Answers1

1

Not what I initially intended, as this is limited to the emotes I downloaded, but my quick and dirty approach was (using Laravel - so pure php would be without helpers like storage_path or dd):

Caution: Those downloads take quite some time. So you will run into timeouts if you just run them with an eg 30s php timeout. So either start it from CLI or set the time limit to infinite (eg set_time_limit(0); or in php.ini: max_execution_time = 0)

Step 1: Download the emotes

Alternative #1: BTTV

<?php

// Download emotes first and put them into a json (multiple times using offset): https://api.betterttv.net/3/emotes/shared/top?offset=1199&limit=100
// Merge them into one emotes.json file
function downloadBTTV() {
    $emotes = json_decode(file_get_contents(storage_path('emotes.json')), false, 512, JSON_THROW_ON_ERROR);
    $used_filenames = [];
    $used_emotes = [];
    foreach($emotes as $emote) {
        try{
            $url = 'https://cdn.betterttv.net/emote/' . $emote->emote->id .  '/1x';
            $filename = $emote->emote->code . '.' . $emote->emote->imageType;
            if(!isset($used_filenames[$filename])) {
                $used_filenames[$filename] = 0;
            }
            $used_filenames[$filename]++;
            $full_filename = storage_path('emotes/'  . $filename);
            if(!is_file($full_filename) && in_array($emotes->emote->code, $used_emotes)) {
                file_put_contents($full_filename, file_get_contents($url));
                $used_emotes[] = $emote->emote->code;
            }
        } catch(\Exception $e) {
            dd(get_defined_vars(), $emote);
        }

    }
}

Alternative #2: FFZ

1) Get FFZ image ids

// Open page https://www.frankerfacez.com/emoticons/wall?q=&sort=count-desc&days=0 and scroll a lot down
// Then execute the following js code on the page

var emotes = {};
document.querySelectorAll('a.thumbnail').forEach(function(el, index){
    var href = el.getAttribute('href');
    var name = el.querySelector('h3').textContent.toLowerCase();
    var id = href.substr(href.lastIndexOf("/")+1, href.indexOf('-') - href.lastIndexOf('/') - 1);
    if(!emotes.hasOwnProperty(name)) {
        emotes[name] = id;
    }
});
console.log(Object.keys(emotes).length)
copy(emotes);

2) Download the images in PHP

<?php
// 2a) Create a variable like that from the clipboard (copied in javascript in step 1)
$ffz_emotes_json = <<<'EOL'
{
  "pog": "210748",
  "omegalul": "128054",
  //...
}
EOL;

function downloadFFZ() {
    global $ffz_emotes_json;

    $emotes = json_decode($ffz_emotes_json, true, 512, JSON_THROW_ON_ERROR);
    $new_emotes = [];
    $skipped_emotes = [];
    foreach($emotes as $emote => $id) {
        $url = 'https://cdn.frankerfacez.com/emoticon/' . $id .  '/1';
        $filename = $emote . '.png';
        $filename_gif = $emote . '.gif';

        $full_filename_png = storage_path('emotes/'  . $filename);
        $full_filename_gif = storage_path('emotes/'  . $filename_gif);

        if(!is_file($full_filename_png) && !is_file($full_filename_gif) ) {
            file_put_contents($full_filename_png, file_get_contents($url));
            $new_emotes[] = $emote;
        } else {
            $skipped_emotes[] = $emote;
        }
    }
    dd($new_emotes, $skipped_emotes);
}

Step 2: Create the emotes dictionary

Now, that you have all those images stored in your storage path, we need to create a dictionary to look them up (as in this approach it is done via javascript).

<?php
function generateEmotesJS()
{
    $files = glob(public_path('img/emotes/*'));
    echo "window.EMOTES = {\n";
    /** @var DirectoryIterator $fileInfo */
    foreach (new DirectoryIterator(public_path('img/emotes/*')) as $fileInfo) {
        if($fileInfo->isDot()) continue;
        echo '    "' . $fileInfo->getBasename('.' .$fileInfo->getExtension()) .'": "' . $fileInfo->getExtension() .  "\",\n";
    }
    echo "};";
}
// store this output in your javascript file

Step 2: Replace emotes in messages

// You already have something like this in your file from the previous step
window.EMOTES = {
    "!treat": "png",
    "(ditto)": "gif",
    "02ayaya": "png",
    // ...
};

// call this function to replace the emotes:
window.replaceEmotes = function(message)
{
    // https://stackoverflow.com/a/59664804/936284
    return message.split(/([\W])/).map(function(el, index){
        if(index % 2 === 0) {
            lower = el.toLowerCase();
            if(EMOTES.hasOwnProperty(lower)) {
                return '<img src="/img/emotes/' + lower + '.' + EMOTES[lower] + '" alt="' + el + '">';
            }
        }
        return el;
    }).join("")
}

// You can now call this:
console.log(replaceEmotes('Hey PogU :)'));
// Returns: Hey <img src="/img/emotes/pogu.png" alt="PogU"> :)
console.log(replaceEmotes("Hey ho OMEGALUL"));
// Returns Hey ho <img src="/img/emotes/omegalul.png" alt="OMEGALUL">
console.log(replaceEmotes("Test Kappa\nTest > all <3"));
// Returns: Test <img src="/img/emotes/kappa.png" alt="Kappa">
// Test > all <3

Like I said, quick and dirty. If you have time, you sure find a better approach. Maybe reverse-engineering the chrome extensions for replacement makes more sense ;)

Alexander Taubenkorb
  • 3,031
  • 2
  • 28
  • 30