@jackwener/opencli 0.7.9 → 0.7.10
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/dist/cli-manifest.json +7 -88
- package/dist/clis/twitter/bookmarks.js +171 -0
- package/dist/clis/twitter/delete.js +0 -1
- package/dist/clis/twitter/followers.js +5 -16
- package/dist/clis/twitter/following.js +3 -4
- package/dist/clis/twitter/like.js +0 -1
- package/dist/clis/twitter/notifications.js +17 -7
- package/dist/clis/twitter/search.js +14 -6
- package/dist/clis/twitter/trending.yaml +8 -2
- package/dist/main.js +0 -0
- package/package.json +1 -1
- package/src/clis/twitter/bookmarks.ts +201 -0
- package/src/clis/twitter/delete.ts +0 -1
- package/src/clis/twitter/followers.ts +5 -16
- package/src/clis/twitter/following.ts +3 -5
- package/src/clis/twitter/like.ts +0 -1
- package/src/clis/twitter/notifications.ts +18 -9
- package/src/clis/twitter/search.ts +14 -7
- package/src/clis/twitter/trending.yaml +8 -2
- package/dist/_debug.js +0 -7
- package/dist/browser-tab.d.ts +0 -2
- package/dist/browser-tab.js +0 -30
- package/dist/browser.d.ts +0 -105
- package/dist/browser.js +0 -644
- package/dist/clis/github/search.d.ts +0 -1
- package/dist/clis/github/search.js +0 -20
- package/dist/clis/index.d.ts +0 -27
- package/dist/clis/index.js +0 -41
- package/dist/clis/twitter/bookmarks.yaml +0 -85
- package/dist/clis/xiaohongshu/me.d.ts +0 -1
- package/dist/clis/xiaohongshu/me.js +0 -86
- package/dist/pipeline/_debug.d.ts +0 -1
- package/dist/pipeline/_debug.js +0 -7
- package/dist/promote.d.ts +0 -1
- package/dist/promote.js +0 -3
- package/dist/register.d.ts +0 -2
- package/dist/register.js +0 -2
- package/dist/scaffold.d.ts +0 -2
- package/dist/scaffold.js +0 -2
- package/dist/smoke.d.ts +0 -2
- package/dist/smoke.js +0 -2
- package/src/clis/twitter/bookmarks.yaml +0 -85
- /package/dist/{_debug.d.ts → clis/twitter/bookmarks.d.ts} +0 -0
package/dist/clis/index.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Import all TypeScript CLI adapters so they self-register.
|
|
3
|
-
*
|
|
4
|
-
* Each TS adapter calls cli() on import, which adds itself to the global registry.
|
|
5
|
-
*/
|
|
6
|
-
import './bilibili/search.js';
|
|
7
|
-
import './bilibili/me.js';
|
|
8
|
-
import './bilibili/favorite.js';
|
|
9
|
-
import './bilibili/history.js';
|
|
10
|
-
import './bilibili/feed.js';
|
|
11
|
-
import './bilibili/user-videos.js';
|
|
12
|
-
import './bilibili/ranking.js';
|
|
13
|
-
import './bilibili/dynamic.js';
|
|
14
|
-
import './github/search.js';
|
|
15
|
-
import './zhihu/question.js';
|
|
16
|
-
import './xiaohongshu/search.js';
|
|
17
|
-
import './xiaohongshu/user.js';
|
|
18
|
-
import './bbc/news.js';
|
|
19
|
-
import './weibo/hot.js';
|
|
20
|
-
import './boss/search.js';
|
|
21
|
-
import './yahoo-finance/quote.js';
|
|
22
|
-
import './reuters/search.js';
|
|
23
|
-
import './smzdm/search.js';
|
|
24
|
-
import './ctrip/search.js';
|
|
25
|
-
import './youtube/search.js';
|
|
26
|
-
import './twitter/search.js';
|
|
27
|
-
import './twitter/profile.js';
|
package/dist/clis/index.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Import all TypeScript CLI adapters so they self-register.
|
|
3
|
-
*
|
|
4
|
-
* Each TS adapter calls cli() on import, which adds itself to the global registry.
|
|
5
|
-
*/
|
|
6
|
-
// bilibili
|
|
7
|
-
import './bilibili/search.js';
|
|
8
|
-
import './bilibili/me.js';
|
|
9
|
-
import './bilibili/favorite.js';
|
|
10
|
-
import './bilibili/history.js';
|
|
11
|
-
import './bilibili/feed.js';
|
|
12
|
-
import './bilibili/user-videos.js';
|
|
13
|
-
import './bilibili/ranking.js';
|
|
14
|
-
import './bilibili/dynamic.js';
|
|
15
|
-
// github
|
|
16
|
-
import './github/search.js';
|
|
17
|
-
// zhihu
|
|
18
|
-
import './zhihu/question.js';
|
|
19
|
-
// xiaohongshu
|
|
20
|
-
import './xiaohongshu/search.js';
|
|
21
|
-
import './xiaohongshu/user.js';
|
|
22
|
-
// bbc
|
|
23
|
-
import './bbc/news.js';
|
|
24
|
-
// weibo
|
|
25
|
-
import './weibo/hot.js';
|
|
26
|
-
// boss
|
|
27
|
-
import './boss/search.js';
|
|
28
|
-
// yahoo-finance
|
|
29
|
-
import './yahoo-finance/quote.js';
|
|
30
|
-
// reuters
|
|
31
|
-
import './reuters/search.js';
|
|
32
|
-
// smzdm
|
|
33
|
-
import './smzdm/search.js';
|
|
34
|
-
// ctrip
|
|
35
|
-
import './ctrip/search.js';
|
|
36
|
-
// youtube
|
|
37
|
-
import './youtube/search.js';
|
|
38
|
-
// twitter
|
|
39
|
-
import './twitter/search.js';
|
|
40
|
-
import './twitter/profile.js';
|
|
41
|
-
// reddit
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
site: twitter
|
|
2
|
-
name: bookmarks
|
|
3
|
-
description: 获取 Twitter 书签列表
|
|
4
|
-
domain: x.com
|
|
5
|
-
browser: true
|
|
6
|
-
|
|
7
|
-
args:
|
|
8
|
-
limit:
|
|
9
|
-
type: int
|
|
10
|
-
default: 20
|
|
11
|
-
description: Number of bookmarks to return (default 20)
|
|
12
|
-
|
|
13
|
-
pipeline:
|
|
14
|
-
- navigate: https://x.com/i/bookmarks
|
|
15
|
-
- wait: 2
|
|
16
|
-
- evaluate: |
|
|
17
|
-
(async () => {
|
|
18
|
-
const ct0 = document.cookie.split(';').map(c=>c.trim()).find(c=>c.startsWith('ct0='))?.split('=')[1];
|
|
19
|
-
if (!ct0) throw new Error('No ct0 cookie. Hint: Not logged into x.com.');
|
|
20
|
-
const bearer = decodeURIComponent('AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA');
|
|
21
|
-
const _h = {'Authorization':'Bearer '+bearer, 'X-Csrf-Token':ct0, 'X-Twitter-Auth-Type':'OAuth2Session', 'X-Twitter-Active-User':'yes'};
|
|
22
|
-
|
|
23
|
-
const count = Math.min(${{ args.limit }}, 100);
|
|
24
|
-
const variables = JSON.stringify({count, includePromotedContent: false});
|
|
25
|
-
const features = JSON.stringify({
|
|
26
|
-
rweb_video_screen_enabled: false, profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
27
|
-
responsive_web_profile_redirect_enabled: false, rweb_tipjar_consumption_enabled: false,
|
|
28
|
-
verified_phone_label_enabled: false, creator_subscriptions_tweet_preview_api_enabled: true,
|
|
29
|
-
responsive_web_graphql_timeline_navigation_enabled: true,
|
|
30
|
-
responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
|
|
31
|
-
premium_content_api_read_enabled: false, communities_web_enable_tweet_community_results_fetch: true,
|
|
32
|
-
c9s_tweet_anatomy_moderator_badge_enabled: true,
|
|
33
|
-
articles_preview_enabled: true, responsive_web_edit_tweet_api_enabled: true,
|
|
34
|
-
graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
|
|
35
|
-
view_counts_everywhere_api_enabled: true, longform_notetweets_consumption_enabled: true,
|
|
36
|
-
responsive_web_twitter_article_tweet_consumption_enabled: true,
|
|
37
|
-
tweet_awards_web_tipping_enabled: false,
|
|
38
|
-
content_disclosure_indicator_enabled: true, content_disclosure_ai_generated_indicator_enabled: true,
|
|
39
|
-
freedom_of_speech_not_reach_fetch_enabled: true, standardized_nudges_misinfo: true,
|
|
40
|
-
tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
|
|
41
|
-
longform_notetweets_rich_text_read_enabled: true, longform_notetweets_inline_media_enabled: false,
|
|
42
|
-
responsive_web_enhance_cards_enabled: false
|
|
43
|
-
});
|
|
44
|
-
const url = '/i/api/graphql/Fy0QMy4q_aZCpkO0PnyLYw/Bookmarks?variables=' + encodeURIComponent(variables) + '&features=' + encodeURIComponent(features);
|
|
45
|
-
const resp = await fetch(url, {headers: _h, credentials: 'include'});
|
|
46
|
-
if (!resp.ok) throw new Error('HTTP ' + resp.status + '. Hint: queryId may have changed.');
|
|
47
|
-
const d = await resp.json();
|
|
48
|
-
|
|
49
|
-
const instructions = d.data?.bookmark_timeline_v2?.timeline?.instructions || d.data?.bookmark_timeline?.timeline?.instructions || [];
|
|
50
|
-
let tweets = [], seen = new Set();
|
|
51
|
-
for (const inst of instructions) {
|
|
52
|
-
for (const entry of (inst.entries || [])) {
|
|
53
|
-
const r = entry.content?.itemContent?.tweet_results?.result;
|
|
54
|
-
if (!r) continue;
|
|
55
|
-
const tw = r.tweet || r;
|
|
56
|
-
const l = tw.legacy || {};
|
|
57
|
-
if (!tw.rest_id || seen.has(tw.rest_id)) continue;
|
|
58
|
-
seen.add(tw.rest_id);
|
|
59
|
-
const u = tw.core?.user_results?.result;
|
|
60
|
-
const nt = tw.note_tweet?.note_tweet_results?.result?.text;
|
|
61
|
-
const screenName = u?.legacy?.screen_name || u?.core?.screen_name;
|
|
62
|
-
tweets.push({
|
|
63
|
-
id: tw.rest_id,
|
|
64
|
-
author: screenName,
|
|
65
|
-
name: u?.legacy?.name || u?.core?.name,
|
|
66
|
-
url: 'https://x.com/' + (screenName || '_') + '/status/' + tw.rest_id,
|
|
67
|
-
text: nt || l.full_text || '',
|
|
68
|
-
likes: l.favorite_count,
|
|
69
|
-
retweets: l.retweet_count,
|
|
70
|
-
created_at: l.created_at
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return tweets;
|
|
75
|
-
})()
|
|
76
|
-
|
|
77
|
-
- map:
|
|
78
|
-
author: ${{ item.author }}
|
|
79
|
-
text: ${{ item.text }}
|
|
80
|
-
likes: ${{ item.likes }}
|
|
81
|
-
url: ${{ item.url }}
|
|
82
|
-
|
|
83
|
-
- limit: ${{ args.limit }}
|
|
84
|
-
|
|
85
|
-
columns: [author, text, likes, url]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Xiaohongshu me — read self profile info.
|
|
3
|
-
*
|
|
4
|
-
* Two-step navigation:
|
|
5
|
-
* 1. /explore → get user_id from Pinia user store
|
|
6
|
-
* 2. /user/profile/{user_id} → poll until userPageData loads, read full profile
|
|
7
|
-
*/
|
|
8
|
-
import { cli, Strategy } from '../../registry.js';
|
|
9
|
-
cli({
|
|
10
|
-
site: 'xiaohongshu',
|
|
11
|
-
name: 'me',
|
|
12
|
-
description: '我的小红书个人信息',
|
|
13
|
-
domain: 'www.xiaohongshu.com',
|
|
14
|
-
strategy: Strategy.COOKIE,
|
|
15
|
-
args: [],
|
|
16
|
-
columns: ['nickname', 'red_id', 'location', 'profession', 'fans', 'follows', 'likes_collected', 'notes'],
|
|
17
|
-
func: async (page) => {
|
|
18
|
-
// Step 1: Navigate to /explore to get user_id
|
|
19
|
-
await page.goto('https://www.xiaohongshu.com/explore');
|
|
20
|
-
await page.wait(2);
|
|
21
|
-
const userId = await page.evaluate(`
|
|
22
|
-
(() => {
|
|
23
|
-
const app = document.querySelector('#app')?.__vue_app__;
|
|
24
|
-
const pinia = app?.config?.globalProperties?.$pinia;
|
|
25
|
-
const store = pinia?._s?.get('user');
|
|
26
|
-
const u = store?.$state?.userInfo || {};
|
|
27
|
-
return u.user_id || u.userId || '';
|
|
28
|
-
})()
|
|
29
|
-
`);
|
|
30
|
-
if (!userId)
|
|
31
|
-
return [{ error: 'Not logged in or user_id not found' }];
|
|
32
|
-
// Step 2: Navigate to real profile page for full data
|
|
33
|
-
await page.goto(`https://www.xiaohongshu.com/user/profile/${userId}`);
|
|
34
|
-
await page.wait(3);
|
|
35
|
-
const data = await page.evaluate(`
|
|
36
|
-
(async () => {
|
|
37
|
-
// Poll for userPageData to be populated (profile page loads async)
|
|
38
|
-
const deadline = Date.now() + 8000;
|
|
39
|
-
let pd = null;
|
|
40
|
-
while (Date.now() < deadline) {
|
|
41
|
-
const app = document.querySelector('#app')?.__vue_app__;
|
|
42
|
-
const pinia = app?.config?.globalProperties?.$pinia;
|
|
43
|
-
const store = pinia?._s?.get('user');
|
|
44
|
-
pd = store?.$state?.userPageData;
|
|
45
|
-
if (pd?.interactions?.length > 0) break;
|
|
46
|
-
await new Promise(r => setTimeout(r, 500));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!pd?.interactions?.length) {
|
|
50
|
-
return { error: 'Profile data did not load' };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const basic = pd.basicInfo || {};
|
|
54
|
-
const interactions = pd.interactions || [];
|
|
55
|
-
const tags = pd.tags || [];
|
|
56
|
-
|
|
57
|
-
const getCount = (type) => {
|
|
58
|
-
const item = interactions.find(i => i.type === type);
|
|
59
|
-
return item ? item.count : '0';
|
|
60
|
-
};
|
|
61
|
-
const getTag = (tagType) => {
|
|
62
|
-
const item = tags.find(t => t.tagType === tagType);
|
|
63
|
-
return item ? item.name : '';
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const app2 = document.querySelector('#app')?.__vue_app__;
|
|
67
|
-
const store2 = app2?.config?.globalProperties?.$pinia?._s?.get('user');
|
|
68
|
-
const noteCount = (store2?.$state?.notes || []).length;
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
nickname: basic.nickname || '',
|
|
72
|
-
red_id: basic.redId || '',
|
|
73
|
-
location: getTag('location') || basic.ipLocation || '',
|
|
74
|
-
profession: getTag('profession') || '',
|
|
75
|
-
fans: getCount('fans'),
|
|
76
|
-
follows: getCount('follows'),
|
|
77
|
-
likes_collected: getCount('interaction'),
|
|
78
|
-
notes: noteCount,
|
|
79
|
-
};
|
|
80
|
-
})()
|
|
81
|
-
`);
|
|
82
|
-
if (!data || data.error)
|
|
83
|
-
return [];
|
|
84
|
-
return [data];
|
|
85
|
-
},
|
|
86
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/pipeline/_debug.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { render, evalExpr } from './pipeline/template.js';
|
|
2
|
-
const ctx = { item: { first: 'X', second: 'Y' } };
|
|
3
|
-
console.log('evalExpr item.first:', JSON.stringify(evalExpr('item.first', ctx)));
|
|
4
|
-
console.log('evalExpr item.second:', JSON.stringify(evalExpr('item.second', ctx)));
|
|
5
|
-
const template = '$' + '{{ item.first }}-$' + '{{ item.second }}';
|
|
6
|
-
console.log('template:', JSON.stringify(template));
|
|
7
|
-
console.log('render result:', JSON.stringify(render(template, ctx)));
|
package/dist/promote.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function promoteCandidate(opts: any): any;
|
package/dist/promote.js
DELETED
package/dist/register.d.ts
DELETED
package/dist/register.js
DELETED
package/dist/scaffold.d.ts
DELETED
package/dist/scaffold.js
DELETED
package/dist/smoke.d.ts
DELETED
package/dist/smoke.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
site: twitter
|
|
2
|
-
name: bookmarks
|
|
3
|
-
description: 获取 Twitter 书签列表
|
|
4
|
-
domain: x.com
|
|
5
|
-
browser: true
|
|
6
|
-
|
|
7
|
-
args:
|
|
8
|
-
limit:
|
|
9
|
-
type: int
|
|
10
|
-
default: 20
|
|
11
|
-
description: Number of bookmarks to return (default 20)
|
|
12
|
-
|
|
13
|
-
pipeline:
|
|
14
|
-
- navigate: https://x.com/i/bookmarks
|
|
15
|
-
- wait: 2
|
|
16
|
-
- evaluate: |
|
|
17
|
-
(async () => {
|
|
18
|
-
const ct0 = document.cookie.split(';').map(c=>c.trim()).find(c=>c.startsWith('ct0='))?.split('=')[1];
|
|
19
|
-
if (!ct0) throw new Error('No ct0 cookie. Hint: Not logged into x.com.');
|
|
20
|
-
const bearer = decodeURIComponent('AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA');
|
|
21
|
-
const _h = {'Authorization':'Bearer '+bearer, 'X-Csrf-Token':ct0, 'X-Twitter-Auth-Type':'OAuth2Session', 'X-Twitter-Active-User':'yes'};
|
|
22
|
-
|
|
23
|
-
const count = Math.min(${{ args.limit }}, 100);
|
|
24
|
-
const variables = JSON.stringify({count, includePromotedContent: false});
|
|
25
|
-
const features = JSON.stringify({
|
|
26
|
-
rweb_video_screen_enabled: false, profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
27
|
-
responsive_web_profile_redirect_enabled: false, rweb_tipjar_consumption_enabled: false,
|
|
28
|
-
verified_phone_label_enabled: false, creator_subscriptions_tweet_preview_api_enabled: true,
|
|
29
|
-
responsive_web_graphql_timeline_navigation_enabled: true,
|
|
30
|
-
responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
|
|
31
|
-
premium_content_api_read_enabled: false, communities_web_enable_tweet_community_results_fetch: true,
|
|
32
|
-
c9s_tweet_anatomy_moderator_badge_enabled: true,
|
|
33
|
-
articles_preview_enabled: true, responsive_web_edit_tweet_api_enabled: true,
|
|
34
|
-
graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
|
|
35
|
-
view_counts_everywhere_api_enabled: true, longform_notetweets_consumption_enabled: true,
|
|
36
|
-
responsive_web_twitter_article_tweet_consumption_enabled: true,
|
|
37
|
-
tweet_awards_web_tipping_enabled: false,
|
|
38
|
-
content_disclosure_indicator_enabled: true, content_disclosure_ai_generated_indicator_enabled: true,
|
|
39
|
-
freedom_of_speech_not_reach_fetch_enabled: true, standardized_nudges_misinfo: true,
|
|
40
|
-
tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
|
|
41
|
-
longform_notetweets_rich_text_read_enabled: true, longform_notetweets_inline_media_enabled: false,
|
|
42
|
-
responsive_web_enhance_cards_enabled: false
|
|
43
|
-
});
|
|
44
|
-
const url = '/i/api/graphql/Fy0QMy4q_aZCpkO0PnyLYw/Bookmarks?variables=' + encodeURIComponent(variables) + '&features=' + encodeURIComponent(features);
|
|
45
|
-
const resp = await fetch(url, {headers: _h, credentials: 'include'});
|
|
46
|
-
if (!resp.ok) throw new Error('HTTP ' + resp.status + '. Hint: queryId may have changed.');
|
|
47
|
-
const d = await resp.json();
|
|
48
|
-
|
|
49
|
-
const instructions = d.data?.bookmark_timeline_v2?.timeline?.instructions || d.data?.bookmark_timeline?.timeline?.instructions || [];
|
|
50
|
-
let tweets = [], seen = new Set();
|
|
51
|
-
for (const inst of instructions) {
|
|
52
|
-
for (const entry of (inst.entries || [])) {
|
|
53
|
-
const r = entry.content?.itemContent?.tweet_results?.result;
|
|
54
|
-
if (!r) continue;
|
|
55
|
-
const tw = r.tweet || r;
|
|
56
|
-
const l = tw.legacy || {};
|
|
57
|
-
if (!tw.rest_id || seen.has(tw.rest_id)) continue;
|
|
58
|
-
seen.add(tw.rest_id);
|
|
59
|
-
const u = tw.core?.user_results?.result;
|
|
60
|
-
const nt = tw.note_tweet?.note_tweet_results?.result?.text;
|
|
61
|
-
const screenName = u?.legacy?.screen_name || u?.core?.screen_name;
|
|
62
|
-
tweets.push({
|
|
63
|
-
id: tw.rest_id,
|
|
64
|
-
author: screenName,
|
|
65
|
-
name: u?.legacy?.name || u?.core?.name,
|
|
66
|
-
url: 'https://x.com/' + (screenName || '_') + '/status/' + tw.rest_id,
|
|
67
|
-
text: nt || l.full_text || '',
|
|
68
|
-
likes: l.favorite_count,
|
|
69
|
-
retweets: l.retweet_count,
|
|
70
|
-
created_at: l.created_at
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return tweets;
|
|
75
|
-
})()
|
|
76
|
-
|
|
77
|
-
- map:
|
|
78
|
-
author: ${{ item.author }}
|
|
79
|
-
text: ${{ item.text }}
|
|
80
|
-
likes: ${{ item.likes }}
|
|
81
|
-
url: ${{ item.url }}
|
|
82
|
-
|
|
83
|
-
- limit: ${{ args.limit }}
|
|
84
|
-
|
|
85
|
-
columns: [author, text, likes, url]
|
|
File without changes
|