Skip to content

Commit 37d840c

Browse files
committed
Update Bluesky component versions and add text encoding functionality
- Bump version of @pipedream/bluesky to 0.1.2 - Update action versions for create-post (0.1.0), like-post (0.0.2), retrieve-thread (0.0.2), new-follower-on-account (0.0.2), new-posts-by-author (0.0.2), and new-timeline-posts (0.0.2) - Introduce textEncoding module for detecting facets in post content - Remove deprecated methods from create-post action
1 parent 0c007db commit 37d840c

File tree

9 files changed

+1577
-42
lines changed

9 files changed

+1577
-42
lines changed

components/bluesky/actions/create-post/create-post.mjs

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import app from "../../bluesky.app.mjs";
22
import constants from "../../common/constants.mjs";
3+
import textEncoding from "../../common/textEncoding.mjs";
34

45
export default {
56
key: "bluesky-create-post",
67
name: "Create Post",
78
description: "Creates a new post on Bluesky. [See the documentation](https://docs.bsky.app/docs/api/com-atproto-repo-create-record).",
8-
version: "0.0.2",
9+
version: "0.1.0",
910
type: "action",
1011
props: {
1112
app,
@@ -15,40 +16,6 @@ export default {
1516
description: "The text content of the post.",
1617
},
1718
},
18-
methods: {
19-
parseUrls(text) {
20-
const spans = [];
21-
const urlRegex = /(?:[$|\W])(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*[-a-zA-Z0-9@%_+~#//=])?)/g;
22-
23-
let match;
24-
while ((match = urlRegex.exec(text)) !== null) {
25-
spans.push({
26-
start: match.index + 1,
27-
end: urlRegex.lastIndex,
28-
url: match[1],
29-
});
30-
}
31-
return spans;
32-
},
33-
parseFacets(text) {
34-
const facets = [];
35-
for (const link of this.parseUrls(text)) {
36-
facets.push({
37-
index: {
38-
byteStart: link["start"],
39-
byteEnd: link["end"],
40-
},
41-
features: [
42-
{
43-
["$type"]: "app.bsky.richtext.facet#link",
44-
uri: link["url"],
45-
},
46-
],
47-
});
48-
}
49-
return facets;
50-
},
51-
},
5219
async run({ $ }) {
5320
const {
5421
app,
@@ -62,7 +29,7 @@ export default {
6229
record: {
6330
["$type"]: constants.RESOURCE_TYPE.POST,
6431
text,
65-
facets: this.parseFacets(text),
32+
facets: await textEncoding.detectFacets(text, app),
6633
createdAt: new Date().toISOString(),
6734
},
6835
},

components/bluesky/actions/like-post/like-post.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default {
55
key: "bluesky-like-post",
66
name: "Like Post",
77
description: "Like a specific post on Bluesky. [See the documentation](https://docs.bsky.app/docs/api/com-atproto-repo-create-record).",
8-
version: "0.0.1",
8+
version: "0.0.2",
99
type: "action",
1010
props: {
1111
app,

components/bluesky/actions/retrieve-thread/retrieve-thread.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default {
44
key: "bluesky-retrieve-thread",
55
name: "Retrieve Thread",
66
description: "Retrieve a full thread of posts. [See the documentation](https://docs.bsky.app/docs/api/app-bsky-feed-get-post-thread).",
7-
version: "0.0.1",
7+
version: "0.0.2",
88
type: "action",
99
props: {
1010
app,
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import TLDs from "./tlds.mjs";
2+
3+
function isValidDomain(str) {
4+
return !!TLDs.find((tld) => {
5+
const i = str.lastIndexOf(tld);
6+
if (i === -1) {
7+
return false;
8+
}
9+
return str.charAt(i - 1) === "." && i === str.length - tld.length;
10+
});
11+
}
12+
13+
function utf16IndexToUtf8Index(i, utf16) {
14+
const encoder = new TextEncoder();
15+
return encoder.encode(utf16.slice(0, i)).byteLength;
16+
}
17+
18+
async function detectFacets(text, app) {
19+
let match;
20+
const facets = [];
21+
22+
// mentions
23+
const re1 = /(^|\s|\()(@)([a-zA-Z0-9.-]+)(\b)/g;
24+
while ((match = re1.exec(text))) {
25+
if (!isValidDomain(match[3]) && !match[3].endsWith(".test")) {
26+
continue; // probably not a handle
27+
}
28+
29+
const start = text.indexOf(match[3], match.index) - 1;
30+
const handle = await app.resolveHandle({
31+
params: {
32+
handle: match[3],
33+
},
34+
});
35+
facets.push({
36+
$type: "app.bsky.richtext.facet",
37+
index: {
38+
byteStart: utf16IndexToUtf8Index(start, text),
39+
byteEnd: utf16IndexToUtf8Index(start + match[3].length + 1, text),
40+
},
41+
features: [
42+
{
43+
$type: "app.bsky.richtext.facet#mention",
44+
did: handle.did, // must be resolved afterwards
45+
},
46+
],
47+
});
48+
}
49+
50+
// links
51+
const re2 =
52+
/(^|\s|\()((https?:\/\/[\S]+)|((?<domain>[a-z][a-z0-9]*(\.[a-z0-9]+)+)[\S]*))/gim;
53+
while ((match = re2.exec(text))) {
54+
let uri = match[2];
55+
if (!uri.startsWith("http")) {
56+
const domain = match.groups?.domain;
57+
if (!domain || !isValidDomain(domain)) {
58+
continue;
59+
}
60+
uri = `https://${uri}`;
61+
}
62+
const start = text.indexOf(match[2], match.index);
63+
const index = {
64+
start,
65+
end: start + match[2].length,
66+
};
67+
// strip ending puncuation
68+
if (/[.,;!?]$/.test(uri)) {
69+
uri = uri.slice(0, -1);
70+
index.end--;
71+
}
72+
if (/[)]$/.test(uri) && !uri.includes("(")) {
73+
uri = uri.slice(0, -1);
74+
index.end--;
75+
}
76+
facets.push({
77+
index: {
78+
byteStart: utf16IndexToUtf8Index(index.start, text),
79+
byteEnd: utf16IndexToUtf8Index(index.end, text),
80+
},
81+
features: [
82+
{
83+
$type: "app.bsky.richtext.facet#link",
84+
uri,
85+
},
86+
],
87+
});
88+
}
89+
90+
const re3 = /(?:^|\s)(#[^\d\s]\S*)(?=\s)?/g;
91+
while ((match = re3.exec(text))) {
92+
let [
93+
tag,
94+
] = match;
95+
const hasLeadingSpace = /^\s/.test(tag);
96+
97+
tag = tag.trim().replace(/\p{P}+$/gu, ""); // strip ending punctuation
98+
99+
// inclusive of #, max of 64 chars
100+
if (tag.length > 66) continue;
101+
102+
const index = match.index + (hasLeadingSpace
103+
? 1
104+
: 0);
105+
106+
facets.push({
107+
index: {
108+
byteStart: utf16IndexToUtf8Index(index, text),
109+
byteEnd: utf16IndexToUtf8Index(index + tag.length, text), // inclusive of last char
110+
},
111+
features: [
112+
{
113+
$type: "app.bsky.richtext.facet#tag",
114+
tag: tag.replace(/^#/, ""),
115+
},
116+
],
117+
});
118+
}
119+
120+
return facets.length > 0
121+
? facets
122+
: undefined;
123+
}
124+
125+
export default {
126+
detectFacets,
127+
};

0 commit comments

Comments
 (0)