@skillful-ai/piece-x 0.0.4 → 0.0.6

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.
Files changed (42) hide show
  1. package/package.json +6 -10
  2. package/src/index.js +18 -2
  3. package/src/index.js.map +1 -1
  4. package/src/lib/common/actions/brand-monitoring.d.ts +7 -0
  5. package/src/lib/common/actions/brand-monitoring.js +218 -0
  6. package/src/lib/common/actions/brand-monitoring.js.map +1 -0
  7. package/src/lib/common/actions/compliance-check.d.ts +6 -0
  8. package/src/lib/common/actions/compliance-check.js +289 -0
  9. package/src/lib/common/actions/compliance-check.js.map +1 -0
  10. package/src/lib/common/actions/create-tweet.js +10 -2
  11. package/src/lib/common/actions/create-tweet.js.map +1 -1
  12. package/src/lib/common/actions/dm-send.d.ts +5 -0
  13. package/src/lib/common/actions/dm-send.js +146 -0
  14. package/src/lib/common/actions/dm-send.js.map +1 -0
  15. package/src/lib/common/actions/get-latest-mentions.js +18 -4
  16. package/src/lib/common/actions/get-latest-mentions.js.map +1 -1
  17. package/src/lib/common/actions/get-own-posts.js +25 -5
  18. package/src/lib/common/actions/get-own-posts.js.map +1 -1
  19. package/src/lib/common/actions/get-replies.js +18 -4
  20. package/src/lib/common/actions/get-replies.js.map +1 -1
  21. package/src/lib/common/actions/get-user-content.js +19 -4
  22. package/src/lib/common/actions/get-user-content.js.map +1 -1
  23. package/src/lib/common/actions/promo-rescue.d.ts +8 -0
  24. package/src/lib/common/actions/promo-rescue.js +491 -0
  25. package/src/lib/common/actions/promo-rescue.js.map +1 -0
  26. package/src/lib/common/actions/prospect-scoring.d.ts +12 -0
  27. package/src/lib/common/actions/prospect-scoring.js +198 -0
  28. package/src/lib/common/actions/prospect-scoring.js.map +1 -0
  29. package/src/lib/common/actions/prospect-search.d.ts +8 -0
  30. package/src/lib/common/actions/prospect-search.js +194 -0
  31. package/src/lib/common/actions/prospect-search.js.map +1 -0
  32. package/src/lib/common/actions/reply-tweet.d.ts +5 -0
  33. package/src/lib/common/actions/reply-tweet.js +165 -0
  34. package/src/lib/common/actions/reply-tweet.js.map +1 -0
  35. package/src/lib/common/actions/search-recent.js +49 -4
  36. package/src/lib/common/actions/search-recent.js.map +1 -1
  37. package/src/lib/common/actions/sentiment-radar.d.ts +7 -0
  38. package/src/lib/common/actions/sentiment-radar.js +496 -0
  39. package/src/lib/common/actions/sentiment-radar.js.map +1 -0
  40. package/src/lib/common/auth.js +5 -3
  41. package/src/lib/common/auth.js.map +1 -1
  42. package/skillful-ai-piece-x-0.0.4.tgz +0 -0
package/package.json CHANGED
@@ -1,29 +1,23 @@
1
1
  {
2
2
  "name": "@skillful-ai/piece-x",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "type": "commonjs",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
7
7
  "dependencies": {
8
8
  "@activepieces/pieces-common": "^0.7.0",
9
9
  "@activepieces/pieces-framework": "^0.20.1",
10
- "@ai-sdk/anthropic": "1.2.12",
11
- "@ai-sdk/google": "1.2.19",
12
- "@ai-sdk/openai": "1.3.22",
13
- "@ai-sdk/replicate": "0.2.8",
14
10
  "@sinclair/typebox": "0.34.11",
15
- "ai": "4.3.16",
16
11
  "axios": "1.8.3",
17
12
  "axios-retry": "4.4.1",
18
13
  "deepmerge-ts": "7.1.0",
19
- "fast-glob": "3.3.3",
20
14
  "mime-types": "2.1.35",
21
15
  "nanoid": "3.3.8",
22
16
  "semver": "7.6.0",
23
17
  "tslib": "^2.3.0",
24
18
  "twitter-api-v2": "^1.25.0",
25
19
  "zod": "^3.23.8",
26
- "@activepieces/shared": "0.17.2"
20
+ "@activepieces/shared": "0.22.0"
27
21
  },
28
22
  "overrides": {
29
23
  "@tryfabric/martian": {
@@ -31,9 +25,11 @@
31
25
  },
32
26
  "vite": {
33
27
  "rollup": "npm:@rollup/wasm-node"
34
- }
28
+ },
29
+ "undici": "6.19.8"
35
30
  },
36
31
  "resolutions": {
37
- "rollup": "npm:@rollup/wasm-node"
32
+ "rollup": "npm:@rollup/wasm-node",
33
+ "undici": "6.19.8"
38
34
  }
39
35
  }
package/src/index.js CHANGED
@@ -10,6 +10,14 @@ const get_latest_mentions_1 = require("./lib/common/actions/get-latest-mentions"
10
10
  const get_own_posts_1 = require("./lib/common/actions/get-own-posts");
11
11
  const get_replies_1 = require("./lib/common/actions/get-replies");
12
12
  const get_user_content_1 = require("./lib/common/actions/get-user-content");
13
+ const prospect_search_1 = require("./lib/common/actions/prospect-search");
14
+ const prospect_scoring_1 = require("./lib/common/actions/prospect-scoring");
15
+ const compliance_check_1 = require("./lib/common/actions/compliance-check");
16
+ const dm_send_1 = require("./lib/common/actions/dm-send");
17
+ const brand_monitoring_1 = require("./lib/common/actions/brand-monitoring");
18
+ const reply_tweet_1 = require("./lib/common/actions/reply-tweet");
19
+ const promo_rescue_1 = require("./lib/common/actions/promo-rescue");
20
+ const sentiment_radar_1 = require("./lib/common/actions/sentiment-radar");
13
21
  const search_recent_1 = require("./lib/common/actions/search-recent");
14
22
  const new_mention_1 = require("./lib/common/triggers/new-mention");
15
23
  const new_posts_from_me_1 = require("./lib/common/triggers/new-posts-from-me");
@@ -18,11 +26,11 @@ const auth_1 = require("./lib/common/auth");
18
26
  const new_unreplied_mention_1 = require("./lib/common/triggers/new-unreplied-mention");
19
27
  exports.twitter = (0, pieces_framework_1.createPiece)({
20
28
  displayName: 'Twitter',
21
- description: 'Post, reply, search, and work with mentions using OAuth2 + twitter-api-v2.',
29
+ description: 'Comprehensive X (formerly Twitter) integration using OAuth2 and API v2. Create and manage posts, search recent content (last 7 days), retrieve user timelines, mentions, and replies. Features intelligent memory tracking to process only new content since last run. Supports media uploads up to 4 images per post and advanced filtering options.',
22
30
  logoUrl: 'https://cdn.activepieces.com/pieces/twitter.png',
23
31
  minimumSupportedRelease: '0.36.1',
24
32
  categories: [shared_1.PieceCategory.COMMUNICATION, shared_1.PieceCategory.MARKETING],
25
- authors: ['Isaac Touma Rodriguez'],
33
+ authors: ['Isaac Touma Rodriguez', 'Haider Shabbir Hamid'],
26
34
  auth: auth_1.twitterAuth,
27
35
  actions: [
28
36
  create_tweet_1.createTweetAction,
@@ -31,6 +39,14 @@ exports.twitter = (0, pieces_framework_1.createPiece)({
31
39
  search_recent_1.searchRecentAction,
32
40
  get_replies_1.getRepliesAction,
33
41
  get_user_content_1.getUserContentAction,
42
+ prospect_search_1.searchProspectsAction,
43
+ prospect_scoring_1.scoreProspectAction,
44
+ compliance_check_1.complianceCheckAction,
45
+ dm_send_1.sendDMAction,
46
+ brand_monitoring_1.brandMonitoringAction,
47
+ reply_tweet_1.replyTweetAction,
48
+ promo_rescue_1.promoRescueAction,
49
+ sentiment_radar_1.sentimentRadarAction,
34
50
  (0, pieces_common_1.createCustomApiCallAction)({
35
51
  auth: auth_1.twitterAuth,
36
52
  baseUrl: () => 'https://api.twitter.com/2',
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/pieces/custom/x/src/index.ts"],"names":[],"mappings":";;;;AAAA,qEAIwC;AACxC,iDAAqD;AACrD,+DAAwE;AAGxE,oEAAsE;AACtE,kFAAmF;AAEnF,sEAA6E;AAC7E,kEAAoE;AACpE,4EAA6E;AAC7E,sEAAwE;AACxE,mEAAsE;AACtE,+EAAgF;AAChF,6EAA+E;AAC/E,4CAAgD;AAChD,uFAAyF;AAG5E,QAAA,OAAO,GAAG,IAAA,8BAAW,EAAC;IACjC,WAAW,EAAE,SAAS;IACtB,WAAW,EACT,4EAA4E;IAC9E,OAAO,EAAE,iDAAiD;IAC1D,uBAAuB,EAAE,QAAQ;IACjC,UAAU,EAAE,CAAC,sBAAa,CAAC,aAAa,EAAE,sBAAa,CAAC,SAAS,CAAC;IAClE,OAAO,EAAE,CAAC,uBAAuB,CAAC;IAClC,IAAI,EAAE,kBAAW;IACjB,OAAO,EAAE;QACP,gCAAiB;QACjB,6CAAuB;QACvB,uCAAuB;QACvB,kCAAkB;QAClB,8BAAgB;QAChB,uCAAoB;QAEpB,IAAA,yCAAyB,EAAC;YACxB,IAAI,EAAE,kBAAW;YACjB,OAAO,EAAE,GAAG,EAAE,CAAC,2BAA2B;YAC1C,WAAW,EAAE,CAAO,IAAI,EAAE,EAAE;gBAAC,OAAA,CAAC;oBAC5B,aAAa,EAAE,UAAW,IAA4B,CAAC,YAAY,EAAE;iBACtE,CAAC,CAAA;cAAA;SACH,CAAC;KACH;IACD,QAAQ,EAAE,CAAC,+BAAiB,EAAE,wCAAqB,EAAE,yCAAqB,EAAE,kDAA0B,CAAC;CACxG,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/pieces/custom/x/src/index.ts"],"names":[],"mappings":";;;;AAAA,qEAGwC;AACxC,iDAAqD;AACrD,+DAAwE;AAGxE,oEAAsE;AACtE,kFAAmF;AAEnF,sEAA6E;AAC7E,kEAAoE;AACpE,4EAA6E;AAC7E,0EAA6E;AAC7E,4EAA4E;AAC5E,4EAA8E;AAC9E,0DAA4D;AAC5D,4EAA8E;AAC9E,kEAAoE;AACpE,oEAAsE;AACtE,0EAA4E;AAC5E,sEAAwE;AACxE,mEAAsE;AACtE,+EAAgF;AAChF,6EAA+E;AAC/E,4CAAgD;AAChD,uFAAyF;AAG5E,QAAA,OAAO,GAAG,IAAA,8BAAW,EAAC;IACjC,WAAW,EAAE,SAAS;IACtB,WAAW,EACT,uVAAuV;IACzV,OAAO,EAAE,iDAAiD;IAC1D,uBAAuB,EAAE,QAAQ;IACjC,UAAU,EAAE,CAAC,sBAAa,CAAC,aAAa,EAAE,sBAAa,CAAC,SAAS,CAAC;IAClE,OAAO,EAAE,CAAC,uBAAuB,EAAE,sBAAsB,CAAC;IAC1D,IAAI,EAAE,kBAAW;IACjB,OAAO,EAAE;QACP,gCAAiB;QACjB,6CAAuB;QACvB,uCAAuB;QACvB,kCAAkB;QAClB,8BAAgB;QAChB,uCAAoB;QACpB,uCAAqB;QACrB,sCAAmB;QACnB,wCAAqB;QACrB,sBAAY;QACZ,wCAAqB;QACrB,8BAAgB;QAChB,gCAAiB;QACjB,sCAAoB;QAEpB,IAAA,yCAAyB,EAAC;YACxB,IAAI,EAAE,kBAAW;YACjB,OAAO,EAAE,GAAG,EAAE,CAAC,2BAA2B;YAC1C,WAAW,EAAE,CAAO,IAAI,EAAE,EAAE;gBAAC,OAAA,CAAC;oBAC5B,aAAa,EAAE,UAAW,IAA4B,CAAC,YAAY,EAAE;iBACtE,CAAC,CAAA;cAAA;SACH,CAAC;KACH;IACD,QAAQ,EAAE,CAAC,+BAAiB,EAAE,wCAAqB,EAAE,yCAAqB,EAAE,kDAA0B,CAAC;CACxG,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare const brandMonitoringAction: import("@activepieces/pieces-framework").IAction<import("@activepieces/pieces-framework").OAuth2Property<import("@activepieces/pieces-framework").OAuth2Props>, {
2
+ keywords: import("@activepieces/pieces-framework").ArrayProperty<true>;
3
+ competitorKeywords: import("@activepieces/pieces-framework").ArrayProperty<false>;
4
+ contextFilters: import("@activepieces/pieces-framework").ArrayProperty<false>;
5
+ maxResults: import("@activepieces/pieces-framework").NumberProperty<false>;
6
+ useMemory: import("@activepieces/pieces-framework").CheckboxProperty<false>;
7
+ }>;
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.brandMonitoringAction = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const pieces_framework_1 = require("@activepieces/pieces-framework");
6
+ const auth_1 = require("../auth");
7
+ const __1 = require("..");
8
+ exports.brandMonitoringAction = (0, pieces_framework_1.createAction)({
9
+ auth: auth_1.twitterAuth,
10
+ name: 'twitter-brand-monitoring',
11
+ displayName: 'Brand Monitoring',
12
+ description: 'Monitor Twitter for brand-relevant conversations and competitor mentions with context filtering.',
13
+ props: {
14
+ keywords: pieces_framework_1.Property.Array({
15
+ displayName: 'Brand Keywords',
16
+ description: 'Keywords related to your brand to monitor',
17
+ required: true,
18
+ }),
19
+ competitorKeywords: pieces_framework_1.Property.Array({
20
+ displayName: 'Competitor Keywords',
21
+ description: 'Keywords related to competitors to monitor',
22
+ required: false,
23
+ }),
24
+ contextFilters: pieces_framework_1.Property.Array({
25
+ displayName: 'Context Filters',
26
+ description: 'Context phrases that tweets must contain to be relevant',
27
+ required: false,
28
+ }),
29
+ maxResults: pieces_framework_1.Property.Number({
30
+ displayName: 'Max Results',
31
+ description: 'Maximum number of conversations to return per keyword',
32
+ defaultValue: 10,
33
+ required: false,
34
+ }),
35
+ useMemory: pieces_framework_1.Property.Checkbox({
36
+ displayName: 'Use Memory',
37
+ description: 'Track processed conversations to avoid duplicates across runs',
38
+ defaultValue: true,
39
+ required: false,
40
+ }),
41
+ },
42
+ run(ctx) {
43
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
44
+ var _a, _b, _c;
45
+ const client = (0, __1.twitterClient)(ctx.auth);
46
+ const { keywords, competitorKeywords = [], contextFilters = [], maxResults = 10, useMemory = true, } = ctx.propsValue;
47
+ if (!keywords || keywords.length === 0) {
48
+ throw new Error('At least one brand keyword is required');
49
+ }
50
+ const relevantConversations = [];
51
+ const allKeywords = [...keywords, ...competitorKeywords];
52
+ const processedTweets = new Set();
53
+ // Get previously processed tweets from memory if enabled
54
+ if (useMemory) {
55
+ const memKey = (0, __1.storeKey)(['brand_monitoring:processed_tweets']);
56
+ const processedTweetsList = (yield ctx.store.get(memKey)) || [];
57
+ processedTweetsList.forEach(tweetId => processedTweets.add(tweetId));
58
+ }
59
+ try {
60
+ for (const keyword of allKeywords) {
61
+ try {
62
+ // Search for recent tweets containing the keyword
63
+ const searchResponse = yield client.v2.search(keyword, {
64
+ max_results: (0, __1.clamp)(maxResults, 10, 100),
65
+ 'tweet.fields': [
66
+ 'id', 'text', 'created_at', 'conversation_id', 'public_metrics',
67
+ 'author_id', 'referenced_tweets'
68
+ ],
69
+ 'user.fields': [
70
+ "created_at",
71
+ "description",
72
+ "entities",
73
+ "id",
74
+ "location",
75
+ "name",
76
+ "pinned_tweet_id",
77
+ "profile_image_url",
78
+ "protected",
79
+ "public_metrics",
80
+ "url",
81
+ "username",
82
+ "verified",
83
+ "verified_type",
84
+ "withheld"
85
+ ],
86
+ expansions: ['author_id'],
87
+ });
88
+ const tweets = searchResponse.data.data || [];
89
+ const users = ((_a = searchResponse.data.includes) === null || _a === void 0 ? void 0 : _a.users) || [];
90
+ const userMap = new Map(users.map(user => [user.id, user]));
91
+ for (const tweet of tweets) {
92
+ try {
93
+ // Skip if already processed
94
+ if (processedTweets.has(tweet.id)) {
95
+ continue;
96
+ }
97
+ const tweetText = tweet.text;
98
+ const user = tweet.author_id ? userMap.get(tweet.author_id) : undefined;
99
+ // Check if tweet contains context phrases (if context filters are provided)
100
+ let hasContext = true;
101
+ if (contextFilters.length > 0) {
102
+ hasContext = contextFilters.some(phrase => {
103
+ const escapedPhrase = phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
104
+ const regex = new RegExp(`\\b${escapedPhrase}\\b`, 'i');
105
+ return regex.test(tweetText);
106
+ });
107
+ }
108
+ if (hasContext) {
109
+ // Get conversation context (replies to this tweet)
110
+ let conversationTweets = [];
111
+ try {
112
+ const repliesResponse = yield client.v2.search(`conversation_id:${tweet.conversation_id}`, {
113
+ max_results: (0, __1.clamp)(maxResults, 5, 50),
114
+ 'tweet.fields': [
115
+ 'id', 'text', 'created_at', 'author_id', 'public_metrics', 'conversation_id'
116
+ ],
117
+ 'user.fields': [
118
+ 'id',
119
+ 'username',
120
+ 'name',
121
+ 'verified',
122
+ 'created_at',
123
+ 'description',
124
+ 'location',
125
+ 'profile_image_url',
126
+ 'public_metrics',
127
+ 'protected',
128
+ 'url',
129
+ ],
130
+ expansions: ['author_id'],
131
+ });
132
+ const replyTweets = repliesResponse.data.data || [];
133
+ const replyUsers = ((_b = repliesResponse.data.includes) === null || _b === void 0 ? void 0 : _b.users) || [];
134
+ const replyUserMap = new Map(replyUsers.map(u => [u.id, u]));
135
+ conversationTweets = replyTweets.map(reply => {
136
+ var _a;
137
+ const replyUser = reply.author_id ? replyUserMap.get(reply.author_id) : undefined;
138
+ return Object.assign(Object.assign({}, reply), { author_info: replyUser
139
+ ? {
140
+ username: replyUser.username,
141
+ name: replyUser.name,
142
+ verified: replyUser.verified,
143
+ followers_count: ((_a = replyUser.public_metrics) === null || _a === void 0 ? void 0 : _a.followers_count) || 0,
144
+ profile_image_url: replyUser.profile_image_url,
145
+ description: replyUser.description,
146
+ location: replyUser.location,
147
+ created_at: replyUser.created_at,
148
+ }
149
+ : null });
150
+ });
151
+ }
152
+ catch (error) {
153
+ console.warn(`Failed to get conversation context for tweet ${tweet.id}:`, error);
154
+ }
155
+ const conversation = {
156
+ tweet_id: tweet.id,
157
+ author_id: tweet.author_id,
158
+ tweet_text: tweet.text,
159
+ created_at: tweet.created_at,
160
+ public_metrics: tweet.public_metrics || {},
161
+ keyword_matched: keyword,
162
+ conversation_tweets: conversationTweets,
163
+ is_competitor_mention: competitorKeywords.includes(keyword),
164
+ author_info: user ? {
165
+ username: user.username,
166
+ name: user.name,
167
+ verified: user.verified,
168
+ followers_count: ((_c = user.public_metrics) === null || _c === void 0 ? void 0 : _c.followers_count) || 0,
169
+ } : null,
170
+ };
171
+ relevantConversations.push(conversation);
172
+ processedTweets.add(tweet.id);
173
+ // Respect max results limit
174
+ if (relevantConversations.length >= maxResults * allKeywords.length) {
175
+ break;
176
+ }
177
+ }
178
+ }
179
+ catch (error) {
180
+ console.warn(`Failed to process tweet ${tweet.id}:`, error);
181
+ continue;
182
+ }
183
+ }
184
+ // Stop if we've reached max results
185
+ if (relevantConversations.length >= maxResults * allKeywords.length) {
186
+ break;
187
+ }
188
+ }
189
+ catch (error) {
190
+ console.warn(`Failed to search keyword '${keyword}':`, error);
191
+ continue;
192
+ }
193
+ }
194
+ // Store processed tweets in memory for next run
195
+ if (useMemory && processedTweets.size > 0) {
196
+ const memKey = (0, __1.storeKey)(['brand_monitoring:processed_tweets']);
197
+ yield ctx.store.put(memKey, Array.from(processedTweets));
198
+ }
199
+ // Sort conversations by engagement (likes + retweets + replies)
200
+ relevantConversations.sort((a, b) => {
201
+ const engagementA = (a.public_metrics.like_count || 0) +
202
+ (a.public_metrics.retweet_count || 0) +
203
+ (a.public_metrics.reply_count || 0);
204
+ const engagementB = (b.public_metrics.like_count || 0) +
205
+ (b.public_metrics.retweet_count || 0) +
206
+ (b.public_metrics.reply_count || 0);
207
+ return engagementB - engagementA;
208
+ });
209
+ return relevantConversations.slice(0, maxResults * allKeywords.length);
210
+ }
211
+ catch (error) {
212
+ console.error(`Brand monitoring failed: ${error}`);
213
+ throw new Error(`Brand monitoring failed: ${error}`);
214
+ }
215
+ });
216
+ },
217
+ });
218
+ //# sourceMappingURL=brand-monitoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-monitoring.js","sourceRoot":"","sources":["../../../../../../../../../packages/pieces/custom/x/src/lib/common/actions/brand-monitoring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare const complianceCheckAction: import("@activepieces/pieces-framework").IAction<import("@activepieces/pieces-framework").OAuth2Property<import("@activepieces/pieces-framework").OAuth2Props>, {
2
+ userId: import("@activepieces/pieces-framework").ShortTextProperty<true>;
3
+ maxResults: import("@activepieces/pieces-framework").NumberProperty<false>;
4
+ complianceIssuesThreshold: import("@activepieces/pieces-framework").NumberProperty<false>;
5
+ useMemory: import("@activepieces/pieces-framework").CheckboxProperty<false>;
6
+ }>;
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.complianceCheckAction = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const pieces_framework_1 = require("@activepieces/pieces-framework");
6
+ const auth_1 = require("../auth");
7
+ const __1 = require("..");
8
+ exports.complianceCheckAction = (0, pieces_framework_1.createAction)({
9
+ auth: auth_1.twitterAuth,
10
+ name: 'twitter-compliance-check',
11
+ displayName: 'Compliance Check',
12
+ description: 'Check if a Twitter user is safe to contact based on comprehensive compliance rules.',
13
+ props: {
14
+ userId: pieces_framework_1.Property.ShortText({
15
+ displayName: 'User ID',
16
+ description: 'Twitter user ID to check',
17
+ required: true,
18
+ }),
19
+ maxResults: pieces_framework_1.Property.Number({
20
+ displayName: 'Max Results',
21
+ description: 'Maximum number of recent tweets to analyze',
22
+ defaultValue: 20,
23
+ required: false,
24
+ }),
25
+ complianceIssuesThreshold: pieces_framework_1.Property.Number({
26
+ displayName: 'Compliance Issues Threshold',
27
+ description: 'Maximum number of compliance issues allowed (default: 3)',
28
+ defaultValue: 3,
29
+ required: false,
30
+ }),
31
+ useMemory: pieces_framework_1.Property.Checkbox({
32
+ displayName: 'Use Memory',
33
+ description: 'Track processed tweets to avoid duplicates across runs',
34
+ defaultValue: true,
35
+ required: false,
36
+ }),
37
+ },
38
+ run(ctx) {
39
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
40
+ const client = (0, __1.twitterClient)(ctx.auth);
41
+ const { userId, maxResults = 20, complianceIssuesThreshold = 3, useMemory = true, } = ctx.propsValue;
42
+ const complianceIssues = [];
43
+ let accountAgeDays = null;
44
+ let recentTweetsCount = 0;
45
+ const processedTweets = new Set();
46
+ // Load memory
47
+ if (useMemory) {
48
+ const memKey = (0, __1.storeKey)([`compliance:processed_tweets:${userId}`]);
49
+ const stored = yield ctx.store.get(memKey);
50
+ if (stored)
51
+ stored.forEach(id => processedTweets.add(id));
52
+ }
53
+ try {
54
+ // Get user details
55
+ const userResponse = yield client.v2.user(userId, {
56
+ 'user.fields': [
57
+ 'created_at',
58
+ 'description',
59
+ 'entities',
60
+ 'id',
61
+ 'location',
62
+ 'name',
63
+ 'pinned_tweet_id',
64
+ 'profile_image_url',
65
+ 'protected',
66
+ 'public_metrics',
67
+ 'url',
68
+ 'username',
69
+ 'verified',
70
+ 'verified_type',
71
+ 'withheld',
72
+ ],
73
+ });
74
+ const user = userResponse.data;
75
+ if (!user) {
76
+ return {
77
+ safe_to_contact: false,
78
+ compliance_issues: ['User not found or inaccessible'],
79
+ check_metadata: {
80
+ account_age_days: null,
81
+ bio_checked: false,
82
+ recent_tweets_checked: 0,
83
+ },
84
+ };
85
+ }
86
+ // 1. Check account age (with safer parsing)
87
+ if (user.created_at) {
88
+ try {
89
+ const createdDate = new Date(user.created_at);
90
+ if (!isNaN(createdDate.getTime())) {
91
+ const now = new Date();
92
+ accountAgeDays = Math.floor((now.getTime() - createdDate.getTime()) / (1000 * 60 * 60 * 24));
93
+ if (accountAgeDays < 10) {
94
+ complianceIssues.push(`Account too new (${accountAgeDays} days old)`);
95
+ }
96
+ }
97
+ else {
98
+ complianceIssues.push('Unable to verify account age');
99
+ }
100
+ }
101
+ catch (error) {
102
+ complianceIssues.push('Unable to verify account age');
103
+ }
104
+ }
105
+ // 2. Check for protected account
106
+ if (user.protected) {
107
+ complianceIssues.push('Account is protected');
108
+ }
109
+ // 3. Enhanced bio analysis
110
+ const bio = user.description || '';
111
+ if (bio) {
112
+ if (checkTextPatterns(bio, NO_DM_PATTERNS)) {
113
+ complianceIssues.push("Bio contains 'No DM' signals");
114
+ }
115
+ if (checkTextPatterns(bio, NO_CONTACT_PATTERNS)) {
116
+ complianceIssues.push("Bio indicates user doesn't want to be contacted");
117
+ }
118
+ if (checkTextPatterns(bio, SUSPICIOUS_PATTERNS)) {
119
+ complianceIssues.push("Bio contains suspicious/spam-like content");
120
+ }
121
+ }
122
+ // 4. Account metrics analysis
123
+ const userMetrics = user.public_metrics || {};
124
+ const metricIssues = analyzeAccountMetrics(userMetrics);
125
+ complianceIssues.push(...metricIssues);
126
+ // 5. Profile completeness check
127
+ const profileIssues = checkProfileCompleteness(user);
128
+ complianceIssues.push(...profileIssues);
129
+ // 6. Enhanced recent tweets analysis
130
+ try {
131
+ const tweetsResponse = yield client.v2.userTimeline(userId, {
132
+ max_results: Math.min(maxResults, 50),
133
+ 'tweet.fields': ['id', 'text', 'created_at', 'author_id', 'public_metrics'],
134
+ });
135
+ const tweets = tweetsResponse.data.data || [];
136
+ recentTweetsCount = tweets.length;
137
+ if (tweets.length > 0) {
138
+ let noDmCount = 0;
139
+ let spamCount = 0;
140
+ for (const tweet of tweets) {
141
+ if (processedTweets.has(tweet.id))
142
+ continue; // Skip already seen tweets
143
+ const tweetText = tweet.text || '';
144
+ if (checkTextPatterns(tweetText, NO_DM_PATTERNS))
145
+ noDmCount++;
146
+ if (checkTextPatterns(tweetText, NO_CONTACT_PATTERNS))
147
+ noDmCount++;
148
+ if (checkTextPatterns(tweetText, SUSPICIOUS_PATTERNS))
149
+ spamCount++;
150
+ processedTweets.add(tweet.id);
151
+ }
152
+ if (noDmCount > 0) {
153
+ complianceIssues.push(`Recent tweets contain 'No DM' signals (${noDmCount} instances)`);
154
+ }
155
+ if (spamCount >= 3) {
156
+ complianceIssues.push(`Multiple recent tweets appear spam-like (${spamCount} instances)`);
157
+ }
158
+ // Check tweet frequency (safe guard: ensure created_at exists)
159
+ if (tweets.length >= 20) {
160
+ const oldestTweet = tweets[tweets.length - 1];
161
+ if (oldestTweet === null || oldestTweet === void 0 ? void 0 : oldestTweet.created_at) {
162
+ try {
163
+ const oldestDate = new Date(oldestTweet.created_at);
164
+ if (!isNaN(oldestDate.getTime())) {
165
+ const now = new Date();
166
+ const daysSpan = Math.max(1, Math.floor((now.getTime() - oldestDate.getTime()) / (1000 * 60 * 60 * 24)));
167
+ const tweetsPerDay = tweets.length / daysSpan;
168
+ if (tweetsPerDay > 10) {
169
+ complianceIssues.push(`High tweet frequency (${tweetsPerDay.toFixed(1)} tweets/day)`);
170
+ }
171
+ }
172
+ }
173
+ catch (error) {
174
+ // Ignore date parsing errors
175
+ }
176
+ }
177
+ }
178
+ }
179
+ // Save memory state
180
+ if (useMemory && processedTweets.size > 0) {
181
+ const memKey = (0, __1.storeKey)([`compliance:processed_tweets:${userId}`]);
182
+ yield ctx.store.put(memKey, Array.from(processedTweets));
183
+ }
184
+ }
185
+ catch (error) {
186
+ complianceIssues.push('Unable to analyze recent tweets');
187
+ }
188
+ // 7. Verification checks
189
+ if (!user.verified) {
190
+ complianceIssues.push('Account is not verified');
191
+ }
192
+ // Determine final safety status
193
+ const safeToContact = complianceIssues.length <= complianceIssuesThreshold;
194
+ return {
195
+ safe_to_contact: safeToContact,
196
+ compliance_issues: complianceIssues,
197
+ check_metadata: {
198
+ account_age_days: accountAgeDays,
199
+ bio_checked: Boolean(bio),
200
+ recent_tweets_checked: recentTweetsCount,
201
+ },
202
+ };
203
+ }
204
+ catch (error) {
205
+ throw new Error(`Compliance check failed: ${error}`);
206
+ }
207
+ });
208
+ },
209
+ });
210
+ // Compliance patterns
211
+ const NO_DM_PATTERNS = [
212
+ /\bno\s+dm\b/i,
213
+ /\bdon'?t\s+dm\b/i,
214
+ /\bno\s+direct\s+message\b/i,
215
+ /\bdon'?t\s+direct\s+message\b/i,
216
+ /\bdo\s+not\s+dm\b/i,
217
+ /\bno\s+dms\b/i,
218
+ /\bdm\s+closed\b/i,
219
+ /\bdms\s+closed\b/i,
220
+ /\bno\s+unsolicited\s+dm\b/i,
221
+ /\bno\s+random\s+dm\b/i,
222
+ /\bdon'?t\s+slide\s+in\b/i,
223
+ /⛔.*dm/i,
224
+ /🚫.*dm/i,
225
+ /❌.*dm/i,
226
+ ];
227
+ const NO_CONTACT_PATTERNS = [
228
+ /\bdon'?t\s+contact\b/i,
229
+ /\bdo\s+not\s+contact\b/i,
230
+ /\bno\s+contact\b/i,
231
+ /\bdon'?t\s+reach\s+out\b/i,
232
+ /\bno\s+solicitation\b/i,
233
+ /\bno\s+spam\b/i,
234
+ /\bnot\s+interested\s+in\s+collaborations?\b/i,
235
+ /\bno\s+business\s+inquiries\b/i,
236
+ /\bno\s+promotions?\b/i,
237
+ /\bno\s+pitches?\b/i,
238
+ ];
239
+ const SUSPICIOUS_PATTERNS = [
240
+ /\b(crypto|nft|forex|trading|investment)\s+(signals?|tips?|advice)\b/i,
241
+ /\bget\s+rich\s+quick\b/i,
242
+ /\bmake\s+money\s+(fast|quick)\b/i,
243
+ /\b\d+k?\s+a\s+month\b/i,
244
+ /\bwork\s+from\s+home\b/i,
245
+ /\bmlm\b/i,
246
+ /\bpyramid\s+scheme\b/i,
247
+ ];
248
+ // Helper functions
249
+ function checkTextPatterns(text, patterns) {
250
+ if (!text)
251
+ return false;
252
+ return patterns.some(pattern => pattern.test(text));
253
+ }
254
+ function analyzeAccountMetrics(userMetrics) {
255
+ const issues = [];
256
+ const followersCount = userMetrics.followers_count || 0;
257
+ const followingCount = userMetrics.following_count || 0;
258
+ const tweetCount = userMetrics.tweet_count || 0;
259
+ if (followersCount < 5) {
260
+ issues.push(`Low follower count (${followersCount} < 5)`);
261
+ }
262
+ if (followersCount > 0) {
263
+ const followingRatio = followingCount / followersCount;
264
+ if (followingRatio > 10.0) {
265
+ issues.push(`High following/followers ratio (${followingRatio.toFixed(1)})`);
266
+ }
267
+ }
268
+ else if (followingCount > 100) {
269
+ issues.push('Following many accounts but has no followers');
270
+ }
271
+ if (tweetCount < 3) {
272
+ issues.push(`Low tweet count (${tweetCount})`);
273
+ }
274
+ return issues;
275
+ }
276
+ function checkProfileCompleteness(user) {
277
+ const issues = [];
278
+ if (!user.profile_image_url || user.profile_image_url.includes('default_profile')) {
279
+ issues.push('Using default profile image');
280
+ }
281
+ if (!user.description || user.description.trim().length < 10) {
282
+ issues.push('Profile bio is missing or too short');
283
+ }
284
+ if (!user.name || user.name.trim() === user.username) {
285
+ issues.push('Display name missing or same as username');
286
+ }
287
+ return issues;
288
+ }
289
+ //# sourceMappingURL=compliance-check.js.map