unoffical wafrn mirror
wafrn.net
atproto
social-network
activitypub
1import { Job } from 'bullmq'
2import {
3 Blocks,
4 EmojiReaction,
5 FederatedHost,
6 Follows,
7 Mutes,
8 Post,
9 PostMentionsUserRelation,
10 User,
11 UserLikesPostRelations,
12 UserOptions,
13 sequelize
14} from '../../models/index.js'
15import { completeEnvironment } from '../backendOptions.js'
16import { getUserIdFromRemoteId } from '../cacheGetters/getUserIdFromRemoteId.js'
17import { getPetitionSigned } from '../activitypub/getPetitionSigned.js'
18import { processUserEmojis } from '../activitypub/processUserEmojis.js'
19import { fediverseTag } from '../../interfaces/fediverse/tags.js'
20import { logger } from '../logger.js'
21import { redisCache } from '../redis.js'
22import { Op } from 'sequelize'
23import { getDeletedUser } from '../cacheGetters/getDeletedUser.js'
24
25// This function will return userid after processing it.
26async function getRemoteActorIdProcessor(job: Job) {
27 const actorUrl: string = job.data.actorUrl
28 const forceUpdate: boolean = job.data.forceUpdate
29 let res: string | User | undefined | null = await getUserIdFromRemoteId(actorUrl)
30 let url = undefined
31 try {
32 url = new URL(actorUrl)
33 } catch (error) {
34 res = await getDeletedUser()
35 url = undefined
36 logger.debug({
37 message: `Invalid url ${actorUrl}`,
38 url: actorUrl,
39 stack: new Error().stack
40 })
41 }
42 if (res === '' || (forceUpdate && url != undefined)) {
43 let federatedHost = await FederatedHost.findOne({
44 where: sequelize.where(
45 sequelize.fn('lower', sequelize.col('displayName')),
46 url?.host ? url.host.toLowerCase() : ''
47 )
48 })
49 const hostBanned = federatedHost?.blocked
50 if (hostBanned) {
51 res = await getDeletedUser()
52 } else {
53 const user = (await User.findByPk(job.data.userId)) as User
54 const userPetition = await getPetitionSigned(user, actorUrl)
55 if (userPetition) {
56 if (!federatedHost && url) {
57 const federatedHostToCreate = {
58 displayName: url.host.toLocaleLowerCase(),
59 publicInbox: userPetition.endpoints?.sharedInbox ? userPetition.endpoints?.sharedInbox : ''
60 }
61 federatedHost = (await FederatedHost.findOrCreate({ where: federatedHostToCreate }))[0]
62 }
63 if (!url || !federatedHost) {
64 logger.warn({ message: 'Url is not valid wtf', trace: new Error().stack })
65 return await getDeletedUser()
66 }
67 const remoteMentionUrl = typeof userPetition.url === 'string' ? userPetition.url : ''
68 let followers = 0
69 let followed = 0
70 if (userPetition.followers) {
71 const followersPetition = await getPetitionSigned(user, userPetition.followers)
72 if (followersPetition && followersPetition.totalItems) {
73 followers = followersPetition.totalItems
74 }
75 }
76 if (userPetition.following) {
77 const followingPetition = await getPetitionSigned(user, userPetition.following)
78 if (followingPetition && followingPetition.totalItems) {
79 followed = followingPetition.totalItems
80 }
81 }
82 const userData = {
83 hideFollows: false,
84 hideProfileNotLoggedIn: false,
85 url: `@${userPetition.preferredUsername}@${url?.host}`,
86 name: userPetition.name ? userPetition.name : userPetition.preferredUsername,
87 email: null,
88 description: userPetition.summary ? userPetition.summary : '',
89 avatar: userPetition.icon?.url
90 ? userPetition.icon.url
91 : `${completeEnvironment.mediaUrl}/uploads/default.webp`,
92 headerImage: userPetition?.image?.url ? userPetition.image.url.toString() : ``,
93 password: 'NOT_A_WAFRN_USER_NOT_REAL_PASSWORD',
94 publicKey: userPetition.publicKey?.publicKeyPem,
95 remoteInbox: userPetition.inbox,
96 remoteId: actorUrl,
97 activated: true,
98 federatedHostId: federatedHost.id,
99 remoteMentionUrl: remoteMentionUrl,
100 followersCollectionUrl: userPetition.followers,
101 followingCollectionUrl: userPetition.following,
102 isBot: userPetition.type != 'Person',
103 followerCount: followers,
104 followingCount: followed,
105 createdAt: userPetition.published ? new Date(userPetition.published) : new Date(),
106 updatedAt: new Date(),
107 NSFW: false,
108 birthDate: new Date(),
109 userMigratedTo: userPetition.movedTo || ''
110 }
111 federatedHost.publicInbox = userPetition.endpoints?.sharedInbox
112 await federatedHost.save()
113 let userRes
114 const existingUsers = await User.findAll({
115 where: {
116 [Op.or]: [
117 sequelize.where(sequelize.fn('lower', sequelize.col('url')), userData.url.toLowerCase()),
118 {
119 remoteId: userData.remoteId
120 }
121 ]
122 }
123 })
124 if (res) {
125 if (res !== (await getDeletedUser())) {
126 userRes = await User.findByPk(res as string)
127 if (existingUsers.length > 1) {
128 logger.debug({
129 message: `Multiple fedi users found for ${userData.url} (${userData.remoteId}): ${existingUsers.length}`
130 })
131 for await (const userWithDuplicatedData of existingUsers.slice(1)) {
132 userWithDuplicatedData.url = userWithDuplicatedData.url + '_DUPLICATED_' + new Date().getTime()
133 userWithDuplicatedData.remoteId =
134 userWithDuplicatedData.remoteId + '_DUPLICATED_' + new Date().getTime()
135 }
136 }
137 if (existingUsers && existingUsers.length > 0 && existingUsers[0] && userRes?.id !== existingUsers[0]?.id) {
138 const existingUser = existingUsers[0]
139 existingUser.activated = false
140 existingUser.remoteId = `${existingUser.remoteId}_OVERWRITTEN_ON${new Date().getTime()}`
141 existingUser.url = `${existingUser.url}_OVERWRITTEN_ON${new Date().getTime()}`
142 await existingUser.save()
143 if (userRes) {
144 const updates = [
145 Follows.update(
146 {
147 followerId: userRes.id
148 },
149 {
150 where: {
151 followerId: existingUser.id
152 }
153 }
154 ),
155 Follows.update(
156 {
157 followedId: userRes.id
158 },
159 {
160 where: {
161 followedId: existingUser.id
162 }
163 }
164 ),
165 Post.update(
166 {
167 userId: userRes.id
168 },
169 {
170 where: {
171 userId: existingUser.id
172 }
173 }
174 ),
175 UserLikesPostRelations.update(
176 {
177 userId: userRes.id
178 },
179 {
180 where: {
181 userId: existingUser.id
182 }
183 }
184 ),
185 EmojiReaction.update(
186 {
187 userId: userRes.id
188 },
189 {
190 where: {
191 userId: existingUser.id
192 }
193 }
194 ),
195 Blocks.update(
196 {
197 blockedId: userRes.id
198 },
199 {
200 where: {
201 blockedId: existingUser.id
202 }
203 }
204 ),
205 Blocks.update(
206 {
207 blockerId: userRes.id
208 },
209 {
210 where: {
211 blockerId: existingUser.id
212 }
213 }
214 ),
215 Mutes.update(
216 {
217 muterId: userRes.id
218 },
219 {
220 where: {
221 muterId: existingUser.id
222 }
223 }
224 ),
225 Mutes.update(
226 {
227 mutedId: userRes.id
228 },
229 {
230 where: {
231 mutedId: existingUser.id
232 }
233 }
234 ),
235 PostMentionsUserRelation.update(
236 {
237 userId: userRes.id
238 },
239 {
240 where: {
241 userId: existingUser.id
242 }
243 }
244 )
245 ]
246 await Promise.all(updates)
247 }
248 await redisCache.del('userRemoteId:' + existingUser.remoteId)
249 }
250 if (userRes) {
251 userRes.set(userData)
252 await userRes.save()
253 } else {
254 redisCache.del('userRemoteId:' + actorUrl.toLocaleLowerCase())
255 }
256 }
257 } else {
258 if (existingUsers && existingUsers[0]) {
259 existingUsers[0].set(userData)
260 await existingUsers[0].save()
261 } else {
262 userRes = await User.create(userData)
263 }
264 }
265 if (
266 userRes &&
267 userRes.id &&
268 userRes.url != completeEnvironment.deletedUser &&
269 userPetition &&
270 userPetition.attachment &&
271 userPetition.attachment.length
272 ) {
273 await UserOptions.destroy({
274 where: {
275 userId: userRes.id,
276 optionName: {
277 [Op.like]: 'fediverse.public.attachment'
278 }
279 }
280 })
281 const properties = userPetition.attachment.filter((elem: any) => elem.type === 'PropertyValue')
282 await UserOptions.create({
283 userId: userRes.id,
284 optionName: `fediverse.public.attachment`,
285 optionValue: JSON.stringify(properties),
286 public: true
287 })
288 }
289 res = userRes?.id ? userRes.id : await getDeletedUser()
290 try {
291 if (userRes) {
292 const tags = userPetition?.tag
293 ? Array.isArray(userPetition.tag)
294 ? userPetition.tag
295 : [userPetition.tag]
296 : []
297 const emojis = [...new Set(tags.filter((elem: fediverseTag) => elem.type === 'Emoji'))]
298 await processUserEmojis(userRes, emojis)
299 }
300 } catch (error) {
301 logger.info({
302 message: `Error processing emojis from user ${userRes?.url}`,
303 error: error,
304 tags: userPetition?.tag,
305 userPetition: userPetition
306 })
307 }
308 }
309 }
310 }
311 return res
312}
313
314export { getRemoteActorIdProcessor }