✨ Add Google Perspective node (#1807)
* ✨ add google perspective node * ✨ add language option * ⚡ fix lint issues * ⚡ Cleanup * 🔥 Remove logging * ⚡ Type all languages * ⚡ Improvements Co-authored-by: ricardo <ricardoespinoza105@gmail.com> Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
const scopes = [
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
];
|
||||
|
||||
export class GooglePerspectiveOAuth2Api implements ICredentialType {
|
||||
name = 'googlePerspectiveOAuth2Api';
|
||||
extends = [
|
||||
'googleOAuth2Api',
|
||||
];
|
||||
displayName = 'Google Perspective OAuth2 API';
|
||||
documentationUrl = 'google';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'Scope',
|
||||
name: 'scope',
|
||||
type: 'hidden' as NodePropertyTypes,
|
||||
default: scopes.join(' '),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function googleApiRequest(
|
||||
this: IExecuteFunctions,
|
||||
method: 'POST',
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
) {
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method,
|
||||
body,
|
||||
uri: `https://commentanalyzer.googleapis.com${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.requestOAuth2.call(this, 'googlePerspectiveOAuth2Api', options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.perspective",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Development"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/perspective"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.perspective/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
AttributesValuesUi,
|
||||
CommentAnalyzeBody,
|
||||
Language,
|
||||
RequestedAttributes,
|
||||
} from './types';
|
||||
|
||||
import {
|
||||
googleApiRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
const ISO6391 = require('iso-639-1');
|
||||
|
||||
export class GooglePerspective implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Google Perspective',
|
||||
name: 'googlePerspective',
|
||||
icon: 'file:perspective.svg',
|
||||
group: [
|
||||
'transform',
|
||||
],
|
||||
version: 1,
|
||||
description: 'Consume Google Perspective API',
|
||||
subtitle: '={{$parameter["operation"]}}',
|
||||
defaults: {
|
||||
name: 'Google Perspective',
|
||||
color: '#200647',
|
||||
},
|
||||
inputs: [
|
||||
'main',
|
||||
],
|
||||
outputs: [
|
||||
'main',
|
||||
],
|
||||
credentials: [
|
||||
{
|
||||
name: 'googlePerspectiveOAuth2Api',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Analyze Comment',
|
||||
value: 'analyzeComment',
|
||||
},
|
||||
],
|
||||
default: 'analyzeComment',
|
||||
},
|
||||
{
|
||||
displayName: 'Text',
|
||||
name: 'text',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'analyzeComment',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Attributes to Analyze',
|
||||
name: 'requestedAttributesUi',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
placeholder: 'Add Atrribute',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'analyzeComment',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Properties',
|
||||
name: 'requestedAttributesValues',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Attribute Name',
|
||||
name: 'attributeName',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Flirtation',
|
||||
value: 'flirtation',
|
||||
},
|
||||
{
|
||||
name: 'Identity Attack',
|
||||
value: 'identity_attack',
|
||||
},
|
||||
{
|
||||
name: 'Insult',
|
||||
value: 'insult',
|
||||
},
|
||||
{
|
||||
name: 'Profanity',
|
||||
value: 'profanity',
|
||||
},
|
||||
{
|
||||
name: 'Severe Toxicity',
|
||||
value: 'severe_toxicity',
|
||||
},
|
||||
{
|
||||
name: 'Sexually Explicit',
|
||||
value: 'sexually_explicit',
|
||||
},
|
||||
{
|
||||
name: 'Threat',
|
||||
value: 'threat',
|
||||
},
|
||||
{
|
||||
name: 'Toxicity',
|
||||
value: 'toxicity',
|
||||
},
|
||||
],
|
||||
description: 'Attribute to analyze in the text. Details <a target="_blank" href="https://developers.perspectiveapi.com/s/about-the-api-attributes-and-languages">here</a>',
|
||||
default: 'flirtation',
|
||||
},
|
||||
{
|
||||
displayName: 'Score Threshold',
|
||||
name: 'scoreThreshold',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
numberStepSize: 0.1,
|
||||
numberPrecision: 2,
|
||||
minValue: 0,
|
||||
maxValue: 1,
|
||||
},
|
||||
description: 'Score above which to return results. At zero, all scores are returned.',
|
||||
default: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'analyzeComment',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Add Option',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Languages',
|
||||
name: 'languages',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLanguages',
|
||||
},
|
||||
default: '',
|
||||
description: 'Languages of the text input. If unspecified, the API will auto-detect the comment language',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
// Get all the available languages to display them to user so that he can
|
||||
// select them easily
|
||||
async getLanguages(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const supportedLanguages = [
|
||||
'English',
|
||||
'Spanish',
|
||||
'French',
|
||||
'German',
|
||||
'Portuguese',
|
||||
'Italian',
|
||||
'Russian',
|
||||
];
|
||||
|
||||
const languages = ISO6391.getAllNames().filter((language: string) => supportedLanguages.includes(language));
|
||||
for (const language of languages) {
|
||||
const languageName = language;
|
||||
const languageId = ISO6391.getCode(language);
|
||||
returnData.push({
|
||||
name: languageName,
|
||||
value: languageId,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
|
||||
const returnData: IDataObject[] = [];
|
||||
let responseData;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
|
||||
try {
|
||||
|
||||
|
||||
if (operation === 'analyzeComment') {
|
||||
|
||||
// https://developers.perspectiveapi.com/s/about-the-api-methods
|
||||
|
||||
const attributes = this.getNodeParameter(
|
||||
'requestedAttributesUi.requestedAttributesValues', i, [],
|
||||
) as AttributesValuesUi[];
|
||||
|
||||
if (!attributes.length) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Please enter at least one attribute to analyze.',
|
||||
);
|
||||
}
|
||||
|
||||
const requestedAttributes = attributes.reduce<RequestedAttributes>((acc, cur) => {
|
||||
return Object.assign(acc, {
|
||||
[cur.attributeName.toUpperCase()]: {
|
||||
scoreType: 'probability',
|
||||
scoreThreshold: cur.scoreThreshold,
|
||||
},
|
||||
});
|
||||
}, {});
|
||||
|
||||
const body: CommentAnalyzeBody = {
|
||||
comment: {
|
||||
type: 'PLAIN_TEXT',
|
||||
text: this.getNodeParameter('text', i) as string,
|
||||
},
|
||||
requestedAttributes,
|
||||
};
|
||||
|
||||
const { languages } = this.getNodeParameter('options', i) as { languages: Language };
|
||||
|
||||
if (languages?.length) {
|
||||
body.languages = languages;
|
||||
}
|
||||
|
||||
responseData = await googleApiRequest.call(this, 'POST', '/v1alpha1/comments:analyze', body);
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
Array.isArray(responseData)
|
||||
? returnData.push(...responseData)
|
||||
: returnData.push(responseData);
|
||||
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(responseData)];
|
||||
}
|
||||
}
|
||||
30
packages/nodes-base/nodes/Google/Perspective/perspective.svg
Normal file
30
packages/nodes-base/nodes/Google/Perspective/perspective.svg
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="6 10 125 125"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath937">
|
||||
<rect
|
||||
style="fill:#0000ff;fill-rule:evenodd"
|
||||
id="rect939"
|
||||
width="123.4947"
|
||||
height="119.02773"
|
||||
x="12.25"
|
||||
y="12.482271" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g
|
||||
id="g532"
|
||||
clip-path="url(#clipPath937)">
|
||||
<path
|
||||
d="M131.53 72a59.79 59.79 0 0 1-53.81 59.49v-119A59.79 59.79 0 0 1 131.53 72zM12.25 78v53.51h53.52A59.81 59.81 0 0 0 12.25 78zm0-12h53.52V12.51A59.81 59.81 0 0 0 12.25 66zm176.83-37.06H223c21.77 0 35.8 11.32 35.8 29.65s-14 29.64-35.8 29.64h-15.22v26.82h-18.7zm33.34 44c11.81 0 17.34-4.55 17.34-14.39s-5.53-14.4-17.34-14.4h-14.64V73zm37.14 11.72v-.61c0-19.93 12.91-33 32-33 18 0 31 12.67 31 33.46v4.18h-44.9c1 9.72 6 14.27 14.14 14.27 5.91 0 10.09-2.58 11.94-7h18.2c-2.7 12.42-14.64 21-30.26 21-19.82.04-32.12-11.96-32.12-32.3zm18.57-7.87h26.2c-1.72-8.12-6.27-12.18-12.91-12.18s-11.69 3.94-13.29 12.18zm54.73-23.74h17.59v9.47C354.26 55.88 361 52.31 370 52.31h3.56v17h-15.5c-4.67 0-7.13 2.46-7.13 7v38.75h-18.07zM378.12 96H395c.62 4.92 4.55 7.5 13.29 7.5 7.38 0 10.94-2.33 10.94-5.65 0-2.71-1.72-3.94-6.15-4.8l-14.79-2.85c-13.65-2.46-19.06-8.12-19.06-18.08 0-12.3 10-21 27.92-21 16.73 0 28.17 8.37 29 20.54h-16.82c-.74-4.42-4.8-7-12.3-7-7 0-10.46 2.22-10.46 5.54 0 2.58 1.72 3.81 6 4.67l14.88 2.83c13.53 2.46 18.94 8.12 18.94 18.21 0 12.91-10.45 21.15-28.29 21.15-18.79-.06-29.37-8.78-29.98-21.06zm68.13-42.95h17.59v9.84c3.7-7.38 10.71-11.56 19.56-11.56 14.89 0 26.33 11.68 26.33 32v.62c0 20.17-12.06 32.84-26.94 32.84-8.12 0-14.76-3.32-18.45-9.47v30.63h-18.09zM491 84.42v-1.6c0-11.32-4.92-17.35-13.16-17.35-8.61 0-13.53 5.91-13.53 17.23v2.7c0 12.18 5 17.22 13.16 17.22 7.9 0 13.53-6.62 13.53-18.2zm26.1.24v-.61c0-19.93 12.92-33 32-33 18 0 31 12.67 31 33.46v4.18h-44.9c1 9.72 6 14.27 14.15 14.27 5.9 0 10.08-2.58 11.93-7h18.2c-2.7 12.42-14.63 21-30.25 21-19.83.04-32.13-11.96-32.13-32.3zm18.57-7.87h26.21c-1.73-8.12-6.28-12.18-12.92-12.18s-11.69 3.94-13.29 12.18zm51.53 7.75v-.74c0-19.31 13.16-32.72 31.37-32.72 16.24 0 27.8 9.35 29.4 24.6h-17.71c-.74-6.64-5.42-10.45-11.44-10.45-7.88 0-13 6.52-13 18.2v1.48c0 11.81 5.16 18 12.79 18 6.52 0 10.95-4.06 11.81-11.2h17.71c-1.6 15.75-13 25.34-29.77 25.34C600 117 587.2 104.34 587.2 84.54zm76.01 9.35V67.32h-10.7V53.05h10.7v-18h18v18h18.21v14.27h-18.25v30.75c0 1.72.74 2.46 2.46 2.46h15.75v14.52H685.6c-14.52 0-22.39-7.51-22.39-21.16zm46-58.31a10.09 10.09 0 0 1 20.17 0 10.09 10.09 0 1 1-20.17 0zm1 17.47h18.08v62h-18.1zm25.19 0H754l14.39 45.27 14.27-45.27h17.71l-20.91 62h-23.15zm68.88 31.61v-.61c0-19.93 12.92-33 32-33 18 0 31 12.67 31 33.46v4.18h-44.9c1 9.72 6 14.27 14.15 14.27 5.9 0 10.09-2.58 11.93-7h18.21c-2.71 12.42-14.64 21-30.26 21-19.83.04-32.13-11.96-32.13-32.3zm18.57-7.87h26.21c-1.73-8.12-6.28-12.18-12.92-12.18s-11.69 3.94-13.29 12.18z"
|
||||
fill="#451735"
|
||||
id="path2" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
26
packages/nodes-base/nodes/Google/Perspective/types.d.ts
vendored
Normal file
26
packages/nodes-base/nodes/Google/Perspective/types.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
export type CommentAnalyzeBody = {
|
||||
comment: Comment;
|
||||
requestedAttributes: RequestedAttributes;
|
||||
languages?: Language;
|
||||
};
|
||||
|
||||
export type Language = 'de' | 'en' | 'fr' | 'ar' | 'es' | 'it' | 'pt' | 'ru';
|
||||
|
||||
export type Comment = {
|
||||
text?: string;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
export type RequestedAttributes = {
|
||||
[key: string]: {
|
||||
scoreType?: string;
|
||||
scoreThreshold?: {
|
||||
value: number
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type AttributesValuesUi = {
|
||||
attributeName: string;
|
||||
scoreThreshold: number;
|
||||
};
|
||||
@@ -111,6 +111,7 @@
|
||||
"dist/credentials/GoogleFirebaseCloudFirestoreOAuth2Api.credentials.js",
|
||||
"dist/credentials/GoogleFirebaseRealtimeDatabaseOAuth2Api.credentials.js",
|
||||
"dist/credentials/GoogleOAuth2Api.credentials.js",
|
||||
"dist/credentials/GooglePerspectiveOAuth2Api.credentials.js",
|
||||
"dist/credentials/GoogleSheetsOAuth2Api.credentials.js",
|
||||
"dist/credentials/GoogleSlidesOAuth2Api.credentials.js",
|
||||
"dist/credentials/GSuiteAdminOAuth2Api.credentials.js",
|
||||
@@ -401,6 +402,7 @@
|
||||
"dist/nodes/Google/Firebase/RealtimeDatabase/RealtimeDatabase.node.js",
|
||||
"dist/nodes/Google/Gmail/Gmail.node.js",
|
||||
"dist/nodes/Google/GSuiteAdmin/GSuiteAdmin.node.js",
|
||||
"dist/nodes/Google/Perspective/GooglePerspective.node.js",
|
||||
"dist/nodes/Google/Sheet/GoogleSheets.node.js",
|
||||
"dist/nodes/Google/Slides/GoogleSlides.node.js",
|
||||
"dist/nodes/Google/Task/GoogleTasks.node.js",
|
||||
|
||||
Reference in New Issue
Block a user