Google Chat Bot. Структура бота та CLI утіліта clasp

Останнім часом мені доводиться все більше приймати участь у розробці ботів для Google Chat, тому в мене вже з’явились дякі практики з оформлення проєкта, якими я і хочу поділитися з вами.

Почнемо з початку, з редактору коду. Але знаєте що? Мене редагування коду у браузері трохи бісить, тому будемо налаштовувати робочий енвайронмент здорової людини локально на робочому комп’ютері. Для цього нам знадобиться git, ваша улюблена IDE та eslint для перевірки що у вас все добре. Все це звучить досить знайомо, тож так ми будемо розробляти код локально, перевіряти кодестайл за допомоги eslint, а за версіонування коду в нас буде відповідати git з усіма його можливостями.

Подивіться як виглядає package.json мого проєкту:

{
  "name": "bender-2.0",
  "version": "7.0.0",
  "description": "The Bender bot for Google Workspace",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/AntonShevchuk/bender-2.0.git"
  },
  "keywords": [
    "Google Workspace",
    "Google Chat API",
    "Apps Script"
  ],
  "author": "Anton Shevchuk",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/AntonShevchuk/bender-2.0/issues"
  },
  "homepage": "https://github.com/AntonShevchuk/bender-2.0#readme",
  "devDependencies": {
    "@eslint/js": "^9.2.0",
    "eslint": "^9.2.0",
    "eslint-plugin-googleappsscript": "^1.0.5",
    "eslint-plugin-jsdoc": "^48.2.3",
    "globals": "^15.1.0"
  }
}

Нічого зайвого, запускай npm install та працюй з кодом…

— Але ж код у нас на Apps Script, його ж не треба ручками буде постійно копіювати?
— Ні, не треба, для цього є консольна утиліта clasp, яка нам як раз допоможе тягати код туди-сюди.

The Apps Script CLI

Далі вас чекає невеличкий переказ офіційної документації, можете відразу по мануалу робити, або навіть пройти відповідну лабу. Тож вибір за вами :)

Почнемо з того, що нам буде потрібно буде встановити nodejs, і вже потім встановлювати clasp глобально:

npm i @google/clasp -g

Якщо все добре, то наступна команда повинна вам вивести перелік доступних команд:

clasp -h

Я особисто ніяк не можу звикнути до назви цієї тулзи, постійно набираю claps ¯\_(ツ)_/¯

Тепер нам треба авторизуватися, виконайте команду login:

clasp login

Далі вам слід буде авторизуватися використовуючи ваш gmail акаунт, та надати доступи до нього. Вам також слід буде ввімкнути Google Apps Script API на сторінці налаштувань:

Enable Google AppScript API

Після цього можна буде подивитися на перелік ваших проєктів:

clasp list

clasp list

Тепер щоб стягнути код до себе використовуйте команду clasp clone та ID вашого проєкту:

clasp clone <scriptID>

clasp clone

Все, тепер ваш код у вас є локально, але з ним відбулись деякі зміни:

# було
actions/actionEditCard.gs
actions/actionEditNotes.gs
commands/slashBender.gs
commands/slashWhisky.gs

# стало
actions/
  |-- actionEditCard.js
  `-- actionEditNotes.js
commands/
  |-- slashBender.js
  `-- slashWhisky.js

Насправді, з теками стало набагато зручніше!

Тепер ви можете вносити зміни до коду, комітити їх до git, а потім заливати в одну команду на script.google.com:

clasp push

clasp push

А тепер прочитайте дуже уважно, та запам’ятайте:

  • коли ви використовуєте команду clasp pull, то всі локальні файли будут замінені на файли з script.google.com
  • коли ви використовуєте команду clasp push, то всі локальні файли будут відправлені до script.google.com, та перетруть всі зміни там

Тож, робимо висновок, що щоб потім не жалітися, то слід редагувати або локально, або безпосередньо на сайті script.google.com, але не треба одночасно і тут, і там вносити зміни, закінчиться тим, що ви щось та загубите.

І ще один момент, до сайту script.google.com будуть залиті лише js-файли та файл appsscript.json, усі інші файли залишаються на вашій відповідальності, тож не забувайте змінити заливати до git.

Підключаємо eslint

Для того щоб перевірити ваш код слід до теки з проєктом додати package.json який я приводив на самому початку та запустити команду npm install:

npm install

Тепер додайте конфігураційний файл самого eslint:

import globals from 'globals';
import pluginJs from '@eslint/js';
import jsdoc from 'eslint-plugin-jsdoc';
import appsScript from 'eslint-plugin-googleappsscript';

export default [
  pluginJs.configs.recommended,
  {
    files: ['**/*.js'],
    plugins: {
      jsdoc,
      googleappsscript: appsScript,
    },
    languageOptions: {
      globals: {
        ...globals.browser,
      },
    },
    rules: {
      'comma-dangle': ['error', 'never'],
      'max-len': ['error', { code: 120, 'ignoreTrailingComments': true  }],
      'camelcase': [
        'error',
        {
          ignoreDestructuring: true,
          ignoreImports: true,
          allow: ['access_type', 'redirect_uris'],
        },
      ],
      'guard-for-in': 'off',
      'no-var': 'off',
      'no-unused-vars': 'off', // Functions aren't used.
      'no-undef': 'off', // Ignore undefined errors for global variables
    },
  }
];

Тепер можна запустити перевірку коде-стайлу вашого коду:

npx eslint

Сподіваюсь, ви не побачити ніяких помилок:

eslint ok

З іншого боку, у цій конфігурації досить лайтові перевірки, тож якщо маєте хист, то відправлйте pull request до мого репозіторію ;)

Структура проєкта

Як я вже писав раніше, в мене в розробці декілька проєктів, тож я намагаюсь розробити зручну структуру для розробки функціоналу. В мене поки виходить наступна структура проєкта:

Google Bot Structure

Така структура дає можливість швидко найти потрібну частину коду. Далі я буду приводити приклади за алфавітом з поясненнями.

Усі екшени потрапили до відповідної теки actions, це те що відбувається коли ви клікаєте по кнопках на картці, там де прописано onClick => action:

actions/
  |-- actionEditCard.js
  |-- ...
  `-- actionEditNotes.js

Усі картки, які ми відправляємо до чату оформлюємо у окремі функції, всі ці функції до окремої теки:

cards/
  |-- cardHome.js
  `-- cardPoll.js

Насправді, бот Bender 2.0 в нас має 5 окремих карток, але деякі з них я не виносив до окремих функцій, хоча можливо це слід було зробити.

Для кожної slash-команди окрема функція, в окремому файлі у теку з усіма іншими slash-командами:

commands/
  |-- slashBender.js
  |-- ...
  `-- slashWhisky.js

Всякі різні допоміжні класи та функції, загалом то тут теж ще слід наводити лад:

components/
  |-- FormInputHandler.js
  |-- ...
  `-- Response.js

Діалоги для взаємодії з користувачами, один файл — один діалог:

dialogs/
  |-- dialogCard.js
  `-- dialogPoll.js

Події, саме тут в нас будуть жити такі важливі функції як onCardClick(), onMessage() та інші:

events/
  |-- onCardClick.js
  |-- ...
  `-- onMessage.js

Допоміжні функції:

helpers/
  `-- htmlEntities.js

Функції, для створення віджетів:

widgets/
  |-- widgetDecoratedText.js
  |-- ...
  `-- widgetTextParagraph.js

Звісно, після того як ви використовуєте команду clasp push, то вся ця структура на script.google.com перетвориться на «плоску» тека/файл.gs, але то вже не так важливо, бо ви вже майже не будете використовувати редагування у браузері.

Source Code

Код бота доступний на GitHub, реліз 7.0.0 відповідає коду з цієї статті.

Bender, реліз 7.0.0
Bender, білд 7.0.0

P.S. Функціонал бота навмисно спрощено відносно релізу 6.0.0, це зроблено задля підготовки до публікації у Google Workspace Market.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.