JavaScript-optional public web frontend for Bluesky
anartia.kelinci.net
sveltekit
atcute
bluesky
typescript
svelte
1import type { ComAtprotoLabelDefs } from '@atcute/atproto';
2import type { Did } from '@atcute/lexicons';
3
4export const FlagsNone = 0;
5
6/** Label blurs media (images and videos in posts) */
7export const FlagsBlurMedia = 1 << 0;
8/** Label blurs content (text in posts), assumes FlagsBlurMedia */
9export const FlagsBlurContent = 1 << 1;
10/** Label can't be self-applied */
11export const FlagsNoSelf = 1 << 2;
12
13type Label = ComAtprotoLabelDefs.Label;
14
15export interface LabelDefinition {
16 name: string;
17 flags: number;
18}
19
20export const LABEL_MAPPING: Record<string, LabelDefinition> = {
21 // Global "system" labels
22 '!hide': {
23 name: `Hidden by moderators`,
24 flags: FlagsBlurContent | FlagsNoSelf,
25 },
26 '!warn': {
27 name: `Content warning`,
28 flags: FlagsBlurContent | FlagsNoSelf,
29 },
30
31 // Global user-applicable labels
32 porn: {
33 name: `Adult content`,
34 flags: FlagsBlurMedia,
35 },
36 sexual: {
37 name: `Sexually suggestive`,
38 flags: FlagsBlurMedia,
39 },
40 'graphic-media': {
41 name: `Graphic media`,
42 flags: FlagsBlurMedia,
43 },
44 nudity: {
45 name: `Nudity`,
46 flags: FlagsBlurMedia,
47 },
48
49 // @moderation.bsky.app's labels
50 'sexual-figurative': {
51 name: `Sexually suggestive (cartoon)`,
52 flags: FlagsBlurMedia | FlagsNoSelf,
53 },
54
55 'self-harm': {
56 name: `Self-harm`,
57 flags: FlagsBlurContent | FlagsNoSelf,
58 },
59 sensitive: {
60 name: `Sensitive content`,
61 flags: FlagsBlurContent | FlagsNoSelf,
62 },
63 extremist: {
64 name: `Extremism`,
65 flags: FlagsBlurContent | FlagsNoSelf,
66 },
67 intolerant: {
68 name: `Intolerance`,
69 flags: FlagsBlurContent | FlagsNoSelf,
70 },
71 threat: {
72 name: `Threats`,
73 flags: FlagsBlurContent | FlagsNoSelf,
74 },
75 rude: {
76 name: `Rude`,
77 flags: FlagsBlurContent | FlagsNoSelf,
78 },
79 illicit: {
80 name: `Illicit content`,
81 flags: FlagsBlurContent | FlagsNoSelf,
82 },
83 security: {
84 name: `Security risk`,
85 flags: FlagsBlurContent | FlagsNoSelf,
86 },
87 'unsafe-link': {
88 name: `Unsafe link`,
89 flags: FlagsBlurContent | FlagsNoSelf,
90 },
91 impersonation: {
92 name: `Impersonation`,
93 flags: FlagsBlurContent | FlagsNoSelf,
94 },
95 misinformation: {
96 name: `Misinformation`,
97 flags: FlagsBlurContent | FlagsNoSelf,
98 },
99 scam: {
100 name: `Scam`,
101 flags: FlagsBlurContent | FlagsNoSelf,
102 },
103 'engagement-farming': {
104 name: `Engagement farming`,
105 flags: FlagsBlurContent | FlagsNoSelf,
106 },
107 spam: {
108 name: `Spam`,
109 flags: FlagsBlurContent | FlagsNoSelf,
110 },
111 rumor: {
112 name: `Rumor`,
113 flags: FlagsBlurContent | FlagsNoSelf,
114 },
115 misleading: {
116 name: `Misleading`,
117 flags: FlagsBlurContent | FlagsNoSelf,
118 },
119 inauthentic: {
120 name: `Inauthentic`,
121 flags: FlagsBlurContent | FlagsNoSelf,
122 },
123};
124
125export const findLabel = (
126 labels: Label[] | undefined,
127 authorDid: Did,
128 mask: number,
129): LabelDefinition | undefined => {
130 if (labels?.length) {
131 for (let idx = 0, len = labels.length; idx < len; idx++) {
132 const label = labels[idx];
133 const val = label.val;
134
135 if (!(val in LABEL_MAPPING)) {
136 continue;
137 }
138
139 const def = LABEL_MAPPING[val];
140
141 if (def.flags & FlagsNoSelf && label.src === authorDid) {
142 continue;
143 }
144
145 if (def.flags & mask) {
146 return def;
147 }
148 }
149 }
150};