Search code examples
javascriptgreasemonkeyuserscriptstampermonkey

Simply `@require`-ing a 3rd party library results in javascript errors?


I'm trying to create a userscript that runs another person's Istrolid AI library. But when I run it, I get:

ERROR: Execution of script 'New Userscript' failed! Interpolator is not defined

My userscript looks like this:

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        www.istrolid.com
// @grant        none
// @require      https://rawgit.com/Rio6/Istrolid-js-ai/master/r26Ai.js
// ==/UserScript==

console.log(r26Ai);

Solution

  • Several issues, big to small:

    1. That library is only valid on game pages (which is where Interpolator is defined).

    2. The @match directive is improper and needs to be set to just those game pages.

    3. The game takes some time to load and initialize -- at least a second.

    4. That library is poorly written and will crash if it's loaded before then.

      Which means that @require can't be used for that library. For now, use script injection instead.

      You will need to use a timer (last resort) or event (Best option) or strategic node (second best option) to tell when to inject the script. Finding an event (if it exists), or strategic node is a bit of an art and page specific. That's beyond scope here, so a timer is used in the sample code below.

    5. The other directives, especially the @name should be set to sensible values or omitted.

    Putting it all together, this script will get you to the next step (which is beyond the scope of this question):

    // ==UserScript==
    // @name         Istrolid.com, use Istrolid Javascript AI API
    // @version      0.2
    // @match        *://www.istrolid.com/game.html*
    // @grant        none
    // ==/UserScript==
    
    /*-- Wait for game to load.  Try to find an event or node that signals
        this, instead of one or two timers.
    */
    var sfStrtTmr  = setInterval ( () => {
        if (typeof Interpolator  === "function") {
            clearInterval (sfStrtTmr);
            setTimeout (loadPoorScript, 1111);
        }
    }, 333);
    
    function loadPoorScript () {
        var newNode     = document.createElement ('script');
        newNode.onload  = runScriptMain;
        newNode.src     = "https://rawgit.com/Rio6/Istrolid-js-ai/master/r26Ai.js";
        document.body.appendChild (newNode);
    }
    
    function runScriptMain () {
        //  ALL OF YOUR CODE GOES HERE.
        console.log ("r26Ai: ",r26Ai);
    }
    

    Important: Note that Interpolator is a page specific function, unique to that site, that we use here as indicator that (a) we're running on an appropriate page and (b) it's time to start checking for game load.