mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
✨ Add Reddit Node (#1345)
* Set up initial scaffolding and auth
* Add grant type to credentials
* Add Account operations and OAuth2 request
* Add post submission functionality
* Refactor resources into resource descriptions
* Refactor request for no auth URL
* Refactor submission description for consistency
* Add listing resource
* Refactor My Account resource into details
* Add request for all items
* Add listings for specific subreddits
* Fix minor linting details
* Add subreddit resource
* Add All-Reddit and Subreddit resource descriptions
* Adjust display options for credentials
* Add subreddit search functionality
* Clean up auth parameter
* Add user resource with GET endpoint
* Add user description
* Add submission search and commenting functionality
* Clean up logging and comments
* Fix minor details
* Fix casing in properties
* Add dividers to execute() method
* Refactor per feedback
* Remove unused description
* Add punctuation to property descriptions
* Fix resources indentation
* Add resource dividers
* Remove deprecated sidebar option
* Make subreddit:get responses consistent
* Remove returnAll and limit from subreddit:get
* Flatten user:get response for about
* Rename comment target property
* Remove best property from post:getAll
* Enrich subreddit search by keyword operation
* Remove unneeded scopes
* Add endpoint documentation
* Add scaffolding for post comment
* Add all operations for postComment resource
* Add all operations for post resource
* Refactor subreddit:getAll
* Fix postComment:getAll
* Flatten responses for profile:get
* ⚡ Improvements
* Fix response traversal for postComment:add
* Flatten response for postComment:reply
* Fix subreddit:getAll with keyword search
* Fix pagination to enforce limit
* Wrap unauthenticated API call in try-catch block
* Add 404 error for empty array responses
* Revert "Fix pagination to enforce limit"
This reverts commit 72548d9523
.
* Turn user:get (gilded) into listing
* ⚡ Small improvement
* ⚡ Improve Reddit-Node
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
d0ac4e8fbe
commit
eb2f14d06d
|
@ -0,0 +1,69 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
const scopes = [
|
||||||
|
'identity',
|
||||||
|
'edit',
|
||||||
|
'history',
|
||||||
|
'mysubreddits',
|
||||||
|
'read',
|
||||||
|
'save',
|
||||||
|
'submit',
|
||||||
|
];
|
||||||
|
|
||||||
|
// https://github.com/reddit-archive/reddit/wiki/OAuth2
|
||||||
|
|
||||||
|
export class RedditOAuth2Api implements ICredentialType {
|
||||||
|
name = 'redditOAuth2Api';
|
||||||
|
extends = [
|
||||||
|
'oAuth2Api',
|
||||||
|
];
|
||||||
|
displayName = 'Reddit OAuth2 API';
|
||||||
|
documentationUrl = 'reddit';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'response_type=code',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'grant_type=authorization_code',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'duration=permanent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Authorization URL',
|
||||||
|
name: 'authUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://www.reddit.com/api/v1/authorize',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access Token URL',
|
||||||
|
name: 'accessTokenUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://www.reddit.com/api/v1/access_token',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Scope',
|
||||||
|
name: 'scope',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: scopes.join(' '),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Authentication',
|
||||||
|
name: 'authentication',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'header',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
145
packages/nodes-base/nodes/Reddit/GenericFunctions.ts
Normal file
145
packages/nodes-base/nodes/Reddit/GenericFunctions.ts
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
IHookFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an authenticated or unauthenticated API request to Reddit.
|
||||||
|
*/
|
||||||
|
export async function redditApiRequest(
|
||||||
|
this: IHookFunctions | IExecuteFunctions,
|
||||||
|
method: string,
|
||||||
|
endpoint: string,
|
||||||
|
qs: IDataObject,
|
||||||
|
): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
|
||||||
|
const authRequired = ['profile', 'post', 'postComment'].includes(resource);
|
||||||
|
|
||||||
|
qs.api_type = 'json';
|
||||||
|
|
||||||
|
const options: OptionsWithUri = {
|
||||||
|
headers: {
|
||||||
|
'user-agent': 'n8n',
|
||||||
|
},
|
||||||
|
method,
|
||||||
|
uri: authRequired ? `https://oauth.reddit.com/${endpoint}` : `https://www.reddit.com/${endpoint}`,
|
||||||
|
qs,
|
||||||
|
json: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!Object.keys(qs).length) {
|
||||||
|
delete options.qs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authRequired) {
|
||||||
|
let response;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await this.helpers.requestOAuth2.call(this, 'redditOAuth2Api', options);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response.body && error.response.body.message) {
|
||||||
|
const message = error.response.body.message;
|
||||||
|
throw new Error(`Reddit error response [${error.statusCode}]: ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((response.errors && response.errors.length !== 0) || (response.json && response.json.errors && response.json.errors.length !== 0)) {
|
||||||
|
const errors = response?.errors || response?.json?.errors;
|
||||||
|
const errorMessage = errors.map((error: []) => error.join('-'));
|
||||||
|
|
||||||
|
throw new Error(`Reddit error response [400]: ${errorMessage.join('|')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await this.helpers.request.call(this, options);
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error?.response?.body?.message;
|
||||||
|
if (errorMessage) {
|
||||||
|
throw new Error(`Reddit error response [${error.statusCode}]: ${errorMessage}`);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an unauthenticated API request to Reddit and return all results.
|
||||||
|
*/
|
||||||
|
export async function redditApiRequestAllItems(
|
||||||
|
this: IHookFunctions | IExecuteFunctions,
|
||||||
|
method: string,
|
||||||
|
endpoint: string,
|
||||||
|
qs: IDataObject,
|
||||||
|
): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0, false) as boolean;
|
||||||
|
|
||||||
|
qs.limit = 100;
|
||||||
|
|
||||||
|
do {
|
||||||
|
responseData = await redditApiRequest.call(this, method, endpoint, qs);
|
||||||
|
if (!Array.isArray(responseData)) {
|
||||||
|
qs.after = responseData.data.after;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endpoint === 'api/search_subreddits.json') {
|
||||||
|
responseData.subreddits.forEach((child: any) => returnData.push(child)); // tslint:disable-line:no-any
|
||||||
|
} else if (resource === 'postComment' && operation === 'getAll') {
|
||||||
|
responseData[1].data.children.forEach((child: any) => returnData.push(child.data)); // tslint:disable-line:no-any
|
||||||
|
} else {
|
||||||
|
responseData.data.children.forEach((child: any) => returnData.push(child.data)); // tslint:disable-line:no-any
|
||||||
|
}
|
||||||
|
if (qs.limit && returnData.length >= qs.limit && returnAll === false) {
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (responseData.data && responseData.data.after);
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a large Reddit listing by returning all items or up to a limit.
|
||||||
|
*/
|
||||||
|
export async function handleListing(
|
||||||
|
this: IExecuteFunctions,
|
||||||
|
i: number,
|
||||||
|
endpoint: string,
|
||||||
|
qs: IDataObject = {},
|
||||||
|
requestMethod: 'GET' | 'POST' = 'GET',
|
||||||
|
): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await redditApiRequestAllItems.call(this, requestMethod, endpoint, qs);
|
||||||
|
} else {
|
||||||
|
const limit = this.getNodeParameter('limit', i);
|
||||||
|
qs.limit = limit;
|
||||||
|
responseData = await redditApiRequestAllItems.call(this, requestMethod, endpoint, qs);
|
||||||
|
responseData = responseData.slice(0, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData;
|
||||||
|
}
|
231
packages/nodes-base/nodes/Reddit/PostCommentDescription.ts
Normal file
231
packages/nodes-base/nodes/Reddit/PostCommentDescription.ts
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const postCommentOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
default: 'create',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a top-level comment in a post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Retrieve all comments in a post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
description: 'Remove a comment from a post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Reply',
|
||||||
|
value: 'reply',
|
||||||
|
description: 'Write a reply to a comment in a post',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const postCommentFields = [
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: create
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post to write the comment to. Found in the post URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]</code>',
|
||||||
|
placeholder: 'l0me7x',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Comment Text',
|
||||||
|
name: 'commentText',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Text of the comment. Markdown supported.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of subreddit where the post is.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post to get all comments from. Found in the post URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]</code>',
|
||||||
|
placeholder: 'l0me7x',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: delete
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Comment ID',
|
||||||
|
name: 'commentId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the comment to remove. Found in the comment URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]/[comment_id]</code>',
|
||||||
|
placeholder: 'gla7fmt',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: reply
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Comment ID',
|
||||||
|
name: 'commentId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the comment to reply to. To be found in the comment URL:<br><code>www.reddit.com/r/[subreddit_name]/comments/[post_id]/[post_title]/[comment_id]</code>',
|
||||||
|
placeholder: 'gl9iroa',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'reply',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Reply Text',
|
||||||
|
name: 'replyText',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Text of the reply. Markdown supported.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'reply',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
351
packages/nodes-base/nodes/Reddit/PostDescription.ts
Normal file
351
packages/nodes-base/nodes/Reddit/PostDescription.ts
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const postOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
default: 'create',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Submit a post to a subreddit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
description: 'Delete a post from a subreddit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a post from a subreddit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all posts from a subreddit',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const postFields = [
|
||||||
|
// ----------------------------------
|
||||||
|
// post: create
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Subreddit to create the post in.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Kind',
|
||||||
|
name: 'kind',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Text Post',
|
||||||
|
value: 'self',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Link Post',
|
||||||
|
value: 'link',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Image Post',
|
||||||
|
value: 'image',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'self',
|
||||||
|
description: 'The kind of the post to create.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Title of the post, up to 300 characters long.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'URL',
|
||||||
|
name: 'url',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'URL of the post.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
kind: [
|
||||||
|
'link',
|
||||||
|
'image',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'text',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Text of the post. Markdown supported.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
kind: [
|
||||||
|
'self',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Resubmit',
|
||||||
|
name: 'resubmit',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'If toggled on, the URL will be posted even if<br>it was already posted to the subreddit before.<br>Otherwise, the re-posting will trigger an error.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
kind: [
|
||||||
|
'link',
|
||||||
|
'image',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// post: delete
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post to delete. Found in the post URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]</code>',
|
||||||
|
placeholder: 'gla7fmt',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// post: get
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of subreddit to retrieve the post from.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post to retrieve. Found in the post URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]</code>',
|
||||||
|
placeholder: 'l0me7x',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// post: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of subreddit to retrieve the posts from.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filters',
|
||||||
|
type: 'collection',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Category',
|
||||||
|
name: 'category',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
default: 'top',
|
||||||
|
description: 'Category of the posts to retrieve.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Top Posts',
|
||||||
|
value: 'top',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Hot Posts',
|
||||||
|
value: 'hot',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'New Posts',
|
||||||
|
value: 'new',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Rising Posts',
|
||||||
|
value: 'rising',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
80
packages/nodes-base/nodes/Reddit/ProfileDescription.ts
Normal file
80
packages/nodes-base/nodes/Reddit/ProfileDescription.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const profileOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'profile',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'get',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
|
||||||
|
export const profileFields = [
|
||||||
|
{
|
||||||
|
displayName: 'Details',
|
||||||
|
name: 'details',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
default: 'identity',
|
||||||
|
description: 'Details of my account to retrieve.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Identity',
|
||||||
|
value: 'identity',
|
||||||
|
description: 'Return the identity of the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Blocked Users',
|
||||||
|
value: 'blockedUsers',
|
||||||
|
description: 'Return the blocked users of the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Friends',
|
||||||
|
value: 'friends',
|
||||||
|
description: 'Return the friends of the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Karma',
|
||||||
|
value: 'karma',
|
||||||
|
description: 'Return the subreddit karma for the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Preferences',
|
||||||
|
value: 'prefs',
|
||||||
|
description: 'Return the settings preferences of the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Trophies',
|
||||||
|
value: 'trophies',
|
||||||
|
description: 'Return the trophies of the logged-in user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'profile',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
425
packages/nodes-base/nodes/Reddit/Reddit.node.ts
Normal file
425
packages/nodes-base/nodes/Reddit/Reddit.node.ts
Normal file
|
@ -0,0 +1,425 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
handleListing,
|
||||||
|
redditApiRequest,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
postCommentFields,
|
||||||
|
postCommentOperations,
|
||||||
|
} from './PostCommentDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
postFields,
|
||||||
|
postOperations,
|
||||||
|
} from './PostDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
profileFields,
|
||||||
|
profileOperations,
|
||||||
|
} from './ProfileDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
subredditFields,
|
||||||
|
subredditOperations,
|
||||||
|
} from './SubredditDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
userFields,
|
||||||
|
userOperations,
|
||||||
|
} from './UserDescription';
|
||||||
|
|
||||||
|
export class Reddit implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Reddit',
|
||||||
|
name: 'reddit',
|
||||||
|
icon: 'file:reddit.svg',
|
||||||
|
group: ['transform'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
description: 'Consume the Reddit API',
|
||||||
|
defaults: {
|
||||||
|
name: 'Reddit',
|
||||||
|
color: '#ff5700',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'redditOAuth2Api',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
'post',
|
||||||
|
'profile',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Resource',
|
||||||
|
name: 'resource',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Post',
|
||||||
|
value: 'post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Post Comment',
|
||||||
|
value: 'postComment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Profile',
|
||||||
|
value: 'profile',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Subreddit',
|
||||||
|
value: 'subreddit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'User',
|
||||||
|
value: 'user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'post',
|
||||||
|
description: 'Resource to consume',
|
||||||
|
},
|
||||||
|
...postCommentOperations,
|
||||||
|
...postCommentFields,
|
||||||
|
...profileOperations,
|
||||||
|
...profileFields,
|
||||||
|
...subredditOperations,
|
||||||
|
...subredditFields,
|
||||||
|
...postOperations,
|
||||||
|
...postFields,
|
||||||
|
...userOperations,
|
||||||
|
...userFields,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
|
||||||
|
// *********************************************************************
|
||||||
|
// post
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (resource === 'post') {
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
// ----------------------------------
|
||||||
|
// post: create
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_submit
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
title: this.getNodeParameter('title', i),
|
||||||
|
sr: this.getNodeParameter('subreddit', i),
|
||||||
|
kind: this.getNodeParameter('kind', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
qs.kind === 'self'
|
||||||
|
? qs.text = this.getNodeParameter('text', i)
|
||||||
|
: qs.url = this.getNodeParameter('url', i);
|
||||||
|
|
||||||
|
if (qs.url) {
|
||||||
|
qs.resubmit = this.getNodeParameter('resubmit', i);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'POST', 'api/submit', qs);
|
||||||
|
|
||||||
|
responseData = responseData.json.data;
|
||||||
|
|
||||||
|
} else if (operation === 'delete') {
|
||||||
|
// ----------------------------------
|
||||||
|
// post: delete
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_del
|
||||||
|
|
||||||
|
const postTypePrefix = 't3_';
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
id: postTypePrefix + this.getNodeParameter('postId', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
await redditApiRequest.call(this, 'POST', 'api/del', qs);
|
||||||
|
|
||||||
|
responseData = { success: true };
|
||||||
|
|
||||||
|
} else if (operation === 'get') {
|
||||||
|
// ----------------------------------
|
||||||
|
// post: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const subreddit = this.getNodeParameter('subreddit', i);
|
||||||
|
const postId = this.getNodeParameter('postId', i) as string;
|
||||||
|
const endpoint = `r/${subreddit}/comments/${postId}.json`;
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
responseData = responseData[0].data.children[0].data;
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
// ----------------------------------
|
||||||
|
// post: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_hot
|
||||||
|
// https://www.reddit.com/dev/api/#GET_new
|
||||||
|
// https://www.reddit.com/dev/api/#GET_rising
|
||||||
|
// https://www.reddit.com/dev/api/#GET_{sort}
|
||||||
|
|
||||||
|
const subreddit = this.getNodeParameter('subreddit', i);
|
||||||
|
let endpoint = `r/${subreddit}.json`;
|
||||||
|
|
||||||
|
const { category } = this.getNodeParameter('filters', i) as { category: string };
|
||||||
|
if (category) {
|
||||||
|
endpoint = `r/${subreddit}/${category}.json`;
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await handleListing.call(this, i, endpoint);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'postComment') {
|
||||||
|
|
||||||
|
// *********************************************************************
|
||||||
|
// postComment
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: create
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_comment
|
||||||
|
|
||||||
|
const postTypePrefix = 't3_';
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
text: this.getNodeParameter('commentText', i),
|
||||||
|
thing_id: postTypePrefix + this.getNodeParameter('postId', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'POST', 'api/comment', qs);
|
||||||
|
responseData = responseData.json.data.things[0].data;
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/r/{subrreddit}/comments/{postId}.json
|
||||||
|
|
||||||
|
const subreddit = this.getNodeParameter('subreddit', i);
|
||||||
|
const postId = this.getNodeParameter('postId', i) as string;
|
||||||
|
const endpoint = `r/${subreddit}/comments/${postId}.json`;
|
||||||
|
|
||||||
|
responseData = await handleListing.call(this, i, endpoint);
|
||||||
|
|
||||||
|
} else if (operation === 'delete') {
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: delete
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_del
|
||||||
|
|
||||||
|
const commentTypePrefix = 't1_';
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
id: commentTypePrefix + this.getNodeParameter('commentId', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
await redditApiRequest.call(this, 'POST', 'api/del', qs);
|
||||||
|
|
||||||
|
responseData = { success: true };
|
||||||
|
|
||||||
|
} else if (operation === 'reply') {
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: reply
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_comment
|
||||||
|
|
||||||
|
const commentTypePrefix = 't1_';
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
text: this.getNodeParameter('replyText', i),
|
||||||
|
thing_id: commentTypePrefix + this.getNodeParameter('commentId', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'POST', 'api/comment', qs);
|
||||||
|
responseData = responseData.json.data.things[0].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'profile') {
|
||||||
|
// *********************************************************************
|
||||||
|
// pprofile
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
// ----------------------------------
|
||||||
|
// profile: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_v1_me
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_v1_me_karma
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_v1_me_prefs
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_v1_me_trophies
|
||||||
|
// https://www.reddit.com/dev/api/#GET_prefs_{where}
|
||||||
|
|
||||||
|
const endpoints: { [key: string]: string } = {
|
||||||
|
identity: 'me',
|
||||||
|
blockedUsers: 'me/blocked',
|
||||||
|
friends: 'me/friends',
|
||||||
|
karma: 'me/karma',
|
||||||
|
prefs: 'me/prefs',
|
||||||
|
trophies: 'me/trophies',
|
||||||
|
};
|
||||||
|
|
||||||
|
const details = this.getNodeParameter('details', i) as string;
|
||||||
|
const endpoint = `api/v1/${endpoints[details]}`;
|
||||||
|
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
|
||||||
|
if (details === 'identity') {
|
||||||
|
responseData = responseData.features;
|
||||||
|
} else if (details === 'friends') {
|
||||||
|
responseData = responseData.data.children;
|
||||||
|
if (!responseData.length) {
|
||||||
|
throw new Error('Reddit error response [404]: Not Found');
|
||||||
|
}
|
||||||
|
} else if (details === 'karma') {
|
||||||
|
responseData = responseData.data;
|
||||||
|
if (!responseData.length) {
|
||||||
|
throw new Error('Reddit error response [404]: Not Found');
|
||||||
|
}
|
||||||
|
} else if (details === 'trophies') {
|
||||||
|
responseData = responseData.data.trophies.map((trophy: IDataObject) => trophy.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'subreddit') {
|
||||||
|
|
||||||
|
// *********************************************************************
|
||||||
|
// subreddit
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
// ----------------------------------
|
||||||
|
// subreddit: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_r_{subreddit}_about
|
||||||
|
// https://www.reddit.com/dev/api/#GET_r_{subreddit}_about_rules
|
||||||
|
|
||||||
|
const subreddit = this.getNodeParameter('subreddit', i);
|
||||||
|
const content = this.getNodeParameter('content', i) as string;
|
||||||
|
const endpoint = `r/${subreddit}/about/${content}.json`;
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
|
||||||
|
if (content === 'rules') {
|
||||||
|
responseData = responseData.rules;
|
||||||
|
} else if (content === 'about') {
|
||||||
|
responseData = responseData.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
// ----------------------------------
|
||||||
|
// subreddit: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_trending_subreddits
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_search_subreddits
|
||||||
|
// https://www.reddit.com/r/subreddits.json
|
||||||
|
|
||||||
|
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
||||||
|
|
||||||
|
if (filters.trending) {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
const endpoint = 'api/trending_subreddits.json';
|
||||||
|
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
responseData = responseData.subreddit_names.map((name: string) => ({ name }));
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', 0) as number;
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (filters.keyword) {
|
||||||
|
const qs: IDataObject = {};
|
||||||
|
qs.query = filters.keyword;
|
||||||
|
|
||||||
|
const endpoint = 'api/search_subreddits.json';
|
||||||
|
responseData = await redditApiRequest.call(this, 'POST', endpoint, qs);
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', 0) as number;
|
||||||
|
responseData = responseData.subreddits.splice(0, limit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const endpoint = 'r/subreddits.json';
|
||||||
|
responseData = await handleListing.call(this, i, endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'user') {
|
||||||
|
// *********************************************************************
|
||||||
|
// user
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// user: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_user_{username}_{where}
|
||||||
|
|
||||||
|
const username = this.getNodeParameter('username', i) as string;
|
||||||
|
const details = this.getNodeParameter('details', i) as string;
|
||||||
|
const endpoint = `user/${username}/${details}.json`;
|
||||||
|
|
||||||
|
responseData = details === 'about'
|
||||||
|
? await redditApiRequest.call(this, 'GET', endpoint, {})
|
||||||
|
: await handleListing.call(this, i, endpoint);
|
||||||
|
|
||||||
|
if (details === 'about') {
|
||||||
|
responseData = responseData.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.isArray(responseData)
|
||||||
|
? returnData.push(...responseData)
|
||||||
|
: returnData.push(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
|
}
|
||||||
|
}
|
162
packages/nodes-base/nodes/Reddit/SubredditDescription.ts
Normal file
162
packages/nodes-base/nodes/Reddit/SubredditDescription.ts
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const subredditOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
default: 'get',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Retrieve background information about a subreddit.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Retrieve information about subreddits from all of Reddit.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const subredditFields = [
|
||||||
|
// ----------------------------------
|
||||||
|
// subreddit: get
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Content',
|
||||||
|
name: 'content',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
default: 'about',
|
||||||
|
description: 'Subreddit content to retrieve.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'About',
|
||||||
|
value: 'about',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Rules',
|
||||||
|
value: 'rules',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of subreddit to retrieve the content from.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// subreddit: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filters',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Keyword',
|
||||||
|
name: 'keyword',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The keyword for the subreddit search.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Trending',
|
||||||
|
name: 'trending',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Currently trending subreddits in all of Reddit.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
140
packages/nodes-base/nodes/Reddit/UserDescription.ts
Normal file
140
packages/nodes-base/nodes/Reddit/UserDescription.ts
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const userOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'get',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const userFields = [
|
||||||
|
{
|
||||||
|
displayName: 'Username',
|
||||||
|
name: 'username',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Reddit ID of the user to retrieve.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Details',
|
||||||
|
name: 'details',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
default: 'about',
|
||||||
|
description: 'Details of the user to retrieve.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'About',
|
||||||
|
value: 'about',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Comments',
|
||||||
|
value: 'comments',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Gilded',
|
||||||
|
value: 'gilded',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Overview',
|
||||||
|
value: 'overview',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Submitted',
|
||||||
|
value: 'submitted',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
details: [
|
||||||
|
'overview',
|
||||||
|
'submitted',
|
||||||
|
'comments',
|
||||||
|
'gilded',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
details: [
|
||||||
|
'comments',
|
||||||
|
'gilded',
|
||||||
|
'overview',
|
||||||
|
'submitted',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
1
packages/nodes-base/nodes/Reddit/reddit.svg
Normal file
1
packages/nodes-base/nodes/Reddit/reddit.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 513 514" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x=".5" y=".5"/><symbol id="A" overflow="visible"><g stroke="none" fill-rule="nonzero"><path d="M0 76.8C0 34.253 34.253 0 76.8 0h358.4C477.747 0 512 34.253 512 76.8v358.4c0 42.547-34.253 76.8-76.8 76.8H76.8C34.253 512 0 477.747 0 435.2z" fill="#ff4500"/><path d="M79 305c0-68.142 78.942-123 177-123s177 54.858 177 123-78.942 123-177 123S79 373.142 79 305z"/><g fill="#ff4500"><path d="M199 347c35 29 79 29 114 0l12 11c-42 35-96 35-138 0z"/><use xlink:href="#C"/><use xlink:href="#C" x="-118"/></g></g><g stroke="#fff" fill="none"><use xlink:href="#C" x="75" y="-160" stroke-width="25"/><path d="M87 282c-45-22-5-92 40-50m298 50c45-22 5-92-40-50m-127-45l24-83 80 16" stroke-width="22"/></g></symbol><defs ><path id="C" d="M287 285a27.94 27.94 0 1 1 56 0 27.94 27.94 0 1 1-56 0z"/></defs></svg>
|
After Width: | Height: | Size: 1,010 B |
|
@ -177,6 +177,7 @@
|
||||||
"dist/credentials/QuestDb.credentials.js",
|
"dist/credentials/QuestDb.credentials.js",
|
||||||
"dist/credentials/QuickBaseApi.credentials.js",
|
"dist/credentials/QuickBaseApi.credentials.js",
|
||||||
"dist/credentials/RabbitMQ.credentials.js",
|
"dist/credentials/RabbitMQ.credentials.js",
|
||||||
|
"dist/credentials/RedditOAuth2Api.credentials.js",
|
||||||
"dist/credentials/Redis.credentials.js",
|
"dist/credentials/Redis.credentials.js",
|
||||||
"dist/credentials/RocketchatApi.credentials.js",
|
"dist/credentials/RocketchatApi.credentials.js",
|
||||||
"dist/credentials/RundeckApi.credentials.js",
|
"dist/credentials/RundeckApi.credentials.js",
|
||||||
|
@ -425,6 +426,7 @@
|
||||||
"dist/nodes/ReadBinaryFile.node.js",
|
"dist/nodes/ReadBinaryFile.node.js",
|
||||||
"dist/nodes/ReadBinaryFiles.node.js",
|
"dist/nodes/ReadBinaryFiles.node.js",
|
||||||
"dist/nodes/ReadPdf.node.js",
|
"dist/nodes/ReadPdf.node.js",
|
||||||
|
"dist/nodes/Reddit/Reddit.node.js",
|
||||||
"dist/nodes/Redis/Redis.node.js",
|
"dist/nodes/Redis/Redis.node.js",
|
||||||
"dist/nodes/RenameKeys.node.js",
|
"dist/nodes/RenameKeys.node.js",
|
||||||
"dist/nodes/Rocketchat/Rocketchat.node.js",
|
"dist/nodes/Rocketchat/Rocketchat.node.js",
|
||||||
|
|
Loading…
Reference in a new issue