import crypto = require("crypto"); import fs = require("fs-extra"); import https = require("https"); import request = require("request"); import co = require("co"); import prompt = require("co-prompt"); const chalk = require("chalk"); const ora = require('ora'); const spinner = ora(); const os = require("os"); const path = require("path"); const util = require("util"); const { spawn } = require('child_process'); import {User} from "./user"; import { exists } from "fs"; import { Facebook, Telegram, Viber } from '../platform'; const supportedLangauge = [ "en" , "fr", "ca", "zh", "es", "de", "hi", "it", "ja", "ko", "pl", "pt", "ru", "sv", "bn" ]; const supportedPlatforms = [ 'facebook', 'telegram', 'twilio', 'viber', 'web', 'wechat' ] export interface Bot { id : string author: string, description: string, lang: string name : string, title: string, platform: string }; export class BotService { private config; private httpClient; private unzip; constructor(config, unzip, httpClient){ this.config = config; this.httpClient = httpClient; this.unzip = unzip; } create(folder){ if (!User.isLoggedIn) { return this.printError(new Error("Looks like you are not logged in. Please type \"rbp login \" to get started.")); } const company = this.config.company; const email = this.config.email; const defaultLanguage = this.config.defaultLanguage; const defaultPlatform = this.config.defaultPlatform; const botFolder = path.join(process.cwd(), folder); if (!fs.existsSync(botFolder)){ fs.mkdirSync(botFolder); } co(function* () { const title = yield prompt(`Title (Title of the Bot):`) || 'Demo Bot'; let name = title.replace(/[\s?.$#,()^!&]+/ig, "-"); name = name.toLowerCase().trim(); const description = yield prompt(`Description (Short Description):`); const platform = yield prompt(`Langauge (${supportedPlatforms.join('|')}):`); const currentUser = User.currentUser; const author = util.format("%s<%s>", currentUser.name || company, currentUser.email || email); const lang = yield prompt(`Langauge (${defaultLanguage}):`); return { title: title, description: description, name: name, author: author, lang: lang, platform: platform }; }).then((bot: Bot) => { const body = { name: bot.name, author: bot.author, owner: User.currentUser.email, config: {}, title: bot.title, description: bot.description, language: bot.lang || defaultLanguage, timezone: this.config.defaultTimezone, platforms: [ bot.platform || this.config.defaultPlatform ] }; spinner.start(); this.update(this.uid(bot.name), body) .then((bot) => { const iconPath = path.join(botFolder, "icon.png"); if (fs.existsSync(iconPath)){ this.uploadIcon(this.uid(bot.name), iconPath) .then(() => { this.onComplete(botFolder, bot); }) .catch((err) => this.printError(err)); return; } else { this.httpClient(`https://s3-us-west-2.amazonaws.com/recime-io/icon/${bot.id}.png`, path.join(botFolder, `icon.png`)).then (()=>{ this.onComplete(botFolder, bot); }); } }) .catch((err) => this.printError(err)); }); } onComplete(folder, bot: Bot){ const filePath = path.join(folder, 'bot.json'); const dirname = path.dirname(filePath); delete bot['config']; delete bot['meta']; // create the directory if (!fs.existsSync(dirname)) { fs.mkdirSync(path.dirname(filePath)); } fs.writeJson(filePath, bot).then (()=>{ spinner.stop(); console.log(`\r\n${chalk.magenta(bot.title)} created successfully.\r\n`); process.exit(0); }).catch((err)=> this.printError(err)); } register(uid){ return new Promise((resolve, reject)=>{ request({ url : `${this.config.apiEndPoint}/bots/${uid}`, json : true, headers : { Authorization : `Bearer ${User.currentUser.token}` } }, (err, response, bot)=>{ if (err){ return reject(err); } if (response.statusCode < 200 || response.statusCode > 299){ return reject(new Error(bot.length ? bot[0].message : 'Bot not Found.')); } const endpoint = `https://${bot.region}-bot-proxy.recime.io/${bot.id}/v1`; const filePath = path.join(process.cwd(), "config.json"); if (fs.existsSync(filePath)) { fs.readJson(filePath).then((env) => { env['UID'] = bot.id; spinner.stop(); const facebook = new Facebook(env); return facebook.setWebhook(endpoint) .then(() => facebook.subscribeToPage()) .then(() => facebook.setWecomeScreen()) .then(() => facebook.setPersistentMenu()) .then(()=>{ const telegram = new Telegram(env); // configure telegram return telegram.setWebhook(endpoint); }) .then (()=>{ const viber = new Viber(env); // configure viber return viber.setWebhook(endpoint); }) .then (()=>{ return this.update(bot.id, { name : bot.name, title: bot.title, description: bot.description, config: env }) .then(()=>{ resolve() }); }) .catch(this.printError); }); } }); }); } update(id, body) : Promise{ return new Promise((resolve, reject)=>{ request({ url : `${this.config.apiEndPoint}/bots/${id}`, json : true, method : "POST", headers : { Authorization : `Bearer ${User.currentUser.token}` }, body : body }, (err, response, body)=>{ if (err){ return reject(err); } if (response.statusCode < 200 || response.statusCode > 299){ return reject(new Error(body.length? body[0].message : "Token has expired. Please type \"rbp login\" to renew your token.")); } resolve(body); }); }); } uploadIcon(uid, pathToFile){ return new Promise((resolve, reject)=>{ request({ url : `${this.config.apiEndPoint}/bots/${uid}/uploads/icon`, method : "POST", json : true, headers : { Authorization : `Bearer ${User.currentUser.token}` }, body : null }, (err, response, body)=>{ if (err){ return reject(err); } const signedUrl = body.url; const stats = fs.statSync(pathToFile); fs.readFile(pathToFile).then ((data)=>{ request({ method : "PUT", url : signedUrl, headers : { 'Content-Length': stats['size'] }, body : data }, (err, response, body)=>{ if(err){ return reject(err); } resolve(body); }); }) }); }); } publish(folder){ const filePath = path.join(process.cwd(), 'bot.json'); if (!fs.existsSync(filePath)) return this.printError("No configuration found."); spinner.start(); fs.readJson(filePath).then(bot => { const iconPath = path.join(folder, "icon.png"); if (fs.existsSync(iconPath)) { this.uploadIcon(bot.id, iconPath) .then(() => { console.log(`\r\n${chalk.magenta(bot.title)} icon updated successfully.\r\n`); this.register(bot.id) .then(()=> process.exit(0)) .catch(this.printError); }) .catch((err) => this.printError(err)); return; } this.register(bot.id) .then(() => process.exit(0)) .catch(this.printError); }).catch(this.printError); } uid(name){ const uid = `${User.currentUser.email};${name}`; return crypto.createHash("md5").update(uid).digest("hex"); } printError(err){ spinner.stop(); console.log(chalk.red(err.message)); process.exit(1) } }