n8n-nodes-glimpse 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/credentials/GlimpseApi.credentials.d.ts +8 -0
- package/dist/credentials/GlimpseApi.credentials.js +38 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +9 -0
- package/dist/nodes/Glimpse/Glimpse.node.d.ts +5 -0
- package/dist/nodes/Glimpse/Glimpse.node.js +177 -0
- package/dist/nodes/Glimpse/GlimpseTrigger.node.d.ts +5 -0
- package/dist/nodes/Glimpse/GlimpseTrigger.node.js +96 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Glimpse HQ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# n8n-nodes-glimpse
|
|
2
|
+
|
|
3
|
+
This is an [n8n](https://n8n.io/) community node. It lets you use [Glimpse](https://www.glimpsehq.io) in your n8n workflows.
|
|
4
|
+
|
|
5
|
+
Glimpse is a competitive intelligence platform that monitors your competitors across 20+ signal types - from blog posts and pricing changes to job postings, ad campaigns, tech stack updates, and more. This node lets you pull those signals into n8n workflows and trigger automations when new competitive activity is detected.
|
|
6
|
+
|
|
7
|
+
[n8n community nodes docs](https://docs.n8n.io/integrations/community-nodes/)
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
|
|
12
|
+
|
|
13
|
+
## Credentials
|
|
14
|
+
|
|
15
|
+
You need a Glimpse API key to authenticate:
|
|
16
|
+
|
|
17
|
+
1. Log into [Glimpse](https://app.www.glimpsehq.io)
|
|
18
|
+
2. Go to **Settings > Integrations**
|
|
19
|
+
3. Under **API Keys**, click **Create new key**
|
|
20
|
+
4. Give it a name (e.g. "n8n") and copy the key (starts with `glmp_`)
|
|
21
|
+
5. In n8n, create a new **Glimpse API** credential and paste the key
|
|
22
|
+
|
|
23
|
+
> The key is only shown once at creation. Store it securely.
|
|
24
|
+
|
|
25
|
+
| Field | Description |
|
|
26
|
+
|-------|-------------|
|
|
27
|
+
| API Key | Your `glmp_...` key from Glimpse settings |
|
|
28
|
+
| Base URL | Default: `https://app.www.glimpsehq.io`. Override for staging or self-hosted. |
|
|
29
|
+
|
|
30
|
+
## Operations
|
|
31
|
+
|
|
32
|
+
### Glimpse node
|
|
33
|
+
|
|
34
|
+
The Glimpse node supports reading competitors and signals.
|
|
35
|
+
|
|
36
|
+
**Competitor**
|
|
37
|
+
|
|
38
|
+
| Operation | Description |
|
|
39
|
+
|-----------|-------------|
|
|
40
|
+
| Get All | List all monitored competitors in your organization |
|
|
41
|
+
| Get | Retrieve a single competitor by ID |
|
|
42
|
+
|
|
43
|
+
**Signal**
|
|
44
|
+
|
|
45
|
+
| Operation | Description |
|
|
46
|
+
|-----------|-------------|
|
|
47
|
+
| Get All | Fetch competitive signals with optional filters |
|
|
48
|
+
|
|
49
|
+
Signal filters:
|
|
50
|
+
|
|
51
|
+
- **Competitor IDs** - comma-separated UUIDs to scope results
|
|
52
|
+
- **Signal Types** - filter by one or more of 23 signal types
|
|
53
|
+
- **Days** - only return signals from the last N days
|
|
54
|
+
- **Limit** - max results per request (up to 500)
|
|
55
|
+
|
|
56
|
+
### Glimpse Trigger node
|
|
57
|
+
|
|
58
|
+
The Glimpse Trigger node polls for new competitive signals and fires when new activity is detected.
|
|
59
|
+
|
|
60
|
+
| Field | Description |
|
|
61
|
+
|-------|-------------|
|
|
62
|
+
| Competitor IDs | Optional. Comma-separated UUIDs to watch. Leave empty for all. |
|
|
63
|
+
| Signal Types | Optional. Multi-select filter. Leave empty for all types. |
|
|
64
|
+
|
|
65
|
+
The trigger uses n8n's built-in polling interval (default: 5 minutes). On each poll, it fetches the latest signals and only emits ones that are newer than the last poll.
|
|
66
|
+
|
|
67
|
+
## Supported signal types
|
|
68
|
+
|
|
69
|
+
| Signal | Description |
|
|
70
|
+
|--------|-------------|
|
|
71
|
+
| blog_post | New blog article published |
|
|
72
|
+
| pricing_change | Pricing page updated |
|
|
73
|
+
| job_posting | New job listing detected |
|
|
74
|
+
| job_removed | Job listing taken down |
|
|
75
|
+
| tech_added | New technology detected in stack |
|
|
76
|
+
| tech_removed | Technology removed from stack |
|
|
77
|
+
| tech_stack_update | General stack change |
|
|
78
|
+
| news_mention | Competitor mentioned in the news |
|
|
79
|
+
| newsletter | Newsletter issue sent |
|
|
80
|
+
| ebook | New ebook or gated content |
|
|
81
|
+
| webinar | New webinar recording |
|
|
82
|
+
| webinar_upcoming | Upcoming webinar announced |
|
|
83
|
+
| linkedin_ad | New LinkedIn ad detected |
|
|
84
|
+
| social_post | Social media post |
|
|
85
|
+
| youtube_video | New YouTube video published |
|
|
86
|
+
| instagram_post | New Instagram post |
|
|
87
|
+
| facebook_post | New Facebook post |
|
|
88
|
+
| facebook_ad | New Facebook ad detected |
|
|
89
|
+
| google_ad | New Google ad detected |
|
|
90
|
+
| user_review | New user review on G2, Capterra, etc. |
|
|
91
|
+
| sitemap_update | Website sitemap changed |
|
|
92
|
+
| page_change | Monitored page content changed |
|
|
93
|
+
| leadership_change | Leadership team update |
|
|
94
|
+
|
|
95
|
+
## Example workflows
|
|
96
|
+
|
|
97
|
+
**Slack alert on pricing changes**
|
|
98
|
+
Glimpse Trigger (types: pricing_change) -> Slack: Send Message
|
|
99
|
+
|
|
100
|
+
**Weekly competitor digest to email**
|
|
101
|
+
Schedule (weekly) -> Glimpse: Get All Signals (days: 7) -> Gmail: Send Email
|
|
102
|
+
|
|
103
|
+
**Log new signals to Google Sheets**
|
|
104
|
+
Glimpse Trigger -> Google Sheets: Append Row
|
|
105
|
+
|
|
106
|
+
**Enrich with AI summary**
|
|
107
|
+
Glimpse Trigger -> OpenAI: Summarize -> Slack: Send Message
|
|
108
|
+
|
|
109
|
+
## Compatibility
|
|
110
|
+
|
|
111
|
+
- Minimum n8n version: **1.0.0**
|
|
112
|
+
- Tested with: n8n 1.x
|
|
113
|
+
|
|
114
|
+
## Resources
|
|
115
|
+
|
|
116
|
+
- [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
|
|
117
|
+
- [Glimpse website](https://www.glimpsehq.io)
|
|
118
|
+
- [Glimpse API documentation](https://www.glimpsehq.io/docs/api)
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class GlimpseApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
authenticate: IAuthenticateGeneric;
|
|
8
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GlimpseApi = void 0;
|
|
4
|
+
class GlimpseApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'glimpseApi';
|
|
7
|
+
this.displayName = 'Glimpse API';
|
|
8
|
+
this.documentationUrl = 'https://www.glimpsehq.io/docs/api';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'API Key',
|
|
12
|
+
name: 'apiKey',
|
|
13
|
+
type: 'string',
|
|
14
|
+
typeOptions: { password: true },
|
|
15
|
+
default: '',
|
|
16
|
+
required: true,
|
|
17
|
+
placeholder: 'glmp_...',
|
|
18
|
+
description: 'Your Glimpse API key. Generate one in Settings > Integrations.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
displayName: 'Base URL',
|
|
22
|
+
name: 'baseUrl',
|
|
23
|
+
type: 'string',
|
|
24
|
+
default: 'https://app.glimpsehq.io',
|
|
25
|
+
description: 'Override for self-hosted or staging environments',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
this.authenticate = {
|
|
29
|
+
type: 'generic',
|
|
30
|
+
properties: {
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: '=Bearer ' + '{{$credentials.apiKey}}',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.GlimpseApi = GlimpseApi;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GlimpseTrigger = exports.Glimpse = exports.GlimpseApi = void 0;
|
|
4
|
+
var GlimpseApi_credentials_1 = require("./credentials/GlimpseApi.credentials");
|
|
5
|
+
Object.defineProperty(exports, "GlimpseApi", { enumerable: true, get: function () { return GlimpseApi_credentials_1.GlimpseApi; } });
|
|
6
|
+
var Glimpse_node_1 = require("./nodes/Glimpse/Glimpse.node");
|
|
7
|
+
Object.defineProperty(exports, "Glimpse", { enumerable: true, get: function () { return Glimpse_node_1.Glimpse; } });
|
|
8
|
+
var GlimpseTrigger_node_1 = require("./nodes/Glimpse/GlimpseTrigger.node");
|
|
9
|
+
Object.defineProperty(exports, "GlimpseTrigger", { enumerable: true, get: function () { return GlimpseTrigger_node_1.GlimpseTrigger; } });
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Glimpse = void 0;
|
|
4
|
+
const SIGNAL_TYPES = [
|
|
5
|
+
'blog_post', 'pricing_change', 'job_posting', 'job_removed',
|
|
6
|
+
'tech_added', 'tech_removed', 'tech_stack_update',
|
|
7
|
+
'news_mention', 'newsletter', 'ebook', 'webinar', 'webinar_upcoming',
|
|
8
|
+
'linkedin_ad', 'social_post', 'youtube_video', 'instagram_post',
|
|
9
|
+
'facebook_post', 'facebook_ad', 'google_ad', 'user_review',
|
|
10
|
+
'sitemap_update', 'page_change', 'leadership_change',
|
|
11
|
+
];
|
|
12
|
+
class Glimpse {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.description = {
|
|
15
|
+
displayName: 'Glimpse',
|
|
16
|
+
name: 'glimpse',
|
|
17
|
+
icon: 'file:glimpse.svg',
|
|
18
|
+
group: ['transform'],
|
|
19
|
+
version: 1,
|
|
20
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
21
|
+
description: 'Read data from the Glimpse competitive intelligence API',
|
|
22
|
+
defaults: { name: 'Glimpse' },
|
|
23
|
+
inputs: ['main'],
|
|
24
|
+
outputs: ['main'],
|
|
25
|
+
credentials: [
|
|
26
|
+
{
|
|
27
|
+
name: 'glimpseApi',
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
properties: [
|
|
32
|
+
{
|
|
33
|
+
displayName: 'Resource',
|
|
34
|
+
name: 'resource',
|
|
35
|
+
type: 'options',
|
|
36
|
+
noDataExpression: true,
|
|
37
|
+
options: [
|
|
38
|
+
{ name: 'Competitor', value: 'competitor' },
|
|
39
|
+
{ name: 'Signal', value: 'signal' },
|
|
40
|
+
],
|
|
41
|
+
default: 'signal',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
displayName: 'Operation',
|
|
45
|
+
name: 'operation',
|
|
46
|
+
type: 'options',
|
|
47
|
+
noDataExpression: true,
|
|
48
|
+
displayOptions: { show: { resource: ['competitor'] } },
|
|
49
|
+
options: [
|
|
50
|
+
{ name: 'Get All', value: 'getAll', action: 'Get all competitors' },
|
|
51
|
+
{ name: 'Get', value: 'get', action: 'Get a competitor' },
|
|
52
|
+
],
|
|
53
|
+
default: 'getAll',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
displayName: 'Competitor ID',
|
|
57
|
+
name: 'competitorId',
|
|
58
|
+
type: 'string',
|
|
59
|
+
required: true,
|
|
60
|
+
displayOptions: { show: { resource: ['competitor'], operation: ['get'] } },
|
|
61
|
+
default: '',
|
|
62
|
+
description: 'The UUID of the competitor to retrieve',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
displayName: 'Limit',
|
|
66
|
+
name: 'competitorLimit',
|
|
67
|
+
type: 'number',
|
|
68
|
+
displayOptions: { show: { resource: ['competitor'], operation: ['getAll'] } },
|
|
69
|
+
default: 100,
|
|
70
|
+
description: 'Max number of competitors to return (max 500)',
|
|
71
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
displayName: 'Operation',
|
|
75
|
+
name: 'operation',
|
|
76
|
+
type: 'options',
|
|
77
|
+
noDataExpression: true,
|
|
78
|
+
displayOptions: { show: { resource: ['signal'] } },
|
|
79
|
+
options: [
|
|
80
|
+
{ name: 'Get All', value: 'getAll', action: 'Get all signals' },
|
|
81
|
+
],
|
|
82
|
+
default: 'getAll',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
displayName: 'Additional Filters',
|
|
86
|
+
name: 'signalFilters',
|
|
87
|
+
type: 'collection',
|
|
88
|
+
placeholder: 'Add Filter',
|
|
89
|
+
default: {},
|
|
90
|
+
displayOptions: { show: { resource: ['signal'], operation: ['getAll'] } },
|
|
91
|
+
options: [
|
|
92
|
+
{
|
|
93
|
+
displayName: 'Competitor IDs',
|
|
94
|
+
name: 'competitorIds',
|
|
95
|
+
type: 'string',
|
|
96
|
+
default: '',
|
|
97
|
+
description: 'Comma-separated competitor UUIDs to filter by',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
displayName: 'Signal Types',
|
|
101
|
+
name: 'types',
|
|
102
|
+
type: 'multiOptions',
|
|
103
|
+
options: SIGNAL_TYPES.map((t) => ({ name: t.replace(/_/g, ' '), value: t })),
|
|
104
|
+
default: [],
|
|
105
|
+
description: 'Filter by signal types',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
displayName: 'Days',
|
|
109
|
+
name: 'days',
|
|
110
|
+
type: 'number',
|
|
111
|
+
default: 7,
|
|
112
|
+
description: 'Only return signals from the last N days',
|
|
113
|
+
typeOptions: { minValue: 1 },
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
displayName: 'Limit',
|
|
117
|
+
name: 'limit',
|
|
118
|
+
type: 'number',
|
|
119
|
+
default: 50,
|
|
120
|
+
description: 'Max signals to return (max 500)',
|
|
121
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async execute() {
|
|
129
|
+
const credentials = await this.getCredentials('glimpseApi');
|
|
130
|
+
const baseUrl = credentials.baseUrl.replace(/\/$/, '');
|
|
131
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
132
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
133
|
+
let responseData;
|
|
134
|
+
if (resource === 'competitor') {
|
|
135
|
+
if (operation === 'get') {
|
|
136
|
+
const id = this.getNodeParameter('competitorId', 0);
|
|
137
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'glimpseApi', {
|
|
138
|
+
method: 'GET',
|
|
139
|
+
url: baseUrl + '/api/v1/competitors/' + id,
|
|
140
|
+
json: true,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const limit = this.getNodeParameter('competitorLimit', 0);
|
|
145
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'glimpseApi', {
|
|
146
|
+
method: 'GET',
|
|
147
|
+
url: baseUrl + '/api/v1/competitors',
|
|
148
|
+
qs: { limit },
|
|
149
|
+
json: true,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const filters = this.getNodeParameter('signalFilters', 0, {});
|
|
155
|
+
const qs = {};
|
|
156
|
+
if (filters.competitorIds)
|
|
157
|
+
qs.competitor_ids = filters.competitorIds;
|
|
158
|
+
if (filters.types && filters.types.length)
|
|
159
|
+
qs.types = filters.types.join(',');
|
|
160
|
+
if (filters.days)
|
|
161
|
+
qs.days = String(filters.days);
|
|
162
|
+
if (filters.limit)
|
|
163
|
+
qs.limit = String(filters.limit);
|
|
164
|
+
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'glimpseApi', {
|
|
165
|
+
method: 'GET',
|
|
166
|
+
url: baseUrl + '/api/v1/signals',
|
|
167
|
+
qs,
|
|
168
|
+
json: true,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const items = Array.isArray(responseData.data)
|
|
172
|
+
? responseData.data
|
|
173
|
+
: [responseData];
|
|
174
|
+
return [this.helpers.returnJsonArray(items)];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.Glimpse = Glimpse;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IPollFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class GlimpseTrigger implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GlimpseTrigger = void 0;
|
|
4
|
+
const SIGNAL_TYPES = [
|
|
5
|
+
'blog_post', 'pricing_change', 'job_posting', 'job_removed',
|
|
6
|
+
'tech_added', 'tech_removed', 'tech_stack_update',
|
|
7
|
+
'news_mention', 'newsletter', 'ebook', 'webinar', 'webinar_upcoming',
|
|
8
|
+
'linkedin_ad', 'social_post', 'youtube_video', 'instagram_post',
|
|
9
|
+
'facebook_post', 'facebook_ad', 'google_ad', 'user_review',
|
|
10
|
+
'sitemap_update', 'page_change', 'leadership_change',
|
|
11
|
+
];
|
|
12
|
+
class GlimpseTrigger {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.description = {
|
|
15
|
+
displayName: 'Glimpse Trigger',
|
|
16
|
+
name: 'glimpseTrigger',
|
|
17
|
+
icon: 'file:glimpse.svg',
|
|
18
|
+
group: ['trigger'],
|
|
19
|
+
version: 1,
|
|
20
|
+
subtitle: 'New competitive signal',
|
|
21
|
+
description: 'Triggers when new competitive intelligence signals are detected in Glimpse',
|
|
22
|
+
defaults: {
|
|
23
|
+
name: 'Glimpse Trigger',
|
|
24
|
+
},
|
|
25
|
+
polling: true,
|
|
26
|
+
inputs: [],
|
|
27
|
+
outputs: ['main'],
|
|
28
|
+
credentials: [
|
|
29
|
+
{
|
|
30
|
+
name: 'glimpseApi',
|
|
31
|
+
required: true,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
properties: [
|
|
35
|
+
{
|
|
36
|
+
displayName: 'Competitor IDs',
|
|
37
|
+
name: 'competitorIds',
|
|
38
|
+
type: 'string',
|
|
39
|
+
default: '',
|
|
40
|
+
description: 'Comma-separated competitor UUIDs. Leave empty for all competitors.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Signal Types',
|
|
44
|
+
name: 'types',
|
|
45
|
+
type: 'multiOptions',
|
|
46
|
+
options: SIGNAL_TYPES.map((t) => ({ name: t.replace(/_/g, ' '), value: t })),
|
|
47
|
+
default: [],
|
|
48
|
+
description: 'Filter by signal types. Leave empty for all types.',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async poll() {
|
|
54
|
+
const credentials = await this.getCredentials('glimpseApi');
|
|
55
|
+
const baseUrl = credentials.baseUrl.replace(/\/$/, '');
|
|
56
|
+
const competitorIds = this.getNodeParameter('competitorIds', '');
|
|
57
|
+
const types = this.getNodeParameter('types', []);
|
|
58
|
+
const staticData = this.getWorkflowStaticData('node');
|
|
59
|
+
const lastPollTime = staticData.lastPollTime;
|
|
60
|
+
const qs = {
|
|
61
|
+
days: '1',
|
|
62
|
+
limit: '100',
|
|
63
|
+
};
|
|
64
|
+
if (competitorIds)
|
|
65
|
+
qs.competitor_ids = competitorIds;
|
|
66
|
+
if (types.length)
|
|
67
|
+
qs.types = types.join(',');
|
|
68
|
+
const responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'glimpseApi', {
|
|
69
|
+
method: 'GET',
|
|
70
|
+
url: baseUrl + '/api/v1/signals',
|
|
71
|
+
qs,
|
|
72
|
+
json: true,
|
|
73
|
+
});
|
|
74
|
+
const signals = responseData.data || [];
|
|
75
|
+
const newSignals = lastPollTime
|
|
76
|
+
? signals.filter((s) => {
|
|
77
|
+
const signalTime = s.published_at || s.created_at;
|
|
78
|
+
return signalTime > lastPollTime;
|
|
79
|
+
})
|
|
80
|
+
: signals;
|
|
81
|
+
if (newSignals.length > 0) {
|
|
82
|
+
const mostRecent = newSignals.reduce((latest, s) => {
|
|
83
|
+
const t = s.published_at || s.created_at;
|
|
84
|
+
return t > latest ? t : latest;
|
|
85
|
+
}, '');
|
|
86
|
+
staticData.lastPollTime = mostRecent;
|
|
87
|
+
}
|
|
88
|
+
else if (!lastPollTime) {
|
|
89
|
+
staticData.lastPollTime = new Date().toISOString();
|
|
90
|
+
}
|
|
91
|
+
if (newSignals.length === 0)
|
|
92
|
+
return null;
|
|
93
|
+
return [this.helpers.returnJsonArray(newSignals)];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.GlimpseTrigger = GlimpseTrigger;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-glimpse",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "N8N community node for Glimpse - competitive intelligence signals",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Glimpse",
|
|
8
|
+
"url": "https://www.glimpsehq.io"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/sven-glimpse/n8n-nodes-glimpse"
|
|
13
|
+
},
|
|
14
|
+
"main": "dist/index.js",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"n8n": {
|
|
20
|
+
"n8nNodesApiVersion": 1,
|
|
21
|
+
"credentials": [
|
|
22
|
+
"dist/credentials/GlimpseApi.credentials.js"
|
|
23
|
+
],
|
|
24
|
+
"nodes": [
|
|
25
|
+
"dist/nodes/Glimpse/Glimpse.node.js",
|
|
26
|
+
"dist/nodes/Glimpse/GlimpseTrigger.node.js"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"n8n-workflow": "^1.0.0",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"n8n-workflow": "^1.0.0"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"n8n-community-node-package",
|
|
38
|
+
"n8n",
|
|
39
|
+
"glimpse",
|
|
40
|
+
"competitive-intelligence",
|
|
41
|
+
"signals"
|
|
42
|
+
],
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
]
|
|
46
|
+
}
|