@evanp/activitypub-bot 0.13.0 → 0.13.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.
Files changed (41) hide show
  1. package/package.json +2 -2
  2. package/.github/dependabot.yml +0 -11
  3. package/.github/workflows/main.yml +0 -34
  4. package/.github/workflows/tag.yml +0 -106
  5. package/.nvmrc +0 -1
  6. package/Dockerfile +0 -17
  7. package/docs/activitypub.bot.drawio +0 -110
  8. package/tests/activitydistributor.test.js +0 -606
  9. package/tests/activityhandler.test.js +0 -2276
  10. package/tests/activitypubclient.test.js +0 -210
  11. package/tests/actorstorage.test.js +0 -283
  12. package/tests/app.test.js +0 -17
  13. package/tests/authorizer.test.js +0 -301
  14. package/tests/bot.donothing.test.js +0 -30
  15. package/tests/bot.ok.test.js +0 -101
  16. package/tests/botcontext.test.js +0 -720
  17. package/tests/botdatastorage.test.js +0 -88
  18. package/tests/botfactory.provincebotfactory.test.js +0 -430
  19. package/tests/digester.test.js +0 -56
  20. package/tests/fixtures/bots.js +0 -27
  21. package/tests/fixtures/eventloggingbot.js +0 -57
  22. package/tests/fixtures/provincebotfactory.js +0 -53
  23. package/tests/httpsignature.test.js +0 -199
  24. package/tests/httpsignatureauthenticator.test.js +0 -463
  25. package/tests/index.test.js +0 -12
  26. package/tests/keystorage.test.js +0 -124
  27. package/tests/microsyntax.test.js +0 -123
  28. package/tests/objectcache.test.js +0 -133
  29. package/tests/objectstorage.test.js +0 -149
  30. package/tests/remotekeystorage.test.js +0 -78
  31. package/tests/routes.actor.test.js +0 -214
  32. package/tests/routes.collection.test.js +0 -310
  33. package/tests/routes.health.test.js +0 -41
  34. package/tests/routes.inbox.test.js +0 -216
  35. package/tests/routes.object.test.js +0 -525
  36. package/tests/routes.server.test.js +0 -69
  37. package/tests/routes.sharedinbox.test.js +0 -473
  38. package/tests/routes.webfinger.test.js +0 -68
  39. package/tests/urlformatter.test.js +0 -164
  40. package/tests/utils/digest.js +0 -7
  41. package/tests/utils/nock.js +0 -499
@@ -1,2276 +0,0 @@
1
- import { describe, it, before, after, beforeEach } from 'node:test'
2
- import assert from 'node:assert'
3
- import { ActivityHandler } from '../lib/activityhandler.js'
4
- import { Sequelize } from 'sequelize'
5
- import { BotDataStorage } from '../lib/botdatastorage.js'
6
- import { ObjectStorage } from '../lib/objectstorage.js'
7
- import { KeyStorage } from '../lib/keystorage.js'
8
- import { UrlFormatter } from '../lib/urlformatter.js'
9
- import { ActivityPubClient } from '../lib/activitypubclient.js'
10
- import { ActivityDistributor } from '../lib/activitydistributor.js'
11
- import { ActorStorage } from '../lib/actorstorage.js'
12
- import { Authorizer } from '../lib/authorizer.js'
13
- import { ObjectCache } from '../lib/objectcache.js'
14
- import as2 from '../lib/activitystreams.js'
15
- import Logger from 'pino'
16
- import bots from './fixtures/bots.js'
17
- import { nockSetup, postInbox, makeActor, nockFormat } from './utils/nock.js'
18
- import { Digester } from '../lib/digester.js'
19
- import { HTTPSignature } from '../lib/httpsignature.js'
20
- import { runMigrations } from '../lib/migrations/index.js'
21
- import { BotContext } from '../lib/botcontext.js'
22
- import { Transformer } from '../lib/microsyntax.js'
23
-
24
- describe('ActivityHandler', () => {
25
- const domain = 'activitypubbot.example'
26
- const origin = `https://${domain}`
27
- let connection = null
28
- let botDataStorage = null
29
- let objectStorage = null
30
- let keyStorage = null
31
- let actorStorage = null
32
- let formatter = null
33
- let client = null
34
- let distributor = null
35
- let authz = null
36
- let cache = null
37
- let handler = null
38
- let logger = null
39
- let botId = null
40
- const botName = 'ok'
41
- let bot = null
42
- const loggerBotName = 'logging'
43
- let lb = null
44
- let lbId = null
45
- let transformer = null
46
- before(async () => {
47
- logger = Logger({ level: 'silent' })
48
- formatter = new UrlFormatter(origin)
49
- connection = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false })
50
- await connection.authenticate()
51
- await runMigrations(connection)
52
- botDataStorage = new BotDataStorage(connection)
53
- objectStorage = new ObjectStorage(connection)
54
- keyStorage = new KeyStorage(connection, logger)
55
- actorStorage = new ActorStorage(connection, formatter)
56
- const signer = new HTTPSignature(logger)
57
- const digester = new Digester(logger)
58
- client = new ActivityPubClient(keyStorage, formatter, signer, digester, logger)
59
- distributor = new ActivityDistributor(client, formatter, actorStorage, logger)
60
- authz = new Authorizer(actorStorage, formatter, client)
61
- cache = new ObjectCache({ longTTL: 3600 * 1000, shortTTL: 300 * 1000, maxItems: 1000 })
62
- transformer = new Transformer(`${origin}/tag/`, client)
63
- await Promise.all(
64
- Object.values(bots).map(bot => bot.initialize(
65
- new BotContext(
66
- bot.username,
67
- botDataStorage,
68
- objectStorage,
69
- actorStorage,
70
- client,
71
- distributor,
72
- formatter,
73
- transformer,
74
- logger
75
- )
76
- ))
77
- )
78
- botId = formatter.format({ username: botName })
79
- lbId = formatter.format({ username: loggerBotName })
80
- bot = bots[botName]
81
- lb = bots[loggerBotName]
82
- await objectStorage.create(await as2.import({
83
- id: formatter.format({ username: 'test1', type: 'object', nanoid: '_pEWsKke-7lACTdM3J_qd' }),
84
- type: 'Object',
85
- attributedTo: formatter.format({ username: 'test1' }),
86
- to: 'as:Public'
87
- }))
88
- nockSetup('social.example')
89
- nockSetup('third.example')
90
- })
91
- after(async () => {
92
- await connection.close()
93
- handler = null
94
- cache = null
95
- authz = null
96
- distributor = null
97
- client = null
98
- formatter = null
99
- actorStorage = null
100
- keyStorage = null
101
- botDataStorage = null
102
- objectStorage = null
103
- connection = null
104
- })
105
- beforeEach(async () => {
106
- Object.assign(postInbox, {})
107
- })
108
- it('can initialize', async () => {
109
- handler = new ActivityHandler(
110
- actorStorage,
111
- objectStorage,
112
- distributor,
113
- formatter,
114
- cache,
115
- authz,
116
- logger,
117
- client
118
- )
119
- assert.ok(handler)
120
- })
121
- it('can handle a create activity', async () => {
122
- const activity = await as2.import({
123
- type: 'Create',
124
- actor: nockFormat({ username: 'remote1' }),
125
- id: nockFormat({ username: 'remote1', type: 'create', num: 1 }),
126
- object: {
127
- id: 'https://social.example/user/remote1/note/1',
128
- type: 'Note',
129
- content: 'Hello, world!',
130
- to: 'as:Public'
131
- },
132
- to: 'as:Public'
133
- })
134
- await handler.handleActivity(bot, activity)
135
- const cached = await cache.get(activity.object?.first.id)
136
- assert.equal(cached.content, 'Hello, world!')
137
- })
138
- it('can handle a create activity with a reply', async () => {
139
- const oid = formatter.format({
140
- username: botName,
141
- type: 'note',
142
- nanoid: 'k5MtHI1aGle4RocLqnw7x'
143
- })
144
- const original = await as2.import({
145
- id: oid,
146
- type: 'Note',
147
- attributedTo: formatter.format({ username: botName }),
148
- to: 'as:Public',
149
- content: 'Original note'
150
- })
151
- await objectStorage.create(original)
152
- const activity = await as2.import({
153
- type: 'Create',
154
- actor: 'https://social.example/user/remote1',
155
- id: 'https://social.example/user/remote1/object/3',
156
- object: {
157
- inReplyTo: oid,
158
- id: 'https://social.example/user/remote1/object/4',
159
- type: 'Note',
160
- content: 'Reply note',
161
- to: 'as:Public'
162
- },
163
- to: 'as:Public'
164
- })
165
- const collection = await objectStorage.getCollection(oid, 'replies')
166
- assert.equal(collection.totalItems, 0)
167
- await handler.handleActivity(bot, activity)
168
- const collection2 = await objectStorage.getCollection(oid, 'replies')
169
- assert.equal(collection2.totalItems, 1)
170
- await handler.onIdle()
171
- assert.equal(postInbox.remote1, 1)
172
- assert.ok(true)
173
- })
174
- it('can handle an update activity', async () => {
175
- const activity = await as2.import({
176
- type: 'Update',
177
- actor: 'https://social.example/user/remote1',
178
- id: 'https://social.example/user/remote1/update/1',
179
- object: {
180
- id: 'https://social.example/user/remote1/note/1',
181
- type: 'Note',
182
- content: 'Hello, world! (updated)',
183
- to: 'as:Public'
184
- },
185
- to: 'as:Public'
186
- })
187
- await handler.handleActivity(bot, activity)
188
- const cached = await cache.get(activity.object?.first.id)
189
- assert.equal(cached.content, 'Hello, world! (updated)')
190
- })
191
- it('can handle a delete activity', async () => {
192
- const activity = await as2.import({
193
- type: 'Delete',
194
- actor: 'https://social.example/user/remote1',
195
- id: 'https://social.example/user/remote1/delete/1',
196
- object: 'https://social.example/user/remote1/note/1',
197
- to: 'as:Public'
198
- })
199
- await handler.handleActivity(bot, activity)
200
- const cached = await cache.get(activity.object?.first.id)
201
- assert.equal(cached, undefined)
202
- })
203
- it('can handle an add activity', async () => {
204
- const activity = await as2.import({
205
- type: 'Add',
206
- actor: 'https://social.example/user/remote1',
207
- id: 'https://social.example/user/remote1/add/1',
208
- object: {
209
- id: 'https://social.example/user/remote1/note/1',
210
- type: 'Note',
211
- attributedTo: 'https://social.example/user/remote1',
212
- to: 'as:Public'
213
- },
214
- target: {
215
- id: 'https://social.example/user/remote1/collection/1',
216
- type: 'Collection',
217
- attributedTo: 'https://social.example/user/remote1',
218
- to: 'as:Public'
219
- },
220
- to: 'as:Public'
221
- })
222
- await handler.handleActivity(bot, activity)
223
- const cached = await cache.get(activity.object?.first.id)
224
- assert.equal(cached.id, activity.object?.first.id)
225
- const cached2 = await cache.get(activity.target?.first.id)
226
- assert.equal(cached2.id, activity.target?.first.id)
227
- assert.equal(
228
- true,
229
- await cache.isMember(activity.target?.first, activity.object?.first)
230
- )
231
- })
232
-
233
- it('can handle a remove activity', async () => {
234
- const activity = await as2.import({
235
- type: 'Remove',
236
- actor: 'https://social.example/user/remote1',
237
- id: 'https://social.example/user/remote1/remove/1',
238
- object: {
239
- id: 'https://social.example/user/remote1/note/1',
240
- type: 'Note',
241
- attributedTo: 'https://social.example/user/remote1',
242
- to: 'as:Public'
243
- },
244
- target: {
245
- id: 'https://social.example/user/remote1/collection/1',
246
- type: 'Collection',
247
- attributedTo: 'https://social.example/user/remote1',
248
- to: 'as:Public'
249
- },
250
- to: 'as:Public'
251
- })
252
- await handler.handleActivity(bot, activity)
253
- const cached = await cache.get(activity.object?.first.id)
254
- assert.equal(cached.id, activity.object?.first.id)
255
- const cached2 = await cache.get(activity.target?.first.id)
256
- assert.equal(cached2.id, activity.target?.first.id)
257
- assert.equal(
258
- false,
259
- await cache.isMember(activity.target?.first, activity.object?.first)
260
- )
261
- })
262
- it('can handle a follow activity', async () => {
263
- const actor = await makeActor('follower1')
264
- assert.equal(
265
- false,
266
- await actorStorage.isInCollection(botName, 'followers', actor))
267
- const activity = await as2.import({
268
- type: 'Follow',
269
- id: 'https://social.example/user/follower1/follow/1',
270
- actor: actor.id,
271
- object: botId,
272
- to: botId
273
- })
274
- await handler.handleActivity(bot, activity)
275
- assert.equal(
276
- true,
277
- await actorStorage.isInCollection(botName, 'followers', actor))
278
- await handler.onIdle()
279
- // accept and add
280
- assert.equal(postInbox.follower1, 2)
281
- })
282
- it('can handle a duplicate follow activity', async () => {
283
- const actor = await makeActor('follower2')
284
- await actorStorage.addToCollection(botName, 'followers', actor)
285
- const activity = await as2.import({
286
- type: 'Follow',
287
- id: 'https://social.example/user/follower2/follow/2',
288
- actor: actor.id,
289
- object: botId,
290
- to: botId
291
- })
292
- await handler.handleActivity(bot, activity)
293
- assert.equal(
294
- true,
295
- await actorStorage.isInCollection(botName, 'followers', actor))
296
- await handler.onIdle()
297
- assert.ok(!postInbox.follower2)
298
- })
299
- it('can handle a follow from a blocked account', async () => {
300
- const actor = await makeActor('follower3')
301
- await actorStorage.addToCollection(botName, 'blocked', actor)
302
- assert.strictEqual(
303
- false,
304
- await actorStorage.isInCollection(botName, 'followers', actor)
305
- )
306
- assert.strictEqual(
307
- true,
308
- await actorStorage.isInCollection(botName, 'blocked', actor)
309
- )
310
- const activity = await as2.import({
311
- type: 'Follow',
312
- id: 'https://social.example/user/follower3/follow/1',
313
- actor: actor.id,
314
- object: botId,
315
- to: botId
316
- })
317
- await handler.handleActivity(bot, activity)
318
- assert.equal(
319
- false,
320
- await actorStorage.isInCollection(botName, 'followers', actor))
321
- await handler.onIdle()
322
- assert.ok(!postInbox.follower3)
323
- })
324
- it('notifies the bot of a follow activity', async () => {
325
- const actor = await makeActor('follower4')
326
- const activity = await as2.import({
327
- type: 'Follow',
328
- id: 'https://social.example/user/follower4/follow/1',
329
- actor: actor.id,
330
- object: lbId,
331
- to: lbId
332
- })
333
- await handler.handleActivity(lb, activity)
334
- assert.ok(lb.follows.has(activity.id))
335
- await handler.onIdle()
336
- })
337
- it('can handle an accept activity', async () => {
338
- const actor = await makeActor('accepter1')
339
- const followActivity = await as2.import({
340
- type: 'Follow',
341
- id: 'https://activitypubbot.example/user/ok/follow/1',
342
- actor: botId,
343
- object: actor.id,
344
- to: actor.id
345
- })
346
- await objectStorage.create(followActivity)
347
- await actorStorage.addToCollection(botName, 'pendingFollowing', followActivity)
348
- assert.equal(
349
- false,
350
- await actorStorage.isInCollection(botName, 'following', actor))
351
- const activity = await as2.import({
352
- type: 'Accept',
353
- id: 'https://social.example/user/remote1/accept/1',
354
- actor: actor.id,
355
- object: followActivity.id,
356
- to: botId
357
- })
358
- await handler.handleActivity(bot, activity)
359
- assert.equal(
360
- true,
361
- await actorStorage.isInCollection(botName, 'following', actor))
362
- assert.equal(
363
- false,
364
- await actorStorage.isInCollection(botName, 'pendingFollowing', followActivity))
365
- })
366
- it('can ignore an accept activity for a non-existing follow', async () => {
367
- const actor = await makeActor('accepter2')
368
- const activity = await as2.import({
369
- type: 'Accept',
370
- id: 'https://social.example/user/accepter2/accept/1',
371
- actor: actor.id,
372
- object: 'https://activitypubbot.example/user/ok/follow/69',
373
- to: botId
374
- })
375
- await handler.handleActivity(bot, activity)
376
- assert.equal(
377
- false,
378
- await actorStorage.isInCollection(botName, 'following', actor))
379
- })
380
- it('can ignore an accept activity from a blocked account', async () => {
381
- const actor = await makeActor('accepter3')
382
- const followActivity = await as2.import({
383
- type: 'Follow',
384
- id: 'https://activitypubbot.example/user/ok/follow/3',
385
- actor: botId,
386
- object: actor.id,
387
- to: actor.id
388
- })
389
- await objectStorage.create(followActivity)
390
- await actorStorage.addToCollection(botName, 'pendingFollowing', followActivity)
391
- await actorStorage.addToCollection(botName, 'blocked', actor)
392
- const activity = await as2.import({
393
- type: 'Accept',
394
- id: 'https://social.example/user/accepter3/accept/1',
395
- actor: actor.id,
396
- object: followActivity.id,
397
- to: botId
398
- })
399
- await handler.handleActivity(bot, activity)
400
- assert.equal(
401
- false,
402
- await actorStorage.isInCollection(botName, 'following', actor))
403
- })
404
-
405
- it('can ignore an accept activity for a remote follow activity', async () => {
406
- const actor = await makeActor('accepter4')
407
- const activity = await as2.import({
408
- type: 'Accept',
409
- id: 'https://social.example/user/accepter3/accept/1',
410
- actor: actor.id,
411
- object: {
412
- type: 'Follow',
413
- id: 'https://third.example/user/other/follow/3',
414
- actor: 'https://third.example/user/other',
415
- object: actor.id,
416
- to: [actor.id, 'as:Public']
417
- },
418
- to: ['https://third.example/user/other', 'as:Public']
419
- })
420
- await handler.handleActivity(bot, activity)
421
- assert.equal(
422
- false,
423
- await actorStorage.isInCollection(botName, 'following', actor))
424
- })
425
- it('can ignore an accept activity for a follow of a different actor', async () => {
426
- const actor5 = await makeActor('accepter5')
427
- const actor6 = await makeActor('accepter6')
428
- const followActivity = await as2.import({
429
- type: 'Follow',
430
- id: 'https://activitypubbot.example/user/ok/follow/6',
431
- actor: botId,
432
- object: actor6.id,
433
- to: [actor6.id, 'as:Public']
434
- })
435
- await objectStorage.create(followActivity)
436
- await actorStorage.addToCollection(botName, 'pendingFollowing', followActivity)
437
- assert.equal(
438
- false,
439
- await actorStorage.isInCollection(botName, 'following', actor5))
440
- const activity = await as2.import({
441
- type: 'Accept',
442
- id: 'https://social.example/user/remote1/accept/1',
443
- actor: actor5.id,
444
- object: followActivity.id,
445
- to: [botId, 'as:Public']
446
- })
447
- await handler.handleActivity(bot, activity)
448
- assert.equal(
449
- false,
450
- await actorStorage.isInCollection(botName, 'following', actor5))
451
- assert.equal(
452
- false,
453
- await actorStorage.isInCollection(botName, 'following', actor6))
454
- assert.equal(
455
- true,
456
- await actorStorage.isInCollection(botName, 'pendingFollowing', followActivity))
457
- })
458
- it('can ignore an accept activity for a follow by a different actor', async () => {
459
- const actor7 = await makeActor('accepter7')
460
- const followActivity = await as2.import({
461
- type: 'Follow',
462
- id: 'https://activitypubbot.example/user/calculon/follow/7',
463
- actor: 'https://activitypubbot.example/user/calculon',
464
- object: actor7.id,
465
- to: [actor7.id, 'as:Public']
466
- })
467
- await objectStorage.create(followActivity)
468
- await actorStorage.addToCollection('calculon', 'pendingFollowing', followActivity)
469
- assert.equal(
470
- false,
471
- await actorStorage.isInCollection(botName, 'following', actor7))
472
- const activity = await as2.import({
473
- type: 'Accept',
474
- id: 'https://social.example/user/accepter7/accept/7',
475
- actor: actor7.id,
476
- object: followActivity.id,
477
- to: [botId, 'as:Public']
478
- })
479
- await handler.handleActivity(bot, activity)
480
- assert.equal(
481
- false,
482
- await actorStorage.isInCollection(botName, 'following', actor7))
483
- assert.equal(
484
- false,
485
- await actorStorage.isInCollection('calculon', 'following', actor7))
486
- assert.equal(
487
- true,
488
- await actorStorage.isInCollection('calculon', 'pendingFollowing', followActivity))
489
- })
490
- it('can handle an reject activity', async () => {
491
- const actor = await makeActor('rejecter1')
492
- const followActivity = await as2.import({
493
- type: 'Follow',
494
- id: 'https://activitypubbot.example/user/ok/follow/101',
495
- actor: botId,
496
- object: actor.id,
497
- to: actor.id
498
- })
499
- await objectStorage.create(followActivity)
500
- await actorStorage.addToCollection(botName, 'pendingFollowing', followActivity)
501
- assert.equal(
502
- false,
503
- await actorStorage.isInCollection(botName, 'following', actor))
504
- const activity = await as2.import({
505
- type: 'Reject',
506
- id: 'https://social.example/user/rejecter1/reject/1',
507
- actor: actor.id,
508
- object: followActivity.id,
509
- to: botId
510
- })
511
- await handler.handleActivity(bot, activity)
512
- assert.equal(
513
- false,
514
- await actorStorage.isInCollection(botName, 'following', actor))
515
- assert.equal(
516
- false,
517
- await actorStorage.isInCollection(botName, 'pendingFollowing', followActivity))
518
- })
519
- it('can ignore an reject activity for a non-existing follow', async () => {
520
- const actor = await makeActor('rejecter2')
521
- const activity = await as2.import({
522
- type: 'Reject',
523
- id: 'https://social.example/user/rejecter2/reject/1',
524
- actor: actor.id,
525
- object: 'https://activitypubbot.example/user/ok/follow/69',
526
- to: botId
527
- })
528
- await handler.handleActivity(bot, activity)
529
- assert.equal(
530
- false,
531
- await actorStorage.isInCollection(botName, 'following', actor))
532
- })
533
- it('can ignore an reject activity from a blocked account', async () => {
534
- const actor = await makeActor('rejecter3')
535
- const followActivity = await as2.import({
536
- type: 'Follow',
537
- id: 'https://activitypubbot.example/user/ok/follow/103',
538
- actor: botId,
539
- object: actor.id,
540
- to: actor.id
541
- })
542
- await objectStorage.create(followActivity)
543
- await actorStorage.addToCollection(botName, 'pendingFollowing', followActivity)
544
- await actorStorage.addToCollection(botName, 'blocked', actor)
545
- const activity = await as2.import({
546
- type: 'Reject',
547
- id: 'https://social.example/user/rejecter3/reject/1',
548
- actor: actor.id,
549
- object: followActivity.id,
550
- to: botId
551
- })
552
- await handler.handleActivity(bot, activity)
553
- assert.equal(
554
- false,
555
- await actorStorage.isInCollection(botName, 'following', actor))
556
- })
557
-
558
- it('can ignore an reject activity for a remote follow activity', async () => {
559
- const actor = await makeActor('rejecter4')
560
- const activity = await as2.import({
561
- type: 'Reject',
562
- id: 'https://social.example/user/rejecter4/reject/1',
563
- actor: actor.id,
564
- object: {
565
- type: 'Follow',
566
- id: 'https://third.example/user/other/follow/103',
567
- actor: 'https://third.example/user/other',
568
- object: actor.id,
569
- to: [actor.id, 'as:Public']
570
- },
571
- to: ['https://third.example/user/other', 'as:Public']
572
- })
573
- await handler.handleActivity(bot, activity)
574
- assert.equal(
575
- false,
576
- await actorStorage.isInCollection(botName, 'following', actor))
577
- })
578
- it('can ignore an reject activity for a follow of a different actor', async () => {
579
- const actor5 = await makeActor('rejecter5')
580
- const actor6 = await makeActor('rejecter6')
581
- const followActivity = await as2.import({
582
- type: 'Follow',
583
- id: 'https://activitypubbot.example/user/ok/follow/106',
584
- actor: botId,
585
- object: actor6.id,
586
- to: [actor6.id, 'as:Public']
587
- })
588
- await objectStorage.create(followActivity)
589
- await actorStorage.addToCollection(botName, 'pendingFollowing', followActivity)
590
- assert.equal(
591
- false,
592
- await actorStorage.isInCollection(botName, 'following', actor5))
593
- const activity = await as2.import({
594
- type: 'Reject',
595
- id: 'https://social.example/user/rejecter5/reject/1',
596
- actor: actor5.id,
597
- object: followActivity.id,
598
- to: [botId, 'as:Public']
599
- })
600
- await handler.handleActivity(bot, activity)
601
- assert.equal(
602
- false,
603
- await actorStorage.isInCollection(botName, 'following', actor5))
604
- assert.equal(
605
- false,
606
- await actorStorage.isInCollection(botName, 'following', actor6))
607
- assert.equal(
608
- true,
609
- await actorStorage.isInCollection(botName, 'pendingFollowing', followActivity))
610
- })
611
- it('can ignore an reject activity for a follow by a different actor', async () => {
612
- const actor7 = await makeActor('rejecter7')
613
- const followActivity = await as2.import({
614
- type: 'Follow',
615
- id: 'https://activitypubbot.example/user/calculon/follow/107',
616
- actor: 'https://activitypubbot.example/user/calculon',
617
- object: actor7.id,
618
- to: [actor7.id, 'as:Public']
619
- })
620
- await objectStorage.create(followActivity)
621
- await actorStorage.addToCollection('calculon', 'pendingFollowing', followActivity)
622
- assert.equal(
623
- false,
624
- await actorStorage.isInCollection(botName, 'following', actor7))
625
- const activity = await as2.import({
626
- type: 'Reject',
627
- id: 'https://social.example/user/rejecter7/reject/7',
628
- actor: actor7.id,
629
- object: followActivity.id,
630
- to: [botId, 'as:Public']
631
- })
632
- await handler.handleActivity(bot, activity)
633
- assert.equal(
634
- false,
635
- await actorStorage.isInCollection(botName, 'following', actor7))
636
- assert.equal(
637
- false,
638
- await actorStorage.isInCollection('calculon', 'following', actor7))
639
- assert.equal(
640
- true,
641
- await actorStorage.isInCollection('calculon', 'pendingFollowing', followActivity))
642
- })
643
- it('can handle a like activity', async () => {
644
- const actor = await makeActor('liker1')
645
- const note = await as2.import({
646
- attributedTo: botId,
647
- id: formatter.format({
648
- username: botName,
649
- type: 'note',
650
- nanoid: '_SivlqjrNpdV3KOJ6cC3L'
651
- }),
652
- type: 'Note',
653
- content: 'Hello, world!',
654
- to: 'as:Public'
655
- })
656
- await objectStorage.create(note)
657
- const activity = await as2.import({
658
- type: 'Like',
659
- actor: actor.id,
660
- id: 'https://social.example/user/liker1/like/1',
661
- object: note.id,
662
- to: [botId, 'as:Public']
663
- })
664
- await handler.handleActivity(bot, activity)
665
- assert.strictEqual(
666
- true,
667
- await objectStorage.isInCollection(note.id, 'likes', activity)
668
- )
669
- await handler.onIdle()
670
- assert.equal(postInbox.liker1, 1)
671
- })
672
- it('can ignore a like activity for a remote object', async () => {
673
- const actor = await makeActor('liker2')
674
- const objectId = 'https://third.example/user/other/note/1'
675
- const activity = await as2.import({
676
- type: 'Like',
677
- actor: actor.id,
678
- id: 'https://social.example/user/liker2/like/1',
679
- object: objectId,
680
- to: [botId, 'as:Public']
681
- })
682
- await handler.handleActivity(bot, activity)
683
- assert.strictEqual(
684
- false,
685
- await objectStorage.isInCollection(objectId, 'likes', activity)
686
- )
687
- })
688
- it('can ignore a like activity for a non-existing object', async () => {
689
- const actor = await makeActor('liker3')
690
- const activity = await as2.import({
691
- type: 'Like',
692
- actor: actor.id,
693
- id: 'https://social.example/user/liker3/like/1',
694
- object: 'https://activitypubbot.example/user/ok/note/doesnotexist',
695
- to: [botId, 'as:Public']
696
- })
697
- await handler.handleActivity(bot, activity)
698
- assert.strictEqual(
699
- false,
700
- await objectStorage.isInCollection(activity.object?.first.id, 'likes', activity)
701
- )
702
- })
703
- it('can ignore a like activity from a blocked account', async () => {
704
- const actor = await makeActor('liker4')
705
- const note = await as2.import({
706
- attributedTo: botId,
707
- id: formatter.format({
708
- username: botName,
709
- type: 'note',
710
- nanoid: 'wpOmBSs04osbTtYoR9C8p'
711
- }),
712
- type: 'Note',
713
- content: 'Hello, world!',
714
- to: 'as:Public'
715
- })
716
- await objectStorage.create(note)
717
- await actorStorage.addToCollection(botName, 'blocked', actor)
718
- const activity = await as2.import({
719
- type: 'Like',
720
- actor: actor.id,
721
- id: 'https://social.example/user/liker4/like/1',
722
- object: note.id,
723
- to: [botId, 'as:Public']
724
- })
725
- await handler.handleActivity(bot, activity)
726
- assert.strictEqual(
727
- false,
728
- await actorStorage.isInCollection(note.id, 'likes', activity)
729
- )
730
- })
731
- it('can ignore a like activity for an unreadable object', async () => {
732
- const actor = await makeActor('liker5')
733
- const note = await as2.import({
734
- attributedTo: botId,
735
- id: formatter.format({
736
- username: botName,
737
- type: 'note',
738
- nanoid: '9FZgbPv3G6MYKGPir0eI6'
739
- }),
740
- type: 'Note',
741
- content: 'Private note @other',
742
- to: [formatter.format({ username: 'other' })],
743
- tags: [{ type: 'Mention', href: formatter.format({ username: 'other' }) }]
744
- })
745
- await objectStorage.create(note)
746
- const activity = await as2.import({
747
- type: 'Like',
748
- actor: actor.id,
749
- id: 'https://social.example/user/liker4/like/1',
750
- object: note.id,
751
- to: [botId, 'as:Public']
752
- })
753
- await handler.handleActivity(bot, activity)
754
- assert.strictEqual(
755
- false,
756
- await objectStorage.isInCollection(note.id, 'likes', activity)
757
- )
758
- })
759
- it('can ignore a like activity for an object by a different actor', async () => {
760
- const actor = await makeActor('liker6')
761
- const note = await as2.import({
762
- attributedTo: formatter.format({ username: 'other' }),
763
- id: formatter.format({
764
- username: botName,
765
- type: 'note',
766
- nanoid: 'p8YbioA43kgZR41N3-tb2'
767
- }),
768
- type: 'Note',
769
- content: 'Public note',
770
- to: ['as:Public']
771
- })
772
- await objectStorage.create(note)
773
- const activity = await as2.import({
774
- type: 'Like',
775
- actor: actor.id,
776
- id: 'https://social.example/user/liker6/like/1',
777
- object: note.id,
778
- to: [botId, 'as:Public']
779
- })
780
- await handler.handleActivity(bot, activity)
781
- assert.strictEqual(
782
- false,
783
- await objectStorage.isInCollection(note.id, 'likes', activity)
784
- )
785
- })
786
- it('can ignore a duplicate like activity', async () => {
787
- const actor = await makeActor('liker7')
788
- const note = await as2.import({
789
- attributedTo: botId,
790
- id: formatter.format({
791
- username: botName,
792
- type: 'note',
793
- nanoid: 'TyCJRI4aMmW2KWtDZSCVM'
794
- }),
795
- type: 'Note',
796
- content: 'Public note',
797
- to: ['as:Public']
798
- })
799
- await objectStorage.create(note)
800
- const activity = await as2.import({
801
- type: 'Like',
802
- actor: actor.id,
803
- id: 'https://social.example/user/liker7/like/1',
804
- object: note.id,
805
- to: [botId, 'as:Public']
806
- })
807
- await handler.handleActivity(bot, activity)
808
- await handler.handleActivity(bot, activity)
809
- assert.strictEqual(
810
- true,
811
- await objectStorage.isInCollection(note.id, 'likes', activity)
812
- )
813
- })
814
- it('can ignore a like activity by an actor that has liked before', async () => {
815
- const actor = await makeActor('liker8')
816
- const note = await as2.import({
817
- attributedTo: botId,
818
- id: formatter.format({
819
- username: botName,
820
- type: 'note',
821
- nanoid: '49s-F59oxQ6dX4SFiCqNg'
822
- }),
823
- type: 'Note',
824
- content: 'Public note',
825
- to: [formatter.format({ username: 'other' })]
826
- })
827
- await objectStorage.create(note)
828
- const activity1 = await as2.import({
829
- type: 'Like',
830
- actor: actor.id,
831
- id: 'https://social.example/user/liker8/like/1',
832
- object: note.id,
833
- to: [botId, 'as:Public']
834
- })
835
- const activity2 = await as2.import({
836
- type: 'Like',
837
- actor: actor.id,
838
- id: 'https://social.example/user/liker8/like/2',
839
- object: note.id,
840
- to: [botId, 'as:Public']
841
- })
842
- await handler.handleActivity(bot, activity1)
843
- await handler.handleActivity(bot, activity2)
844
- assert.strictEqual(
845
- false,
846
- await objectStorage.isInCollection(note.id, 'likes', activity2)
847
- )
848
- })
849
- it('notifies the bot of a like activity', async () => {
850
- const actor = await makeActor('liker9')
851
- const note = await as2.import({
852
- attributedTo: lbId,
853
- id: formatter.format({
854
- username: loggerBotName,
855
- type: 'note',
856
- nanoid: 'IGeAucyHD-s3Ywg9X9sCo'
857
- }),
858
- type: 'Note',
859
- content: 'Hello, world!',
860
- to: 'as:Public'
861
- })
862
- await objectStorage.create(note)
863
- const activity = await as2.import({
864
- type: 'Like',
865
- actor: actor.id,
866
- id: nockFormat({
867
- username: 'liker9',
868
- type: 'Like',
869
- nanoid: '3fKK6LcMtqrAp1Ekn471u'
870
- }),
871
- object: note.id,
872
- to: [lbId, 'as:Public']
873
- })
874
- await handler.handleActivity(lb, activity)
875
- assert.ok(lb.likes.has(activity.id))
876
- })
877
- it('can handle an announce activity', async () => {
878
- const actor = await makeActor('announcer1')
879
- const note = await as2.import({
880
- attributedTo: botId,
881
- id: formatter.format({
882
- username: botName,
883
- type: 'note',
884
- nanoid: 'odQN6GR4v71ZxN1wsstvl'
885
- }),
886
- type: 'Note',
887
- content: 'Hello, world!',
888
- to: 'as:Public'
889
- })
890
- await objectStorage.create(note)
891
- const activity = await as2.import({
892
- type: 'Announce',
893
- actor: actor.id,
894
- id: 'https://social.example/user/announcer1/announce/1',
895
- object: note.id,
896
- to: [botId, 'as:Public']
897
- })
898
- await handler.handleActivity(bot, activity)
899
- assert.strictEqual(
900
- true,
901
- await objectStorage.isInCollection(note.id, 'shares', activity)
902
- )
903
- await handler.onIdle()
904
- assert.equal(postInbox.announcer1, 1)
905
- })
906
- it('can ignore an announce activity for a remote object', async () => {
907
- const actor = await makeActor('announcer2')
908
- const objectId = 'https://third.example/user/other/note/1'
909
- const activity = await as2.import({
910
- type: 'Announce',
911
- actor: actor.id,
912
- id: 'https://social.example/user/announcer2/announce/1',
913
- object: objectId,
914
- to: [botId, 'as:Public']
915
- })
916
- await handler.handleActivity(bot, activity)
917
- assert.strictEqual(
918
- false,
919
- await objectStorage.isInCollection(objectId, 'shares', activity)
920
- )
921
- })
922
- it('can ignore an announce activity for a non-existing object', async () => {
923
- const actor = await makeActor('announcer3')
924
- const activity = await as2.import({
925
- type: 'Announce',
926
- actor: actor.id,
927
- id: 'https://social.example/user/announcer3/announce/1',
928
- object: 'https://activitypubbot.example/user/ok/note/doesnotexist',
929
- to: [botId, 'as:Public']
930
- })
931
- await handler.handleActivity(bot, activity)
932
- assert.strictEqual(
933
- false,
934
- await objectStorage.isInCollection(activity.object?.first.id, 'shares', activity)
935
- )
936
- })
937
- it('can ignore an announce activity from a blocked account', async () => {
938
- const actor = await makeActor('announcer4')
939
- const note = await as2.import({
940
- attributedTo: botId,
941
- id: formatter.format({
942
- username: botName,
943
- type: 'note',
944
- nanoid: 'GMvbLj8rKzbtx1kvjCGUm'
945
- }),
946
- type: 'Note',
947
- content: 'Hello, world!',
948
- to: 'as:Public'
949
- })
950
- await objectStorage.create(note)
951
- await actorStorage.addToCollection(botName, 'blocked', actor)
952
- const activity = await as2.import({
953
- type: 'Announce',
954
- actor: actor.id,
955
- id: 'https://social.example/user/announcer4/announce/1',
956
- object: note.id,
957
- to: [botId, 'as:Public']
958
- })
959
- await handler.handleActivity(bot, activity)
960
- assert.strictEqual(
961
- false,
962
- await actorStorage.isInCollection(note.id, 'shares', activity)
963
- )
964
- })
965
- it('can ignore an announce activity for an unreadable object', async () => {
966
- const actor = await makeActor('announcer5')
967
- const note = await as2.import({
968
- attributedTo: botId,
969
- id: formatter.format({
970
- username: botName,
971
- type: 'note',
972
- nanoid: 'yWyHTZH9VtAA1ViEl7sil'
973
- }),
974
- type: 'Note',
975
- content: 'Private note @other',
976
- to: [formatter.format({ username: 'other' })],
977
- tags: [{ type: 'Mention', href: formatter.format({ username: 'other' }) }]
978
- })
979
- await objectStorage.create(note)
980
- const activity = await as2.import({
981
- type: 'Announce',
982
- actor: actor.id,
983
- id: 'https://social.example/user/announcer4/announce/1',
984
- object: note.id,
985
- to: [botId, 'as:Public']
986
- })
987
- await handler.handleActivity(bot, activity)
988
- assert.strictEqual(
989
- false,
990
- await objectStorage.isInCollection(note.id, 'shares', activity)
991
- )
992
- })
993
- it('can ignore an announce activity for an object by a different actor', async () => {
994
- const actor = await makeActor('announcer6')
995
- const note = await as2.import({
996
- attributedTo: formatter.format({ username: 'other' }),
997
- id: formatter.format({
998
- username: botName,
999
- type: 'note',
1000
- nanoid: 'CoI4vcLRjG7f9Sj9yK-6g'
1001
- }),
1002
- type: 'Note',
1003
- content: 'Public note',
1004
- to: ['as:Public']
1005
- })
1006
- await objectStorage.create(note)
1007
- const activity = await as2.import({
1008
- type: 'Announce',
1009
- actor: actor.id,
1010
- id: 'https://social.example/user/announcer6/announce/1',
1011
- object: note.id,
1012
- to: [botId, 'as:Public']
1013
- })
1014
- await handler.handleActivity(bot, activity)
1015
- assert.strictEqual(
1016
- false,
1017
- await objectStorage.isInCollection(note.id, 'shares', activity)
1018
- )
1019
- })
1020
- it('can ignore a duplicate announce activity', async () => {
1021
- const actor = await makeActor('announcer7')
1022
- const note = await as2.import({
1023
- attributedTo: botId,
1024
- id: formatter.format({
1025
- username: botName,
1026
- type: 'note',
1027
- nanoid: 'ndzHHtejBL83v3iiqsl4L'
1028
- }),
1029
- type: 'Note',
1030
- content: 'Public note',
1031
- to: ['as:Public']
1032
- })
1033
- await objectStorage.create(note)
1034
- const activity = await as2.import({
1035
- type: 'Announce',
1036
- actor: actor.id,
1037
- id: 'https://social.example/user/announcer7/announce/1',
1038
- object: note.id,
1039
- to: [botId, 'as:Public']
1040
- })
1041
- await handler.handleActivity(bot, activity)
1042
- await handler.handleActivity(bot, activity)
1043
- assert.strictEqual(
1044
- true,
1045
- await objectStorage.isInCollection(note.id, 'shares', activity)
1046
- )
1047
- })
1048
- it('can ignore an announce activity by an actor that has shared before', async () => {
1049
- const actor = await makeActor('announcer8')
1050
- const note = await as2.import({
1051
- attributedTo: botId,
1052
- id: formatter.format({
1053
- username: botName,
1054
- type: 'note',
1055
- nanoid: '7AAsKT9oKqM3PnXELNYB7'
1056
- }),
1057
- type: 'Note',
1058
- content: 'Public note',
1059
- to: [formatter.format({ username: 'other' })]
1060
- })
1061
- await objectStorage.create(note)
1062
- const activity1 = await as2.import({
1063
- type: 'Announce',
1064
- actor: actor.id,
1065
- id: 'https://social.example/user/announcer8/announce/1',
1066
- object: note.id,
1067
- to: [botId, 'as:Public']
1068
- })
1069
- const activity2 = await as2.import({
1070
- type: 'Announce',
1071
- actor: actor.id,
1072
- id: 'https://social.example/user/announcer8/announce/2',
1073
- object: note.id,
1074
- to: [botId, 'as:Public']
1075
- })
1076
- await handler.handleActivity(bot, activity1)
1077
- await handler.handleActivity(bot, activity2)
1078
- assert.strictEqual(
1079
- false,
1080
- await objectStorage.isInCollection(note.id, 'shares', activity2)
1081
- )
1082
- })
1083
- it('notifies the bot on an announce activity', async () => {
1084
- const actor = await makeActor('announcer9')
1085
- const note = await as2.import({
1086
- attributedTo: lbId,
1087
- id: formatter.format({
1088
- username: loggerBotName,
1089
- type: 'note',
1090
- nanoid: 'LNCVgovrjpA6oSKnGDax2'
1091
- }),
1092
- type: 'Note',
1093
- content: 'Hello, world!',
1094
- to: 'as:Public'
1095
- })
1096
- await objectStorage.create(note)
1097
- const activity = await as2.import({
1098
- type: 'Announce',
1099
- actor: actor.id,
1100
- id: nockFormat({
1101
- username: 'announcer9',
1102
- type: 'Announce',
1103
- nanoid: 'LmVvlEBHNf2X6nfgzMe6F'
1104
- }),
1105
- object: note.id,
1106
- to: [lbId, 'as:Public']
1107
- })
1108
- await handler.handleActivity(lb, activity)
1109
- assert.ok(lb.shares.has(activity.id))
1110
- })
1111
- it('can handle a block activity', async () => {
1112
- const actor = await makeActor('blocker1')
1113
- await actorStorage.addToCollection(botName, 'followers', actor)
1114
- await actorStorage.addToCollection(botName, 'following', actor)
1115
- const activity = await as2.import({
1116
- type: 'Block',
1117
- id: 'https://social.example/user/blocker1/block/1',
1118
- actor: actor.id,
1119
- object: botId,
1120
- to: botId
1121
- })
1122
- await handler.handleActivity(bot, activity)
1123
- assert.equal(
1124
- false,
1125
- await actorStorage.isInCollection(botName, 'followers', actor))
1126
- assert.equal(
1127
- false,
1128
- await actorStorage.isInCollection(botName, 'following', actor))
1129
- })
1130
- it('can handle a block activity for a pending user', async () => {
1131
- const actor = await makeActor('blocker2')
1132
- await actorStorage.addToCollection(botName, 'pendingFollowing', actor)
1133
- const activity = await as2.import({
1134
- type: 'Block',
1135
- id: 'https://social.example/user/blocker2/block/1',
1136
- actor: actor.id,
1137
- object: botId,
1138
- to: botId
1139
- })
1140
- await handler.handleActivity(bot, activity)
1141
- assert.equal(
1142
- false,
1143
- await actorStorage.isInCollection(botName, 'pendingFollowing', actor))
1144
- })
1145
- it('can handle a flag activity for an actor', async () => {
1146
- const actor = await makeActor('flagger1')
1147
- const activity = await as2.import({
1148
- type: 'Flag',
1149
- actor: actor.id,
1150
- id: 'https://social.example/user/flagger1/flag/1',
1151
- object: botId,
1152
- to: [botId, formatter.format({ server: true })]
1153
- })
1154
- await handler.handleActivity(bot, activity)
1155
- })
1156
- it('can handle a flag activity for an object', async () => {
1157
- const actor = await makeActor('flagger2')
1158
- const note = await as2.import({
1159
- attributedTo: botId,
1160
- id: formatter.format({
1161
- username: botName,
1162
- type: 'note',
1163
- nanoid: 'h3q3QZy2BzYwX7a4vJ5v3'
1164
- }),
1165
- type: 'Note',
1166
- content: 'Hello, world!',
1167
- to: 'as:Public'
1168
- })
1169
- await objectStorage.create(note)
1170
- const activity = await as2.import({
1171
- type: 'Flag',
1172
- actor: actor.id,
1173
- id: 'https://social.example/user/flagger2/flag/1',
1174
- object: note.id,
1175
- to: [botId, 'as:Public']
1176
- })
1177
- await handler.handleActivity(bot, activity)
1178
- })
1179
- it('can handle an undo for an unrecognized activity type', async () => {
1180
- const actor = await makeActor('undoer1')
1181
- const activity = await as2.import({
1182
- '@context': [
1183
- 'https://www.w3.org/ns/activitystreams',
1184
- {
1185
- ex: 'https://example.com/ns/',
1186
- Foo: {
1187
- '@id': 'ex:Foo',
1188
- '@type': '@id'
1189
- }
1190
- }
1191
- ],
1192
- type: 'Undo',
1193
- actor: actor.id,
1194
- id: 'https://social.example/user/undoer1/undo/1',
1195
- object: {
1196
- type: 'Foo',
1197
- id: 'https://social.example/user/undoer1/foo/1'
1198
- },
1199
- to: botId
1200
- })
1201
- await handler.handleActivity(bot, activity)
1202
- })
1203
- it('can handle an undo for a like activity', async () => {
1204
- const actor = await makeActor('undoer2')
1205
- const note = await as2.import({
1206
- attributedTo: botId,
1207
- id: formatter.format({
1208
- username: botName,
1209
- type: 'note',
1210
- nanoid: 'aQ8TL9jHhudjiQSqE8tYN'
1211
- }),
1212
- type: 'Note',
1213
- content: 'Hello, world!',
1214
- to: 'as:Public'
1215
- })
1216
- await objectStorage.create(note)
1217
- const activity = await as2.import({
1218
- type: 'Like',
1219
- actor: actor.id,
1220
- id: 'https://social.example/user/undoer2/like/1',
1221
- object: note.id,
1222
- to: [botId, 'as:Public']
1223
- })
1224
- await handler.handleActivity(bot, activity)
1225
- assert.strictEqual(
1226
- true,
1227
- await objectStorage.isInCollection(note.id, 'likes', activity)
1228
- )
1229
- const undoActivity = await as2.import({
1230
- type: 'Undo',
1231
- actor: actor.id,
1232
- id: 'https://social.example/user/undoer2/undo/1',
1233
- object: {
1234
- type: 'Like',
1235
- id: activity.id,
1236
- actor: actor.id,
1237
- object: note.id,
1238
- to: [botId, 'as:Public']
1239
- },
1240
- to: [botId, 'as:Public']
1241
- })
1242
- await handler.handleActivity(bot, undoActivity)
1243
- assert.strictEqual(
1244
- false,
1245
- await objectStorage.isInCollection(note.id, 'likes', activity)
1246
- )
1247
- })
1248
- it('can ignore an undo for a like activity with a different actor', async () => {
1249
- const note = await as2.import({
1250
- attributedTo: botId,
1251
- id: formatter.format({
1252
- username: botName,
1253
- type: 'note',
1254
- nanoid: 'elgLDhn0kty204Tk8rcMD'
1255
- }),
1256
- type: 'Note',
1257
- content: 'Hello, world!',
1258
- to: 'as:Public'
1259
- })
1260
- await objectStorage.create(note)
1261
- const liker = await makeActor('liker9', 'third.example')
1262
- const likeActivity = await as2.import({
1263
- type: 'Like',
1264
- actor: liker.id,
1265
- id: nockFormat({ domain: 'third.example', username: 'liker9', type: 'like', num: 1, obj: note.id }),
1266
- object: note.id,
1267
- to: [botId, 'as:Public']
1268
- })
1269
- await handler.handleActivity(bot, likeActivity)
1270
- assert.strictEqual(
1271
- true,
1272
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1273
- )
1274
- const undoer = await makeActor('undoer3', 'social.example')
1275
- const undoActivity = await as2.import({
1276
- type: 'Undo',
1277
- actor: undoer.id,
1278
- id: nockFormat({ domain: 'social.example', username: 'undoer3', type: 'undo', num: 1, obj: likeActivity.id }),
1279
- object: {
1280
- type: 'Like',
1281
- id: likeActivity.id
1282
- },
1283
- to: [botId, 'as:Public']
1284
- })
1285
- await handler.handleActivity(bot, undoActivity)
1286
- assert.strictEqual(
1287
- true,
1288
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1289
- )
1290
- })
1291
- it('can ignore an undo for a like activity of a remote object', async () => {
1292
- const actor = await makeActor('undoer4')
1293
- const activity = await as2.import({
1294
- type: 'Undo',
1295
- actor: actor.id,
1296
- id: 'https://social.example/user/undoer4/undo/1',
1297
- object: {
1298
- type: 'Like',
1299
- id: 'https://social.example/user/undoer4/like/1',
1300
- actor: 'https://social.example/user/undoer4',
1301
- object: 'https://third.example/user/other/note/1',
1302
- to: [botId, 'as:Public']
1303
- },
1304
- to: [botId, 'as:Public']
1305
- })
1306
- await handler.handleActivity(bot, activity)
1307
- assert.ok(true)
1308
- })
1309
- it('can ignore an undo for a like activity of a non-existent object', async () => {
1310
- const actor = await makeActor('undoer5')
1311
- const activity = await as2.import({
1312
- type: 'Undo',
1313
- actor: actor.id,
1314
- id: 'https://social.example/user/undoer5/undo/1',
1315
- object: {
1316
- type: 'Like',
1317
- id: 'https://social.example/user/undoer5/like/1',
1318
- actor: actor.id,
1319
- object: 'https://activitypubbot.example/user/ok/note/doesnotexist',
1320
- to: [botId, 'as:Public']
1321
- },
1322
- to: [botId, 'as:Public']
1323
- })
1324
- await handler.handleActivity(bot, activity)
1325
- assert.ok(true)
1326
- })
1327
- it('can ignore an undo for a like activity of an unreadable object', async () => {
1328
- const actor = await makeActor('undoer6')
1329
- const note = await as2.import({
1330
- attributedTo: botId,
1331
- id: formatter.format({
1332
- username: botName,
1333
- type: 'note',
1334
- nanoid: 'C-pFLhIGnM1XlpmXgNlfW'
1335
- }),
1336
- type: 'Note',
1337
- content: 'Hello, world!',
1338
- to: formatter.format({ username: 'other', collection: 'followers' })
1339
- })
1340
- await objectStorage.create(note)
1341
- const activity = await as2.import({
1342
- type: 'Undo',
1343
- actor: actor.id,
1344
- id: 'https://social.example/user/undoer6/undo/1',
1345
- object: {
1346
- type: 'Like',
1347
- id: 'https://social.example/user/undoer6/like/1',
1348
- actor: actor.id,
1349
- object: note.id,
1350
- to: [botId]
1351
- },
1352
- to: [botId]
1353
- })
1354
- await handler.handleActivity(bot, activity)
1355
- assert.ok(true)
1356
- })
1357
- it('can ignore an undo for a like activity of a blocked actor', async () => {
1358
- const actor = await makeActor('undoer7')
1359
- await actorStorage.addToCollection(botName, 'blocked', actor)
1360
- const note = await as2.import({
1361
- attributedTo: botId,
1362
- id: formatter.format({
1363
- username: botName,
1364
- type: 'note',
1365
- nanoid: 'rV_iftsHDMdAQBqfgg8DD'
1366
- }),
1367
- type: 'Note',
1368
- content: 'Hello, world!',
1369
- to: 'as:Public'
1370
- })
1371
- await objectStorage.create(note)
1372
- const activity = await as2.import({
1373
- type: 'Undo',
1374
- actor: actor.id,
1375
- id: 'https://social.example/user/undoer7/undo/1',
1376
- object: {
1377
- type: 'Like',
1378
- id: 'https://social.example/user/undoer7/like/1',
1379
- actor: actor.id,
1380
- object: note.id,
1381
- to: [botId]
1382
- },
1383
- to: [botId]
1384
- })
1385
- await handler.handleActivity(bot, activity)
1386
- assert.ok(true)
1387
- })
1388
- it('can ignore an undo for a like activity that has already been undone', async () => {
1389
- const note = await as2.import({
1390
- attributedTo: botId,
1391
- id: formatter.format({
1392
- username: botName,
1393
- type: 'note',
1394
- nanoid: 'KxQLHLAENW_CpMycvcpx4'
1395
- }),
1396
- type: 'Note',
1397
- content: 'Hello, world!',
1398
- to: 'as:Public'
1399
- })
1400
- await objectStorage.create(note)
1401
- const actor = await makeActor('undoer8')
1402
- const likeActivity = await as2.import({
1403
- type: 'Like',
1404
- actor: actor.id,
1405
- id: 'https://social.example/user/undoer8/like/1',
1406
- object: note.id,
1407
- to: [botId, 'as:Public']
1408
- })
1409
- await handler.handleActivity(bot, likeActivity)
1410
- assert.strictEqual(
1411
- true,
1412
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1413
- )
1414
- const undoActivity = await as2.import({
1415
- type: 'Undo',
1416
- actor: actor.id,
1417
- id: 'https://social.example/user/undoer8/undo/1',
1418
- object: {
1419
- type: 'Like',
1420
- id: 'https://social.example/user/undoer8/like/1',
1421
- actor: actor.id,
1422
- object: note.id,
1423
- to: [botId, 'as:Public']
1424
- },
1425
- to: [botId, 'as:Public']
1426
- })
1427
- await handler.handleActivity(bot, undoActivity)
1428
- assert.strictEqual(
1429
- false,
1430
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1431
- )
1432
- const duplicateActivity = await as2.import({
1433
- type: 'Undo',
1434
- actor: actor.id,
1435
- id: 'https://social.example/user/undoer8/undo/2',
1436
- object: {
1437
- type: 'Like',
1438
- id: 'https://social.example/user/undoer8/like/1',
1439
- actor: actor.id,
1440
- object: note.id,
1441
- to: [botId, 'as:Public']
1442
- },
1443
- to: [botId, 'as:Public']
1444
- })
1445
- await handler.handleActivity(bot, duplicateActivity)
1446
- assert.ok(true)
1447
- })
1448
- it('can handle an undo for a like activity followed by another like', async () => {
1449
- const note = await as2.import({
1450
- attributedTo: botId,
1451
- id: formatter.format({
1452
- username: botName,
1453
- type: 'note',
1454
- nanoid: 'LE2yKAebFSmMqSjN6naLl'
1455
- }),
1456
- type: 'Note',
1457
- content: 'Hello, world!',
1458
- to: 'as:Public'
1459
- })
1460
- await objectStorage.create(note)
1461
- const actor = await makeActor('undoer9')
1462
- const likeActivity = await as2.import({
1463
- type: 'Like',
1464
- actor: actor.id,
1465
- id: 'https://social.example/user/undoer9/like/1',
1466
- object: note.id,
1467
- to: [botId, 'as:Public']
1468
- })
1469
- await handler.handleActivity(bot, likeActivity)
1470
- assert.strictEqual(
1471
- true,
1472
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1473
- )
1474
- const undoActivity = await as2.import({
1475
- type: 'Undo',
1476
- actor: actor.id,
1477
- id: 'https://social.example/user/undoer9/undo/1',
1478
- object: {
1479
- type: 'Like',
1480
- id: 'https://social.example/user/undoer9/like/1',
1481
- actor: actor.id,
1482
- object: note.id,
1483
- to: [botId, 'as:Public']
1484
- },
1485
- to: [botId, 'as:Public']
1486
- })
1487
- await handler.handleActivity(bot, undoActivity)
1488
- assert.strictEqual(
1489
- false,
1490
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1491
- )
1492
- const reLikeActivity = await as2.import({
1493
- type: 'Like',
1494
- actor: actor.id,
1495
- id: 'https://social.example/user/undoer9/like/2',
1496
- object: note.id,
1497
- to: [botId, 'as:Public']
1498
- })
1499
- await handler.handleActivity(bot, reLikeActivity)
1500
- assert.strictEqual(
1501
- true,
1502
- await objectStorage.isInCollection(note.id, 'likes', reLikeActivity)
1503
- )
1504
- assert.strictEqual(
1505
- false,
1506
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1507
- )
1508
- })
1509
- it('can handle an undo for a like activity by id', async () => {
1510
- const actor = await makeActor('undoer10')
1511
- const note = await as2.import({
1512
- attributedTo: botId,
1513
- id: formatter.format({
1514
- username: botName,
1515
- type: 'note',
1516
- nanoid: 'nhzIHLcnHgU2l0lMb7dRl'
1517
- }),
1518
- type: 'Note',
1519
- content: 'Hello, world!',
1520
- to: 'as:Public'
1521
- })
1522
- await objectStorage.create(note)
1523
- const likeActivity = await as2.import({
1524
- type: 'Like',
1525
- actor: actor.id,
1526
- id: 'https://social.example/user/undoer10/like/1/activitypubbot.example/user/ok/note/nhzIHLcnHgU2l0lMb7dRl',
1527
- object: note.id,
1528
- to: [botId, 'as:Public']
1529
- })
1530
- await handler.handleActivity(bot, likeActivity)
1531
- assert.strictEqual(
1532
- true,
1533
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1534
- )
1535
- const undoActivity = await as2.import({
1536
- type: 'Undo',
1537
- actor: actor.id,
1538
- id: 'https://social.example/user/undoer2/undo/1/social.example/user/undoer10/like/1/activitypubbot.example/user/ok/note/nhzIHLcnHgU2l0lMb7dRl',
1539
- object: likeActivity.id,
1540
- to: [botId, 'as:Public']
1541
- })
1542
- await handler.handleActivity(bot, undoActivity)
1543
- assert.strictEqual(
1544
- false,
1545
- await objectStorage.isInCollection(note.id, 'likes', likeActivity)
1546
- )
1547
- })
1548
- it('can handle an undo for a share activity', async () => {
1549
- const actor = await makeActor('undoer11')
1550
- const note = await as2.import({
1551
- attributedTo: botId,
1552
- id: formatter.format({
1553
- username: botName,
1554
- type: 'note',
1555
- nanoid: '1lLOwN_Xo6NOitowWyMYM'
1556
- }),
1557
- type: 'Note',
1558
- content: 'Hello, world!',
1559
- to: 'as:Public'
1560
- })
1561
- await objectStorage.create(note)
1562
- const activity = await as2.import({
1563
- type: 'Announce',
1564
- actor: actor.id,
1565
- id: nockFormat({ username: 'undoer11', type: 'announce', num: 1, obj: note.id }),
1566
- object: note.id,
1567
- to: [botId, 'as:Public']
1568
- })
1569
- await handler.handleActivity(bot, activity)
1570
- assert.strictEqual(
1571
- true,
1572
- await objectStorage.isInCollection(note.id, 'shares', activity)
1573
- )
1574
- const undoActivity = await as2.import({
1575
- type: 'Undo',
1576
- actor: actor.id,
1577
- id: nockFormat({ username: 'undoer11', type: 'undo', num: 1, obj: activity.id }),
1578
- object: {
1579
- type: activity.type,
1580
- id: activity.id,
1581
- actor: actor.id,
1582
- object: note.id,
1583
- to: [botId, 'as:Public']
1584
- },
1585
- to: [botId, 'as:Public']
1586
- })
1587
- await handler.handleActivity(bot, undoActivity)
1588
- assert.strictEqual(
1589
- false,
1590
- await objectStorage.isInCollection(note.id, 'likes', activity)
1591
- )
1592
- })
1593
- it('can ignore an undo for a share activity with a different actor', async () => {
1594
- const note = await as2.import({
1595
- attributedTo: botId,
1596
- id: formatter.format({
1597
- username: botName,
1598
- type: 'note',
1599
- nanoid: 'kmK_TdUg1l8hasDwa7hGo'
1600
- }),
1601
- type: 'Note',
1602
- content: 'Hello, world!',
1603
- to: 'as:Public'
1604
- })
1605
- await objectStorage.create(note)
1606
- const sharer = await makeActor('sharer10', 'third.example')
1607
- const shareActivity = await as2.import({
1608
- type: 'Announce',
1609
- actor: sharer.id,
1610
- id: nockFormat({ domain: 'third.example', username: 'sharer10', type: 'announce', num: 1, obj: note.id }),
1611
- object: note.id,
1612
- to: [botId, 'as:Public']
1613
- })
1614
- await handler.handleActivity(bot, shareActivity)
1615
- assert.strictEqual(
1616
- true,
1617
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1618
- )
1619
- const undoer = await makeActor('undoer12', 'social.example')
1620
- const undoActivity = await as2.import({
1621
- type: 'Undo',
1622
- actor: undoer.id,
1623
- id: nockFormat({ domain: 'social.example', username: 'undoer12', type: 'undo', num: 1, obj: shareActivity.id }),
1624
- object: {
1625
- type: 'Announce',
1626
- id: shareActivity.id
1627
- },
1628
- to: [botId, 'as:Public']
1629
- })
1630
- await handler.handleActivity(bot, undoActivity)
1631
- assert.strictEqual(
1632
- true,
1633
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1634
- )
1635
- })
1636
- it('can ignore an undo for a share activity of a remote object', async () => {
1637
- const actor = await makeActor('undoer13')
1638
- const remoteObjectId = nockFormat({ domain: 'third.example', username: 'other', type: 'note', num: 1 })
1639
- const announceActivityId = nockFormat({ username: 'undoer13', type: 'announce', num: 1, obj: remoteObjectId })
1640
- const activity = await as2.import({
1641
- type: 'Undo',
1642
- actor: actor.id,
1643
- id: nockFormat({ username: 'undoer13', type: 'undo', num: 1, obj: announceActivityId }),
1644
- object: {
1645
- type: 'Announce',
1646
- id: announceActivityId,
1647
- actor: actor.id,
1648
- object: remoteObjectId,
1649
- to: [botId, 'as:Public']
1650
- },
1651
- to: [botId, 'as:Public']
1652
- })
1653
- await handler.handleActivity(bot, activity)
1654
- assert.ok(true)
1655
- })
1656
- it('can ignore an undo for a share activity of a non-existent object', async () => {
1657
- const actor = await makeActor('undoer14')
1658
- const dne = formatter.format({ username: botName, type: 'note', nanoid: 'doesnotexist' })
1659
- const announceActivityId = nockFormat({ username: 'undoer14', type: 'announce', num: 1, obj: dne })
1660
- const activity = await as2.import({
1661
- type: 'Undo',
1662
- actor: actor.id,
1663
- id: nockFormat({ username: 'undoer14', type: 'undo', num: 1, obj: announceActivityId }),
1664
- object: {
1665
- type: 'Announce',
1666
- id: announceActivityId,
1667
- actor: actor.id,
1668
- object: dne,
1669
- to: [botId, 'as:Public']
1670
- },
1671
- to: [botId, 'as:Public']
1672
- })
1673
- await handler.handleActivity(bot, activity)
1674
- assert.ok(true)
1675
- })
1676
- it('can ignore an undo for a share activity of an unreadable object', async () => {
1677
- const actor = await makeActor('undoer15')
1678
- const note = await as2.import({
1679
- attributedTo: botId,
1680
- id: formatter.format({
1681
- username: botName,
1682
- type: 'note',
1683
- nanoid: 'mQ--bYVZLm9miMOUrbYU5'
1684
- }),
1685
- type: 'Note',
1686
- content: 'Hello, world!',
1687
- to: formatter.format({ username: botName, collection: 'followers' })
1688
- })
1689
- await objectStorage.create(note)
1690
- const announceActivityId = nockFormat({ username: 'undoer15', type: 'announce', num: 1, obj: note.id })
1691
- const activity = await as2.import({
1692
- type: 'Undo',
1693
- actor: actor.id,
1694
- id: nockFormat({ username: 'undoer15', type: 'undo', num: 1, obj: announceActivityId }),
1695
- object: {
1696
- type: 'Announce',
1697
- id: announceActivityId,
1698
- actor: actor.id,
1699
- object: note.id,
1700
- to: [botId]
1701
- },
1702
- to: [botId]
1703
- })
1704
- await handler.handleActivity(bot, activity)
1705
- assert.ok(true)
1706
- })
1707
- it('can ignore an undo for a share activity of a blocked actor', async () => {
1708
- const actor = await makeActor('undoer16')
1709
- await actorStorage.addToCollection(botName, 'blocked', actor)
1710
- const note = await as2.import({
1711
- attributedTo: botId,
1712
- id: formatter.format({
1713
- username: botName,
1714
- type: 'note',
1715
- nanoid: 'fbsPvVofkIcWt8HZA7NpK'
1716
- }),
1717
- type: 'Note',
1718
- content: 'Hello, world!',
1719
- to: 'as:Public'
1720
- })
1721
- await objectStorage.create(note)
1722
- const shareActivityId = nockFormat({ username: 'undoer16', type: 'announce', num: 1, obj: note.id })
1723
- const activity = await as2.import({
1724
- type: 'Undo',
1725
- actor: actor.id,
1726
- id: nockFormat({ username: 'undoer16', type: 'undo', num: 1, obj: shareActivityId }),
1727
- object: {
1728
- type: 'Announce',
1729
- id: shareActivityId,
1730
- actor: actor.id,
1731
- object: note.id,
1732
- to: [botId]
1733
- },
1734
- to: [botId]
1735
- })
1736
- await handler.handleActivity(bot, activity)
1737
- assert.ok(true)
1738
- })
1739
- it('can ignore an undo for a share activity that has already been undone', async () => {
1740
- const note = await as2.import({
1741
- attributedTo: botId,
1742
- id: formatter.format({
1743
- username: botName,
1744
- type: 'note',
1745
- nanoid: '0YpKR9l9ugvaAx2V-WPUd'
1746
- }),
1747
- type: 'Note',
1748
- content: 'Hello, world!',
1749
- to: 'as:Public'
1750
- })
1751
- await objectStorage.create(note)
1752
- const actor = await makeActor('undoer17')
1753
- const shareActivity = await as2.import({
1754
- type: 'Announce',
1755
- actor: actor.id,
1756
- id: nockFormat({ username: 'undoer17', type: 'announce', num: 1, obj: note.id }),
1757
- object: note.id,
1758
- to: [botId, 'as:Public']
1759
- })
1760
- await handler.handleActivity(bot, shareActivity)
1761
- assert.strictEqual(
1762
- true,
1763
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1764
- )
1765
- const undoActivity = await as2.import({
1766
- type: 'Undo',
1767
- actor: actor.id,
1768
- id: nockFormat({ username: 'undoer17', type: 'undo', num: 1, obj: shareActivity.id }),
1769
- object: {
1770
- type: 'Announce',
1771
- id: shareActivity.id,
1772
- actor: actor.id,
1773
- object: note.id,
1774
- to: [botId, 'as:Public']
1775
- },
1776
- to: [botId, 'as:Public']
1777
- })
1778
- await handler.handleActivity(bot, undoActivity)
1779
- assert.strictEqual(
1780
- false,
1781
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1782
- )
1783
- const duplicateActivity = await as2.import({
1784
- type: 'Undo',
1785
- actor: actor.id,
1786
- id: nockFormat({ username: 'undoer17', type: 'undo', num: 2, obj: shareActivity.id }),
1787
- object: {
1788
- type: 'Announce',
1789
- id: shareActivity.id,
1790
- actor: actor.id,
1791
- object: note.id,
1792
- to: [botId, 'as:Public']
1793
- },
1794
- to: [botId, 'as:Public']
1795
- })
1796
- await handler.handleActivity(bot, duplicateActivity)
1797
- assert.ok(true)
1798
- })
1799
- it('can handle an undo for a share activity followed by another share', async () => {
1800
- const note = await as2.import({
1801
- attributedTo: botId,
1802
- id: formatter.format({
1803
- username: botName,
1804
- type: 'note',
1805
- nanoid: 'DzCmKY2rzy7tWNr7CJvf1'
1806
- }),
1807
- type: 'Note',
1808
- content: 'Hello, world!',
1809
- to: 'as:Public'
1810
- })
1811
- await objectStorage.create(note)
1812
- const actor = await makeActor('undoer18')
1813
- const shareActivity = await as2.import({
1814
- type: 'Announce',
1815
- actor: actor.id,
1816
- id: nockFormat({ username: 'undoer18', type: 'announce', num: 1, obj: note.id }),
1817
- object: note.id,
1818
- to: [botId, 'as:Public']
1819
- })
1820
- await handler.handleActivity(bot, shareActivity)
1821
- assert.strictEqual(
1822
- true,
1823
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1824
- )
1825
- const undoActivity = await as2.import({
1826
- type: 'Undo',
1827
- actor: actor.id,
1828
- id: nockFormat({ username: 'undoer18', type: 'undo', num: 1, obj: shareActivity.id }),
1829
- object: {
1830
- type: 'Announce',
1831
- id: shareActivity.id,
1832
- actor: actor.id,
1833
- object: note.id,
1834
- to: [botId, 'as:Public']
1835
- },
1836
- to: [botId, 'as:Public']
1837
- })
1838
- await handler.handleActivity(bot, undoActivity)
1839
- assert.strictEqual(
1840
- false,
1841
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1842
- )
1843
- const reShareActivity = await as2.import({
1844
- type: 'Announce',
1845
- actor: actor.id,
1846
- id: nockFormat({ username: 'undoer18', type: 'announce', num: 2, obj: note.id }),
1847
- object: note.id,
1848
- to: [botId, 'as:Public']
1849
- })
1850
- await handler.handleActivity(bot, reShareActivity)
1851
- assert.strictEqual(
1852
- true,
1853
- await objectStorage.isInCollection(note.id, 'shares', reShareActivity)
1854
- )
1855
- assert.strictEqual(
1856
- false,
1857
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1858
- )
1859
- })
1860
- it('can handle an undo for a share activity by id', async () => {
1861
- const actor = await makeActor('undoer19')
1862
- const note = await as2.import({
1863
- attributedTo: botId,
1864
- id: formatter.format({
1865
- username: botName,
1866
- type: 'note',
1867
- nanoid: 'YYTvtiZm4h9J8jMsWS3Gq'
1868
- }),
1869
- type: 'Note',
1870
- content: 'Hello, world!',
1871
- to: 'as:Public'
1872
- })
1873
- await objectStorage.create(note)
1874
- const shareActivity = await as2.import({
1875
- type: 'Announce',
1876
- actor: actor.id,
1877
- id: nockFormat({ username: 'undoer19', type: 'announce', num: 1, obj: note.id }),
1878
- object: note.id,
1879
- to: [botId, 'as:Public']
1880
- })
1881
- await handler.handleActivity(bot, shareActivity)
1882
- assert.strictEqual(
1883
- true,
1884
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1885
- )
1886
- const undoActivity = await as2.import({
1887
- type: 'Undo',
1888
- actor: actor.id,
1889
- id: nockFormat({ username: 'undoer19', type: 'undo', num: 1, obj: shareActivity.id }),
1890
- object: shareActivity.id,
1891
- to: [botId, 'as:Public']
1892
- })
1893
- await handler.handleActivity(bot, undoActivity)
1894
- assert.strictEqual(
1895
- false,
1896
- await objectStorage.isInCollection(note.id, 'shares', shareActivity)
1897
- )
1898
- })
1899
- it('can handle an undo for a block activity', async () => {
1900
- const actor = await makeActor('undoer20')
1901
- const blockActivity = await as2.import({
1902
- type: 'Block',
1903
- actor: actor.id,
1904
- id: nockFormat({ username: 'undoer20', type: 'block', num: 1, obj: botId }),
1905
- object: botId,
1906
- to: botId
1907
- })
1908
- await handler.handleActivity(bot, blockActivity)
1909
- assert.ok(true)
1910
- const undoActivity = await as2.import({
1911
- type: 'Undo',
1912
- actor: actor.id,
1913
- id: nockFormat({ username: 'undoer20', type: 'undo', num: 1, obj: blockActivity.id }),
1914
- object: {
1915
- type: 'Block',
1916
- id: blockActivity.id,
1917
- actor: actor.id,
1918
- object: botId,
1919
- to: botId
1920
- },
1921
- to: botId
1922
- })
1923
- await handler.handleActivity(bot, undoActivity)
1924
- assert.ok(true)
1925
- })
1926
- it('can handle an undo for a block activity by id', async () => {
1927
- const actor = await makeActor('undoer21')
1928
- const blockActivity = await as2.import({
1929
- type: 'Block',
1930
- actor: actor.id,
1931
- id: nockFormat({ username: 'undoer21', type: 'block', num: 1, obj: botId }),
1932
- object: botId,
1933
- to: botId
1934
- })
1935
- await handler.handleActivity(bot, blockActivity)
1936
- assert.ok(true)
1937
- const undoActivity = await as2.import({
1938
- type: 'Undo',
1939
- actor: actor.id,
1940
- id: nockFormat({ username: 'undoer21', type: 'undo', num: 1, obj: blockActivity.id }),
1941
- object: blockActivity.id,
1942
- to: botId
1943
- })
1944
- await handler.handleActivity(bot, undoActivity)
1945
- assert.ok(true)
1946
- })
1947
-
1948
- it('can ignore an undo for a block activity of another user', async () => {
1949
- const actor = await makeActor('undoer22')
1950
- const otherId = nockFormat({ username: 'other', domain: 'third.example' })
1951
- const blockActivity = await as2.import({
1952
- type: 'Block',
1953
- actor: actor.id,
1954
- id: nockFormat({ username: 'undoer22', type: 'block', num: 1, obj: otherId }),
1955
- object: otherId,
1956
- to: ['as:Public']
1957
- })
1958
- await handler.handleActivity(bot, blockActivity)
1959
- assert.ok(true)
1960
- const undoActivity = await as2.import({
1961
- type: 'Undo',
1962
- actor: actor.id,
1963
- id: nockFormat({ username: 'undoer22', type: 'undo', num: 1, obj: blockActivity.id }),
1964
- object: {
1965
- type: 'Block',
1966
- id: blockActivity.id,
1967
- actor: actor.id,
1968
- object: otherId,
1969
- to: ['as:Public']
1970
- },
1971
- to: ['as:Public']
1972
- })
1973
- await handler.handleActivity(bot, undoActivity)
1974
- assert.ok(true)
1975
- })
1976
- it('can handle an undo for a follow activity', async () => {
1977
- const username = 'undoer23'
1978
- const actor = await makeActor(username)
1979
- const followActivity = await as2.import({
1980
- type: 'Follow',
1981
- actor: actor.id,
1982
- id: nockFormat({ username, type: 'follow', num: 1, obj: botId }),
1983
- object: botId,
1984
- to: [botId, 'as:Public']
1985
- })
1986
- await handler.handleActivity(bot, followActivity)
1987
- assert.strictEqual(
1988
- true,
1989
- await actorStorage.isInCollection(botName, 'followers', actor)
1990
- )
1991
- const undoActivity = await as2.import({
1992
- type: 'Undo',
1993
- actor: actor.id,
1994
- id: nockFormat({ username, type: 'undo', num: 1, obj: followActivity.id }),
1995
- object: {
1996
- type: 'Follow',
1997
- id: followActivity.id,
1998
- actor: actor.id,
1999
- object: botId,
2000
- to: [botId, 'as:Public']
2001
- },
2002
- to: botId
2003
- })
2004
- await handler.handleActivity(bot, undoActivity)
2005
- assert.strictEqual(
2006
- false,
2007
- await actorStorage.isInCollection(botName, 'followers', actor)
2008
- )
2009
- })
2010
- it('can handle an undo for a follow by id', async () => {
2011
- const username = 'undoer24'
2012
- const actor = await makeActor(username)
2013
- const followActivityId = nockFormat({ username, type: 'follow', num: 1, obj: botId })
2014
- const followActivity = await as2.import({
2015
- type: 'Follow',
2016
- actor: actor.id,
2017
- id: followActivityId,
2018
- object: botId,
2019
- to: [botId, 'as:Public']
2020
- })
2021
- await handler.handleActivity(bot, followActivity)
2022
- assert.strictEqual(
2023
- true,
2024
- await actorStorage.isInCollection(botName, 'followers', actor)
2025
- )
2026
- const undoActivity = await as2.import({
2027
- type: 'Undo',
2028
- actor: actor.id,
2029
- id: nockFormat({ username, type: 'undo', num: 1, obj: followActivity.id }),
2030
- object: followActivityId,
2031
- to: [botId, 'as:Public']
2032
- })
2033
- await handler.handleActivity(bot, undoActivity)
2034
- assert.strictEqual(
2035
- false,
2036
- await actorStorage.isInCollection(botName, 'followers', actor)
2037
- )
2038
- })
2039
- it('can ignore an undo for a follow activity of another user', async () => {
2040
- const username = 'undoer25'
2041
- const actor = await makeActor(username)
2042
- const otherId = nockFormat({ username: 'other', domain: 'third.example' })
2043
- const followActivity = await as2.import({
2044
- type: 'Follow',
2045
- actor: actor.id,
2046
- id: nockFormat({ username, type: 'follow', num: 1, obj: otherId }),
2047
- object: otherId,
2048
- to: ['as:Public']
2049
- })
2050
- await handler.handleActivity(bot, followActivity)
2051
- assert.strictEqual(
2052
- false,
2053
- await actorStorage.isInCollection(botName, 'followers', actor)
2054
- )
2055
- const undoActivity = await as2.import({
2056
- type: 'Undo',
2057
- actor: actor.id,
2058
- id: nockFormat({ username, type: 'undo', num: 1, obj: followActivity.id }),
2059
- object: {
2060
- type: 'Follow',
2061
- id: followActivity.id,
2062
- actor: actor.id,
2063
- object: otherId,
2064
- to: ['as:Public']
2065
- },
2066
- to: ['as:Public']
2067
- })
2068
- await handler.handleActivity(bot, undoActivity)
2069
- assert.ok(true)
2070
- })
2071
- it('can ignore an undo for a follow activity by another user', async () => {
2072
- const username = 'undoer26'
2073
- const otherName = 'other'
2074
- const actor = await makeActor(username)
2075
- const other = await makeActor(otherName, 'third.example')
2076
- const followActivity = await as2.import({
2077
- type: 'Follow',
2078
- actor: other.id,
2079
- id: nockFormat({ domain: 'third.example', username: otherName, type: 'follow', num: 1, obj: botId }),
2080
- object: botId,
2081
- to: [botId, 'as:Public']
2082
- })
2083
- await handler.handleActivity(bot, followActivity)
2084
- assert.strictEqual(
2085
- true,
2086
- await actorStorage.isInCollection(botName, 'followers', other)
2087
- )
2088
- const undoActivity = await as2.import({
2089
- type: 'Undo',
2090
- actor: actor.id,
2091
- id: nockFormat({ username, type: 'undo', num: 1, obj: followActivity.id }),
2092
- object: {
2093
- type: 'Follow',
2094
- id: followActivity.id,
2095
- actor: other.id,
2096
- object: botId,
2097
- to: [botId, 'as:Public']
2098
- },
2099
- to: [botId, 'as:Public']
2100
- })
2101
- await handler.handleActivity(bot, undoActivity)
2102
- assert.strictEqual(
2103
- true,
2104
- await actorStorage.isInCollection(botName, 'followers', other)
2105
- )
2106
- })
2107
- it('can handle an undo for a follow activity followed by another follow', async () => {
2108
- const username = 'undoer27'
2109
- const actor = await makeActor(username)
2110
- const followActivity = await as2.import({
2111
- type: 'Follow',
2112
- actor: actor.id,
2113
- id: nockFormat({ username, type: 'follow', num: 1, obj: botId }),
2114
- object: botId,
2115
- to: [botId, 'as:Public']
2116
- })
2117
- await handler.handleActivity(bot, followActivity)
2118
- assert.strictEqual(
2119
- true,
2120
- await actorStorage.isInCollection(botName, 'followers', actor)
2121
- )
2122
- const undoActivity = await as2.import({
2123
- type: 'Undo',
2124
- actor: actor.id,
2125
- id: nockFormat({ username, type: 'undo', num: 1, obj: followActivity.id }),
2126
- object: {
2127
- type: 'Follow',
2128
- id: followActivity.id,
2129
- actor: actor.id,
2130
- object: botId,
2131
- to: [botId, 'as:Public']
2132
- },
2133
- to: [botId, 'as:Public']
2134
- })
2135
- await handler.handleActivity(bot, undoActivity)
2136
- assert.strictEqual(
2137
- false,
2138
- await actorStorage.isInCollection(botName, 'followers', actor)
2139
- )
2140
- const reFollowActivity = await as2.import({
2141
- type: 'Follow',
2142
- actor: actor.id,
2143
- id: nockFormat({ username, type: 'follow', num: 2, obj: botId }),
2144
- object: botId,
2145
- to: [botId, 'as:Public']
2146
- })
2147
- await handler.handleActivity(bot, reFollowActivity)
2148
- assert.strictEqual(
2149
- true,
2150
- await actorStorage.isInCollection(botName, 'followers', actor)
2151
- )
2152
- })
2153
- it('can ignore an undo for a follow activity that has already been undone', async () => {
2154
- const username = 'undoer28'
2155
- const actor = await makeActor(username)
2156
- const followActivity = await as2.import({
2157
- type: 'Follow',
2158
- actor: actor.id,
2159
- id: nockFormat({ username, type: 'follow', num: 1, obj: botId }),
2160
- object: botId,
2161
- to: [botId, 'as:Public']
2162
- })
2163
- await handler.handleActivity(bot, followActivity)
2164
- assert.strictEqual(
2165
- true,
2166
- await actorStorage.isInCollection(botName, 'followers', actor)
2167
- )
2168
- const undoActivity = await as2.import({
2169
- type: 'Undo',
2170
- actor: actor.id,
2171
- id: nockFormat({ username, type: 'undo', num: 1, obj: followActivity.id }),
2172
- object: {
2173
- type: 'Follow',
2174
- id: followActivity.id,
2175
- actor: actor.id,
2176
- object: botId,
2177
- to: [botId, 'as:Public']
2178
- },
2179
- to: [botId, 'as:Public']
2180
- })
2181
- await handler.handleActivity(bot, undoActivity)
2182
- assert.strictEqual(
2183
- false,
2184
- await actorStorage.isInCollection(botName, 'followers', actor)
2185
- )
2186
- const duplicateActivity = await as2.import({
2187
- type: 'Undo',
2188
- actor: actor.id,
2189
- id: nockFormat({ username, type: 'undo', num: 2, obj: followActivity.id }),
2190
- object: {
2191
- type: 'Follow',
2192
- id: followActivity.id,
2193
- actor: actor.id,
2194
- object: botId,
2195
- to: [botId, 'as:Public']
2196
- },
2197
- to: [botId, 'as:Public']
2198
- })
2199
- await handler.handleActivity(bot, duplicateActivity)
2200
- assert.ok(true)
2201
- })
2202
- it('can ignore an undo for a follow activity by a blocked actor', async () => {
2203
- const username = 'undoer29'
2204
- const actor = await makeActor(username)
2205
- await actorStorage.addToCollection(botName, 'blocked', actor)
2206
- const followActivity = await as2.import({
2207
- type: 'Follow',
2208
- actor: actor.id,
2209
- id: nockFormat({ username, type: 'follow', num: 1, obj: botId }),
2210
- object: botId,
2211
- to: [botId, 'as:Public']
2212
- })
2213
- await handler.handleActivity(bot, followActivity)
2214
- assert.strictEqual(
2215
- false,
2216
- await actorStorage.isInCollection(botName, 'followers', actor)
2217
- )
2218
- const undoActivity = await as2.import({
2219
- type: 'Undo',
2220
- actor: actor.id,
2221
- id: nockFormat({ username, type: 'undo', num: 1, obj: followActivity.id }),
2222
- object: {
2223
- type: 'Follow',
2224
- id: followActivity.id,
2225
- actor: actor.id,
2226
- object: botId,
2227
- to: [botId, 'as:Public']
2228
- },
2229
- to: [botId, 'as:Public']
2230
- })
2231
- await handler.handleActivity(bot, undoActivity)
2232
- assert.ok(true)
2233
- })
2234
- it('manages a thread with a direct reply', async () => {
2235
- const idProps = {
2236
- username: botName,
2237
- type: 'note',
2238
- nanoid: 'lbsAZQ6JeLiq060MgImdF'
2239
- }
2240
- const oid = formatter.format(idProps)
2241
- const threadId = formatter.format({ ...idProps, collection: 'thread' })
2242
- const original = await as2.import({
2243
- '@context': [
2244
- 'https://www.w3.org/ns/activitystreams',
2245
- 'https://purl.archive.org/socialweb/thread/1.0'
2246
- ],
2247
- id: oid,
2248
- type: 'Note',
2249
- attributedTo: formatter.format({ username: botName }),
2250
- to: 'as:Public',
2251
- content: 'Original note',
2252
- thread: threadId,
2253
- context: threadId
2254
- })
2255
- await objectStorage.create(original)
2256
- await objectStorage.addToCollection(oid, 'thread', original)
2257
- const activity = await as2.import({
2258
- type: 'Create',
2259
- actor: 'https://social.example/user/remote1',
2260
- id: 'https://social.example/user/remote1/object/23',
2261
- object: {
2262
- inReplyTo: oid,
2263
- id: 'https://social.example/user/remote1/object/24',
2264
- type: 'Note',
2265
- content: 'Reply note',
2266
- to: 'as:Public',
2267
- thread: threadId
2268
- },
2269
- to: 'as:Public'
2270
- })
2271
- await handler.handleActivity(bot, activity)
2272
- const collection2 = await objectStorage.getCollection(oid, 'thread')
2273
- assert.equal(collection2.totalItems, 2)
2274
- await handler.onIdle()
2275
- })
2276
- })