@pipedream/bluesky 0.1.1 → 0.1.3

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.
@@ -1,11 +1,17 @@
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.2",
9
+ version: "0.1.2",
10
+ annotations: {
11
+ destructiveHint: false,
12
+ openWorldHint: true,
13
+ readOnlyHint: false,
14
+ },
9
15
  type: "action",
10
16
  props: {
11
17
  app,
@@ -15,40 +21,6 @@ export default {
15
21
  description: "The text content of the post.",
16
22
  },
17
23
  },
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
24
  async run({ $ }) {
53
25
  const {
54
26
  app,
@@ -62,7 +34,7 @@ export default {
62
34
  record: {
63
35
  ["$type"]: constants.RESOURCE_TYPE.POST,
64
36
  text,
65
- facets: this.parseFacets(text),
37
+ facets: await textEncoding.detectFacets(text, app),
66
38
  createdAt: new Date().toISOString(),
67
39
  },
68
40
  },
@@ -5,7 +5,12 @@ 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.1",
8
+ version: "0.0.4",
9
+ annotations: {
10
+ destructiveHint: false,
11
+ openWorldHint: true,
12
+ readOnlyHint: false,
13
+ },
9
14
  type: "action",
10
15
  props: {
11
16
  app,
@@ -4,7 +4,12 @@ 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.1",
7
+ version: "0.0.4",
8
+ annotations: {
9
+ destructiveHint: false,
10
+ openWorldHint: true,
11
+ readOnlyHint: true,
12
+ },
8
13
  type: "action",
9
14
  props: {
10
15
  app,
@@ -2,7 +2,7 @@ const BASE_URL = "https://bsky.social";
2
2
  const VERSION_PATH = "/xrpc";
3
3
 
4
4
  const INTERACTION_EVENT = {
5
- REQUES_TLESS: "app.bsky.feed.defs#requestLess",
5
+ REQUEST_LESS: "app.bsky.feed.defs#requestLess",
6
6
  REQUEST_MORE: "app.bsky.feed.defs#requestMore",
7
7
  CLICK_THROUGH_ITEM: "app.bsky.feed.defs#clickthroughItem",
8
8
  CLICK_THROUGH_AUTHOR: "app.bsky.feed.defs#clickthroughAuthor",
@@ -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
+ };