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
 | 
			
		||||
yarn-lock.json
 | 
			
		||||
packages/saas.gbapp.zip
 | 
			
		||||
logo.svg
 | 
			
		||||
screenshot.png
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,9 +59,11 @@ import { GBHubSpotPackage } from '../../hubspot.gblib/index.js';
 | 
			
		|||
import open from 'open';
 | 
			
		||||
import ngrok from 'ngrok';
 | 
			
		||||
import Path from 'path';
 | 
			
		||||
import { file } from 'googleapis/build/src/apis/file/index.js';
 | 
			
		||||
import { GBUtil } from '../../../src/util.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
 | 
			
		||||
| 
						 | 
				
			
			@ -665,6 +667,50 @@ ENDPOINT_UPDATE=true
 | 
			
		|||
    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),
 | 
			
		||||
          paramLogoImageWidth: this.core.getParam(instance, 'Logo Image Width', 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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -305,18 +305,17 @@ class GBUIApp extends React.Component {
 | 
			
		|||
    let chat = <div />;
 | 
			
		||||
    let gbCss = <div />;
 | 
			
		||||
    let seo = <div />;
 | 
			
		||||
 | 
			
		||||
    let sideBar = (
 | 
			
		||||
      <div className="sidebar">
 | 
			
		||||
        <SidebarMenu chat={this.chat} instance={this.state.instanceClient} />
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
    let sideBar = <div />;
 | 
			
		||||
 | 
			
		||||
    if (this.state.line) {
 | 
			
		||||
      if (this.state.instanceClient) {
 | 
			
		||||
        let color1 = this.state.instanceClient.color1;
 | 
			
		||||
        gbCss = <GBCss instance={this.state.instanceClient} />;
 | 
			
		||||
        seo = <SEO instance={this.state.instanceClient} />;
 | 
			
		||||
        const token = this.state.instanceClient.speechToken;
 | 
			
		||||
        
 | 
			
		||||
        document.body.style.setProperty('background-color', this.state.instanceClient.color2, 'important');
 | 
			
		||||
        
 | 
			
		||||
        chat = (
 | 
			
		||||
          <ReactWebChat
 | 
			
		||||
            ref={chat => {
 | 
			
		||||
| 
						 | 
				
			
			@ -329,15 +328,25 @@ class GBUIApp extends React.Component {
 | 
			
		|||
            })}
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this.state.instanceClient) {
 | 
			
		||||
      sideBar = '';
 | 
			
		||||
        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>
 | 
			
		||||
        );
 | 
			
		||||
    
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
      <StaticContent>
 | 
			
		||||
 | 
			
		||||
        {seo}
 | 
			
		||||
        <div>
 | 
			
		||||
          {gbCss}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ class SideBarMenu extends React.Component {
 | 
			
		|||
        <div className="tittleSideBarMenu">
 | 
			
		||||
          <img
 | 
			
		||||
            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" />
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -254,29 +254,28 @@ export class ChatServices {
 | 
			
		|||
  public static async answerByGPT(
 | 
			
		||||
    min: GBMinInstance,
 | 
			
		||||
    user,
 | 
			
		||||
    pid,
 | 
			
		||||
    question: string,
 | 
			
		||||
    searchScore: number,
 | 
			
		||||
    subjects: GuaribasSubject[]
 | 
			
		||||
    question: string, mode=null
 | 
			
		||||
  ) {
 | 
			
		||||
    if (!process.env.OPENAI_API_KEY) {
 | 
			
		||||
      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'];
 | 
			
		||||
 | 
			
		||||
    if (!this.memoryMap[user.userSystemId]) {
 | 
			
		||||
      this.memoryMap[user.userSystemId] = new BufferWindowMemory({
 | 
			
		||||
    const memory = new BufferWindowMemory({
 | 
			
		||||
      returnMessages: true,
 | 
			
		||||
      memoryKey: 'chat_history',
 | 
			
		||||
      inputKey: 'input',
 | 
			
		||||
      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({
 | 
			
		||||
      openAIApiKey: process.env.OPENAI_API_KEY,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,7 +53,6 @@ import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
 | 
			
		|||
import { Document } from 'langchain/document';
 | 
			
		||||
import getColors from 'get-image-colors';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  GBDialogStep,
 | 
			
		||||
  GBLog,
 | 
			
		||||
| 
						 | 
				
			
			@ -377,7 +376,7 @@ export class KBService implements IGBKBService {
 | 
			
		|||
        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[]> {
 | 
			
		||||
| 
						 | 
				
			
			@ -853,13 +852,13 @@ export class KBService implements IGBKBService {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  async saveHtmlPage(min, url: string, page: Page): Promise<string | null> {
 | 
			
		||||
    page.setCacheEnabled(false);
 | 
			
		||||
    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'];
 | 
			
		||||
      if (contentType && contentType.includes('text/html')) {
 | 
			
		||||
        const buffer = await response.buffer();
 | 
			
		||||
        const buffer = await page.content();
 | 
			
		||||
        const urlObj = new URL(url);
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			@ -882,12 +881,12 @@ export class KBService implements IGBKBService {
 | 
			
		|||
    try {
 | 
			
		||||
      if (
 | 
			
		||||
        depth > maxDepth ||
 | 
			
		||||
        (visited.has(url) ||
 | 
			
		||||
        visited.has(url) ||
 | 
			
		||||
        url.endsWith('.jpg') ||
 | 
			
		||||
        url.endsWith('.pdf') ||
 | 
			
		||||
        url.endsWith('.jpg') ||
 | 
			
		||||
        url.endsWith('.png') ||
 | 
			
		||||
          url.endsWith('.mp4'))
 | 
			
		||||
        url.endsWith('.mp4')
 | 
			
		||||
      ) {
 | 
			
		||||
        return [];
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -904,6 +903,7 @@ export class KBService implements IGBKBService {
 | 
			
		|||
      }
 | 
			
		||||
      const currentDomain = new URL(page.url()).hostname;
 | 
			
		||||
      let links = await page.evaluate(currentDomain => {
 | 
			
		||||
        
 | 
			
		||||
        const anchors = Array.from(document.querySelectorAll('a')).filter(p => {
 | 
			
		||||
          try {
 | 
			
		||||
            return currentDomain == new URL(p.href).hostname;
 | 
			
		||||
| 
						 | 
				
			
			@ -946,9 +946,41 @@ export class KBService implements IGBKBService {
 | 
			
		|||
      return [filename, ...childLinks]; // Include the filename of the cached file
 | 
			
		||||
    } catch (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.
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -960,27 +992,45 @@ export class KBService implements IGBKBService {
 | 
			
		|||
  ): Promise<any> {
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    if (website) {
 | 
			
		||||
      const browser = await puppeteer.launch({ headless: false });
 | 
			
		||||
      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
 | 
			
		||||
 | 
			
		||||
      await page.screenshot({ path: '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 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 maxDepth = 2; // Maximum depth of recursion
 | 
			
		||||
      const visited = new Set<string>();
 | 
			
		||||
      files = files.concat(await this.crawl(min, website, visited, 0, maxDepth, page));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -997,7 +1047,6 @@ export class KBService implements IGBKBService {
 | 
			
		|||
        await vectorStore.addDocuments(flattenedDocuments);
 | 
			
		||||
        await vectorStore.save(min['vectorStorePath']);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    files = await walkPromise(urlJoin(localPath, 'docs'));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue