@pipedream/bluesky 0.1.1 → 0.1.2
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/actions/create-post/create-post.mjs +3 -36
- package/actions/like-post/like-post.mjs +1 -1
- package/actions/retrieve-thread/retrieve-thread.mjs +1 -1
- package/common/textEncoding.mjs +127 -0
- package/common/tlds.mjs +1441 -0
- package/package.json +1 -1
- package/sources/new-follower-on-account/new-follower-on-account.mjs +1 -1
- package/sources/new-posts-by-author/new-posts-by-author.mjs +1 -1
- package/sources/new-timeline-posts/new-timeline-posts.mjs +1 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import app from "../../bluesky.app.mjs";
|
|
2
2
|
import constants from "../../common/constants.mjs";
|
|
3
|
+
import textEncoding from "../../common/textEncoding.mjs";
|
|
3
4
|
|
|
4
5
|
export default {
|
|
5
6
|
key: "bluesky-create-post",
|
|
6
7
|
name: "Create Post",
|
|
7
8
|
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
|
|
9
|
+
version: "0.1.0",
|
|
9
10
|
type: "action",
|
|
10
11
|
props: {
|
|
11
12
|
app,
|
|
@@ -15,40 +16,6 @@ export default {
|
|
|
15
16
|
description: "The text content of the post.",
|
|
16
17
|
},
|
|
17
18
|
},
|
|
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
|
-
},
|
|
52
19
|
async run({ $ }) {
|
|
53
20
|
const {
|
|
54
21
|
app,
|
|
@@ -62,7 +29,7 @@ export default {
|
|
|
62
29
|
record: {
|
|
63
30
|
["$type"]: constants.RESOURCE_TYPE.POST,
|
|
64
31
|
text,
|
|
65
|
-
facets:
|
|
32
|
+
facets: await textEncoding.detectFacets(text, app),
|
|
66
33
|
createdAt: new Date().toISOString(),
|
|
67
34
|
},
|
|
68
35
|
},
|
|
@@ -5,7 +5,7 @@ export default {
|
|
|
5
5
|
key: "bluesky-like-post",
|
|
6
6
|
name: "Like Post",
|
|
7
7
|
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.
|
|
8
|
+
version: "0.0.2",
|
|
9
9
|
type: "action",
|
|
10
10
|
props: {
|
|
11
11
|
app,
|
|
@@ -4,7 +4,7 @@ export default {
|
|
|
4
4
|
key: "bluesky-retrieve-thread",
|
|
5
5
|
name: "Retrieve Thread",
|
|
6
6
|
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.
|
|
7
|
+
version: "0.0.2",
|
|
8
8
|
type: "action",
|
|
9
9
|
props: {
|
|
10
10
|
app,
|
|
@@ -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
|
+
};
|