···
2
2
3
3
import (
4
4
"fmt"
5
5
+
"strings"
5
6
6
7
appbsky "github.com/bluesky-social/indigo/api/bsky"
7
8
"github.com/bluesky-social/indigo/atproto/syntax"
···
71
72
}
72
73
73
74
func ExtractPostBlobCIDsPost(post *appbsky.FeedPost) []string {
75
75
+
if post.Embed == nil {
76
76
+
return []string{}
77
77
+
}
78
78
+
74
79
var out []string
75
80
if post.Embed.EmbedImages != nil {
76
81
for _, img := range post.Embed.EmbedImages.Images {
···
85
90
}
86
91
}
87
92
}
93
93
+
if post.Embed.EmbedGallery != nil {
94
94
+
for _, item := range post.Embed.EmbedGallery.Items {
95
95
+
if item.EmbedGallery_Image != nil && item.EmbedGallery_Image.Image != nil {
96
96
+
out = append(out, item.EmbedGallery_Image.Image.Ref.String())
97
97
+
}
98
98
+
}
99
99
+
}
100
100
+
88
101
return DedupeStrings(out)
89
102
}
90
103
···
100
113
}
101
114
102
115
func ExtractTextTokensPost(post *appbsky.FeedPost) []string {
103
103
-
s := post.Text
116
116
+
s := strings.Builder{}
117
117
+
s.WriteString(post.Text)
104
118
if post.Embed != nil {
105
119
if post.Embed.EmbedImages != nil {
106
120
for _, img := range post.Embed.EmbedImages.Images {
107
121
if img.Alt != "" {
108
108
-
s += " " + img.Alt
122
122
+
s.WriteString(" " + img.Alt)
109
123
}
110
124
}
111
125
}
···
114
128
if media.EmbedImages != nil {
115
129
for _, img := range media.EmbedImages.Images {
116
130
if img.Alt != "" {
117
117
-
s += " " + img.Alt
131
131
+
s.WriteString(" " + img.Alt)
118
132
}
119
133
}
120
134
}
121
135
}
136
136
+
if post.Embed.EmbedGallery != nil {
137
137
+
for _, item := range post.Embed.EmbedGallery.Items {
138
138
+
if item.EmbedGallery_Image != nil && item.EmbedGallery_Image.Alt != "" {
139
139
+
s.WriteString(" " + item.EmbedGallery_Image.Alt)
140
140
+
}
141
141
+
}
142
142
+
}
122
143
}
123
123
-
return keyword.TokenizeText(s)
144
144
+
return keyword.TokenizeText(s.String())
124
145
}
125
146
126
147
func ExtractTextTokensProfile(profile *appbsky.ActorProfile) []string {
···
1
1
package helpers
2
2
3
3
import (
4
4
+
"testing"
5
5
+
4
6
comatproto "github.com/bluesky-social/indigo/api/atproto"
5
7
appbsky "github.com/bluesky-social/indigo/api/bsky"
6
6
-
"testing"
8
8
+
"github.com/bluesky-social/indigo/automod/keyword"
9
9
+
lexutil "github.com/bluesky-social/indigo/lex/util"
7
10
11
11
+
"github.com/ipfs/go-cid"
8
12
"github.com/stretchr/testify/assert"
9
13
)
10
14
···
139
143
assert.True(PostMentionsAnyDid(post, didList1))
140
144
assert.False(PostMentionsAnyDid(post, didList2))
141
145
}
146
146
+
147
147
+
func mustBlob(t *testing.T, c string) *lexutil.LexBlob {
148
148
+
t.Helper()
149
149
+
parsed, err := cid.Decode(c)
150
150
+
if err != nil {
151
151
+
t.Fatalf("invalid test CID %q: %v", c, err)
152
152
+
}
153
153
+
return &lexutil.LexBlob{
154
154
+
Ref: lexutil.LexLink(parsed),
155
155
+
MimeType: "image/jpeg",
156
156
+
Size: 1024,
157
157
+
}
158
158
+
}
159
159
+
160
160
+
func TestExtractPostBlobCIDsPost(t *testing.T) {
161
161
+
cidA := "bafkreieqq463374bbcbeq7gpmet5rvrpeqow6t4rtjzrkhnlumdylagaqa"
162
162
+
cidB := "bafkreicwamkg77pijyudfbdmskelsnuztr6gp62lqfjv3e3urbs3gxnv2m"
163
163
+
164
164
+
tests := []struct {
165
165
+
name string
166
166
+
embed *appbsky.FeedPost_Embed
167
167
+
expected []string
168
168
+
}{
169
169
+
{
170
170
+
name: "nil embed",
171
171
+
embed: nil,
172
172
+
expected: []string{},
173
173
+
},
174
174
+
{
175
175
+
name: "empty embed",
176
176
+
embed: &appbsky.FeedPost_Embed{},
177
177
+
expected: nil,
178
178
+
},
179
179
+
{
180
180
+
name: "images only",
181
181
+
embed: &appbsky.FeedPost_Embed{
182
182
+
EmbedImages: &appbsky.EmbedImages{
183
183
+
Images: []*appbsky.EmbedImages_Image{
184
184
+
{Image: mustBlob(t, cidA)},
185
185
+
{Image: mustBlob(t, cidB)},
186
186
+
},
187
187
+
},
188
188
+
},
189
189
+
expected: []string{cidA, cidB},
190
190
+
},
191
191
+
{
192
192
+
name: "recordWithMedia images",
193
193
+
embed: &appbsky.FeedPost_Embed{
194
194
+
EmbedRecordWithMedia: &appbsky.EmbedRecordWithMedia{
195
195
+
Media: &appbsky.EmbedRecordWithMedia_Media{
196
196
+
EmbedImages: &appbsky.EmbedImages{
197
197
+
Images: []*appbsky.EmbedImages_Image{
198
198
+
{Image: mustBlob(t, cidA)},
199
199
+
},
200
200
+
},
201
201
+
},
202
202
+
},
203
203
+
},
204
204
+
expected: []string{cidA},
205
205
+
},
206
206
+
{
207
207
+
name: "gallery only",
208
208
+
embed: &appbsky.FeedPost_Embed{
209
209
+
EmbedGallery: &appbsky.EmbedGallery{
210
210
+
Items: []*appbsky.EmbedGallery_Items_Elem{
211
211
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Image: mustBlob(t, cidA)}},
212
212
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Image: mustBlob(t, cidB)}},
213
213
+
},
214
214
+
},
215
215
+
},
216
216
+
expected: []string{cidA, cidB},
217
217
+
},
218
218
+
{
219
219
+
name: "gallery with nil EmbedGallery_Image element",
220
220
+
embed: &appbsky.FeedPost_Embed{
221
221
+
EmbedGallery: &appbsky.EmbedGallery{
222
222
+
Items: []*appbsky.EmbedGallery_Items_Elem{
223
223
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Image: mustBlob(t, cidA)}},
224
224
+
{EmbedGallery_Image: nil},
225
225
+
},
226
226
+
},
227
227
+
},
228
228
+
expected: []string{cidA},
229
229
+
},
230
230
+
{
231
231
+
name: "gallery with nil Image blob",
232
232
+
embed: &appbsky.FeedPost_Embed{
233
233
+
EmbedGallery: &appbsky.EmbedGallery{
234
234
+
Items: []*appbsky.EmbedGallery_Items_Elem{
235
235
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Image: mustBlob(t, cidA)}},
236
236
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Image: nil}},
237
237
+
},
238
238
+
},
239
239
+
},
240
240
+
expected: []string{cidA},
241
241
+
},
242
242
+
{
243
243
+
name: "images and gallery with duplicate CID are deduped",
244
244
+
embed: &appbsky.FeedPost_Embed{
245
245
+
EmbedImages: &appbsky.EmbedImages{
246
246
+
Images: []*appbsky.EmbedImages_Image{
247
247
+
{Image: mustBlob(t, cidA)},
248
248
+
},
249
249
+
},
250
250
+
EmbedGallery: &appbsky.EmbedGallery{
251
251
+
Items: []*appbsky.EmbedGallery_Items_Elem{
252
252
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Image: mustBlob(t, cidA)}},
253
253
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Image: mustBlob(t, cidB)}},
254
254
+
},
255
255
+
},
256
256
+
},
257
257
+
expected: []string{cidA, cidB},
258
258
+
},
259
259
+
}
260
260
+
261
261
+
for _, tc := range tests {
262
262
+
t.Run(tc.name, func(t *testing.T) {
263
263
+
assert := assert.New(t)
264
264
+
post := &appbsky.FeedPost{
265
265
+
Text: "irrelevant",
266
266
+
Embed: tc.embed,
267
267
+
}
268
268
+
got := ExtractPostBlobCIDsPost(post)
269
269
+
assert.ElementsMatch(tc.expected, got)
270
270
+
})
271
271
+
}
272
272
+
}
273
273
+
274
274
+
func TestExtractTextTokensPost(t *testing.T) {
275
275
+
tests := []struct {
276
276
+
name string
277
277
+
text string
278
278
+
embed *appbsky.FeedPost_Embed
279
279
+
expectedText string
280
280
+
}{
281
281
+
{
282
282
+
name: "text only, no embed",
283
283
+
text: "hello world",
284
284
+
embed: nil,
285
285
+
expectedText: "hello world",
286
286
+
},
287
287
+
{
288
288
+
name: "text plus image alt",
289
289
+
text: "hello",
290
290
+
embed: &appbsky.FeedPost_Embed{
291
291
+
EmbedImages: &appbsky.EmbedImages{
292
292
+
Images: []*appbsky.EmbedImages_Image{
293
293
+
{Alt: "sunset"},
294
294
+
},
295
295
+
},
296
296
+
},
297
297
+
expectedText: "hello sunset",
298
298
+
},
299
299
+
{
300
300
+
name: "text plus recordWithMedia image alt",
301
301
+
text: "hi",
302
302
+
embed: &appbsky.FeedPost_Embed{
303
303
+
EmbedRecordWithMedia: &appbsky.EmbedRecordWithMedia{
304
304
+
Media: &appbsky.EmbedRecordWithMedia_Media{
305
305
+
EmbedImages: &appbsky.EmbedImages{
306
306
+
Images: []*appbsky.EmbedImages_Image{
307
307
+
{Alt: "cat"},
308
308
+
},
309
309
+
},
310
310
+
},
311
311
+
},
312
312
+
},
313
313
+
expectedText: "hi cat",
314
314
+
},
315
315
+
{
316
316
+
name: "text plus gallery alts",
317
317
+
text: "post",
318
318
+
embed: &appbsky.FeedPost_Embed{
319
319
+
EmbedGallery: &appbsky.EmbedGallery{
320
320
+
Items: []*appbsky.EmbedGallery_Items_Elem{
321
321
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Alt: "one"}},
322
322
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Alt: "two"}},
323
323
+
},
324
324
+
},
325
325
+
},
326
326
+
expectedText: "post one two",
327
327
+
},
328
328
+
{
329
329
+
name: "gallery with nil EmbedGallery_Image element",
330
330
+
text: "x",
331
331
+
embed: &appbsky.FeedPost_Embed{
332
332
+
EmbedGallery: &appbsky.EmbedGallery{
333
333
+
Items: []*appbsky.EmbedGallery_Items_Elem{
334
334
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Alt: "a"}},
335
335
+
{EmbedGallery_Image: nil},
336
336
+
},
337
337
+
},
338
338
+
},
339
339
+
expectedText: "x a",
340
340
+
},
341
341
+
{
342
342
+
name: "gallery item with empty alt is skipped",
343
343
+
text: "x",
344
344
+
embed: &appbsky.FeedPost_Embed{
345
345
+
EmbedGallery: &appbsky.EmbedGallery{
346
346
+
Items: []*appbsky.EmbedGallery_Items_Elem{
347
347
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Alt: ""}},
348
348
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Alt: "b"}},
349
349
+
},
350
350
+
},
351
351
+
},
352
352
+
expectedText: "x b",
353
353
+
},
354
354
+
{
355
355
+
name: "combined images and gallery alts",
356
356
+
text: "start",
357
357
+
embed: &appbsky.FeedPost_Embed{
358
358
+
EmbedImages: &appbsky.EmbedImages{
359
359
+
Images: []*appbsky.EmbedImages_Image{
360
360
+
{Alt: "img"},
361
361
+
},
362
362
+
},
363
363
+
EmbedGallery: &appbsky.EmbedGallery{
364
364
+
Items: []*appbsky.EmbedGallery_Items_Elem{
365
365
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Alt: "g1"}},
366
366
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Alt: "g2"}},
367
367
+
},
368
368
+
},
369
369
+
},
370
370
+
expectedText: "start img g1 g2",
371
371
+
},
372
372
+
{
373
373
+
name: "empty post text with gallery alt",
374
374
+
text: "",
375
375
+
embed: &appbsky.FeedPost_Embed{
376
376
+
EmbedGallery: &appbsky.EmbedGallery{
377
377
+
Items: []*appbsky.EmbedGallery_Items_Elem{
378
378
+
{EmbedGallery_Image: &appbsky.EmbedGallery_Image{Alt: "only"}},
379
379
+
},
380
380
+
},
381
381
+
},
382
382
+
expectedText: " only",
383
383
+
},
384
384
+
}
385
385
+
386
386
+
for _, tc := range tests {
387
387
+
t.Run(tc.name, func(t *testing.T) {
388
388
+
assert := assert.New(t)
389
389
+
post := &appbsky.FeedPost{
390
390
+
Text: tc.text,
391
391
+
Embed: tc.embed,
392
392
+
}
393
393
+
got := ExtractTextTokensPost(post)
394
394
+
assert.Equal(keyword.TokenizeText(tc.expectedText), got)
395
395
+
})
396
396
+
}
397
397
+
}
···
43
43
}
44
44
tagTextRatio := float64(tagChars) / float64(len(post.Text))
45
45
// if there is an image, allow some more tags
46
46
-
if len(tags) > 4 && tagTextRatio > 0.6 && post.Embed.EmbedImages == nil {
46
46
+
hasImages := (post.Embed.EmbedImages != nil && len(post.Embed.EmbedImages.Images) > 0) || (post.Embed.EmbedGallery != nil && len(post.Embed.EmbedGallery.Items) > 0)
47
47
+
if len(tags) > 4 && tagTextRatio > 0.6 && !hasImages {
47
48
c.AddRecordFlag("many-hashtags")
48
49
c.Notify("slack")
49
50
} else if len(tags) > 7 && tagTextRatio > 0.8 {