new(all): Auto import for logo, colors and website content.
This commit is contained in:
parent
7fe50d95c4
commit
e91c3a4e06
7 changed files with 176 additions and 65 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -28,3 +28,5 @@ yarn-error.log
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn-lock.json
|
yarn-lock.json
|
||||||
packages/saas.gbapp.zip
|
packages/saas.gbapp.zip
|
||||||
|
logo.svg
|
||||||
|
screenshot.png
|
||||||
|
|
|
@ -59,9 +59,11 @@ import { GBHubSpotPackage } from '../../hubspot.gblib/index.js';
|
||||||
import open from 'open';
|
import open from 'open';
|
||||||
import ngrok from 'ngrok';
|
import ngrok from 'ngrok';
|
||||||
import Path from 'path';
|
import Path from 'path';
|
||||||
import { file } from 'googleapis/build/src/apis/file/index.js';
|
|
||||||
import { GBUtil } from '../../../src/util.js';
|
import { GBUtil } from '../../../src/util.js';
|
||||||
import { GBLogEx } from './GBLogEx.js';
|
import { GBLogEx } from './GBLogEx.js';
|
||||||
|
import { GBDeployer } from './GBDeployer.js';
|
||||||
|
import { SystemKeywords } from '../../basic.gblib/services/SystemKeywords.js';
|
||||||
|
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GBCoreService contains main logic for handling storage services related
|
* GBCoreService contains main logic for handling storage services related
|
||||||
|
@ -665,6 +667,50 @@ ENDPOINT_UPDATE=true
|
||||||
await installationDeployer.openStorageFirewall(group, serverName);
|
await installationDeployer.openStorageFirewall(group, serverName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async setConfig(min, name: string, value: any): Promise<any> {
|
||||||
|
|
||||||
|
// Handles calls for BASIC persistence on sheet files.
|
||||||
|
|
||||||
|
GBLog.info( `Defining Config.xlsx variable ${name}= '${value}'...`);
|
||||||
|
|
||||||
|
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
|
||||||
|
|
||||||
|
const maxLines = 512;
|
||||||
|
const file = "Config.xlsx";
|
||||||
|
const path = DialogKeywords.getGBAIPath(min.botId, `gbot`);;
|
||||||
|
|
||||||
|
let document = await (new SystemKeywords()).internalGetDocument(client, baseUrl, path, file);
|
||||||
|
|
||||||
|
// Creates workbook session that will be discarded.
|
||||||
|
|
||||||
|
let sheets = await client
|
||||||
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
let results = await client
|
||||||
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')`)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
const rows = results.text;
|
||||||
|
let address = '';
|
||||||
|
|
||||||
|
// Fills the row variable.
|
||||||
|
|
||||||
|
for (let i = 1; i <= rows.length; i++) {
|
||||||
|
let result = rows[i - 1][0];
|
||||||
|
if (result && result.toLowerCase() === name.toLowerCase()) {
|
||||||
|
address = `B${i}:B${i}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = { values: [[]] };
|
||||||
|
body.values[0][0] = value;
|
||||||
|
|
||||||
|
await client
|
||||||
|
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`)
|
||||||
|
.patch(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -687,7 +687,13 @@ export class GBMinService {
|
||||||
paramLogoImageAlt: this.core.getParam(instance, 'Logo Image Alt', null),
|
paramLogoImageAlt: this.core.getParam(instance, 'Logo Image Alt', null),
|
||||||
paramLogoImageWidth: this.core.getParam(instance, 'Logo Image Width', null),
|
paramLogoImageWidth: this.core.getParam(instance, 'Logo Image Width', null),
|
||||||
paramLogoImageHeight: this.core.getParam(instance, 'Logo Image Height', null),
|
paramLogoImageHeight: this.core.getParam(instance, 'Logo Image Height', null),
|
||||||
paramLogoImageType: this.core.getParam(instance, 'Logo Image Type', null)
|
paramLogoImageType: this.core.getParam(instance, 'Logo Image Type', null),
|
||||||
|
logo: this.core.getParam(instance, 'Logo', null),
|
||||||
|
color1: this.core.getParam(instance, 'Color1', null),
|
||||||
|
color2: this.core.getParam(instance, 'Color2', null),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -39,7 +39,7 @@ import GBBulletPlayer from './players/GBBulletPlayer.js';
|
||||||
import SidebarMenu from './components/SidebarMenu.js';
|
import SidebarMenu from './components/SidebarMenu.js';
|
||||||
import SEO from './components/SEO.js';
|
import SEO from './components/SEO.js';
|
||||||
import GBCss from './components/GBCss.js';
|
import GBCss from './components/GBCss.js';
|
||||||
import { DirectLine} from 'botframework-directlinejs';
|
import { DirectLine } from 'botframework-directlinejs';
|
||||||
import { ConnectionStatus } from 'botframework-directlinejs';
|
import { ConnectionStatus } from 'botframework-directlinejs';
|
||||||
import ReactWebChat from 'botframework-webchat';
|
import ReactWebChat from 'botframework-webchat';
|
||||||
import { UserAgentApplication } from 'msal';
|
import { UserAgentApplication } from 'msal';
|
||||||
|
@ -257,16 +257,16 @@ class GBUIApp extends React.Component {
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'multiurl':
|
case 'multiurl':
|
||||||
playerComponent = (
|
playerComponent = (
|
||||||
<GBMultiUrlPlayer
|
<GBMultiUrlPlayer
|
||||||
app={this}
|
app={this}
|
||||||
ref={player => {
|
ref={player => {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'image':
|
case 'image':
|
||||||
playerComponent = (
|
playerComponent = (
|
||||||
<GBImagePlayer
|
<GBImagePlayer
|
||||||
app={this}
|
app={this}
|
||||||
|
@ -305,18 +305,17 @@ class GBUIApp extends React.Component {
|
||||||
let chat = <div />;
|
let chat = <div />;
|
||||||
let gbCss = <div />;
|
let gbCss = <div />;
|
||||||
let seo = <div />;
|
let seo = <div />;
|
||||||
|
let sideBar = <div />;
|
||||||
let sideBar = (
|
|
||||||
<div className="sidebar">
|
|
||||||
<SidebarMenu chat={this.chat} instance={this.state.instanceClient} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.state.line) {
|
if (this.state.line) {
|
||||||
if (this.state.instanceClient) {
|
if (this.state.instanceClient) {
|
||||||
|
let color1 = this.state.instanceClient.color1;
|
||||||
gbCss = <GBCss instance={this.state.instanceClient} />;
|
gbCss = <GBCss instance={this.state.instanceClient} />;
|
||||||
seo = <SEO instance={this.state.instanceClient} />;
|
seo = <SEO instance={this.state.instanceClient} />;
|
||||||
const token = this.state.instanceClient.speechToken;
|
const token = this.state.instanceClient.speechToken;
|
||||||
|
|
||||||
|
document.body.style.setProperty('background-color', this.state.instanceClient.color2, 'important');
|
||||||
|
|
||||||
chat = (
|
chat = (
|
||||||
<ReactWebChat
|
<ReactWebChat
|
||||||
ref={chat => {
|
ref={chat => {
|
||||||
|
@ -329,15 +328,25 @@ class GBUIApp extends React.Component {
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sideBar = (
|
||||||
|
<div
|
||||||
|
className="sidebar"
|
||||||
|
ref={node => {
|
||||||
|
if (node) {
|
||||||
|
node.style.setProperty('background-color', this.state.instanceClient.color1, 'important');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SidebarMenu chat={this.chat} instance={this.state.instanceClient} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.state.instanceClient) {
|
|
||||||
sideBar = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StaticContent>
|
<StaticContent>
|
||||||
|
|
||||||
{seo}
|
{seo}
|
||||||
<div>
|
<div>
|
||||||
{gbCss}
|
{gbCss}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class SideBarMenu extends React.Component {
|
||||||
<div className="tittleSideBarMenu">
|
<div className="tittleSideBarMenu">
|
||||||
<img
|
<img
|
||||||
className="pragmatismoLogo"
|
className="pragmatismoLogo"
|
||||||
src={"/themes/" + this.props.instance.theme + "/images/logo.png"}
|
src={this.props.instance.botId + "/cache/" + this.props.instance.logo}
|
||||||
alt="General Bots Logo" />
|
alt="General Bots Logo" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -254,29 +254,28 @@ export class ChatServices {
|
||||||
public static async answerByGPT(
|
public static async answerByGPT(
|
||||||
min: GBMinInstance,
|
min: GBMinInstance,
|
||||||
user,
|
user,
|
||||||
pid,
|
question: string, mode=null
|
||||||
question: string,
|
|
||||||
searchScore: number,
|
|
||||||
subjects: GuaribasSubject[]
|
|
||||||
) {
|
) {
|
||||||
if (!process.env.OPENAI_API_KEY) {
|
if (!process.env.OPENAI_API_KEY) {
|
||||||
return { answer: undefined, questionId: 0 };
|
return { answer: undefined, questionId: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const LLMMode = min.core.getParam(min.instance, 'Answer Mode', 'direct');
|
const LLMMode = mode??min.core.getParam(min.instance, 'Answer Mode', 'direct');
|
||||||
|
|
||||||
const docsContext = min['vectorStore'];
|
const docsContext = min['vectorStore'];
|
||||||
|
|
||||||
if (!this.memoryMap[user.userSystemId]) {
|
const memory = new BufferWindowMemory({
|
||||||
this.memoryMap[user.userSystemId] = new BufferWindowMemory({
|
returnMessages: true,
|
||||||
returnMessages: true,
|
memoryKey: 'chat_history',
|
||||||
memoryKey: 'chat_history',
|
inputKey: 'input',
|
||||||
inputKey: 'input',
|
k: 2
|
||||||
k: 2
|
});
|
||||||
});
|
|
||||||
|
if (user && !this.memoryMap[user.userSystemId]) {
|
||||||
|
this.memoryMap[user.userSystemId] = memory;
|
||||||
}
|
}
|
||||||
const memory = this.memoryMap[user.userSystemId];
|
|
||||||
const systemPrompt = this.userSystemPrompt[user.userSystemId];
|
const systemPrompt = user?this.userSystemPrompt[user.userSystemId]:'';
|
||||||
|
|
||||||
const model = new ChatOpenAI({
|
const model = new ChatOpenAI({
|
||||||
openAIApiKey: process.env.OPENAI_API_KEY,
|
openAIApiKey: process.env.OPENAI_API_KEY,
|
||||||
|
|
|
@ -53,7 +53,6 @@ import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
|
||||||
import { Document } from 'langchain/document';
|
import { Document } from 'langchain/document';
|
||||||
import getColors from 'get-image-colors';
|
import getColors from 'get-image-colors';
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GBDialogStep,
|
GBDialogStep,
|
||||||
GBLog,
|
GBLog,
|
||||||
|
@ -377,7 +376,7 @@ export class KBService implements IGBKBService {
|
||||||
returnedScore: ${returnedScore} < required (searchScore): ${searchScore}`
|
returnedScore: ${returnedScore} < required (searchScore): ${searchScore}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return await ChatServices.answerByGPT(min, user, pid, query, searchScore, subjects);
|
return await ChatServices.answerByGPT(min, user, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSubjectItems(instanceId: number, parentId: number): Promise<GuaribasSubject[]> {
|
public async getSubjectItems(instanceId: number, parentId: number): Promise<GuaribasSubject[]> {
|
||||||
|
@ -853,13 +852,13 @@ export class KBService implements IGBKBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveHtmlPage(min, url: string, page: Page): Promise<string | null> {
|
async saveHtmlPage(min, url: string, page: Page): Promise<string | null> {
|
||||||
|
page.setCacheEnabled(false);
|
||||||
const response = await page.goto(url);
|
const response = await page.goto(url);
|
||||||
|
|
||||||
|
|
||||||
if (response.headers && response.status() === 200) {
|
if (response.headers && (response.status() === 200 || response.status() === 304)) {
|
||||||
const contentType = response.headers()['content-type'];
|
const contentType = response.headers()['content-type'];
|
||||||
if (contentType && contentType.includes('text/html')) {
|
if (contentType && contentType.includes('text/html')) {
|
||||||
const buffer = await response.buffer();
|
const buffer = await page.content();
|
||||||
const urlObj = new URL(url);
|
const urlObj = new URL(url);
|
||||||
const urlPath = urlObj.pathname.endsWith('/') ? urlObj.pathname.slice(0, -1) : urlObj.pathname; // Remove trailing slash if present
|
const urlPath = urlObj.pathname.endsWith('/') ? urlObj.pathname.slice(0, -1) : urlObj.pathname; // Remove trailing slash if present
|
||||||
let filename = urlPath.split('/').pop() || 'index'; // Get the filename from the URL path or set it to 'index.html' as default
|
let filename = urlPath.split('/').pop() || 'index'; // Get the filename from the URL path or set it to 'index.html' as default
|
||||||
|
@ -882,12 +881,12 @@ export class KBService implements IGBKBService {
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
depth > maxDepth ||
|
depth > maxDepth ||
|
||||||
(visited.has(url) ||
|
visited.has(url) ||
|
||||||
url.endsWith('.jpg') ||
|
url.endsWith('.jpg') ||
|
||||||
url.endsWith('.pdf') ||
|
url.endsWith('.pdf') ||
|
||||||
url.endsWith('.jpg') ||
|
url.endsWith('.jpg') ||
|
||||||
url.endsWith('.png') ||
|
url.endsWith('.png') ||
|
||||||
url.endsWith('.mp4'))
|
url.endsWith('.mp4')
|
||||||
) {
|
) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -904,6 +903,7 @@ export class KBService implements IGBKBService {
|
||||||
}
|
}
|
||||||
const currentDomain = new URL(page.url()).hostname;
|
const currentDomain = new URL(page.url()).hostname;
|
||||||
let links = await page.evaluate(currentDomain => {
|
let links = await page.evaluate(currentDomain => {
|
||||||
|
|
||||||
const anchors = Array.from(document.querySelectorAll('a')).filter(p => {
|
const anchors = Array.from(document.querySelectorAll('a')).filter(p => {
|
||||||
try {
|
try {
|
||||||
return currentDomain == new URL(p.href).hostname;
|
return currentDomain == new URL(p.href).hostname;
|
||||||
|
@ -938,7 +938,7 @@ export class KBService implements IGBKBService {
|
||||||
const childLinks = [];
|
const childLinks = [];
|
||||||
for (const link of filteredLinks) {
|
for (const link of filteredLinks) {
|
||||||
const links = await this.crawl(min, link, visited, depth + 1, maxDepth, page);
|
const links = await this.crawl(min, link, visited, depth + 1, maxDepth, page);
|
||||||
if (links){
|
if (links) {
|
||||||
childLinks.push(...links);
|
childLinks.push(...links);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -946,9 +946,41 @@ export class KBService implements IGBKBService {
|
||||||
return [filename, ...childLinks]; // Include the filename of the cached file
|
return [filename, ...childLinks]; // Include the filename of the cached file
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await GBLogEx.info(min, error);
|
await GBLogEx.info(min, error);
|
||||||
|
return []; // Include the filename of the cached file
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLogoByPage(page) {
|
||||||
|
const checkPossibilities = async (page, possibilities) => {
|
||||||
|
for (const possibility of possibilities) {
|
||||||
|
const { tag, attributes } = possibility;
|
||||||
|
|
||||||
|
for (const attribute of attributes) {
|
||||||
|
const selector = `${tag}[${attribute}*="logo"]`;
|
||||||
|
const elements = await page.$$(selector);
|
||||||
|
|
||||||
|
for (const element of elements) {
|
||||||
|
const src = await page.evaluate(el => el.getAttribute('src'), element);
|
||||||
|
if (src) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Array of possibilities to check for the logo
|
||||||
|
const possibilities = [
|
||||||
|
{ tag: 'img', attributes: ['src', 'alt', 'class'] }, // Check for img elements with specific attributes
|
||||||
|
{ tag: 'svg', attributes: ['class', 'aria-label'] } // Check for svg elements with specific attributes
|
||||||
|
// Add more possibilities as needed
|
||||||
|
];
|
||||||
|
|
||||||
|
return await checkPossibilities(page, possibilities);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import all .docx files in reading comprehension folder.
|
* Import all .docx files in reading comprehension folder.
|
||||||
*/
|
*/
|
||||||
|
@ -960,32 +992,50 @@ export class KBService implements IGBKBService {
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
let files = [];
|
let files = [];
|
||||||
|
|
||||||
|
Fs.rmSync(min['vectorStorePath'], { recursive: true, force: true });
|
||||||
|
let path = DialogKeywords.getGBAIPath(min.botId, `gbot`);
|
||||||
|
const directoryPath = Path.join(process.env.PWD, 'work', path, 'Website');
|
||||||
|
Fs.rmSync(directoryPath, { recursive: true, force: true });
|
||||||
|
|
||||||
const website = min.core.getParam<string>(min.instance, 'Website', null);
|
const website = min.core.getParam<string>(min.instance, 'Website', null);
|
||||||
|
|
||||||
if (website) {
|
if (website) {
|
||||||
const browser = await puppeteer.launch({ headless: false });
|
const browser = await puppeteer.launch({ headless: false });
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const response = await page.goto(website);
|
await page.setRequestInterception(true);
|
||||||
|
|
||||||
await page.screenshot({ path: 'screenshot.png' });
|
page.on('request', req => {
|
||||||
|
if (req.resourceType() === 'image' || req.resourceType() === 'stylesheet') {
|
||||||
|
req.abort();
|
||||||
|
} else {
|
||||||
|
req.continue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await page.goto(website);
|
||||||
|
|
||||||
|
const logo = await this.getLogoByPage(page);
|
||||||
|
let path = DialogKeywords.getGBAIPath(min.botId);
|
||||||
|
const logoPath = Path.join(process.env.PWD, 'work', path, 'cache');
|
||||||
|
const baseUrl = page.url().split('/').slice(0, 3).join('/');
|
||||||
|
const logoBinary = await page.goto(urlJoin(baseUrl, logo));
|
||||||
|
const buffer = await logoBinary.buffer();
|
||||||
|
const logoFilename = Path.basename(logo);
|
||||||
|
Fs.writeFileSync(Path.join(logoPath, logoFilename), buffer);
|
||||||
|
await min.core['setConfig'](min, 'Logo', logoFilename);
|
||||||
|
|
||||||
// Extract dominant colors from the screenshot
|
// Extract dominant colors from the screenshot
|
||||||
|
|
||||||
|
await page.screenshot({ path: 'screenshot.png' });
|
||||||
const colors = await getColors('screenshot.png');
|
const colors = await getColors('screenshot.png');
|
||||||
|
await min.core['setConfig'](min, 'Color1', colors[0].hex());
|
||||||
|
await min.core['setConfig'](min, 'Color2', colors[1].hex());
|
||||||
|
|
||||||
// Assuming you want the two most dominant colors
|
const maxDepth = 2; // Maximum depth of recursion
|
||||||
const mainColor1 = colors[0].hex();
|
|
||||||
const mainColor2 = colors[1].hex();
|
|
||||||
|
|
||||||
console.log('Main Color 1:', mainColor1);
|
|
||||||
console.log('Main Color 2:', mainColor2);
|
|
||||||
|
|
||||||
|
|
||||||
const maxDepth = 1; // Maximum depth of recursion
|
|
||||||
const visited = new Set<string>();
|
const visited = new Set<string>();
|
||||||
files = files.concat(await this.crawl(min, website, visited, 0, maxDepth, page));
|
files = files.concat(await this.crawl(min, website, visited, 0, maxDepth, page));
|
||||||
|
|
||||||
await browser.close();
|
await browser.close();
|
||||||
|
|
||||||
files.shift();
|
files.shift();
|
||||||
|
|
||||||
await CollectionUtil.asyncForEach(files, async file => {
|
await CollectionUtil.asyncForEach(files, async file => {
|
||||||
|
@ -997,7 +1047,6 @@ export class KBService implements IGBKBService {
|
||||||
await vectorStore.addDocuments(flattenedDocuments);
|
await vectorStore.addDocuments(flattenedDocuments);
|
||||||
await vectorStore.save(min['vectorStorePath']);
|
await vectorStore.save(min['vectorStorePath']);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
files = await walkPromise(urlJoin(localPath, 'docs'));
|
files = await walkPromise(urlJoin(localPath, 'docs'));
|
||||||
|
|
Loading…
Add table
Reference in a new issue