Останнім часом мені доводиться все більше приймати участь у розробці ботів для 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 на сторінці налаштувань:
Після цього можна буде подивитися на перелік ваших проєктів:
clasp list
Тепер щоб стягнути код до себе використовуйте команду clasp clone
та ID вашого проєкту:
clasp clone <scriptID>
Все, тепер ваш код у вас є локально, але з ним відбулись деякі зміни:
# було 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 pull
, то всі локальні файли будут замінені на файли з script.google.com - коли ви використовуєте команду
clasp push
, то всі локальні файли будут відправлені до script.google.com, та перетруть всі зміни там
Тож, робимо висновок, що щоб потім не жалітися, то слід редагувати або локально, або безпосередньо на сайті script.google.com, але не треба одночасно і тут, і там вносити зміни, закінчиться тим, що ви щось та загубите.
І ще один момент, до сайту script.google.com будуть залиті лише js-файли та файл appsscript.json, усі інші файли залишаються на вашій відповідальності, тож не забувайте змінити заливати до git.
Підключаємо eslint
Для того щоб перевірити ваш код слід до теки з проєктом додати package.json
який я приводив на самому початку та запустити команду 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
Сподіваюсь, ви не побачити ніяких помилок:
З іншого боку, у цій конфігурації досить лайтові перевірки, тож якщо маєте хист, то відправлйте pull request до мого репозіторію ;)
Структура проєкта
Як я вже писав раніше, в мене в розробці декілька проєктів, тож я намагаюсь розробити зручну структуру для розробки функціоналу. В мене поки виходить наступна структура проєкта:
Така структура дає можливість швидко найти потрібну частину коду. Далі я буду приводити приклади за алфавітом з поясненнями.
Усі екшени потрапили до відповідної теки 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 відповідає коду з цієї статті.
P.S. Функціонал бота навмисно спрощено відносно релізу 6.0.0, це зроблено задля підготовки до публікації у Google Workspace Market.