refactor(GBOService, MainModel): clean up code and enhance model structure for better clarity and maintainability
All checks were successful
GBCI / build (push) Successful in 1m30s

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-04-21 22:29:26 -03:00
parent 9e08cb5e64
commit e3ac4f58b3
7 changed files with 248 additions and 538 deletions

View file

@ -459,6 +459,7 @@ export class GBMinService {
this.createCheckHealthAddress(GBServer.globals.server, min, min.instance);
// Setups official handler for WhatsApp.
GBServer.globals.server

219
packages/saas.gbapp/dialog/NewUserDialog.ts Executable file → Normal file
View file

@ -1,36 +1,4 @@
// BotServer/packages/saas.gbapp/dialog/NewUserDialog.ts
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| 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, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| 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.com.br. |
| 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. |
| |
\*****************************************************************************/
'use strict';
import { IGBDialog, GBMinInstance } from 'botlib';
import { Messages } from '../strings.js';
import { MainService } from '../service/MainService.js';
@ -44,22 +12,22 @@ export class NewUserDialog extends IGBDialog {
id: '/welcome_saas_plan',
waterfall: [
async step => {
const locale = 'en-US';
await step.context.sendActivity('Please choose your plan:');
await step.context.sendActivity('1. Personal - $9.99/month (basic features)');
await step.context.sendActivity('2. Professional - $29.99/month (advanced features)');
return await step.prompt('textPrompt', 'Enter 1 or 2 to select your plan:');
await step.context.sendActivity('1. Free - $0/month (basic features)');
await step.context.sendActivity('2. Personal - $50/month (more features)');
await step.context.sendActivity('3. Professional - $150/month (advanced features)');
return await step.prompt('textPrompt', 'Enter 1, 2 or 3 to select your plan:');
},
async step => {
const planChoice = step.context.activity.text.trim();
if (planChoice === '1') {
step.activeDialog.state.options.planId = 'personal';
step.activeDialog.state.options.amount = 9.99;
step.activeDialog.state.options.planId = 'free';
} else if (planChoice === '2') {
step.activeDialog.state.options.planId = 'personal';
} else if (planChoice === '3') {
step.activeDialog.state.options.planId = 'professional';
step.activeDialog.state.options.amount = 29.99;
} else {
await step.context.sendActivity('Invalid choice. Please select 1 or 2.');
await step.context.sendActivity('Invalid choice. Please select 1, 2 or 3.');
return await step.replaceDialog('/welcome_saas_plan');
}
return await step.replaceDialog('/welcome_saas_botname', step.activeDialog.state.options);
@ -102,50 +70,16 @@ export class NewUserDialog extends IGBDialog {
};
}
static getStripePaymentDialog(min: GBMinInstance) {
return {
id: '/welcome_saas_stripe_payment',
waterfall: [
async step => {
const locale = 'en-US';
await step.context.sendActivity(`Please enter your credit card details for the ${step.activeDialog.state.options.planId} plan ($${step.activeDialog.state.options.amount}/month):`);
return await step.prompt('textPrompt', 'Card number (e.g., 4242424242424242):');
},
async step => {
step.activeDialog.state.options.ccNumber = step.context.activity.text.trim();
return await step.prompt('textPrompt', 'Expiration month (MM):');
},
async step => {
step.activeDialog.state.options.ccExpiresOnMonth = step.context.activity.text.trim();
return await step.prompt('textPrompt', 'Expiration year (YYYY):');
},
async step => {
step.activeDialog.state.options.ccExpiresOnYear = step.context.activity.text.trim();
return await step.prompt('textPrompt', 'CVC:');
},
async step => {
step.activeDialog.state.options.ccSecuritycode = step.context.activity.text.trim();
await step.context.sendActivity('Processing payment...');
await NewUserDialog.createBot(step, min, false);
return await step.replaceDialog('/ask', { isReturning: true });
}
]
};
}
static getBotTemplateDialog(min: GBMinInstance) {
return {
id: '/welcome_saas_bottemplate',
waterfall: [
async step => {
const locale = 'en-US';
await step.context.sendActivity('Aqui estão alguns modelos para você escolher:');
await step.context.sendActivity('Here are some templates to choose from:');
let gboService = new GBOService();
const list = await gboService.listTemplates(min);
let templateMessage = undefined;
await CollectionUtil.asyncForEach(list, async item => {
if (item.name !== 'Shared.gbai') {
templateMessage = templateMessage ? `${templateMessage}\n- ${item.name}` : `- ${item.name}`;
@ -154,108 +88,63 @@ export class NewUserDialog extends IGBDialog {
await step.context.sendActivity(templateMessage);
step.activeDialog.state.options.templateList = list;
return await step.prompt('textPrompt', `Qual modelo de bot você gostaria de usar?`);
return await step.prompt('textPrompt', `Which bot template would you like to use?`);
},
async step => {
const list = step.activeDialog.state.options.templateList;
let template = null;
let gboService = new GBOService();
await CollectionUtil.asyncForEach(list, async item => {
if (gboService.kmpSearch(step.context.activity.originalText, item.name) != -1) {
template = item.name;
}
});
if (template === null) {
await step.context.sendActivity(`Escolha, por favor, um destes modelos listados.`);
await step.context.sendActivity(`Please choose one of the listed templates.`);
return await step.replaceDialog('/welcome_saas_bottemplate', step.activeDialog.state.options);
} else {
step.activeDialog.state.options.templateName = template;
await NewUserDialog.createBot(step, min, true);
const service = new MainService();
const result: any = await service.startSubscriptionProcess(
min,
step.activeDialog.state.options.name,
step.activeDialog.state.options.email,
step.activeDialog.state.options.mobile,
step.activeDialog.state.options.botName,
template,
step.activeDialog.state.options.planId
);
return await step.replaceDialog('/ask', { isReturning: true });
if (step.activeDialog.state.options.planId === 'free') {
await step.context.sendActivity(`Your free bot has been created! Access it here: ${result.botUrl}`);
return await step.replaceDialog('/ask', { isReturning: true });
} else {
await step.context.sendActivity(`Please complete your payment here: ${result.paymentUrl}`);
await step.context.sendActivity('I will check for payment completion every few seconds...');
try {
const finalResult = await service.waitForPaymentCompletion(
min,
result.subscriptionId,
template
);
await step.context.sendActivity(`Payment verified and bot created successfully!`);
await step.context.sendActivity(`Access your bot here: ${finalResult.botUrl}`);
return await step.replaceDialog('/ask', { isReturning: true });
} catch (error) {
await step.context.sendActivity(`Error: ${error.message}`);
return await step.replaceDialog('/welcome_saas_plan');
}
}
}
}
]
};
}
static getReturnFromCC(min: GBMinInstance) {
return {
id: '/welcome_saas_return_cc',
waterfall: [
async step => {
const locale = 'en-US';
await step.context.sendActivity(Messages[locale].thanks_payment);
await NewUserDialog.createBot(step, min, false);
return await step.replaceDialog('/ask', { isReturning: true });
}
]
};
}
static getReturnFromDocument(min: GBMinInstance) {
return {
id: '/welcome_saas_return_document',
waterfall: [
async step => {
step.activeDialog.state.options.nextDialog = 'welcome_saas_return_payment';
return await step.replaceDialog('/bank_payment_type', step.activeDialog.state.options);
}
]
};
}
static getReturnFromPayment(min: GBMinInstance) {
return {
id: '/welcome_saas_return_payment',
waterfall: [
async step => {
if (step.activeDialog.state.options.paymentType === 'cc') {
step.activeDialog.state.options.nextDialog = 'welcome_saas_return_cc';
await step.replaceDialog(`/bank_ccnumber`, step.activeDialog.state.options);
} else {
const locale = 'en-US';
await step.context.sendActivity(Messages[locale].boleto_mail);
await step.context.sendActivity('textPrompt', Messages[locale].thanks_payment);
await NewUserDialog.createBot(step, min, false);
return await step.replaceDialog('/ask', { isReturning: true });
}
}
]
};
}
private static async createBot(step: any, min: GBMinInstance, free: boolean) {
const locale = 'en-US';
await step.context.sendActivity(Messages[locale].ok_procceding_creation);
const url = `${process.env.BOT_ID}/${step.activeDialog.state.options.botName}`;
await step.context.sendActivity(Messages[locale].bot_created(url));
const service = new MainService();
await service.createSubscription(
min,
step.activeDialog.state.options.name,
step.activeDialog.state.options.document,
step.activeDialog.state.options.email,
step.activeDialog.state.options.mobile,
step.activeDialog.state.options.botName,
step.activeDialog.state.options.ccNumber,
step.activeDialog.state.options.ccExpiresOnMonth,
step.activeDialog.state.options.ccExpiresOnYear,
step.activeDialog.state.options.ccSecuritycode,
step.activeDialog.state.options.templateName,
free, step.activeDialog.state.options.planId,
);
}
static getDialog(min: GBMinInstance) {
return {
id: '/welcome_saas',
@ -263,23 +152,21 @@ export class NewUserDialog extends IGBDialog {
async step => {
const locale = 'en-US';
step.activeDialog.state.options.document = null;
step.activeDialog.state.options.email = null;
step.activeDialog.state.options.botName = null;
step.activeDialog.state.options.ccNumber = null;
step.activeDialog.state.options.ccExpiresOnMonth = null;
step.activeDialog.state.options.ccExpiresOnYear = null;
step.activeDialog.state.options.ccSecuritycode = null;
step.activeDialog.state.options.templateName = null;
step.activeDialog.state.options.planId = null;
step.activeDialog.state.options.amount = null;
step.activeDialog.state.options = {
document: null,
email: null,
botName: null,
templateName: null,
planId: null,
name: null,
mobile: null,
nextDialog: 'welcome_saas_plan'
};
await step.context.sendActivity(Messages[locale].welcome);
const mobile = step.context.activity.from.id;
step.activeDialog.state.options.nextDialog = 'welcome_saas_plan';
if (isNaN(mobile as any)) {
await step.context.sendActivity(Messages[locale].ok_get_information);
return await step.replaceDialog('/profile_name', step.activeDialog.state.options);

View file

@ -48,9 +48,9 @@ export class SaaSPackage implements IGBPackage {
return [NewUserDialog.getDialog(min),
NewUserDialog.getBotNameDialog(min),
NewUserDialog.getBotTemplateDialog(min),
NewUserDialog.getReturnFromPayment(min),
NewUserDialog.getReturnFromCC(min),
NewUserDialog.getReturnFromDocument(min),
NewUserDialog.getPlanSelectionDialog(min),
];
}

131
packages/saas.gbapp/model/MainModel.ts Executable file → Normal file
View file

@ -1,88 +1,59 @@
// BotServer/packages/saas.gbapp/model/MainModel.ts
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| 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, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| 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.com.br. |
| 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. |
| |
\*****************************************************************************/
"use strict"
import {
Table,
Column,
Model,
HasMany,
BelongsTo,
BelongsToMany,
Length,
ForeignKey,
CreatedAt,
UpdatedAt,
DataType,
IsUUID,
PrimaryKey,
AutoIncrement
} from "sequelize-typescript"
import { Table, Column, Model, DataType } from 'sequelize-typescript';
@Table({ tableName: 'GBOnlineSubscription' })
export class GBOnlineSubscription extends Model<GBOnlineSubscription> {
@PrimaryKey
@AutoIncrement
@Column
Id: number
@Column
instanceId: number;
@Column
externalSubscriptionId: string;
@Column
saasSubscriptionStatus: string;
@Column
isFreeTrial: boolean;
@Column
planId: string;
@Column
quantity: number;
@Column
lastCCFourDigits: string;
@Column
status: string;
@Column({
type: DataType.DECIMAL(10, 2)
primaryKey: true,
autoIncrement: true,
type: DataType.INTEGER
})
amount: number;
declare subscriptionId: number;
}
@Column(DataType.INTEGER)
declare instanceId: number;
@Column(DataType.STRING(100))
declare customerName: string;
@Column(DataType.STRING(100))
declare customerEmail: string;
@Column(DataType.STRING(100))
declare stripeSessionId: string;
@Column(DataType.STRING(100))
declare stripePaymentIntentId: string;
@Column(DataType.STRING(20))
declare customerMobile: string;
@Column(DataType.STRING(50))
declare botName: string;
@Column(DataType.STRING(20))
declare planId: string;
@Column(DataType.STRING(20))
declare status: string; // 'pending_payment', 'active', 'cancelled'
@Column(DataType.FLOAT)
declare paymentAmount: number;
@Column(DataType.STRING(500))
declare paymentUrl: string;
@Column(DataType.STRING(100))
declare paymentToken: string;
@Column(DataType.STRING(4))
declare lastCCFourDigits: string;
@Column(DataType.DATE)
declare createdAt: Date;
@Column(DataType.DATE)
declare activatedAt: Date;
}

View file

@ -1,32 +1,4 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| 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, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| 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.com.br. |
| 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. |
| |
\*****************************************************************************/
// General Bots Copyright (c) pragmatismo.com.br. All rights reserved. Licensed under the AGPL-3.0.
"use strict"
@ -44,17 +16,12 @@ import path from "path";
import { Client } from "minio";
export class GBOService {
public isValidCardNumber(ccNumber) {
constructor() {
}
public isValidSecurityCode(ccNumber, cvcNumber) {
}
public isValidExpireDate(month, year) {
}
public async sendEmail(token: string, to: string, from: string,
subject: string, text: string, html: string) {
return new Promise<any>((resolve, reject) => {

349
packages/saas.gbapp/service/MainService.ts Executable file → Normal file
View file

@ -1,35 +1,4 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| 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, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| 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.com.br. |
| 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. |
| |
\*****************************************************************************/
'use strict';
// BotServer/packages/saas.gbapp/service/MainService.ts
import { GBOnlineSubscription } from '../model/MainModel.js';
import { GBMinInstance, GBLog } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework';
@ -37,124 +6,142 @@ import urlJoin from 'url-join';
import { GBOService } from './GBOService.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import Stripe from 'stripe';
import { GBUtil } from '../../../src/util.js';
export class MainService {
private gboService: GBOService;
private stripe: Stripe;
private readonly PAYMENT_CHECK_INTERVAL = 5000; // 5 seconds
private readonly PAYMENT_CHECK_TIMEOUT = 300000; // 5 minutes timeout
constructor() {
this.gboService = new GBOService();
this.stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
}
async createStripeCustomer(name: string, email: string, paymentMethodId: string) {
const customer = await this.stripe.customers.create({
name,
email,
payment_method: paymentMethodId,
invoice_settings: {
default_payment_method: paymentMethodId
}
});
return customer;
}
async createStripeSubscription(customerId: string, priceId: string) {
const subscription = await this.stripe.subscriptions.create({
customer: customerId,
items: [{ price: priceId }],
expand: ['latest_invoice.payment_intent']
});
return subscription;
}
async createPaymentMethod(cardNumber: string, expMonth: number, expYear: number, cvc: string) {
const paymentMethod = await this.stripe.paymentMethods.create({
type: 'card',
card: {
number: cardNumber,
exp_month: expMonth,
exp_year: expYear,
cvc: cvc
}
}, {});
return paymentMethod;
}
async createSubscription(
public async startSubscriptionProcess(
min: GBMinInstance,
name: string,
document: string,
email: string,
mobile: string,
botName: string,
ccNumber: string,
ccExpiresOnMonth: number,
ccExpiresOnYear: number,
ccCode: string,
templateName: string,
free: boolean, planId: string,
planId: string
) {
let externalSubscriptionId = null;
if (!free) {
try {
// Create Stripe payment method
const paymentMethod = await this.createPaymentMethod(
ccNumber,
ccExpiresOnMonth,
ccExpiresOnYear,
ccCode
);
// Create Stripe customer
const customer = await this.createStripeCustomer(
name,
email,
paymentMethod.id
);
// Determine price ID based on plan
const priceId = planId === 'professional'
? process.env.STRIPE_PROFESSIONAL_PRICE_ID
: process.env.STRIPE_PERSONAL_PRICE_ID;
// Create subscription
const subscription = await this.createStripeSubscription(
customer.id,
priceId
);
externalSubscriptionId = subscription.id;
} catch (error) {
GBLog.error(`Stripe payment failed: ${error.message}`);
throw error;
}
}
// Syncs internal subscription management
const status = free ? 'FreeTrial' : 'Active';
GBLog.info(`Creating subscription for ${name} (${email}, ${mobile}) with status: ${status}`);
const quantity = 1;
const amount = 1;
// Create initial subscription record
const subscription = await GBOnlineSubscription.create({
instanceId: min.instance.instanceId,
isFreeTrial: free,
customerName: name,
customerEmail: email,
customerMobile: mobile,
botName: botName,
planId: planId,
quantity: quantity,
status: status,
amount: amount,
lastCCFourDigits: ccNumber ? ccNumber.slice(-4) : null
status: planId === 'free' ? 'active' : 'pending_payment',
createdAt: new Date(),
activatedAt: planId === 'free' ? new Date() : null
});
// Creates a bot
GBLog.info('Deploying a blank bot to storage...');
const instance = await min.deployService.deployBlankBot(botName, mobile, email);
if (planId === 'free') {
return await this.createBotResources(min, subscription, templateName);
} else {
const priceId = this.getPriceIdForPlan(planId);
const session = await this.stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [{
price: priceId,
quantity: 1,
}],
mode: 'subscription',
metadata: {
subscriptionId: subscription.subscriptionId.toString(),
botName: botName
}
});
GBLog.info('Creating subscription...');
subscription.instanceId = instance.instanceId;
subscription.externalSubscriptionId = externalSubscriptionId;
await subscription.save();
await subscription.update({
stripeSessionId: session.id
});
return {
paymentUrl: session.url,
subscriptionId: subscription.subscriptionId,
nextStep: 'Please complete the payment in the new window. I will check for completion automatically.'
};
}
}
public async waitForPaymentCompletion(
min: GBMinInstance,
subscriptionId: number,
templateName: string
): Promise<any> {
const startTime = Date.now();
while ((Date.now() - startTime) < this.PAYMENT_CHECK_TIMEOUT) {
const subscription = await GBOnlineSubscription.findOne({
where: { subscriptionId }
});
if (!subscription) {
throw new Error('Subscription not found');
}
if (subscription.stripeSessionId) {
const session = await this.stripe.checkout.sessions.retrieve(
subscription.stripeSessionId,
{ expand: ['payment_intent'] }
);
if (session.payment_status === 'paid') {
await subscription.update({
status: 'active',
activatedAt: new Date(),
stripePaymentIntentId: (session.payment_intent as any)?.id
});
return await this.createBotResources(min, subscription, templateName);
}
if (session.payment_status === 'unpaid' || session.status === 'expired') {
throw new Error('Payment failed or session expired. Please try again.');
}
}
await GBUtil.sleep(this.PAYMENT_CHECK_INTERVAL);
}
throw new Error('Payment processing timed out. Please check your payment and try again.');
}
private getPriceIdForPlan(planId: string): string {
const priceIds = {
personal: process.env.STRIPE_PERSONAL_PLAN_PRICE_ID,
professional: process.env.STRIPE_PROFESSIONAL_PLAN_PRICE_ID
};
if (!priceIds[planId]) {
throw new Error(`No price ID configured for plan: ${planId}`);
}
return priceIds[planId];
}
private async createBotResources(
min: GBMinInstance,
subscription: GBOnlineSubscription,
templateName: string
) {
GBLog.info('Deploying a blank bot to storage...');
const instance = await min.deployService.deployBlankBot(
subscription.botName,
subscription.customerMobile,
subscription.customerEmail
);
await subscription.update({
instanceId: instance.instanceId
});
let token =
GBConfigService.get('GB_MODE') === 'legacy' ?
@ -163,98 +150,32 @@ export class MainService {
let siteId = process.env.STORAGE_SITE_ID;
let libraryId = process.env.STORAGE_LIBRARY;
let gboService = new GBOService();
let sleep = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
GBLog.info('Creating .gbai folder ...');
let item = await gboService.createRootFolder(token, `${botName}.gbai`, siteId, libraryId);
let item = await this.gboService.createRootFolder(
token,
`${subscription.botName}.gbai`,
siteId,
libraryId
);
GBLog.info('Copying Templates...');
await gboService.copyTemplates(min, item, templateName, 'gbkb', botName);
await gboService.copyTemplates(min, item, templateName, 'gbot', botName);
await gboService.copyTemplates(min, item, templateName, 'gbtheme', botName);
await gboService.copyTemplates(min, item, templateName, 'gbdata', botName);
await gboService.copyTemplates(min, item, templateName, 'gbdialog', botName);
await gboService.copyTemplates(min, item, templateName, 'gbdrive', botName);
await this.gboService.copyTemplates(min, item, templateName, 'gbkb', subscription.botName);
await this.gboService.copyTemplates(min, item, templateName, 'gbot', subscription.botName);
await this.gboService.copyTemplates(min, item, templateName, 'gbtheme', subscription.botName);
await this.gboService.copyTemplates(min, item, templateName, 'gbdata', subscription.botName);
await this.gboService.copyTemplates(min, item, templateName, 'gbdialog', subscription.botName);
await this.gboService.copyTemplates(min, item, templateName, 'gbdrive', subscription.botName);
await sleep(10000);
GBLog.info('Configuring .gbot...');
await min.core['setConfig'](min, instance.botId, "Can Publish", mobile + ";");
await min.core['setConfig'](min, instance.botId, "Admin Notify E-mail", email);
await min.core['setConfig'](min, instance.botId, "Can Publish", subscription.customerMobile + ";");
await min.core['setConfig'](min, instance.botId, "Admin Notify E-mail", subscription.customerEmail);
await min.core['setConfig'](min, instance.botId, 'WebDav Username', instance.botId);
await min.core['setConfig'](min, instance.botId, 'WebDav Secret', instance.adminPass);
GBLog.info('Bot creation done.');
return {
success: true,
botUrl: urlJoin(process.env.BOT_URL, subscription.botName)
};
}
public async otherTasks(min, botName, webUrl, instance, language) {
let message = `Seu bot ${botName} está disponível no endereço:
<br/><a href="${urlJoin(process.env.BOT_URL, botName)}">${urlJoin(process.env.BOT_URL, botName)}</a>.
<br/>
<br/>Os pacotes do General Bots (ex: .gbkb, .gbtheme) para seu Bot devem ser editados no repositório de pacotes:
<br/>
<br/><a href="${webUrl}">${webUrl}</a>.
<br/>
<br/> Digite /publish do seu WhatsApp para publicar os pacotes. Seu número está autorizado na pasta ${botName}.gbot/Config.xlsx
<br/>
<br/>
<br/>O arquivo .zip em anexo pode ser importado no Teams conforme instruções em:
<br/><a href="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/apps-upload">https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/apps-upload</a>.
<br/>
<br/>Log in to the Teams client with your Microsoft 365 account.
<br/>Select Apps and choose Upload a custom app.
<br/>Select this .zip file attached to this e-mail. An install dialog displays.
<br/>Add your Bot to Teams.
<br/>
<br/>Atenciosamente,
<br/>General Bots Online.
<br/><a href=""></a>
<br/>
<br/>E-mail remetido por Pragmatismo.
<br/>`;
message = await min.conversationalService.translate(
min,
message,
language
);
GBLog.info('Generating MS Teams manifest....');
const appManifest = await min.deployService.getBotManifest(min.instance);
// GBLog.info( 'Sending e-mails....');
// const emailToken = process.env.SAAS_SENDGRID_API_KEY;
// gboService.sendEmail(
// emailToken,
// email,
// `${botName}`,
// message,
// message,
// {
// content: appManifest,
// filename: `${min.instance.botId}-Teams.zip`,
// type: `application/zip`,
// disposition: "attachment"
// }
// );
const contacts = process.env.SECURITY_LIST.split(';');
// TODO: await CollectionUtil.asyncForEach(contacts, async item => {
// await (min.whatsAppDirectLine as any)['sendToDevice'](
// item,
// `Novo bot criado agora: http://gb.pragmatismo.com.br/${botName} para *${name}* (${email}, ${mobile}). Por favor, entre em contato para que mais um bot seja configurado adequadamente. `
// );
// });
// GBLog.info( 'Sharing .gbai folder...');
// await gboService.shareFolder(token, item.parentReference.driveId, item.id, email);
}
}
}

View file

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="boot.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<!-- Do not interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js\/debug[\/]?" />
</rule>
<!-- First we consider whether the incoming URL matches a physical file in the /public folder. -->
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
<!-- All other URLs are mapped to the node.js site entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="boot.js"/>
</rule>
</rules>
</rewrite>
<!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<!-- Make sure error responses are left untouched -->
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>