123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- /* eslint-disable no-underscore-dangle */
- import browser from 'webextension-polyfill';
- import { objectHasKey, fileSaver, isObject } from '@/utils/helper';
- import { tasks } from '@/utils/shared';
- import dataExporter, { generateJSON } from '@/utils/data-exporter';
- import compareBlockValue from '@/utils/compare-block-value';
- import errorMessage from './error-message';
- import { executeWebhook } from '@/utils/webhookUtil';
- function getBlockConnection(block, index = 1) {
- const blockId = block.outputs[`output_${index}`]?.connections[0]?.node;
- return blockId;
- }
- function convertData(data, type) {
- let result = data;
- switch (type) {
- case 'integer':
- result = +data.replace(/\D+/g, '');
- break;
- case 'boolean':
- result = Boolean(data);
- break;
- default:
- }
- return result;
- }
- function generateBlockError(block, code) {
- const message = errorMessage(code || 'no-tab', tasks[block.name]);
- const error = new Error(message);
- error.nextBlockId = getBlockConnection(block);
- return error;
- }
- export async function closeTab(block) {
- const nextBlockId = getBlockConnection(block);
- try {
- const { data } = block;
- let tabIds;
- if (data.activeTab && this.tabId) {
- tabIds = this.tabId;
- } else if (data.url) {
- tabIds = (await browser.tabs.query({ url: data.url })).map(
- (tab) => tab.id
- );
- }
- if (tabIds) await browser.tabs.remove(tabIds);
- return {
- nextBlockId,
- data: '',
- };
- } catch (error) {
- const errorInstance = typeof error === 'string' ? new Error(error) : error;
- errorInstance.nextBlockId = nextBlockId;
- throw error;
- }
- }
- export async function trigger(block) {
- const nextBlockId = getBlockConnection(block);
- try {
- if (block.data.type === 'visit-web' && this.tabId) {
- await browser.tabs.executeScript(this.tabId, {
- file: './contentScript.bundle.js',
- });
- this._connectTab(this.tabId);
- }
- return { nextBlockId, data: '' };
- } catch (error) {
- const errorInstance = new Error(error);
- errorInstance.nextBlockId = nextBlockId;
- throw errorInstance;
- }
- }
- export function loopBreakpoint(block, prevBlockData) {
- return new Promise((resolve) => {
- const currentLoop = this.loopList[block.data.loopId];
- if (
- currentLoop &&
- currentLoop.index < currentLoop.maxLoop - 1 &&
- currentLoop.index <= currentLoop.data.length - 1
- ) {
- resolve({
- data: '',
- nextBlockId: currentLoop.blockId,
- });
- } else {
- resolve({
- data: prevBlockData,
- nextBlockId: getBlockConnection(block),
- });
- }
- });
- }
- export function loopData(block) {
- return new Promise((resolve) => {
- const { data } = block;
- if (this.loopList[data.loopId]) {
- this.loopList[data.loopId].index += 1;
- this.loopData[data.loopId] =
- this.loopList[data.loopId].data[this.loopList[data.loopId].index];
- } else {
- const currLoopData =
- data.loopThrough === 'data-columns'
- ? generateJSON(Object.keys(this.data), this.data)
- : JSON.parse(data.loopData);
- this.loopList[data.loopId] = {
- index: 0,
- data: currLoopData,
- id: data.loopId,
- blockId: block.id,
- maxLoop: data.maxLoop || currLoopData.length,
- };
- /* eslint-disable-next-line */
- this.loopData[data.loopId] = currLoopData[0];
- }
- resolve({
- data: this.loopData[data.loopId],
- nextBlockId: getBlockConnection(block),
- });
- });
- }
- export function goBack(block) {
- return new Promise((resolve, reject) => {
- const nextBlockId = getBlockConnection(block);
- if (!this.tabId) {
- reject(generateBlockError(block));
- return;
- }
- browser.tabs
- .goBack(this.tabId)
- .then(() => {
- resolve({
- nextBlockId,
- data: '',
- });
- })
- .catch((error) => {
- error.nextBlockId = nextBlockId;
- reject(error);
- });
- });
- }
- export function forwardPage(block) {
- return new Promise((resolve, reject) => {
- const nextBlockId = getBlockConnection(block);
- if (!this.tabId) {
- reject(generateBlockError(block));
- return;
- }
- browser.tabs
- .goForward(this.tabId)
- .then(() => {
- resolve({
- nextBlockId,
- data: '',
- });
- })
- .catch((error) => {
- error.nextBlockId = nextBlockId;
- reject(error);
- });
- });
- }
- function tabUpdatedListener(tab) {
- return new Promise((resolve, reject) => {
- this._listener({
- name: 'tab-updated',
- id: tab.id,
- once: true,
- callback: async (tabId, changeInfo, deleteListener) => {
- if (changeInfo.status !== 'complete') return;
- try {
- await browser.tabs.executeScript(tabId, {
- file: './contentScript.bundle.js',
- });
- deleteListener();
- this._connectTab(tabId);
- resolve();
- } catch (error) {
- console.error(error);
- reject(error);
- }
- },
- });
- });
- }
- export async function newTab(block) {
- try {
- const { updatePrevTab, url, active } = block.data;
- if (updatePrevTab && this.tabId) {
- await browser.tabs.update(this.tabId, { url, active });
- } else {
- const { id, windowId } = await browser.tabs.create({ url, active });
- this.tabId = id;
- this.windowId = windowId;
- }
- await tabUpdatedListener.call(this, { id: this.tabId });
- return {
- data: url,
- nextBlockId: getBlockConnection(block),
- };
- } catch (error) {
- console.error(error);
- throw error;
- }
- }
- export async function activeTab(block) {
- const nextBlockId = getBlockConnection(block);
- try {
- const data = {
- nextBlockId,
- data: '',
- };
- if (this.tabId) {
- await browser.tabs.update(this.tabId, { active: true });
- return data;
- }
- const [tab] = await browser.tabs.query({
- active: true,
- currentWindow: true,
- });
- await browser.tabs.executeScript(tab.id, {
- file: './contentScript.bundle.js',
- });
- this.tabId = tab.id;
- this.windowId = tab.windowId;
- this._connectTab(tab.id);
- return data;
- } catch (error) {
- console.error(error);
- return {
- data: '',
- message: error.message || error,
- nextBlockId,
- };
- }
- }
- export async function takeScreenshot(block) {
- const nextBlockId = getBlockConnection(block);
- const { ext, quality, captureActiveTab, fileName } = block.data;
- function saveImage(uri) {
- const image = new Image();
- image.onload = () => {
- const name = `${fileName || 'Screenshot'}.${ext || 'png'}`;
- const canvas = document.createElement('canvas');
- canvas.width = image.width;
- canvas.height = image.height;
- const context = canvas.getContext('2d');
- context.drawImage(image, 0, 0);
- fileSaver(name, canvas.toDataURL());
- };
- image.src = uri;
- }
- try {
- const options = {
- quality,
- format: ext || 'png',
- };
- if (captureActiveTab) {
- if (!this.tabId) {
- throw new Error(errorMessage('no-tab', block));
- }
- const [tab] = await browser.tabs.query({
- active: true,
- currentWindow: true,
- });
- await browser.windows.update(this.windowId, { focused: true });
- await browser.tabs.update(this.tabId, { active: true });
- await new Promise((resolve) => setTimeout(resolve, 500));
- const uri = await browser.tabs.captureVisibleTab(options);
- if (tab) {
- await browser.windows.update(tab.windowId, { focused: true });
- await browser.tabs.update(tab.id, { active: true });
- }
- saveImage(uri);
- } else {
- const uri = await browser.tabs.captureVisibleTab(options);
- saveImage(uri);
- }
- return { data: '', nextBlockId };
- } catch (error) {
- error.nextBlockId = nextBlockId;
- throw error;
- }
- }
- export function interactionHandler(block) {
- return new Promise((resolve, reject) => {
- const nextBlockId = getBlockConnection(block);
- if (!this.connectedTab) {
- reject(generateBlockError(block));
- return;
- }
- this.connectedTab.postMessage({ isBlock: true, ...block });
- this._listener({
- name: 'tab-message',
- id: block.name,
- once: true,
- delay: block.name === 'link' ? 5000 : 0,
- callback: (data) => {
- if (data?.isError) {
- const error = new Error(data.message);
- error.nextBlockId = nextBlockId;
- reject(error);
- return;
- }
- const getColumn = (name) =>
- this.workflow.dataColumns.find((item) => item.name === name) || {
- name: 'column',
- type: 'text',
- };
- const pushData = (column, value) => {
- this.data[column.name]?.push(convertData(value, column.type));
- };
- if (objectHasKey(block.data, 'dataColumn')) {
- const column = getColumn(block.data.dataColumn);
- if (block.data.saveData) {
- if (Array.isArray(data)) {
- data.forEach((item) => {
- pushData(column, item);
- });
- } else {
- pushData(column, data);
- }
- }
- } else if (block.name === 'javascript-code') {
- const memoColumn = {};
- const pushObjectData = (obj) => {
- Object.entries(obj).forEach(([key, value]) => {
- let column;
- if (memoColumn[key]) {
- column = memoColumn[key];
- } else {
- const currentColumn = getColumn(key);
- column = currentColumn;
- memoColumn[key] = currentColumn;
- }
- pushData(column, value);
- });
- };
- if (Array.isArray(data)) {
- data.forEach((obj) => {
- if (isObject(obj)) pushObjectData(obj);
- });
- } else if (isObject(data)) {
- pushObjectData(data);
- }
- }
- resolve({
- data,
- nextBlockId,
- });
- },
- });
- });
- }
- export function delay(block) {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve({
- nextBlockId: getBlockConnection(block),
- data: '',
- });
- }, block.data.time);
- });
- }
- export function exportData(block) {
- return new Promise((resolve) => {
- dataExporter(this.data, block.data);
- resolve({
- data: '',
- nextBlockId: getBlockConnection(block),
- });
- });
- }
- export function elementExists(block) {
- return new Promise((resolve, reject) => {
- if (!this.connectedTab) {
- reject(generateBlockError(block));
- return;
- }
- this.connectedTab.postMessage({ isBlock: true, ...block });
- this._listener({
- name: 'tab-message',
- id: block.name,
- once: true,
- callback: (data) => {
- resolve({
- data,
- nextBlockId: getBlockConnection(block, data ? 1 : 2),
- });
- },
- });
- });
- }
- export function conditions({ data, outputs }, prevBlockData) {
- return new Promise((resolve, reject) => {
- if (data.conditions.length === 0) {
- reject(new Error('Conditions is empty'));
- return;
- }
- let outputIndex = data.conditions.length + 1;
- let resultData = '';
- const prevData = Array.isArray(prevBlockData)
- ? prevBlockData[0]
- : prevBlockData;
- data.conditions.forEach(({ type, value }, index) => {
- const result = compareBlockValue(type, prevData, value);
- if (result) {
- resultData = value;
- outputIndex = index + 1;
- }
- });
- resolve({
- data: resultData,
- nextBlockId: getBlockConnection({ outputs }, outputIndex),
- });
- });
- }
- export function repeatTask({ data, id, outputs }) {
- return new Promise((resolve) => {
- if (this.repeatedTasks[id] >= data.repeatFor) {
- resolve({
- data: data.repeatFor,
- nextBlockId: getBlockConnection({ outputs }),
- });
- } else {
- this.repeatedTasks[id] = (this.repeatedTasks[id] || 1) + 1;
- resolve({
- data: data.repeatFor,
- nextBlockId: getBlockConnection({ outputs }, 2),
- });
- }
- });
- }
- export function webhook({ data, outputs }) {
- return new Promise((resolve, reject) => {
- if (!data.url) {
- reject(new Error('URL is empty'));
- return;
- }
- if (!data.url.startsWith('http')) {
- reject(new Error('URL is not valid'));
- return;
- }
- executeWebhook(data)
- .then(() => {
- resolve({
- data: '',
- nextBlockId: getBlockConnection({ outputs }),
- });
- })
- .catch((error) => {
- reject(error);
- });
- });
- }
|