【问题标题】:Userscript that requires microphone access prompts for permissions in Chrome multiple times需要麦克风访问权限的用户脚本多次提示 Chrome 中的权限
【发布时间】:2014-08-05 22:56:49
【问题描述】:

我为一个名为 TagPro 的游戏编写了一个在页面上运行的用户脚本,该游戏允许语音识别。它会监听关键词和短语,并根据单词将短语放入不同的聊天频道。

我正在 Chrome 的 Tampermonkey 扩展程序中运行脚本。我使用的语音识别库是Annyang

在每场游戏开始时,Chrome 都会通过如下提示确认您要允许该网站使用您的麦克风:

我遇到的问题是,有时在游戏进行到一半时,提示会再次出现。它不会在每场比赛中发生,但当它发生时,它通常会发生不止一次。我没有注意到任何可以使其可重现的模式。这使得诊断变得非常困难,更不用说修复了。我的脚本是否存在可能导致此问题的错误?

// ==UserScript==
// @name          TagPro Speech To Text
// @namespace     http://www.reddit.com/u/undergroundmonorail
// @description   Say a message out loud to say it into chat.
// @include       http://tagpro-*.koalabeast.com:*
// @include       http://tangent.jukejuice.com:*
// @include       http://maptest.newcompte.fr:*
// @include       http://justletme.be*
// @license       MIT
// @author        monorail
// @version       0.2
// ==/UserScript==

(function() {
    // https://github.com/TalAter/annyang
    // I couldn't figure out how to load it dynamically, so I just copypasted
    // the minified version.
    // @require works in Firefox, but not Chrome, and this is way easier than
    // any alternative I found.
    (function(a){"use strict";var b=this,c=b.SpeechRecognition||b.webkitSpeechRecognition||b.mozSpeechRecognition||b.msSpeechRecognition||b.oSpeechRecognition;if(!c)return b.annyang=null,a;var d,e,f=[],g={start:[],error:[],end:[],result:[],resultMatch:[],resultNoMatch:[],errorNetwork:[],errorPermissionBlocked:[],errorPermissionDenied:[]},h=0,i=!1,j="font-weight: bold; color: #00f;",k=/\s*\((.*?)\)\s*/g,l=/(\(\?:[^)]+\))\?/g,m=/(\(\?)?:\w+/g,n=/\*\w+/g,o=/[\-{}\[\]+?.,\\\^$|#]/g,p=function(a){return a=a.replace(o,"\\$&").replace(k,"(?:$1)?").replace(m,function(a,b){return b?a:"([^\\s]+)"}).replace(n,"(.*?)").replace(l,"\\s*$1?\\s*"),new RegExp("^"+a+"$","i")},q=function(a){a.forEach(function(a){a.callback.apply(a.context)})},r=function(){d===a&&b.annyang.init({},!1)};b.annyang={init:function(k,l){l=l===a?!0:!!l,d&&d.abort&&d.abort(),d=new c,d.maxAlternatives=5,d.continuous=!0,d.lang="en-US",d.onstart=function(){q(g.start)},d.onerror=function(a){switch(q(g.error),a.error){case"network":q(g.errorNetwork);break;case"not-allowed":case"service-not-allowed":e=!1,(new Date).getTime()-h<200?q(g.errorPermissionBlocked):q(g.errorPermissionDenied)}},d.onend=function(){if(q(g.end),e){var a=(new Date).getTime()-h;1e3>a?setTimeout(b.annyang.start,1e3-a):b.annyang.start()}},d.onresult=function(a){q(g.result);for(var c,d=a.results[a.resultIndex],e=0;e<d.length;e++){c=d[e].transcript.trim(),i&&b.console.log("Speech recognized: %c"+c,j);for(var h=0,k=f.length;k>h;h++){var l=f[h].command.exec(c);if(l){var m=l.slice(1);return i&&(b.console.log("command matched: %c"+f[h].originalPhrase,j),m.length&&b.console.log("with parameters",m)),f[h].callback.apply(this,m),q(g.resultMatch),!0}}}return q(g.resultNoMatch),!1},l&&(f=[]),k.length&&this.addCommands(k)},start:function(b){r(),b=b||{},e=b.autoRestart!==a?!!b.autoRestart:!0,h=(new Date).getTime(),d.start()},abort:function(){r(),e=!1,d.abort()},debug:function(a){i=arguments.length>0?!!a:!0},setLanguage:function(a){r(),d.lang=a},addCommands:function(a){var c,d;r();for(var e in a)if(a.hasOwnProperty(e)){if(c=b[a[e]]||a[e],"function"!=typeof c)continue;d=p(e),f.push({command:d,callback:c,originalPhrase:e})}i&&b.console.log("Commands successfully loaded: %c"+f.length,j)},removeCommands:function(a){a=Array.isArray(a)?a:[a],f=f.filter(function(b){for(var c=0;c<a.length;c++)if(a[c]===b.originalPhrase)return!1;return!0})},addCallback:function(c,d,e){if(g[c]!==a){var f=b[d]||d;"function"==typeof f&&g[c].push({callback:f,context:e||this})}}}}).call(this);

    // The following code is the function for sending a chat message. This is
    // how every userscript that touches chat does it. It's almost definitely
    // not related to the problem.

    var lastMessage = 0;

    var chat = function(message, all) {
        var limit = 500 + 10;
        var now = new Date();
        var timeDiff = now - lastMessage;
        if (timeDiff > limit) {
            tagpro.socket.emit("chat", {
                message: message,
                toAll: all
            });
            lastMessage = new Date();
        } else if (timeDiff >= 0) {
            setTimeout(chat, limit - timeDiff, chatMessage)
        }
    }

    // Code that I wrote begins here.

    var team = function(message) { chat(message, 0); };
    var all = function(message) { chat(message, 1); };
    var group = function(message) {
        if (tagpro.group.socket) {tagpro.group.socket.emit('chat', message);}
    };

    commands = { 'say *message': all,
                 'team *message': team,
                 'group *message': group };

    annyang.addCommands(commands);

    annyang.start();

})();

【问题讨论】:

  • 感谢@BrockAdams 的编辑。 greasemonkey 描述说如果您的问题涉及tampermonkey,请使用它。如果错了,应该改吗?
  • 那是很久以前遗留下来的。我更新了标签 wiki。

标签: javascript google-chrome tampermonkey


【解决方案1】:

Chrome 的语音识别实施只是不时停止。 为了处理这个问题,annyang 重新启动它(除非它被您或用户手动停止)。由于您的页面不使用 HTTPS,因此您授予 Chrome 在该页面上使用语音识别的权限不会持续存在,它会一次又一次地请求用户许可。 这就是为什么在页面上使用语音识别时建议使用 HTTPS。

【讨论】:

    【解决方案2】:

    这很有趣。

    所以我取消了annyang.js 以找到onendonerror 函数。在onerror 调用中,我控制台记录了错误以获取:

    注意error: "no-speech"

    你知道这是怎么回事......

    查看 W3 规范:https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html#dfn-sre.nospeech

    上面写着:

    “不说话” 未检测到语音。

    长话短说,你太安静了。

    至于预防 - 深入了解 annyang.js,尤其是 onerror 事件。或者在需要时反复唱 ABC。

    【讨论】:

      【解决方案3】:

      由于在 iframe 中加载 AJAX 用于各种目的,您可能会收到额外的提示。 将您的代码包装在框架检查中:

      if (window.top == window.self) {
          //-- Only run on the master (top) page...
          (function() {
              // https://github.com/TalAter/annyang
              // I couldn't figure out how to load it dynamically, so I just copypasted
              // the minified version.
              // @require works in Firefox, but not Chrome, and this is way easier than
              // any alternative I found.
              (function(a){"use strict";var b=this,c=b.SpeechRecognition||b.webkitSpeechRecognition|| ...
      
          // etc...
      }
      


      要完全消除提示,您必须使用成熟的扩展程序。然后您可以使用this other answer 中的技术。 (但是,如果您想轻松共享扩展程序,您还必须与 Chrome 商店有关。)

      【讨论】:

      • 既然您已经提到了这种可能性,我可能会考虑创建一个实际的扩展。我想弄清楚这一点已经有一段时间了,但是没有灵感的项目我学习不好。现在我有一个项目,我想我会试一试!谢谢你。 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-04
      相关资源
      最近更新 更多