1. Lợi điểm khi sử dụng GAS

Chào các bạn, sau hai tháng kể từ bài viết đầu tiên, Enso đã trở lại với bài viết thứ hai về Google Apps Script (GAS).

Trong bài viết này, Enso sẽ trình bày về cách tạo Slack Bot bằng GAS. Để tạo Bot nói chung hay Slack Bot nói riêng có khá nhiều cách. Trong bài viết Tạo Skype Bot với Microsoft Bot Framework, KYO trình bày về cách tạo Bot với Microsoft Bot Framework. Vậy dùng GAS để tạo Slack Bot có lợi điểm gì? Có thể kể đến những lợi điểm sau:

  • Không cần phải có hosting riêng, tất cả đều hoạt động trên server của Google.
  • Liên kết dễ dàng với các dịch vụ khác của Google (Google Spreadsheet, Google Form, Google Calendar, Gmail,...).
  • Dễ dàng thiết lập trigger (giống như cronjob trong linux hay nodejs).

Enso sẽ sử dụng lại ví dụ của KYO để trình bày cách tạo Slack Bot bằng GAS, đó là ví dụ Quản lý Todo của công ty.

2. Ứng dụng quản lý Todo của công ty

Công ty quản lý các task trên Google Spreadsheet. Bot sẽ có các chức năng chính sau:

  • Liệt kê danh sách các task có trạng thái Active.
  • Thêm một task mới.
  • Cập nhật trạng thái của một task (Active, Done, Cancel).
  • Xoá một task.
  • Vào một thời điểm trong ngày, tự động liệt kê danh sách các task có trạng thái Active.

Trước khi bắt đầu, chúng ta cần chuẩn bị:

  • Một số kiến thức cơ bản về JavaScript.
  • Tài khoản Google.
  • Quyền tích hợp ứng dụng vào Slack team (Chúng ta có thể tạo một team slack hoàn toàn miễn phí tại đây).

3. Các bước thực hiện

3.1. Tạo Google Spreadsheet quản lý Todo

Create-Google-Sheet

  • Đầu tiên chúng ta cần tạo một spreadsheet mới. Các task sẽ được lưu theo từng dòng.
  • Dòng đầu tiên là tên các cột (ID, Created time, Due time, Created user, Task, Participant, Note, Status).
  • Để tiện theo dõi, chúng ta có thể ghim dòng đầu tiên lại bằng View -> Freeze -> 1 row.
  • Tiếp đó chúng ta tạo dữ liệu cho cột Status như sau:
  • Chọn toàn bộ cột Status (cột H)
  • Chọn Data -> Data validation.. -> Criteria: List of items
  • Nhập dữ liệu Active, Done,Cancel
  • Lúc này cell H1 sẽ báo lỗi vì tên cột là Status không hợp lệ. Chọn cell H1, chọn Data -> Data validation.. -> Remove validation

3.2 Tạo script project

Tiếp theo chúng ta sẽ tạo một script project từ Tools -> Script editor.... Sau khi project được tạo, ta đặt tên cho project, đổi tên file thành main.gs, xoá code mặc định được sinh ra và thêm vào dòng code sau:

function doPost(request) {
  var params = request.parameters;

  // MORE CODE WILL GO HERE

  return ContentService.createTextOutput().setMimeType(ContentService.MimeType.JSON);
}

Hãy cùng tìm hiểu các dòng code này.

  • Hàm doPost(request) nhận và xử lý dữ liệu được truyền qua POST request. Sau khi script project được triển khai dưới dạng ứng dụng web, GAS sẽ mặc định gọi hàm này khi nhận được POST request.
  • request.parameters object chứa toàn bộ payload / data của POST request.
  • Câu lệnh return để tránh Slack sẽ báo lỗi The script completed but did not return anything.

Tiếp theo, chúng ta triển khai script project dưới dạng ứng dụng web. Chọn Publish -> Deploy as web app..., các thông số thiết lập như sau:

  • Project version: New
  • Execute the app as: Me
  • Who has access to the app: Anyone, even anonymous

Sau khi triển khai xong, chúng ta sẽ lưu lại Current web app URL để thiết lập ở bước kế tiếp.

3.3 Đăng ký Slack app

Trong nhiều bài hướng dẫn cũ, Outgoing Webhooks được sử dụng để gửi dữ liệu ra bên ngoài. Tuy nhiên hiện tại theo như tài liệu hướng dẫn của Slack, cách này đã cũ và có nhiều hạn chế, không tận dụng được nhiều tính năng. Và hiện tại, Enso tìm mãi mà không còn tìm thấy nơi thiết lập Outgoing Webhooks. Do đó, chúng ta sẽ sử dụng Slack app để triển khai ứng dụng quản lý Todo.

Bước 1: Đăng nhập vào trang quản lý Slack và truy cập vào https://api.slack.com/apps/new để tạo mới ứng dụng
Create-new-slack-app

  • App Name: tuỳ chọn
  • Development Slack Workspace: chọn team mà chúng ta sẽ tích hợp Slack app này vào.

Bước 2: Sau khi bấm nút [Create App], chúng ta sẽ được chuyển tới trang thiết lập thông tin cơ bản (Basic Information) cho Slack App này.

  • Tìm và lưu Verification Token trong mục App Credentials. Token này sẽ được gửi kèm theo dữ liệu tới ứng dụng web. Chúng ta sẽ sử dụng token để kiểm tra xem dữ liệu nhận được có đúng là của Slack app của chúng ta hay không.

Verification-Token

  • Chúng ta cũng có thể cập nhật thêm thông tin cho Slack app ở mục Display Information, ví dụ như thêm app icon, chỉnh background color (chú ý là app icon phải là hình vuông, có kích thước trong khoảng 512px tới 2000px)

App-Icon

Bước 3: Để gửi dữ liệu tới ứng dụng web, thay vì sử dụng Outgoing Webhook, chúng ta sẽ đăng ký Slack command cho Slack app bằng cách chọn Slack Commands trong mục Features và bấm Create New Command.
Create-Slack-Command

  • Command: bắt đầu bởi /
  • Request URL: là địa chỉ nhận POST request khi thực thi Slack command. Ở đây chính là Current web app URL được lưu lại tại mục 3.2
  • Short Description: mô tả ngắn gọn về Slack command
  • Usage Hint: liệt kê các chức năng được hỗ trợ.

Bước 4: Đăng ký Incoming Webhook để nhận tin nhắn từ ứng dụng web bằng cách chọn Incoming Webhooks trong mục Feature, kích hoạt Incoming Webhooks và bấm Add New Webhook to Workspace.
Add-New-Webhook

  • Post to: chọn channel mà Slack app sẽ được tích hợp.

Sau khi tạo Incoming Webhook xong, chúng ta sẽ thu được Webhook URL. URL này được ứng dụng web sử dụng để gửi tin nhắn vào channel của Slack.

Tại thời điểm này, để kiểm tra có thể gửi được tin nhắn thông qua Webhook URL hay không, chúng ta có thể chạy câu lệnh trong Sample curl request to post to a channel.
Webhook URL
Ngoài Incoming WebhooksSlack Commands, Slack app còn hỗ trợ các tính năng khác như Interactive Components, OAuth & Permissions, Event Subscriptions, Bot Users, etc. cùng với kho API phong phú. Tuy nhiên bởi tính năng đơn giản của ứng dụng quản lý Todo, chúng ta không sử dụng đến chúng. Nếu các bạn có ý định phát triển những ứng dụng phức tạp hơn, có thể tham khảo api document hay các tutorial của Slack.

3.4. Cài đặt các chức năng của ứng dụng quản lý Todo

Ứng dụng quản lý Todo sẽ hoạt động như sau. Trong channel của Slack team (ví dụ #todo), các thành viên sử dụng câu lệnh /todo để giao tiếp với ứng dụng web. Để nắm được cách sử dụng câu lệnh, gõ /todo help, ứng dụng web sẽ gửi về channel hướng dẫn như hình bên dưới.
todo-help
todo-commands

Về cài đặt chi tiết, Enso đã đưa code lên trên Github (https://github.com/ensozen/todo-slackapp). Các bạn có thể tham khảo để nắm được rõ hơn logic hoạt động của ứng dụng.

3.5. Một số vấn đề cần chú ý

3.5.1. Project Properties

Với các hằng số, đặc biệt các hằng số lưu thông tin config "nhạy cảm" như Verification Token của Slack app, ID của Spreadsheet file hay Incoming Webhook URL, mặc dù có thể sử dụng trực tiếp trong code nhưng để tiện cho việc chia sẻ code, quản lý và thay đổi về sau, chúng ta nên sử dụng Properties Service của GAS.

Properties service cung cấp cho chúng ta một cơ chế để lưu dữ liệu trong cặp (key-value). Có ba loại properties như sau:

  • Script properties: dữ liệu được chia sẻ giữa tất cả user của một script, một add-on, hay một ứng dụng web. Ví dụ: thông tin config cho toàn bộ ứng dụng web như username, password của database,…
  • User properties: dữ liệu được phân biệt đối với từng user của một script, một add-on, hay một ứng dụng web. Ví dụ: thông tin config của từng user như ngôn ngữ, theme,…
  • Document properties: dữ liệu được chia sẻ giữa tất cả user của một add-on trong một document đang mở. Ví dụ: thông tin config của từng document như URL của đồ thị nhúng trong document.

Đối với ứng dụng quản lý Todo, chúng ta sẽ sử dụng Script properties. Để thêm một property qua giao diện của Script Editor, ta chọn File -> Project properties -> Script properties và bấm [Add row]

Properties

API của Properties Service, các bạn có thể tham khảo tại đây. Tất nhiên ta cũng có thể thêm một property thông qua các API này.

3.5.2. Làm việc với thư viện bên ngoài script project

Trong ứng dụng quản lý Todo, để thao tác, xử lý với dữ liệu date, datetime, Enso sử dụng thư viện momentjs. Đây là một thư viện rất mạnh về xử lý dữ liệu date, datetime. Tuy nhiên điều mà Enso muốn đề cập ở đây không phải là về thư viện này mà là về cách thêm và sử dụng thư viện bên ngoài trong GAS script project. Có hai loại thư viện bên ngoài như sau:

  • Loại thứ nhất: Thư viện là các GAS script project được người khác chia sẻ. Để sử dụng các thư viện này, chúng ta cần phải biết được project key của chúng. Ví dụ, có một thư viện về Slack API được viết cho GAS khá nổi tiếng là SlackApp, nếu chúng ta muốn sử dụng thư viện này, chọn Resources -> Libraries… và nhập project key và bấm [Add]. Sau đó chọn Version muốn sử dụng (hiện tại Version mới nhất là 22). Identifier chính là từ khoá giúp chúng ta gọi tới API của thư viện này từ project của chúng ta.
    Slack API Lib for GAS
    Tuy nhiên theo như khuyến cáo của Google, việc sử dụng các thư viện ngoài như thế này sẽ ảnh hưởng lớn tới tốc độ của ứng dụng, do đó chúng ta nên hạn chế sử dụng nhất có thể. Enso cũng không sử dụng thư viện SlackApp này mà tự viết lại code.

Chi tiết về loại thư viện thứ nhất này, các bạn có thể tham khảo tại đây

  • Loại thứ hai: chính là các thư viện js như momentjs được Enso sử dụng trong project. Để thêm thư viện này vào trong project chúng ta sử dụng hàm eval().

Hàm eval() trong javascript về cơ bản chuyển một string thành code. Bằng cách sử dụng hàm này, chúng ta đã thêm tất cả code của thư viện js vào GAS project, do đó không làm ảnh hưởng tới tốc độ, hiệu năng xử lý của ứng dụng như loại thứ nhất.

eval(UrlFetchApp.fetch('https://momentjs.com/downloads/moment.min.js').getContentText());
3.5.3. Triggers

Triggers là một cơ chế giúp Apps Script thực hiện các hàm một cách tự động khi một sự kiện nào đó xảy ra.

Ví dụ đối với script project triển khai dưới dạng ứng dụng web như ứng dụng quản lý Todo của chúng ta, khi một ứng dụng nào đó gửi một POST request tới thì hàm doPost() sẽ được tự động thực thi.

Đối với ứng dụng quản lý Todo, một yêu cầu được đặt ra là vào các thời điểm nào đó trong ngày (ví dụ 7-8h sáng và 16-17h chiều), ứng dụng sẽ tự động gửi danh sách các task có trạng thái Active đến channel của Slack. Có hai cách để thực hiện: thông qua giao diện của Script Editor và thông qua code.

Cách 1: Chọn Edit -> Current project's triggers và bấm [No triggers set up. Click here to add one now]. Ví dụ để trong khoảng , chúng ta sẽ thêm hai trigger như sau:
triggers

Cách 2: Có thể tạo trigger bằng code. Để tạo hai trigger giống như Cách 1, chúng ta sẽ viết code như trong file trigger.gs

function createTimeDrivenTriggers() {
  // Trigger 07:00 -> 08:00.
  ScriptApp.newTrigger('postTaskList')
      .timeBased()
      .everyDays(1)
      .inTimezone("Asia/Ho_Chi_Minh")
      .atHour(7)
      .create();
  
  // Trigger 16:00 -> 17:00.
  ScriptApp.newTrigger('postTaskList')
      .timeBased()
      .everyDays(1)
      .inTimezone("Asia/Ho_Chi_Minh")
      .atHour(16)
      .create();
}

Sau đó chọn Run -> Run function -> createTimeDrivenTriggers, hai trigger sẽ được tạo ra.

Các bạn có thể tham khảo tại đây để biết thêm chi tiết.

4. Kết luận

Qua ứng dụng quản lý Todo của công ty, chúng ta đã nắm được cách tạo một ứng dụng trên Slack và cách kết nối ứng dụng Slack này với ứng dụng web được tạo ra bằng GAS. Trong đó doPost() là hai hàm đóng vai trò rất quan trọng. Ngoài doPost() còn có doGet(), với hai hàm này, chúng ta có thể triển khai một web service có hỗ trợ RESTful API mà lại hoàn toàn miễn phí. Hai bài viết của Enso với hai ví dụ chỉ là một phần rất nhỏ trong vô vàn ứng dụng mà GAS có thể đem lại. Nếu có ứng dụng nào hay, hy vọng các bạn chia sẻ lại với Enso :) Trong phần 3, cũng là phần cuối cùng trong sê-ri bài viết "Nghịch cùng Google Apps Script" này, Enso sẽ trình bày về cách sử dụng GAS để chia sẻ, cung cấp Slack app Todo này với các team khác trong cộng đồng người dùng Slack. Hẹn gặp lại các bạn vào một ngày gần nhất :)

Tài liệu tham khảo

P/S

Phiên bản code trong Github todo-slackapp có thêm chức năng user mention trong danh sách "Người thực hiện". Chức năng này sẽ giúp những thành viên chịu trách nhiệm các task chú ý hơn.
User-Mention

Chức năng này sẽ dựa vào tên của các thành viên trong danh sách "Người thực hiện" để tìm kiếm ID của thành viên và mention tới thành viên bằng ID này. Để chức năng này hoạt động được,

  • Chúng ta phải cho phép Slack App truy cập thông tin của các thành viên trong team bằng cách truy cập vào OAuth && Permissions trong Features, tìm tới mục Scopes và thêm vào quyền Access your workspace’s profile information (Slack sẽ yêu cầu cài lại app để cập nhật quyền mới)
    Scopes

  • Để lấy được thông tin tất cả các thành viên trong team, chúng ta sẽ sử dụng API users.list trong Slack Web API cùng với OAuth Access Token. Các bạn sẽ tìm thấy token này trong mục OAuth & Permissions
    Authen-Token

  • Sau khi lấy được, chúng ta cần thêm OAuth Access Token vào Script Properties của GAS script project
    thông qua menu bar của Script Editor: File -> Project properties -> Script properties

Have fun :)