We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Missing `noise_words.list` for de-de causes issues for fallback skills
The missing `noise_words.list` for de-de causes errors for fallback skills:

```
06:42:27.050 | ERROR    |   246 | mycroft.skills.skill_loader:_create_skill_instance:297 | Skill __init__ failed with TypeError('expected str, bytes or os.PathLike object, not NoneType')
Traceback (most recent call last):
  File "/opt/mycroft/mycroft/skills/skill_loader.py", line 294, in _create_skill_instance
    self.instance = skill_module.create_skill()
  File "/opt/mycroft/skills/fallback-wolfram-alpha.mycroftai/__init__.py", line 238, in create_skill
    return WolframAlphaSkill()
  File "/opt/mycroft/skills/fallback-wolfram-alpha.mycroftai/__init__.py", line 40, in __init__
    super().__init__()
  File "/opt/mycroft/mycroft/skills/common_query_skill.py", line 71, in __init__
    with open(noise_words_filename) as f:
TypeError: expected str, bytes or os.PathLike object, not NoneType
```

**To Reproduce**
Steps to reproduce the behavior:
1. Configure language to `de-de`
2. Restart Skills
3. See `mycroft-console-client`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mycroft/skills/common_query_skill.py`
Content:
```
1 # Copyright 2018 Mycroft AI Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 import time
15 
16 from enum import IntEnum
17 from abc import ABC, abstractmethod
18 from .mycroft_skill import MycroftSkill
19 
20 from mycroft.configuration import Configuration
21 from mycroft.util.file_utils import resolve_resource_file
22 
23 
24 class CQSMatchLevel(IntEnum):
25     EXACT = 1  # Skill could find a specific answer for the question
26     CATEGORY = 2  # Skill could find an answer from a category in the query
27     GENERAL = 3  # The query could be processed as a general quer
28 
29 
30 # Copy of CQSMatchLevel to use if the skill returns visual media
31 CQSVisualMatchLevel = IntEnum('CQSVisualMatchLevel',
32                               [e.name for e in CQSMatchLevel])
33 
34 
35 def is_CQSVisualMatchLevel(match_level):
36     return isinstance(match_level, type(CQSVisualMatchLevel.EXACT))
37 
38 
39 """these are for the confidence calculation"""
40 # how much each topic word is worth
41 # when found in the answer
42 TOPIC_MATCH_RELEVANCE = 5
43 
44 # elevate relevance above all else
45 RELEVANCE_MULTIPLIER = 2
46 
47 # we like longer articles but only so much
48 MAX_ANSWER_LEN_FOR_CONFIDENCE = 50
49 
50 # higher number - less bias for word length
51 WORD_COUNT_DIVISOR = 100
52 
53 
54 class CommonQuerySkill(MycroftSkill, ABC):
55     """Question answering skills should be based on this class.
56 
57     The skill author needs to implement `CQS_match_query_phrase` returning an
58     answer and can optionally implement `CQS_action` to perform additional
59     actions if the skill's answer is selected.
60 
61     This class works in conjunction with skill-query which collects
62     answers from several skills presenting the best one available.
63     """
64 
65     def __init__(self, name=None, bus=None):
66         super().__init__(name, bus)
67         noise_words_filepath = "text/%s/noise_words.list" % (self.lang,)
68         noise_words_filename = resolve_resource_file(noise_words_filepath)
69         self.translated_noise_words = []
70         try:
71             with open(noise_words_filename) as f:
72                 self.translated_noise_words = f.read().strip()
73             self.translated_noise_words = self.translated_noise_words.split()
74         except FileNotFoundError:
75             self.log.warning("Missing noise_words.list file in res/text/lang")
76 
77         # these should probably be configurable
78         self.level_confidence = {
79             CQSMatchLevel.EXACT: 0.9,
80             CQSMatchLevel.CATEGORY: 0.6,
81             CQSMatchLevel.GENERAL: 0.5
82         }
83 
84     def bind(self, bus):
85         """Overrides the default bind method of MycroftSkill.
86 
87         This registers messagebus handlers for the skill during startup
88         but is nothing the skill author needs to consider.
89         """
90         if bus:
91             super().bind(bus)
92             self.add_event('question:query', self.__handle_question_query)
93             self.add_event('question:action', self.__handle_query_action)
94 
95     def __handle_question_query(self, message):
96         search_phrase = message.data["phrase"]
97 
98         # First, notify the requestor that we are attempting to handle
99         # (this extends a timeout while this skill looks for a match)
100         self.bus.emit(message.response({"phrase": search_phrase,
101                                         "skill_id": self.skill_id,
102                                         "searching": True}))
103 
104         # Now invoke the CQS handler to let the skill perform its search
105         result = self.CQS_match_query_phrase(search_phrase)
106 
107         if result:
108             match = result[0]
109             level = result[1]
110             answer = result[2]
111             callback = result[3] if len(result) > 3 else None
112             confidence = self.__calc_confidence(
113                 match, search_phrase, level, answer)
114             self.bus.emit(message.response({"phrase": search_phrase,
115                                             "skill_id": self.skill_id,
116                                             "answer": answer,
117                                             "callback_data": callback,
118                                             "conf": confidence}))
119         else:
120             # Signal we are done (can't handle it)
121             self.bus.emit(message.response({"phrase": search_phrase,
122                                             "skill_id": self.skill_id,
123                                             "searching": False}))
124 
125     def remove_noise(self, phrase):
126         """remove noise to produce essence of question"""
127         phrase = ' ' + phrase + ' '
128         for word in self.translated_noise_words:
129             mtch = ' ' + word + ' '
130             if phrase.find(mtch) > -1:
131                 phrase = phrase.replace(mtch, " ")
132         phrase = ' '.join(phrase.split())
133         return phrase.strip()
134 
135     def __calc_confidence(self, match, phrase, level, answer):
136         # Assume the more of the words that get consumed, the better the match
137         consumed_pct = len(match.split()) / len(phrase.split())
138         if consumed_pct > 1.0:
139             consumed_pct = 1.0
140         consumed_pct /= 10
141 
142         # bonus for more sentences
143         num_sentences = float(float(len(answer.split("."))) / float(10))
144 
145         # Add bonus if match has visuals and the device supports them.
146         bonus = 0.0
147         if is_CQSVisualMatchLevel(level) and self.gui.connected:
148             bonus = 0.1
149 
150         # extract topic
151         topic = self.remove_noise(match)
152 
153         # calculate relevance
154         answer = answer.lower()
155         matches = 0
156         for word in topic.split(' '):
157             if answer.find(word) > -1:
158                 matches += TOPIC_MATCH_RELEVANCE
159 
160         answer_size = len(answer.split(" "))
161         answer_size = min(MAX_ANSWER_LEN_FOR_CONFIDENCE, answer_size)
162 
163         relevance = 0.0
164         if answer_size > 0:
165             relevance = float(float(matches) / float(answer_size))
166 
167         relevance = relevance * RELEVANCE_MULTIPLIER
168 
169         # extra credit for more words up to a point
170         wc_mod = float(float(answer_size) / float(WORD_COUNT_DIVISOR)) * 2
171 
172         confidence = self.level_confidence[level] + \
173             consumed_pct + bonus + num_sentences + relevance + wc_mod
174 
175         return confidence
176 
177     def __handle_query_action(self, message):
178         """Message handler for question:action.
179 
180         Extracts phrase and data from message forward this to the skills
181         CQS_action method.
182         """
183         if message.data["skill_id"] != self.skill_id:
184             # Not for this skill!
185             return
186         phrase = message.data["phrase"]
187         data = message.data.get("callback_data")
188         # Invoke derived class to provide playback data
189         self.CQS_action(phrase, data)
190 
191     @abstractmethod
192     def CQS_match_query_phrase(self, phrase):
193         """Analyze phrase to see if it is a play-able phrase with this skill.
194 
195         Needs to be implemented by the skill.
196 
197         Args:
198             phrase (str): User phrase, "What is an aardwark"
199 
200         Returns:
201             (match, CQSMatchLevel[, callback_data]) or None: Tuple containing
202                  a string with the appropriate matching phrase, the PlayMatch
203                  type, and optionally data to return in the callback if the
204                  match is selected.
205         """
206         # Derived classes must implement this, e.g.
207         return None
208 
209     def CQS_action(self, phrase, data):
210         """Take additional action IF the skill is selected.
211 
212         The speech is handled by the common query but if the chosen skill
213         wants to display media, set a context or prepare for sending
214         information info over e-mail this can be implemented here.
215 
216         Args:
217             phrase (str): User phrase uttered after "Play", e.g. "some music"
218             data (dict): Callback data specified in match_query_phrase()
219         """
220         # Derived classes may implement this if they use additional media
221         # or wish to set context after being called.
222         pass
223
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mycroft/skills/common_query_skill.py b/mycroft/skills/common_query_skill.py
--- a/mycroft/skills/common_query_skill.py
+++ b/mycroft/skills/common_query_skill.py
@@ -11,13 +11,10 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-import time
-
 from enum import IntEnum
 from abc import ABC, abstractmethod
 from .mycroft_skill import MycroftSkill
 
-from mycroft.configuration import Configuration
 from mycroft.util.file_utils import resolve_resource_file
 
 
@@ -68,11 +65,15 @@
         noise_words_filename = resolve_resource_file(noise_words_filepath)
         self.translated_noise_words = []
         try:
-            with open(noise_words_filename) as f:
-                self.translated_noise_words = f.read().strip()
-            self.translated_noise_words = self.translated_noise_words.split()
+            if noise_words_filename:
+                with open(noise_words_filename) as f:
+                    read_noise_words = f.read().strip()
+                self.translated_noise_words = read_noise_words.split()
+            else:
+                raise FileNotFoundError
         except FileNotFoundError:
-            self.log.warning("Missing noise_words.list file in res/text/lang")
+            self.log.warning("Missing noise_words.list file in "
+                             f"res/text/{self.lang}")
 
         # these should probably be configurable
         self.level_confidence = {

{"golden_diff": "diff --git a/mycroft/skills/common_query_skill.py b/mycroft/skills/common_query_skill.py\n--- a/mycroft/skills/common_query_skill.py\n+++ b/mycroft/skills/common_query_skill.py\n@@ -11,13 +11,10 @@\n # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n # See the License for the specific language governing permissions and\n # limitations under the License.\n-import time\n-\n from enum import IntEnum\n from abc import ABC, abstractmethod\n from .mycroft_skill import MycroftSkill\n \n-from mycroft.configuration import Configuration\n from mycroft.util.file_utils import resolve_resource_file\n \n \n@@ -68,11 +65,15 @@\n         noise_words_filename = resolve_resource_file(noise_words_filepath)\n         self.translated_noise_words = []\n         try:\n-            with open(noise_words_filename) as f:\n-                self.translated_noise_words = f.read().strip()\n-            self.translated_noise_words = self.translated_noise_words.split()\n+            if noise_words_filename:\n+                with open(noise_words_filename) as f:\n+                    read_noise_words = f.read().strip()\n+                self.translated_noise_words = read_noise_words.split()\n+            else:\n+                raise FileNotFoundError\n         except FileNotFoundError:\n-            self.log.warning(\"Missing noise_words.list file in res/text/lang\")\n+            self.log.warning(\"Missing noise_words.list file in \"\n+                             f\"res/text/{self.lang}\")\n \n         # these should probably be configurable\n         self.level_confidence = {\n", "issue": "Missing `noise_words.list` for de-de causes issues for fallback skills\nThe missing `noise_words.list` for de-de causes errors for fallback skills:\r\n\r\n```\r\n06:42:27.050 | ERROR    |   246 | mycroft.skills.skill_loader:_create_skill_instance:297 | Skill __init__ failed with TypeError('expected str, bytes or os.PathLike object, not NoneType')\r\nTraceback (most recent call last):\r\n  File \"/opt/mycroft/mycroft/skills/skill_loader.py\", line 294, in _create_skill_instance\r\n    self.instance = skill_module.create_skill()\r\n  File \"/opt/mycroft/skills/fallback-wolfram-alpha.mycroftai/__init__.py\", line 238, in create_skill\r\n    return WolframAlphaSkill()\r\n  File \"/opt/mycroft/skills/fallback-wolfram-alpha.mycroftai/__init__.py\", line 40, in __init__\r\n    super().__init__()\r\n  File \"/opt/mycroft/mycroft/skills/common_query_skill.py\", line 71, in __init__\r\n    with open(noise_words_filename) as f:\r\nTypeError: expected str, bytes or os.PathLike object, not NoneType\r\n```\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. Configure language to `de-de`\r\n2. Restart Skills\r\n3. See `mycroft-console-client`\n", "before_files": [{"content": "# Copyright 2018 Mycroft AI Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport time\n\nfrom enum import IntEnum\nfrom abc import ABC, abstractmethod\nfrom .mycroft_skill import MycroftSkill\n\nfrom mycroft.configuration import Configuration\nfrom mycroft.util.file_utils import resolve_resource_file\n\n\nclass CQSMatchLevel(IntEnum):\n    EXACT = 1  # Skill could find a specific answer for the question\n    CATEGORY = 2  # Skill could find an answer from a category in the query\n    GENERAL = 3  # The query could be processed as a general quer\n\n\n# Copy of CQSMatchLevel to use if the skill returns visual media\nCQSVisualMatchLevel = IntEnum('CQSVisualMatchLevel',\n                              [e.name for e in CQSMatchLevel])\n\n\ndef is_CQSVisualMatchLevel(match_level):\n    return isinstance(match_level, type(CQSVisualMatchLevel.EXACT))\n\n\n\"\"\"these are for the confidence calculation\"\"\"\n# how much each topic word is worth\n# when found in the answer\nTOPIC_MATCH_RELEVANCE = 5\n\n# elevate relevance above all else\nRELEVANCE_MULTIPLIER = 2\n\n# we like longer articles but only so much\nMAX_ANSWER_LEN_FOR_CONFIDENCE = 50\n\n# higher number - less bias for word length\nWORD_COUNT_DIVISOR = 100\n\n\nclass CommonQuerySkill(MycroftSkill, ABC):\n    \"\"\"Question answering skills should be based on this class.\n\n    The skill author needs to implement `CQS_match_query_phrase` returning an\n    answer and can optionally implement `CQS_action` to perform additional\n    actions if the skill's answer is selected.\n\n    This class works in conjunction with skill-query which collects\n    answers from several skills presenting the best one available.\n    \"\"\"\n\n    def __init__(self, name=None, bus=None):\n        super().__init__(name, bus)\n        noise_words_filepath = \"text/%s/noise_words.list\" % (self.lang,)\n        noise_words_filename = resolve_resource_file(noise_words_filepath)\n        self.translated_noise_words = []\n        try:\n            with open(noise_words_filename) as f:\n                self.translated_noise_words = f.read().strip()\n            self.translated_noise_words = self.translated_noise_words.split()\n        except FileNotFoundError:\n            self.log.warning(\"Missing noise_words.list file in res/text/lang\")\n\n        # these should probably be configurable\n        self.level_confidence = {\n            CQSMatchLevel.EXACT: 0.9,\n            CQSMatchLevel.CATEGORY: 0.6,\n            CQSMatchLevel.GENERAL: 0.5\n        }\n\n    def bind(self, bus):\n        \"\"\"Overrides the default bind method of MycroftSkill.\n\n        This registers messagebus handlers for the skill during startup\n        but is nothing the skill author needs to consider.\n        \"\"\"\n        if bus:\n            super().bind(bus)\n            self.add_event('question:query', self.__handle_question_query)\n            self.add_event('question:action', self.__handle_query_action)\n\n    def __handle_question_query(self, message):\n        search_phrase = message.data[\"phrase\"]\n\n        # First, notify the requestor that we are attempting to handle\n        # (this extends a timeout while this skill looks for a match)\n        self.bus.emit(message.response({\"phrase\": search_phrase,\n                                        \"skill_id\": self.skill_id,\n                                        \"searching\": True}))\n\n        # Now invoke the CQS handler to let the skill perform its search\n        result = self.CQS_match_query_phrase(search_phrase)\n\n        if result:\n            match = result[0]\n            level = result[1]\n            answer = result[2]\n            callback = result[3] if len(result) > 3 else None\n            confidence = self.__calc_confidence(\n                match, search_phrase, level, answer)\n            self.bus.emit(message.response({\"phrase\": search_phrase,\n                                            \"skill_id\": self.skill_id,\n                                            \"answer\": answer,\n                                            \"callback_data\": callback,\n                                            \"conf\": confidence}))\n        else:\n            # Signal we are done (can't handle it)\n            self.bus.emit(message.response({\"phrase\": search_phrase,\n                                            \"skill_id\": self.skill_id,\n                                            \"searching\": False}))\n\n    def remove_noise(self, phrase):\n        \"\"\"remove noise to produce essence of question\"\"\"\n        phrase = ' ' + phrase + ' '\n        for word in self.translated_noise_words:\n            mtch = ' ' + word + ' '\n            if phrase.find(mtch) > -1:\n                phrase = phrase.replace(mtch, \" \")\n        phrase = ' '.join(phrase.split())\n        return phrase.strip()\n\n    def __calc_confidence(self, match, phrase, level, answer):\n        # Assume the more of the words that get consumed, the better the match\n        consumed_pct = len(match.split()) / len(phrase.split())\n        if consumed_pct > 1.0:\n            consumed_pct = 1.0\n        consumed_pct /= 10\n\n        # bonus for more sentences\n        num_sentences = float(float(len(answer.split(\".\"))) / float(10))\n\n        # Add bonus if match has visuals and the device supports them.\n        bonus = 0.0\n        if is_CQSVisualMatchLevel(level) and self.gui.connected:\n            bonus = 0.1\n\n        # extract topic\n        topic = self.remove_noise(match)\n\n        # calculate relevance\n        answer = answer.lower()\n        matches = 0\n        for word in topic.split(' '):\n            if answer.find(word) > -1:\n                matches += TOPIC_MATCH_RELEVANCE\n\n        answer_size = len(answer.split(\" \"))\n        answer_size = min(MAX_ANSWER_LEN_FOR_CONFIDENCE, answer_size)\n\n        relevance = 0.0\n        if answer_size > 0:\n            relevance = float(float(matches) / float(answer_size))\n\n        relevance = relevance * RELEVANCE_MULTIPLIER\n\n        # extra credit for more words up to a point\n        wc_mod = float(float(answer_size) / float(WORD_COUNT_DIVISOR)) * 2\n\n        confidence = self.level_confidence[level] + \\\n            consumed_pct + bonus + num_sentences + relevance + wc_mod\n\n        return confidence\n\n    def __handle_query_action(self, message):\n        \"\"\"Message handler for question:action.\n\n        Extracts phrase and data from message forward this to the skills\n        CQS_action method.\n        \"\"\"\n        if message.data[\"skill_id\"] != self.skill_id:\n            # Not for this skill!\n            return\n        phrase = message.data[\"phrase\"]\n        data = message.data.get(\"callback_data\")\n        # Invoke derived class to provide playback data\n        self.CQS_action(phrase, data)\n\n    @abstractmethod\n    def CQS_match_query_phrase(self, phrase):\n        \"\"\"Analyze phrase to see if it is a play-able phrase with this skill.\n\n        Needs to be implemented by the skill.\n\n        Args:\n            phrase (str): User phrase, \"What is an aardwark\"\n\n        Returns:\n            (match, CQSMatchLevel[, callback_data]) or None: Tuple containing\n                 a string with the appropriate matching phrase, the PlayMatch\n                 type, and optionally data to return in the callback if the\n                 match is selected.\n        \"\"\"\n        # Derived classes must implement this, e.g.\n        return None\n\n    def CQS_action(self, phrase, data):\n        \"\"\"Take additional action IF the skill is selected.\n\n        The speech is handled by the common query but if the chosen skill\n        wants to display media, set a context or prepare for sending\n        information info over e-mail this can be implemented here.\n\n        Args:\n            phrase (str): User phrase uttered after \"Play\", e.g. \"some music\"\n            data (dict): Callback data specified in match_query_phrase()\n        \"\"\"\n        # Derived classes may implement this if they use additional media\n        # or wish to set context after being called.\n        pass\n", "path": "mycroft/skills/common_query_skill.py"}], "after_files": [{"content": "# Copyright 2018 Mycroft AI Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom enum import IntEnum\nfrom abc import ABC, abstractmethod\nfrom .mycroft_skill import MycroftSkill\n\nfrom mycroft.util.file_utils import resolve_resource_file\n\n\nclass CQSMatchLevel(IntEnum):\n    EXACT = 1  # Skill could find a specific answer for the question\n    CATEGORY = 2  # Skill could find an answer from a category in the query\n    GENERAL = 3  # The query could be processed as a general quer\n\n\n# Copy of CQSMatchLevel to use if the skill returns visual media\nCQSVisualMatchLevel = IntEnum('CQSVisualMatchLevel',\n                              [e.name for e in CQSMatchLevel])\n\n\ndef is_CQSVisualMatchLevel(match_level):\n    return isinstance(match_level, type(CQSVisualMatchLevel.EXACT))\n\n\n\"\"\"these are for the confidence calculation\"\"\"\n# how much each topic word is worth\n# when found in the answer\nTOPIC_MATCH_RELEVANCE = 5\n\n# elevate relevance above all else\nRELEVANCE_MULTIPLIER = 2\n\n# we like longer articles but only so much\nMAX_ANSWER_LEN_FOR_CONFIDENCE = 50\n\n# higher number - less bias for word length\nWORD_COUNT_DIVISOR = 100\n\n\nclass CommonQuerySkill(MycroftSkill, ABC):\n    \"\"\"Question answering skills should be based on this class.\n\n    The skill author needs to implement `CQS_match_query_phrase` returning an\n    answer and can optionally implement `CQS_action` to perform additional\n    actions if the skill's answer is selected.\n\n    This class works in conjunction with skill-query which collects\n    answers from several skills presenting the best one available.\n    \"\"\"\n\n    def __init__(self, name=None, bus=None):\n        super().__init__(name, bus)\n        noise_words_filepath = \"text/%s/noise_words.list\" % (self.lang,)\n        noise_words_filename = resolve_resource_file(noise_words_filepath)\n        self.translated_noise_words = []\n        try:\n            if noise_words_filename:\n                with open(noise_words_filename) as f:\n                    read_noise_words = f.read().strip()\n                self.translated_noise_words = read_noise_words.split()\n            else:\n                raise FileNotFoundError\n        except FileNotFoundError:\n            self.log.warning(\"Missing noise_words.list file in \"\n                             f\"res/text/{self.lang}\")\n\n        # these should probably be configurable\n        self.level_confidence = {\n            CQSMatchLevel.EXACT: 0.9,\n            CQSMatchLevel.CATEGORY: 0.6,\n            CQSMatchLevel.GENERAL: 0.5\n        }\n\n    def bind(self, bus):\n        \"\"\"Overrides the default bind method of MycroftSkill.\n\n        This registers messagebus handlers for the skill during startup\n        but is nothing the skill author needs to consider.\n        \"\"\"\n        if bus:\n            super().bind(bus)\n            self.add_event('question:query', self.__handle_question_query)\n            self.add_event('question:action', self.__handle_query_action)\n\n    def __handle_question_query(self, message):\n        search_phrase = message.data[\"phrase\"]\n\n        # First, notify the requestor that we are attempting to handle\n        # (this extends a timeout while this skill looks for a match)\n        self.bus.emit(message.response({\"phrase\": search_phrase,\n                                        \"skill_id\": self.skill_id,\n                                        \"searching\": True}))\n\n        # Now invoke the CQS handler to let the skill perform its search\n        result = self.CQS_match_query_phrase(search_phrase)\n\n        if result:\n            match = result[0]\n            level = result[1]\n            answer = result[2]\n            callback = result[3] if len(result) > 3 else None\n            confidence = self.__calc_confidence(\n                match, search_phrase, level, answer)\n            self.bus.emit(message.response({\"phrase\": search_phrase,\n                                            \"skill_id\": self.skill_id,\n                                            \"answer\": answer,\n                                            \"callback_data\": callback,\n                                            \"conf\": confidence}))\n        else:\n            # Signal we are done (can't handle it)\n            self.bus.emit(message.response({\"phrase\": search_phrase,\n                                            \"skill_id\": self.skill_id,\n                                            \"searching\": False}))\n\n    def remove_noise(self, phrase):\n        \"\"\"remove noise to produce essence of question\"\"\"\n        phrase = ' ' + phrase + ' '\n        for word in self.translated_noise_words:\n            mtch = ' ' + word + ' '\n            if phrase.find(mtch) > -1:\n                phrase = phrase.replace(mtch, \" \")\n        phrase = ' '.join(phrase.split())\n        return phrase.strip()\n\n    def __calc_confidence(self, match, phrase, level, answer):\n        # Assume the more of the words that get consumed, the better the match\n        consumed_pct = len(match.split()) / len(phrase.split())\n        if consumed_pct > 1.0:\n            consumed_pct = 1.0\n        consumed_pct /= 10\n\n        # bonus for more sentences\n        num_sentences = float(float(len(answer.split(\".\"))) / float(10))\n\n        # Add bonus if match has visuals and the device supports them.\n        bonus = 0.0\n        if is_CQSVisualMatchLevel(level) and self.gui.connected:\n            bonus = 0.1\n\n        # extract topic\n        topic = self.remove_noise(match)\n\n        # calculate relevance\n        answer = answer.lower()\n        matches = 0\n        for word in topic.split(' '):\n            if answer.find(word) > -1:\n                matches += TOPIC_MATCH_RELEVANCE\n\n        answer_size = len(answer.split(\" \"))\n        answer_size = min(MAX_ANSWER_LEN_FOR_CONFIDENCE, answer_size)\n\n        relevance = 0.0\n        if answer_size > 0:\n            relevance = float(float(matches) / float(answer_size))\n\n        relevance = relevance * RELEVANCE_MULTIPLIER\n\n        # extra credit for more words up to a point\n        wc_mod = float(float(answer_size) / float(WORD_COUNT_DIVISOR)) * 2\n\n        confidence = self.level_confidence[level] + \\\n            consumed_pct + bonus + num_sentences + relevance + wc_mod\n\n        return confidence\n\n    def __handle_query_action(self, message):\n        \"\"\"Message handler for question:action.\n\n        Extracts phrase and data from message forward this to the skills\n        CQS_action method.\n        \"\"\"\n        if message.data[\"skill_id\"] != self.skill_id:\n            # Not for this skill!\n            return\n        phrase = message.data[\"phrase\"]\n        data = message.data.get(\"callback_data\")\n        # Invoke derived class to provide playback data\n        self.CQS_action(phrase, data)\n\n    @abstractmethod\n    def CQS_match_query_phrase(self, phrase):\n        \"\"\"Analyze phrase to see if it is a play-able phrase with this skill.\n\n        Needs to be implemented by the skill.\n\n        Args:\n            phrase (str): User phrase, \"What is an aardwark\"\n\n        Returns:\n            (match, CQSMatchLevel[, callback_data]) or None: Tuple containing\n                 a string with the appropriate matching phrase, the PlayMatch\n                 type, and optionally data to return in the callback if the\n                 match is selected.\n        \"\"\"\n        # Derived classes must implement this, e.g.\n        return None\n\n    def CQS_action(self, phrase, data):\n        \"\"\"Take additional action IF the skill is selected.\n\n        The speech is handled by the common query but if the chosen skill\n        wants to display media, set a context or prepare for sending\n        information info over e-mail this can be implemented here.\n\n        Args:\n            phrase (str): User phrase uttered after \"Play\", e.g. \"some music\"\n            data (dict): Callback data specified in match_query_phrase()\n        \"\"\"\n        # Derived classes may implement this if they use additional media\n        # or wish to set context after being called.\n        pass\n", "path": "mycroft/skills/common_query_skill.py"}]}