-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: clear conversations without purpose (#717)
* add participant to chat on subcourse join * mark matchee convo as readonly or writeable * subcourse purposes * add chat access enum * check if members have write rights * add new chat meta data * convert meta data as string * add groupType to metadata, dont stringify strings * get matchId and subcourseIds for contact * add mutation for contact chats * adjust integration-test * review changes: renamings and types * Fix tests & type * functions to remove subcourse and match mark past subcourse chat mark empty conversation * no dissolved or cancelled contact * get all conversations * review changes * add cron job file * remove useless functions * add function to flag inactive conversations * fix async * add reduce * remove bbb * adjust clean up function * add send system message * add message on clean up * remove comment * review changes * no contacts from past courses * every to some * adjust to yesterday --------- Co-authored-by: Jonas Wilms <[email protected]>
- Loading branch information
Showing
7 changed files
with
131 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import moment from 'moment'; | ||
import { getMatch, getSubcourse } from '../../../graphql/util'; | ||
import { getAllConversations, markConversationAsReadOnly, sendSystemMessage } from '../../../common/chat'; | ||
import { getLogger } from '../../../common/logger/logger'; | ||
import { Lecture } from '../../../graphql/generated'; | ||
import { SystemMessage } from '../../../common/chat/types'; | ||
import systemMessages from '../../../common/chat/localization'; | ||
import { checkChatMembersAccessRights, countChatParticipants } from '../../../common/chat/helper'; | ||
|
||
const logger = getLogger('FlagOldConversationsAsRO'); | ||
|
||
enum ConversationType { | ||
GROUP = 'group', | ||
ONE_ON_ONE = 'one_on_one', | ||
} | ||
|
||
type conversationsToDeactivate = { | ||
id: string; | ||
conversationType: ConversationType; | ||
}; | ||
|
||
// one to one chats (if match) whose match was dissolved 30 days ago should be "disabled" (readonly). | ||
// This will allow users to continue writing for another 30 days after match disolving. | ||
async function isActiveMatch(id: number): Promise<boolean> { | ||
const today = moment().endOf('day'); | ||
const match = await getMatch(id); | ||
const dissolvedAtPlus30Days = moment(match.dissolvedAt).add(30, 'days'); | ||
return !dissolvedAtPlus30Days.isBefore(today); | ||
} | ||
|
||
async function isActiveSubcourse(id: number): Promise<boolean> { | ||
const today = moment().endOf('day'); | ||
const subcourse = await getSubcourse(id, true); | ||
const isSubcourseCancelled = subcourse.cancelled; | ||
|
||
if (isSubcourseCancelled) { | ||
return false; | ||
} | ||
|
||
const lastLecutre = subcourse.lecture.sort((a: Lecture, b: Lecture) => moment(a.start).milliseconds() - moment(b.start).milliseconds()).pop(); | ||
const lastLecturePlus30Days = moment(lastLecutre.start).add(30, 'days'); | ||
const is30DaysBeforeToday = lastLecturePlus30Days.isBefore(today); | ||
return !is30DaysBeforeToday; | ||
} | ||
|
||
export default async function flagInactiveConversationsAsReadonly() { | ||
const conversations = await getAllConversations(); | ||
const conversationsToFlag: conversationsToDeactivate[] = []; | ||
|
||
for (const conversation of conversations.data) { | ||
let shouldMarkAsReadonly: boolean; | ||
|
||
// to prevent to flag already deactivated chats we check if the conversation is already readonly (only readMembers) | ||
const countParticipants = countChatParticipants(conversation); | ||
const { readMembers } = checkChatMembersAccessRights(conversation); | ||
const isChatReadOnly = readMembers.length === countParticipants; | ||
if (isChatReadOnly) { | ||
return; | ||
} | ||
|
||
if (conversation.custom.subcourse) { | ||
const subcourseIds: number[] = JSON.parse(conversation.custom.subcourse); | ||
const allSubcoursesActive = await Promise.all(subcourseIds.map((id) => isActiveSubcourse(id))); | ||
shouldMarkAsReadonly = allSubcoursesActive.every((active) => active === false); | ||
} | ||
if (conversation.custom.prospectSubcourse) { | ||
const prospectSubcourses: number[] = conversation.custom.prospectSubcourse; | ||
const allProspectSubcoursesActive = await Promise.all(prospectSubcourses.map((id) => isActiveSubcourse(id))); | ||
shouldMarkAsReadonly = allProspectSubcoursesActive.every((active) => active === false); | ||
} | ||
|
||
if (conversation.custom.match) { | ||
const match = JSON.parse(conversation.custom.match); | ||
const matchId = match.matchId; | ||
const isMatchActive = await isActiveMatch(matchId); | ||
shouldMarkAsReadonly = !isMatchActive; | ||
} | ||
|
||
if (shouldMarkAsReadonly) { | ||
conversationsToFlag.push({ | ||
id: conversation.id, | ||
conversationType: conversation.custom.match ? ConversationType.ONE_ON_ONE : ConversationType.GROUP, | ||
}); | ||
} | ||
} | ||
|
||
if (conversationsToFlag.length > 0) { | ||
for (const convo of conversationsToFlag) { | ||
await markConversationAsReadOnly(convo.id); | ||
if (convo.conversationType === ConversationType.ONE_ON_ONE) { | ||
await sendSystemMessage(systemMessages.de.deactivated, convo.id, SystemMessage.ONE_ON_ONE_OVER); | ||
} else { | ||
await sendSystemMessage(systemMessages.de.deactivated, convo.id, SystemMessage.GROUP_OVER); | ||
} | ||
logger.info('Mark converstation as readonly', { conversationId: convo.id, conversationType: convo.conversationType }); | ||
} | ||
} else { | ||
logger.info('No conversation to mark as readonly'); | ||
} | ||
} |