Skip to content

Commit

Permalink
Merge pull request #329 from polkasafe/fix/postCommentAddedIssue
Browse files Browse the repository at this point in the history
fix: post comment added issue
  • Loading branch information
This-Is-Prince authored Apr 23, 2024
2 parents da68280 + 19de504 commit 625b9e8
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function getMentionedUsernames(content: string): string[] {
// matches for spaces around it and/or html tags, except for the anchor tag
const mentionedUsernamesPattern = /(?<=(?:^|\s+|<((?!a\b)\w+)>|&nbsp;)@)\w+(?=(?:\s+|&nbsp;|<\/((?!a\b)\w+)>))/g;
return String(content).match(mentionedUsernamesPattern) || [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { NotificationService } from '../../NotificationService';
import getTemplateRender from '../../global-utils/getTemplateRender';
import { NOTIFICATION_SOURCE } from '../../notification_engine_constants';
import getHouseNotificationPrefsFromTHNotificationPrefs from './getHouseNotificationPrefsFromTHNotificationPrefs';
import getMentionedUsernames from './getMentionedUsernames';
import { ETHContentType, ITHUser } from './types';

interface Args {
firestore_db: FirebaseFirestore.Firestore
authorUsername: string | null;
htmlContent: string;
house_id: string;
type: ETHContentType;
url: string;
}

const TRIGGER_NAME = 'newMention';
const SOURCE = NOTIFICATION_SOURCE.TOWNHALL;

export default async function sendMentionNotifications(args : Args) {
if (!args) throw Error(`Missing arguments for trigger: ${TRIGGER_NAME}`);
const { firestore_db, authorUsername, htmlContent, house_id, type, url } = args;
console.log(`Running trigger: ${TRIGGER_NAME}, with args: ${JSON.stringify({ authorUsername, htmlContent, house_id, type, url })}`);

const mentionedUsernames = getMentionedUsernames(htmlContent).filter((username) => username !== authorUsername);
console.log(`Mentioned usernames: ${JSON.stringify(mentionedUsernames)}`);
if (!mentionedUsernames.length) return;

for (const mentionedUsername of mentionedUsernames) {
// get user preferences
const mentionedUserDocSnapshot = await firestore_db.collection('users').where('username', '==', mentionedUsername).limit(1).get();
if (mentionedUserDocSnapshot.empty) continue;

const mentionedUserData = mentionedUserDocSnapshot.docs[0].data() as ITHUser;
if (!mentionedUserData || !mentionedUserData.notification_preferences) continue;

const mentionedUserNotificationPreferences = getHouseNotificationPrefsFromTHNotificationPrefs(mentionedUserData.notification_preferences, house_id);
if (!mentionedUserNotificationPreferences) continue;

if (!mentionedUserNotificationPreferences.triggerPreferences?.[TRIGGER_NAME].enabled || !(mentionedUserNotificationPreferences.triggerPreferences?.[TRIGGER_NAME]?.mention_types || []).includes(type)) continue;

let domain = '';
try {
const urlObject = new URL(url);
domain = urlObject.origin; // This gets the protocol + hostname + port (if any)
} catch (error) {
console.error('Invalid URL: ', error);
}

const { htmlMessage, markdownMessage, textMessage, subject } = await getTemplateRender(
SOURCE,
TRIGGER_NAME,
{
...args,
authorUsername,
url,
content: htmlContent,
domain: domain,
username: mentionedUsername,
mentionType: type
});

const notificationServiceInstance = new NotificationService(
SOURCE,
TRIGGER_NAME,
htmlMessage,
markdownMessage,
textMessage,
subject,
{
link: url
}
);

console.log(`Sending notification for trigger: ${TRIGGER_NAME}, mention type: ${type} by user ${mentionedUserData.id}`);
await notificationServiceInstance.notifyAllChannels(mentionedUserNotificationPreferences);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export interface ITHPost {

// PROJECT FOR CUSTOM DEPLOYMENT
project_id: string;

subscribers: string[];
}

export interface ITHUserNotificationPreferences {
Expand Down Expand Up @@ -104,6 +106,12 @@ export interface ITHNotification {
url?: string;
}

export enum ETHContentType {
COMMENT = 'comment',
REPLY = 'reply',
POST = 'post'
}

export enum ETHBountySource {
TWITTER = 'twitter',
LENS = 'lens',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import getSourceFirebaseAdmin from '../../global-utils/getSourceFirebaseAdmin';
import { NOTIFICATION_SOURCE } from '../../notification_engine_constants';
import getTemplateRender from '../../global-utils/getTemplateRender';
import { thCommentRef, thPostRef, thUserRef } from '../_utils/thFirestoreRefs';
import { ITHComment, ITHUser } from '../_utils/types';
import { ETHContentType, ITHComment, ITHPost, ITHUser, ITHUserNotificationPreferences } from '../_utils/types';
import getHouseNotificationPrefsFromTHNotificationPrefs from '../_utils/getHouseNotificationPrefsFromTHNotificationPrefs';
import { generatePostUrl } from '../_utils/generateUrl';
import Showdown from 'showdown';
import sendMentionNotifications from '../_utils/sendMentionNotifications';

const TRIGGER_NAME = 'postCommentAdded';
const SOURCE = NOTIFICATION_SOURCE.TOWNHALL;
Expand All @@ -31,50 +34,95 @@ export default async function postCommentAdded(args: Args) {

// get post
const postId = commentData.post_id;
const postData = (await thPostRef(firestore_db, postId).get()).data();
const postData = (await thPostRef(firestore_db, postId).get()).data() as ITHPost;

if (!postData) {
throw Error(`Post with id ${postId} not found`);
}

const subscribers = [...(postData?.subscribers || []), postData.user_id].filter((user_id) => !!user_id) as string[]; // add post author to subscribers
if (!subscribers || !subscribers?.length) {
console.log(`No subscribers for a ${postData.post_type} type, post ${postId} in house ${postData?.house_id}.`);
return;
}

// get comment author
const commentAuthorData = (await thUserRef(firestore_db, commentData.user_id).get()).data() as ITHUser;

if (!commentAuthorData) {
throw Error(`Comment author with id ${commentData.user_id} not found`);
}

// fetch all users who have newPostCreated trigger enabled for this network
const subscribersSnapshot = await firestore_db
.collection('users')
.where(`notification_preferences.triggerPreferences.${postData.house_id}.${TRIGGER_NAME}.enabled`, '==', true)
.get();
const commentUrl = `${generatePostUrl(postData)}#${comment_id}`;

const converter = new Showdown.Converter();
const commentHTML = converter.makeHtml(commentData.content);

console.log(`Found ${subscribersSnapshot.size} subscribers for TRIGGER_NAME ${TRIGGER_NAME}`);
for (const userId of subscribers) {
if (userId === commentAuthorData.id) continue;

for (const subscriberDoc of subscribersSnapshot.docs) {
const subscriberData = subscriberDoc.data() as ITHUser;
if (!subscriberData.notification_preferences) continue;
const userDoc = await thUserRef(firestore_db, userId).get();
if (!userDoc.exists) continue;
const userData = userDoc.data() as ITHUser;
if (!userData) continue;

console.log(`Subscribed user for ${TRIGGER_NAME} with id: ${subscriberData.id}`);
const userTHNotificationPreferences: ITHUserNotificationPreferences | null = userData.notification_preferences || null;
if (!userTHNotificationPreferences && userId !== postData.user_id) continue; // only skip if user is not the post author

const subscriberNotificationPreferences = getHouseNotificationPrefsFromTHNotificationPrefs(
subscriberData.notification_preferences,
let userNotificationPreferences = getHouseNotificationPrefsFromTHNotificationPrefs(
userTHNotificationPreferences,
postData.house_id
);

if (!subscriberNotificationPreferences) continue;

const link = `https://www.townhallgov.com/${postData.house_id}/post/${postId}`;
// send notification to post author even if he hasn't set any notification preferences (or for this trigger)
if (userId === postData.user_id) {
// only skip if user has explicitly disabled 'commentsOnMyPosts' sub-trigger
if (userNotificationPreferences.triggerPreferences?.[TRIGGER_NAME]?.enabled === false ||
!(userNotificationPreferences.triggerPreferences?.[TRIGGER_NAME]?.sub_triggers || ['commentsOnMyPosts']).includes('commentsOnMyPosts')
) continue;

// pseudo notification prefs with 'commentsOnMyPosts' sub-trigger enabled (to make default behaviour as enabled)
userNotificationPreferences = {
...userNotificationPreferences,
triggerPreferences: {
...userNotificationPreferences.triggerPreferences,
[TRIGGER_NAME]: {
...userNotificationPreferences.triggerPreferences?.[TRIGGER_NAME],
enabled: true,
name: TRIGGER_NAME,
sub_triggers: ['commentsOnMyPosts']
}
}
};
} else {
// only skip if user has explicitly disabled 'commentsOnSubscribedPosts' sub-trigger
if (userNotificationPreferences.triggerPreferences?.[TRIGGER_NAME]?.enabled === false ||
!(userNotificationPreferences.triggerPreferences?.[TRIGGER_NAME]?.sub_triggers || ['commentsOnSubscribedPosts']).includes('commentsOnSubscribedPosts')
) continue;

// pseudo notification prefs with 'commentsOnSubscribedPosts' sub-trigger enabled (to make default behaviour as enabled)
userNotificationPreferences = {
...userNotificationPreferences,
triggerPreferences: {
...userNotificationPreferences.triggerPreferences,
[TRIGGER_NAME]: {
...userNotificationPreferences.triggerPreferences?.[TRIGGER_NAME],
enabled: true,
name: TRIGGER_NAME,
sub_triggers: ['commentsOnSubscribedPosts']
}
}
};
}

const { htmlMessage, markdownMessage, textMessage, subject } = await getTemplateRender(SOURCE, TRIGGER_NAME, {
...args,
username: subscriberData.name || !subscriberData.is_username_autogenerated ? subscriberData.username : 'user',
username: userData.name || !userData.is_username_autogenerated ? userData.username : 'user',
comment_author: commentAuthorData.name || !commentAuthorData.is_username_autogenerated ? commentAuthorData.username : 'user',
proposal_title: postData.title,
link,
link: commentUrl,
post_type: (`${postData.post_type}`).replaceAll('_', ' '),
comment: commentData.content
comment: commentHTML
});

const notificationServiceInstance = new NotificationService(
Expand All @@ -85,15 +133,24 @@ export default async function postCommentAdded(args: Args) {
textMessage,
subject,
{
link
link: commentUrl
}
);

console.log(
`Sending notification to user_id ${subscriberDoc.id} for trigger ${TRIGGER_NAME} on house ${postData.house_id} for postId ${postData.id}`
`Sending notification to user_id ${userData.id} for trigger ${TRIGGER_NAME} on house ${postData.house_id} for postId ${postData.id}`
);
await notificationServiceInstance.notifyAllChannels(subscriberNotificationPreferences);
await notificationServiceInstance.notifyAllChannels(userNotificationPreferences);
}

await sendMentionNotifications({
firestore_db,
authorUsername: commentAuthorData.username,
htmlContent: commentHTML,
house_id: postData.house_id || commentData.house_id,
type: ETHContentType.COMMENT,
url: commentUrl
});

return;
}

0 comments on commit 625b9e8

Please sign in to comment.