Create a plugin
World time plugin
In this example, you'll learn step-by-step how to develop by the creation of a plugin which provides the time in the world's major cities.
- Step 1: Developing in a terminal
- Step 2: Using
Plugin Studio
- Step 3: Modify plugin properties after creation
- Step 4: Modify the plugin script after creation
- Step 5: Add natural language processing to the plugin script with the 'relations' object
- Step 6: Add properties
- Step 7: Installing and importing npm modules
- Step 8: Modify the plugin script to make it functional
- Step 9: Localize messages in several languages
- Step 10: Add an information page
Warning
This plugin is part of the A.V.A.T.A.R. plugin library.
If you've already installed it, please remove it or save it before continuing !!
Create the plugin
- Start the server in a terminal
-
Create a plugin by Plugin Studio
- Plugin name :
worldTime
- Displayed name:
World time
- Rules:
- YES
- By the syntax of the sentence
- Script methods: Select
Language localization methods
- Add an image
- Select the image
<A.V.A.T.A.R>/resources/app/assets/images/PluginCreation/worldTime.png
- Select the image
- No documentation
- Plugin name :
Define plugin rules
-
- Click on Properties tab
- Modify the action
test
byget
- Modify the rule by
what time {be}
- See Root matches in the Matches table for further details on writing rule
- Open the contextual menu
- Save properties using the
Save
menu
- Save properties using the
- Close Plugin Studio
- Click on Later to not restart A.V.A.T.A.R.
-
Modify the plugin script file in Visual Studio (or another text editor)
- Open the file
<A.V.A.T.A.R>/resources/app/core/plugins/worldTime/worldTime.js
- Modify the
command
object and the method nametest
byget
- Change variable
client
todata.client
in theAvatar.speak
function - As an accessory, you can delete all comments and imports added during plugin creation
Expected result:
time.js//language pak let Locale; export async function init() { if (!await Avatar.lang.addPluginPak("worldTime")) { return error('worldTime: unable to load language pak files'); } } export async function action(data, callback) { try { Locale = await Avatar.lang.getPak("worldTime", data.language) if (!Locale) { throw new Error (`worldTime: Unbale to find the '${data.language}' language pak.`); } const tblActions = { get : () => get(data) } info("worldTime:", data.action.command, L.get("plugin.from"), data.client); tblActions[data.action.command]() } catch (err) { if (data.client) Avatar.Speech.end(data.client); if (err.message) error(err.message); } callback(); } const get = (data) => { Avatar.speak(Locale.get(["message.test", data.client]), data.client); }
- Open the file
-
Restart A.V.A.T.A.R
- Do a Ctrl +
C
in the terminal to stop the server - Get the last command
npm start .
to restart the server
- Do a Ctrl +
-
Test the plugin
- Start a client if you haven't already done so
- Execute the
what time is it
rule
Warning
If the message
Error: time: Unable to find the 'XX' language pak
appears in the A.V.A.T.A.R console, check if the client's language pak exists in the time/locales directory. If it doesn't, copy the existing language pak as XX.pak (where XX is the short code of your language), then modify the value of thetest
key.
Plugin modification
We're now going to make sure we have a working plugin that will allow us to request the time for all the world's major cities.
-
First of all, we can check whether the 'relations' object might be useful for plugin development (see also Natural Language Processing).
- Add a console.log() to the get() function to display the 'relation' object
time.js
const get = (data) => { Avatar.speak(Locale.get(["message.test", data.client]), data.client); console.log('Relation:', data.relations); }
- Restart A.V.A.T.A.R in the terminal
-
Execute the
what time is it in new york
rule -
We can see the relations.location object
-
Test several rules with cities around the world to check:
- That most of the world's largest cities are correctly recognized
- If there is no city in the rule, the relation.location object does not exist.
-
The tests are conclusive in the majority of cases, so we're going to use language processing to develop the plugin, otherwise we'd have to develop something more complex and less elegant. It's very interesting and will make our lives easier.
- Add a console.log() to the get() function to display the 'relation' object
Tip
Some cities, such as Beijing (or Pekin), are not recognized as timeZones. This small problem, not inherent to language processing and not seen in this example, can be simply dealt with on a case-by-case basis by the moment-timezone
module.
-
Add parameters to properties
As the timezone is on a main city, we'll add 2 parameters:
- A
zone
parameter for a default timezone when there is no city in the rule - A
speech
parameter so that the default timezone message relates to the city we live in (Let's get it right!).
Modify the plugin settings as shown below:
- A
-
Install the required modules
-
We need 2 import modules:
underscore
is an A.V.A.T.A.R module, so there's no need to install it (See npm packages for futher details)moment-timezone
: This module is to be installed
-
Open a terminal and navigate to the plugin folder
* Install thecd <A.V.A..T.A.R>/resources/app/cores/plugin/worldTime
moment-timezone
modulenpm install moment-timezone
-
-
Add the imports to the script at the beginning of the file
worldTime.jsimport _ from 'underscore'; import moment from 'moment-timezone';
-
Modify the get function to include the time search as follows
Note: Modify Avatar.speak with a localized sentence in your language
worldTime.jsconst get = (data) => { try { // Defines the city, NLP relation or default const city = data.relations?.location?.text ? data.relations.location.text : Config.modules.worldTime.default.zone; // Defines the city for the speech const speechCity = city === Config.modules.worldTime.default.zone ? Config.modules.worldTime.default.speech : city; // Updates chars in the city to be conform to timeZones const location = city.replace(/ /gi,"_"); // Retreives timeZones const timeZones = Intl.supportedValuesOf('timeZone'); // Searchs for the city in the timeZones table let even = _.find(timeZones, item => { return item.toLowerCase().indexOf(`${location}`.toLowerCase()) !== -1; }); if (even) { // even can be a string or a Array even = typeof even === 'string' ? even : even[0]; // Defines the location (e.g. 'en' or 'fr') for a language-correct sentence. moment.locale(data.language); // Builds the sentence const sentence = moment.tz(Date.now(), even).format(`[In ${speechCity}, it is] H:m:s [the] dddd DD MMMM`); // Speaks the sentence Avatar.speak(sentence, data.client); } else { // Speaks a default sentence if no timezone Avatar.speak(`i'm sorry, i couldn't retrieve the ${speechCity} time`, data.client); } } catch (err) { if (data.client) Avatar.Speech.end(data.client); if (err.message) error(err.message); } }
- Restart A.V.A.T.A.R in the terminal
- Execute the
what time is it in new york
rule
Success
The worldTime
plugin gives you the time for the world's biggest cities!
Localize the plugin
We can now add message localization. For the example, we'll add English and French.
-
Create (or modify) 2 language packs in the locales directory as follows.
- Replacing the city variable with a $$ (see the get function)
{ "message": { "time":"[in $$, it is] H:m:s [the] dddd MMMM DD" }, "error": { "notime":"i'm sorry, i couldn't retrieve the $$ time" } }
{ "message": { "time":"[a $$, il est] H:m:s [le] dddd DD MMMM" }, "error": { "notime":"je suis désolé, je n'ai pas réussi à récupérer l'heure de $$" } }
-
Modify the script file for multi-language support
worldTime.jsconst get = (data) => { try { // Defines the city, NLP relation or default const city = data.relations?.location?.text ? data.relations.location.text : Config.modules.worldTime.default.zone; // Defines the city for the speech const speechCity = city === Config.modules.worldTime.default.zone ? Config.modules.worldTime.default.speech : city; // Updates chars in the city to be conform to timeZones const location = city.replace(/ /gi,"_"); // Retreives timeZones const timeZones = Intl.supportedValuesOf('timeZone'); // Searchs for the city in the timeZones table let even = _.find(timeZones, item => { return item.toLowerCase().indexOf(`${location}`.toLowerCase()) !== -1; }); if (even) { // even can be a string or a Array even = typeof even === 'string' ? even : even[0]; // Defines the location (e.g. 'en' or 'fr') for a language-correct sentence. moment.locale(data.language); // Builds the sentence const sentence = moment.tz(Date.now(), even).format(Locale.get(["message.time", speechCity])); // Speaks the sentence Avatar.speak(sentence, data.client); } else { // Speaks a default sentence if no timezone Avatar.speak(Locale.get(["error.notime", speechCity]), data.client); } } catch (err) { if (data.client) Avatar.Speech.end(data.client); if (err.message) error(err.message); } }
Plugin information page
We can now add an information page in Markdown format.
Note
The Markdown format is an HTML page creation format with a very simple writing style that requires no special knowledge of HTML.
For your information, this document is written in Markdown, which is also the writing format adopted by GitHub.
See also mkdocs-material for more information.
Reminder
File name | Description |
---|---|
info.md |
If no info_<application language>.md file exists, then this file is used. |
info_<application language>.md |
application language: Short code for application language If this file exists then it is used. Example: info_en.md |
For the example, we'll add English and French.
-
Open a terminal and navigate to the plugin's assets directory.
cd <A.V.A..T.A.R>/resources/app/cores/plugin/worldTime/assets
-
Create 2 information files in Markdown format for English and French as follows
Note: You can consult the Valid Options section of the showdown github project for more details on the writing possibilities (text, table, list, link, etc.).
# World time  World time lets you find out the time for all world's major cities. ## Rules ### Default location - `what time is it`: Returns the time for the default location ### Major world city - `what time is it in new york`: Returns the time for New York city - `in new york, what time is it`: Returns the time for New York city ## Parameters * `default.zone`: The default city (the _timeZone_) if no city is in the rule * `default.speech`: The default city to speak if no city is in the rule ## Adding a language You can localize A.V.A.T.A.R speech by adding a language pack to the _WorldTime/locales_ folder, copying an existing pack and modifying the key values.
# World time  World time vous permet de connaître l'heure pour toutes les grandes villes du monde. ## Règles ### Emplacement par défaut - `what time is it`: Retourne l'heure de l'emplacement par défaut ### Grande ville du monde - `what time is it in new york`: Retourne l'heure pour la ville de New York - `in new york, what time is it`: Retourne l'heure pour la ville de New York ## Paramètres * `default.zone`: La ville par défaut (la _timeZone_) si aucune ville n'est mentionnée dans la règle * `default.speech`: La ville à utiliser pour le dialogue d'A.V.A.T.A.R si aucune ville n'est mentionnée dans la règle. ## Ajouter un langage Vous pouvez localiser le dialogue d'A.V.A.T.A.R en ajoutant un pack de langues dans le dossier _WorldTime/locales_, en copiant un pack existant et en modifiant les valeurs de clés.
-
Restart A.V.A.T.A.R in the terminal
-
Display the plugin information page in Plugin studio
-
You can change the application language to check the result in another language