unoffical wafrn mirror wafrn.net
atproto social-network activitypub
at angular21 861 lines 27 kB view raw
1import { Op } from "sequelize"; 2import { 3 Blocks, 4 Emoji, 5 FederatedHost, 6 Media, 7 Post, 8 PostMentionsUserRelation, 9 ServerBlock, 10 PostTag, 11 User, 12 sequelize, 13 Ask, 14 Notification, 15 EmojiReaction, 16 PostAncestor, 17 PostReport, 18 QuestionPoll, 19 Quotes, 20 RemoteUserPostView, 21 SilencedPost, 22 UserBitesPostRelation, 23 UserBookmarkedPosts, 24 UserLikesPostRelations, 25} from "../../models/index.js"; 26import { completeEnvironment } from "../backendOptions.js"; 27import { logger } from "../logger.js"; 28import { getRemoteActor } from "./getRemoteActor.js"; 29import { getPetitionSigned } from "./getPetitionSigned.js"; 30import { fediverseTag } from "../../interfaces/fediverse/tags.js"; 31import { loadPoll } from "./loadPollFromPost.js"; 32import { getApObjectPrivacy } from "./getPrivacy.js"; 33import dompurify from "isomorphic-dompurify"; 34import { Queue } from "bullmq"; 35import { bulkCreateNotifications } from "../pushNotifications.js"; 36import { getDeletedUser } from "../cacheGetters/getDeletedUser.js"; 37import { Privacy } from "../../models/post.js"; 38import { 39 getAtProtoThread, 40 getPostThreadSafe, 41 processSinglePost, 42} from "../../atproto/utils/getAtProtoThread.js"; 43import * as cheerio from "cheerio"; 44import { 45 PostView, 46 ThreadViewPost, 47} from "@atproto/api/dist/client/types/app/bsky/feed/defs.js"; 48import { getAdminUser } from "../getAdminAndDeletedUser.js"; 49 50const updateMediaDataQueue = new Queue("processRemoteMediaData", { 51 connection: completeEnvironment.bullmqConnection, 52 defaultJobOptions: { 53 removeOnComplete: true, 54 attempts: 3, 55 backoff: { 56 type: "exponential", 57 delay: 1000, 58 }, 59 removeOnFail: true, 60 }, 61}); 62 63async function getPostThreadRecursive( 64 user: any, 65 remotePostId: string | null, 66 remotePostObject?: any, 67 localPostToForceUpdate?: string, 68 options?: any 69) { 70 if (remotePostId === null) return; 71 72 const deletedUser = getDeletedUser(); 73 try { 74 remotePostId.startsWith( 75 `${completeEnvironment.frontendUrl}/fediverse/post/` 76 ); 77 } catch (error) { 78 logger.info({ 79 message: "Error with url on post", 80 object: remotePostId, 81 stack: new Error().stack, 82 }); 83 return; 84 } 85 if ( 86 remotePostId.startsWith( 87 `${completeEnvironment.frontendUrl}/fediverse/post/` 88 ) 89 ) { 90 // we are looking at a local post 91 const partToRemove = `${completeEnvironment.frontendUrl}/fediverse/post/`; 92 const postId = remotePostId.substring(partToRemove.length); 93 return await Post.findOne({ 94 where: { 95 id: postId, 96 }, 97 }); 98 } 99 if (completeEnvironment.enableBsky && remotePostId.startsWith("at://")) { 100 // Bluesky post. Likely coming from an import 101 const postInDatabase = await Post.findOne({ 102 where: { 103 bskyUri: remotePostId, 104 }, 105 }); 106 if (postInDatabase) { 107 return postInDatabase; 108 } else if (!remotePostObject) { 109 const postId = await getAtProtoThread(remotePostId); 110 return await Post.findByPk(postId); 111 } 112 } 113 const postInDatabase = await Post.findOne({ 114 where: { 115 remotePostId: remotePostId, 116 }, 117 }); 118 if (postInDatabase && !localPostToForceUpdate) { 119 if (postInDatabase.remotePostId) { 120 const parentPostPetition = await getPetitionSigned( 121 user, 122 postInDatabase.remotePostId 123 ); 124 if (parentPostPetition) { 125 await loadPoll(parentPostPetition, postInDatabase, user); 126 } 127 } 128 return postInDatabase; 129 } else { 130 try { 131 const postPetition = remotePostObject 132 ? remotePostObject 133 : await getPetitionSigned(user, remotePostId); 134 if (postPetition && !localPostToForceUpdate) { 135 const remotePostInDatabase = await Post.findOne({ 136 where: { 137 remotePostId: postPetition.id, 138 }, 139 }); 140 if (remotePostInDatabase) { 141 if (remotePostInDatabase.remotePostId) { 142 const parentPostPetition = await getPetitionSigned( 143 user, 144 remotePostInDatabase.remotePostId 145 ); 146 if (parentPostPetition) { 147 await loadPoll(parentPostPetition, remotePostInDatabase, user); 148 } 149 } 150 return remotePostInDatabase; 151 } 152 } 153 // peertube: what the fuck 154 let actorUrl = postPetition.attributedTo; 155 if (Array.isArray(actorUrl)) { 156 actorUrl = actorUrl[0].id; 157 } 158 const remoteUser = await getRemoteActor(actorUrl, user); 159 if (remoteUser) { 160 const remoteHost = (await FederatedHost.findByPk( 161 remoteUser.federatedHostId as string 162 )) as FederatedHost; 163 const remoteUserServerBaned = remoteHost?.blocked 164 ? remoteHost.blocked 165 : false; 166 // HACK: some implementations (GTS IM LOOKING AT YOU) may send a single element instead of an array 167 // I should had used a funciton instead of this dirty thing, BUT you see, its late. Im eepy 168 // also this code is CRITICAL. A failure here is a big problem. So this hack it is 169 postPetition.tag = !Array.isArray(postPetition.tag) 170 ? [postPetition.tag].filter((elem) => elem) 171 : postPetition.tag; 172 const medias: any[] = []; 173 const fediTags: fediverseTag[] = [ 174 ...new Set<fediverseTag>( 175 postPetition.tag 176 ?.filter((elem: fediverseTag) => 177 [ 178 postPetition.tag.some( 179 (tag: fediverseTag) => tag.type == "WafrnHashtag" 180 ) 181 ? "WafrnHashtag" 182 : "Hashtag", 183 ].includes(elem.type) 184 ) 185 .map((elem: fediverseTag) => { 186 return { href: elem.href, type: elem.type, name: elem.name }; 187 }) 188 ), 189 ]; 190 let fediMentions: fediverseTag[] = postPetition.tag?.filter( 191 (elem: fediverseTag) => elem.type === "Mention" 192 ); 193 if (fediMentions == undefined) { 194 fediMentions = postPetition.to.map((elem: string) => { 195 return { href: elem }; 196 }); 197 } 198 const fediEmojis: any[] = postPetition.tag?.filter( 199 (elem: fediverseTag) => elem.type === "Emoji" 200 ); 201 202 const privacy = getApObjectPrivacy(postPetition, remoteUser); 203 204 let postTextContent = `${ 205 postPetition.content ? postPetition.content : "" 206 }`; // Fix for bridgy giving this as undefined 207 if (postPetition.type == "Video") { 208 // peertube federation. We just add a link to the video, federating this is HELL 209 postTextContent = 210 postTextContent + 211 ` <a href="${postPetition.id}" target="_blank">${postPetition.id}</a>`; 212 } 213 if ( 214 postPetition.tag && 215 postPetition.tag.some( 216 (tag: fediverseTag) => tag.type === "WafrnHashtag" 217 ) 218 ) { 219 // Ok we have wafrn hashtags with us, we are probably talking with another wafrn! Crazy, I know 220 const dom = cheerio.load(postTextContent); 221 const tags = dom("a.hashtag").html(""); 222 postTextContent = dom.html(); 223 } 224 if ( 225 postPetition.attachment && 226 postPetition.attachment.length > 0 && 227 (!remoteUser.banned || options?.allowMediaFromBanned) 228 ) { 229 for await (const remoteFile of postPetition.attachment) { 230 if (remoteFile.type !== "Link") { 231 const wafrnMedia = await Media.create({ 232 url: remoteFile.url, 233 NSFW: postPetition?.sensitive, 234 userId: 235 remoteUserServerBaned || remoteUser.banned 236 ? ( 237 await deletedUser 238 )?.id 239 : remoteUser.id, 240 description: remoteFile.name, 241 ipUpload: "IMAGE_FROM_OTHER_FEDIVERSE_INSTANCE", 242 mediaOrder: postPetition.attachment.indexOf(remoteFile), // could be non consecutive but its ok 243 external: true, 244 mediaType: remoteFile.mediaType ? remoteFile.mediaType : "", 245 blurhash: remoteFile.blurhash ? remoteFile.blurhash : null, 246 height: remoteFile.height ? remoteFile.height : null, 247 width: remoteFile.width ? remoteFile.width : null, 248 }); 249 if ( 250 !wafrnMedia.mediaType || 251 (wafrnMedia.mediaType?.startsWith("image") && !wafrnMedia.width) 252 ) { 253 await updateMediaDataQueue.add(`updateMedia:${wafrnMedia.id}`, { 254 mediaId: wafrnMedia.id, 255 }); 256 } 257 medias.push(wafrnMedia); 258 } else { 259 postTextContent = 260 "" + 261 postTextContent + 262 `<a href="${remoteFile.href}" >${remoteFile.href}</a>`; 263 } 264 } 265 } 266 const lemmyName = postPetition.name ? postPetition.name : ""; 267 postTextContent = postTextContent 268 ? postTextContent 269 : `<p>${lemmyName}</p>`; 270 let createdAt = new Date(postPetition.published); 271 if (createdAt.getTime() > new Date().getTime()) { 272 createdAt = new Date(); 273 } 274 275 let bskyUri: string | undefined, bskyCid: string | undefined; 276 let existingBskyPost: Post | undefined; 277 // check if it's a bridgy post or a post from a wafrn by checking a valid FEP-fffd 278 if (postPetition.url && Array.isArray(postPetition.url)) { 279 const url = postPetition.url as Array< 280 string | { type: string; href: string } 281 >; 282 const firstFffd = url.find((x) => typeof x !== "string"); 283 // check if it starts at at:// then its a bridged post, we do not touch it if it's not 284 if (firstFffd && firstFffd.href.startsWith("at://")) { 285 // get it's bsky counterparts first, we need the cid 286 const thread = await getPostThreadSafe({ 287 uri: firstFffd.href, 288 }); 289 if (thread && thread.success) { 290 try { 291 const threadView = thread.data.thread as ThreadViewPost; 292 bskyCid = threadView.post.cid; 293 bskyUri = threadView.post.uri; 294 // check if it cames from wafrn 295 if ( 296 !( 297 threadView.post.record as { 298 fediverseId: string | undefined; 299 } 300 ).fediverseId 301 ) { 302 // this is a bridgy fed post, assume main post is on bsky, use bsky user 303 const postId = await processSinglePost(threadView.post); 304 if (postId) { 305 const post = await Post.findByPk(postId); 306 if (post) { 307 post.remotePostId = postPetition.id; 308 await post.save(); 309 return post; 310 } 311 } 312 } else { 313 // now this is a wafrn post, where we do a thing little bit different 314 // first we going to check if the post is already on db because this can break everything 315 let existingPost = await Post.findOne({ 316 where: { 317 bskyCid: threadView.post.cid, 318 remotePostId: null, 319 }, 320 }); 321 if (existingPost) { 322 existingBskyPost = existingPost; 323 // do not attempt to merge it right now, this will crash backend 324 bskyCid = undefined; 325 bskyUri = undefined; 326 } 327 } 328 } catch {} 329 } 330 } 331 } 332 333 const postToCreate: any = { 334 content: "" + postTextContent, 335 content_warning: postPetition.summary 336 ? postPetition.summary 337 : remoteUser.NSFW 338 ? "User is marked as NSFW by this instance staff. Possible NSFW without tagging" 339 : "", 340 createdAt: createdAt, 341 updatedAt: createdAt, 342 userId: 343 remoteUserServerBaned || remoteUser.banned 344 ? (await deletedUser)?.id 345 : remoteUser.id, 346 remotePostId: postPetition.id, 347 privacy: privacy, 348 bskyUri: postPetition.blueskyUri, 349 displayUrl: Array.isArray(postPetition.url) 350 ? postPetition.url[0] 351 : postPetition.url, 352 bskyCid: postPetition.blueskyCid, 353 ...(bskyCid && bskyUri 354 ? { 355 bskyCid, 356 bskyUri, 357 } 358 : {}), 359 }; 360 361 if (postPetition.name) { 362 postToCreate.title = postPetition.name; 363 } 364 365 const mentionedUsersIds: string[] = []; 366 const quotes: any[] = []; 367 try { 368 if (!remoteUser.banned && !remoteUserServerBaned) { 369 for await (const mention of fediMentions) { 370 let mentionedUser; 371 if ( 372 mention.href?.indexOf(completeEnvironment.frontendUrl) !== -1 373 ) { 374 const username = mention.href?.substring( 375 `${completeEnvironment.frontendUrl}/fediverse/blog/`.length 376 ) as string; 377 mentionedUser = await User.findOne({ 378 where: sequelize.where( 379 sequelize.fn("lower", sequelize.col("url")), 380 username.toLowerCase() 381 ), 382 }); 383 } else { 384 mentionedUser = await getRemoteActor(mention.href, user); 385 } 386 if ( 387 mentionedUser?.id && 388 mentionedUser.id != (await deletedUser)?.id && 389 !mentionedUsersIds.includes(mentionedUser.id) 390 ) { 391 mentionedUsersIds.push(mentionedUser.id); 392 } 393 } 394 } 395 } catch (error) { 396 logger.info({ message: "problem processing mentions", error }); 397 } 398 399 if ( 400 postPetition.inReplyTo && 401 postPetition.id !== postPetition.inReplyTo 402 ) { 403 const parent = await getPostThreadRecursive( 404 user, 405 postPetition.inReplyTo.id 406 ? postPetition.inReplyTo.id 407 : postPetition.inReplyTo 408 ); 409 postToCreate.parentId = parent?.id; 410 } 411 412 const existingPost = localPostToForceUpdate 413 ? await Post.findByPk(localPostToForceUpdate) 414 : undefined; 415 416 if (existingPost) { 417 existingPost.set(postToCreate); 418 await existingPost.save(); 419 await loadPoll(postPetition, existingPost, user); 420 } 421 422 const newPost = existingPost 423 ? existingPost 424 : await Post.create(postToCreate); 425 try { 426 if (!remoteUser.banned && !remoteUserServerBaned && fediEmojis) { 427 processEmojis(newPost, fediEmojis); 428 } 429 } catch (error) { 430 logger.debug("Problem processing emojis"); 431 } 432 newPost.setMedias(medias); 433 try { 434 if (postPetition.quote || postPetition.quoteUrl) { 435 const urlQuote = postPetition.quoteUrl || postPetition.quote; 436 const postToQuote = await getPostThreadRecursive(user, urlQuote); 437 if (postToQuote && postToQuote.privacy != Privacy.DirectMessage) { 438 quotes.push(postToQuote); 439 } 440 if (!postToQuote) { 441 postToCreate.content = 442 postToCreate.content + `<p>RE: ${urlQuote}</p>`; 443 } 444 const postsToQuotePromise: any[] = []; 445 postPetition.tag 446 ?.filter((elem: fediverseTag) => elem.type === "Link") 447 .forEach((quote: fediverseTag) => { 448 postsToQuotePromise.push( 449 getPostThreadRecursive(user, quote.href as string) 450 ); 451 postToCreate.content = postToCreate.content.replace( 452 quote.name, 453 "" 454 ); 455 }); 456 const quotesToAdd = await Promise.allSettled(postsToQuotePromise); 457 const quotesThatWillGetAdded = quotesToAdd.filter( 458 (elem) => 459 elem.status === "fulfilled" && 460 elem.value && 461 elem.value.privacy !== 10 462 ); 463 quotesThatWillGetAdded.forEach((quot) => { 464 if ( 465 quot.status === "fulfilled" && 466 !quotes.map((q) => q.id).includes(quot.value.id) 467 ) { 468 quotes.push(quot.value); 469 } 470 }); 471 } 472 } catch (error) { 473 logger.info("Error processing quotes"); 474 logger.debug(error); 475 } 476 newPost.setQuoted(quotes); 477 478 await newPost.save(); 479 480 await bulkCreateNotifications( 481 quotes.map((quote) => ({ 482 notificationType: "QUOTE", 483 notifiedUserId: quote.userId, 484 userId: newPost.userId, 485 postId: newPost.id, 486 createdAt: new Date(newPost.createdAt), 487 })), 488 { 489 postContent: newPost.content, 490 userUrl: remoteUser.url, 491 } 492 ); 493 try { 494 if (!remoteUser.banned && !remoteUserServerBaned) { 495 await addTagsToPost(newPost, fediTags); 496 } 497 } catch (error) { 498 logger.info("problem processing tags"); 499 } 500 try { 501 await addAsksToPost(newPost, fediTags); 502 } catch (error) {} 503 if (mentionedUsersIds.length != 0) { 504 await processMentions(newPost, mentionedUsersIds); 505 } 506 await loadPoll(remotePostObject, newPost, user); 507 const postCleanContent = dompurify 508 .sanitize(newPost.content, { ALLOWED_TAGS: [] }) 509 .trim(); 510 const mentions = await newPost.getMentionPost(); 511 if (postCleanContent.startsWith("!ask") && mentions.length === 1) { 512 let askContent = postCleanContent.split( 513 `!ask @${mentions[0].url}` 514 )[1]; 515 if (askContent.startsWith("@" + completeEnvironment.instanceUrl)) { 516 askContent = askContent.split( 517 "@" + completeEnvironment.instanceUrl 518 )[1]; 519 } 520 await Ask.create({ 521 question: askContent, 522 userAsker: newPost.userId, 523 userAsked: mentions[0].id, 524 answered: false, 525 apObject: JSON.stringify(postPetition), 526 }); 527 } 528 529 if (existingBskyPost) { 530 // very expensive updates! but only happens when bsky 531 // post is already on db but the fedi post is not 532 await EmojiReaction.update( 533 { 534 postId: newPost.id, 535 }, 536 { 537 where: { 538 postId: existingBskyPost.id, 539 }, 540 } 541 ); 542 await Notification.update( 543 { 544 postId: newPost.id, 545 }, 546 { 547 where: { 548 postId: existingBskyPost.id, 549 }, 550 } 551 ); 552 await PostReport.update( 553 { 554 postId: newPost.id, 555 }, 556 { 557 where: { 558 postId: existingBskyPost.id, 559 }, 560 } 561 ); 562 try { 563 await PostAncestor.update( 564 { 565 postsId: newPost.id, 566 }, 567 { 568 where: { 569 postsId: existingBskyPost.id, 570 }, 571 } 572 ); 573 } catch {} 574 await QuestionPoll.update( 575 { 576 postId: newPost.id, 577 }, 578 { 579 where: { 580 postId: existingBskyPost.id, 581 }, 582 } 583 ); 584 await Quotes.update( 585 { 586 quoterPostId: newPost.id, 587 }, 588 { 589 where: { 590 quoterPostId: existingBskyPost.id, 591 }, 592 } 593 ); 594 if ( 595 !(await Quotes.findOne({ 596 where: { 597 quotedPostId: newPost.id, 598 }, 599 })) 600 ) { 601 await Quotes.update( 602 { 603 quotedPostId: newPost.id, 604 }, 605 { 606 where: { 607 quotedPostId: existingBskyPost.id, 608 }, 609 } 610 ); 611 } 612 await RemoteUserPostView.update( 613 { 614 postId: newPost.id, 615 }, 616 { 617 where: { 618 postId: existingBskyPost.id, 619 }, 620 } 621 ); 622 await SilencedPost.update( 623 { 624 postId: newPost.id, 625 }, 626 { 627 where: { 628 postId: existingBskyPost.id, 629 }, 630 } 631 ); 632 await SilencedPost.update( 633 { 634 postId: newPost.id, 635 }, 636 { 637 where: { 638 postId: existingBskyPost.id, 639 }, 640 } 641 ); 642 await UserBitesPostRelation.update( 643 { 644 postId: newPost.id, 645 }, 646 { 647 where: { 648 postId: existingBskyPost.id, 649 }, 650 } 651 ); 652 await UserBookmarkedPosts.update( 653 { 654 postId: newPost.id, 655 }, 656 { 657 where: { 658 postId: existingBskyPost.id, 659 }, 660 } 661 ); 662 await UserLikesPostRelations.update( 663 { 664 postId: newPost.id, 665 }, 666 { 667 where: { 668 postId: existingBskyPost.id, 669 }, 670 } 671 ); 672 await Post.update( 673 { 674 parentId: newPost.id, 675 }, 676 { 677 where: { 678 parentId: existingBskyPost.id, 679 }, 680 } 681 ); 682 683 // now we delete the existing bsky post 684 await existingBskyPost.destroy(); 685 686 // THEN we merge it 687 newPost.bskyCid = existingBskyPost.bskyCid; 688 newPost.bskyUri = existingBskyPost.bskyUri; 689 await newPost.save(); 690 } 691 692 return newPost; 693 } 694 } catch (error) { 695 logger.trace({ 696 message: "error getting remote post", 697 url: remotePostId, 698 user: user.url, 699 problem: error, 700 }); 701 return null; 702 } 703 } 704} 705 706async function addAsksToPost(post: Post, tags: fediverseTag[]) { 707 const asks = tags.filter((elem) => elem.type === "AskQuestion"); 708 if (asks.length) { 709 const ask = asks[0]; 710 const userAsker = await getRemoteActor( 711 ask.actor as string, 712 await getAdminUser() 713 ); 714 const textToRemove = ask.representation as string; 715 const askText = ask.name; 716 if (textToRemove) { 717 post.content = post.content.replace(textToRemove, ""); 718 await Ask.create({ 719 answered: true, 720 postId: post.id, 721 userAsker: userAsker ? userAsker.id : undefined, 722 userAsked: post.userId, 723 }); 724 await post.save(); 725 } 726 } 727} 728 729async function addTagsToPost(post: any, originalTags: fediverseTag[]) { 730 let tags = [...originalTags]; 731 const res = await post.setPostTags([]); 732 if (tags.some((elem) => elem.name == "WafrnHashtag")) { 733 tags = tags.filter((elem) => elem.name == "WafrnHashtag"); 734 } 735 return await PostTag.bulkCreate( 736 tags 737 .filter((elem) => elem && post && elem.name && post.id) 738 .map((elem) => { 739 return { 740 tagName: elem?.name?.replace("#", ""), 741 postId: post.id, 742 }; 743 }) 744 ); 745} 746 747async function processMentions(post: any, userIds: string[]) { 748 await post.setMentionPost([]); 749 await Notification.destroy({ 750 where: { 751 notificationType: "MENTION", 752 postId: post.id, 753 }, 754 }); 755 const blocks = await Blocks.findAll({ 756 where: { 757 blockerId: { 758 [Op.in]: userIds, 759 }, 760 blockedId: post.userId, 761 }, 762 }); 763 const remoteUser = await User.findByPk(post.userId, { 764 attributes: ["url", "federatedHostId"], 765 }); 766 const userServerBlocks = await ServerBlock.findAll({ 767 where: { 768 userBlockerId: { 769 [Op.in]: userIds, 770 }, 771 blockedServerId: remoteUser?.federatedHostId || "", 772 }, 773 }); 774 const blockerIds: string[] = blocks 775 .map((block: any) => block.blockerId) 776 .concat(userServerBlocks.map((elem: any) => elem.userBlockerId)); 777 778 await bulkCreateNotifications( 779 userIds.map((mentionedUserId) => ({ 780 notificationType: "MENTION", 781 notifiedUserId: mentionedUserId, 782 userId: post.userId, 783 postId: post.id, 784 createdAt: new Date(post.createdAt), 785 })), 786 { 787 postContent: post.content, 788 userUrl: remoteUser?.url, 789 } 790 ); 791 792 return await PostMentionsUserRelation.bulkCreate( 793 userIds 794 .filter((elem) => !blockerIds.includes(elem)) 795 .map((elem) => { 796 return { 797 postId: post.id, 798 userId: elem, 799 }; 800 }), 801 { 802 ignoreDuplicates: true, 803 } 804 ); 805} 806 807async function processEmojis(post: any, fediEmojis: any[]) { 808 let emojis: any[] = []; 809 let res: any; 810 const emojiIds: string[] = Array.from( 811 new Set(fediEmojis.map((emoji: any) => emoji.id)) 812 ); 813 const foundEmojis = await Emoji.findAll({ 814 where: { 815 id: { 816 [Op.in]: emojiIds, 817 }, 818 }, 819 }); 820 foundEmojis.forEach((emoji: any) => { 821 const newData = fediEmojis.find( 822 (foundEmoji: any) => foundEmoji.id === emoji.id 823 ); 824 if (newData && newData.icon?.url) { 825 emoji.set({ 826 url: newData.icon.url, 827 }); 828 emoji.save(); 829 } else { 830 logger.debug("issue with emoji"); 831 logger.debug(emoji); 832 logger.debug(newData); 833 } 834 }); 835 emojis = emojis.concat(foundEmojis); 836 const notFoundEmojis = fediEmojis.filter( 837 (elem: any) => !foundEmojis.find((found: any) => found.id === elem.id) 838 ); 839 if (fediEmojis && notFoundEmojis && notFoundEmojis.length > 0) { 840 try { 841 const newEmojis = notFoundEmojis.map((newEmoji: any) => { 842 return { 843 id: newEmoji.id ? newEmoji.id : newEmoji.name + newEmoji.icon?.url, 844 name: newEmoji.name, 845 external: true, 846 url: newEmoji.icon?.url, 847 }; 848 }); 849 emojis = emojis.concat( 850 await Emoji.bulkCreate(newEmojis, { ignoreDuplicates: true }) 851 ); 852 } catch (error) { 853 logger.debug("Error with emojis"); 854 logger.debug(error); 855 } 856 } 857 858 return await post.setEmojis(emojis); 859} 860 861export { getPostThreadRecursive };