Skip to content

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

  1. Start the server in a terminal
  2. 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
    • No documentation

    plugin-creation

Define plugin rules

  1. Modify plugin properties

    • Click on Properties tab
    • Modify the action test by get
    • 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

    timeProperties

    • Close Plugin Studio
    • Click on Later to not restart A.V.A.T.A.R.
  2. 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 name test by get
    • Change variable client to data.client in the Avatar.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);
    
    }
    
  3. 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
  4. 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 the test 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.

  1. The 'relations' object

    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

      timezone

    • We can see the relations.location object

    • Test several rules with cities around the world to check:

      1. That most of the world's largest cities are correctly recognized
      2. 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.

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.

  1. 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:

    defaultzone

  2. 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

      cd <A.V.A..T.A.R>/resources/app/cores/plugin/worldTime
      
      * Install the moment-timezone module

      npm install moment-timezone
      

      timezone

  3. Add the imports to the script at the beginning of the file

    worldTime.js
    import _ from 'underscore';
    import moment from 'moment-timezone';
    
  4. Modify the get function to include the time search as follows

    Note: Modify Avatar.speak with a localized sentence in your language

    worldTime.js
    const 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.

  1. 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 $$"
        }
    }
    
  2. Modify the script file for multi-language support

    worldTime.js
    const 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.

  1. Open a terminal and navigate to the plugin's assets directory.

    cd <A.V.A..T.A.R>/resources/app/cores/plugin/worldTime/assets
    
  2. 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
    
    ![worldTime](../../core/plugins/worldTime/assets/images/worldTime.png =100x*)
    
    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
    
    ![worldTime](../../core/plugins/worldTime/assets/images/worldTime.png =100x*)
    
    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.
    
  3. Restart A.V.A.T.A.R in the terminal

  4. Display the plugin information page in Plugin studio

    info_en

    info_fr

  5. You can change the application language to check the result in another language



IntroductionMeteo