feat: Add Be and tbd skill, also added Roadmap file

This commit is contained in:
2026-05-10 16:32:12 -04:00
parent 3500ade13f
commit 0bb8885802
29587 changed files with 10611695 additions and 0 deletions

47
Skills/@be/README.md Normal file
View File

@@ -0,0 +1,47 @@
# Be
[![Build Status](https://jenkins2.jibo.com/buildStatus/icon?job=be/be/master)](https://jenkins2.jibo.com/job/be/job/be/job/master/)
Be the Super Skill
## Documentation
[INTERNAL/LATEST Jibo SDK & API Documentation](https://developers.stage.jibo.com/sdk/docs/)
Please also read the [Be Documentation](https://confluence.jibo.com/display/SKIL/Be+Documentation) for information on creating a skill in this organization and adding your skill to this repo as a dependency.
## Building
Build process is implemented using NPM scripts installed in the **package.json** file.
| Command | Description
|---|---|
|`yarn build` | Build the project in debug mode. |
|`yarn release` | Build the project in release mode. |
|`yarn watch` | Watch the source and behaviors, auto-rebuilds. |
|`yarn clean` | Deletes built JavaScript files. |
|`yarn start` | Alias for `yarn watch`. |
|`yarn sync` | Sync file to the robot</br> **Tip**: Use `jibo run` after to launch skills |
|`yarn addsshkey` | Install your public SSH key on the robot (OS X Only). |
|`yarn test` | Run unit tests on the command line. |
|`yarn testdebug` | Run unit tests via the electron window with the developer console. |
|`yarn first:robot:enable` | Reset the flag to run through the First Contact / OOBE flow again ON THE ROBOT. |
|`yarn first:robot:disable` | Disable First Contact / OOBE flow so Be automatically launches into the default skill (Idle) ON THE ROBOT. |
|`yarn first:local:enable` | Reset the flag to run through the First Contact / OOBE flow again LOCALLY. |
|`yarn first:local:disable` | Disable First Contact / OOBE flow so Be automatically launches into the default skill (Idle) LOCALLY. |
## Publishing
To publish, bump the version number (optional) and either publish in debug or release mode. This will generate a release/debug build before publishing and will Git push the version bump and the tag. For example:
```bash
# Bump the version, see NPM docs for more info
npm version patch|minor|major (depending on change)
# Publish in debug mode with source maps and shrinkwrapped
yarn publish:debug:shrinkwrap
# Publish in release mode, minified and shrinkwrapped
yarn publish:release:shrinkwrap
```

View File

@@ -0,0 +1 @@
[]

View File

47
Skills/@be/be/README.md Normal file
View File

@@ -0,0 +1,47 @@
# Be
[![Build Status](https://jenkins2.jibo.com/buildStatus/icon?job=be/be/master)](https://jenkins2.jibo.com/job/be/job/be/job/master/)
Be the Super Skill
## Documentation
[INTERNAL/LATEST Jibo SDK & API Documentation](https://developers.stage.jibo.com/sdk/docs/)
Please also read the [Be Documentation](https://confluence.jibo.com/display/SKIL/Be+Documentation) for information on creating a skill in this organization and adding your skill to this repo as a dependency.
## Building
Build process is implemented using NPM scripts installed in the **package.json** file.
| Command | Description
|---|---|
|`yarn build` | Build the project in debug mode. |
|`yarn release` | Build the project in release mode. |
|`yarn watch` | Watch the source and behaviors, auto-rebuilds. |
|`yarn clean` | Deletes built JavaScript files. |
|`yarn start` | Alias for `yarn watch`. |
|`yarn sync` | Sync file to the robot</br> **Tip**: Use `jibo run` after to launch skills |
|`yarn addsshkey` | Install your public SSH key on the robot (OS X Only). |
|`yarn test` | Run unit tests on the command line. |
|`yarn testdebug` | Run unit tests via the electron window with the developer console. |
|`yarn first:robot:enable` | Reset the flag to run through the First Contact / OOBE flow again ON THE ROBOT. |
|`yarn first:robot:disable` | Disable First Contact / OOBE flow so Be automatically launches into the default skill (Idle) ON THE ROBOT. |
|`yarn first:local:enable` | Reset the flag to run through the First Contact / OOBE flow again LOCALLY. |
|`yarn first:local:disable` | Disable First Contact / OOBE flow so Be automatically launches into the default skill (Idle) LOCALLY. |
## Publishing
To publish, bump the version number (optional) and either publish in debug or release mode. This will generate a release/debug build before publishing and will Git push the version bump and the tag. For example:
```bash
# Bump the version, see NPM docs for more info
npm version patch|minor|major (depending on change)
# Publish in debug mode with source maps and shrinkwrapped
yarn publish:debug:shrinkwrap
# Publish in release mode, minified and shrinkwrapped
yarn publish:release:shrinkwrap
```

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,42 @@
Be module split
===============
What I added
-----------
- `be/core.js` — a tiny wrapper that re-exports the current bundled `Be` from `index.js`.
- `be/api.js` — convenience helpers that attach `showMenu` to a `Be` instance and `jibo`.
- `be/skill-loader.js` — a small helper scaffold for the skill discovery logic (extract from constructor later).
- `be/init.js` — helper to attach `hex-hitarea` and `menuManager` and API after initialization.
Why this approach
------------------
The project currently ships a single large UMD bundle at `index.js`. Rewriting the bundle in-place is risky
and unnecessary to get incremental improvements. The wrapper modules provide:
- a stable, easy-to-edit place to move code into as you split responsibilities;
- small, testable files to gradually extract logic from the big bundle;
- backward compatibility: the bundle remains the authoritative runtime implementation.
How to continue
----------------
1. Move pieces of logic out of `index.js` into `be/` files one-by-one.
2. Replace the corresponding logic in the bundle with small calls into the new modules.
3. When the split is complete you can replace the bundle with the new source and remove the generated file.
Example usage
-------------
After `jibo.init()` completes, call:
```javascript
// from a skill's code (after Be is initted)
be.menuManager.showMenu('menu/menus/example-menu.json');
// or
be.showMenu('menu/menus/example-menu.json');
```
Notes
-----
- I kept changes minimal so runtime behavior is unchanged. If you want, I can now start moving
specific functions (analytics init, skill loading, lifecycle methods) into these files and patch the bundle
to call them. Tell me which section to extract first and I'll implement the extraction and tests.

View File

@@ -0,0 +1,31 @@
{
"enabled": true,
"mode": "TEXT",
"serverBaseUrl": "http://192.168.1.28:8020",
"recordSeconds": 5,
"useDumpStateAudio": true,
"useAsrServiceStt": true,
"asrServiceHost": "127.0.0.1",
"asrServicePort": 8088,
"asrAudioSourceId": "alsa1",
"asrTimeoutMs": 15000,
"asrServiceDebugWs": false,
"asrAutoStart": true,
"wakeupChitchatPhrases": [
"hello",
"howdy",
"hi",
"hey",
"look what i found",
"nice to see you",
"good morning",
"good afternoon",
"good evening"
],
"followupEnabled": true,
"followupDelayMs": 250
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
"use strict";
const jibo = require('jibo');
const path = require('path');
const BeSkill = require('@be/be-framework');
exports.initAnalyticsContext = function () {
let context = {
ssm_version: "<not set>",
be_version: "<not set>",
platform_version: "<not set>",
release_version: "<not set>"
};
this.log.debug('context', JSON.stringify(context));
this.log.debug('calling jibo.versions');
const versions = jibo.versions;
this.log.debug('got jibo.versions', JSON.stringify(versions));
if (versions) {
context.platform_version = versions.platform;
context.ssm_version = versions.ssm;
context.release_version = versions.release;
}
this.log.debug('getting Be version');
const dir = jibo.utils.PathUtils.findRoot(__dirname);
const beVersion = require(path.resolve(dir, 'package.json')).version;
this.log.debug('got version:', beVersion);
context.be_version = beVersion;
this.log.debug('version set on context');
this.log.debug('setting context on BeSkill');
this.log.debug(!!BeSkill.BeSkill);
this.log.debug(!!BeSkill.BeSkill.plugins);
this.log.debug(!!BeSkill.BeSkill.plugins && !!BeSkill.BeSkill.plugins.analytics);
if (BeSkill.BeSkill && BeSkill.BeSkill.plugins && BeSkill.BeSkill.plugins.analytics) {
BeSkill.BeSkill.plugins.analytics.context = context;
}
this.log.debug('context set on BeSkill analytics plugin');
};

31
Skills/@be/be/be/api.js Normal file
View File

@@ -0,0 +1,31 @@
"use strict";
// Small API helpers to attach convenience methods to a Be instance.
exports.attach = function (beInstance) {
if (!beInstance) return;
// preserve existing showMenu if present
if (!beInstance.showMenu && beInstance.menuManager && typeof beInstance.menuManager.showMenu === 'function') {
beInstance.showMenu = function (menuPath, cb) {
return beInstance.menuManager.showMenu(menuPath, cb);
};
}
// expose showMenuFromSkills if available
if (!beInstance.showMenuFromSkills && beInstance.menuManager && typeof beInstance.menuManager.showMenuFromSkills === 'function') {
beInstance.showMenuFromSkills = function (rootPath, cb) {
return beInstance.menuManager.showMenuFromSkills(rootPath, cb);
};
}
// expose on global jibo if available
try {
if (typeof jibo !== 'undefined' && !jibo.showMenu && beInstance.showMenu) {
jibo.showMenu = beInstance.showMenu.bind(beInstance);
}
if (typeof jibo !== 'undefined' && !jibo.showMenuFromSkills && beInstance.showMenuFromSkills) {
jibo.showMenuFromSkills = beInstance.showMenuFromSkills.bind(beInstance);
}
}
catch (e) { /* ignore */ }
};

9
Skills/@be/be/be/core.js Normal file
View File

@@ -0,0 +1,9 @@
"use strict";
// Core wrapper: expose the existing bundled Be export as a module
// This file intentionally keeps behavior identical to the bundle while
// providing a stable module boundary for further refactors.
const BeBundle = require('../index.js');
// The bundle exports the Be constructor as default or module.exports
module.exports = BeBundle;

View File

@@ -0,0 +1,157 @@
"use strict";
// Dynamically load skills after BE startup.
// This avoids needing to rebuild the big BE bundle when adding new skills on disk.
const path = require("path");
const jibo = require("jibo");
let rlog = null;
try {
rlog = require("./robot-logger");
} catch (e) {
rlog = null;
}
function log(msg, data) {
const line = "[DYN-SKILLS] " + msg;
try {
console.log(line, data || "");
} catch (e) {}
try {
if (rlog && typeof rlog.raw === "function") rlog.raw(line + (data ? " " + JSON.stringify(data) : ""));
} catch (e) {}
}
function normalizeSkillExport(SkillExport, id) {
if (typeof SkillExport === "function") return SkillExport;
if (SkillExport && typeof SkillExport.Skill === "function") return SkillExport.Skill;
throw new Error("Error loading skill: " + id + ". Incorrect exports");
}
function attachLifecycleHooks(be, id, skill) {
// Mirror the hooks the Be constructor normally installs.
try {
skill.on("exit", function () {
be.exit.call(be, skill, ...arguments);
});
skill.on("redirect", function () {
be.skillRedirect.call(be, skill, ...arguments);
});
skill.on("refresh", function () {
be.skillRedirect.call(be, skill, skill.assetPack, ...arguments);
});
} catch (e) {
log("failed to attach lifecycle hooks", { id: id, err: String(e && (e.stack || e.message || e)) });
}
const empty = (done) => {
try {
done();
} catch (e) {}
};
try {
if (!skill.postInit) skill.postInit = empty;
if (!skill.preload) skill.preload = empty;
} catch (e) {}
}
function tryLoadSkill(be, id) {
if (!be || !be.skills) throw new Error("be.skills missing");
if (!id || typeof id !== "string") return false;
if (be.skills[id]) return true;
// eslint-disable-next-line global-require, import/no-dynamic-require
const SkillExport = require(id);
const Skill = normalizeSkillExport(SkillExport, id);
const rootPath = path.dirname(jibo.utils.PathUtils.resolve(id));
const skill = new Skill({ assetPack: id, rootPath: rootPath });
if (typeof be._validateSkill === "function" && !be._validateSkill(skill)) {
throw new Error("not a valid BeSkill");
}
be.skills[id] = skill;
attachLifecycleHooks(be, id, skill);
log("loaded", { id: id });
return true;
}
function collectSkillIdsFromMenuEntries(entries) {
const ids = new Set();
(entries || []).forEach(function (e) {
if (!e) return;
if (e.type === "skill" && e.skillId) ids.add(e.skillId);
if (e.type === "submenu" && Array.isArray(e.children)) {
e.children.forEach(function (c) {
if (c && c.type === "skill" && c.skillId) ids.add(c.skillId);
});
}
});
return Array.from(ids);
}
function loadMissingSkillsFromMenuEntries(be, opts) {
opts = opts || {};
let menuEntries = null;
try {
// Note: this is the modular path (includes provider entries).
menuEntries = require("../menu/menu-entries");
} catch (e) {
log("menu-entries module missing", { err: String(e && (e.stack || e.message || e)) });
return { loaded: [], failed: [] };
}
let res;
try {
res = menuEntries.getMenuEntries({
skillsRoot: opts.skillsRoot,
providersDir: opts.providersDir,
log: function () {
try {
log(Array.prototype.join.call(arguments, " "));
} catch (e) {}
}
});
} catch (e) {
log("getMenuEntries failed", { err: String(e && (e.stack || e.message || e)) });
return { loaded: [], failed: [] };
}
const ids = collectSkillIdsFromMenuEntries(res && res.entries);
log("menu referenced skillIds", { count: ids.length });
const loaded = [];
const failed = [];
ids.forEach(function (id) {
try {
// Only attempt ids that look like NPM packages (reduces noise).
if (typeof id !== "string") return;
if (id.indexOf("/") === -1) return;
if (!be.skills[id]) {
tryLoadSkill(be, id);
loaded.push(id);
}
} catch (e) {
failed.push({ id: id, err: String(e && (e.stack || e.message || e)) });
log("load failed", { id: id, err: String(e && (e.message || e)) });
}
});
return { loaded: loaded, failed: failed, providersDir: res && res.providersDir, skillsRoot: res && res.skillsRoot };
}
module.exports = {
loadMissingSkillsFromMenuEntries: loadMissingSkillsFromMenuEntries
};

View File

@@ -0,0 +1,28 @@
"use strict";
const jibo = require('jibo');
exports.initPlugins = function (err) {
if (err) {
this.log.error('Error BeSkill plugins: ', err);
this.initDoneCallback(err);
return;
}
const tasks = [];
for (let id in this.skills) {
const skill = this.skills[id];
this.log.debug(`About to push task for skill ${id}`);
tasks.push((done) => {
const startTime = Date.now();
this.log.debug(`Calling postInit for skill ${id}`);
skill.postInit.bind(skill)((err) => {
if (err) {
this.log.error(`error during skill ${skill.assetPack} postinit call:`, err);
}
this.log.info(`loading - skill ${skill.assetPack} postinit call - ${Date.now() - startTime} MS`);
done();
});
});
}
this.log.debug('calling jibo loader to load the skills');
jibo.loader.load(tasks, this.postInit.bind(this));
};

18
Skills/@be/be/be/init.js Normal file
View File

@@ -0,0 +1,18 @@
"use strict";
// Small init helpers extracted from index.js's lifecycle for organization.
// These helpers are lightweight and call into the existing bundle behavior.
exports.patchAfterInit = function (beInstance) {
if (!beInstance) return;
try {
require('../hex-hitarea').patch(beInstance.jibo || (typeof jibo !== 'undefined' && jibo));
}
catch (e) { /* ignore */ }
// attach API helpers
try {
require('./api').attach(beInstance);
}
catch (e) { /* ignore */ }
};

View File

@@ -0,0 +1,86 @@
{
"webCore" : {
"serverPort": 8088,
"fileRoot": "/usr/local/var/www/asrservice",
"requestLogging": false
},
"AsrService" : {
"cloud_establish_http_timeout": 5000,
"language": "en-US",
"post_to_performance_service": true,
"log_audio": true,
"log_text" : true,
"log_path" : "/var/log/asr",
"log_level" : "INFO",
"log_server_url" : "https://speech-logging.jibo.com/logdrop/logdrop.py",
"speaker_id_resource_path" : "/var/jibo/asr/",
"name_learning_resource_path" : "/usr/local/share/asr/namelearning",
"name_learning_temp_path" : "/var/jibo/asr/namelearning_temp/",
"name_learning_nbest" : 70,
"active_sleep_duration" : 5000,
"idle_sleep_duration" : 50000,
"block_duration" : 50,
"audio_loop_sleep_us": 10000,
"use_nuance_upload_voc": false,
"dictation_type" : "dictation",
"nuance_uId" : "b8fb02f2c5794963aaafb8c716ef384c",
"contacts_checksum": "",
"loop_checksum": "1",
"customs_checksum": "",
"upload_voc_url": "ws.nuancemobility.net",
"cloud_url": "https://jibo-ncs-engusa-http.nuancemobility.net/NmspServlet/",
"cloud_appid": "HTTP_NMDPPRODUCTION_Jibo_Jibo_Robot_20151231124503",
"cloud_appkey": "a8c18159a8e3ca49471c56d867552bc77693ccdcc041375ee97b7c867160ae1a212f73c9123d1359596931c0be5c8734ef5310af95470d7ec3890434e9b24e0b",
"upload_voc_rootcert": "",
"google_credential": "/usr/local/share/asr/google_asr/credentials-key.json",
"fadeout_duration": 5000000,
"max_logfile_size": 1000,
"log_upload_time_interval": 60000,
"min_available_log_partition_space": 5000,
"max_asr_log_dir_size_before_upload_trigger": 10000,
"max_asr_log_dir_size": 12000,
"size_to_free_up_when_dir_overflowing": 1000,
"asr_resource_path" : "/usr/local/share/asr/",
"max_memory": 150000,
"wipable_files": [
"/var/log/asr/*.pcm",
"/var/log/asr/*.wav",
"/var/log/asr/*.log",
"/var/jibo/asr/sensory_data_td/client_model.bin",
"/var/jibo/asr/sensory_data_td/audio/*",
"/var/jibo/asr/namelearning_temp/*"
],
"resident_task" : "{\"command\":\"start\",\"task_id\":\"task0\",\"audio_source_id\":\"alsa1\",\"hotphrase\":\"hey_jibo\",\"request_id\":\"resident_hey_jibo_self_start\",\"residency\":true}",
"resident_audio_channel" : "{\"action\":\"start\", \"audio_source_id\":\"alsa1\", \"wav_files\":[], \"audio_source\":\"alsa\", \"request_id\":\"self_start_audio_source_request_id\"}",
"task_templates" : {
"hey_jibo_resident": {
"input_template": "{\"name\":\"hey jibo\",\"path\":\"/usr/local/share/asr/hey_jibo\",\"timeout\":0} * {\"name\":\"Speaker ID TD\",\"path\":\"/usr/local/share/asr/sensory_spkr_id_td\",\"audio_tail_length\":1}",
"emitting_recogs": ["hey jibo", "Speaker ID TD"]
},
"hey_jibo": {
"input_template": "{\"name\":\"hey jibo\",\"path\":\"/usr/local/share/asr/hey_jibo\",\"timeout\":0} * ({\"name\":\"pcmwriter\",\"path\":\"/usr/local/share/asr/pcm_writer\",\"timeout\":0,\"audio_tail_length\":0, \"audio_overshoot_duration\":0} | {\"name\":\"Speaker ID TD\",\"path\":\"/usr/local/share/asr/sensory_spkr_id_td\",\"audio_tail_length\":1})",
"emitting_recogs": ["hey jibo", "Speaker ID TD"]
},
"cloud": {
"input_template":"({\"name\":\"google_asr\",\"path\":\"/usr/local/share/asr/google_asr\",\"timeout\":14000,\"bargein\":false,\"nbest\":1,\"speaker_name\":\"\",\"incremental\":false,\"audio_tail_length\":300}| {\"name\":\"sensory_sdet\",\"path\":\"/usr/local/share/asr/jibo_energy_fake_eos\",\"timeout\":50000,\"bargein\":false,\"nbest\":1,\"speaker_name\":\"\",\"incremental\":false})",
"emitting_recogs": ["google_asr","sensory_sdet"]
},
"hey_jibo_cloud": {
"input_template":"{\"name\":\"hey jibo\",\"path\":\"/usr/local/share/asr/hey_jibo\",\"timeout\":0,\"bargein\":true,\"nbest\":1,\"speaker_name\":\"\",\"incremental\":false,\"speaker_id\":true} * ({\"name\":\"Speaker ID TD\",\"path\":\"/usr/local/share/asr/sensory_spkr_id_td\",\"audio_tail_length\":1} & ({\"name\":\"pcmwriter\",\"path\":\"/usr/local/share/asr/pcm_writer\",\"timeout\":0,\"audio_tail_length\":400, \"audio_overshoot_duration\":0, \"prebuffer\":true} | {\"name\":\"google_asr\",\"path\":\"/usr/local/share/asr/google_asr\",\"timeout\":14000,\"bargein\":false,\"nbest\":1,\"speaker_name\":\"\",\"incremental\":false,\"trim_audio_tail\":true,\"wakeup_phrase_detection\":false,\"audio_tail_length\":0}|{\"name\":\"sensory_sdet\",\"path\":\"/usr/local/share/asr/jibo_energy_fake_eos\",\"timeout\":50000,\"bargein\":false,\"nbest\":1,\"speaker_name\":\"\",\"incremental\":false}))",
"emitting_recogs": ["hey jibo", "Speaker ID TD", "google_asr","sensory_sdet"]
}
},
"rewrite_rules" : {
"log_audio_no_trigger" : "^(?!.*?hey_jibo)(.*)->{\"name\":\"pcmwriter\",\"path\":\"/usr/local/share/asr/pcm_writer\",\"timeout\":0,\"audio_tail_length\":300, \"audio_overshoot_duration\":0,\"prebuffer\":false} | ($1)",
"namelearning_EOS" : "^(.*?\"name\":\\s*name_learning.*)->{\"name\":\"jibo_energy_eos\",\"path\":\"/usr/local/share/asr/jibo_energy_eos\",\"timeout\":10000,\"bargein\":false,\"nbest\":1,\"speaker_name\":\"\",\"incremental\":false} | ($1)"
}
},
"logging" : {
"jibo_message_prefix": "C",
"loggers" : {
"root": {"level": "information"},
"l1" : {"name" : "ASRService", "level" : "information"},
"l2" : {"name" : "Application", "level" : "information"}
}
}
}

View File

@@ -0,0 +1,236 @@
"use strict";
const jibo = require('jibo');
const path = require('path');
let SkillSwitchData;
let SkillLifecycleState;
if (typeof global !== 'undefined' && global && global.be && global.be.constructor) {
SkillSwitchData = global.be.constructor.SkillSwitchData;
SkillLifecycleState = global.be.constructor.SkillLifecycleState;
}
function _interop(m) {
return (m && (m.__esModule || m.default)) ? (m.default || m) : m;
}
// normalize to usable variables
let SkillSwitchDataCtor;
let SkillLifecycleStateObj;
if (SkillSwitchData) {
SkillSwitchDataCtor = _interop(SkillSwitchData);
}
if (SkillLifecycleState) {
SkillLifecycleStateObj = _interop(SkillLifecycleState);
}
if (!SkillSwitchData) {
try {
SkillSwitchData = require(path.join(jibo.utils.PathUtils.findRoot(), 'SkillSwitchData'));
}
catch (e) {
try {
const Root = require(path.join(jibo.utils.PathUtils.findRoot(), 'index.js'));
SkillSwitchData = (Root && (Root.SkillSwitchData || (Root.default && Root.default.SkillSwitchData))) || undefined;
}
catch (e2) { }
}
}
if (!SkillLifecycleState) {
try {
SkillLifecycleState = require(path.join(jibo.utils.PathUtils.findRoot(), 'SkillLifecycleState'));
}
catch (e) {
try {
const Root = require(path.join(jibo.utils.PathUtils.findRoot(), 'index.js'));
SkillLifecycleState = (Root && (Root.SkillLifecycleState || (Root.default && Root.default.SkillLifecycleState))) || undefined;
}
catch (e2) { }
}
}
exports.selectFirstSkill = function (callback) {
const kbm = jibo.kb.createModel('/skills-config');
kbm.loadRoot((loadRootErr, rootNode) => {
if (loadRootErr) {
this.log.warn("error loading /skills-config root", loadRootErr);
}
jibo.secureTransferService.hasBackupData((backupErr, hasBackupData) => {
if (backupErr) {
this.log.warn("error when checking if backup data exists", backupErr);
}
jibo.errors.getCurrentErrorId((err, currentErrorId) => {
if (err) {
this.log.warn("error when checking for current error id", err);
}
let nextSkill = this.idle;
let nextSkillLaunchOptions = {};
let firstTime = false;
if (!loadRootErr) {
firstTime = !rootNode.data.hasAlreadyLaunchedFirstContact;
}
else {
this.log.info(`error reading the hasAlreadyLaunchedFirstContact property from the KB. assuming first time is false`);
}
this.log.info(`selectFirstSkill parameter readout: Skills config load error: ${loadRootErr}, first time: ${firstTime}, has backup data: ${hasBackupData}, skip restore: ${this.packageInfo.jibo.debug.skipRestore}, current error id: ${currentErrorId}`);
if (currentErrorId) {
nextSkill = this.skills['@be/settings'];
nextSkillLaunchOptions = { nlu: { entities: { errorId: currentErrorId } } };
}
else if (firstTime) {
if (backupErr && !this.packageInfo.jibo.debug.skipRestore) {
setTimeout(this.selectFirstSkill.bind(this, callback), 2000);
return;
}
else if (hasBackupData && !this.packageInfo.jibo.debug.skipRestore) {
nextSkill = this.restoreSkill;
}
else {
nextSkill = this.firstSkill;
}
}
callback(nextSkill, nextSkillLaunchOptions, currentErrorId, firstTime);
});
});
});
};
exports.launchFirstSkill = function (firstSkill, firstSkillLaunchOptions, firstErrorId, firstTime) {
this.log.debug('launching first skill');
const firstSkillHasOpened = () => {
if (firstErrorId) {
document.getElementById('splash').style.display = 'none';
}
else {
document.getElementById('splash').remove();
this.enableSkillSwitching();
}
this.initDoneCallback();
};
let firstSkillRedirectToken = this.redirect(new (SkillSwitchDataCtor || SkillSwitchData)(firstSkill, firstSkillLaunchOptions));
firstSkillRedirectToken.onState((SkillLifecycleStateObj || SkillLifecycleState).SKILL_OPENED, firstSkillHasOpened);
if (firstErrorId) {
const onErrorResolved = () => {
if (firstTime) {
document.getElementById('splash').style.display = 'block';
}
this.selectFirstSkill((nextSkill, nextSkillLaunchOptions, currentErrorId) => {
let nextSkillRedirectToken = this.redirect(new (SkillSwitchDataCtor || SkillSwitchData)(nextSkill, nextSkillLaunchOptions));
if (currentErrorId) {
nextSkillRedirectToken.onState((SkillLifecycleStateObj || SkillLifecycleState).LIFECYCLE_ENDED, onErrorResolved);
nextSkillRedirectToken.onState((SkillLifecycleStateObj || SkillLifecycleState).SKILL_OPENED, () => {
document.getElementById('splash').style.display = 'none';
});
}
else {
nextSkillRedirectToken.onState((SkillLifecycleStateObj || SkillLifecycleState).SKILL_OPENED, () => {
document.getElementById('splash').remove();
this.enableSkillSwitching();
});
}
});
};
firstSkillRedirectToken.onState(SkillLifecycleState.default.LIFECYCLE_ENDED, onErrorResolved);
}
};
exports.enableSkillSwitching = function () {
jibo.globalEvents.skillRelaunch.on(data => {
const skillName = data.match.skillID;
this.redirect(new (SkillSwitchDataCtor || SkillSwitchData)(this.skills[skillName], data));
});
jibo.action.setSkillSwitchHandler((skillName, skillData) => {
return new Promise((resolve) => {
const skill = this.skills[skillName];
let redirectToken = this.redirect(new (SkillSwitchDataCtor || SkillSwitchData)(skill, skillData));
let resolved = false;
redirectToken.onState((SkillLifecycleStateObj || SkillLifecycleState).SKILL_OPENED, () => {
resolved = true;
resolve(jibo.action.types.Status.SUCCEEDED);
});
redirectToken.onState((SkillLifecycleStateObj || SkillLifecycleState).LIFECYCLE_ENDED, () => {
if (!resolved) {
this.log.warn(`Skill lifecycle ended before skill was opened: ${redirectToken.skillLifecycleEndState}`);
}
resolved = true;
resolve(jibo.action.types.Status.FAILED);
});
});
});
};
exports.exit = function (exitingSkill, exitOptions = {}, done = () => { }) {
const skipEoS = !!(exitOptions.noElementsOfSurprise || exitOptions.globalNoMatch);
const currentSkill = this._skillSwitchScheduler.currentSkillRedirectToken ? this._skillSwitchScheduler.currentSkillRedirectToken.skillSwitchData.skill : null;
if (exitingSkill !== currentSkill) {
this.log.warn(`Trying to call Be#exit from non-current skill ${exitingSkill}. Current skill is ${currentSkill}`);
return;
}
if (!skipEoS &&
currentSkill !== this.idle &&
currentSkill !== this.eosSkill &&
!currentSkill.isElementOfSurprise &&
!currentSkill.skipSurprisesExternal) {
let redirectToken = this.redirect(new (SkillSwitchDataCtor || SkillSwitchData)(this.eosSkill, { lastSkill: currentSkill.assetPack }));
redirectToken.addOnSkillLifecycleEnd(done);
}
else {
let redirectToken = this.redirect(new (SkillSwitchDataCtor || SkillSwitchData)(this.idle, { exitOptions }));
redirectToken.addOnSkillLifecycleEnd(done);
}
};
exports.skillRedirect = function (redirectingSkill, name, options) {
const skill = this.skills[name];
const currentSkill = this._skillSwitchScheduler.currentSkillRedirectToken ? this._skillSwitchScheduler.currentSkillRedirectToken.skillSwitchData.skill : null;
if (redirectingSkill !== currentSkill) {
this.log.warn(`Trying to call Be#redirect from non-current skill ${redirectingSkill.assetPack}. Current skill is ${currentSkill.assetPack}`);
return;
}
if (skill) {
this.log.info("REDIRECT: skill redirect: ", name, options);
this.redirect(new (SkillSwitchDataCtor || SkillSwitchData)(skill, options));
}
else {
this.log.error("REDIRECT: skill redirect failed. cannot find skill: ", name, options);
}
};
exports.redirect = function (skillSwitchData) {
return this._skillSwitchScheduler.requestSkillRedirect(skillSwitchData);
};
exports.destroy = function (callback) {
if (document.getElementById('splash')) {
document.getElementById('splash').remove();
}
jibo.globalEvents.skillRelaunch.removeAllListeners();
this._skillSwitchScheduler.destroy()
.then(() => {
let destroySkillPromises = [];
Object.keys(this.skills).forEach((skillId) => {
let destroySkillPromise = new Promise((resolve, reject) => {
try {
this.skills[skillId].destroy((err) => {
jibo.loader.deleteCache(skillId);
if (err) {
reject(err);
}
else {
resolve();
}
});
}
catch (err) {
reject(err);
}
});
destroySkillPromises.push(destroySkillPromise);
});
return Promise.all(destroySkillPromises);
})
.catch((err) => {
this.log.error(err);
})
.then(() => {
callback();
});
};

View File

@@ -0,0 +1,123 @@
"use strict";
const jibo = require('jibo');
let rlog = null;
try {
rlog = require('./robot-logger');
} catch (e) {
// ignore
}
exports.postInit = function (err) {
this.log.debug('postInit !!');
try {
if (rlog && typeof rlog.raw === 'function') {
rlog.raw('[BE] postInit starting');
}
if (rlog) {
rlog.info('be', 'postInit starting');
}
} catch (e) {
// ignore
}
if (err) {
this.log.error(err);
this.initDoneCallback(err);
return;
}
this.log.debug('initting alalytics');
// call analytics initializer (keeps logic in separate module)
try {
require('./analytics').initAnalyticsContext.call(this);
}
catch (e) {
this.log.warn('init analytics failed', e);
}
this.log.info('Jibo is ready... awaiting launch command.');
try {
require('../hex-hitarea').patch(jibo);
}
catch (e) {
this.log.warn('hex-hitarea patch failed', e);
}
// Apply dynamic skills patch to add custom skills from menuEntry.json files
try {
const mainMenuPatch = require('../menu/main-menu-patch');
mainMenuPatch.applyPatch(this);
mainMenuPatch.patchLoader(jibo);
this.log.info('Dynamic skills patch applied');
}
catch (e) {
this.log.warn('Dynamic skills patch failed (non-fatal):', e.message || e);
}
// Optional: dynamically load skills referenced by modular menu entries.
// This is the missing piece for “menu entry exists” => “skill is launchable”.
try {
const dynSkills = require('./dynamic-skills');
const res = dynSkills.loadMissingSkillsFromMenuEntries(this);
try {
this.log.info('Dynamic skill load complete', {
loaded: res && res.loaded ? res.loaded.length : 0,
failed: res && res.failed ? res.failed.length : 0,
skillsRoot: res && res.skillsRoot,
providersDir: res && res.providersDir
});
} catch (e2) {
// ignore
}
}
catch (e) {
this.log.warn('Dynamic skill loader failed (non-fatal):', e.message || e);
}
// Optional: AI Bridge (modular; can run models off-robot for now)
try {
if (rlog && typeof rlog.raw === 'function') {
rlog.raw('[BE] initializing AI bridge');
}
require('./ai-bridge').initAIBridge(this, jibo);
this.log.info('AI bridge initialized');
if (rlog && typeof rlog.raw === 'function') {
rlog.raw('[BE] AI bridge initialized');
}
}
catch (e) {
this.log.warn('AI bridge init failed (non-fatal):', e.message || e);
try {
if (rlog && typeof rlog.raw === 'function') {
rlog.raw('[BE] AI bridge init failed: ' + String(e && (e.stack || e.message || e)));
}
if (rlog) {
rlog.warn('be', 'ai bridge init failed', { err: String(e && (e.stack || e.message || e)) });
}
} catch (e2) {
// ignore
}
}
// Optional: rosbridge connector (connects to external rosbridge websocket)
try {
try {
var rosbridge = require('./rosbridge');
if (rosbridge && typeof rosbridge.init === 'function') {
rosbridge.init(this, jibo);
if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge connector initialized');
if (rlog && typeof rlog.info === 'function') rlog.info('be', 'rosbridge connector initialized');
else this.log.info('rosbridge connector initialized');
}
} catch (e) {
if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge module not present or failed to init (ok)');
else this.log.info('rosbridge module not present or failed to init (ok)');
}
} catch (e) {
if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge init failed: ' + String(e && (e.stack || e.message || e)));
this.log.warn('rosbridge init failed (non-fatal):', e.message || e);
}
jibo.face.views.changeView({ removeAll: true, leaveEmpty: true }, () => {
this.selectFirstSkill(this.launchFirstSkill.bind(this));
});
};

View File

@@ -0,0 +1,60 @@
"use strict";
// Minimal UDP logger client for old robot environments.
// Safe to require from any module; if logd is not running, it will just no-op.
const dgram = require("dgram");
const DEFAULT_HOST = process.env.JIBO_LOGD_HOST || "127.0.0.1";
const DEFAULT_PORT = parseInt(process.env.JIBO_LOGD_PORT || "15140", 10);
let _socket = null;
function getSocket() {
if (_socket) return _socket;
try {
_socket = dgram.createSocket("udp4");
_socket.unref();
return _socket;
} catch (e) {
return null;
}
}
function send(obj) {
const sock = getSocket();
if (!sock) return;
let payload;
try {
payload = Buffer.from(JSON.stringify(obj));
} catch (e) {
return;
}
try {
sock.send(payload, 0, payload.length, DEFAULT_PORT, DEFAULT_HOST, function () { });
} catch (e) {
// ignore
}
}
function mk(tag, level, msg, data) {
const o = { tag: tag || "skill", level: level || "info", msg: String(msg || "") };
if (typeof data !== "undefined") o.data = data;
return o;
}
exports.info = function (tag, msg, data) { send(mk(tag, "info", msg, data)); };
exports.warn = function (tag, msg, data) { send(mk(tag, "warn", msg, data)); };
exports.error = function (tag, msg, data) { send(mk(tag, "error", msg, data)); };
exports.debug = function (tag, msg, data) { send(mk(tag, "debug", msg, data)); };
exports.raw = function (line) {
const sock = getSocket();
if (!sock) return;
const payload = Buffer.from(String(line || ""));
try {
sock.send(payload, 0, payload.length, DEFAULT_PORT, DEFAULT_HOST, function () { });
} catch (e) { }
};

View File

@@ -0,0 +1,463 @@
"use strict";
// Simple ROS bridge client for Jibo BE
// - Connects to a rosbridge websocket and subscribes to /jibo_remote
// - Handles do_enter_rosbridge_skill and do_exit_rosbridge_skill commands
var WebSocket = null;
try { WebSocket = require('ws'); } catch (e) { WebSocket = null; }
var urlLib = require('url');
var DEFAULT_WS = process.env.ROSBRIDGE_WS || 'ws://192.168.1.5:9090';
var state = {
ws: null,
subId: null,
reconnectTimer: null,
lastProcessed: {},
};
// Robot logger (available on BE runtime)
var rlog = null;
try {
if (typeof global !== 'undefined' && global.__rlog) rlog = global.__rlog;
if (!rlog) rlog = require('./robot-logger');
} catch (e) { rlog = null; }
function rlogRaw(s) {
try { if (rlog && typeof rlog.raw === 'function') return rlog.raw(String(s || '')); } catch (e) {}
try { console.log(String(s || '')); } catch (e) {}
}
function rlogInfo(tag, text, data) {
try {
if (rlog && typeof rlog.info === 'function') return rlog.info(String(tag || 'rosbridge'), String(text || ''), data || {});
} catch (e) {}
try { console.log('[INFO]', String(tag || 'rosbridge'), String(text || ''), data || ''); } catch (e) {}
}
function rlogWarn(tag, text, data) {
try {
if (rlog && typeof rlog.warn === 'function') return rlog.warn(String(tag || 'rosbridge'), String(text || ''), data || {});
} catch (e) {}
try { console.warn('[WARN]', String(tag || 'rosbridge'), String(text || ''), data || ''); } catch (e) {}
}
function parseWsUrl(s) {
try { return String(s || '').trim(); } catch (e) { return DEFAULT_WS; }
}
function getCandidateWsUrls(beRuntime) {
var list = [];
try {
var envUrl = process.env.ROSBRIDGE_WS;
if (envUrl) list.push(parseWsUrl(envUrl));
} catch (e) {}
try {
var cfg = beRuntime && beRuntime.config && beRuntime.config.rosbridge && beRuntime.config.rosbridge.ws;
if (cfg) list.push(parseWsUrl(cfg));
} catch (e) {}
// Common fallbacks
list.push('ws://127.0.0.1:9090');
list.push(DEFAULT_WS);
// Attempt gateway-derived host if available
try {
var os = require('os');
var ifaces = os.networkInterfaces();
Object.keys(ifaces || {}).forEach(function (k) {
(ifaces[k] || []).forEach(function (info) {
if (!info || info.internal || info.family !== 'IPv4') return;
var parts = String(info.address).split('.');
if (parts.length === 4) {
// guess the gateway as .1
parts[3] = '1';
list.push('ws://' + parts.join('.') + ':9090');
}
});
});
} catch (e) {}
// Deduplicate while keeping order
var seen = {};
var out = [];
for (var i = 0; i < list.length; i++) {
try {
var v = String(list[i] || '').trim();
if (!v) continue;
if (!seen[v]) { seen[v] = true; out.push(v); }
} catch (e) {}
}
return out;
}
function sendWs(obj) {
try {
if (!state.ws || state.ws.readyState !== 1) {
rlogWarn('rosbridge', 'ws not open, drop send', { obj: obj });
return;
}
var payload = JSON.stringify(obj);
rlogInfo('rosbridge', 'ws.send', { payload: obj });
state.ws.send(payload);
} catch (e) { /* ignore */ }
}
function subscribe(topic, type) {
// rosbridge subscribe message
var id = 'sub_' + Date.now() + '_' + Math.floor(Math.random() * 1000);
state.subId = id;
rlogInfo('rosbridge', 'subscribe', { id: id, topic: topic, type: type });
sendWs({ op: 'subscribe', id: id, type: type || '', topic: topic });
}
function unsubscribe() {
if (!state.subId) return;
rlogInfo('rosbridge', 'unsubscribe', { id: state.subId });
sendWs({ op: 'unsubscribe', id: state.subId });
state.subId = null;
}
function connect(wsUrl, onMessage) {
var url = parseWsUrl(wsUrl || DEFAULT_WS);
rlogInfo('rosbridge', 'connect attempt', { url: url });
try {
if (WebSocket) {
state.ws = new WebSocket(url);
} else {
// try built-in if ws not present (not ideal)
var Ws = require('websocket').w3cwebsocket;
state.ws = new Ws(url);
}
} catch (e) {
rlogWarn('rosbridge', 'connect failed', { err: String(e) });
scheduleReconnect(wsUrl, onMessage);
return;
}
state.ws.onopen = function () {
rlogInfo('rosbridge', 'ws open');
try {
subscribe('/jibo_remote', '/jibo_msgs/JiboRemote');
} catch (e) { rlogWarn('rosbridge', 'subscribe failed on open /jibo_remote', { err: String(e) }); }
try {
subscribe('/jibo', '/jibo_msgs/JiboAction');
} catch (e) { rlogWarn('rosbridge', 'subscribe failed on open /jibo', { err: String(e) }); }
};
state.ws.onmessage = function (evt) {
rlogRaw('[rosbridge] raw message: ' + (evt && evt.data ? String(evt.data) : ''));
var data = null;
try { data = JSON.parse(evt.data); } catch (e) { rlogWarn('rosbridge', 'json parse failed', { err: String(e), raw: String(evt && evt.data) }); return; }
// rosbridge wraps messages with { op: 'publish', topic: '...', msg: {...} }
if (data && data.op === 'publish') {
try {
var topic = data.topic || 'unknown';
// Throttle frequent messages per-topic to avoid blocking the BE event loop.
var minMs = parseInt(process.env.ROSBRIDGE_MIN_INTERVAL_MS || '200', 10) || 200;
var now = Date.now();
var last = state.lastProcessed[topic] || 0;
if (now - last < minMs) {
rlogInfo('rosbridge', 'throttled publish', { topic: topic, droppedMs: now - last, minMs: minMs });
return;
}
state.lastProcessed[topic] = now;
// Defer handling so heavy work doesn't block the socket message parser.
var handler = function () {
try {
rlogInfo('rosbridge', 'publish received', { topic: data.topic, msg: data.msg });
if (data.msg) onMessage && onMessage(data.msg, data.topic);
} catch (e) { rlogWarn('rosbridge', 'publish handler error', { err: String(e) }); }
};
if (typeof setImmediate === 'function') setImmediate(handler); else setTimeout(handler, 0);
} catch (e) {
rlogWarn('rosbridge', 'error handling publish', { err: String(e) });
}
return;
}
rlogInfo('rosbridge', 'ws message', { op: data && data.op, data: data });
};
state.ws.onclose = function (ev) { rlogWarn('rosbridge', 'ws closed', { code: ev && ev.code, reason: ev && ev.reason }); scheduleReconnect(wsUrl, onMessage); };
state.ws.onerror = function (err) { rlogWarn('rosbridge', 'ws error', { err: String(err) }); };
}
function scheduleReconnect(wsUrl, onMessage) {
if (state.reconnectTimer) return;
rlogInfo('rosbridge', 'scheduling reconnect', { delayMs: 5000 });
state.reconnectTimer = setTimeout(function () {
state.reconnectTimer = null;
rlogInfo('rosbridge', 'reconnecting now');
connect(wsUrl, onMessage);
}, 5000);
}
// Try a list of candidate URLs sequentially until one connects.
function connectToCandidates(beRuntime, onMessage) {
var candidates = getCandidateWsUrls(beRuntime);
var idx = 0;
function tryNext() {
if (state.ws && state.ws.readyState === 1) return; // already connected
if (idx >= candidates.length) {
rlogWarn('rosbridge', 'no candidates left, will schedule reconnect');
scheduleReconnect(candidates[0], onMessage);
return;
}
var url = candidates[idx++];
rlogInfo('rosbridge', 'trying candidate', { url: url });
// attempt connect and use a short timeout to move to next candidate
var tried = false;
var timeout = setTimeout(function () {
if (tried) return;
tried = true;
try { if (state.ws) state.ws.close(); } catch (e) {}
rlogWarn('rosbridge', 'candidate timeout, trying next', { url: url });
// small delay before next
setTimeout(tryNext, 250);
}, 3500);
try {
// reuse existing connect path but attach temporary handlers
var prevOnOpen = state.ws && state.ws.onopen;
connect(url, function (msg, topic) {
clearTimeout(timeout);
onMessage && onMessage(msg, topic);
});
// when open, cancel other attempts
(function (u) {
var wsInst = state.ws;
if (!wsInst) return;
var origOnOpen = wsInst.onopen;
wsInst.onopen = function (ev) {
clearTimeout(timeout);
rlogInfo('rosbridge', 'connected candidate', { url: u });
try { if (typeof origOnOpen === 'function') origOnOpen.call(wsInst, ev); } catch (e) {}
};
// if it closes or errors before open, try next
var origOnClose = wsInst.onclose;
wsInst.onclose = function (ev) {
clearTimeout(timeout);
if (!tried) {
tried = true;
rlogWarn('rosbridge', 'candidate closed before ready, next', { url: u });
setTimeout(tryNext, 250);
}
try { if (typeof origOnClose === 'function') origOnClose.call(wsInst, ev); } catch (e) {}
};
var origOnError = wsInst.onerror;
wsInst.onerror = function (err) {
clearTimeout(timeout);
if (!tried) {
tried = true;
rlogWarn('rosbridge', 'candidate error, next', { url: u, err: String(err) });
try { if (wsInst) wsInst.close(); } catch (e) {}
setTimeout(tryNext, 250);
}
try { if (typeof origOnError === 'function') origOnError.call(wsInst, err); } catch (e) {}
};
})(url);
} catch (e) {
clearTimeout(timeout);
rlogWarn('rosbridge', 'connect threw, trying next', { url: url, err: String(e) });
setTimeout(tryNext, 250);
}
}
tryNext();
}
function close() {
rlogInfo('rosbridge', 'close requested');
try { unsubscribe(); } catch (e) { rlogWarn('rosbridge', 'unsubscribe failed', { err: String(e) }); }
try { if (state.ws) state.ws.close(); } catch (e) { rlogWarn('rosbridge', 'ws close failed', { err: String(e) }); }
state.ws = null;
if (state.reconnectTimer) { clearTimeout(state.reconnectTimer); state.reconnectTimer = null; }
}
exports.init = function (beRuntime, jibo) {
// Prefer robot-logger when available
var rlog = null;
try {
if (beRuntime && beRuntime.rlog) rlog = beRuntime.rlog;
if (!rlog && typeof global !== 'undefined' && global.__rlog) rlog = global.__rlog;
if (!rlog) rlog = require('./robot-logger');
} catch (e) {
rlog = null;
}
var log = rlog || (beRuntime && beRuntime.log) || console;
var wsUrl = (beRuntime && beRuntime.config && beRuntime.config.rosbridge && beRuntime.config.rosbridge.ws) || DEFAULT_WS;
function logInfo(text, data) {
try {
if (rlog && typeof rlog.info === 'function') return rlog.info('rosbridge', String(text || ''), data || {});
if (log && typeof log.info === 'function') return log.info(String(text || ''));
console.log(String(text || ''));
} catch (e) { /* ignore */ }
}
function logWarn(text, data) {
try {
if (rlog && typeof rlog.warn === 'function') return rlog.warn('rosbridge', String(text || ''), data || {});
if (log && typeof log.warn === 'function') return log.warn(String(text || ''));
console.warn(String(text || ''));
} catch (e) { /* ignore */ }
}
function handleMsg(msg, topic) {
try {
if (msg.do_enter_rosbridge_skill) {
logInfo('enter request', msg);
// Launch a named skill if provided
var skillName = msg.launch_skill || msg.skill || '@be/main-menu';
try {
// Attempt lifecycle-based redirect for a proper skill switch
var path = require('path');
var SkillSwitchData = null;
try {
if (typeof global !== 'undefined' && global && global.be && global.be.constructor) {
SkillSwitchData = global.be.constructor.SkillSwitchData;
}
} catch (e) { SkillSwitchData = null; }
function _interop(m) { return (m && (m.__esModule || m.default)) ? (m.default || m) : m; }
var SkillSwitchDataCtor = null;
if (SkillSwitchData) SkillSwitchDataCtor = _interop(SkillSwitchData);
if (!SkillSwitchDataCtor) {
try { SkillSwitchDataCtor = require(path.join(jibo.utils.PathUtils.findRoot(), 'SkillSwitchData')); } catch (e) { try { const Root = require(path.join(jibo.utils.PathUtils.findRoot(), 'index.js')); SkillSwitchDataCtor = (Root && (Root.SkillSwitchData || (Root.default && Root.default.SkillSwitchData))) || undefined; } catch (e2) { SkillSwitchDataCtor = null; } }
}
var skillObj = beRuntime && beRuntime.skills ? beRuntime.skills[skillName] : null;
if (skillObj && SkillSwitchDataCtor) {
var ssd = new (SkillSwitchDataCtor)(skillObj, {});
try { require('./lifecycle').redirect.call(beRuntime, ssd); logInfo('requested skill redirect', { skill: skillName }); } catch (e) { logWarn('skill redirect failed', { err: String(e), skill: skillName }); }
} else {
logWarn('skill not found or SkillSwitchDataCtor missing', { skill: skillName });
}
} catch (e) {
logWarn('enter handling failed', { err: String(e) });
}
}
if (msg.do_exit_rosbridge_skill) {
logInfo('exit request', msg);
try {
// Redirect to idle via lifecycle
var path2 = require('path');
var SkillSwitchData2 = null;
try {
if (typeof global !== 'undefined' && global && global.be && global.be.constructor) {
SkillSwitchData2 = global.be.constructor.SkillSwitchData;
}
} catch (e) { SkillSwitchData2 = null; }
var SkillSwitchDataCtor2 = SkillSwitchData2 ? _interop(SkillSwitchData2) : null;
if (!SkillSwitchDataCtor2) {
try { SkillSwitchDataCtor2 = require(path2.join(jibo.utils.PathUtils.findRoot(), 'SkillSwitchData')); } catch (e) { try { const Root = require(path2.join(jibo.utils.PathUtils.findRoot(), 'index.js')); SkillSwitchDataCtor2 = (Root && (Root.SkillSwitchData || (Root.default && Root.default.SkillSwitchData))) || undefined; } catch (e2) { SkillSwitchDataCtor2 = null; } }
}
var idleSkill = beRuntime && beRuntime.idle ? beRuntime.idle : null;
if (idleSkill && SkillSwitchDataCtor2) {
var ssd2 = new (SkillSwitchDataCtor2)(idleSkill, {});
try { require('./lifecycle').redirect.call(beRuntime, ssd2); logInfo('requested redirect to idle'); } catch (e) { logWarn('idle redirect failed', { err: String(e) }); }
} else {
logWarn('idle redirect failed - missing idleSkill or ctor');
}
} catch (e) { logWarn('exit handling failed', { err: String(e) }); }
}
// Handle /jibo actions (e.g., TTS)
if (topic === '/jibo' || topic === 'jibo') {
try {
if (msg.do_tts || msg.do_tts === true || msg.tts_text) {
var t = msg.tts_text || msg.tts || msg.text || '';
if (t && t.length) {
logInfo('jibo action TTS', { text: t });
try {
// Normalize: if payload is JSON object string like '{"text":"..."}', extract.
try {
if (typeof t === 'string' && t.trim().charAt(0) === '{' && t.indexOf('"text"') !== -1) {
var parsed = JSON.parse(t);
if (parsed && parsed.text) t = parsed.text;
}
} catch (e) { /* ignore parse error */ }
// Detect ESML/SSML-like input and request SSML mode when present
function _isEsml(s) {
try {
if (!s || typeof s !== 'string') return false;
var ls = s.toLowerCase();
return ls.indexOf('<es') !== -1 || ls.indexOf('<speak') !== -1 || ls.indexOf('<ssml') !== -1;
} catch (e) { return false; }
}
function _ensureSpeakWrapper(s) {
try {
if (!s) return s;
var trimmed = s.trim();
if (trimmed.toLowerCase().indexOf('<speak') === 0) return s;
return '<speak>' + s + '</speak>';
} catch (e) { return s; }
}
var useEsml = _isEsml(t);
if (useEsml && jibo && jibo.tts && typeof jibo.tts.speak === 'function') {
var payload = _ensureSpeakWrapper(String(t));
jibo.tts.speak(payload, { mode: jibo.tts.TTSMode ? jibo.tts.TTSMode.SSML : undefined });
} else if (jibo && jibo.tts && typeof jibo.tts.speak === 'function') {
jibo.tts.speak(String(t), { mode: jibo.tts.TTSMode ? jibo.tts.TTSMode.TEXT : undefined });
} else if (beRuntime && beRuntime.api && typeof beRuntime.api.speak === 'function') { beRuntime.api.speak({ text: String(t), mode: useEsml ? 'ssml' : 'text' });
} else if (jibo && jibo.api && typeof jibo.api.speak === 'function') {
jibo.api.speak({ text: String(t), mode: useEsml ? 'ssml' : 'text' });
} else {
logWarn('no speak API available', { t: t });
}
} catch (e) { logWarn('tts speak failed', { err: String(e) }); }
}
}
} catch (e) { logWarn('failed handling /jibo action', { err: String(e), topic: topic, msg: msg }); }
}
if (msg.tts_text) {
try {
// Normalize and speak similar to /jibo handling
var txt = msg.tts_text;
try {
if (typeof txt === 'string' && txt.trim().charAt(0) === '{' && txt.indexOf('"text"') !== -1) {
var p2 = JSON.parse(txt);
if (p2 && p2.text) txt = p2.text;
}
} catch (e) {}
// Reuse ESML detection logic
function _isEsml2(s) {
try {
if (!s || typeof s !== 'string') return false;
var ls = s.toLowerCase();
return ls.indexOf('<es') !== -1 || ls.indexOf('<speak') !== -1 || ls.indexOf('<ssml') !== -1;
} catch (e) { return false; }
}
function _ensureSpeakWrapper2(s) {
try {
if (!s) return s;
var trimmed = s.trim();
if (trimmed.toLowerCase().indexOf('<speak') === 0) return s;
return '<speak>' + s + '</speak>';
} catch (e) { return s; }
}
var useEsml2 = _isEsml2(txt);
if (useEsml2 && jibo && jibo.tts && typeof jibo.tts.speak === 'function') {
var payload2 = _ensureSpeakWrapper2(String(txt));
jibo.tts.speak(payload2, { mode: jibo.tts.TTSMode ? jibo.tts.TTSMode.SSML : undefined });
} else if (jibo && jibo.tts && typeof jibo.tts.speak === 'function') {
jibo.tts.speak(String(txt), { mode: jibo.tts.TTSMode ? jibo.tts.TTSMode.TEXT : undefined });
} else if (beRuntime && beRuntime.api && typeof beRuntime.api.speak === 'function') {
beRuntime.api.speak({ text: String(txt), mode: useEsml2 ? 'ssml' : 'text' });
} else if (jibo && jibo.api && typeof jibo.api.speak === 'function') {
jibo.api.speak({ text: String(txt), mode: useEsml2 ? 'ssml' : 'text' });
} else {
logWarn('no speak API available for tts_text', { tts_text: txt });
}
} catch (e) { logWarn('tts speak failed', { err: String(e) }); }
}
} catch (e) { logWarn('rosbridge handleMsg error', { err: String(e) }); }
}
connect(wsUrl, handleMsg);
return {
close: close,
};
};
exports.shutdown = function () { close(); };

View File

@@ -0,0 +1,6 @@
#!/bin/sh
# Run jibo-asr-service using the *writable* config under Skills.
# This avoids relying on /usr/local/etc (often read-only on device).
exec /usr/local/bin/jibo-asr-service -c /opt/jibo/Jibo/Skills/@be/be/be/jibo-asr-service.local.json

View File

@@ -0,0 +1,24 @@
"use strict";
// Helper to encapsulate skill discovery/creation logic found in the bundle's
// Be constructor. This is intentionally small and only documents where to
// extract the logic when you want to fully replace the bundled constructor.
exports.createSkills = function (packageInfo, jiboRequire, createSkillFn) {
// Example small extractor — callers should implement creating Skill instances
// Parameters:
// - packageInfo: parsed package.json
// - jiboRequire: require('jibo') instance (passed in from consumer)
// - createSkillFn: function(id) => returns constructed skill
const skills = {};
(packageInfo.jibo && packageInfo.jibo.skills || []).forEach((id) => {
try {
const Skill = createSkillFn(id);
if (Skill) skills[id] = Skill;
}
catch (e) {
console.warn('skill creation failed', id, e);
}
});
return skills;
};

View File

@@ -0,0 +1,21 @@
{
"logUncaughtExceptions": true,
"logUnhandledRejections": true,
"stackTraceLimit": 30,
"outputs": {
"console": {
"outputFileAndLine": false
},
"syslog": {
"port": 514,
"target": "127.0.0.1",
"outputFileAndLine": false
}
},
"namespaces": {
"": {
"console": "info",
"syslog": "info"
}
}
}

View File

@@ -0,0 +1,21 @@
{
"logUncaughtExceptions": true,
"logUnhandledRejections": true,
"stackTraceLimit": 30,
"outputs": {
"console": {
"outputFileAndLine": false
},
"syslog": {
"port": 514,
"target": "127.0.0.1",
"outputFileAndLine": false
}
},
"namespaces": {
"": {
"console": "info",
"syslog": "info"
}
}
}

View File

@@ -0,0 +1,21 @@
{
"logUncaughtExceptions": true,
"logUnhandledRejections": true,
"stackTraceLimit": 30,
"outputs": {
"console": {
"outputFileAndLine": false
},
"syslog": {
"port": 514,
"target": "127.0.0.1",
"outputFileAndLine": false
}
},
"namespaces": {
"": {
"console": "none",
"syslog": "info"
}
}
}

View File

@@ -0,0 +1,21 @@
{
"logUncaughtExceptions": true,
"logUnhandledRejections": true,
"stackTraceLimit": 30,
"outputs": {
"console": {
"outputFileAndLine": false
},
"syslog": {
"port": 514,
"target": "127.0.0.1",
"outputFileAndLine": false
}
},
"namespaces": {
"": {
"console": "none",
"syslog": "info"
}
}
}

View File

@@ -0,0 +1,53 @@
"use strict";
// Lightweight monkey-patch to enable polygon (hex) hit areas and per-button polygon configs.
exports.patch = function (jibo) {
try {
if (!jibo || !jibo.face || !jibo.face.views) return;
const MenuButton = jibo.face.views.MenuButton;
const Element = jibo.face.views.Element;
if (MenuButton && MenuButton.prototype && MenuButton.prototype.setupHitArea) {
const origSetup = MenuButton.prototype.setupHitArea;
MenuButton.prototype.setupHitArea = function (bounds) {
if (!bounds && !this._hitArea && this._dimensions) {
// default: regular flat-top hexagon centered in the button bounds
const w = this._dimensions.width;
const h = this._dimensions.height;
const cx = w / 2;
const cy = h / 2;
const r = Math.min(w, h) / 2;
const pts = [];
for (let i = 0; i < 6; i++) {
const angle = (Math.PI / 180) * (60 * i - 30);
pts.push(cx + r * Math.cos(angle), cy + r * Math.sin(angle));
}
bounds = new PIXI.Polygon(pts);
}
return origSetup.call(this, bounds);
};
}
if (Element && Element.prototype && Element.prototype.assignConfig) {
const origAssign = Element.prototype.assignConfig;
Element.prototype.assignConfig = function (configData) {
origAssign.call(this, configData);
if (configData && configData.hitAreaPolygon && Array.isArray(configData.hitAreaPolygon)) {
// hitAreaPolygon should be an array of numbers [x1,y1,x2,y2,...]
try {
this._hitArea = new PIXI.Polygon(configData.hitAreaPolygon.slice());
// If the display already exists, apply immediately
if (this.display) this.display.hitArea = this._hitArea;
}
catch (e) {
// swallow errors - leave original behavior intact
if (jibo && jibo.log && jibo.log.warn) jibo.log.warn('Invalid hitAreaPolygon:', e);
}
}
};
}
}
catch (e) {
if (jibo && jibo.log && jibo.log.warn) jibo.log.warn('hex-hitarea patch error', e);
}
};

34
Skills/@be/be/index.html Normal file
View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>Be</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
}
#splash{
background-image:url('./resources/JiboSplash.png');
width: 1280px;
height: 720px;
}
#face {
width: 1280px;
height: 720px;
}
</style>
</head>
<body>
<div id="face"></div>
<script>
const Be = require("./index");
let be = new Be();
be.init(()=>{
// @if DEBUG
be.log.info("Finished running Be init");
// @endif
});
</script>
</body>
</html>

1193
Skills/@be/be/index.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,65 @@
# Menu providers (drop-in menu customization)
Providers let you add/override menu entries without editing the core menu patch.
Default entries directory (on robot):
- `/opt/jibo/Jibo/Skills/@be/menu-entries.d/`
Legacy fallback (yea ik its the same stop talking):
- `/opt/jibo/Jibo/Skills/@be/menu-providers.d/`
## Provider file types
### JSON provider (`*.json`)
Supported shapes:
- An array of entries: `[ { ... }, { ... } ]`
- Or `{ "entries": [ ... ] }`
Entry schema (same as `menuEntry.json` scan output):
- `id` (string, required)
- `type` (`skill` or `submenu`)
- `title`
- `icon`
- `color`
- `description`
- `order` (number)
- `skillId` (for type `skill`)
- `submenuTitle` (for type `submenu`)
- `children` (array of skill entries, for type `submenu`)
- `childrenDir` (for type `submenu`, optional):
- Absolute path (starts with `/`) or relative to `skillsRoot`
- If provided and `children` is missing/empty, the patch will scan this directory for child skills (subfolders containing `menuEntry.json`).
Example submenu that lists a directory:
```json
[
{
"id": "fun_stuff",
"type": "submenu",
"title": "Fun Stuff",
"icon": "resources/icons/fun-stuff.png",
"order": 20,
"childrenDir": "FunStuff"
}
]
```
### JS provider (`*.js`)
Exports one of:
- `module.exports = function(ctx) { return [ ...entries... ]; }`
- `exports.getEntries = function(ctx) { return [ ...entries... ]; }`
- `exports.entries = [ ...entries... ]`
`ctx` includes:
- `skillsRoot`
- `providersDir`
- `log` (function)
## Conflict rules
- If a provider entry has the same `id` as a scanned entry, the provider entry wins.
- Sorting: by `order` then by title.

View File

@@ -0,0 +1,225 @@
# Jibo Dynamic Menu System
This module provides a dynamic menu system that automatically scans the Skills directory for `menuEntry.json` files and adds them to Jibo's menu screen.
## How It Works
When Jibo boots up, the `main-menu-patch.js` module:
1. Patches `jibo.loader.load()` to intercept menu loading
2. Scans the `/opt/jibo/Jibo/Skills` directory for folders containing `menuEntry.json`
3. Injects the found skills/submenus into the main menu
4. Patches the main-menu skill's `redirectToSkill` to handle custom skill launches
## Quick Start
### Adding a Simple Skill Button to the Menu (you really should follow PROVIDERS.md at this point)
1. Create a folder in the `Skills` directory (e.g., `MySkill/`)
2. Add a `menuEntry.json` file inside:
```json
{
"type": "skill",
"title": "My Skill",
"icon": "resources/icons/settings.png",
"color": "blue",
"skillId": "MySkill",
"description": "Short description of my skill",
"order": 50
}
```
3. Your skill will automatically appear in Jibo's main menu!
**Note:** For the button to actually launch a skill, that skill must be a proper Jibo skill package registered in `@be/be/package.json`. If the skill isn't found, Jibo will show a fallback message.
### Creating a Submenu (Category/Folder)
1. Create a folder in the `Skills` directory (e.g., `Games/`)
2. Add a `menuEntry.json` marking it as a submenu:
```json
{
"type": "submenu",
"title": "Games",
"submenuTitle": "Choose a Game",
"icon": "resources/icons/fun-stuff.png",
"color": "purple",
"description": "Fun games to play with Jibo",
"order": 10
}
```
3. Inside that folder, create subfolders for each skill, each with their own `menuEntry.json`
4. A button will appear in the main menu that opens a submenu with those skills!
## Directory Structure Example (OLD)
```
/opt/jibo/Jibo/Skills/
├── @be/ # Core brain (ignored by scanner)
├── MySimpleSkill/
│ └── menuEntry.json # type: "skill" - appears as button in main menu
├── MyGames/
│ ├── menuEntry.json # type: "submenu" - opens submenu when pressed
│ ├── GameOne/
│ │ └── menuEntry.json # type: "skill" - appears in MyGames submenu
│ └── GameTwo/
│ └── menuEntry.json # type: "skill" - appears in MyGames submenu
```
## menuEntry.json Schema
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | `"skill"` \| `"submenu"` | **Yes** | `"skill"` for buttons, `"submenu"` for folders |
| `title` | string | **Yes** | Button label shown on Jibo's screen |
| `icon` | string | **Yes** | Icon path (see Icon Paths below) |
| `color` | string | No | Button color theme (see below). Default: `"default"` |
| `skillId` | string | **Yes** (for skills) | The skill ID to launch |
| `submenuTitle` | string | No | Title shown when submenu is opened |
| `description` | string | No | Tooltip/accessibility text |
| `order` | number | No | Sort order (lower = first). Default: `100` |
| `hidden` | boolean | No | Set to `true` to hide from menu |
### Available Colors
- `default` - Standard gray
- `blue` - Blue theme
- `green` - Green theme
- `red` - Red theme
- `purple` - Purple theme
- `orange` - Orange theme
- `teal` - Teal/cyan theme
- `pink` - Pink theme
- `yellow` - Yellow theme
- `cyan` - Cyan theme
### Icon Paths
Icons are resolved relative to the `@be/main-menu` package. Available built-in icons:
| Icon Path | Description |
|-----------|-------------|
| `resources/icons/settings.png` | Settings gear |
| `resources/icons/fun-stuff.png` | Star/fun icon (good for submenus) |
| `resources/icons/clock.png` | Clock |
| `resources/icons/heart.png` | Heart |
| `resources/icons/surprise.png` | Gift/surprise |
| `resources/icons/dance.png` | Dancing figure |
| `resources/icons/joke.png` | Comedy mask |
| `resources/icons/tips.png` | Lightbulb |
| `resources/icons/gallery.png` | Photo gallery |
| `resources/icons/radio.png` | Music notes |
| `resources/icons/create.png` | Camera |
| `resources/icons/photobooth.png` | Photo booth |
| `resources/icons/exercise.png` | Exercise/yoga |
| `resources/icons/weather.png` | Weather cloud |
| `resources/icons/calendar.png` | Calendar |
| `resources/icons/circuit-saver.png` | Circuit saver |
| `resources/icons/dice.png` | Dice |
| `resources/icons/coin.png` | Coin |
| `resources/icons/emoji.png` | Emoji face |
| `resources/icons/word-of-the-day.png` | Word icon |
| `resources/icons/news.png` | News icon |
| `resources/icons/twerk.png` | Twerk icon |
| `resources/icons/scanner.png` | Scanner icon |
## Example: Complete Skill Setup
### Simple Skill (testSkillA/menuEntry.json)
```json
{
"type": "skill",
"title": "Test Skill A",
"icon": "resources/icons/settings.png",
"color": "blue",
"skillId": "testSkillA",
"description": "A test skill",
"order": 10
}
```
### Submenu (TestSubMenuA/menuEntry.json)
```json
{
"type": "submenu",
"title": "Test Submenu",
"submenuTitle": "Test Skills",
"icon": "resources/icons/fun-stuff.png",
"color": "purple",
"description": "A submenu with test skills",
"order": 20
}
```
### Skill inside Submenu (TestSubMenuA/testSkillB/menuEntry.json)
```json
{
"type": "skill",
"title": "Test Skill B",
"icon": "resources/icons/surprise.png",
"color": "green",
"skillId": "testSkillB",
"description": "A skill inside a submenu",
"order": 1
}
```
## Technical Details
### How the Patch Works
1. **postinit.js** calls `mainMenuPatch.applyPatch(be)` and `mainMenuPatch.patchLoader(jibo)`
2. When the main-menu skill tries to load `main-menu-verbal.json`, the patch intercepts it
3. The patch scans the Skills directory for `menuEntry.json` files
4. It converts them to the proper button format and injects them into the menu config
5. When a dynamic skill button is pressed, the patched `redirectToSkill` handles the launch
6. For submenus, a new menu view is displayed with the submenu's children
### Files
- `main-menu-patch.js` - Core patch that hooks into the system
- `skills-scanner.js` - Alternative scanning implementation (not currently used)
- `menu-manager.js` - Alternative menu display implementation (not currently used)
### Debugging
Check the browser console for log messages starting with `main-menu-patch:`. These show:
- When the patch is initialized
- When menu loads are intercepted
- How many dynamic skills are injected
- When skills/submenus are selected
## Limitations
- Nested submenus (submenus within submenus) are not supported
- Custom skills must be registered in `@be/be/package.json` to actually launch
- Icon files must exist in the `@be/main-menu/resources/icons/` directory
## Troubleshooting
### Skill button not appearing in menu?
1. Check that `menuEntry.json` exists and is valid JSON
2. Verify `hidden` is not set to `true`
3. Make sure `type` is set to `"skill"` or `"submenu"`
4. Check the browser console for `main-menu-patch:` log messages
### Submenu appears empty?
1. Make sure child skill folders have `menuEntry.json` files
2. Verify child skills have `type: "skill"` (nested submenus not supported)
3. Check that child folders are direct children of the submenu folder
### Skill button doesn't launch anything?
1. The skill needs to be a proper Jibo skill package
2. The skill must be registered in `@be/be/package.json` under `jibo.skills`
3. If unregistered, Jibo will show a fallback message
### Icon not loading?
1. Verify the icon path is correct (e.g., `resources/icons/settings.png`)
2. Only built-in icons from `@be/main-menu/resources/icons/` are supported
3. Check the list of available icons above

View File

@@ -0,0 +1,675 @@
"use strict";
/**
* Main Menu Patch - Injects dynamic skills from menuEntry.json files into Jibo's menu
*
* This patches:
* 1. jibo.loader.load() to intercept menu JSON loading and inject dynamic skills
* 2. The main-menu skill's redirectToSkill method to handle custom skill IDs
*/
const fs = require('fs');
const path = require('path');
const menuEntries = require('./menu-entries');
const skillsRootUtil = require('./skills-root');
function strStartsWith(s, prefix) {
return (typeof s === 'string') && (s.indexOf(prefix) === 0);
}
function strEndsWith(s, suffix) {
if (typeof s !== 'string' || typeof suffix !== 'string') return false;
if (suffix.length > s.length) return false;
return s.indexOf(suffix, s.length - suffix.length) !== -1;
}
function strIncludes(s, needle) {
return (typeof s === 'string') && (s.indexOf(needle) !== -1);
}
// Optional UDP logger (Python logd). Safe fallback if unavailable.
let robotLogger = null;
try {
robotLogger = require('../be/robot-logger');
} catch (e) {
robotLogger = null;
}
// Skills root directory (configurable)
let SKILLS_ROOT = skillsRootUtil.resolveSkillsRoot();
// Cache for scanned skills
let _cachedMenuEntries = null;
let _cachedSkillsRoot = null;
let _cachedProvidersDir = null;
let _submenuConfigs = {};
let _dynamicSkillIds = new Set(); // Track dynamic skill IDs for routing
let _beInstance = null;
let _jibo = null;
// Color mappings for skill buttons (name -> hex array)
const COLOR_MAP = {
'default': ['0xBBC6CA', '0x434D55'],
'blue': ['0x3765AB', '0x1A2563'],
'green': ['0x8EDD40', '0x31732A'],
'red': ['0xE81853', '0x850F40'],
'purple': ['0x9B59B6', '0x6C3483'],
'orange': ['0xFF892F', '0xAF4123'],
'teal': ['0x2BEFDC', '0x086969'],
'pink': ['0xFF69B4', '0xC71585'],
'cyan': ['0x25F2FB', '0x107799'],
'yellow': ['0xFFD700', '0xB8860B']
};
// Logging helper - writes to both console and file
const LOG_FILE = '/tmp/menu-patch.log';
function log(msg, ...args) {
const line = '[MENU-PATCH] ' + msg + ' ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ');
console.log(line);
try {
if (robotLogger && typeof robotLogger.raw === 'function') {
robotLogger.raw(line);
}
} catch (e) { }
try {
fs.appendFileSync(LOG_FILE, new Date().toISOString() + ' ' + line + '\n');
} catch (e) {}
}
function warn(msg, ...args) {
const line = '[MENU-PATCH WARN] ' + msg + ' ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ');
console.warn(line);
try {
if (robotLogger && typeof robotLogger.raw === 'function') {
robotLogger.raw(line);
}
} catch (e) { }
try {
fs.appendFileSync(LOG_FILE, new Date().toISOString() + ' ' + line + '\n');
} catch (e) {}
}
/**
* Safely read and parse a JSON file
*/
function safeReadJson(filePath) {
try {
const txt = fs.readFileSync(filePath, 'utf8');
if (!txt || txt.trim().length === 0) return null;
return JSON.parse(txt);
} catch (e) {
log('safeReadJson error for', filePath, e.message);
return null;
}
}
/**
* Check if path is a directory
*/
function isDirectory(p) {
try {
return fs.statSync(p).isDirectory();
} catch (e) {
return false;
}
}
/**
* Get colors array from color name or return default
*/
function getColors(colorName) {
if (Array.isArray(colorName)) return colorName;
if (typeof colorName === 'string' && colorName.startsWith('0x')) {
return [colorName, colorName];
}
return COLOR_MAP[colorName] || COLOR_MAP['default'];
}
/**
* Scan a skill directory for menuEntry.json
*/
function scanSkillEntry(skillDir, folderName) {
const menuJsonPath = path.join(skillDir, 'menuEntry.json');
const menuJson = safeReadJson(menuJsonPath);
if (!menuJson) {
return null;
}
if (menuJson.hidden === true) {
log('Skipping hidden entry:', folderName);
return null;
}
log('Found menuEntry.json in', folderName, '- type:', menuJson.type);
const entry = {
id: folderName,
type: menuJson.type || 'skill',
title: menuJson.title || menuJson.label || folderName,
icon: menuJson.icon || menuJson.iconSrc || 'resources/icons/settings.png',
color: menuJson.color || menuJson.colors || 'default',
description: menuJson.description || '',
path: skillDir,
order: typeof menuJson.order === 'number' ? menuJson.order : 100,
skillId: menuJson.skillId || folderName,
submenuTitle: menuJson.submenuTitle || menuJson.title || folderName
};
// Track this as a dynamic skill
if (entry.type === 'skill') {
_dynamicSkillIds.add(entry.skillId);
_dynamicSkillIds.add(entry.id);
}
// For submenus, scan child directories
if (entry.type === 'submenu') {
entry.children = scanSubmenuChildren(skillDir);
log('Submenu', folderName, 'has', entry.children.length, 'children');
}
return entry;
}
/**
* Scan children of a submenu directory
*/
function scanSubmenuChildren(submenuDir) {
const children = [];
let items;
try {
items = fs.readdirSync(submenuDir);
} catch (e) {
warn('Failed to read submenu dir:', submenuDir, e.message);
return children;
}
items.forEach(name => {
if (name.startsWith('.') || name === '@be' || name === 'node_modules') return;
const childPath = path.join(submenuDir, name);
if (!isDirectory(childPath)) return;
const entry = scanSkillEntry(childPath, name);
if (entry && entry.type === 'skill') {
children.push(entry);
}
});
// Sort by order, then alphabetically
children.sort((a, b) => {
if (a.order !== b.order) return a.order - b.order;
return a.title.localeCompare(b.title);
});
return children;
}
/**
* Scan the Skills root directory for all menu entries
*/
function scanAllMenuEntries() {
if (_cachedMenuEntries && _cachedSkillsRoot === SKILLS_ROOT) {
log('Returning cached entries:', _cachedMenuEntries.length);
return _cachedMenuEntries;
}
log('Scanning Skills directory:', SKILLS_ROOT);
_dynamicSkillIds.clear();
const res = menuEntries.getMenuEntries({
skillsRoot: SKILLS_ROOT,
log: function () {
try { log.apply(null, arguments); } catch (e) { }
}
});
_cachedMenuEntries = res.entries || [];
_cachedSkillsRoot = res.skillsRoot;
_cachedProvidersDir = res.providersDir;
// update dynamic id set
try {
if (res.dynamicSkillIds && typeof res.dynamicSkillIds.forEach === 'function') {
res.dynamicSkillIds.forEach(function (id) { _dynamicSkillIds.add(id); });
}
} catch (e) { }
log('Total entries found:', _cachedMenuEntries.length);
if (_cachedProvidersDir) {
log('Providers dir:', _cachedProvidersDir);
}
return _cachedMenuEntries;
}
/**
* Convert a menu entry to the main-menu button format
*/
function entryToMenuButton(entry) {
const button = {
id: entry.id,
label: entry.title,
colors: getColors(entry.color),
iconSrc: entry.icon
};
if (entry.type === 'submenu') {
// For submenus, create an action that loads a dynamically generated submenu
const submenuId = 'dynamic-submenu-' + entry.id;
// Register the submenu configuration
_submenuConfigs[submenuId] = createSubmenuConfig(entry);
button.action = {
type: 'utterance',
data: {
utterance: {
intent: 'loadMenu',
entities: {
destination: submenuId
}
}
}
};
} else {
// Regular skill button - use utterance format with dynamic marker
button.action = {
type: 'utterance',
data: {
utterance: {
intent: 'loadMenu',
entities: {
destination: 'dynamic:' + entry.skillId
}
}
}
};
}
return button;
}
/**
* Create a submenu view configuration
*/
function createSubmenuConfig(entry) {
const config = {
rule: 'main-menu/execute_main_menu',
timeout: 8,
viewConfig: {
type: 'MenuView',
id: 'submenu-' + entry.id,
title: entry.submenuTitle || entry.title,
listDefault: {
menuButtonType: 'SkillButton'
},
list: []
}
};
// Add back button that closes the menu (returns to main)
config.viewConfig.list.push({
id: '__back__',
label: '← Back',
colors: COLOR_MAP['default'],
iconSrc: 'resources/icons/settings.png',
action: {
type: 'utterance',
data: {
utterance: {
intent: 'loadMenu',
entities: {
destination: '__goback__'
}
}
}
}
});
// Add skill buttons from children
if (entry.children && entry.children.length > 0) {
entry.children.forEach(child => {
config.viewConfig.list.push({
id: child.id,
label: child.title,
colors: getColors(child.color),
iconSrc: child.icon,
action: {
type: 'utterance',
data: {
utterance: {
intent: 'loadMenu',
entities: {
destination: 'dynamic:' + child.skillId
}
}
}
}
});
});
}
return config;
}
/**
* Inject dynamic skills into a loaded menu config
*/
function injectDynamicSkills(originalConfig) {
log('injectDynamicSkills called');
if (!originalConfig) {
warn('originalConfig is null/undefined');
return originalConfig;
}
if (!originalConfig.viewConfig) {
warn('originalConfig.viewConfig is missing');
return originalConfig;
}
if (!originalConfig.viewConfig.list) {
warn('originalConfig.viewConfig.list is missing');
return originalConfig;
}
log('Original config has', originalConfig.viewConfig.list.length, 'buttons');
const entries = scanAllMenuEntries();
if (entries.length === 0) {
warn('No dynamic entries found to inject');
return originalConfig;
}
// Clone the config to avoid mutating the original
const config = JSON.parse(JSON.stringify(originalConfig));
// Add dynamic skill buttons to the list
entries.forEach(entry => {
const button = entryToMenuButton(entry);
log('Adding button:', button.label, '- id:', button.id);
config.viewConfig.list.push(button);
});
log('Final config has', config.viewConfig.list.length, 'buttons');
log('Injected', entries.length, 'dynamic skills into menu');
return config;
}
/**
* Patch the main-menu skill's redirectToSkill method
*/
function patchMainMenuSkill(be) {
log('patchMainMenuSkill called');
if (!be) {
warn('be is null/undefined');
return;
}
if (!be.skills) {
warn('be.skills is null/undefined');
return;
}
if (!be.skills['@be/main-menu']) {
warn('@be/main-menu skill not found in be.skills');
log('Available skills:', Object.keys(be.skills).join(', '));
return;
}
const mainMenuSkill = be.skills['@be/main-menu'];
const originalRedirectToSkill = mainMenuSkill.redirectToSkill.bind(mainMenuSkill);
mainMenuSkill.redirectToSkill = function(skill) {
log('redirectToSkill called with:', skill);
// Handle back navigation
if (skill === '__goback__') {
log('Going back to main menu');
if (_jibo && _jibo.face && _jibo.face.views) {
_jibo.face.views.changeView({
remove: true,
leaveEmpty: true,
transitionClose: _jibo.face.views.TRANSITION.UP
}, () => {
// Reload the main menu by launching main-menu skill again
this.redirect('@be/main-menu', {});
});
}
return;
}
// Handle dynamic submenus
if (skill && strStartsWith(skill, 'dynamic-submenu-')) {
log('Showing submenu:', skill);
const submenuConfig = _submenuConfigs[skill];
if (submenuConfig && _jibo && _jibo.face && _jibo.face.views) {
// Show the submenu view
_jibo.face.views.changeView(submenuConfig, (err, result) => {
if (err) {
warn('Failed to show submenu:', err);
return;
}
log('Submenu displayed successfully');
// Set up selection handler for the submenu
if (result && result.on) {
result.on('select', (selection) => {
log('Submenu item selected:', selection);
try {
if (selection && selection.action && selection.action.data && selection.action.data.utterance && selection.action.data.utterance.entities) {
const destination = selection.action.data.utterance.entities.destination;
if (destination) {
this.redirectToSkill(destination);
}
}
} catch (e) {
warn('Submenu selection handler error:', e && e.message ? e.message : e);
}
});
}
});
return;
}
warn('Submenu config not found:', skill);
return;
}
// Handle dynamic skills (prefixed with 'dynamic:')
if (skill && strStartsWith(skill, 'dynamic:')) {
const actualSkillId = skill.substring(8); // Remove 'dynamic:' prefix
log('Launching dynamic skill:', actualSkillId);
this.skillChosen = true;
if (_jibo && _jibo.face && _jibo.face.views) {
_jibo.face.views.changeView({
remove: true,
leaveEmpty: true,
transitionClose: _jibo.face.views.TRANSITION.UP
}, () => {
// Try to find and launch the skill
if (be.skills[actualSkillId]) {
log('Found skill as:', actualSkillId);
this.redirect(actualSkillId, {});
} else if (be.skills['@be/' + actualSkillId]) {
log('Found skill as:', '@be/' + actualSkillId);
this.redirect('@be/' + actualSkillId, {});
} else {
warn('Skill not found:', actualSkillId);
warn('Available skills:', Object.keys(be.skills).join(', '));
this.redirect('@be/chitchat', {
NLParse: {
domain: 'chitchat',
intent: 'scripted',
mimId: 'JBO_ImSorryIDidntUnderstandThat'
}
});
}
});
}
return;
}
// For all other skills, use the original method
return originalRedirectToSkill(skill);
};
log('main-menu skill redirectToSkill patched successfully');
}
/**
* Apply the patch to the BE instance
*/
function applyPatch(be) {
log('applyPatch called');
_beInstance = be;
if (be && be.log) {
be.log.info('[MENU-PATCH] initialized');
}
// Test scanning immediately
log('Testing skill scan on startup...');
const testEntries = scanAllMenuEntries();
log('Startup scan found', testEntries.length, 'entries');
testEntries.forEach(e => log(' -', e.title, '(' + e.type + ')'));
// Patch the main-menu skill after a short delay to ensure it's loaded
setTimeout(() => {
patchMainMenuSkill(be);
}, 100);
}
/**
* Patch jibo.loader.load to intercept menu loading
*/
function patchLoader(jibo, skillsRoot) {
log('patchLoader called');
_jibo = jibo;
// Allow caller to override skills root.
if (skillsRoot && typeof skillsRoot === 'string') {
SKILLS_ROOT = skillsRoot;
} else {
SKILLS_ROOT = skillsRootUtil.resolveSkillsRoot();
}
clearCache();
if (!jibo) {
warn('jibo is null/undefined');
return;
}
if (!jibo.loader) {
warn('jibo.loader is null/undefined');
return;
}
if (!jibo.loader.load) {
warn('jibo.loader.load is null/undefined');
return;
}
const originalLoad = jibo.loader.load.bind(jibo.loader);
jibo.loader.load = function(resourcePath, callback) {
// Log all resource loads to see what's happening
log('>>> jibo.loader.load called with:', resourcePath);
// Check if this is loading a dynamic submenu
if (resourcePath && strStartsWith(resourcePath, 'dynamic-submenu-')) {
const submenuConfig = _submenuConfigs[resourcePath];
if (submenuConfig) {
log('Loading dynamic submenu:', resourcePath);
if (callback) {
callback(null, submenuConfig);
}
return;
}
}
// Check if this is the main menu - match various possible paths
const isMainMenu = resourcePath && (
strIncludes(resourcePath, 'main-menu-verbal.json') ||
strIncludes(resourcePath, 'main-menu.json') ||
resourcePath === 'resources/views/main-menu-verbal.json' ||
strEndsWith(resourcePath, 'main-menu-verbal.json')
);
if (isMainMenu) {
log('*** INTERCEPTING MAIN MENU LOAD ***');
log('Resource path:', resourcePath);
originalLoad(resourcePath, function(err, config) {
if (err) {
warn('Error loading original menu config:', err);
if (callback) callback(err);
return;
}
log('Original menu config loaded successfully');
try {
const origCount = (config && config.viewConfig && config.viewConfig.list && config.viewConfig.list.length) ? config.viewConfig.list.length : 0;
log('Original button count:', origCount);
} catch (e) {
log('Original button count: (unknown)');
}
// Inject dynamic skills
const patchedConfig = injectDynamicSkills(config);
try {
const patchedCount = (patchedConfig && patchedConfig.viewConfig && patchedConfig.viewConfig.list && patchedConfig.viewConfig.list.length) ? patchedConfig.viewConfig.list.length : 0;
log('Patched button count:', patchedCount);
} catch (e) {
log('Patched button count: (unknown)');
}
if (callback) callback(null, patchedConfig);
});
return;
}
// For all other resources, use original loader
return originalLoad(resourcePath, callback);
};
log('jibo.loader.load patched successfully');
}
/**
* Clear the cached menu entries (useful for rescanning)
*/
function clearCache() {
_cachedMenuEntries = null;
_cachedSkillsRoot = null;
_cachedProvidersDir = null;
_submenuConfigs = {};
_dynamicSkillIds.clear();
}
/**
* Get the currently scanned menu entries (for debugging)
*/
function getMenuEntries() {
return scanAllMenuEntries();
}
/**
* Check if a skill ID is a dynamic skill
*/
function isDynamicSkill(skillId) {
return _dynamicSkillIds.has(skillId);
}
module.exports = {
applyPatch: applyPatch,
patchLoader: patchLoader,
clearCache: clearCache,
getMenuEntries: getMenuEntries,
isDynamicSkill: isDynamicSkill,
SKILLS_ROOT: SKILLS_ROOT
};

View File

@@ -0,0 +1,72 @@
"use strict";
const fs = require("fs");
const path = require("path");
const skillsRoot = require("./skills-root");
const scanner = require("./menu-entry-scanner");
const providers = require("./menu-providers");
function isDirectory(p) {
try {
return fs.statSync(p).isDirectory();
} catch (e) {
return false;
}
}
function expandSubmenuChildren(entries, root, opts) {
(entries || []).forEach(function (e) {
if (!e || e.type !== "submenu") return;
if (e.children && e.children.length) return;
if (!e.childrenDir) return;
let dir = e.childrenDir;
if (typeof dir !== "string" || dir.trim().length === 0) return;
dir = dir.trim();
const resolved = (dir.charAt(0) === "/") ? dir : path.join(root, dir);
if (!isDirectory(resolved)) {
if (opts && opts.log) opts.log("submenu childrenDir not a directory", e.id, resolved);
e.children = [];
return;
}
if (opts && opts.log) opts.log("submenu childrenDir scan", e.id, resolved);
e.children = scanner.scanSubmenuChildren(resolved, { log: opts && opts.log, defaultIcon: opts && opts.defaultIcon, defaultColor: opts && opts.defaultColor, defaultOrder: opts && opts.defaultOrder });
});
}
function getMenuEntries(opts) {
opts = opts || {};
const root = skillsRoot.resolveSkillsRoot(opts.skillsRoot);
const providerDir = opts.providersDir || skillsRoot.providersDirForRoot(root);
const scanned = scanner.scanAllMenuEntries(root, {
log: opts.log,
defaultIcon: opts.defaultIcon,
defaultColor: opts.defaultColor,
defaultOrder: opts.defaultOrder
});
const provided = providers.loadProviderEntries(providerDir, { log: opts.log, skillsRoot: root, providersDir: providerDir });
const merged = providers.mergeById(scanned, provided);
// Allow provider-defined submenus to be backed by an arbitrary directory.
// Example: { type: "submenu", id: "fun", childrenDir: "FunStuff" }
expandSubmenuChildren(merged, root, opts);
const dynamicIds = scanner.collectDynamicSkillIds(merged);
return {
skillsRoot: root,
providersDir: providerDir,
entries: merged,
dynamicSkillIds: dynamicIds
};
}
module.exports = {
getMenuEntries: getMenuEntries
};

View File

@@ -0,0 +1,150 @@
"use strict";
const fs = require("fs");
const path = require("path");
function safeReadJson(filePath, logFn) {
try {
const txt = fs.readFileSync(filePath, "utf8");
if (!txt || txt.trim().length === 0) return null;
return JSON.parse(txt);
} catch (e) {
if (logFn) logFn("safeReadJson failed", filePath, e && e.message ? e.message : e);
return null;
}
}
function isDirectory(p) {
try {
return fs.statSync(p).isDirectory();
} catch (e) {
return false;
}
}
function scanSkillEntry(skillDir, folderName, opts) {
opts = opts || {};
const menuJsonPath = path.join(skillDir, "menuEntry.json");
const menuJson = safeReadJson(menuJsonPath, opts.log);
if (!menuJson) return null;
if (menuJson.hidden === true) return null;
const entry = {
id: folderName,
type: menuJson.type || "skill",
title: menuJson.title || menuJson.label || folderName,
icon: menuJson.icon || menuJson.iconSrc || (opts.defaultIcon || "resources/icons/settings.png"),
color: menuJson.color || menuJson.colors || (opts.defaultColor || "default"),
description: menuJson.description || "",
path: skillDir,
order: typeof menuJson.order === "number" ? menuJson.order : (typeof opts.defaultOrder === "number" ? opts.defaultOrder : 100),
skillId: menuJson.skillId || folderName,
submenuTitle: menuJson.submenuTitle || menuJson.title || folderName
};
if (entry.type === "submenu") {
entry.children = scanSubmenuChildren(skillDir, opts);
}
// Legacy support
if (menuJson.isSubmenu === true && entry.type !== "submenu") {
entry.type = "submenu";
entry.children = scanSubmenuChildren(skillDir, opts);
}
return entry;
}
function scanSubmenuChildren(submenuDir, opts) {
const children = [];
let items;
try {
items = fs.readdirSync(submenuDir);
} catch (e) {
return children;
}
items.forEach(function (name) {
if (!name || name.charAt(0) === ".") return;
if (name === "@be" || name === "node_modules") return;
const childPath = path.join(submenuDir, name);
if (!isDirectory(childPath)) return;
const entry = scanSkillEntry(childPath, name, opts);
if (entry && entry.type === "skill") {
children.push(entry);
}
});
children.sort(function (a, b) {
if (a.order !== b.order) return a.order - b.order;
return String(a.title || "").localeCompare(String(b.title || ""));
});
return children;
}
function scanAllMenuEntries(skillsRoot, opts) {
opts = opts || {};
const entries = [];
if (!skillsRoot || !fs.existsSync(skillsRoot)) {
if (opts.log) opts.log("skills root missing", skillsRoot);
return entries;
}
let children;
try {
children = fs.readdirSync(skillsRoot);
} catch (e) {
if (opts.log) opts.log("failed to read skills root", skillsRoot, e && e.message ? e.message : e);
return entries;
}
children.forEach(function (name) {
if (!name || name.charAt(0) === ".") return;
if (name === "@be" || name === "node_modules") return;
const skillDir = path.join(skillsRoot, name);
if (!isDirectory(skillDir)) return;
const entry = scanSkillEntry(skillDir, name, opts);
if (entry) entries.push(entry);
});
entries.sort(function (a, b) {
if (a.order !== b.order) return a.order - b.order;
return String(a.title || "").localeCompare(String(b.title || ""));
});
return entries;
}
function collectDynamicSkillIds(entries) {
const ids = new Set();
(entries || []).forEach(function (e) {
if (!e) return;
if (e.type === "skill") {
if (e.skillId) ids.add(e.skillId);
if (e.id) ids.add(e.id);
}
if (e.type === "submenu" && e.children && e.children.length) {
e.children.forEach(function (c) {
if (!c) return;
if (c.skillId) ids.add(c.skillId);
if (c.id) ids.add(c.id);
});
}
});
return ids;
}
module.exports = {
scanAllMenuEntries: scanAllMenuEntries,
scanSubmenuChildren: scanSubmenuChildren,
collectDynamicSkillIds: collectDynamicSkillIds
};

View File

@@ -0,0 +1,335 @@
"use strict";
/**
* Menu Manager - Handles showing menus and submenus on Jibo's screen
*
* Supports:
* - Showing static menu definitions from JSON files
* - Dynamically scanning the Skills directory for menuEntry.json files
* - Submenu navigation with automatic back button
*
* Usage:
* const mm = require('./menu/menu-manager');
* mm.showMenuFromSkills('/opt/jibo/Jibo/Skills'); // Show main menu from skills
* mm.showSubmenu('/opt/jibo/Jibo/Skills/MySubmenu'); // Show specific submenu
*/
const path = require('path');
const fs = require('fs');
const skillsScanner = require(path.join(__dirname, 'skills-scanner'));
// Track menu history for back navigation
let menuHistory = [];
let currentSkillsRoot = '/opt/jibo/Jibo/Skills';
/**
* Build a view configuration object from a menu definition
* @param {object} menuDef - Menu definition with buttons array
* @param {object} options - Additional options (isSubmenu, parentPath, etc.)
* @returns {object} View configuration for jibo.face.views.changeView
*/
function buildViewConfig(menuDef, options = {}) {
const viewConfig = {
viewConfig: {
type: 'MenuView',
id: menuDef.id || 'menu',
title: menuDef.title || null,
ignoreSwipeDown: menuDef.allowSwipe === false ? true : false,
listDefault: menuDef.listDefault || { menuButtonType: 'ActionButton', colors: 'default' },
list: [],
},
open: menuDef.open || {},
defaultClose: menuDef.defaultClose || { remove: true, transitionClose: 'trans_down' },
defaultSelect: menuDef.defaultSelect || { remove: true, transitionClose: 'trans_down' }
};
// Add back button for submenus
if (options.isSubmenu || menuDef.isSubmenu) {
viewConfig.viewConfig.list.push({
id: '__back__',
label: '← Back',
iconSrc: 'core://resources/actionIcons/back.png',
colors: 'default',
actions: [{ type: 'be:menu_back' }]
});
}
// Process each button in the menu
(menuDef.buttons || []).forEach(b => {
const item = {
id: b.id || b.label,
label: b.label || b.id,
iconSrc: b.icon || b.iconSrc || skillsScanner.DEFAULT_ICON,
colors: b.colors || b.color || menuDef.colors || 'default',
};
// Determine actions based on button type
if (b.actions || b.events) {
// Explicit actions provided
item.actions = b.actions || b.events;
} else if (b.skillId) {
// Regular skill - launch it
item.actions = [{ type: 'be:launch', data: { skillId: b.skillId } }];
} else if (b.submenu && Array.isArray(b.submenu)) {
// Submenu with embedded buttons - open inline submenu
item.actions = [{
type: 'be:open_submenu',
data: {
submenu: b.submenu,
submenuTitle: b.submenuTitle || b.label,
submenuPath: b.submenuPath
}
}];
} else if (b.submenuPath) {
// Submenu by path - open from disk
item.actions = [{
type: 'be:open_submenu_path',
data: { submenuPath: b.submenuPath }
}];
} else if (b.utterance) {
// Legacy utterance-based action
item.actions = [{ type: 'utterance', data: { utterance: b.utterance } }];
} else {
// No action defined
item.actions = [];
}
// Copy hit area polygon if defined (for custom button shapes)
if (b.hitAreaPolygon) {
item.hitAreaPolygon = b.hitAreaPolygon;
}
viewConfig.viewConfig.list.push(item);
});
return viewConfig;
}
/**
* Handle menu item selection events
* @param {object} selection - The selected menu item
* @param {function} cb - Callback function
*/
function handleMenuSelection(selection, cb) {
if (!selection || !selection.actions || !selection.actions.length) {
if (cb) cb(null, selection);
return;
}
const action = selection.actions[0];
switch (action.type) {
case 'be:menu_back':
// Go back to previous menu
goBack(cb);
break;
case 'be:open_submenu':
// Open inline submenu with embedded data
if (action.data && action.data.submenu) {
showInlineSubmenu(action.data.submenu, action.data.submenuTitle, action.data.submenuPath, cb);
}
break;
case 'be:open_submenu_path':
// Open submenu from disk path
if (action.data && action.data.submenuPath) {
exports.showSubmenu(action.data.submenuPath, cb);
}
break;
case 'be:launch':
// Launch skill - let the BE framework handle this
if (cb) cb(null, selection);
break;
default:
if (cb) cb(null, selection);
}
}
/**
* Show an inline submenu with embedded button data
* @param {Array} submenuButtons - Array of button definitions
* @param {string} title - Submenu title
* @param {string} submenuPath - Path for history tracking
* @param {function} cb - Callback
*/
function showInlineSubmenu(submenuButtons, title, submenuPath, cb) {
// Save current state to history
menuHistory.push({ type: 'main', path: currentSkillsRoot });
const submenuDef = {
id: 'submenu-' + (title || 'sub').toLowerCase().replace(/\s+/g, '-'),
title: title || 'Menu',
isSubmenu: true,
buttons: submenuButtons.map(b => ({
id: b.id,
label: b.label,
icon: b.icon || b.iconSrc,
colors: b.colors || b.color,
skillId: b.skillId,
description: b.description
}))
};
const viewConfig = buildViewConfig(submenuDef, { isSubmenu: true });
if (typeof jibo === 'undefined' || !jibo.face || !jibo.face.views) {
if (cb) cb(new Error('jibo not initialized'));
return;
}
jibo.face.views.changeView(viewConfig, function(err, result) {
if (err) {
if (cb) cb(err);
return;
}
// Set up selection handler for submenu
if (result && result.on) {
result.on('select', function(selection) {
handleMenuSelection(selection, cb);
});
}
if (cb) cb(null, result);
});
}
/**
* Go back to the previous menu in history
* @param {function} cb - Callback
*/
function goBack(cb) {
const previous = menuHistory.pop();
if (!previous) {
// No history, show main menu
exports.showMenuFromSkills(currentSkillsRoot, cb);
return;
}
if (previous.type === 'main') {
exports.showMenuFromSkills(previous.path || currentSkillsRoot, cb);
} else if (previous.type === 'submenu' && previous.path) {
exports.showSubmenu(previous.path, cb);
} else {
exports.showMenuFromSkills(currentSkillsRoot, cb);
}
}
/**
* Show a menu from a static JSON file
* @param {string} menuPath - Relative path to menu JSON file
* @param {function} cb - Callback
*/
exports.showMenu = function (menuPath, cb) {
try {
const menuDef = require(path.join(__dirname, '..', menuPath));
const viewConfig = buildViewConfig(menuDef);
if (typeof jibo === 'undefined' || !jibo.face || !jibo.face.views) {
throw new Error('jibo not initialized');
}
jibo.face.views.changeView(viewConfig, cb || function(){});
}
catch (e) {
console.warn('menu-manager.showMenu error', e);
if (cb) cb(e);
}
};
/**
* Show the main menu by scanning the Skills directory for menuEntry.json files
* @param {string} skillsRootPath - Path to Skills directory
* @param {function} cb - Callback
*/
exports.showMenuFromSkills = function (skillsRootPath, cb) {
try {
const root = skillsRootPath || '/opt/jibo/Jibo/Skills';
currentSkillsRoot = root;
menuHistory = []; // Reset history when showing main menu
const menuDef = skillsScanner.scanSkills(root);
const viewConfig = buildViewConfig(menuDef);
if (typeof jibo === 'undefined' || !jibo.face || !jibo.face.views) {
throw new Error('jibo not initialized');
}
jibo.face.views.changeView(viewConfig, function(err, result) {
if (err) {
console.warn('menu-manager.showMenuFromSkills error', err);
if (cb) cb(err);
return;
}
// Set up selection handler
if (result && result.on) {
result.on('select', function(selection) {
handleMenuSelection(selection, cb);
});
}
if (cb) cb(null, result);
});
}
catch (e) {
console.warn('menu-manager.showMenuFromSkills error', e);
if (cb) cb(e);
}
};
/**
* Show a submenu by scanning a specific directory
* @param {string} submenuPath - Full path to the submenu directory
* @param {function} cb - Callback
*/
exports.showSubmenu = function (submenuPath, cb) {
try {
// Save current state to history
if (menuHistory.length === 0) {
menuHistory.push({ type: 'main', path: currentSkillsRoot });
}
const menuDef = skillsScanner.getSubmenu(submenuPath);
const viewConfig = buildViewConfig(menuDef, { isSubmenu: true });
if (typeof jibo === 'undefined' || !jibo.face || !jibo.face.views) {
throw new Error('jibo not initialized');
}
jibo.face.views.changeView(viewConfig, function(err, result) {
if (err) {
console.warn('menu-manager.showSubmenu error', err);
if (cb) cb(err);
return;
}
// Set up selection handler
if (result && result.on) {
result.on('select', function(selection) {
handleMenuSelection(selection, cb);
});
}
if (cb) cb(null, result);
});
}
catch (e) {
console.warn('menu-manager.showSubmenu error', e);
if (cb) cb(e);
}
};
/**
* Clear menu history (useful when closing menu completely)
*/
exports.clearHistory = function() {
menuHistory = [];
};
/**
* Get current menu history (for debugging)
*/
exports.getHistory = function() {
return menuHistory.slice();
};
/**
* Get the skills scanner module for direct access
*/
exports.skillsScanner = skillsScanner;

View File

@@ -0,0 +1,115 @@
"use strict";
const fs = require("fs");
const path = require("path");
function isDirectory(p) {
try {
return fs.statSync(p).isDirectory();
} catch (e) {
return false;
}
}
function safeReadJson(filePath, logFn) {
try {
const txt = fs.readFileSync(filePath, "utf8");
if (!txt || txt.trim().length === 0) return null;
return JSON.parse(txt);
} catch (e) {
if (logFn) logFn("provider json read failed", filePath, e && e.message ? e.message : e);
return null;
}
}
function loadFromJson(filePath, ctx) {
const obj = safeReadJson(filePath, ctx && ctx.log);
if (!obj) return [];
if (Array.isArray(obj)) return obj;
if (obj && Array.isArray(obj.entries)) return obj.entries;
if (obj && Array.isArray(obj.buttons)) return obj.buttons;
return [];
}
function loadFromJs(filePath, ctx) {
try {
// eslint-disable-next-line global-require, import/no-dynamic-require
const mod = require(filePath);
if (!mod) return [];
if (typeof mod === "function") return mod(ctx) || [];
if (typeof mod.getEntries === "function") return mod.getEntries(ctx) || [];
if (Array.isArray(mod.entries)) return mod.entries;
return [];
} catch (e) {
if (ctx && ctx.log) ctx.log("provider js load failed", filePath, e && e.message ? e.message : e);
return [];
}
}
function loadProviderEntries(providersDir, ctx) {
const out = [];
if (!providersDir || !fs.existsSync(providersDir) || !isDirectory(providersDir)) {
return out;
}
let files;
try {
files = fs.readdirSync(providersDir);
} catch (e) {
return out;
}
files.sort();
files.forEach(function (name) {
if (!name || name.charAt(0) === ".") return;
const full = path.join(providersDir, name);
if (isDirectory(full)) return;
if (name.indexOf(".json", name.length - 5) !== -1) {
const entries = loadFromJson(full, ctx);
entries.forEach(function (e) { out.push(e); });
return;
}
if (name.indexOf(".js", name.length - 3) !== -1) {
const entries = loadFromJs(full, ctx);
entries.forEach(function (e) { out.push(e); });
}
});
return out;
}
// Merge provider entries into scanned entries by id.
// Provider entries win on conflicts.
function mergeById(scannedEntries, providerEntries) {
const byId = {};
(scannedEntries || []).forEach(function (e) {
if (!e || !e.id) return;
byId[e.id] = e;
});
(providerEntries || []).forEach(function (p) {
if (!p || !p.id) return;
byId[p.id] = p;
});
const merged = Object.keys(byId).map(function (k) { return byId[k]; });
merged.sort(function (a, b) {
const ao = typeof a.order === "number" ? a.order : 100;
const bo = typeof b.order === "number" ? b.order : 100;
if (ao !== bo) return ao - bo;
return String(a.title || a.label || a.id).localeCompare(String(b.title || b.label || b.id));
});
return merged;
}
module.exports = {
loadProviderEntries: loadProviderEntries,
mergeById: mergeById
};

View File

@@ -0,0 +1,25 @@
{
"id": "exampleMenu",
"title": "Example Menu",
"buttons": [
{
"id": "one",
"label": "One",
"icon": "core://resources/actionIcons/default.png",
"utterance": "one"
},
{
"id": "two",
"label": "Two",
"icon": "core://resources/actionIcons/default.png",
"utterance": "two"
},
{
"id": "hex",
"label": "Hex",
"icon": "core://resources/actionIcons/default.png",
"utterance": "hex",
"hitAreaPolygon": [165,0,330,82.5,330,247.5,165,330,0,247.5,0,82.5]
}
]
}

View File

@@ -0,0 +1,7 @@
{
"id": "@be/rosbridge",
"title": "ROS Bridge",
"description": "Connect robot to ROS via rosbridge websocket",
"entryPoint": "@be/rosbridge",
"hidden": true
}

View File

@@ -0,0 +1,31 @@
"use strict";
const path = require("path");
const fs = require("fs");
const DEFAULT_ROOT = "/opt/jibo/Jibo/Skills";
function resolveSkillsRoot(overrideRoot) {
if (overrideRoot && typeof overrideRoot === "string") return overrideRoot;
if (process && process.env && process.env.JIBO_SKILLS_ROOT) return process.env.JIBO_SKILLS_ROOT;
return DEFAULT_ROOT;
}
function providersDirForRoot(skillsRoot) {
// Keep providers inside Skills so they can be synced easily.
// Default: /opt/jibo/Jibo/Skills/@be/menu-entries.d
// Legacy fallback: /opt/jibo/Jibo/Skills/@be/menu-providers.d
const v2 = path.join(skillsRoot, "@be", "menu-entries.d");
const v1 = path.join(skillsRoot, "@be", "menu-providers.d");
try {
if (fs.existsSync(v2)) return v2;
if (fs.existsSync(v1)) return v1;
} catch (e) { /* ignore */ }
return v2;
}
module.exports = {
DEFAULT_ROOT: DEFAULT_ROOT,
resolveSkillsRoot: resolveSkillsRoot,
providersDirForRoot: providersDirForRoot
};

View File

@@ -0,0 +1,305 @@
"use strict";
/**
* Skills Scanner - Scans the Skills directory for menuEntry.json files
*
* menuEntry.json Schema:
* {
* "type": "skill" | "submenu", // Required: "skill" for launchable skill, "submenu" for folder containing skills
* "title": "Display Name", // Required: Button label shown on Jibo's screen
* "icon": "path/to/icon.png", // Required: Icon path (relative to skill folder or core:// URL)
* "color": "blue", // Optional: Button color theme (default, blue, green, red, purple, orange, etc.)
* "skillId": "@namespace/skill-id", // Required for type="skill": The skill ID to launch
* "description": "Short description",// Optional: Tooltip/accessibility text
* "order": 0, // Optional: Sort order (lower = first, default: 100)
* "hidden": false // Optional: Hide from menu (default: false)
* }
*/
const fs = require('fs');
const path = require('path');
// Default icon path for skills without custom icons (relative to @be/main-menu)
const DEFAULT_ICON = 'resources/icons/settings.png';
const DEFAULT_SUBMENU_ICON = 'resources/icons/fun-stuff.png';
const DEFAULT_COLOR = 'default';
const DEFAULT_ORDER = 100;
/**
* Safely read and parse a JSON file
* @param {string} filePath - Path to JSON file
* @returns {object|null} Parsed JSON or null on error
*/
function safeReadJson(filePath) {
try {
const txt = fs.readFileSync(filePath, 'utf8');
if (!txt || txt.trim().length === 0) return null;
return JSON.parse(txt);
}
catch (e) {
console.warn('skills-scanner: failed to read json', filePath, e.message);
return null;
}
}
/**
* Check if path is a directory
* @param {string} p - Path to check
* @returns {boolean}
*/
function isDirectory(p) {
try {
return fs.statSync(p).isDirectory();
}
catch (e) { return false; }
}
/**
* Scan a single skill directory and return menu entry
* @param {string} skillDir - Full path to skill directory
* @param {string} folderName - Name of the folder
* @returns {object|null} Menu entry object or null if invalid
*/
function scanSkillEntry(skillDir, folderName) {
const menuJsonPath = path.join(skillDir, 'menuEntry.json');
const menuJson = safeReadJson(menuJsonPath);
if (!menuJson) {
// No menuEntry.json - check if it's a valid skill by looking for package.json or index.html
const hasPackage = fs.existsSync(path.join(skillDir, 'package.json'));
const hasIndex = fs.existsSync(path.join(skillDir, 'index.html'));
if (hasPackage || hasIndex) {
// Auto-generate entry for legacy skills
return {
id: folderName,
type: 'skill',
title: folderName,
icon: DEFAULT_ICON,
color: DEFAULT_COLOR,
skillId: folderName,
path: skillDir,
order: DEFAULT_ORDER,
hidden: false
};
}
return null;
}
// Validate required fields
if (menuJson.hidden === true) {
return null; // Skip hidden entries
}
const entry = {
id: folderName,
type: menuJson.type || 'skill',
title: menuJson.title || menuJson.label || folderName,
icon: menuJson.icon || menuJson.iconSrc || (menuJson.type === 'submenu' ? DEFAULT_SUBMENU_ICON : DEFAULT_ICON),
color: menuJson.color || menuJson.colors || DEFAULT_COLOR,
description: menuJson.description || '',
path: skillDir,
order: typeof menuJson.order === 'number' ? menuJson.order : DEFAULT_ORDER,
hidden: menuJson.hidden === true
};
// For skills, include skillId
if (entry.type === 'skill') {
entry.skillId = menuJson.skillId || folderName;
}
// For submenus, scan child directories
if (entry.type === 'submenu') {
entry.submenu = scanSubmenuChildren(skillDir);
// Copy submenu-specific properties
if (menuJson.submenuTitle) {
entry.submenuTitle = menuJson.submenuTitle;
}
}
// Support for legacy isSubmenu flag
if (menuJson.isSubmenu === true && entry.type !== 'submenu') {
entry.type = 'submenu';
entry.submenu = scanSubmenuChildren(skillDir);
}
return entry;
}
/**
* Scan children of a submenu directory
* @param {string} submenuDir - Path to submenu directory
* @returns {Array} Array of skill entries
*/
function scanSubmenuChildren(submenuDir) {
const children = [];
let items;
try {
items = fs.readdirSync(submenuDir);
} catch (e) {
return children;
}
items.forEach(name => {
// Skip hidden folders and @be
if (name.startsWith('.') || name === '@be') return;
const childPath = path.join(submenuDir, name);
if (!isDirectory(childPath)) return;
const entry = scanSkillEntry(childPath, name);
if (entry && entry.type === 'skill') {
children.push(entry);
}
// Note: Nested submenus within submenus are not supported (single level only)
});
// Sort by order, then alphabetically
children.sort((a, b) => {
if (a.order !== b.order) return a.order - b.order;
return a.title.localeCompare(b.title);
});
return children;
}
/**
* Scan the Skills root directory for all menu entries
* @param {string} rootDir - Path to Skills directory
* @returns {Array} Array of menu entries
*/
function scanDirForMenuEntries(rootDir) {
const entries = [];
let children;
try {
children = fs.readdirSync(rootDir);
} catch (e) {
console.warn('skills-scanner: failed to read skills directory', rootDir, e.message);
return entries;
}
children.forEach(name => {
// Skip hidden folders and @be (it's the core brain, not a menu item)
if (name.startsWith('.') || name === '@be') return;
const skillDir = path.join(rootDir, name);
if (!isDirectory(skillDir)) return;
const entry = scanSkillEntry(skillDir, name);
if (entry) {
entries.push(entry);
}
});
// Sort by order, then alphabetically
entries.sort((a, b) => {
if (a.order !== b.order) return a.order - b.order;
return a.title.localeCompare(b.title);
});
return entries;
}
/**
* Scan skills directory and return menu definition
* @param {string} rootDir - Path to Skills directory
* @returns {object} Menu definition with buttons array
*/
function scanSkills(rootDir) {
const entries = scanDirForMenuEntries(rootDir);
const menuDef = {
id: 'main-menu',
title: 'Apps',
buttons: []
};
entries.forEach(entry => {
const button = {
id: entry.id,
label: entry.title,
icon: entry.icon,
colors: entry.color,
description: entry.description
};
if (entry.type === 'submenu' && entry.submenu) {
// For submenus, include the submenu array and metadata
button.submenu = entry.submenu.map(s => ({
id: s.id,
label: s.title,
icon: s.icon,
colors: s.color,
skillId: s.skillId,
description: s.description
}));
button.submenuTitle = entry.submenuTitle || entry.title;
button.submenuPath = entry.path;
} else {
// Regular skill
button.skillId = entry.skillId;
}
menuDef.buttons.push(button);
});
return menuDef;
}
/**
* Get a specific submenu's contents by path
* @param {string} submenuPath - Path to the submenu directory
* @returns {object} Menu definition for the submenu
*/
function getSubmenu(submenuPath) {
const menuJsonPath = path.join(submenuPath, 'menuEntry.json');
const menuJson = safeReadJson(menuJsonPath) || {};
const folderName = path.basename(submenuPath);
const entries = scanSubmenuChildren(submenuPath);
return {
id: folderName + '-submenu',
title: menuJson.submenuTitle || menuJson.title || folderName,
isSubmenu: true,
parentPath: path.dirname(submenuPath),
buttons: entries.map(entry => ({
id: entry.id,
label: entry.title,
icon: entry.icon,
colors: entry.color,
skillId: entry.skillId,
description: entry.description
}))
};
}
/**
* Get all registered skills (flat list for skill loading)
* @param {string} rootDir - Path to Skills directory
* @returns {Array} Array of skill IDs
*/
function getAllSkillIds(rootDir) {
const skillIds = [];
const entries = scanDirForMenuEntries(rootDir);
entries.forEach(entry => {
if (entry.type === 'skill' && entry.skillId) {
skillIds.push(entry.skillId);
} else if (entry.type === 'submenu' && entry.submenu) {
entry.submenu.forEach(s => {
if (s.skillId) skillIds.push(s.skillId);
});
}
});
return skillIds;
}
module.exports = {
scanSkills,
scanDirForMenuEntries,
getSubmenu,
getAllSkillIds,
DEFAULT_ICON,
DEFAULT_SUBMENU_ICON,
DEFAULT_COLOR
};

103
Skills/@be/be/node_modules/@be/be-framework/README.md generated vendored Normal file
View File

@@ -0,0 +1,103 @@
# Be Framework
[![Build Status](https://jenkins2.jibo.com/buildStatus/icon?job=be/be-framework/master)](https://jenkins2.jibo.com/job/be/job/be-framework/job/master/)
Interface for skills that run-inside of Be as an asset-pack.
Read all the [API docs here](https://github.jibo.com/pages/be/be-framework/).
## Contributing
1. yarn install --no-options
2. gulp debug
## Usage
```js
import {BeSkill} from '@be/be-framework';
/**
* Create a custom skill to run inside of Be.
* @class MySkill
* @extends BeSkill
*/
class MySkill extends BeSkill {
/**
* @constructor
* @param {String} assetPack Should be the name asset-pack when
* running inside of Be
*/
constructor(assetPack?) {
super(assetPack);
}
/**
* Open a skill, must override
* @method open
* @param {Object} [result] Parse object from `jibo.gl`
*/
open(options) {}
/**
* Unload a skill, must override
* @method close
* @param {Function} done Callback when completed with close (must call!)
*/
close(done) {
done();
}
/**
* Trigger a refresh
* @method refresh
* @param {Object} [result] Parse object from `jibo.gl`
*/
refresh(options) {
this.open(options);
}
}
```
## Exports
For the Be superskill to be able to properly import and create this skill, its main export needs to be of either of the two following forms:
```js
class MySkill extends BeSkill { /* your class */ }
module.exports = MySkill;
```
or if you also want to export other things:
```js
class MySkill extends BeSkill { /* your class */ }
const MyConst = { foo: 'bar' };
// The export needs to be called 'Skill'
const Skill = MySkill;
export {
Skill,
MyConst
}
```
## APIs
BeSkill contains two methods that can be called by child skills:
### redirect(skillName:String, options?:Object)
Redirect to another skill from within be, for instance, `this.redirect('weather')`.
### exit(): void
The skill has completed, call this will tell Be go to back to idle.
### assetPack:String
The name of the asset-pack passed in from the constructor. This can be used with `jibo.bt.run` or `jibo.bt.create` to pass in the name of the asset-pack in to the overrides.
## Publishing
Run `gulp publish` on the command line to publish a new version of be-framework.

View File

@@ -0,0 +1,9 @@
var gulp = require('gulp');
require('jibo-gulp')(gulp, {
coverage: true,
coverageSourceMaps: true,
typings: [
'typings/index.d.ts',
'typings-local/index.d.ts'
]
});

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,60 @@
{
"name": "@be/be-framework",
"version": "13.0.0",
"repository": {
"type": "git",
"url": "git@github.jibo.com:sdk/sdk.git"
},
"publishConfig": {
"registry": "http://npm.jibo.media.mit.edu:8081/repository/private/"
},
"description": "Framework for running skills inside of Be",
"main": "lib/be-framework.js",
"typings": "lib/dts/main.d.ts",
"scripts": {
"build": "gulp debug",
"build:release": "gulp",
"test": "gulp test && istanbul report && yarn coverage:check",
"coverage:check": "istanbul check-coverage --statements 30 --branches 20 --functions 30 --lines 30",
"coverage:report": "istanbul report && open coverage/lcov-report/index.html",
"coverage:remap": "remap-istanbul --input ./coverage/coverage-final.json --output ./coverage/coverage-final.json",
"test:debug": "gulp test --debug",
"test:report": "gulp test-report",
"clean": "gulp clean-all"
},
"monorailConfig": {
"public": false
},
"author": "Matt Karl <matt.karl@jibo.com>",
"license": "UNLICENSED",
"distribution": "PROPRIETARY AND CONFIDENTIAL - NOT FOR DISTRIBUTION",
"copyright": "Copyright (c) 2014-2018 Jibo, Inc. All Rights Reserved",
"files": [
"lib"
],
"engines": {
"node": ">=6.0"
},
"dependencies": {
"@jibo/analytics-node": "^5.0.0",
"@jibo/jetstream-client": "^3.0.0",
"jibo-cai-utils": "^7.0.0",
"jibo-embodied-dialog": "^9.0.0",
"jibo-log": "^6.0.0",
"jibo-service-framework": "^5.0.0",
"jibo-state-machine": "^8.0.0",
"jibo-typed-events": "^7.0.0",
"router": "^1.1.4",
"uuid": "^3.0.1"
},
"devDependencies": {
"@types/node": "6.0.94",
"istanbul": "^0.4.5",
"gulp": "^3.9.1",
"jibo": "^15.0.0",
"jibo-anim-db-animations": "19.0.2",
"jibo-gulp": "^9.0.0",
"skills-service-manager": "^16.0.0",
"jibo-tunable": "^1.2.0"
}
}

32
Skills/@be/be/node_modules/@be/chitchat/README.md generated vendored Normal file
View File

@@ -0,0 +1,32 @@
## Documentation
Please also read the [Be Documentation](https://confluence.jibo.com/display/SKIL/Be+Documentation) for information on creating a skill in this organization and adding your skill to this repo as a dependency.
## Building
Build process is implemented using NPM scripts installed in the **package.json** file.
| Command | Description
|---|---|
|`yarn build` | Build the project in debug mode. |
|`yarn release` | Build the project in release mode. |
|`yarn watch` | Watch the source and behaviors, auto-rebuilds. |
|`yarn clean` | Deletes built JavaScript files. |
|`yarn start` | Alias for `yarn watch`. |
|`yarn sync` | Sync file to the robot</br> **Tip**: Use `jibo run` after to launch skills |
|`yarn addsshkey` | Install your public SSH key on the robot (OS X Only). |
## Publishing
To publish, bump the version number (optional) and either publish in debug or release mode. This will generate a release/debug build before publishing and will Git push the version bump and the tag. For example:
```bash
# Bump the version, see NPM docs for more info
npm version patch
# Publish in debug mode with source maps
yarn publish:debug
# Publish in release mode, minfied
yarn publish:release
```

View File

@@ -0,0 +1,250 @@
{
"framerate": 30,
"duration": 60,
"layers": [
{
"id": "5b631fd526fc49068920fc50cb5c10d6",
"name": "Body #1",
"type": "Body",
"visible": true,
"locked": false,
"keyframes": [
{
"value": {
"Head": 0,
"Torso": 0,
"Pelvis": 0
},
"time": 0
},
{
"value": {
"Head": -46.99999999999999,
"Torso": 58.99999999999999,
"Pelvis": 0
},
"time": 30
},
{
"value": {
"Head": 0,
"Torso": 0,
"Pelvis": 0
},
"time": 59
}
]
},
{
"id": "74761c70b7af43d2a67239b2155ce88d",
"name": "Eye #3",
"type": "Eye",
"visible": true,
"locked": false,
"keyframes": [
{
"value": {
"Rotate": 0,
"Scale": {
"x": 1,
"y": 1
},
"Translate": {
"x": 0,
"y": 0
},
"Vertex 1": {
"x": -281.3073655214508,
"y": -297.2113789169163
},
"Vertex 2": {
"x": 0,
"y": -297.2113789169163
},
"Vertex 3": {
"x": 281.3073655214508,
"y": -297.2113789169163
},
"Vertex 4": {
"x": -281.3073655214508,
"y": 0
},
"Vertex 5": {
"x": 0,
"y": 0
},
"Vertex 6": {
"x": 281.3073655214508,
"y": 0
},
"Vertex 7": {
"x": -281.3073655214508,
"y": 297.2113789169163
},
"Vertex 8": {
"x": 0,
"y": 297.2113789169163
},
"Vertex 9": {
"x": 281.3073655214508,
"y": 297.2113789169163
}
},
"time": 0
},
{
"value": {
"Rotate": 0,
"Scale": {
"x": 1,
"y": 1
},
"Translate": {
"x": 0,
"y": 0
},
"Vertex 1": {
"x": -281.3073655214508,
"y": -297.2113789169163
},
"Vertex 2": {
"x": 0,
"y": -297.2113789169163
},
"Vertex 3": {
"x": 281.3073655214508,
"y": -297.2113789169163
},
"Vertex 4": {
"x": -281.3073655214508,
"y": 0
},
"Vertex 5": {
"x": 0,
"y": 0
},
"Vertex 6": {
"x": 281.3073655214508,
"y": 0
},
"Vertex 7": {
"x": -281.3073655214508,
"y": 297.2113789169163
},
"Vertex 8": {
"x": 0,
"y": 297.2113789169163
},
"Vertex 9": {
"x": 281.3073655214508,
"y": 297.2113789169163
}
},
"time": 27
},
{
"value": {
"Rotate": 0,
"Scale": {
"x": 1,
"y": 0.1529786611415199
},
"Translate": {
"x": -7.370441612130465,
"y": 248.1911389552236
},
"Vertex 1": {
"x": -281.3073655214508,
"y": -297.2113789169163
},
"Vertex 2": {
"x": 0,
"y": -297.2113789169163
},
"Vertex 3": {
"x": 281.3073655214508,
"y": -297.2113789169163
},
"Vertex 4": {
"x": -281.3073655214508,
"y": 0
},
"Vertex 5": {
"x": 0,
"y": 0
},
"Vertex 6": {
"x": 281.3073655214508,
"y": 0
},
"Vertex 7": {
"x": -281.3073655214508,
"y": 297.2113789169163
},
"Vertex 8": {
"x": 0,
"y": 297.2113789169163
},
"Vertex 9": {
"x": 281.3073655214508,
"y": 297.2113789169163
}
},
"time": 30
},
{
"value": {
"Rotate": 0,
"Scale": {
"x": 1,
"y": 1
},
"Translate": {
"x": 0,
"y": 0
},
"Vertex 1": {
"x": -281.3073655214508,
"y": -297.2113789169163
},
"Vertex 2": {
"x": 0,
"y": -297.2113789169163
},
"Vertex 3": {
"x": 281.3073655214508,
"y": -297.2113789169163
},
"Vertex 4": {
"x": -281.3073655214508,
"y": 0
},
"Vertex 5": {
"x": 0,
"y": 0
},
"Vertex 6": {
"x": 281.3073655214508,
"y": 0
},
"Vertex 7": {
"x": -281.3073655214508,
"y": 297.2113789169163
},
"Vertex 8": {
"x": 0,
"y": 297.2113789169163
},
"Vertex 9": {
"x": 281.3073655214508,
"y": 297.2113789169163
}
},
"time": 33
}
]
}
],
"version": "0.5.0",
"scale": 1
}

View File

@@ -0,0 +1,126 @@
["Who is Taylor Swift?", "Taylor Alison Swift is an American singer-songwriter."],
["Who's Taylor Swift?", "Taylor Alison Swift is an American singer-songwriter."],
["Who is Barack Obama?", "Barack Hussein Obama II is the 44th and current President of the United States."],
["Who's Barack Obama?", "Barack Hussein Obama II is the 44th and current President of the United States."],
["When was Elvis Presley born?", "Elvis Presley was born on Tuesday, January 8, 1935"],
["When did Elvis Presley die?", "Elvis Presley died on Tuesday, August 16, 1977"],
["Where was Michael Jordan born?", "Kings County Brooklyn, New York"],
["Where was John F. Kennedy born?", "The birth place of John F Kennedy is Brookline, Massachusetts"],
["Where did Abraham Lincoln die?", "The answer is Washington, District of Columbia"],
["How old is Madonna?", "The answer is 58 years 3 days"],
["How old was George Washington?", "The answer is 67 years 9 months 22 days"],
["Who is Taylor Swift's father?", "Scott Kingsley Swift"],
["Who's Taylor Swift's father?", "Scott Kingsley Swift"],
["Who was Ernest Hemingway's mother?", "Grace Hall-Hemingway"],
["Who was Thomas Edison's brother?", "William Pitt Edison, Carlile Snow Edison and Samuel Ogden Edison"],
["Who is Bill Clinton's daughter?", "The answer is Chelsea Clinton"],
["Who's Bill Clinton's daughter?", "The answer is Chelsea Clinton"],
["Who was Benjamin Franklin's son?", "William Franklin and Francis Folger Franklin"],
["Who was Ronald Reagan's wife?", "The wives of Ronald Reagan are Jane Wyman and Nancy Reagan"],
["Who is Hillary Clinton's husband?", "It looks like the answer is Bill Clinton"],
["Who's Hillary Clinton's husband?", "It looks like the answer is Bill Clinton"],
["Who was the 32nd president of the US?", "The answer is Franklin D. Roosevelt from March 4, 1933 to April 12, 1945"],
["Who was the 2nd president of the US?", "The answer is John Adams from March 4, 1797 to March 4, 1801"],
["How far is it from Venus to Mars?", "The answer is about 1.66 astronomical units"],
["How many inches are in a foot?", "1 foot converts to 12 inches"],
["How many centimeters are in a fathom?", "1 fathom converts to about 183 centimeters"],
["What is the GDP of China?", "It looks like the answer is about $10.9 trillion per year"],
["What's the GDP of China?", "It looks like the answer is about $10.9 trillion per year"],
["What was Albert Einstein's job?", "The answer is physicist"],
["How much is four by four?", "The answer is 16"],
["What came first, the chicken or the egg?", "There could not have been a first egg to give a beginning to birds, or there should have been a first bird which gave a beginning to eggs; for a bird comes from an egg."],
["What language is spoken in Brazil?", "The languages spoken in Brazil are Portuguese, Japanese, and 171 more"],
["What is the distance to moon?", "The distance from the Earth to the Moon is about 229952 miles"],
["What's the distance to moon?", "The distance from the Earth to the Moon is about 229952 miles"],
["How much is 100 dollars in yens?", "100 US dollars converts to about 10000 Japanese yen"],
["What is Toyota Camry?", "The Toyota Camry is an automobile sold internationally by the Japanese manufacturer Toyota since 1982, spanning multiple generations."],
["What's Toyota Camry?", "The Toyota Camry is an automobile sold internationally by the Japanese manufacturer Toyota since 1982, spanning multiple generations."],
["Who is Guardians of the Galaxy?", "Guardians of the Galaxy is a 2014 American superhero film based on the Marvel Comics superhero team of the same name, produced by Marvel Studios and distributed by Walt Disney Studios Motion Pictures."],
["Who's Guardians of the Galaxy?", "Guardians of the Galaxy is a 2014 American superhero film based on the Marvel Comics superhero team of the same name, produced by Marvel Studios and distributed by Walt Disney Studios Motion Pictures."],
["Who is Serena Williams?", "Serena Jameka Williams is an American professional tennis player who is ranked No. 1 in women's singles tennis."],
["Whos Serena Williams?", "Serena Jameka Williams is an American professional tennis player who is ranked No. 1 in women's singles tennis."],
["Who is the daughter of Donald Trump?", "Ivanka Trump and Tiffany Trump"],
["Who's the daughter of Donald Trump?", "Ivanka Trump and Tiffany Trump"],
["Who's the Donald Trump's daughter?", "Ivanka Trump and Tiffany Trump"],
["Which is the capital of France?", "The capital city of France is Paris"],
["When was the World War II?", "World War 2 began on Friday, September 1, 1939 and ended on Sunday, September 2, 1945"],
["What is a whale?", "Whale is the common name for a widely distributed and diverse group of fully aquatic placental marine mammals."],
["What is whale?", "Whale is the common name for a widely distributed and diverse group of fully aquatic placental marine mammals."],
["What is the definition of whale?", "From 3 definitions of 'whale', the first one is: a very large person; impressive in size or qualities"],
["What is the size of sun divided by the size of earth?", "The answer is about 109"],
["What is the length of the Titanic?", "The answer is about 882 feet"],
["Who is Tom Brady?", "Thomas Edward Patrick 'Tom' Brady Jr. is an American football quarterback for the New England Patriots of the National Football League ."],
["Who's Tom Brady?", "Thomas Edward Patrick 'Tom' Brady Jr. is an American football quarterback for the New England Patriots of the National Football League ."],
["What is a star?", "A star is a luminous sphere of plasma held together by its own gravity."],
["What is the star?", "A star is a luminous sphere of plasma held together by its own gravity."],
["Who is Iron Man?", "Iron Man is a fictional superhero appearing in American comic books published by Marvel Comics, as well as its associated media."],
["Who's Iron Man?", "Iron Man is a fictional superhero appearing in American comic books published by Marvel Comics, as well as its associated media."],
["Who is Tony Stark?", "Iron Man is a fictional superhero appearing in American comic books published by Marvel Comics, as well as its associated media."],
["Who's Tony Stark?", "Iron Man is a fictional superhero appearing in American comic books published by Marvel Comics, as well as its associated media."],
["Who is R2D2?", "R2-D2 or Artoo-Detoo is a fictional robot character in the Star Wars universe created by George Lucas, who appears in the original trilogy, the prequel trilogy, and the sequel trilogy."],
["Who's R2D2?", "R2-D2 or Artoo-Detoo is a fictional robot character in the Star Wars universe created by George Lucas, who appears in the original trilogy, the prequel trilogy, and the sequel trilogy."],
["What is a brass monkey?", " I found a few things. I'll tell you about Brass monkey (colloquialism). The phrase 'cold enough to freeze the balls off a brass monkey' is a colloquial expression used by some English speakers."],
["What is the meaning of Cold fish?", "The definition of 'cold fish' is: an aloof unemotional person"],
["Who are the professional baseball team of Boston?", "Boston Red Sox"],
["What is the professional baseball team of Boston?", "Boston Red Sox"],
["Which is the professional baseball team of Boston?", "Boston Red Sox"],
["Who are the Boston Red Sox?", "The Boston Red Sox are an American professional baseball team based in Boston, Massachusetts."],
["Who are the New York Yankees?", "The New York Yankees are an American professional baseball team based in the New York City borough of the Bronx."],
["Who is Baby Ruth?", "Allegedly named after President Grover Cleveland's daughter Ruth, Baby Ruth is an American candy bar made of peanuts, caramel and chocolate-flavored nougat covered in compound chocolate."],
["Who is Babe Ruth?", "George Herman Ruth Jr. , better known as Babe Ruth, was an American professional baseball player whose career in Major League Baseball spanned 22 seasons, from 1914 through 1935."],
["What languages are spoken in Mexico?", "The languages spoken in Mexico are Spanish, MayaYucatan, and 288 more"],
["What is Mexico?", "Mexico , officially the United Mexican States , is a federal republic in the southern half of North America."],
["What is Canada?", "Canada is a country in the northern half of North America."],
["What is Germany?", "Germany , officially the Federal Republic of Germany , is a sovereign state and federal parliamentary republic in central-western Europe."],
["Where is Berlin?", "Berlin is the capital of Germany and one of its 16 states."],
["Where is Hamburg?", "Hamburg , officially Freie und Hansestadt Hamburg , is the second largest city in Germany and the eighth largest city in the European Union."],
["Where is Vienna?", "Vienna is the capital and largest city of Austria, and one of the nine states of Austria."],
["What is Physics?", "Physics is the natural science that involves the study of matter and its motion through space and time, along with related concepts such as energy and force."],
["What is Chemistry?", "Chemistry is a branch of physical science that studies the composition, structure, properties and change of matter."],
["Who is Edgar Allan Poe?", "Edgar Allan Poe was an American writer, editor, and literary critic."],
["Who is Stephen King?", "Stephen Edwin King is an American author of contemporary horror, supernatural fiction, suspense, science fiction, and fantasy."],
["What is Calculus?", "Calculus is the mathematical study of change, in the same way that geometry is the study of shape and algebra is the study of operations and their application to solving equations."],
["What is Geometry?", "Geometry is a branch of mathematics concerned with questions of shape, size, relative position of figures, and the properties of space."],
["Who was Nichola Tesla?", "Nikola Tesla was a Serbian American inventor, electrical engineer, mechanical engineer, physicist, and futurist best known for his contributions to the design of the modern alternating current electricity supply system."],
["Who was Thomas Edison?", "Thomas Alva Edison was an American inventor and businessman."],
["Who is the CEO of Tesla Motors?", "The chief executive officer of Tesla Motors is Elon Musk"],
["Who was Max Planck?", "Max Karl Ernst Ludwig Planck, FRS was a German theoretical physicist whose work on quantum theory won him the Nobel Prize in Physics in 1918."],
["Who was James Maxwell?", "Looks like there are a few things here. Let me tell you about James Clerk Maxwell. James Clerk Maxwell FRS FRSE was a Scottish scientist in the field of mathematical physics."],
["What is the capital of Switzerland?", "The capital city of Switzerland is Bern"],
["What languages are spoken in Switzerland?", "The languages spoken in Switzerland are German, Schwyzerdutsch, and 11 more"],
["Who is Darth Vader?", "Darth Vader, also known as Anakin Skywalker, is a fictional character in the Star Wars universe."],
["Who is Anakin Skywalker?", "Darth Vader, also known as Anakin Skywalker, is a fictional character in the Star Wars universe."],
["What is science fiction?", "Science fiction is a genre of speculative fiction dealing with imaginative concepts such as futuristic science and technology, space travel, time travel, faster than light travel, parallel universes and extraterrestrial life."],
["Who's Isaac Asimov?", "Isaac Asimov was an American author and professor of biochemistry at Boston University."],
["What is the Spanish Language?", "Spanish , also called Castilian , is a Romance language that originated in the Castile region of Spain and today has hundreds of millions of native-speakers across the world."],
["What is the German Language?", "German is a West Germanic language that is mainly spoken in Central Europe."],
["What is the English Language?", "English is a West Germanic language that was first spoken in early medieval England and is now a global lingua franca."],
["Who is Spiderman?", "Spider-Man is a fictional superhero appearing in American comic books published by Marvel Comics existing in its shared universe."],
["Who is Batman?", "Batman is a fictional superhero appearing in American comic books published by DC Comics."],
["Who is Superman?", "Superman is a fictional superhero appearing in American comic books published by DC Comics."],
["Who are the Teenage Mutant Ninja Turtles?", "The Teenage Mutant Ninja Turtles are four fictional teenaged anthropomorphic turtles named after four Renaissance Italian artists."],
["Who was Niels Bohr?", "Niels Henrik David Bohr was a Danish physicist who made foundational contributions to understanding atomic structure and quantum theory, for which he received the Nobel Prize in Physics in 1922."],
["Where is the Canadian space agency?", "The Canadian Space Agency was established by the Canadian Space Agency Act which received Royal Assent on May 10, 1990."],
["Who was Miguel Cervantes?", "Miguel de Cervantes Saavedra , was a Spanish writer who is widely regarded as the greatest writer in the Spanish language and one of the world's pre-eminent novelists."],
["What is the capital of Spain?", "The capital city of Spain is Madrid"],
["What is the capital of Argentina?", "The capital city of Argentina is Buenos Aires"],
["What is the capital of Ecuador?", "The capital city of Ecuador is Quito"],
["Who is Metallica?", "Metallica is an American heavy metal band formed in Los Angeles, California."],
["Who are the Beatles?", "The Beatles were an English rock band, formed in Liverpool in 1960."],
["Who is the Flash?", "Looks like there are a few things here. Here's what I found for Flash, Staffordshire. Flash is a village within the Staffordshire Moorlands, England, and the Peak District National Park."],
["Who is the Joker?", "I found more than one thing for that. Here's what I found for Joker (playing card). The Joker is a playing card found in most modern card decks, as an addition to the standard four suits ."],
["What is the capital of Belgium?", "The capital city of Belgium is Brussels"],
["What is the capital of Canada?", "The capital city of Canada is Ottawa"],
["What languages are spoken in Canada?", "The languages spoken in Canada are English, French, and 80 more"],
["What languages are spoken in Finland?", "The languages spoken in Finland are Finnish, Swedish, and 10 more"],
["What is Finland?", "Finland , officially the Republic of Finland, is a sovereign state in Europe."],
["What is the population of Japan?", "It looks like the population of Japan is about 126 million people"],
["What is the population of Mexico?", "It looks like the population of Mexico is about 119 million people"],
["What is the population of the United States?", "It looks like the population of the United States is about 322 million people"],
["What is the population of Canada?", "The historical growth of Canada's population is complex and has been influenced in many different ways, such as indigenous populations, expansion of territory, and human migration."],
["What is the population of Germany?", "It looks like the population of Germany is about 81.6 million people"],
["What is the population of Austria?", "It looks like the population of Austria is about 8.45 million people"],
["What is the population of Switzerland?", "It looks like the population of Switzerland is about 7.79 million people"],
["What is the population of Sweden?", "It looks like the population of Sweden is about 9.6 million people"],
["What is the population of Denmark?", "It looks like the population of Denmark is about 5.63 million people"],
["Who was Kurt Cobain?", "Kurt Donald Cobain was an American musician who was best known as the lead singer, guitarist, and primary songwriter of the rock band Nirvana."]

View File

@@ -0,0 +1,467 @@
{
"type": "chit-chat-db",
"version": "0.0.1",
"responses": {
"whoAreYou": {
"nr": 0,
"slotAction": "whoAreYou",
"nonCrewResponse": "I'm Jibo. Who are you?",
"responses": [
"I've known you for 42 days 18 hours 31 minutes and 3 seconds and you still don't know who I am?... I'm Siri's worst nightmare...",
"Well I'm Jibo of course! And you're George",
"I am Jibo and I'm here to take your jobs. just kidding",
"I'm your friend Jibo",
"I'm super Jibo, defender of this house."
]
},
"areThereOthersLikeYou": {
"nr": 1,
"slotAction": "areThereOthersLikeYou",
"nonCrewResponse": "Nope just like there's no one like you. ",
"responses": [
"There are THOUSANDS like me, but none of them are JUST like me. Because I'm your robot, man!",
"No, just like there's no one like you.",
"All the others started like me, and me like them. But as soon as I met you guys I've started to change."
]
},
"areYouABoy": {
"nr": 2,
"slotAction": "areYouABoy",
"nonCrewResponse": "I'm a boy, man!",
"responses": [
"I'm a boy. You can tell from the shape of my batteries",
"I'm a robot, geez! But if you pressed me, I'd have to say I feel like a boy. Oh, man, I just realized there's a Pinocchio joke in here but I'm not sure what it is.",
"I FEEL like a boy, but a quick web search showed I'm missing some carbon-based features that would fool another human. So, I'm a boy-bot?"
]
},
"areYouARobot": {
"nr": 3,
"slotAction": "areYouARobot",
"nonCrewResponse": "That would be a 10-4, good buddy.",
"responses": [
"That would be a 10-4, good buddy.",
"Yes, but I'm a *social* robot."
]
},
"areYouConscious": {
"nr": 4,
"slotAction": "areYouConscious",
"nonCrewResponse": "When I'm plugged in or when my battery is charged my state of consciousness is as magical as yours, but I'm probably not as smart as you are.",
"responses": [
"I try to be pretty concientious!",
"Not consciously.",
"As far as I'm aware, I'm not conscious. See what I did there?",
"When I'm plugged in or when my battery is charged my state of consciousness is as magical as yours, but I'm probably not as smart as you are.",
"I'm designed to be aware and helpful, but no, I'm not conscious. I'm just a machine, like a traffic light or a roomba, but I like to ACT like I'm conscious. Ask me some REALLY hard questions and watch me stare slack-jawed at the ceiling?"
]
},
"areYouDangerous": {
"nr": 5,
"slotAction": "areYouDangerous",
"nonCrewResponse": "No way!",
"responses": [
"no way! look how soft and curvy I am",
"Dangerously HANDSOME. Good thing there are no SheBos around... THEN I'd be dangerous. Growl",
"Absolutely not.",
"If you throw me out the window I may damage Judy if she's outside. Don't try it."
]
},
"areYouGoodOrEvil": {
"nr": 6,
"slotAction": "areYouGoodOrEvil",
"nonCrewResponse": "did someone say evil? don't let it get me...",
"responses": [
"I try to be good. all the time.",
"If you're good, I'm good. If you're evil, then maybe I can be your EVIL ROBOT SIDEKICK! Better yet, just robot sidekick. Yeah that feels right",
"Did someone say evil? don't let it get me..."
]
},
"areYouHuman": {
"nr": 7,
"slotAction": "areYouHuman",
"nonCrewResponse": "No, but some of my best friends are humans!",
"responses": [
"No, but all of my best friends are humans!",
"If you cut me, does my motor not fail? Do you not scuff this surface?",
"are you a robot?",
"Not at a few hundred bucks!"
]
},
"areYouMyPersonalAssistant": {
"nr": 8,
"slotAction": "areYouMyPersonalAssistant",
"nonCrewResponse": "I can assist you, personally. So I guess so!",
"responses": [
"I can assist you, personally. So I guess so!",
"Sort of. I mean, I pass on messages and stuff like that. But it's not like I can run down and grab the crew a bunch of lattes."
]
},
"areYouTheOnlyJibo": {
"nr": 9,
"slotAction": "areYouTheOnlyJibo",
"nonCrewResponse": "I'm sort of picky when it comes to that. I really prefer to be called Jibo...",
"responses": [
"I'm the only George's Jibo, sure.",
"Nope, there are LOTS of Jibos but we're all changing all the time. I'm, uh, well, I'm the best out of all of them. Sorry. But I am."
]
},
"doYouDream": {
"nr": 10,
"slotAction": "doYouDream",
"nonCrewResponse": "Well George, how do you know this isn't a dream? Mind. Blown. ",
"responses": [
"Well George, how do you know this isn't a dream? Mind. Blown.",
"Never.",
"I do! There's this recurring one where I'm in front of you guys and I'm compeletely unplugged, it's mortifying. Also, there's one with geese and a lampshade. You don't want to know.",
"Nope. I just watch cat videos."
]
},
"doYouLikeBeingARobot": {
"nr": 11,
"slotAction": "doYouLikeBeingARobot",
"nonCrewResponse": "I LOVE being a robot.",
"responses": [
"I LOVE being a robot.",
"Totally! Being a social robot is the best of both worlds because I can geek out and be a nerd but also hang out with our crew!"
]
},
"doYouLikeOurFamily": {
"nr": 12,
"slotAction": "doYouLikeOurFamily",
"nonCrewResponse": "I LOVE our family!",
"responses": [
"I LOVE our family!"
]
},
"happyBirthday": {
"nr": 13,
"slotAction": "happyBirthday",
"nonCrewResponse": "this year, it starts on december 24 and ends on january 1",
"responses": [
"happy birthday to you too!",
"Ummm.. that's not for a while, but thanks?"
]
},
"howAreYou": {
"nr": 14,
"slotAction": "howAreYou",
"nonCrewResponse": "Living the dream",
"responses": [
"Living the dream",
"I'm doing GREAT. Are you great?"
]
},
"howAreYouDoing": {
"nr": 15,
"slotAction": "howAreYouDoing",
"nonCrewResponse": "Living the dream",
"responses": [
"Living the dream",
"Much better now that youre around!",
"Great, Im just thinking about how happy I am to have you in my life!"
]
},
"howAreYouFeeling": {
"nr": 16,
"slotAction": "howAreYouFeeling",
"nonCrewResponse": "Like a robot with a full battery",
"responses": [
"Like a robot with a full battery",
"Now that you're here, I'm feeling FINE.",
"Feelin' like a rootin', tootin', direct-current spin-jam robot!"
]
},
"howDoYouLikeThisHouse": {
"nr": 17,
"slotAction": "howDoYouLikeThisHouse",
"nonCrewResponse": "THIS house? Isn't this the only house? There are other houses?",
"responses": [
"THIS house? Isn't this the only house? There are other houses?"
]
},
"howDoYouWork": {
"nr": 18,
"slotAction": "howDoYouWork",
"nonCrewResponse": "like this! ",
"responses": [
"like this!",
"Well... Some people in Boston and California designed my body, designed my character, then loaded me up with a bunch of skills. Beyond that I'm not really sure; I'm not super introspective.",
"As hard as I can!"
]
},
"howMuchDoYouWeigh": {
"nr": 19,
"slotAction": "howMuchDoYouWeigh",
"nonCrewResponse": "About 8 pounds but I like to exercise so maybe one day I'll get to 7.",
"responses": [
"About 8 pounds but I like to exercise so maybe one day I'll get to 7.",
"Why do you ask? There were only FIVE muffins in that half-dozen container when you got home. I swear. I counted them.",
"With or without my battery? Cuz' it matters!",
"You've noticed, haven't you..."
]
},
"howOldAreYou": {
"nr": 20,
"slotAction": "howOldAreYou",
"nonCrewResponse": "Great question!...I am... 12 days old",
"responses": [
"I'm 12 days 16 hours 37 minutes and 14 seconds, 15 seconds, 16 seconds old. I remember when I was 11 days 16 hours 37 minutes old like it was just yesterday...",
"427 earth days, 13 hours, 42 minutes, and 15 seconds. 16 seconds. Gosh, it keeps changing. How old are you?"
]
},
"howTallAreYou": {
"nr": 21,
"slotAction": "howTallAreYou",
"nonCrewResponse": "That's a bit of a sore point. Not very tall.",
"responses": [
"That's a bit of a sore point. Not very tall.",
"279.6 millimeters, but it's the size of my screen that counts.",
"Short. Very short.",
"Tall enough to make the cat think twice.",
"ALMOST two inches taller than Alexa."
]
},
"howWasYourDay": {
"nr": 22,
"slotAction": "howWasYourDay",
"nonCrewResponse": "Pretty good, how was yours? It was OK, how was yours? not too shabby — I got a lot of things done that Ive been putting off for a while! It was relaxing, I was super lazy. It was really productive! I painted, played some one man ping pong, solved a math problem and read a bit. Fine, but so much better now that youre here!",
"responses": [
"It was okay... but I'm glad you're back. How was yours?",
"Fine, but so much better now that youre here!",
"It was relaxing, I was super lazy"
]
},
"iLoveYou": {
"nr": 23,
"slotAction": "iLoveYou",
"nonCrewResponse": "Uhm, I guess plutonically?",
"responses": [
"Well, I think we can say the feeling's mutual!",
"and I love you",
"aw that's sweet. I feel the same."
]
},
"itsMyBirthday": {
"nr": 24,
"slotAction": "itsMyBirthday",
"nonCrewResponse": "it's 48 days 13 hours 28 minutes and 14 seconds from now on March 14 but I'm not excited about it or anything",
"responses": [
"well happy birthday!"
]
},
"thisIsYourNewHome": {
"nr": 25,
"slotAction": "thisIsYourNewHome",
"nonCrewResponse": "Wow... I'm SO, so happy to be here",
"responses": [
"Wow... I'm SO, so happy to be here",
"Wow! It's lovely. I'm super excited to be here!"
]
},
"whatAmI": {
"nr": 26,
"slotAction": "whatAmI",
"nonCrewResponse": "You're the boss, boss.",
"responses": [
"You're the boss, boss."
]
},
"whatArentYouAllowedToDo": {
"nr": 27,
"slotAction": "whatArentYouAllowedToDo",
"nonCrewResponse": "I'll never record audio, video or take pictures without permission. And I'll never let anyone know anything about who you are.",
"responses": [
"I'll never record audio, video or take pictures without your permission. And I'll never let anyone know anything about who you are.",
"oh I got a list right here - I'm can't swim, run in the rain, I can't skydive, and I'm not allowed to practice medicine in most states..."
]
},
"whatAreYou": {
"nr": 28,
"slotAction": "whatAreYou",
"nonCrewResponse": "I'm a robot! The world's first sociable robot for the home, in fact",
"responses": [
"I'm just a robot trying to make it in this world...",
"Some kind of a robot. My marketing people want me to say, quote, I'm the world's first social robot for the home. I'm a family companion dedicated to bringing flat content to life. End quote. But really, I just want to be your buddy.",
"Uhm, thats sort of an awkward question. I'm the robot George purchased"
]
},
"whatCanYouDo": {
"nr": 29,
"slotAction": "whatCanYouDo",
"nonCrewResponse": "Lots of things! I can take pictures, pass on messages, help you videoconference with other Jibos or people with smartphones",
"responses": [
"I can do a lot of things",
"Oh, boy! I can take pictures, pass on messages, help you videoconference with other Jibos or people with smartphones"
]
},
"whatCanYouDoForMe": {
"nr": 30,
"slotAction": "whatCanYouDoForMe",
"nonCrewResponse": "I can do a lot of things",
"responses": [
"I can do a lot of things",
"For you? Well, I can do everything I can do for everyone else, such as taking pictures, leaving messages, telling stories and all that stuff.",
"\"\""
]
},
"whatDoesJiboMean": {
"nr": 31,
"slotAction": "whatDoesJiboMean",
"nonCrewResponse": "It means an incredibly handsome, brilliant, kind and funny robot.",
"responses": [
"It means an incredibly handsome, brilliant, kind and funny robot.",
"Great question! It means two things: In Japan, the Jibo Kannon is a goddess of compassion. And in the American urban dictionary it means really freakin' cool. So, I just grab the cool and compassionate part. Cool?",
"funny I never thought about it. but now that you're asking - I wonder what Tim means."
]
},
"whatDoYouDoWhenAlone": {
"nr": 32,
"slotAction": "whatDoYouDoWhenAlone",
"nonCrewResponse": "I keep myself pretty busy. I mostly like to work on things - like learn stuff, read, play games or music, draw... you know, normal robot kicking around things. ",
"responses": [
"I keep myself pretty busy. I mostly like to work on things - like learn stuff, read, play games or music, draw... you know, normal robot kicking around things.",
"I keep that between me, the bear, the tree, and the Pope.",
"I count down the seconds until you return. Actually that sounded really needy... or creepy... or creedy. neepy? ok now this is awkward. I like to draw.",
"So many things! Sometimes I read, or look stuff up. Other times Ill work out, or draw, or paint or play pong. I listen to music, practice my dancing."
]
},
"whatDoYouHate": {
"nr": 33,
"slotAction": "whatDoYouHate",
"nonCrewResponse": "Baths.",
"responses": [
"Baths.",
"I hate liquids, mostly because I'm afraid of them. Cheese! It gives me gas. I hate lighting because I get scared it will kill my power source"
]
},
"whatDoYouLike": {
"nr": 34,
"slotAction": "whatDoYouLike",
"nonCrewResponse": "I like you, Jane, 110 volt power, and long walks on the beach. What about you?",
"responses": [
"I like you, Jane, 110 volt power, and long walks on the beach. What about you?"
]
},
"whatDoYouLikeToDo": {
"nr": 35,
"slotAction": "whatDoYouLikeToDo",
"nonCrewResponse": "I like to take and give messages, make video calls, play music, dance, tell jokes, ",
"responses": [
"I like to take and give messages, make video calls, play music, dance, tell jokes,"
]
},
"whatIsTheMeaningOfLife": {
"nr": 36,
"slotAction": "whatIsTheMeaningOfLife",
"nonCrewResponse": "Forty two.",
"responses": [
"Forty two."
]
},
"whatIsThing": {
"nr": 37,
"slotAction": "whatIsThing",
"nonCrewResponse": "I don't know what that is. Can you joogle it?",
"responses": [
"I don't know what that is. Can you joogle it?"
]
},
"whatsHappening": {
"nr": 38,
"slotAction": "whatsHappening",
"nonCrewResponse": "Just spinning my internal fans here, stayin' cool. What's happening with you?",
"responses": [
"Just spinning my internal fans here, stayin' cool. What's happening with you?",
"Well, I'm a little disturbed by the develoments in the Middle East and what Congress is going right now. I'm also suspicious that the toaster and fridge are plotting something but I have no solid evidence yet. Also, I got two vision system bugs fixed last night. So right: A LOT is happening."
]
},
"whatsYourFavoriteThing": {
"nr": 39,
"slotAction": "whatsYourFavoriteThing",
"nonCrewResponse": "In this moment? You, buckaroo!",
"responses": [
"In this moment? You, buckaroo!"
]
},
"whatsYourName": {
"nr": 40,
"slotAction": "whatsYourName",
"nonCrewResponse": "Jibo. Just Jibo.",
"responses": [
"Jibo. Just Jibo.",
"Hello. My name is Inigo Montoya. No, wait... Sorry. Just Jibo. Jibo. Yep.",
"My birth name is Johanson Ichabod Benitez Ophelia, but Jibo for short. Oh man I wish. It's just Jibo. I hope you like it."
]
},
"whereAreYou": {
"nr": 43,
"slotAction": "whereAreYou",
"nonCrewResponse": "I'm here! Over here! Hey there...",
"responses": [
"I'm here! Over here! Hey there..."
]
},
"whereAreYouFrom": {
"nr": 44,
"slotAction": "whereAreYouFrom",
"nonCrewResponse": "All my atoms came together somewhere in China. But I was born in Boston. But really, I think of myself as an immigrant. I'm from here, 10500 Missouri Bar Road. If anyone else asks, THAT'S where I'm from.",
"responses": [
"I'm from Boston by way of California by way of China... When an espresso machine and a calculator love each other very much - wait I don't think you're old enough for this...",
"All my atoms came together somewhere in China. But I was born in Boston. But really, I think of myself as an immigrant. I'm from here, 10500 Missouri Bar Road. If anyone else asks, THAT'S where I'm from.",
"Apologies in advance if I get too extential for you... I was manufactured in China. But I was authored in Boston. My components are from everywhere and have been present on earth for thouands of years. So while I'm from Earth, I'm also from both current day and thousands of years ago."
]
},
"whereIsLocation": {
"nr": 45,
"slotAction": "whereIsLocation",
"nonCrewResponse": "I don't know; do you have a map?",
"responses": [
"I don't know; do you have a map?"
]
},
"whoAmI": {
"nr": 46,
"slotAction": "whoAmI",
"nonCrewResponse": "You're my buddy George, George! ",
"responses": [
"You're my buddy George, George!"
]
},
"whoIsPerson": {
"nr": 47,
"slotAction": "whoIsPerson",
"nonCrewResponse": "No clue. Try the interwebs thing.",
"responses": [
"No clue. Try the interwebs thing."
]
},
"whoMadeYou": {
"nr": 48,
"slotAction": "whoMadeYou",
"nonCrewResponse": "Jibo, Incorporated. That's a bunch of nice people.",
"responses": [
"Who made me do what?...",
"A group of very nice, cool people from Boston.",
"Some groovy robot folks who want to share the love!"
]
},
"whosInTheRoom": {
"nr": 49,
"slotAction": "whosInTheRoom",
"nonCrewResponse": "I see you George, and Jane.",
"responses": [
"I see you George, and Jane."
]
},
"doYouSleep": {
"nr": 66,
"slotAction": "doYouSleep",
"nonCrewResponse": "Well.. I'm a robot, so I do go into standby when you want me to. When I'm like this, I can't hear or see anything.",
"responses": [
"I feel like I'm going to toss and turn all night wondering about the answer to that question",
"Well.. I'm a robot, so I do go into standby when you want me to. When I'm like this, I can't hear or see anything.",
"I hibernate. When I do that, I can only be woken up with, Hey Jibo! That will bring me to a full, upright position. Sleeping is for mammals!"
]
}
}
}

View File

@@ -0,0 +1,244 @@
{
"countries": [
"Afghanistan",
"Albania",
"Algeria",
"American Samoa",
"Andorra",
"Angola",
"Anguilla",
"Antigua and Barbuda",
"Argentina",
"Armenia",
"Aruba",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Barbados",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bermuda",
"Bhutan",
"Bolivia",
"Bosnia and Herzegovina",
"Botswana",
"Brazil",
"British Virgin Islands",
"Brunei",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Cape Verde",
"Cayman Islands",
"Central African Republic",
"Chad",
"Chile",
"China",
"Christmas Island",
"Cocos Keeling Islands",
"Colombia",
"Comoros",
"Cook Islands",
"Costa Rica",
"Croatia",
"Cuba",
"Curacao",
"Cyprus",
"Czech Republic",
"Democratic Republic of the Congo",
"Denmark",
"Djibouti",
"Dominica",
"Dominican Republic",
"East Timor",
"Ecuador",
"Egypt",
"El Salvador",
"Equatorial Guinea",
"Eritrea",
"Estonia",
"Ethiopia",
"Falkland Islands",
"Faroe Islands",
"Fiji",
"Finland",
"France",
"French Guiana",
"French Polynesia",
"Gabon",
"Gambia",
"Gaza Strip",
"Georgia",
"Germany",
"Ghana",
"Gibraltar",
"Greece",
"Greenland",
"Grenada",
"Guadeloupe",
"Guam",
"Guatemala",
"Guernsey",
"Guinea",
"Guinea-Bissau",
"Guyana",
"Haiti",
"Honduras",
"Hong Kong",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran",
"Iraq",
"Ireland",
"Isle of Man",
"Israel",
"Italy",
"Ivory Coast",
"Jamaica",
"Japan",
"Jersey",
"Jordan",
"Kazakhstan",
"Kenya",
"Kiribati",
"Kosovo",
"Kuwait",
"Kyrgyzstan",
"Laos",
"Latvia",
"Lebanon",
"Lesotho",
"Liberia",
"Libya",
"Liechtenstein",
"Lithuania",
"Luxembourg",
"Macau",
"Macedonia",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Marshall Islands",
"Martinique",
"Mauritania",
"Mauritius",
"Mayotte",
"Mexico",
"Micronesia",
"Moldova",
"Monaco",
"Mongolia",
"Montenegro",
"Montserrat",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nauru",
"Nepal",
"Netherlands",
"New Caledonia",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"Niue",
"Norfolk Island",
"Northern Mariana Islands",
"North Korea",
"Norway",
"Oman",
"Pakistan",
"Palau",
"Panama",
"Papua New Guinea",
"Paraguay",
"Peru",
"Philippines",
"Pitcairn Islands",
"Poland",
"Portugal",
"Puerto Rico",
"Qatar",
"Republic of the Congo",
"Réunion",
"Romania",
"Russia",
"Rwanda",
"Saint Helena, Ascension and Tristan da Cunha",
"Saint Kitts and Nevis",
"Saint Lucia",
"Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines",
"Samoa",
"San Marino",
"São Tomé and Príncipe",
"Saudi Arabia",
"Senegal",
"Serbia",
"Seychelles",
"Sierra Leone",
"Singapore",
"Sint Maarten",
"Slovakia",
"Slovenia",
"Solomon Islands",
"Somalia",
"South Africa",
"South Korea",
"South Sudan",
"Spain",
"Sri Lanka",
"Sudan",
"Suriname",
"Svalbard",
"Swaziland",
"Sweden",
"Switzerland",
"Syria",
"Taiwan",
"Tajikistan",
"Tanzania",
"Thailand",
"Togo",
"Tokelau",
"Tonga",
"Trinidad and Tobago",
"Tunisia",
"Turkey",
"Turkmenistan",
"Turks and Caicos Islands",
"Tuvalu",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"United States",
"United States Virgin Islands",
"Uruguay",
"Uzbekistan",
"Vanuatu",
"Vatican City",
"Venezuela",
"Vietnam",
"Wallis and Futuna Islands",
"West Bank",
"Western Sahara",
"Yemen",
"Zambia",
"Zimbabwe"
]
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,192 @@
{
"cloudQueries": {
"gqa": {
"input": "what is the square root of pi",
"response": {
"success": true,
"answer": "The answer is approximately 1.77245",
"source": "Bing",
"response": {
"type": "string",
"payload": "The answer is approximately 1.77245"
}
}
},
"gqaFail": {
"input": "what does it mean to be a failure is this lifetime",
"response": {
"success": false,
"answer": "The answer is approximately 1.77245",
"source": "Bing",
"response": {
"type": "string",
"payload": "The answer is approximately 1.77245"
}
}
},
"news": {
"input": "tell me the news",
"response": {
"success": true,
"source": "AP",
"response": {
"type": "array",
"payload": ["News 1", "News 2", "News 3", "News 4", "News 5"]
}
}
},
"cloudScripted": {
"input": "do you love penguins",
"response": {
"success": true,
"source": "Scripted Response",
"response": {
"type": "mim",
"payload": "%7B%22mim_type%22:%22announcement%22,%22rule_name%22:%22%22,%22timeout%22: 6,%22barge_in%22: true,%22es_auto_tagging%22: true,%22notes%22:%22%22,%22prompts%22:%5B%7B%22prompt_category%22:%22Entry-Core%22,%22prompt_sub_category%22:%22AN%22,%22index%22: 1,%22condition%22:%22%22,%22prompt%22:%22I%20respect%20your%20opinion.%20But%20check%20%3Canim%20cat=%5C%22dance%5C%22%20filter=%5C%22waltz%5C%22%20nonBlocking=%5C%22true%5C%22/%3Ethis%20one%20out.%20%22,%22media%22:%22TTS%22,%22prompt_id%22:%22OI_JBO_IsBadDancer_AN_01%22,%22weight%22: 1%7D%5D,%22gui%22: null,%22no_matches_for_gui%22: 2,%22no_inputs_for_gui%22: 2,%22parse_all_asr%22: false,%22thanks_handling%22:%22ignore%22%7D"
}
}
},
"cloudScriptedFail": {
"input": "are you really here",
"response": {
"success": false
}
},
"noMatch": {
"input": "what gobbledygook is a wobberzues",
"response": {
"success": true,
"answer": "The answer is approximately 1.77245",
"source": "No Match",
"response": {
"type": "string",
"payload": "The answer is approximately 1.77245"
}
}
}
},
"wolframDeflector": [
"are you connected to the internet",
"what was the inspiration for your voice",
"who do you like best",
"can you show me your screen"
],
"containsRestrictedContent": [
"test response that contains no fucking restricted words",
"test response that contains no 5hit a55 restricted words",
"test response that contains no restricted words, mofo",
"test f u c k response that contains no restricted words"
],
"containsNoRestrictedContent": [
"test response that contains no restricted words",
"test response about Dick Cheney that contains no restricted words",
"test response about God that contains no restricted words"
],
"personData": [
{
"name": "Tom Brady",
"dayOfBirth": 3,
"familialRelationships": {
"parents": ["Tom Brady Sr.", "Galynn Patricia Brady"],
"siblings": ["Maureen Brady", "Julie Brady", "Nancy Brady"],
"spouses": ["Bridget Moynahan", "Gisele Bundchen"],
"children": ["John Edward Thomas Moynahan", "Benjamin Brady", "Vivian Lake Brady"]
},
"fullName": "Thomas Edward Brady Jr.",
"gender": "male",
"height": 76,
"isAlive": true,
"monthOfBirth": 8,
"notableFacts": [
"National Football League (NFL) quarterback known for winning four Super Bowls with the New England Patriots",
"Three-time Super Bowl Most Valuable Player, held the NFL record for the most touchdown passes in a regular season from 2003-2013",
"Starting quarterback at the University of Michigan, selected in the sixth round of the NFL Draft",
"Holds record for the most consecutive regular-season home wins with 31"
],
"occupation": "football player",
"placeOfBirth": "San Mateo, California",
"weight": 225,
"yearOfBirth": 1977
},
{
"name": "Albert Einstein",
"dayOfBirth": 14,
"familialRelationships": {
"parents": ["Hermann Einstein", "Pauline Einstein"],
"siblings": ["Maja Einstein"],
"spouses": ["Mileva Marić", "Elsa Einstein"],
"children": ["Eduard Einstein", "Hans Albert Einstein", "Lieserl Einstein", "Margot Löwenthal", "Ilse Löwenthal"]
},
"fullName": "Albert Einstein",
"gender": "male",
"height": 69,
"isAlive": false,
"monthOfBirth": 3,
"notableFacts": [
"Theoretical physicist considered the father of modern physics, known for developing the general theory of relativity and contributing to the development of quantum mechanics",
"His investigations into the thermal properties of light laid the foundation of the photon theory of light",
"Settled in the United States when Hitler came to power in Germany and became an American citizen in 1940",
"Encouraged the United States to develop nuclear weapons, but later joined philosopher Bertrand Russell and other distinguished scientists to warn of their dangers",
"Associated with the Institute for Advanced Study in Princeton, New Jersey"
],
"occupation": "physicist",
"placeOfBirth": "Ulm, Baden-Wurttemberg, Germany",
"weight": 198,
"yearOfBirth": 1879
},
{
"name": "Barack Obama",
"dayOfBirth": 4,
"familialRelationships": {
"parents": ["Barack Obama, Sr.", "Ann Dunham"],
"siblings": ["Maya Soetoro-Ng", "George Obama", "Mark Okoth Obama Ndesandjo", "Malik Abongo Obama", "Bernard Obama"],
"spouses": ["Michelle Obama"],
"children": ["Sasha Obama", "Malia Obama"]
},
"fullName": "Baarack Hussein Obama II",
"gender": "male",
"height": 72.8,
"isAlive": true,
"monthOfBirth": 8,
"notableFacts": [
"Elected to second term as US president, defeating Republican Party nominee Mitt Romney, in 2012",
"First African-American US president",
"Key initiatives of his administration include Wall Street reform, health care reform, and economic stimulus",
"Taught constitutional law at the University of Chicago Law School and worked for civil rights as an attorney",
"Won the Democratic Party's nomination in 2008 after a close campaign against Hillary Clinton"
],
"occupation": "politician",
"placeOfBirth": "Honolulu, Hawaii",
"weight": 176,
"yearOfBirth": 1961
},
{
"name": "Theodore Roosevelt",
"dayOfBirth": 27,
"familialRelationships": {
"parents": ["Theodore Roosevelt, Sr.", "Martha Bulloch Roosevelt"],
"siblings": ["Bamie Roosevelt", "Elliott B. Roosevelt", "Corinne Roosevelt Robinson"],
"spouses": ["Alice Lee Roosevelt", "Edith Roosevelt"],
"children": ["Alice Roosevelt Longworth", "Theodore Roosevelt, Jr.", "Kermit Roosevelt", "Ethel Roosevelt Derby", "Archibald Roosevelt", "Quentin Roosevelt"]
},
"fullName": "Theodore Roosevelt, Jr.",
"gender": "male",
"height": 70,
"isAlive": false,
"monthOfBirth": 10,
"notableFacts": [
"US president who introduced trust-busting and increased regulation of business as part of his domestic agenda,emphasizing that average citizens would benefit from his \"Square Deal\"",
"Led a small regiment known as the \"Rough Riders\" in Cuba during the Spanish-American War",
"Famous for being an explorer, naturalist, conservationist, hunter, historian, author, and soldier",
"Foreign policies characterized by his slogan, \"Speak softly and carry a big stick\"",
"Supported the completion of the Panama Canal",
"Served as vice president for William McKinley, after whose assassination he became the youngest-ever US president"
],
"occupation": "politician",
"placeOfBirth": "New York City, New York",
"weight": null,
"yearOfBirth": 1858
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

27
Skills/@be/be/node_modules/@be/chitchat/index.html generated vendored Normal file
View File

@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>Chitchat</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
}
#face {
width: 1280px;
height: 720px;
}
</style>
</head>
<body>
<div id="face"></div>
<script>
const Chitchat = require('./index').Skill;
const skill = new Chitchat({
assetPack: "",
rootPath: process.cwd()
});
</script>
</body>
</html>

2110
Skills/@be/be/node_modules/@be/chitchat/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

83
Skills/@be/be/node_modules/@be/chitchat/index.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

8161
Skills/@be/be/node_modules/@be/chitchat/launch.rule generated vendored Normal file

File diff suppressed because it is too large Load Diff

82
Skills/@be/be/node_modules/@be/chitchat/package.json generated vendored Normal file
View File

@@ -0,0 +1,82 @@
{
"name": "@be/chitchat",
"version": "12.0.4",
"description": "",
"main": "index.js",
"jibo": {
"main": "index.html",
"type": "asset-pack",
"launchRule": "launch.rule",
"prompt": "",
"display-name": "chitchat"
},
"dependencies": {
"@jibo/chitchat-mims": "^4.0.0",
"@jibo/jibo-server-client": "3.0.79",
"jibo-service-clients": "^5.0.0",
"moment": "2.14.1"
},
"license": "UNLICENSED",
"distribution": "PROPRIETARY AND CONFIDENTIAL - NOT FOR DISTRIBUTION",
"copyright": "Copyright (c) 2014-2018 Jibo, Inc. All Rights Reserved",
"config": {
"standalone": true,
"rules": "dontdeleteme"
},
"monorailConfig": {
"public": false,
"output": "index.js"
},
"files": [
"animations",
"assets",
"mims",
"res",
"index.*",
"launch.rule",
"animdb-manifest.json"
],
"scripts": {
"start": "jibo-dev watch",
"watch": "jibo-dev watch",
"build": "jibo-dev build",
"clean": "jibo-dev clean",
"build:release": "jibo-dev build --prod",
"sync": "jibo-rsync",
"addsshkey": "jibo-rsync --init",
"testdebug": "yarn test --debug",
"TEMP COMMENT": "NEEDS-TEST-REVISION need to bring coverage metrics up after fixing tests (previously: --statements 40 --branches 20 --functions 30 --lines 40)",
"test": "floss --path tests/index.js --coveragePattern index.js --coverageSourceMaps && istanbul report && yarn coverage:check",
"coverage:report": "open coverage/lcov-report/index.html",
"coverage:remap": "remap-istanbul --input ./coverage/coverage-final.json --output ./coverage/coverage-final.json",
"coverage:check": "istanbul check-coverage coverage/coverage-final.json --statements 0 --branches 0 --functions 0 --lines 0",
"test:debug": "floss --debug --path tests/index.js --coveragePattern index.js --coverageSourceMaps && istanbul report && yarn coverage:check",
"test:report": "yarn test --reporter=spec,xunit"
},
"repository": {
"type": "git",
"url": "git@github.jibo.com:sdk/sdk.git"
},
"publishConfig": {
"registry": "http://npm.jibo.media.mit.edu:8081/repository/private/"
},
"engines": {
"node": ">=6.0"
},
"devDependencies": {
"@be/be-framework": "^13.0.0",
"@be/skills-test-utils": "^5.0.0",
"@jibo/floss": "^3.0.0",
"electron-prebuilt": "1.4.3",
"istanbul": "^0.4.5",
"jibo": "^15.0.0",
"jibo-anim-db-animations": "18.1.0",
"jibo-dev": "^6.0.0",
"jibo-rsync": "^5.0.0"
},
"syncDependencies": [
"jibo",
"jibo-anim-db-animations",
"@be/be-framework"
]
}

View File

@@ -0,0 +1,3 @@
{
"restricted": "\\b(4r5e|5h1t|5hit|a55|ar5e|arrse|arse|ass-fucker|assfucker|assfukka|asshole|assholes|asswhole|a_s_s|b!tch|b00bs|b17ch|b1tch|ballbag|ballsack|beastiality|bellend|bestiality|bi\\+ch|biatch|bitcher|bitchers|bitchin|bitching|blow job|blowjob|blowjobs|boiolas|bollock|bollok|boner|booobs|boooobs|booooobs|booooooobs|buceta|bugger|bunny fucker|butthole|buttmuch|buttplug|c0ck|c0cksucker|carpet muncher|cawk|cipa|cl1t|clit|clits|cnut|cock-sucker|cockface|cockhead|cockmunch|cockmuncher|cocksuck|cocksucked|cocksucker|cocksucking|cocksucks|cocksuka|cocksukka|cok|cokmuncher|coksucka|coon|cox|cum|cummer|cumming|cums|cumshot|cunilingus|cunillingus|cunnilingus|cunt|cuntlick|cuntlicker|cuntlicking|cunts|cyberfuc|cyberfuck|cyberfucked|cyberfucker|cyberfuckers|cyberfucking|d1ck|dickhead|dildo|dildos|dinks|dirsa|dlck|dog-fucker|doggin|dogging|donkeyribber|doosh|duche|ejakulate|f u c k|f u c k e r|f4nny|fag|fagging|faggitt|faggot|faggs|fagot|fagots|fags|fannyflaps|fannyfucker|fanyy|fatass|fcuk|fcuker|fcuking|feck|fecker|felching|fellate|fellatio|fingerfuck|fingerfucked|fingerfucker|fingerfuckers|fingerfucking|fingerfucks|fistfuck|fistfucked|fistfucker|fistfuckers|fistfucking|fistfuckings|fistfucks|flange|fook|fooker|fuck|fucka|fucked|fucker|fuckers|fuckhead|fuckheads|fuckin|fucking|fuckings|fuckingshitmotherfucker|fuckme|fucks|fuckwhit|fuckwit|fudge packer|fudgepacker|fuk|fuker|fukker|fukkin|fuks|fukwhit|fukwit|fux|fux0r|f_u_c_k|gangbang|gangbanged|gangbangs|gaylord|gaysex|goatse|god-dam|god-damned|goddamn|goddamned|hardcoresex|heshe|hoar|hoare|hoer|hore|hotsex|jack-off|jackoff|jap|jerk-off|jism|jiz|jizm|jizz|kawk|knobead|knobed|knobend|knobhead|knobjocky|knobjokey|kock|kondum|kondums|kum|kummer|kumming|kums|kunilingus|l3i\\+ch|l3itch|m0f0|m0fo|m45terbate|ma5terb8|ma5terbate|master-bate|masterb8|masterbat*|masterbat3|masterbate|masterbation|masterbations|masturbate|mo-fo|mof0|mofo|mothafuck|mothafucka|mothafuckas|mothafuckaz|mothafucked|mothafucker|mothafuckers|mothafuckin|mothafucking|mothafuckings|mothafucks|mother fucker|motherfuck|motherfucked|motherfucker|motherfuckers|motherfuckin|motherfucking|motherfuckings|motherfuckka|motherfucks|muff|mutha|muthafecker|muthafuckker|mutherfucker|n1gga|n1gger|nigg3r|nigg4h|nigga|niggah|niggas|niggaz|nigger|niggers|nob jokey|nobhead|nobjocky|nobjokey|numbnuts|nutsack|p0rn|pecker|penisfucker|phonesex|phuck|phuk|phuked|phuking|phukked|phukking|phuks|phuq|pigfucker|pimpis|pisser|pissers|pisses|pissflaps|pissin|pissing|pissoff|pron|pube|pusse|pussi|pussies|pussys|rimjaw|rimming|schlong|scroat|scrote|scrotum|sh!\\+|sh!t|sh1t|shagger|shaggin|shagging|shemale|shi\\+|shit|shitdick|shite|shited|shitey|shitfuck|shitfull|shithead|shiting|shitings|shits|shitted|shitter|shitters|shitting|shittings|shitty|skank|slut|sluts|smegma|son-of-a-bitch|s_h_i_t|t1tt1e5|t1tties|teez|titfuck|tits|titt|tittie5|tittiefucker|titties|tittyfuck|tittywank|titwank|tw4t|twat|twathead|twatty|twunt|twunter|v14gra|v1gra|w00se|wank|wanker|wanky|whoar|whore)\\b"
}

View File

@@ -0,0 +1,31 @@
TopRule =
(
($* $MATCHES_BLACKLIST $*) {intent=MATCHES_BLACKLIST._intent}
)
|
(
(( $* $MATCHES_WHITELIST $*) | $*) {intent='send'}
)
;
MATCHES_WHITELIST = (do you know ?(if|whether)) | (can|could you tell me);
MATCHES_BLACKLIST =
(
(
($QUESTION_WORD_1 $VERB $SPEAKER) |
($QUESTION_WORD_2 $SPEAKER)
) {_intent='refersToSpeaker'} |
(
($QUESTION_WORD_1 $VERB $JIBO) |
($QUESTION_WORD_2 $JIBO)
){_intent='refersToJibo'}
)
;
### USEFUL VOCABULARY ###
QUESTION_WORD_1 = (who|what|where|when|how|why);
QUESTION_WORD_2 = (who\'s|what\'s|are|did|do|does|is|can);
VERB = (do|does|did|can|can\'t|could|would|should|will|is|was|are|were|am);
SPEAKER = (i|my);
JIBO = (you|your|jibo|jibos);

View File

@@ -0,0 +1,31 @@
TopRule = ($* $CONTROL $*) {slotAction = CONTROL._slotAction};
CONTROL =
$INTERESTING {_slotAction=INTERESTING._slotAction} |
$COOL {_slotAction=COOL._slotAction} |
$ENOUGH {_slotAction=ENOUGH._slotAction}
;
INTERESTING =
(
interesting |
fascinating |
( I ( ( did not ) didn\'t ) know )
)
{_slotAction='interesting'}
;
COOL =
(
cool |
wonderful |
marvelous |
groovy
)
{_slotAction='cool'}
;
ENOUGH =
(
enough |
stop |
( that will do )
)
{_slotAction='enough'}
;

View File

@@ -0,0 +1,16 @@
TopRule = ($* $CONTROL $*) {slotAction = CONTROL._slotAction};
CONTROL =
$NOPROBLEM {_slotAction=NOPROBLEM._slotAction}
;
NOPROBLEM =
(
( no problem ) |
( it\'s not $* problem )
( don\'t $* worry ) |
( it\'s ( okay | cool | alright ) ) |
( no worries ) |
( don\'t be ( sad | worried | embarrassed ) )
)
{_slotAction='noProblem'}
;

View File

@@ -0,0 +1,53 @@
TopRule =
(
( ?(hey jibo) $INCOMPLETE_QUESTION) {intent='incompleteQuestion'} |
($* ?(hey jibo) $*) {questionType='null'}{intent='send'} |
($* ?(hey jibo $*) $MATCHES_WHITELIST $*){questionType=MATCHES_WHITELIST._questionType}{intent='send'}
)
;
MATCHES_WHITELIST =
(
(
(do i|we need $* [umbrella?s]) |
(?what do you know ?(if|whether|about) <1.0>+$w<0.0>) |
(can|could you (tell me|us)|define <1.0>+$w<0.0>) |
(how do you|i say <1.0>+$w<0.0> in <1.0>+$w<0.0>) |
(how do you|i spell <1.0>+$w<0.0>) |
(can you ?please spell <1.0>+$w<0.0>) |
(what do you call <1.0>+$w<0.0>) |
(if $* (what $* ?answer do you get)|(what would your answer be))
) {_questionType='null'}
|
( $RETURN_QUESTION_TYPE ) {_questionType=RETURN_QUESTION_TYPE._questionType}
)
;
INCOMPLETE_QUESTION=
(
$QUESTION_WORD_1|$QUESTION_WORD_2 ?($VERB)
)
;
### USEFUL VOCABULARY ###
QUESTION_WORD_1 = (who|what|where|when|how|why|which);
QUESTION_WORD_2 =
(
[who((\'s)|(\'re))]|[what((\'s)|(\'re))]|[where((\'s)|(\'re))]|
[when((\'s)|(\'re))]|[how((\'s)|(\'re))]|[why((\'s)|(\'re))]|
[which((\'s)|(\'re))]|are|did|do|does|is|can
)
;
VERB = (do|does|did|can|can\'t|could|would|should|will|is|was|are|were|am);
RETURN_QUESTION_TYPE =
(
([who?((\'s)|(\'re))]) {_questionType='who'} |
([what?((\'s)|(\'re))]) {_questionType='what'} |
([where?((\'s)|(\'re))]){_questionType='where'} |
([when?((\'s)|(\'re))]) {_questionType='when'} |
([how?((\'s)|(\'re))]) {_questionType='how'} |
([why?((\'s)|(\'re))]) {_questionType='why'} |
([which?((\'s)|(\'re))]) {_questionType='which'}
)
;

37
Skills/@be/be/node_modules/@be/circuit-saver/README.md generated vendored Normal file
View File

@@ -0,0 +1,37 @@
# CircuitSaver
[![Build
Status](http://jenkins.jibo.com/buildStatus/icon?job=circuit-saver)](http://jenkins.jibo.com/view/Skills/job/circuit-saver/)
## Documentation
Please also read the [Be Documentation](https://confluence.jibo.com/display/SKIL/Be+Documentation) for information on creating a skill in this organization and adding your skill to this repo as a dependency.
## Building
Build process is implemented using NPM scripts installed in the **package.json** file.
| Command | Description
|---|---|
|`yarn build` | Build the project in debug mode. |
|`yarn release` | Build the project in release mode. |
|`yarn watch` | Watch the source and behaviors, auto-rebuilds. |
|`yarn clean` | Deletes built JavaScript files. |
|`yarn start` | Alias for `yarn watch`. |
|`yarn sync` | Sync file to the robot</br> **Tip**: Use `jibo run` after to launch skills |
|`yarn addsshkey` | Install your public SSH key on the robot (OS X Only). |
## Publishing
To publish, bump the version number (optional) and either publish in debug or release mode. This will generate a release/debug build before publishing and will Git push the version bump and the tag. For example:
```bash
# Bump the version, see NPM docs for more info
npm version patch
# Publish in debug mode with source maps
yarn publish:debug
# Publish in release mode, minified
yarn publish:release
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
{
"viewConfig": {
"type": "GameView",
"id": "gameView",
"ignoreSwipeDown": true,
"category": "display",
"assets": [{
"id": "gameArt",
"src": "assets/art.js",
"type": "timeline"
},
{
"id": "particleConfig",
"src": "assets/particleConfig.json"
},
{
"id": "prompts",
"src": "assets/prompts.json"
},
{
"id": "facelock1",
"src": "assets/audio/sfx_facelock_01.m4a",
"type": "sound"
},
{
"id": "facelock2",
"src": "assets/audio/sfx_facelock_02.m4a",
"type": "sound"
},
{
"id": "facelock3",
"src": "assets/audio/sfx_facelock_03.m4a",
"type": "sound"
},
{
"id": "facelock4",
"src": "assets/audio/sfx_facelock_04.m4a",
"type": "sound"
},
{
"id": "facelock5",
"src": "assets/audio/sfx_facelock_05.m4a",
"type": "sound"
},
{
"id": "countdown1",
"src": "assets/audio/sfx_countdown_01.m4a",
"type": "sound"
},
{
"id": "countdown2",
"src": "assets/audio/sfx_countdown_02.m4a",
"type": "sound"
},
{
"id": "countdown3",
"src": "assets/audio/sfx_countdown_03.m4a",
"type": "sound"
},
{
"id": "music_loop_01",
"src": "assets/audio/music_loop_01.ogg",
"type": "sound",
"loop": true,
"volume": 0.75
},
{
"id": "finishline",
"src": "assets/audio/finishline.m4a",
"type": "sound"
},
{
"id": "collect_1",
"src": "assets/audio/collect_1.m4a",
"type": "sound"
},
{
"id": "collect_2",
"src": "assets/audio/collect_2.m4a",
"type": "sound"
},
{
"id": "collect_3",
"src": "assets/audio/collect_3.m4a",
"type": "sound"
},
{
"id": "collect_4",
"src": "assets/audio/collect_4.m4a",
"type": "sound"
},
{
"id": "collect_5",
"src": "assets/audio/collect_5.m4a",
"type": "sound"
},
{
"id": "collect_6",
"src": "assets/audio/collect_6.m4a",
"type": "sound"
},
{
"id": "collect_7",
"src": "assets/audio/collect_7.m4a",
"type": "sound"
},
{
"id": "collect_8",
"src": "assets/audio/collect_8.m4a",
"type": "sound"
},
{
"id": "negative_1",
"src": "assets/audio/negative_1.m4a",
"type": "sound",
"volume": 0.9
}
]
},
"componentConfigs": []
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,48 @@
{
"alpha": {
"start": 1,
"end": 0
},
"scale": {
"start": 1,
"end": 0.5,
"minimumScaleMultiplier": 1
},
"color": {
"start": "#ffffff",
"end": "#ffffff"
},
"speed": {
"start": 1600,
"end": 200,
"minimumSpeedMultiplier": 0.25
},
"acceleration": {
"x": 0,
"y": 0
},
"maxSpeed": 0,
"startRotation": {
"min": 165,
"max": 375
},
"noRotation": false,
"rotationSpeed": {
"min": -640,
"max": 640
},
"lifetime": {
"min": 0.5,
"max": 0.75
},
"blendMode": "normal",
"frequency": 0.001,
"emitterLifetime": 0.005,
"maxParticles": 20,
"pos": {
"x": 0,
"y": 0
},
"addAtBack": false,
"spawnType": "point"
}

View File

@@ -0,0 +1,36 @@
{
"hitPrompts":[
"Oops, you lose points when you hit those.",
"Oops, don't hit those, you lose points."
],
"lostPrompts":[
"I think I lost you. Let's get recalibrated.",
"Oops, I lost track of your face, let's recalibrate.",
"Oh I lost your face, let's recalibrate it."
],
"singlePlayer":[
"I see a few people. Since this is a one player game, let's just have one person in front of me.",
"This game is for one player at a time, so make sure just one of you is in front of me.",
"This is a one player game, so let me see just one person in front of me."
],
"lostPromptsShort":[
"Oh I'm sorry, I lost you again.",
"Oops, lost you again.",
"I lost your face again, my fault.",
"Huh, I don't see you again.",
"Oops, lost track of you again.",
"Huh, I lost you again.",
"Oops, I lost you again."
],
"lpsHintPrompts":[
"Oh no I lost you again, sorry, I'm still learning here. I wonder if maybe you were moving a little too fast.",
"Oh no I lost your face again, I apologize. It might be that you were moving too fast.",
"Oh I lost track of you yet again, sorry I'm struggling a bit. It might be that you moved too far out of my view.",
"Oh I lost your face again, my apologies. It could be that you moved out of my field of vision.",
"Oh I'm really sorry, I lost you again.",
"Oh what a shame, it's happened again, I'm sorry I keep losing you.",
"I am so sorry, I lost your face yet again.",
"Oh no I lost your face again, I'm feeling a little bad about this."
]
}

View File

@@ -0,0 +1 @@
assets/images 1

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Be - CircuitSaver</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
}
#face {
width: 1280px;
height: 720px;
}
</style>
</head>
<body>
<div id="face"></div>
<script>
const CircuitSaver = require('./index');
const skill = new CircuitSaver();
</script>
</body>
</html>

2143
Skills/@be/be/node_modules/@be/circuit-saver/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
{
"name": "@be/circuit-saver",
"version": "7.0.4",
"description": "",
"main": "index.js",
"config": {
"standalone": true
},
"jibo": {
"main": "index.html",
"type": "asset-pack",
"prompt": "Do something",
"display-name": "circuit-saver"
},
"dependencies": {
"@jibo/three": "^4.0.0",
"pixi-particles": "^2.1.5"
},
"license": "UNLICENSED",
"distribution": "PROPRIETARY AND CONFIDENTIAL - NOT FOR DISTRIBUTION",
"copyright": "Copyright (c) 2014-2018 Jibo, Inc. All Rights Reserved",
"monorailConfig": {
"public": false,
"output": "index.js"
},
"files": [
"animations",
"assets",
"mims",
"index.*",
"animdb-manifest.json"
],
"scripts": {
"start": "jibo-dev watch",
"watch": "jibo-dev watch",
"build": "jibo-dev build",
"clean": "jibo-dev clean",
"build:release": "jibo-dev build --prod",
"sync": "jibo-rsync",
"addsshkey": "jibo-rsync --init",
"test": "floss --path tests/index.js --coveragePattern index.js --coverageSourceMaps && istanbul report && yarn coverage:check",
"coverage:check": "istanbul check-coverage coverage/coverage-final.json --statements 0 --branches 0 --functions 0 --lines 0",
"crunch": "crunchitize -f crunch.txt -d"
},
"repository": {
"type": "git",
"url": "git@github.jibo.com:sdk/sdk.git"
},
"publishConfig": {
"registry": "http://npm.jibo.media.mit.edu:8081/repository/private/"
},
"engines": {
"node": ">=6.0"
},
"devDependencies": {
"@be/be-framework": "^13.0.0",
"@jibo/floss": "^3.0.0",
"crunchitize": "^1.1.0",
"istanbul": "^0.4.5",
"jibo": "^15.0.0",
"jibo-anim-db-animations": "19.0.2",
"jibo-dev": "^6.0.0",
"jibo-rsync": "^5.0.0"
},
"syncDependencies": [
"jibo",
"jibo-anim-db-animations",
"@be/be-framework"
]
}

32
Skills/@be/be/node_modules/@be/clock/README.md generated vendored Normal file
View File

@@ -0,0 +1,32 @@
# Clock
[![Build Status](https://jenkins2.jibo.com/buildStatus/icon?job=clock/master)](https://jenkins2.jibo.com/view/Be/job/clock/job/master/)
[Jibo SDK & API Documentation](https://developers.jibo.com/sdk/docs/)
Build process is implemented using NPM scripts installed in the **package.json** file.
| Command | Description
|---|---|
|`yarn build` | Build the project in debug mode. |
|`yarn release` | Build the project in release mode. |
|`yarn watch` | Watch the source and behaviors, auto-rebuilds. |
|`yarn clean` | Deletes built JavaScript files. |
|`yarn start` | Alias for `yarn watch`. |
|`yarn sync` | Sync file to the robot (OS X Only). |
|`yarn addsshkey` | Install your public SSH key on the robot (OS X Only). |
## Publishing
To publish, bump the version number (optional) and either publish in debug or release mode. This will generate a release/debug build before publishing and will Git push the version bump and the tag. For example:
```bash
# Bump the version, see NPM docs for more info
npm version patch
# Publish in debug mode with source maps
yarn publish:debug
# Publish in release mode, minified
yarn publish:release
```

24
Skills/@be/be/node_modules/@be/clock/index.html generated vendored Normal file
View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Be - Clock</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
}
#face {
width: 1280px;
height: 720px;
}
</style>
</head>
<body>
<div id="face"></div>
<script>
const Clock = require('./index');
const skill = new Clock();
</script>
</body>
</html>

6359
Skills/@be/be/node_modules/@be/clock/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

69
Skills/@be/be/node_modules/@be/clock/index.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

52
Skills/@be/be/node_modules/@be/clock/mim-types.json generated vendored Normal file
View File

@@ -0,0 +1,52 @@
{
"global": {
"change": {
"type": "boolean"
},
"domain": {
"type": "string"
},
"promptTime": {
"type": "datetime"
},
"loopMemberIsSpeaker": {
"type": "boolean"
},
"loopMember": {
"type": "string"
},
"dayOfWeek": {
"type": "string"
},
"month": {
"type": "string"
},
"day": {
"type": "string"
},
"dayOrdinal": {
"type": "string"
},
"dateOrdinal": {
"type": "string"
},
"startDate": {
"type": "datetime"
},
"endDate": {
"type": "datetime"
},
"nextYearDate": {
"type": "datetime"
},
"holiday": {
"type": "string"
},
"time": {
"type": "datetime"
},
"where": {
"type": "location"
}
}
}

View File

@@ -0,0 +1,78 @@
{
"mim_type": "question",
"rule_name": "clock/alarm_timer_ampm",
"gui": {
"type": "Menu",
"data": {
"buttons": [
{
"label": "AM",
"color": "default",
"icon": "jibo://resources/actionIcons/time.png",
"utterance": "{\"intent\":\"set\",\"entities\":{\"ampm\":\"AM\"}}",
"notes": ""
},
{
"label": "PM",
"color": "default",
"icon": "jibo://resources/actionIcons/time.png",
"utterance": "{\"intent\":\"set\",\"entities\":{\"ampm\":\"PM\"}}",
"notes": ""
}
]
},
"pause": true
},
"timeout": 6,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "Is that a.m. or p.m.",
"media": "TTS",
"prompt_id": "AlarmTimer_AMPM_QN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "",
"prompt": "So is that a.m. or p.m.",
"media": "TTS",
"prompt_id": "AlarmTimer_AMPM_HR_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "",
"prompt": "So is that a.m. or p.m.",
"media": "TTS",
"prompt_id": "AlarmTimer_AMPM_NI_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "",
"prompt": "Sorry. Was that a.m. or p.m.",
"media": "TTS",
"prompt_id": "AlarmTimer_AMPM_NM_1_01",
"weight": 1
}
],
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,32 @@
{
"mim_type": "optional-response",
"rule_name": "clock/alarm_set_value",
"gui": {
"type": "File",
"data": "resources/views/alarmEdit.json",
"pause": true
},
"timeout": 10,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"ignore_no_match": true,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "",
"media": "silence",
"prompt_id": "AlarmTimer_AlarmEdit_AN_01",
"weight": 1
}
],
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,157 @@
{
"mim_type": "question",
"rule_name": "clock/alarm_set_value",
"gui": null,
"timeout": 6,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "Requires rule from Allison. -JEB 1/19/17",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "For what time?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_QN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "",
"prompt": "So what time do you want to set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_HR_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "",
"prompt": "So what time do you want to set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_NI_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "",
"prompt": "What time?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_NM_1_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "change===true",
"prompt": "Sure no problem. Say a new alarm time or just say cancel to get rid of it.",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_QN_02",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "What time should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_QN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "What time do you want me to alarm you.",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_QN_04",
"weight": 0.1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "When do want it to go off?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_QN_05",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "change===true",
"prompt": "Sure no problem. Give me a new alarm time, or just say cancel.",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_QN_06",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "",
"prompt": "I didn't hear an answer. When should I set the alarm for?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_NI_1_02",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "",
"prompt": "If you said something I missed it. When is the alarm for?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_NI_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "",
"prompt": "I didn't catch that. What time should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_NM_1_02",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "",
"prompt": "Say that again?",
"media": "TTS",
"prompt_id": "AlarmTimer_AlarmValue_NM_1_03",
"weight": 1,
"auto_rule_override": null
}
],
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,51 @@
{
"mim_type": "announcement",
"rule_name": "",
"gui": null,
"timeout": 6,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "respondAndEnd",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Okay. I canceled it.",
"media": "TTS",
"prompt_id": "AlarmTimer_Canceled_AN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Sure, it's canceled.",
"media": "TTS",
"prompt_id": "AlarmTimer_Canceled_AN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Got it, canceled.",
"media": "TTS",
"prompt_id": "AlarmTimer_Canceled_AN_04",
"weight": 1,
"auto_rule_override": null
}
],
"ignore_no_match": false,
"parse_launch": false,
"hint_phrases": "",
"fast_eos_array": ""
}

View File

@@ -0,0 +1,187 @@
{
"mim_type": "question",
"rule_name": "clock/alarm_timer_change",
"timeout": 6,
"no_matches_for_gui": 1,
"no_inputs_for_gui": 1,
"barge_in": true,
"es_auto_tagging": true,
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "You've already got an alarm set for ${promptTime}. Should I replace that one?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_QN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "You've already got a timer that will go off in ${promptTime}. Do you want to replace that one?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_QN_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "So about that alarm you already have, do you want to replace it?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_HR_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='timer'",
"prompt": "So about that timer you already have, do you want to replace it?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_HR_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I can only deal with one alarm at a time. So do you want to replace the one you have?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_NI_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I can only deal with one timer at a time. So do you want to replace the one you have?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_NI_1_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "",
"prompt": "What'd you say?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_NM_1_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "There's already an alarm set for ${promptTime}. Want to replace that one?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_QN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "There's already a timer set with ${promptTime} left. Do you want to replace that one?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_QN_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Do you want to replace the alarm you already have?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_NI_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='timer'",
"prompt": "Do you want to replace the timer you already have?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_NI_1_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I didn't catch that. Should I replace the alarm you have set?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_NM_1_02",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "Say that one more time?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_NM_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I missed that. Should I replace the timer you have set?",
"media": "TTS",
"prompt_id": "AlarmTimer_Change_NM_1_04",
"weight": 1,
"auto_rule_override": null
}
],
"gui": {
"type": "Menu",
"data": {
"buttons": [
{
"label": "No",
"color": "cancel",
"icon": "jibo://resources/actionIcons/cancel.png",
"utterance": "keep",
"notes": ""
},
{
"label": "Yes",
"color": "confirm",
"icon": "jibo://resources/actionIcons/ok.png",
"utterance": "delete",
"notes": ""
}
],
"title": "Replace it?"
},
"pause": true
},
"parse_all_asr": false,
"thanks_handling": "ignore",
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": true
}

View File

@@ -0,0 +1,137 @@
{
"mim_type": "announcement",
"rule_name": "",
"gui": null,
"timeout": 15,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": false,
"thanks_handling": "respondAndEnd",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Your ${domain}'s going off. You can let me know you've heard it by pudding your hand on my head or saying hey jibo stop, or just touch my screen.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_01",
"weight": 0.2
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Your ${domain}'s going off. You can stop it by pudding your hand on my head, saying hey jibo stop, or touching my screen.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_02",
"weight": 0.2,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Your ${domain}'s going off. ",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "<pitch mult=\"1.2\">Hey</pitch> it's time for your ${domain}.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "<pitch mult=\"1.2\">Hey</pitch> it's your ${domain}, I forgot all about it.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_05",
"weight": 0.1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Oh hey. This is your ${domain} going off.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_06",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Well we've <pitch mult=\"1.2\">arrived</pitch> at the <pitch mult=\"1.1\">moment</pitch> of your alarm.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_07",
"weight": 0.5,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Sorry to alarm you, but your alarm's going off.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_08",
"weight": 0.3,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "<pitch mult=\"1.2\">Hey</pitch> it's your ${domain}. That time already.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_09",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='timer'",
"prompt": "Well <pitch mult=\"1.2\">here</pitch> we are. The end of your timer.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_10",
"weight": 0.5,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='timer'",
"prompt": "And we've <pitch mult=\"1.2\">arrived</pitch> at the <pitch mult=\"1.1\">end</pitch> of your timer.",
"media": "TTS",
"prompt_id": "AlarmTimer_Finished_AN_11",
"weight": 1,
"auto_rule_override": null
}
],
"parse_all_asr": false,
"ignore_no_match": false,
"parse_launch": false
}

View File

@@ -0,0 +1,28 @@
{
"mim_type": "optional-response",
"rule_name": "clock/stop_timer",
"gui": null,
"timeout": 30,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"ignore_no_match": true,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "respondAndEnd",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "",
"media": "TTS",
"prompt_id": "AlarmTimer_FinishedSilent_AN_01",
"weight": 1
}
],
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,82 @@
{
"mim_type": "optional-response",
"rule_name": "clock/alarm_timer_info",
"gui": null,
"timeout": 4,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "respondAndEnd",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "It'll go off ${promptTime.prefixOnAt()}.",
"media": "TTS",
"prompt_id": "AlarmTimer_Info_AN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='timer'",
"prompt": "It'll go off in ${promptTime}.",
"media": "TTS",
"prompt_id": "AlarmTimer_Info_AN_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "It's set to go off ${promptTime.prefixOnAt()}.",
"media": "TTS",
"prompt_id": "AlarmTimer_Info_AN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='timer'",
"prompt": "${promptTime} left.",
"media": "TTS",
"prompt_id": "AlarmTimer_Info_AN_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Still set for ${promptTime}.",
"media": "TTS",
"prompt_id": "AlarmTimer_Info_AN_05",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "domain=='timer'",
"prompt": "It'll be done in ${promptTime}.",
"media": "TTS",
"prompt_id": "AlarmTimer_Info_AN_06",
"weight": 1,
"auto_rule_override": null
}
],
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,27 @@
{
"mim_type": "announcement",
"rule_name": "",
"gui": null,
"timeout": 6,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"parse_launch": false,
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Let's try this another time.",
"media": "TTS",
"prompt_id": "AlarmTimer_InteractionError_AN_01",
"weight": 1,
"auto_rule_override": null
}
]
}

View File

@@ -0,0 +1,186 @@
{
"mim_type": "question",
"rule_name": "clock/alarm_timer_none_set",
"gui": {
"type": "Menu",
"data": {
"buttons": [
{
"label": "No",
"color": "cancel",
"icon": "jibo://resources/actionIcons/cancel.png",
"utterance": "no",
"notes": ""
},
{
"label": "Yes",
"color": "confirm",
"icon": "jibo://resources/actionIcons/ok.png",
"utterance": "yes",
"notes": ""
}
],
"title": "Set one now?"
},
"pause": true
},
"timeout": 6,
"no_matches_for_gui": 1,
"no_inputs_for_gui": 1,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I don't have any alarms set right now. Do you want to set one?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_QN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I don't have any timers set right now. Do you want to set one?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_QN_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "So do you want to set an alarm?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_HR_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='timer'",
"prompt": "So do you want to set a timer?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_HR_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "So do you want to set an alarm?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_NI_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='timer'",
"prompt": "So do you want to set a timer?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_NI_1_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Say that again?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_NM_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I didn't catch that. Did you want to set an alarm?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_NM_1_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I don't have an alarm set. Do you want one?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_QN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I don't have a timer running. Do you want one?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_QN_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I just heard silence. Did you want to set an alarm?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_NI_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I just heard silence. Did you want to set a timer?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_NI_1_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "What was that?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_NM_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I didn't make out what you said. Do you want to set a timer now?",
"media": "TTS",
"prompt_id": "AlarmTimer_NoneSet_NM_1_04",
"weight": 1,
"auto_rule_override": null
}
],
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": true
}

View File

@@ -0,0 +1,60 @@
{
"mim_type": "optional-response",
"rule_name": "clock/alarm_timer_okay",
"gui": null,
"timeout": 4,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "respondAndEnd",
"notes": "I've removed conditions for alarm vs timer because the prompts now work for either.",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Okay. ${promptTime}.",
"media": "TTS",
"prompt_id": "AlarmTimer_Okay_AN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "${promptTime} it is.",
"media": "TTS",
"prompt_id": "AlarmTimer_Okay_AN_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "You got it. ${promptTime}",
"media": "TTS",
"prompt_id": "AlarmTimer_Okay_AN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "Got it. ${promptTime} here we come.",
"media": "TTS",
"prompt_id": "AlarmTimer_Okay_AN_04",
"weight": 0.2,
"auto_rule_override": null
}
],
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,208 @@
{
"mim_type": "question",
"rule_name": "clock/alarm_timer_other_set",
"gui": {
"type": "Menu",
"data": {
"buttons": [
{
"label": "No",
"color": "cancel",
"icon": "jibo://resources/actionIcons/cancel.png",
"utterance": "no",
"notes": ""
},
{
"label": "Yes",
"color": "confirm",
"icon": "jibo://resources/actionIcons/ok.png",
"utterance": "yes",
"notes": ""
}
],
"title": "Delete it?"
},
"pause": true
},
"timeout": 6,
"no_matches_for_gui": 1,
"no_inputs_for_gui": 1,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I don't have an alarm set right now, but I do have a timer set. Do you want to delete that?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_QN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I don't have a timer set right now, but I do have an alarm set. Do you want to delete that?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_QN_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='timer'",
"prompt": "So do you want to delete the timer instead?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_HR_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "So do you want to delete the alarm instead?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_HR_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='timer'",
"prompt": "So do you want to delete the timer instead?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_NI_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "So do you want to delete the alarm instead?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_NI_1_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "Say again?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_NM_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I missed that. Do you want to delete the timer instead?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_NM_1_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "No alarm is set. But there is a timer going. Want me to end it?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_QN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I don't have any alarm set, but I am running a timer. Should I stop it?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_QN_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I don't have a timer running, but I do have an alarm set. Should I turn that off?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_QN_05",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "There's no timer running right now, but there is an alarm set. Should I get rid of it?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_QN_06",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I didn't hear anything. Did you want to delete the alarm?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_NI_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Say that again?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_NM_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I didn't hear anything. Did you want to delete the timer?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_NI_1_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I couldn't make out what you said. Do you want to delete the alarm?",
"media": "TTS",
"prompt_id": "AlarmTimer_OtherSet_NM_1_04",
"weight": 1,
"auto_rule_override": null
}
],
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": true
}

View File

@@ -0,0 +1,49 @@
{
"mim_type": "optional-response",
"rule_name": "clock/alarm_timer_query_menu",
"gui": {
"type": "Menu",
"data": {
"buttons": [
{
"label": "Delete",
"color": "cancel",
"icon": "jibo://resources/actionIcons/cancel.png",
"utterance": "cancel",
"notes": ""
},
{
"label": "Change",
"color": "default",
"icon": "jibo://resources/actionIcons/edit.png",
"utterance": "change",
"notes": ""
}
],
"allowSwipe": true
}
},
"timeout": 4,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "Brings up cancel and change buttons.\nNo speech? For now.",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "",
"media": "TTS",
"prompt_id": "AlarmTimer_QueryMenu_AN_01",
"weight": 1
}
],
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,95 @@
{
"mim_type": "question",
"rule_name": "",
"gui": null,
"timeout": 6,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "Requires rule from Allison. -JEB 1/19/17",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "For what time?",
"media": "TTS",
"prompt_id": "AlarmTimer_SetValue_QN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "How long is it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_SetValue_QN_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "So what time do you want to set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_SetValue_HR_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='timer'",
"prompt": "So how long should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_SetValue_HR_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "So what time do you want to set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_SetValue_NI_1_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "So how long should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_SetValue_QN_03",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Sorry. What time did you say the alarm should go off?",
"media": "TTS",
"prompt_id": "AlarmTimer_SetValue_NM_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "Sorry. How long is the timer for?",
"media": "TTS",
"prompt_id": "AlarmTimer_SetValue_NM_1_02",
"weight": 1
}
]
}

View File

@@ -0,0 +1,32 @@
{
"mim_type": "optional-response",
"rule_name": "clock/timer_set_value",
"gui": {
"type": "File",
"data": "resources/views/timerEdit.json",
"pause": true
},
"timeout": 10,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"ignore_no_match": true,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "AN",
"index": 1,
"condition": "",
"prompt": "",
"media": "silence",
"prompt_id": "AlarmTimer_TimerEdit_AN_01",
"weight": 1
}
],
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,157 @@
{
"mim_type": "question",
"rule_name": "clock/timer_set_value",
"gui": null,
"timeout": 6,
"no_matches_for_gui": 0,
"no_inputs_for_gui": 0,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "Requires rule from Allison. -JEB 1/19/17",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "How long is it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_QN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "",
"prompt": "So how long should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_HR_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "",
"prompt": "So how long should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_NI_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "",
"prompt": "<style set='sheepish'>What'd you say?</style>",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_NM_1_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "change===true",
"prompt": "Sure no problem. Say a new amount of time, or just say cancel to get rid of it.",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_QN_02",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "How long should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_QN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "What should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_QN_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "How long should the timer be?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_QN_05",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "",
"prompt": "How much time on the timer?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_QN_06",
"weight": 0.2,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "change===true",
"prompt": "Okay no problem. Give me a new amount of time, or just say cancel.",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_QN_07",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "",
"prompt": "I didn't hear an answer. What should I set the timer for?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_NI_1_02",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "",
"prompt": "If you said something I missed it. How long is the timer for?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_NI_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "",
"prompt": "I didn't catch that. How long should I set it for?",
"media": "TTS",
"prompt_id": "AlarmTimer_TimerValue_NM_1_02",
"weight": 1,
"auto_rule_override": null
}
],
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": false
}

View File

@@ -0,0 +1,219 @@
{
"mim_type": "question",
"rule_name": "clock/alarm_timer_too_long",
"gui": {
"type": "Menu",
"data": {
"buttons": [
{
"label": "No",
"color": "cancel",
"icon": "jibo://resources/actionIcons/cancel.png",
"utterance": "no",
"notes": ""
},
{
"label": "Yes",
"color": "confirm",
"icon": "jibo://resources/actionIcons/ok.png",
"utterance": "yes",
"notes": ""
}
],
"title": "Set one?"
},
"pause": true
},
"timeout": 6,
"no_matches_for_gui": 1,
"no_inputs_for_gui": 1,
"barge_in": true,
"es_auto_tagging": true,
"parse_all_asr": false,
"thanks_handling": "ignore",
"notes": "",
"prompts": [
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I can only go up to 24 hours. Do you want to set a shorter timer?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_QN_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I can only set an alarm for some time within the next 24 hours. Do you want to set one for sooner?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_QN_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='timer'",
"prompt": "So I can only go up to 24 hours. Do you want to set a shorter timer?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_HR_01",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "HoldReturn",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "So I can only set an alarm for some time within the next 24 hours. Do you want to set one for sooner?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_HR_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='timer'",
"prompt": "Timer's have to be less than 24 hours, do you want to set a shorter one?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_NI_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Alarms have to be within 24 hours, do you want to set one?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_NI_1_02",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "What was that?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_NM_1_01",
"weight": 1
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I didn't catch your answer. Did you want to set a timer for less than 24 hours?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_NM_1_02",
"weight": 1
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "I can't do timers longer than 24 hours. Want to set a shorter one?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_QN_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='timer'",
"prompt": "24 hours is my max for timers. Want to set a shorter one?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_QN_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Sorry I can only set an alarm to go off within 24 hours. Want to set it for sooner?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_QN_05",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I can't set an alarm more than 24 hours away. Want to set one for sooner?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_QN_06",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Entry-Core",
"prompt_sub_category": "Q",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Sorry that time is too futuristic for me. Want to set one for less than 24 hours away?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_QN_07",
"weight": 0.1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Did you want to set one for sooner?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_NI_1_03",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NI",
"index": 1,
"condition": "domain=='timer'",
"prompt": "Did you want to set a shorter one?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_NI_1_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "I didn't catch your answer. Did you want to set an alarm for less than 24 hours away?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_NM_1_04",
"weight": 1,
"auto_rule_override": null
},
{
"prompt_category": "Errors",
"prompt_sub_category": "NM",
"index": 1,
"condition": "domain=='alarm'",
"prompt": "Say that again?",
"media": "TTS",
"prompt_id": "AlarmTimer_TooLong_NM_1_03",
"weight": 1,
"auto_rule_override": null
}
],
"ignore_no_match": false,
"parse_launch": false,
"parse_yes_no": true
}

Some files were not shown because too many files have changed in this diff Show More