@shaivpidadi/trends-js 0.0.0-beta.2 → 0.0.0-beta.4

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/README.md CHANGED
@@ -0,0 +1,201 @@
1
+ 🚧 WIP: Temporary workaround for [#175](https://github.com/pat310/google-trends-api/issues/175)
2
+
3
+ # @shaivpidadi/trends-js
4
+
5
+ A TypeScript library for interacting with the Google Trends API. This package provides a simple and type-safe way to access Google Trends data programmatically.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @shaivpidadi/trends-js
11
+ ```
12
+
13
+ ## Features
14
+
15
+ - Get daily trending topics
16
+ - Get real-time trending topics
17
+ - Get autocomplete suggestions
18
+ - Explore trends data
19
+ - Get interest by region data
20
+ - TypeScript support
21
+ - Promise-based API
22
+
23
+ ## Usage
24
+
25
+ ### Importing
26
+
27
+ ```typescript
28
+ import GoogleTrendsApi from '@shaivpidadi/trends-js';
29
+ ```
30
+
31
+ ### Daily Trends
32
+
33
+ Get daily trending topics for a specific region:
34
+
35
+ ```typescript
36
+ const result = await GoogleTrendsApi.dailyTrends({
37
+ geo: 'US', // Default: 'US'
38
+ lang: 'en' // Default: 'en'
39
+ });
40
+
41
+ // Result structure:
42
+ // {
43
+ // allTrendingStories: Array<...>,
44
+ // summary: string[]
45
+ // }
46
+ ```
47
+
48
+ ### Real-Time Trends
49
+
50
+ Get real-time trending topics:
51
+
52
+ ```typescript
53
+ const result = await GoogleTrendsApi.realTimeTrends({
54
+ geo: 'US', // Default: 'US'
55
+ trendingHours: 4 // Default: 4
56
+ });
57
+
58
+ // Result structure:
59
+ // {
60
+ // allTrendingStories: Array<...>,
61
+ // summary: string[]
62
+ // }
63
+ ```
64
+
65
+ ### Autocomplete
66
+
67
+ Get search suggestions for a keyword:
68
+
69
+ ```typescript
70
+ const suggestions = await GoogleTrendsApi.autocomplete(
71
+ 'bitcoin', // Keyword to get suggestions for
72
+ 'en-US' // Language (default: 'en-US')
73
+ );
74
+
75
+ // Returns: string[]
76
+ ```
77
+
78
+ ### Explore
79
+
80
+ Get widget data for a keyword:
81
+
82
+ ```typescript
83
+ const result = await GoogleTrendsApi.explore({
84
+ keyword: 'bitcoin',
85
+ geo: 'US', // Default: 'US'
86
+ time: 'today 12-m', // Default: 'today 12-m'
87
+ category: 0, // Default: 0
88
+ property: '', // Default: ''
89
+ hl: 'en-US' // Default: 'en-US'
90
+ });
91
+
92
+ // Result structure:
93
+ // {
94
+ // widgets: Array<{
95
+ // id: string,
96
+ // request: {...},
97
+ // token: string
98
+ // }>
99
+ // }
100
+ ```
101
+
102
+ ### Interest by Region
103
+
104
+ Get interest data by region:
105
+
106
+ ```typescript
107
+ const result = await GoogleTrendsApi.interestByRegion({
108
+ keyword: 'Stock Market', // Required - string or string[]
109
+ startTime: new Date('2024-01-01'), // Optional - defaults to 2004-01-01
110
+ endTime: new Date(), // Optional - defaults to current date
111
+ geo: 'US', // Optional - string or string[] - defaults to 'US'
112
+ resolution: 'REGION', // Optional - 'COUNTRY' | 'REGION' | 'CITY' | 'DMA'
113
+ hl: 'en-US', // Optional - defaults to 'en-US'
114
+ timezone: -240, // Optional - defaults to local timezone
115
+ category: 0 // Optional - defaults to 0
116
+ });
117
+
118
+ // Result structure:
119
+ // {
120
+ // default: {
121
+ // geoMapData: Array<{
122
+ // geoCode: string,
123
+ // geoName: string,
124
+ // value: number[],
125
+ // formattedValue: string[],
126
+ // maxValueIndex: number,
127
+ // hasData: boolean[],
128
+ // coordinates?: {
129
+ // lat: number,
130
+ // lng: number
131
+ // }
132
+ // }>
133
+ // }
134
+ // }
135
+ ```
136
+
137
+ Example with multiple keywords and regions:
138
+
139
+ ```typescript
140
+ const result = await GoogleTrendsApi.interestByRegion({
141
+ keyword: ['wine', 'peanuts'],
142
+ geo: ['US-CA', 'US-VA'],
143
+ startTime: new Date('2024-01-01'),
144
+ endTime: new Date(),
145
+ resolution: 'CITY'
146
+ });
147
+ ```
148
+
149
+ ## API Reference
150
+
151
+ ### DailyTrendsOptions
152
+
153
+ ```typescript
154
+ interface DailyTrendsOptions {
155
+ geo?: string; // Default: 'US'
156
+ lang?: string; // Default: 'en'
157
+ }
158
+ ```
159
+
160
+ ### RealTimeTrendsOptions
161
+
162
+ ```typescript
163
+ interface RealTimeTrendsOptions {
164
+ geo: string;
165
+ trendingHours?: number; // Default: 4
166
+ }
167
+ ```
168
+
169
+ ### ExploreOptions
170
+
171
+ ```typescript
172
+ interface ExploreOptions {
173
+ keyword: string;
174
+ geo?: string; // Default: 'US'
175
+ time?: string; // Default: 'today 12-m'
176
+ category?: number; // Default: 0
177
+ property?: string; // Default: ''
178
+ hl?: string; // Default: 'en-US'
179
+ }
180
+ ```
181
+
182
+ ### InterestByRegionOptions
183
+
184
+ ```typescript
185
+ interface InterestByRegionOptions {
186
+ keyword: string | string[]; // Required - search term(s)
187
+ startTime?: Date; // Optional - start date
188
+ endTime?: Date; // Optional - end date
189
+ geo?: string | string[]; // Optional - geocode(s)
190
+ resolution?: 'COUNTRY' | 'REGION' | 'CITY' | 'DMA'; // Optional
191
+ hl?: string; // Optional - language code
192
+ timezone?: number; // Optional - timezone offset
193
+ category?: number; // Optional - category number
194
+ }
195
+ ```
196
+
197
+ ## Development
198
+
199
+ ### Building
200
+
201
+ ```
package/lib/constants.js CHANGED
@@ -23,7 +23,7 @@ exports.GOOGLE_TRENDS_MAPPER = {
23
23
  },
24
24
  ["explore" /* GoogleTrendsEndpoints.explore */]: {
25
25
  path: '/trends/api/explore',
26
- method: 'GET',
26
+ method: 'POST',
27
27
  host: GOOGLE_TRENDS_BASE_URL,
28
28
  url: `https://${GOOGLE_TRENDS_BASE_URL}/trends/api/explore`,
29
29
  headers: {},
@@ -4,7 +4,7 @@ export declare class GoogleTrendsApi {
4
4
  dailyTrends({ geo, lang }: DailyTrendingTopicsOptions): Promise<DailyTrendingTopics>;
5
5
  realTimeTrends({ geo, trendingHours }: RealTimeTrendsOptions): Promise<DailyTrendingTopics>;
6
6
  explore({ keyword, geo, time, category, property, hl, }: ExploreOptions): Promise<ExploreResponse>;
7
- interestByRegion({ keyword, geo, time, resolution, hl, }: InterestByRegionOptions): Promise<InterestByRegionResponse>;
7
+ interestByRegion({ keyword, startTime, endTime, geo, resolution, hl, timezone, category }: InterestByRegionOptions): Promise<InterestByRegionResponse>;
8
8
  }
9
9
  declare const _default: GoogleTrendsApi;
10
10
  export default _default;
@@ -120,44 +120,68 @@ class GoogleTrendsApi {
120
120
  }
121
121
  });
122
122
  }
123
- interestByRegion({ keyword, geo = 'US', time = 'today 12-m', resolution = 'REGION', hl = 'en-US', }) {
123
+ //
124
+ interestByRegion({ keyword, startTime = new Date('2004-01-01'), endTime = new Date(), geo = 'US', resolution = 'REGION', hl = 'en-US', timezone = new Date().getTimezoneOffset(), category = 0 }) {
124
125
  return __awaiter(this, void 0, void 0, function* () {
125
- // First get the widget token from explore
126
- const exploreResponse = yield this.explore({ keyword, geo, time, hl });
127
- const widget = exploreResponse.widgets.find((w) => w.id === 'GEO_MAP_0');
126
+ const formatDate = (date) => {
127
+ return date.toISOString().split('T')[0];
128
+ };
129
+ const formatTrendsDate = (date) => {
130
+ const pad = (n) => n.toString().padStart(2, '0');
131
+ const yyyy = date.getFullYear();
132
+ const mm = pad(date.getMonth() + 1);
133
+ const dd = pad(date.getDate());
134
+ const hh = pad(date.getHours());
135
+ const min = pad(date.getMinutes());
136
+ const ss = pad(date.getSeconds());
137
+ return `${yyyy}-${mm}-${dd}T${hh}\\:${min}\\:${ss}`;
138
+ };
139
+ const getDateRangeParam = (date) => {
140
+ const yesterday = new Date(date);
141
+ yesterday.setDate(date.getDate() - 1);
142
+ const formattedStart = formatTrendsDate(yesterday);
143
+ const formattedEnd = formatTrendsDate(date);
144
+ return `${formattedStart} ${formattedEnd}`;
145
+ };
146
+ const exploreResponse = yield this.explore({
147
+ keyword: Array.isArray(keyword) ? keyword[0] : keyword,
148
+ geo: Array.isArray(geo) ? geo[0] : geo,
149
+ time: `${getDateRangeParam(startTime)} ${getDateRangeParam(endTime)}`,
150
+ category,
151
+ hl
152
+ });
153
+ const widget = exploreResponse.widgets.find(w => w.id === 'GEO_MAP');
128
154
  if (!widget) {
129
155
  return { default: { geoMapData: [] } };
130
156
  }
131
157
  const options = Object.assign(Object.assign({}, constants_1.GOOGLE_TRENDS_MAPPER["interestByRegion" /* GoogleTrendsEndpoints.interestByRegion */]), { qs: {
132
158
  hl,
133
- tz: '240',
159
+ tz: timezone.toString(),
134
160
  req: JSON.stringify({
135
- geo: { country: geo },
136
- comparisonItem: [
137
- {
138
- time,
161
+ geo: {
162
+ country: Array.isArray(geo) ? geo[0] : geo
163
+ },
164
+ comparisonItem: [{
165
+ time: `${formatDate(startTime)} ${formatDate(endTime)}`,
139
166
  complexKeywordsRestriction: {
140
- keyword: [
141
- {
167
+ keyword: [{
142
168
  type: 'BROAD',
143
- value: keyword,
144
- },
145
- ],
146
- },
147
- },
148
- ],
169
+ value: Array.isArray(keyword) ? keyword[0] : keyword
170
+ }]
171
+ }
172
+ }],
149
173
  resolution,
150
174
  locale: hl,
151
175
  requestOptions: {
152
176
  property: '',
153
- backend: 'IZG',
154
- category: 0,
177
+ backend: 'CM',
178
+ category
155
179
  },
156
180
  userConfig: {
157
- userType: 'USER_TYPE_SCRAPER',
158
- },
181
+ userType: 'USER_TYPE_LEGIT_USER'
182
+ }
159
183
  }),
160
- token: widget.token,
184
+ token: widget.token
161
185
  } });
162
186
  try {
163
187
  const response = yield (0, request_1.request)(options.url, options);
@@ -167,7 +191,6 @@ class GoogleTrendsApi {
167
191
  return data;
168
192
  }
169
193
  catch (error) {
170
- console.error('Interest by region request failed:', error);
171
194
  return { default: { geoMapData: [] } };
172
195
  }
173
196
  });
@@ -1 +1,5 @@
1
- export declare const request: (url: string, options: RequestInit) => Promise<any>;
1
+ export declare const request: (url: string, options: {
2
+ method?: string;
3
+ qs?: Record<string, any>;
4
+ body?: string;
5
+ }) => Promise<Response>;
@@ -8,10 +8,75 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.request = void 0;
16
+ const https_1 = __importDefault(require("https"));
17
+ const querystring_1 = __importDefault(require("querystring"));
18
+ let cookieVal;
19
+ function rereq(options) {
20
+ return new Promise((resolve, reject) => {
21
+ const req = https_1.default.request(options, (res) => {
22
+ let chunk = '';
23
+ res.on('data', (data) => {
24
+ chunk += data;
25
+ });
26
+ res.on('end', () => {
27
+ resolve(chunk.toString());
28
+ });
29
+ });
30
+ req.on('error', (e) => {
31
+ reject(e);
32
+ });
33
+ req.end();
34
+ });
35
+ }
13
36
  const request = (url, options) => __awaiter(void 0, void 0, void 0, function* () {
14
- const response = yield fetch(url, options);
15
- return response;
37
+ const parsedUrl = new URL(url);
38
+ const requestOptions = {
39
+ host: parsedUrl.host,
40
+ method: options.method || 'POST',
41
+ path: `${parsedUrl.pathname}${options.qs ? '?' + querystring_1.default.stringify(options.qs) : ''}`,
42
+ headers: {}
43
+ };
44
+ if (cookieVal) {
45
+ requestOptions.headers = { 'cookie': cookieVal };
46
+ }
47
+ const response = yield new Promise((resolve, reject) => {
48
+ const req = https_1.default.request(requestOptions, (res) => {
49
+ let chunk = '';
50
+ res.on('data', (data) => {
51
+ chunk += data;
52
+ });
53
+ res.on('end', () => __awaiter(void 0, void 0, void 0, function* () {
54
+ if (res.statusCode === 429 && res.headers['set-cookie']) {
55
+ cookieVal = res.headers['set-cookie'][0].split(';')[0];
56
+ requestOptions.headers = { 'cookie': cookieVal };
57
+ try {
58
+ const retryResponse = yield rereq(requestOptions);
59
+ resolve(retryResponse);
60
+ }
61
+ catch (err) {
62
+ reject(err);
63
+ }
64
+ }
65
+ else {
66
+ resolve(chunk.toString());
67
+ }
68
+ }));
69
+ });
70
+ if (options.body) {
71
+ req.write(options.body);
72
+ }
73
+ req.on('error', (e) => {
74
+ reject(e);
75
+ });
76
+ req.end();
77
+ });
78
+ return {
79
+ text: () => Promise.resolve(response)
80
+ };
16
81
  });
17
82
  exports.request = request;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shaivpidadi/trends-js",
3
- "version": "0.0.0-beta.2",
3
+ "version": "0.0.0-beta.4",
4
4
  "description": "Google Trends API for Node.js",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",