@evanp/activitypub-bot 0.45.4 → 0.45.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/lib/botcontext.js +13 -0
- package/lib/bots/followback.js +82 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,21 @@ and this project adheres to
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.45.6] - 2026-04-27
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Patch up database problem where actors are
|
|
17
|
+
stored in `pendingFollowing` instead of
|
|
18
|
+
`Follow` activities.
|
|
19
|
+
|
|
20
|
+
## [0.45.5] - 2026-04-27
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Clear stale follow requests in followback bot
|
|
25
|
+
- Synchronize followers collection in followback bot
|
|
26
|
+
|
|
12
27
|
## [0.45.4] - 2026-04-26
|
|
13
28
|
|
|
14
29
|
### Fixed
|
package/lib/botcontext.js
CHANGED
|
@@ -278,6 +278,15 @@ export class BotContext {
|
|
|
278
278
|
'pendingFollowing',
|
|
279
279
|
followActivity
|
|
280
280
|
)
|
|
281
|
+
// Buggy code used to put the actor in
|
|
282
|
+
// the collection
|
|
283
|
+
if (await this.#actorStorage.isInCollection(this.#botId, 'pendingFollowing', actor)) {
|
|
284
|
+
await this.#actorStorage.removeFromCollection(
|
|
285
|
+
this.#botId,
|
|
286
|
+
'pendingFollowing',
|
|
287
|
+
actor
|
|
288
|
+
)
|
|
289
|
+
}
|
|
281
290
|
}
|
|
282
291
|
await this.#actorStorage.removeFromCollection(
|
|
283
292
|
this.#botId,
|
|
@@ -487,6 +496,10 @@ export class BotContext {
|
|
|
487
496
|
yield * this.#actorStorage.items(this.#botId, 'following')
|
|
488
497
|
}
|
|
489
498
|
|
|
499
|
+
async * pendingFollowing () {
|
|
500
|
+
yield * this.#actorStorage.items(this.#botId, 'pendingFollowing')
|
|
501
|
+
}
|
|
502
|
+
|
|
490
503
|
async addFollowingUnsafe (actor) {
|
|
491
504
|
assert.ok(actor)
|
|
492
505
|
assert.equal(typeof actor, 'object')
|
package/lib/bots/followback.js
CHANGED
|
@@ -3,14 +3,33 @@ import Bot from '../bot.js'
|
|
|
3
3
|
const DEFAULT_NAME = 'FollowBackBot'
|
|
4
4
|
const DEFAULT_DESCRIPTION = 'A bot that follows you back'
|
|
5
5
|
|
|
6
|
+
const NS = 'https://www.w3.org/ns/activitystreams#'
|
|
7
|
+
const FOLLOW = `${NS}Follow`
|
|
8
|
+
|
|
9
|
+
// 7-day default timeout
|
|
10
|
+
|
|
11
|
+
const DEFAULT_STALE_FOLLOW_TIMEOUT = 7 * 24 * 60 * 60 * 1000
|
|
12
|
+
|
|
6
13
|
export default class FollowBackBot extends Bot {
|
|
7
14
|
#fullname
|
|
8
15
|
#description
|
|
16
|
+
#staleFollowTimeout
|
|
9
17
|
|
|
10
18
|
constructor (username, options = {}) {
|
|
11
19
|
super(username, options)
|
|
12
20
|
this.#fullname = options.fullname || DEFAULT_NAME
|
|
13
21
|
this.#description = options.description || DEFAULT_DESCRIPTION
|
|
22
|
+
this.#staleFollowTimeout = ('staleFollowTimeout' in options)
|
|
23
|
+
? options.staleFollowTimeout
|
|
24
|
+
: DEFAULT_STALE_FOLLOW_TIMEOUT
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async initialize (context) {
|
|
28
|
+
await super.initialize(context)
|
|
29
|
+
await this.#undoStalePendingFollowing()
|
|
30
|
+
// Drain the queue so undos arrive before re-follows
|
|
31
|
+
await this._context.onIdle()
|
|
32
|
+
await this.#synchronizeFollowers()
|
|
14
33
|
}
|
|
15
34
|
|
|
16
35
|
get fullname () {
|
|
@@ -30,4 +49,67 @@ export default class FollowBackBot extends Bot {
|
|
|
30
49
|
this._context.logger.info({ actorId: actor.id }, 'Unfollowing user back')
|
|
31
50
|
await this._context.unfollowActor(actor)
|
|
32
51
|
}
|
|
52
|
+
|
|
53
|
+
async #synchronizeFollowers () {
|
|
54
|
+
for await (const follower of this._context.followers()) {
|
|
55
|
+
try {
|
|
56
|
+
if (!await this._context.isFollowing(follower) &&
|
|
57
|
+
!await this._context.isPendingFollowing(follower)) {
|
|
58
|
+
await this._context.followActor(follower)
|
|
59
|
+
this._context.logger.info(
|
|
60
|
+
{
|
|
61
|
+
actorId: follower.id
|
|
62
|
+
},
|
|
63
|
+
'Synchronized a follower not yet followed'
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
this._context.logger.error(
|
|
68
|
+
{
|
|
69
|
+
err,
|
|
70
|
+
follower: follower.id
|
|
71
|
+
},
|
|
72
|
+
'Error checking for followback; skipping'
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async #undoStalePendingFollowing () {
|
|
79
|
+
const now = new Date()
|
|
80
|
+
for await (const follow of this._context.pendingFollowing()) {
|
|
81
|
+
try {
|
|
82
|
+
const activity = await this._context.getObject(follow.id)
|
|
83
|
+
if (activity.type === FOLLOW) {
|
|
84
|
+
if (activity.published && (now - activity.published > this.#staleFollowTimeout)) {
|
|
85
|
+
await this._context.unfollowActor(activity.object.first)
|
|
86
|
+
this._context.logger.info(
|
|
87
|
+
{
|
|
88
|
+
actorId: activity.object.first?.id,
|
|
89
|
+
published: activity.published
|
|
90
|
+
},
|
|
91
|
+
'Unfollowed stale actor'
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
} else if (activity.inbox) {
|
|
95
|
+
const actor = activity
|
|
96
|
+
this._context.logger.warn(
|
|
97
|
+
{
|
|
98
|
+
actorId: actor.id
|
|
99
|
+
},
|
|
100
|
+
'actor incorrectly in pendingFollowing'
|
|
101
|
+
)
|
|
102
|
+
await this._context.unfollowActor(actor)
|
|
103
|
+
}
|
|
104
|
+
} catch (err) {
|
|
105
|
+
this._context.logger.error(
|
|
106
|
+
{
|
|
107
|
+
err,
|
|
108
|
+
activity: follow.id
|
|
109
|
+
},
|
|
110
|
+
'Error checking stale follow; skipping'
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
33
115
|
}
|