Skip to content

Instantly share code, notes, and snippets.

@chrahunt
Last active January 7, 2016 03:38
Show Gist options
  • Select an option

  • Save chrahunt/6ad88b678838fd1218f3 to your computer and use it in GitHub Desktop.

Select an option

Save chrahunt/6ad88b678838fd1218f3 to your computer and use it in GitHub Desktop.

Revisions

  1. chrahunt revised this gist Jan 7, 2016. 1 changed file with 168 additions and 0 deletions.
    168 changes: 168 additions & 0 deletions tagpro-talk.user.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,168 @@
    // ==UserScript==
    // @name TagPro Talk
    // @namespace http://reddit.com/user/snaps_
    // @description Talk in TagPro
    // @require https://gist.github.com/chrahunt/4843f0258c516882eea0/raw/loopback.user.js
    // @require https://craig.global.ssl.fastly.net/js/mousetrap/mousetrap.min.js
    // @downloadURL https://gist.github.com/chrahunt/todo
    // @include http://tagpro-*.koalabeast.com:*
    // @include http://maptest*.newcompte.fr:*
    // @include http://tangent.jukejuice.com:*
    // @license MIT
    // @author snaps
    // @version 0.1.0
    // @grant GM_getResourceURL
    // @run-at document-start
    // ==/UserScript==

    var HTTPS_LINK = "https://rawgit.com/chrahunt/6ad88b678838fd1218f3/raw/9a2a66b109859bc601998ef4003cd39f6433b07c/listen.html";

    var ORIGIN_REGEX = /^(.*?:\/\/.*?)\//;

    // 2-way frame communication, implements protocol.
    function Frame(url) {
    var iframe = document.createElement("iframe");
    iframe.src = url;
    iframe.style.display = "none";
    this.origin = url.match(ORIGIN_REGEX)[1];
    this.listeners = {};

    var self = this;
    // Introduce self.
    iframe.addEventListener("load", function () {
    self.target = iframe.contentWindow;
    self.send("init");
    }, false);

    window.addEventListener("message", function (event) {
    var origin = event.origin || event.originalEvent.origin;
    if (origin !== self.origin || event.source !== self.target)
    return;
    var message = event.data;
    var name = message.name;
    console.log("Content: Message received: '" + name + "'");
    if (self.listeners.hasOwnProperty(name)) {
    console.log("Content: Calling listeners.");
    self.listeners[name].forEach(function (fn) {
    fn(message.data);
    });
    } else {
    console.log("Content: No listeners set");
    }
    });

    document.body.appendChild(iframe);
    }

    Frame.prototype.send = function(name, data) {
    var arg = {
    name: name
    };
    if (typeof data !== "undefined") {
    arg.data = data;
    }
    this.target.postMessage(arg, this.origin);
    };

    Frame.prototype.on = function(name, fn) {
    if (!this.listeners.hasOwnProperty(name)) {
    this.listeners[name] = [];
    }
    this.listeners[name].push(fn);
    };

    Frame.prototype.removeListener = function(name, fn) {
    if (this.listeners.hasOwnProperty(name)) {
    var i = this.listeners[name].indexOf(fn);
    if (i !== -1) {
    this.listeners[name].splice(i, 1);
    }
    }
    };

    function Messenger(socket) {
    this.socket = socket;
    }

    Messenger.prototype.send = function(msg) {
    this.socket.emit("chat", {
    message: msg.substring(0, 70),
    toAll: true
    });
    };

    // Listen for things to happen with these keys
    function KeyState(start, reset, send) {
    this.listening = false;
    }

    KeyState.prototype.onstart = function(fn) {
    // body...
    };

    function showInfo(msg) {
    tagpro.socket.emit("local:chat", {
    to: "all",
    from: null,
    message: msg
    });
    }

    // When key is pressed, start listening. As words come in, display in chat element look-alike.
    // When done, allow pressing enter to send.
    // or escape to stop

    // override tagpro hotkey functionality when active
    // stop listening only when disabled
    // if text at end of input, flash red.

    setTimeout(function setup() {
    // Wait for TagPro to get chat socket
    if (typeof tagpro == "undefined" || !tagpro.socket) {
    setTimeout(setup, 500);
    return;
    }

    var messenger = new Messenger(tagpro.socket);
    var frame = new Frame(HTTPS_LINK);
    var recognizing = false;
    var output = "";
    Mousetrap.bind("ctrl+z", function (e) {
    if (recognizing) {
    console.log("Content: Stopping recognition.");
    frame.send("sr.stop");
    if (output !== "") {
    console.log("Sending message: " + output);
    messenger.send(output);
    output = "";
    }
    recognizing = false;
    } else {
    console.log("Content: Starting recognition.");
    frame.send("sr.start");
    recognizing = true;
    }
    return false;
    });

    var input = $("<div>");
    var first = $("<span>");
    first.css({color: "white"});
    var second = $("<span>");
    second.css({color: "gray"});
    input.append(first);
    input.append(second);
    $("body").append(input);

    frame.on("sr.result", function (result) {
    first.text(result.final_transcript);
    output = result.final_transcript;
    second.text(result.interim_transcript);
    });

    frame.on("sr.error", function (result) {
    console.log("SR error: %o", result);
    });
    // initialize key listener
    // on keypress
    }, 50);
  2. chrahunt revised this gist Jan 7, 2016. 1 changed file with 17 additions and 0 deletions.
    17 changes: 17 additions & 0 deletions listen.html
    Original file line number Diff line number Diff line change
    @@ -74,6 +74,20 @@

    var self = new InnerFrame(TAGPRO_URLS);

    // Multiline Function String - Nate Ferrero - Public Domain
    // http://stackoverflow.com/a/14496573/1698058
    function heredoc (f) {
    return f.toString().match(/\/\*\s*([\s\S]*?)\s*\*\//m)[1];
    }

    var grammar = heredoc(function(){/*
    #JSGF V1.0;
    grammar terms;
    public <powerup> = tagpro | juke juice | rolling bomb;
    */});

    // Initialize web speech.
    var recognition, status, error, start_timestamp;
    if (!window.webkitSpeechRecognition) {
    @@ -83,6 +97,9 @@
    status = "idle";
    var ignore_onend = false;
    recognition = new webkitSpeechRecognition();
    var speechRecognitionList = new webkitSpeechGrammarList();
    speechRecognitionList.addFromString(grammar, 1);
    recognition.grammars = speechRecognitionList;
    recognition.continuous = true;
    recognition.interimResults = true;

  3. chrahunt revised this gist Jan 7, 2016. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions listen.html
    Original file line number Diff line number Diff line change
    @@ -24,6 +24,7 @@
    if (message.name === "init") {
    console.log("Listener: Initializing 2-way.");
    self.target = event.source;
    self.origin = event.origin;
    initialized = true;
    }
    } else if (event.source === self.target) {
  4. chrahunt revised this gist Jan 7, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion listen.html
    Original file line number Diff line number Diff line change
    @@ -30,7 +30,7 @@
    var message = event.data;
    var name = message.name;
    console.log("Listener: Message received '" + name + "'");
    if (self.listeners.hasOwnProperty[name]) {
    if (self.listeners.hasOwnProperty(name)) {
    console.log("Calling listeners.");
    self.listeners[name].forEach(function (fn) {
    fn(message.data);
  5. chrahunt revised this gist Jan 7, 2016. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion listen.html
    Original file line number Diff line number Diff line change
    @@ -36,7 +36,7 @@
    fn(message.data);
    });
    } else {
    console.log("No listeners set.");
    console.log("Listener: No listeners set.");
    }
    } else {
    console.warn("Listener: Sender not recognized for message.");
    @@ -55,6 +55,7 @@
    };

    InnerFrame.prototype.on = function(name, fn) {
    console.log("Listener: Adding function for '" + name + "'");
    if (!this.listeners.hasOwnProperty(name)) {
    this.listeners[name] = [];
    }
  6. chrahunt revised this gist Jan 7, 2016. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions listen.html
    Original file line number Diff line number Diff line change
    @@ -31,9 +31,12 @@
    var name = message.name;
    console.log("Listener: Message received '" + name + "'");
    if (self.listeners.hasOwnProperty[name]) {
    console.log("Calling listeners.");
    self.listeners[name].forEach(function (fn) {
    fn(message.data);
    });
    } else {
    console.log("No listeners set.");
    }
    } else {
    console.warn("Listener: Sender not recognized for message.");
  7. chrahunt revised this gist Jan 7, 2016. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions listen.html
    Original file line number Diff line number Diff line change
    @@ -22,17 +22,21 @@
    // TODO: Check for URLs.
    var message = event.data;
    if (message.name === "init") {
    console.log("Listener: Initializing 2-way.");
    self.target = event.source;
    initialized = true;
    }
    } else if (event.source === self.target) {
    var message = event.data;
    var name = message.name;
    console.log("Listener: Message received '" + name + "'");
    if (self.listeners.hasOwnProperty[name]) {
    self.listeners[name].forEach(function (fn) {
    fn(message.data);
    });
    }
    } else {
    console.warn("Listener: Sender not recognized for message.");
    }
    }, false);
    }
  8. chrahunt revised this gist Jan 7, 2016. 1 changed file with 8 additions and 3 deletions.
    11 changes: 8 additions & 3 deletions listen.html
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@
    // Takes regexes for URLs.
    function InnerFrame(urls) {
    var initialized = false;
    this.id = null;
    this.listeners = {};
    var self = this;

    window.addEventListener("message", function (event) {
    @@ -26,8 +26,13 @@
    initialized = true;
    }
    } else if (event.source === self.target) {

    // ensure source is window.
    var message = event.data;
    var name = message.name;
    if (self.listeners.hasOwnProperty[name]) {
    self.listeners[name].forEach(function (fn) {
    fn(message.data);
    });
    }
    }
    }, false);
    }
  9. chrahunt revised this gist Jan 7, 2016. 1 changed file with 132 additions and 104 deletions.
    236 changes: 132 additions & 104 deletions listen.html
    Original file line number Diff line number Diff line change
    @@ -4,122 +4,150 @@
    <body>
    <script>
    console.log("Listener: Loading page.");
    // TODO: Restrict to specific domains.
    var domains = [];

    window.addEventListener("message", receiveMessage, false);

    var otherwindow = null;
    var start_timestamp;
    var recognition;
    function receiveMessage(event) {
    console.log("Listener: Received message.");
    // TODO: check event origin.
    var data = event.data;
    if (validData(data)) {
    var name = data.name;
    console.log("Listener: Message name: " + name);
    if (name === "start") {
    console.log("Listener: Starting recognition.");
    start_timestamp = data.data.time;
    recognition.start();
    } else if (name === "stop") {
    console.log("Listener: Stopping recognition.");
    recognition.stop();
    } else if (name === "status") {
    console.log("Listener: Status.");
    } else if (name === "init") {
    // Get the other window.
    otherwindow = event.source;
    init();
    } else if (name === "abort") {
    console.log("Listener: Aborting recognition.");
    recognition.abort();

    var TAGPRO_URLS = [
    "http://tagpro-\\w*\\.koalabeast\\.com:\\d+",
    "http://tagpro.gg:\\d+",
    "http://maptest\\d?.newcompte.fr:\\d+"
    ];

    // Takes regexes for URLs.
    function InnerFrame(urls) {
    var initialized = false;
    this.id = null;
    var self = this;

    window.addEventListener("message", function (event) {
    if (!initialized) {
    // TODO: Check for URLs.
    var message = event.data;
    if (message.name === "init") {
    self.target = event.source;
    initialized = true;
    }
    } else if (event.source === self.target) {

    // ensure source is window.
    }
    }
    }, false);
    }

    function sendMessage(name, data) {
    if (otherwindow === null) {
    console.error("Listener: Other window not set!");
    } else {
    console.log("Listener: Sending message: " + name);
    var msg = {
    name: name
    };
    if (typeof data !== "undefined") {
    msg.data = data;
    InnerFrame.prototype.send = function(name, data) {
    var arg = {
    name: name
    };
    if (typeof data !== "undefined") {
    arg.data = data;
    }
    this.target.postMessage(arg, this.origin);
    };

    InnerFrame.prototype.on = function(name, fn) {
    if (!this.listeners.hasOwnProperty(name)) {
    this.listeners[name] = [];
    }
    this.listeners[name].push(fn);
    };

    InnerFrame.prototype.removeListener = function(name, fn) {
    if (this.listeners.hasOwnProperty(name)) {
    var i = this.listeners[name].indexOf(fn);
    if (i !== -1) {
    this.listeners[name].splice(i, 1);
    }
    }
    }
    };

    function validData(o) {
    return o.hasOwnProperty("name");
    }
    var self = new InnerFrame(TAGPRO_URLS);

    // Initialize web speech.
    function init() {
    console.log("Listener: Initializing.");
    if (!window.webkitSpeechRecognition) {
    // bad, say something.
    console.error("Listener: Need Web Speech API");
    } else {
    var ignore_onend = false;
    recognition = new webkitSpeechRecognition();
    recognition.continuous = true;
    recognition.interimResults = true;

    recognition.onstart = function() {
    recognizing = true;
    sendMessage("sr.start");
    };

    recognition.onerror = function(event) {
    if (event.error == 'no-speech') {
    sendMessage("sr.error", "no_speech");
    ignore_onend = true;
    }
    if (event.error == 'audio-capture') {
    sendMessage("sr.error", "no_mic");
    ignore_onend = true;
    }
    if (event.error == 'not-allowed') {
    if (event.timeStamp - start_timestamp < 100) {
    sendMessage("sr.error", "mic_blocked");
    } else {
    sendMessage("sr.error", "mic_denied");
    }
    ignore_onend = true;
    }
    };
    var recognition, status, error, start_timestamp;
    if (!window.webkitSpeechRecognition) {
    status = "error";
    error = "no_api";
    } else {
    status = "idle";
    var ignore_onend = false;
    recognition = new webkitSpeechRecognition();
    recognition.continuous = true;
    recognition.interimResults = true;

    recognition.onend = function() {
    recognizing = false;
    if (ignore_onend) {
    return;
    recognition.onstart = function() {
    status = "listening";
    self.send("sr.start");
    };

    recognition.onerror = function(event) {
    if (event.error == 'no-speech') {
    self.send("sr.error", "no_speech");
    status = "sr.error";
    ignore_onend = true;
    }
    if (event.error == 'audio-capture') {
    self.send("sr.error", "no_mic");
    status = "sr.error";
    ignore_onend = true;
    }
    if (event.error == 'not-allowed') {
    if (event.timeStamp - start_timestamp < 100) {
    self.send("sr.error", "mic_blocked");
    } else {
    self.send("sr.error", "mic_denied");
    }
    sendMessage("sr.end");
    };

    var final_transcript = '';
    recognition.onresult = function(event) {
    var interim_transcript = '';
    for (var i = event.resultIndex; i < event.results.length; ++i) {
    if (event.results[i].isFinal) {
    final_transcript += event.results[i][0].transcript;
    } else {
    interim_transcript += event.results[i][0].transcript;
    }
    status = "sr.error";
    ignore_onend = true;
    }
    };

    recognition.onend = function() {
    if (ignore_onend) {
    return;
    }
    status = "idle";
    self.send("sr.end");
    };

    var final_transcript = '';
    recognition.onresult = function(event) {
    var interim_transcript = '';
    for (var i = event.resultIndex; i < event.results.length; ++i) {
    if (event.results[i].isFinal) {
    final_transcript += event.results[i][0].transcript;
    } else {
    interim_transcript += event.results[i][0].transcript;
    }
    console.log("Listener: Final: " + final_transcript);
    console.log("Listener: Interim: " + interim_transcript);
    sendMessage("sr.result", {
    final_transcript: final_transcript,
    interim_transcript: interim_transcript
    });
    };
    }
    }
    console.log("Listener: Final: " + final_transcript);
    console.log("Listener: Interim: " + interim_transcript);
    self.send("sr.result", {
    final_transcript: final_transcript,
    interim_transcript: interim_transcript
    });
    };
    }

    // Communication initialization.
    self.on("sr.start", function () {
    console.log("Listener: Starting recognition.");
    start_timestamp = Date.now();
    final_transcript = "";
    recognition.start();
    });

    self.on("sr.stop", function () {
    console.log("Listener: Stopping recognition.");
    recognition.stop();
    });

    self.on("sr.status", function () {
    console.log("Listener: Status.");
    });

    self.on("sr.abort", function () {
    console.log("Listener: Aborting recognition.");
    recognition.abort();
    });

    </script>
    </body>
    </html>
  10. chrahunt created this gist Jan 6, 2016.
    125 changes: 125 additions & 0 deletions listen.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,125 @@
    <!DOCTYPE html>
    <html>
    <head></head>
    <body>
    <script>
    console.log("Listener: Loading page.");
    // TODO: Restrict to specific domains.
    var domains = [];

    window.addEventListener("message", receiveMessage, false);

    var otherwindow = null;
    var start_timestamp;
    var recognition;
    function receiveMessage(event) {
    console.log("Listener: Received message.");
    // TODO: check event origin.
    var data = event.data;
    if (validData(data)) {
    var name = data.name;
    console.log("Listener: Message name: " + name);
    if (name === "start") {
    console.log("Listener: Starting recognition.");
    start_timestamp = data.data.time;
    recognition.start();
    } else if (name === "stop") {
    console.log("Listener: Stopping recognition.");
    recognition.stop();
    } else if (name === "status") {
    console.log("Listener: Status.");
    } else if (name === "init") {
    // Get the other window.
    otherwindow = event.source;
    init();
    } else if (name === "abort") {
    console.log("Listener: Aborting recognition.");
    recognition.abort();
    }
    }
    }

    function sendMessage(name, data) {
    if (otherwindow === null) {
    console.error("Listener: Other window not set!");
    } else {
    console.log("Listener: Sending message: " + name);
    var msg = {
    name: name
    };
    if (typeof data !== "undefined") {
    msg.data = data;
    }
    }
    }

    function validData(o) {
    return o.hasOwnProperty("name");
    }

    // Initialize web speech.
    function init() {
    console.log("Listener: Initializing.");
    if (!window.webkitSpeechRecognition) {
    // bad, say something.
    console.error("Listener: Need Web Speech API");
    } else {
    var ignore_onend = false;
    recognition = new webkitSpeechRecognition();
    recognition.continuous = true;
    recognition.interimResults = true;

    recognition.onstart = function() {
    recognizing = true;
    sendMessage("sr.start");
    };

    recognition.onerror = function(event) {
    if (event.error == 'no-speech') {
    sendMessage("sr.error", "no_speech");
    ignore_onend = true;
    }
    if (event.error == 'audio-capture') {
    sendMessage("sr.error", "no_mic");
    ignore_onend = true;
    }
    if (event.error == 'not-allowed') {
    if (event.timeStamp - start_timestamp < 100) {
    sendMessage("sr.error", "mic_blocked");
    } else {
    sendMessage("sr.error", "mic_denied");
    }
    ignore_onend = true;
    }
    };

    recognition.onend = function() {
    recognizing = false;
    if (ignore_onend) {
    return;
    }
    sendMessage("sr.end");
    };

    var final_transcript = '';
    recognition.onresult = function(event) {
    var interim_transcript = '';
    for (var i = event.resultIndex; i < event.results.length; ++i) {
    if (event.results[i].isFinal) {
    final_transcript += event.results[i][0].transcript;
    } else {
    interim_transcript += event.results[i][0].transcript;
    }
    }
    console.log("Listener: Final: " + final_transcript);
    console.log("Listener: Interim: " + interim_transcript);
    sendMessage("sr.result", {
    final_transcript: final_transcript,
    interim_transcript: interim_transcript
    });
    };
    }
    }
    </script>
    </body>
    </html>