Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/bye-lemon_dddd-robot.json
T

1 line
27 KiB
JSON

{"repo": "bye-lemon/dddd-robot", "files": {"client/stt.py": {"3c7d72711243fab2c9e1d6ff82ca2be408d74707": {"t": 1785674320.5955064, "n": 25997, "h": "0dae606fd16eaa5a", "c": "#!/usr/bin/env python2\n# -*- coding: utf-8-*-\nfrom __future__ import print_function\nfrom __future__ import absolute_import\nimport os\nimport base64\nimport wave\nimport json\nimport urlparse\nimport tempfile\nimport logging\nimport urllib\nfrom abc import ABCMeta, abstractmethod\nimport requests\nfrom . import dingdangpath\nfrom . import diagnose\nfrom . import vocabcompiler\nfrom . import config\nfrom uuid import getnode as get_mac\nimport hashlib\nimport datetime\nimport hmac\nimport sys\nimport urllib3\nfrom dateutil import parser as dparser\n\nurllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)\n\ntry:\n reload # Python 2\nexcept NameError: # Python 3\n from importlib import reload\n\nreload(sys)\nsys.setdefaultencoding('utf8')\n\n\nclass AbstractSTTEngine(object):\n \"\"\"\n Generic parent class for all STT engines\n \"\"\"\n\n __metaclass__ = ABCMeta\n VOCABULARY_TYPE = None\n\n @classmethod\n def get_config(cls):\n return {}\n\n @classmethod\n def get_instance(cls, vocabulary_name, phrases):\n profile = cls.get_config()\n if cls.VOCABULARY_TYPE:\n vocabulary = cls.VOCABULARY_TYPE(vocabulary_name,\n path=dingdangpath.config(\n 'vocabularies'))\n if not vocabulary.matches_phrases(phrases):\n vocabulary.compile(phrases)\n profile['vocabulary'] = vocabulary\n instance = cls(**profile)\n return instance\n\n @classmethod\n def get_passive_instance(cls):\n phrases = vocabcompiler.get_keyword_phrases()\n return cls.get_instance('keyword', phrases)\n\n @classmethod\n def get_active_instance(cls):\n phrases = vocabcompiler.get_all_phrases()\n return cls.get_instance('default', phrases)\n\n @classmethod\n def get_music_instance(cls):\n phrases = vocabcompiler.get_all_phrases()\n return cls.get_instance('music', phrases)\n\n @classmethod\n @abstractmethod\n def is_available(cls):\n return True\n\n @abstractmethod\n def transcribe(self, fp):\n pass\n\n @classmethod\n def transcribe_keyword(self, fp):\n pass\n\n\nclass PocketSphinxSTT(AbstractSTTEngine):\n \"\"\"\n The default Speech-to-Text implementation which relies on PocketSphinx.\n \"\"\"\n\n SLUG = 'sphinx'\n VOCABULARY_TYPE = vocabcompiler.PocketsphinxVocabulary\n\n def __init__(self, vocabulary, hmm_dir=\"/usr/local/share/\" +\n \"pocketsphinx/model/hmm/en_US/hub4wsj_sc_8k\", **kwargs):\n \"\"\"\n Initiates the pocketsphinx instance.\n\n Arguments:\n vocabulary -- a PocketsphinxVocabulary instance\n hmm_dir -- the path of the Hidden Markov Model (HMM)\n \"\"\"\n\n self._logger = logging.getLogger(__name__)\n\n # quirky bug where first import doesn't work\n try:\n import pocketsphinx as ps\n except Exception:\n import pocketsphinx as ps\n\n with tempfile.NamedTemporaryFile(prefix='psdecoder_',\n suffix='.log', delete=False) as f:\n self._logfile = f.name\n\n self._logger.debug(\"Initializing PocketSphinx Decoder with hmm_dir \" +\n \"'%s'\", hmm_dir)\n\n # Perform some checks on the hmm_dir so that we can display more\n # meaningful error messages if neccessary\n if not os.path.exists(hmm_dir):\n msg = (\"hmm_dir '%s' does not exist! Please make sure that you \" +\n \"have set the correct hmm_dir in your profile.\") % hmm_dir\n self._logger.error(msg)\n raise RuntimeError(msg)\n # Lets check if all required files are there. Refer to:\n # http://cmusphinx.sourceforge.net/wiki/acousticmodelformat\n # for details\n missing_hmm_files = []\n for fname in ('mdef', 'feat.params', 'means', 'noisedict',\n 'transition_matrices', 'variances'):\n if not os.path.exists(os.path.join(hmm_dir, fname)):\n missing_hmm_files.append(fname)\n mixweights = os.path.exists(os.path.join(hmm_dir, 'mixture_weights'))\n sendump = os.path.exists(os.path.join(hmm_dir, 'sendump'))\n if not mixweights and not sendump:\n # We only need mixture_weights OR sendump\n missing_hmm_files.append('mixture_weights or sendump')\n if missing_hmm_files:\n self._logger.warning(\"hmm_dir '%s' is missing files: %s. Please \" +\n \"make sure that you have set the correct \" +\n \"hmm_dir in your profile.\",\n hmm_dir, ', '.join(missing_hmm_files))\n\n self._decoder = ps.Decoder(hmm=hmm_dir, logfn=self._logfile,\n **vocabulary.decoder_kwargs)\n\n def __del__(self):\n os.remove(self._logfile)\n\n @classmethod\n def get_config(cls):\n # Try to get hmm_dir from config\n return config.get('pocketsphinx', {})\n\n def transcribe(self, fp):\n \"\"\"\n Performs STT, transcribing an audio file and returning the result.\n\n Arguments:\n fp -- a file object containing audio data\n \"\"\"\n\n fp.seek(44)\n\n # FIXME: Can't use the Decoder.decode_raw() here, because\n # pocketsphinx segfaults with tempfile.SpooledTemporaryFile()\n data = fp.read()\n self._decoder.start_utt()\n self._decoder.process_raw(data, False, True)\n self._decoder.end_utt()\n\n result = self._decoder.get_hyp()\n with open(self._logfile, 'r+') as f:\n for line in f:\n self._logger.debug(line.strip())\n f.truncate()\n\n transcribed = [result[0]]\n self._logger.info('PocketSphinx 识别到了:%r', transcribed)\n return transcribed\n\n def transcribe_keyword(self, data):\n \"\"\"\n Performs STT, transcribing the keyword audio file\n and returning the result.\n\n Arguments:\n data -- a stream object containing audio data\n \"\"\"\n\n # FIXME: Can't use the Decoder.decode_raw() here, because\n # pocketsphinx segfaults with tempfile.SpooledTemporaryFile()\n self._decoder.start_utt()\n self._decoder.process_raw(data, False, True)\n self._decoder.end_utt()\n\n result = self._decoder.get_hyp()\n\n transcribed = [result[0]]\n self._logger.info('PocketSphinx 识别到了:%r', transcribed)\n return transcribed\n\n @classmethod\n def is_available(cls):\n return diagnose.check_python_import('pocketsphinx')\n\n\nclass BaiduSTT(AbstractSTTEngine):\n \"\"\"\n 百度的语音识别API.\n 要使用本模块, 首先到 yuyin.baidu.com 注册一个开发者账号,\n 之后创建一个新应用, 然后在应用管理的\"查看key\"中获得 API Key 和 Secret Key\n 填入 profile.xml 中.\n ...\n baidu_yuyin: 'AIzaSyDoHmTEToZUQrltmORWS4Ott0OHVA62tw8'\n api_key: 'LMFYhLdXSSthxCNLR7uxFszQ'\n secret_key: '14dbd10057xu7b256e537455698c0e4e'\n ...\n \"\"\"\n\n SLUG = \"baidu-stt\"\n\n def __init__(self, api_key, secret_key, **kwargs):\n self._logger = logging.getLogger(__name__)\n self.api_key = api_key\n self.secret_key = secret_key\n self.token = ''\n\n @classmethod\n def get_config(cls):\n # Try to get baidu_yuyin config from config\n return config.get('baidu_yuyin', {})\n\n def get_token(self):\n cache = open(os.path.join(dingdangpath.TEMP_PATH, 'baidustt.ini'),\n 'a+')\n try:\n pms = cache.readlines()\n if len(pms) > 0:\n time = pms[0]\n tk = pms[1]\n # 计算token是否过期 官方说明一个月,这里保守29天\n time = dparser.parse(time)\n endtime = datetime.datetime.now()\n if (endtime - time).days <= 29:\n return tk\n finally:\n cache.close()\n URL = 'http://openapi.baidu.com/oauth/2.0/token'\n params = urllib.urlencode({'grant_type': 'client_credentials',\n 'client_id': self.api_key,\n 'client_secret': self.secret_key})\n r = requests.get(URL, params=params)\n try:\n r.raise_for_status()\n token = r.json()['access_token']\n return token\n except requests.exceptions.HTTPError:\n self._logger.critical('Token request failed with response: %r',\n r.text,\n exc_info=True)\n return ''\n\n def transcribe(self, fp):\n try:\n wav_file = wave.open(fp, 'rb')\n except IOError:\n self._logger.critical('wav file not found: %s',\n fp,\n exc_info=True)\n return []\n n_frames = wav_file.getnframes()\n frame_rate = wav_file.getframerate()\n audio = wav_file.readframes(n_frames)\n base_data = base64.b64encode(audio)\n if self.token == '':\n self.token = self.get_token()\n data = {\"format\": \"wav\",\n \"token\": self.token,\n \"len\": len(audio),\n \"rate\": frame_rate,\n \"speech\": base_data,\n \"cuid\": str(get_mac())[:32],\n \"channel\": 1}\n data = json.dumps(data)\n r = requests.post('http://vop.baidu.com/server_api',\n data=data,\n headers={'content-type': 'application/json'})\n try:\n r.raise_for_status()\n text = ''\n if 'result' in r.json():\n text = r.json()['result'][0].encode('utf-8')\n except requests.exceptions.HTTPError:\n self._logger.critical('Request failed with response: %r',\n r.text,\n exc_info=True)\n return []\n except requests.exceptions.RequestException:\n self._logger.critical('Request failed.', exc_info=True)\n return []\n except ValueError as e:\n self._logger.critical('Cannot parse response: %s',\n e.args[0])\n return []\n except KeyError:\n self._logger.critical('Cannot parse response.',\n exc_info=True)\n return []\n else:\n transcribed = []\n if text:\n transcribed.append(text.upper())\n self._logger.info(u'百度语音识别到了: %s' % text)\n return transcribed\n\n @classmethod\n def is_available(cls):\n return diagnose.check_network_connection()\n\n\nclass IFlyTekSTT(AbstractSTTEngine):\n \"\"\"\n 科大讯飞的语音识别API.\n 要使用本模块, 首先到 http://aiui.xfyun.cn/default/index 注册一个开发者账号,\n 之后创建一个新应用, 然后在应用管理的那查看 API id 和 API Key\n 填入 profile.xml 中.\n \"\"\"\n\n SLUG = \"iflytek-stt\"\n\n def __init__(self, api_id, api_key, url, **kwargs):\n self._logger = logging.getLogger(__name__)\n self.api_id = api_id\n self.api_key = api_key\n self.url = url\n\n @classmethod\n def get_config(cls):\n # Try to get iflytek_yuyin config from config\n return config.get('iflytek_yuyin', {})\n\n def transcribe(self, fp):\n try:\n wav_file = wave.open(fp, 'rb')\n except IOError:\n self._logger.critical('wav file not found: %s',\n fp,\n exc_info=True)\n return []\n n_frames = wav_file.getnframes()\n frame_rate = wav_file.getframerate()\n Param = str({\n \"auf\": \"16k\",\n \"aue\": \"raw\",\n \"scene\": \"main\",\n \"sample_rate\": \"%s\" % str(frame_rate)\n })\n XParam = base64.b64encode(Param)\n audio = wav_file.readframes(n_frames)\n base_data = base64.b64encode(audio)\n data = {\n 'voice_data': base_data,\n 'api_id': self.api_id,\n 'api_key': self.api_key,\n 'sample_rate': frame_rate,\n 'XParam': XParam\n }\n r = requests.post(self.url, data=data)\n try:\n r.raise_for_status()\n text = ''\n if r.json()['code'] == '00000':\n text = r.json()['data']['result'].encode('utf-8')\n except requests.exceptions.HTTPError:\n self._logger.critical('Request failed with response: %r',\n r.text,\n exc_info=True)\n return []\n except requests.exceptions.RequestException:\n self._logger.critical('Request failed.', exc_info=True)\n return []\n except ValueError as e:\n self._logger.critical('Cannot parse response: %s',\n e.args[0])\n return []\n except KeyError:\n self._logger.critical('Cannot parse response.',\n exc_info=True)\n return []\n else:\n self._logger.warning('Cannot parse response.(code: %s)' %\n r.json()['code'])\n transcribed = []\n if text:\n transcribed.append(text.upper())\n self._logger.info(u'讯飞语音识别到了: %s' % text)\n return transcribed\n\n @classmethod\n def is_available(cls):\n return diagnose.check_network_connection()\n\n\nclass ALiBaBaSTT(AbstractSTTEngine):\n \"\"\"\n 阿里云的语音识别API.\n 要使用本模块, 首先到 https://data.aliyun.com/product/nls 注册一个开发者账号,\n 然后查看自己的AK信息,填入 profile.xml 中.\n \"\"\"\n\n SLUG = \"ali-stt\"\n\n def __init__(self, ak_id, ak_secret, **kwargs):\n self._logger = logging.getLogger(__name__)\n self.ak_id = ak_id\n self.ak_secret = ak_secret\n\n @classmethod\n def get_config(cls):\n # Try to get ali_yuyin config from config\n return config.get('ali_yuyin', {})\n\n def to_md5_base64(self, strBody):\n hash = hashlib.md5()\n hash.update(self.body)\n m = hash.digest().encode('base64').strip()\n hash = hashlib.md5()\n hash.update(m)\n return hash.digest().encode('base64').strip()\n\n def to_sha1_base64(self, stringToSign, secret):\n hmacsha1 = hmac.new(str(secret), str(stringToSign), hashlib.sha1)\n return base64.b64encode(hmacsha1.digest())\n\n def transcribe(self, fp):\n try:\n wav_file = wave.open(fp, 'rb')\n except IOError:\n self._logger.critical('wav file not found: %s',\n fp,\n exc_info=True)\n return []\n n_frames = wav_file.getnframes()\n frame_rate = wav_file.getframerate()\n audio = wav_file.readframes(n_frames)\n date = datetime.datetime.strftime(datetime.datetime.utcnow(),\n \"%a, %d %b %Y %H:%M:%S GMT\")\n options = {\n 'url': 'https://nlsapi.aliyun.com/recognize?model=chat',\n 'method': 'POST',\n 'body': audio,\n }\n headers = {\n 'authorization': '',\n 'content-type': 'audio/wav; samplerate=%s' % str(frame_rate),\n 'accept': 'application/json',\n 'date': date,\n 'Content-Length': str(len(audio))\n }\n\n self.body = ''\n if 'body' in options:\n self.body = options['body']\n\n bodymd5 = ''\n if not self.body == '':\n bodymd5 = self.to_md5_base64(self.body)\n\n stringToSign = options['method'] + '\\n' + \\\n headers['accept'] + '\\n' + bodymd5 + '\\n' + \\\n headers['content-type'] + '\\n' + headers['date']\n signature = self.to_sha1_base64(stringToSign, self.ak_secret)\n\n authHeader = 'Dataplus ' + self.ak_id + ':' + signature\n headers['authorization'] = authHeader\n url = options['url']\n r = requests.post(url, data=self.body, headers=headers, verify=False)\n try:\n text = ''\n if 'result' in r.json():\n text = r.json()['result'].encode('utf-8')\n except requests.exceptions.HTTPError:\n self._logger.critical('Request failed with response: %r',\n r.text,\n exc_info=True)\n return []\n except requests.exceptions.RequestException:\n self._logger.critical('Request failed.', exc_info=True)\n return []\n except ValueError as e:\n self._logger.critical('Cannot parse response: %s',\n e.args[0])\n return []\n except KeyError:\n self._logger.critical('Cannot parse response.',\n exc_info=True)\n return []\n else:\n transcribed = []\n if text:\n transcribed.append(text.upper())\n self._logger.info(u'阿里云语音识别到了: %s' % text)\n return transcribed\n\n @classmethod\n def is_available(cls):\n return diagnose.check_network_connection()\n\n\nclass SnowboySTT(AbstractSTTEngine):\n \"\"\"\n Snowboy STT 离线识别引擎(只适用于离线唤醒)\n ...\n snowboy:\n model: '/home/pi/.dingdang/snowboy/dingdangdingdang.pmdl' # 唤醒词模型\n sensitivity: \"0.5\" # 敏感度\n ...\n \"\"\"\n\n SLUG = \"snowboy-stt\"\n\n def __init__(self, sensitivity, model, hotword, **kwargs):\n self._logger = logging.getLogger(__name__)\n self.sensitivity = sensitivity\n self.hotword = hotword\n self.model = model\n self.resource_file = os.path.join(dingdangpath.LIB_PATH,\n 'snowboy/common.res')\n try:\n from .snowboy import snowboydetect\n except Exception as e:\n self._logger.critical(e)\n if 'libf77blas.so' in e.message:\n self._logger.critical(\"您可能需要安装一个so包加载库:sudo \"\n \"apt-get install libatlas-base-dev\")\n return\n self.detector = snowboydetect.SnowboyDetect(\n resource_filename=self.resource_file,\n model_str=self.model)\n self.detector.SetAudioGain(1)\n self.detector.SetSensitivity(self.sensitivity)\n\n @classmethod\n def get_config(cls):\n # Try to get snowboy config from config\n profile = config.get('snowboy', {})\n\n if 'model' not in profile:\n profile['model'] = os.path.join(\n dingdangpath.LIB_PATH, 'snowboy/dingdang.pmdl')\n if 'sensitivity' not in profile:\n profile['sensitivity'] = \"0.5\"\n profile['hotword'] = config.get('hotword', 'DINGDANG')\n\n return profile\n\n def transcribe(self, fp):\n fp.seek(44)\n data = fp.read()\n ans = self.detector.RunDetection(data)\n if ans > 0:\n self._logger.info('snowboy 识别到了: %r', self.hotword)\n return [self.hotword]\n else:\n return []\n\n def transcribe_keyword(self, data):\n \"\"\"\n Performs STT, transcribing the keyword audio file\n and returning the result.\n\n Arguments:\n data -- a stream object containing audio data\n \"\"\"\n\n ans = self.detector.RunDetection(data)\n if ans > 0:\n self._logger.info('snowboy 识别到了: %r', self.hotword)\n return [self.hotword]\n else:\n return []\n\n @classmethod\n def is_available(cls):\n return diagnose.check_python_import('snowboy.snowboydetect')\n\n\nclass GoogleSTT(AbstractSTTEngine):\n \"\"\"\n Speech-To-Text implementation which relies on the Google Speech API.\n\n This implementation requires a Google API key to be present in profile.yml\n\n To obtain an API key:\n 1. Join the Chromium Dev group:\n https://groups.google.com/a/chromium.org/forum/?fromgroups#!forum/chromium-dev\n 2. Create a project through the Google Developers console:\n https://console.developers.google.com/project\n 3. Select your project. In the sidebar, navigate to \"APIs & Auth.\" Activate\n the Speech API.\n 4. Under \"APIs & Auth,\" navigate to \"Credentials.\" Create a new key for\n public API access.\n\n Excerpt from sample profile.yml:\n\n ...\n stt_engine: google-stt\n ...\n google_yuyin:\n api_key: $YOUR_KEY_HERE\n\n \"\"\"\n\n SLUG = 'google-stt'\n\n def __init__(self, api_key=None, language='en-us', **kwargs):\n # FIXME: get init args from config\n \"\"\"\n Arguments:\n api_key - the public api key which allows access to Google APIs\n \"\"\"\n self._logger = logging.getLogger(__name__)\n self._request_url = None\n self._language = None\n self._api_key = None\n self._http = requests.Session()\n self.language = language\n self.api_key = api_key\n\n @property\n def request_url(self):\n return self._request_url\n\n @property\n def language(self):\n return self._language\n\n @language.setter\n def language(self, value):\n self._language = value\n self._regenerate_request_url()\n\n @property\n def api_key(self):\n return self._api_key\n\n @api_key.setter\n def api_key(self, value):\n self._api_key = value\n self._regenerate_request_url()\n\n def _regenerate_request_url(self):\n if self.api_key and self.language:\n query = urllib.urlencode({'output': 'json',\n 'client': 'chromium',\n 'key': self.api_key,\n 'lang': self.language,\n 'maxresults': 6,\n 'pfilter': 2})\n self._request_url = urlparse.urlunparse(\n ('https', 'www.google.com', '/speech-api/v2/recognize', '',\n query, ''))\n else:\n self._request_url = None\n\n @classmethod\n def get_config(cls):\n # Try to get hmm_dir from config\n return config.get('google_yuyin', {})\n\n def transcribe(self, fp):\n \"\"\"\n Performs STT via the Google Speech API, transcribing an audio file and\n returning an English string.\n\n Arguments:\n audio_file_path -- the path to the .wav file to be transcribed\n \"\"\"\n\n if not self.api_key:\n self._logger.critical('API key missing, transcription request ' +\n 'aborted.')\n return []\n elif not self.language:\n self._logger.critical('Language info missing, transcription ' +\n 'request aborted.')\n return []\n\n wav = wave.open(fp, 'rb')\n frame_rate = wav.getframerate()\n wav.close()\n data = fp.read()\n\n headers = {'content-type': 'audio/l16; rate=%s' % frame_rate}\n r = self._http.post(self.request_url, data=data, headers=headers)\n try:\n r.raise_for_status()\n except requests.exceptions.HTTPError:\n self._logger.critical('Request failed with http status %d',\n r.status_code)\n if r.status_code == requests.codes['forbidden']:\n self._logger.warning('Status 403 is probably caused by an ' +\n 'invalid Google API key.')\n return []\n r.encoding = 'utf-8'\n try:\n # We cannot simply use r.json() because Google sends invalid json\n # (i.e. multiple json objects, seperated by newlines. We only want\n # the last one).\n response = json.loads(list(r.text.strip().split('\\n', 1))[-1])\n if len(response['result']) == 0:\n # Response result is empty\n raise ValueError('Nothing has been transcribed.')\n results = [alt['transcript'] for alt\n in response['result'][0]['alternative']]\n except ValueError as e:\n self._logger.warning('Empty response: %s', e.args[0])\n results = []\n except (KeyError, IndexError):\n self._logger.warning('Cannot parse response.', exc_info=True)\n results = []\n else:\n # Convert all results to uppercase\n results = tuple(result.upper() for result in results)\n self._logger.info('Transcribed: %r', results)\n return results\n\n @classmethod\n def is_available(cls):\n return diagnose.check_network_connection()\n\n\ndef get_engine_by_slug(slug=None):\n \"\"\"\n Returns:\n An STT Engine implementation available on the current platform\n\n Raises:\n ValueError if no speaker implementation is supported on this platform\n \"\"\"\n\n if not slug or type(slug) is not str:\n raise TypeError(\"Invalid slug '%s'\", slug)\n\n selected_engines = filter(lambda engine: hasattr(engine, \"SLUG\") and\n engine.SLUG == slug, get_engines())\n if len(selected_engines) == 0:\n raise ValueError(\"No STT engine found for slug '%s'\" % slug)\n else:\n if len(selected_engines) > 1:\n print((\"WARNING: Multiple STT engines found for slug '%s'. \" +\n \"This is most certainly a bug.\") % slug)\n engine = selected_engines[0]\n if not engine.is_available():\n raise ValueError((\"STT engine '%s' is not available (due to \" +\n \"missing dependencies, missing \" +\n \"dependencies, etc.)\") % slug)\n return engine\n\n\ndef get_engines():\n def get_subclasses(cls):\n subclasses = set()\n for subclass in cls.__subclasses__():\n subclasses.add(subclass)\n subclasses.update(get_subclasses(subclass))\n return subclasses\n return [tts_engine for tts_engine in\n list(get_subclasses(AbstractSTTEngine))\n if hasattr(tts_engine, 'SLUG') and tts_engine.SLUG]\n"}}}}