-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
228 lines (206 loc) · 6.23 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
const core = require("@actions/core");
const RSSParser = require("rss-parser");
const https = require("https");
// ---------------------------------------------------------------------------------------------------------------------
// Generic Node.js API to post on LinkedIn
// ---------------------------------------------------------------------------------------------------------------------
const accessToken = core.getInput("ln_access_token");
const feedList = core.getInput("feed_list");
const embedImage = core.getInput("embed_image");
const lastPostPath = core.getInput("last_post_path");
const commitUser = core.getInput("commit_user");
const commitEmail = core.getInput("commit_email");
const commitMessage = core.getInput("commit_message");
// Get LinkedIn ID, i.e. ownerId
function getLinkedinId(accessToken) {
return new Promise((resolve, reject) => {
const hostname = "api.linkedin.com";
const path = "/v2/userinfo";
const method = "GET";
const headers = {
Authorization: "Bearer " + accessToken,
"cache-control": "no-cache",
"X-Restli-Protocol-Version": "2.0.0",
};
const body = "";
_request(method, hostname, path, headers, body)
.then((r) => {
// Check if sub has anything or else call /v2/me
if (JSON.parse(r.body).sub) return resolve(JSON.parse(r.body).sub);
// If sub is empty, call /v2/me
const hostname = "api.linkedin.com";
const path = "/v2/me";
const method = "GET";
_request(method, hostname, path, headers, body)
.then((r) => {
resolve(JSON.parse(r.body).id);
})
.catch((e) => reject(e));
})
.catch((e) => reject(e));
});
}
// Check if post has already been published
function wasPostPublished(feed) {
// Read .lastPost file in .github/workflows/ to check if the post has been posted
const fs = require("fs");
const path = require("path");
const lastPost = path.join(process.env.GITHUB_WORKSPACE, lastPostPath);
let lastPostContent = "";
try {
lastPostContent = fs.readFileSync(lastPost, "utf8");
} catch (e) {
console.log("No .lastPost.txt file found");
// Create directories if they dont exist
fs.mkdirSync(path.dirname(lastPost), { recursive: true });
// Create file if it doesn't exist
fs.writeFileSync(lastPost, "");
}
// If the post has been posted, skip
if (lastPostContent === feed.items[0].link) {
console.log("Post already posted");
return true;
}
// If the post has not been posted, post
fs.writeFileSync(lastPost, feed.items[0].link);
return false;
}
function pushPastFile() {
// push the file changes to repository
const { exec } = require("child_process");
exec("git config --global user.email " + commitEmail);
exec("git config --global user.name " + commitUser);
exec("git add .");
exec("git commit -m '" + commitMessage + "'");
exec("git push");
}
// Publish content on LinkedIn
function postShare(
accessToken,
ownerId,
title,
text,
shareUrl,
shareThumbnailUrl
) {
return new Promise((resolve, reject) => {
const hostname = "api.linkedin.com";
const path = "/v2/shares";
const method = "POST";
const body = {
owner: "urn:li:person:" + ownerId,
subject: title,
text: {
text, // max 1300 characters
},
content: {
contentEntities: [
{
entityLocation: shareUrl,
thumbnails: [
{
resolvedUrl: shareThumbnailUrl,
},
],
},
],
title,
},
distribution: {
linkedInDistributionTarget: {},
},
};
const headers = {
Authorization: "Bearer " + accessToken,
"cache-control": "no-cache",
"X-Restli-Protocol-Version": "2.0.0",
"Content-Type": "application/json",
"x-li-format": "json",
"Content-Length": Buffer.byteLength(JSON.stringify(body)),
};
_request(method, hostname, path, headers, JSON.stringify(body))
.then((r) => {
resolve(r);
})
.catch((e) => reject(e));
});
}
// Generic HTTP requester
function _request(method, hostname, path, headers, body) {
return new Promise((resolve, reject) => {
const reqOpts = {
method,
hostname,
path,
headers,
rejectUnauthorized: false, // WARNING: accepting unauthorised end points for testing ONLY
};
let resBody = "";
const req = https.request(reqOpts, (res) => {
res.on("data", (data) => {
resBody += data.toString("utf8");
});
res.on("end", () => {
resolve({
status: res.statusCode,
headers: res.headers,
body: resBody,
});
});
});
req.on("error", (e) => {
reject(e);
});
if (method !== "GET") {
req.write(body);
}
req.end();
});
}
try {
const parse = async (url) => {
const feed = await new RSSParser().parseURL(url);
console.log(feed.title);
getLinkedinId(accessToken)
.then((ownerId) => {
const pastPostCheck = wasPostPublished(feed);
if (pastPostCheck) {
core.warning("Post was already published");
core.warning("Ending job because post was already published");
return;
}
postShare(
accessToken,
ownerId,
feed.title,
feed.items[0].title,
feed.items[0].link,
embedImage ?? feed.items[0].link
)
.then((r) => {
console.log(r); // status 201 signal successful posting
if (r.status === 401) {
core.setFailed(
"Failed to post on LinkedIn, please check your access token is valid"
);
return;
} else if (r.status !== 201) {
core.setFailed("Failed to post on LinkedIn");
return;
}
if (!pastPostCheck) {
pushPastFile();
}
})
.catch((e) => console.log(e));
})
.catch((e) => console.log(e));
console.log(
`${feed.items[0].title} - ${feed.items[0].link}\n${feed.items[0].contentSnippet}\n\n`
);
};
console.log("Parsing " + feedList);
parse(feedList);
} catch (error) {
core.setFailed(error.message);
}