2018-04-21 02:59:30 -03:00
|
|
|
/*****************************************************************************\
|
|
|
|
| ( )_ _ |
|
|
|
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
2020-07-01 15:00:40 -03:00
|
|
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
|
2019-03-09 16:59:31 -03:00
|
|
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
2018-04-21 02:59:30 -03:00
|
|
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
|
|
| | | ( )_) | |
|
|
|
|
| (_) \___/' |
|
|
|
|
| |
|
|
|
|
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
|
|
|
|
| Licensed under the AGPL-3.0. |
|
2018-11-11 19:09:18 -02:00
|
|
|
| |
|
2018-04-21 02:59:30 -03:00
|
|
|
| According to our dual licensing model, this program can be used either |
|
|
|
|
| under the terms of the GNU Affero General Public License, version 3, |
|
|
|
|
| or under a proprietary license. |
|
|
|
|
| |
|
|
|
|
| The texts of the GNU Affero General Public License with an additional |
|
|
|
|
| permission and of our proprietary license can be found at and |
|
|
|
|
| in the LICENSE file you have received along with this program. |
|
|
|
|
| |
|
|
|
|
| This program is distributed in the hope that it will be useful, |
|
2018-09-11 19:40:53 -03:00
|
|
|
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
|
2018-04-21 02:59:30 -03:00
|
|
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
|
| GNU Affero General Public License for more details. |
|
|
|
|
| |
|
|
|
|
| "General Bots" is a registered trademark of Pragmatismo.io. |
|
|
|
|
| The licensing of the program under the AGPLv3 does not imply a |
|
|
|
|
| trademark license. Therefore any rights, title and interest in |
|
|
|
|
| our trademarks remain entirely with us. |
|
|
|
|
| |
|
|
|
|
\*****************************************************************************/
|
2018-11-12 12:20:44 -02:00
|
|
|
'use strict';
|
2019-03-08 19:13:00 -03:00
|
|
|
import { GBLog, GBMinInstance } from 'botlib';
|
2021-01-15 08:46:28 -03:00
|
|
|
import { CollectionUtil } from 'pragmatismo-io-framework';
|
2019-03-08 06:37:13 -03:00
|
|
|
import * as request from 'request-promise-native';
|
2019-03-09 16:59:31 -03:00
|
|
|
import urlJoin = require('url-join');
|
2019-02-23 13:17:21 -03:00
|
|
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
2020-12-27 13:30:56 -03:00
|
|
|
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
2020-12-01 18:01:53 -03:00
|
|
|
import { SecService } from '../../security.gbapp/services/SecService';
|
2020-05-11 10:41:41 -03:00
|
|
|
const request = require('request-promise-native');
|
2020-12-13 10:02:49 -03:00
|
|
|
const MicrosoftGraph = require('@microsoft/microsoft-graph-client');
|
2021-01-15 08:46:28 -03:00
|
|
|
const path = require('path');
|
2019-02-25 08:36:43 -03:00
|
|
|
|
2019-03-09 16:59:31 -03:00
|
|
|
/**
|
|
|
|
* @fileoverview General Bots server core.
|
|
|
|
*/
|
2020-12-28 18:43:34 -03:00
|
|
|
|
2021-01-15 08:46:28 -03:00
|
|
|
/**
|
|
|
|
* BASIC system class for extra manipulation of bot behaviour.
|
|
|
|
*/
|
2020-12-27 13:30:56 -03:00
|
|
|
export class SystemKeywords {
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Reference to minimal bot instance.
|
|
|
|
*/
|
2019-02-19 15:30:07 -03:00
|
|
|
public min: GBMinInstance;
|
2020-12-27 13:30:56 -03:00
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Reference to the deployer service.
|
|
|
|
*/
|
2019-03-08 06:37:13 -03:00
|
|
|
private readonly deployer: GBDeployer;
|
2019-02-19 15:30:07 -03:00
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* When creating this keyword facade, a bot instance is
|
|
|
|
* specified among the deployer service.
|
|
|
|
*/
|
2019-03-08 06:37:13 -03:00
|
|
|
constructor(min: GBMinInstance, deployer: GBDeployer) {
|
2019-02-19 15:30:07 -03:00
|
|
|
this.min = min;
|
2019-03-08 06:37:13 -03:00
|
|
|
this.deployer = deployer;
|
2019-02-19 15:30:07 -03:00
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Retrives token and initialize drive client API.
|
|
|
|
*/
|
2020-12-27 13:30:56 -03:00
|
|
|
private async internalGetDriveClient() {
|
|
|
|
let token = await this.min.adminService.acquireElevatedToken(this.min.instance.instanceId);
|
|
|
|
let siteId = process.env.STORAGE_SITE_ID;
|
|
|
|
let libraryId = process.env.STORAGE_LIBRARY;
|
|
|
|
|
|
|
|
let client = MicrosoftGraph.Client.init({
|
|
|
|
authProvider: done => {
|
|
|
|
done(null, token);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
const baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}`;
|
2021-01-12 18:33:21 -03:00
|
|
|
return [baseUrl, client];
|
2020-12-27 13:30:56 -03:00
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Retrives the content of a given URL.
|
|
|
|
*/
|
2020-05-11 10:41:41 -03:00
|
|
|
public async getFileContents(url) {
|
|
|
|
const options = {
|
|
|
|
url: url,
|
|
|
|
method: 'GET',
|
|
|
|
encoding: 'binary'
|
|
|
|
};
|
2020-12-14 09:28:12 -03:00
|
|
|
const res = await request(options);
|
2020-12-27 13:30:56 -03:00
|
|
|
Buffer.from(res, 'binary').toString();
|
2020-05-11 10:41:41 -03:00
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Retrives a random id with a length of five, every time it is called.
|
|
|
|
*/
|
2020-05-11 10:41:41 -03:00
|
|
|
public async getRandomId() {
|
|
|
|
return GBAdminService.getRndReadableIdentifier().substr(5);
|
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Retrives stock inforation for a given symbol.
|
|
|
|
*/
|
2020-05-11 10:41:41 -03:00
|
|
|
public async getStock(symbol) {
|
|
|
|
var options = {
|
|
|
|
uri: `http://live-nse.herokuapp.com/?symbol=${symbol}`
|
|
|
|
};
|
|
|
|
|
|
|
|
let data = await request.get(options);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Prepares the next dialog to be shown to the specified user.
|
|
|
|
*/
|
2020-12-01 18:01:53 -03:00
|
|
|
public async gotoDialog(from: string, dialogName: string) {
|
|
|
|
let sec = new SecService();
|
|
|
|
let user = await sec.getUserFromSystemId(from);
|
|
|
|
if (!user) {
|
|
|
|
user = await sec.ensureUser(this.min.instance.instanceId, from, from, null, 'whatsapp', 'from');
|
|
|
|
}
|
|
|
|
await sec.updateUserHearOnDialog(user.userId, dialogName);
|
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Holds script execution for the number of seconds specified.
|
|
|
|
*
|
|
|
|
* @example WAIT 5 ' This will wait five seconds.
|
|
|
|
*
|
|
|
|
*/
|
2019-02-25 08:36:43 -03:00
|
|
|
public async wait(seconds: number) {
|
2019-03-08 06:37:13 -03:00
|
|
|
// tslint:disable-next-line no-string-based-set-timeout
|
2020-11-08 13:39:18 -03:00
|
|
|
GBLog.info(`BASIC: Talking to a specific user (TALK TO).`);
|
2019-03-08 06:37:13 -03:00
|
|
|
const timeout = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
2019-02-25 08:36:43 -03:00
|
|
|
await timeout(seconds * 1000);
|
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Sends a text message to the mobile number specified.
|
|
|
|
*
|
|
|
|
* @example TALK TO "+199988887777", "Message text here"
|
|
|
|
*
|
|
|
|
*/
|
2020-11-08 13:39:18 -03:00
|
|
|
public async talkTo(mobile: any, message: string) {
|
|
|
|
GBLog.info(`BASIC: Talking '${message}' to a specific user (${mobile}) (TALK TO). `);
|
|
|
|
await this.min.conversationalService.sendMarkdownToMobile(this.min, null, mobile, message);
|
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Sends a SMS message to the mobile number specified.
|
|
|
|
*
|
|
|
|
* @example SEND SMS TO "+199988887777", "Message text here"
|
|
|
|
*
|
|
|
|
*/
|
2020-11-26 12:45:10 -03:00
|
|
|
public async sendSmsTo(mobile, message) {
|
|
|
|
await this.min.conversationalService.sendSms(this.min, mobile, message);
|
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Defines a cell value in the tabular file.
|
|
|
|
*
|
|
|
|
* @example SET "file.xlsx", "A2", 4500
|
|
|
|
*
|
|
|
|
*/
|
2020-10-14 13:43:58 -03:00
|
|
|
public async set(file: string, address: string, value: any): Promise<any> {
|
2020-11-08 13:39:18 -03:00
|
|
|
GBLog.info(`BASIC: Defining '${address}' in '${file}' to '${value}' (SET). `);
|
2020-08-15 11:39:43 -03:00
|
|
|
|
2020-12-27 13:30:56 -03:00
|
|
|
let [baseUrl, client] = await this.internalGetDriveClient();
|
2020-08-15 11:39:43 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
const botId = this.min.instance.botId;
|
|
|
|
const path = `/${botId}.gbai/${botId}.gbdata`;
|
2020-08-15 11:39:43 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
address = address.indexOf(':') !== -1 ? address : address + ":" + address;
|
2020-08-15 11:39:43 -03:00
|
|
|
|
2020-12-27 13:30:56 -03:00
|
|
|
let document = await this.internalGetDocument(client, baseUrl, path, file);
|
2020-10-14 13:43:58 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
let body = { values: [[]] };
|
|
|
|
body.values[0][0] = value;
|
2020-08-15 11:39:43 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
let sheets = await client
|
2020-12-27 13:30:56 -03:00
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`)
|
2020-12-14 09:28:12 -03:00
|
|
|
.get();
|
|
|
|
|
|
|
|
await client
|
2020-12-27 13:30:56 -03:00
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`)
|
2020-12-14 09:28:12 -03:00
|
|
|
.patch(body);
|
2020-08-15 11:39:43 -03:00
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Retrives a document from the drive, given a path and filename.
|
|
|
|
*/
|
2020-12-27 13:30:56 -03:00
|
|
|
private async internalGetDocument(client: any, baseUrl: any, path: string, file: string) {
|
|
|
|
let res = await client
|
|
|
|
.api(`${baseUrl}/drive/root:${path}:/children`)
|
|
|
|
.get();
|
2020-05-11 10:41:41 -03:00
|
|
|
|
2020-12-27 13:30:56 -03:00
|
|
|
let documents = res.value.filter(m => {
|
|
|
|
return m.name.toLowerCase() === file.toLowerCase();
|
2020-12-14 09:28:12 -03:00
|
|
|
});
|
2020-12-27 13:30:56 -03:00
|
|
|
|
|
|
|
if (!documents || documents.length === 0) {
|
|
|
|
throw `File '${file}' specified on GBasic command not found. Check the .gbdata or the .gbdialog associated.`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return documents[0];
|
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Saves the content of several variables to a new row in a tabular file.
|
|
|
|
*
|
|
|
|
* @exaple SAVE "customers.xlsx", name, email, phone, address, city, state, country
|
|
|
|
*
|
|
|
|
*/
|
2020-12-27 13:30:56 -03:00
|
|
|
public async save(file: string, ...args): Promise<any> {
|
|
|
|
GBLog.info(`BASIC: Saving '${file}' (SAVE). Args: ${args.join(',')}.`);
|
|
|
|
let [baseUrl, client] = await this.internalGetDriveClient();
|
2020-12-14 09:28:12 -03:00
|
|
|
const botId = this.min.instance.botId;
|
|
|
|
const path = `/${botId}.gbai/${botId}.gbdata`;
|
2020-05-11 10:41:41 -03:00
|
|
|
|
2020-12-27 13:30:56 -03:00
|
|
|
let document = await this.internalGetDocument(client, baseUrl, path, file);
|
|
|
|
let sheets = await client
|
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`)
|
2020-12-14 09:28:12 -03:00
|
|
|
.get();
|
2020-05-11 10:41:41 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
await client
|
2020-12-27 13:30:56 -03:00
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A2:DX2')/insert`)
|
2020-12-14 09:28:12 -03:00
|
|
|
.post({});
|
2020-05-11 10:41:41 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
if (args.length > 128) {
|
|
|
|
throw `File '${file}' has a SAVE call with more than 128 arguments. Check the .gbdialog associated.`;
|
|
|
|
}
|
2020-05-11 10:41:41 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
let body = { values: [[]] };
|
|
|
|
for (let index = 0; index < 128; index++) {
|
|
|
|
body.values[0][index] = args[index];
|
|
|
|
}
|
2020-12-27 13:30:56 -03:00
|
|
|
await client
|
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A2:DX2')`)
|
2020-12-14 09:28:12 -03:00
|
|
|
.patch(body);
|
2020-05-11 10:41:41 -03:00
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Retrives the content of a cell in a tabular file.
|
|
|
|
*
|
|
|
|
* @example value = GET "file.xlsx", "A2"
|
|
|
|
*
|
|
|
|
*/
|
2020-08-15 11:39:43 -03:00
|
|
|
public async get(file: string, address: string): Promise<any> {
|
2021-02-28 12:26:44 -03:00
|
|
|
GBLog.info(`BASIC: GET '${address}' in '${file}'.`);
|
2020-12-27 13:30:56 -03:00
|
|
|
let [baseUrl, client] = await this.internalGetDriveClient();
|
2020-05-14 12:47:46 -03:00
|
|
|
const botId = this.min.instance.botId;
|
2020-05-14 17:16:27 -03:00
|
|
|
const path = `/${botId}.gbai/${botId}.gbdata`;
|
2020-05-14 12:47:46 -03:00
|
|
|
|
2020-12-27 13:30:56 -03:00
|
|
|
let document = await this.internalGetDocument(client, baseUrl, path, file);
|
2020-08-15 11:39:43 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
// Creates workbook session that will be discarded.
|
2021-01-15 08:46:28 -03:00
|
|
|
|
2020-12-27 13:30:56 -03:00
|
|
|
let sheets = await client
|
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`)
|
|
|
|
.get();
|
2020-08-15 11:39:43 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
let results = await client
|
2020-12-27 13:30:56 -03:00
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`)
|
2020-12-14 09:28:12 -03:00
|
|
|
.get();
|
2020-08-15 11:39:43 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
let val = results.text[0][0];
|
|
|
|
GBLog.info(`BASIC: Getting '${file}' (GET). Value= ${val}.`);
|
|
|
|
return val;
|
2020-08-15 11:39:43 -03:00
|
|
|
}
|
2020-11-08 13:39:18 -03:00
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Finds a value or multi-value results in a tabular file.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* rows = FIND "file.xlsx", "A2=active"
|
|
|
|
* i = 1
|
|
|
|
* do while i < ubound(row)
|
|
|
|
* row = rows[i]
|
|
|
|
* send sms to "+" + row.mobile, "Hello " + row.namee + "! "
|
|
|
|
* loop
|
|
|
|
*
|
|
|
|
*/
|
2021-02-28 12:26:44 -03:00
|
|
|
public async find(file: string, ...args ): Promise<any> {
|
|
|
|
GBLog.info(`BASIC: FIND running on ${file}...`);
|
2020-12-27 13:30:56 -03:00
|
|
|
let [baseUrl, client] = await this.internalGetDriveClient();
|
2020-11-08 13:39:18 -03:00
|
|
|
const botId = this.min.instance.botId;
|
|
|
|
const path = `/${botId}.gbai/${botId}.gbdata`;
|
|
|
|
|
2020-12-27 13:30:56 -03:00
|
|
|
let document = await this.internalGetDocument(client, baseUrl, path, file);
|
2020-11-08 13:39:18 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
if (args.length > 1) {
|
|
|
|
throw `File '${file}' has a FIND call with more than 1 arguments. Check the .gbdialog associated.`;
|
|
|
|
}
|
2020-11-08 13:39:18 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
// Creates workbook session that will be discarded.
|
|
|
|
const filter = args[0].split('=');
|
|
|
|
const columnName = filter[0];
|
|
|
|
const value = filter[1];
|
2020-12-27 13:30:56 -03:00
|
|
|
let sheets = await client
|
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`)
|
|
|
|
.get();
|
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
let results = await client
|
2020-12-27 13:30:56 -03:00
|
|
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:Z100')`)
|
2020-12-14 09:28:12 -03:00
|
|
|
.get();
|
|
|
|
|
|
|
|
let columnIndex = 0;
|
|
|
|
const header = results.text[0];
|
|
|
|
for (; columnIndex < header.length; columnIndex++) {
|
|
|
|
if (header[columnIndex].toLowerCase() === columnName.toLowerCase()) {
|
|
|
|
break;
|
2020-11-08 13:39:18 -03:00
|
|
|
}
|
2020-12-14 09:28:12 -03:00
|
|
|
}
|
2020-11-08 13:39:18 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
// As BASIC uses arrays starting with 1 (one) as index,
|
|
|
|
// a ghost element is added at 0 (zero) position.
|
|
|
|
let array = [];
|
|
|
|
array.push({ 'this is a base 1': 'array' });
|
|
|
|
let foundIndex = 0;
|
|
|
|
for (; foundIndex < results.text.length; foundIndex++) {
|
|
|
|
// Filter results action.
|
|
|
|
if (results.text[foundIndex][columnIndex].toLowerCase() === value.toLowerCase()) {
|
|
|
|
let output = {};
|
|
|
|
const row = results.text[foundIndex];
|
|
|
|
for (let colIndex = 0; colIndex < row.length; colIndex++) {
|
|
|
|
output[header[colIndex]] = row[colIndex];
|
2020-11-08 13:39:18 -03:00
|
|
|
}
|
2020-12-14 09:28:12 -03:00
|
|
|
output['line'] = foundIndex + 1;
|
|
|
|
array.push(output);
|
2020-11-08 13:39:18 -03:00
|
|
|
}
|
2020-12-14 09:28:12 -03:00
|
|
|
}
|
2020-11-08 13:39:18 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
if (array.length === 1) {
|
2020-12-28 09:27:35 -03:00
|
|
|
GBLog.info(`BASIC: FIND the data set is EMPTY (zero results).`);
|
2020-12-14 09:28:12 -03:00
|
|
|
return null;
|
|
|
|
} else if (array.length === 2) {
|
|
|
|
GBLog.info(`BASIC: FIND single result: ${array[0]}.`);
|
|
|
|
return array[1];
|
|
|
|
} else {
|
|
|
|
GBLog.info(`BASIC: FIND multiple result count: ${array.length}.`);
|
|
|
|
return array;
|
|
|
|
}
|
2020-12-13 10:02:49 -03:00
|
|
|
}
|
|
|
|
|
2021-01-15 19:21:27 -03:00
|
|
|
|
|
|
|
|
|
|
|
|
2020-12-13 10:02:49 -03:00
|
|
|
/**
|
2020-12-28 09:27:35 -03:00
|
|
|
* Creates a folder in the bot instance drive.
|
2020-12-27 13:30:56 -03:00
|
|
|
*
|
2020-12-28 09:27:35 -03:00
|
|
|
* @example folder = CREATE FOLDER "notes\01"
|
2020-12-27 13:30:56 -03:00
|
|
|
*
|
2020-12-13 10:02:49 -03:00
|
|
|
*/
|
|
|
|
public async createFolder(name: string) {
|
|
|
|
|
2020-12-27 13:30:56 -03:00
|
|
|
let [baseUrl, client] = await this.internalGetDriveClient();
|
2020-12-13 10:02:49 -03:00
|
|
|
const botId = this.min.instance.botId;
|
2021-01-15 08:46:28 -03:00
|
|
|
let path = `/${botId}.gbai/${botId}.gbdata`;
|
|
|
|
|
|
|
|
// Extracts each part of path to call create folder to each
|
|
|
|
// one of them.
|
|
|
|
|
|
|
|
name = name.replace(/\\/gi, '/');
|
|
|
|
const parts = name.split('/');
|
|
|
|
let lastFolder = null;
|
|
|
|
|
2021-01-15 11:48:18 -03:00
|
|
|
// Creates each subfolder.
|
|
|
|
|
2021-01-15 08:46:28 -03:00
|
|
|
await CollectionUtil.asyncForEach(parts, async item => {
|
|
|
|
|
2021-01-15 11:48:18 -03:00
|
|
|
// Calls drive API.
|
2020-12-13 10:02:49 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
const body = {
|
2021-01-15 11:48:18 -03:00
|
|
|
"name": item,
|
2020-12-14 09:28:12 -03:00
|
|
|
"folder": {},
|
2021-01-15 11:48:18 -03:00
|
|
|
"@microsoft.graph.conflictBehavior": "fail"
|
2020-12-27 13:30:56 -03:00
|
|
|
};
|
2021-01-15 11:48:18 -03:00
|
|
|
|
|
|
|
try {
|
|
|
|
lastFolder = await client
|
|
|
|
.api(`${baseUrl}/drive/root:/${path}:/children`)
|
|
|
|
.post(body);
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
if (error.code !== "nameAlreadyExists") {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lastFolder = await client
|
|
|
|
.api(`${baseUrl}/drive/root:/${urlJoin(path, item)}`)
|
|
|
|
.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Increments path to the next child be created.
|
|
|
|
|
|
|
|
path = urlJoin(path, item);
|
2020-12-14 09:28:12 -03:00
|
|
|
});
|
2021-01-15 08:46:28 -03:00
|
|
|
return lastFolder;
|
2020-12-13 10:02:49 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-12-28 09:27:35 -03:00
|
|
|
* Shares a folder from the drive to a e-mail recipient.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
*
|
2020-12-13 10:02:49 -03:00
|
|
|
* folder = CREATE FOLDER "notes\10"
|
|
|
|
* SHARE FOLDER folder, "nome@domain.com", "E-mail message"
|
2020-12-27 13:30:56 -03:00
|
|
|
*
|
2020-12-13 10:02:49 -03:00
|
|
|
*/
|
|
|
|
public async shareFolder(folderReference, email: string, message: string) {
|
2020-12-27 13:30:56 -03:00
|
|
|
let [, client] = await this.internalGetDriveClient();
|
2020-12-13 10:02:49 -03:00
|
|
|
const driveId = folderReference.parentReference.driveId;
|
|
|
|
const itemId = folderReference.id;
|
2021-01-15 11:48:18 -03:00
|
|
|
const body = {
|
|
|
|
"recipients": [{ "email": email }],
|
|
|
|
"message": message,
|
|
|
|
"requireSignIn": true,
|
|
|
|
"sendInvitation": true,
|
|
|
|
"roles": ["write"]
|
|
|
|
};
|
2020-12-13 10:02:49 -03:00
|
|
|
|
2021-01-15 11:48:18 -03:00
|
|
|
await client
|
|
|
|
.api(`https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/invite`)
|
|
|
|
.post(body);
|
2020-12-13 10:02:49 -03:00
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Copies a drive file from a place to another .
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* COPY "template.xlsx", "reports\" + customerName + "\final.xlsx"
|
|
|
|
*
|
|
|
|
*/
|
2021-01-15 08:46:28 -03:00
|
|
|
public async copyFile(src, dest) {
|
2021-02-28 12:26:44 -03:00
|
|
|
GBLog.info(`BASIC: BEGINING COPY '${src}' to '${dest}'`);
|
2021-01-15 08:46:28 -03:00
|
|
|
let [baseUrl, client] = await this.internalGetDriveClient();
|
|
|
|
const botId = this.min.instance.botId;
|
2021-01-15 11:48:18 -03:00
|
|
|
|
|
|
|
// Normalizes all slashes.
|
|
|
|
|
|
|
|
src = src.replace(/\\/gi, '/');
|
|
|
|
dest = dest.replace(/\\/gi, '/');
|
|
|
|
|
|
|
|
// Determines full path at source and destination.
|
|
|
|
|
2021-01-15 08:46:28 -03:00
|
|
|
const root = urlJoin(`/${botId}.gbai/${botId}.gbdata`);
|
|
|
|
const srcPath = urlJoin(root, src);
|
|
|
|
const dstPath = urlJoin(`/${botId}.gbai/${botId}.gbdata`, dest);
|
2021-01-15 11:48:18 -03:00
|
|
|
|
|
|
|
// Checks if the destination contains subfolders that
|
|
|
|
// need to be created.
|
|
|
|
|
2021-01-15 08:46:28 -03:00
|
|
|
let folder;
|
2021-01-15 11:48:18 -03:00
|
|
|
if (dest.indexOf('/') !== -1) {
|
2021-01-15 08:46:28 -03:00
|
|
|
const pathOnly = path.dirname(dest);
|
|
|
|
folder = await this.createFolder(pathOnly);
|
|
|
|
}
|
2021-01-15 11:48:18 -03:00
|
|
|
else {
|
2021-01-15 08:46:28 -03:00
|
|
|
folder = await client.api(
|
|
|
|
`${baseUrl}/drive/root:/${root}`)
|
|
|
|
.get();
|
|
|
|
}
|
|
|
|
|
2021-01-15 11:48:18 -03:00
|
|
|
// Performs the copy operation getting a reference
|
|
|
|
// to the source and calling /copy on drive API.
|
|
|
|
|
2021-01-15 08:46:28 -03:00
|
|
|
try {
|
|
|
|
const srcFile = await client.api(
|
|
|
|
`${baseUrl}/drive/root:/${srcPath}`)
|
|
|
|
.get();
|
2021-01-15 11:48:18 -03:00
|
|
|
const destFile = {
|
2021-01-15 08:46:28 -03:00
|
|
|
"parentReference": { driveId: folder.parentReference.driveId, id: folder.id },
|
2021-01-15 11:48:18 -03:00
|
|
|
"name": `${path.basename(dest)}`
|
2021-01-15 08:46:28 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
return await client.api(
|
|
|
|
`${baseUrl}/drive/items/${srcFile.id}/copy`)
|
|
|
|
.post(destFile);
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
if (error.code === "itemNotFound") {
|
|
|
|
GBLog.info(`BASIC: COPY source file not found: ${srcPath}.`);
|
|
|
|
} else if (error.code === "nameAlreadyExists") {
|
|
|
|
GBLog.info(`BASIC: COPY destination file already exists: ${dstPath}.`);
|
|
|
|
}
|
|
|
|
throw error;
|
|
|
|
}
|
2021-02-28 12:26:44 -03:00
|
|
|
GBLog.info(`BASIC: FINISHED COPY '${src}' to '${dest}'`);
|
2020-11-08 13:39:18 -03:00
|
|
|
}
|
|
|
|
|
2021-01-15 19:21:27 -03:00
|
|
|
/**
|
|
|
|
* Converts a drive file from a place to another .
|
|
|
|
*
|
|
|
|
* Supported sources csv, doc, docx, odp, ods, odt, pot, potm, potx, pps,
|
|
|
|
* ppsx, ppsxm, ppt, pptm, pptx, rtf, xls, xlsx
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* CONVERT "customers.xlsx" TO "reports\" + today + ".pdf"
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public async convert(src, dest) {
|
2021-01-28 08:45:29 -03:00
|
|
|
GBLog.info(`BASIC: CONVERT '${src}' to '${dest}'`);
|
2021-01-15 19:21:27 -03:00
|
|
|
let [baseUrl, client] = await this.internalGetDriveClient();
|
|
|
|
const botId = this.min.instance.botId;
|
|
|
|
|
|
|
|
// Normalizes all slashes.
|
|
|
|
|
|
|
|
src = src.replace(/\\/gi, '/');
|
|
|
|
dest = dest.replace(/\\/gi, '/');
|
|
|
|
|
|
|
|
// Determines full path at source and destination.
|
|
|
|
|
|
|
|
const root = urlJoin(`/${botId}.gbai/${botId}.gbdata`);
|
|
|
|
const srcPath = urlJoin(root, src);
|
|
|
|
const dstPath = urlJoin(`/${botId}.gbai/${botId}.gbdata`, dest);
|
|
|
|
|
|
|
|
// Checks if the destination contains subfolders that
|
|
|
|
// need to be created.
|
|
|
|
|
|
|
|
if (dest.indexOf('/') !== -1) {
|
|
|
|
const pathOnly = path.dirname(dest);
|
2021-02-02 15:37:35 -03:00
|
|
|
await this.createFolder(pathOnly);
|
2021-01-15 19:21:27 -03:00
|
|
|
}
|
2021-02-02 15:37:35 -03:00
|
|
|
|
2021-01-15 19:21:27 -03:00
|
|
|
// Performs the conversion operation getting a reference
|
|
|
|
// to the source and calling /content on drive API.
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const res = await client
|
|
|
|
.api(`${baseUrl}/drive/root:/${srcPath}:/content?format=pdf`)
|
|
|
|
.get();
|
|
|
|
|
|
|
|
const streamToString = (stream) => {
|
|
|
|
const chunks = []
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
stream.on('data', chunk => chunks.push(chunk))
|
|
|
|
stream.on('error', reject)
|
|
|
|
stream.on('end', () => resolve(Buffer.concat(chunks)))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = await streamToString(res);
|
|
|
|
|
|
|
|
await client
|
|
|
|
.api(`${baseUrl}/drive/root:/${dstPath}:/content`)
|
|
|
|
.put(result);
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
if (error.code === "itemNotFound") {
|
|
|
|
GBLog.info(`BASIC: CONVERT source file not found: ${srcPath}.`);
|
|
|
|
} else if (error.code === "nameAlreadyExists") {
|
|
|
|
GBLog.info(`BASIC: CONVERT destination file already exists: ${dstPath}.`);
|
|
|
|
}
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-28 09:27:35 -03:00
|
|
|
/**
|
|
|
|
* Generate a secure and unique password.
|
|
|
|
*
|
|
|
|
* @example pass = PASSWORD
|
|
|
|
*
|
|
|
|
*/
|
2019-02-25 08:36:43 -03:00
|
|
|
public generatePassword() {
|
2019-02-23 13:17:21 -03:00
|
|
|
return GBAdminService.getRndPassword();
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:37:13 -03:00
|
|
|
/**
|
2020-12-28 09:27:35 -03:00
|
|
|
* Sends an e-mail.
|
|
|
|
*
|
|
|
|
* @example
|
2019-03-08 06:37:13 -03:00
|
|
|
*/
|
|
|
|
public async sendEmail(to, subject, body) {
|
|
|
|
// tslint:disable-next-line:no-console
|
2019-03-08 19:13:00 -03:00
|
|
|
GBLog.info(`[E-mail]: to:${to}, subject: ${subject}, body: ${body}.`);
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-12-28 09:27:35 -03:00
|
|
|
* Calls any REST API by using GET HTTP method.
|
|
|
|
*
|
|
|
|
* @example user = get "http://server/users/1"
|
|
|
|
*
|
2019-03-08 06:37:13 -03:00
|
|
|
*/
|
2020-12-27 13:30:56 -03:00
|
|
|
public async getByHttp(url: string) {
|
2020-08-15 11:39:43 -03:00
|
|
|
const options = {
|
|
|
|
uri: url
|
|
|
|
};
|
|
|
|
|
|
|
|
let result = await request.get(options);
|
|
|
|
GBLog.info(`[GET]: ${url} : ${result}`);
|
|
|
|
return JSON.parse(result);
|
|
|
|
}
|
2019-03-08 06:37:13 -03:00
|
|
|
|
2020-08-15 11:39:43 -03:00
|
|
|
/**
|
2020-12-28 09:27:35 -03:00
|
|
|
* Calls any REST API by using POST HTTP method.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* user = post "http://server/path", "data"
|
|
|
|
* talk "The updated user area is" + user.area
|
|
|
|
*
|
2020-08-15 11:39:43 -03:00
|
|
|
*/
|
2020-12-27 13:30:56 -03:00
|
|
|
public async postByHttp(url: string, data) {
|
2019-03-08 06:37:13 -03:00
|
|
|
const options = {
|
2020-08-15 11:39:43 -03:00
|
|
|
uri: url,
|
|
|
|
json: true,
|
|
|
|
body: data
|
2019-03-08 06:37:13 -03:00
|
|
|
};
|
|
|
|
|
2020-08-15 11:39:43 -03:00
|
|
|
let result = await request.post(options);
|
|
|
|
GBLog.info(`[POST]: ${url} (${data}): ${result}`);
|
|
|
|
return JSON.parse(result);
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
|
|
|
|
2020-05-14 12:47:46 -03:00
|
|
|
public async numberOnly(text: string) {
|
2020-08-15 11:39:43 -03:00
|
|
|
return text.replace(/\D/gi, '');
|
2020-05-14 12:47:46 -03:00
|
|
|
}
|
2019-02-19 15:30:07 -03:00
|
|
|
}
|