Skip to content

Commit 5ca3aa1

Browse files
committed
feat(syndication): target CodingCat.dev Posts Google Task list with deduplication and 1-tap links
1 parent 7af2a71 commit 5ca3aa1

1 file changed

Lines changed: 81 additions & 26 deletions

File tree

‎scripts/run-syndicate-post.mjs‎

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,41 +58,96 @@ async function createGoogleSyndicationTask({ title, canonicalUrl, tags = [] }) {
5858
return;
5959
}
6060

61-
const taskTitle = `Cross-post to Hashnode & Medium: ${title}`;
61+
const hnUrl = `https://news.ycombinator.com/submitlink?u=${encodeURIComponent(canonicalUrl)}&t=${encodeURIComponent(title)}`;
62+
const taskTitle = `Post: ${title}`;
6263
const taskNotes = [
63-
`Canonical URL: ${canonicalUrl}`,
64+
`📌 COPY-PASTE TITLE:`,
65+
title,
66+
``,
67+
`🔗 CANONICAL URL:`,
68+
canonicalUrl,
6469
`Tags: ${tags.join(", ")}`,
6570
``,
66-
`1. Hashnode (Free UI Editor):`,
67-
` - Open: https://hashnode.com/draft`,
68-
` - Set Original Article URL (Canonical): ${canonicalUrl}`,
71+
`🚀 1-TAP CROSS-POST LINKS:`,
72+
`1. Hacker News (1-Tap Submit):`,
73+
` ${hnUrl}`,
6974
``,
70-
`2. Medium (1-Click Import):`,
71-
` - Open: https://medium.com/p/import`,
72-
` - Paste: ${canonicalUrl} (automatically sets canonical tag)`,
75+
`2. Medium (1-Click Import - automatically sets canonical tag):`,
76+
` https://medium.com/p/import`,
77+
` Paste: ${canonicalUrl}`,
7378
``,
74-
`3. daily.dev Squad:`,
75-
` - Open: https://daily.dev/squads/codingcatdev -> Click "Share a link" -> Paste ${canonicalUrl}`,
79+
`3. Hashnode (Free UI Editor):`,
80+
` https://hashnode.com/draft`,
81+
` Original Article URL: ${canonicalUrl}`,
7682
].join("\n");
7783

78-
// 1. Try Google Tasks API first
79-
const tasksRes = await fetch("https://tasks.googleapis.com/tasks/v1/lists/@default/tasks", {
80-
method: "POST",
81-
headers: {
82-
Authorization: `Bearer ${accessToken}`,
83-
"Content-Type": "application/json",
84-
},
85-
body: JSON.stringify({
86-
title: taskTitle,
87-
notes: taskNotes,
88-
due: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
89-
}),
84+
// 1. Try Google Tasks API first (targeting the "CodingCat.dev Posts" list)
85+
let targetListId = "@default";
86+
const listsRes = await fetch("https://tasks.googleapis.com/tasks/v1/users/@me/lists?maxResults=100", {
87+
headers: { Authorization: `Bearer ${accessToken}` },
9088
});
89+
if (listsRes.ok) {
90+
const listsData = await listsRes.json();
91+
const lists = listsData.items || [];
92+
const foundList = lists.find(
93+
(l) => (l.title || "").trim().toLowerCase() === "codingcat.dev posts"
94+
);
95+
if (foundList?.id) {
96+
targetListId = foundList.id;
97+
} else {
98+
const createListRes = await fetch("https://tasks.googleapis.com/tasks/v1/users/@me/lists", {
99+
method: "POST",
100+
headers: {
101+
Authorization: `Bearer ${accessToken}`,
102+
"Content-Type": "application/json",
103+
},
104+
body: JSON.stringify({ title: "CodingCat.dev Posts" }),
105+
});
106+
if (createListRes.ok) {
107+
const createdList = await createListRes.json();
108+
if (createdList?.id) targetListId = createdList.id;
109+
}
110+
}
91111

92-
if (tasksRes.ok) {
93-
const createdTask = await tasksRes.json();
94-
console.log(`✓ Created Google Task: "${taskTitle}" (ID: ${createdTask.id})`);
95-
return;
112+
// Check existing tasks in targetListId to prevent duplicates
113+
const existingTasksRes = await fetch(
114+
`https://tasks.googleapis.com/tasks/v1/lists/${encodeURIComponent(targetListId)}/tasks?maxResults=100`,
115+
{ headers: { Authorization: `Bearer ${accessToken}` } }
116+
);
117+
if (existingTasksRes.ok) {
118+
const existingData = await existingTasksRes.json();
119+
const existingTask = (existingData.items || []).find(
120+
(t) =>
121+
(t.notes && t.notes.includes(canonicalUrl)) ||
122+
(t.title && t.title.toLowerCase() === taskTitle.toLowerCase())
123+
);
124+
if (existingTask) {
125+
console.log(`✓ Google Task already exists in "CodingCat.dev Posts": "${taskTitle}" (ID: ${existingTask.id})`);
126+
return;
127+
}
128+
}
129+
130+
const tasksRes = await fetch(
131+
`https://tasks.googleapis.com/tasks/v1/lists/${encodeURIComponent(targetListId)}/tasks`,
132+
{
133+
method: "POST",
134+
headers: {
135+
Authorization: `Bearer ${accessToken}`,
136+
"Content-Type": "application/json",
137+
},
138+
body: JSON.stringify({
139+
title: taskTitle,
140+
notes: taskNotes,
141+
due: new Date().toISOString(),
142+
}),
143+
}
144+
);
145+
146+
if (tasksRes.ok) {
147+
const createdTask = await tasksRes.json();
148+
console.log(`✓ Created Google Task in "CodingCat.dev Posts": "${taskTitle}" (ID: ${createdTask.id})`);
149+
return;
150+
}
96151
}
97152

98153
// 2. Fallback to Google Calendar To-Do Event on alex.patterson@gmail.com if Tasks scope is not yet authorized

0 commit comments

Comments
 (0)